From e5fc9e7a666e5964b60e05903b90aa832354b68c Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Fri, 12 Nov 2010 17:33:17 +0100 Subject: netfilter: nf_conntrack: don't always initialize ct->proto ct->proto is big(60 bytes) due to structure ip_ct_tcp, and we don't need to initialize the whole for all the other protocols. This patch moves proto to the end of structure nf_conn, and pushes the initialization down to the individual protocols. Signed-off-by: Changli Gao Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_conntrack.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index caf17db87dbc..abfff1e8e0d0 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -116,14 +116,14 @@ struct nf_conn { u_int32_t secmark; #endif - /* Storage reserved for other modules: */ - union nf_conntrack_proto proto; - /* Extensions */ struct nf_ct_ext *ext; #ifdef CONFIG_NET_NS struct net *ct_net; #endif + + /* Storage reserved for other modules, must be the last member */ + union nf_conntrack_proto proto; }; static inline struct nf_conn * -- cgit v1.2.3 From 0f8e80044b26b4b30213a3fdffebd325cdc21362 Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Mon, 15 Nov 2010 11:51:06 +0100 Subject: netfilter: nf_conntrack: define ct_*_info as needed Signed-off-by: Changli Gao Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_conntrack.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index abfff1e8e0d0..8a58901c96d3 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -50,11 +50,24 @@ union nf_conntrack_expect_proto { /* per conntrack: application helper private data */ union nf_conntrack_help { /* insert conntrack helper private data (master) here */ +#if defined(CONFIG_NF_CONNTRACK_FTP) || defined(CONFIG_NF_CONNTRACK_FTP_MODULE) struct nf_ct_ftp_master ct_ftp_info; +#endif +#if defined(CONFIG_NF_CONNTRACK_PPTP) || \ + defined(CONFIG_NF_CONNTRACK_PPTP_MODULE) struct nf_ct_pptp_master ct_pptp_info; +#endif +#if defined(CONFIG_NF_CONNTRACK_H323) || \ + defined(CONFIG_NF_CONNTRACK_H323_MODULE) struct nf_ct_h323_master ct_h323_info; +#endif +#if defined(CONFIG_NF_CONNTRACK_SANE) || \ + defined(CONFIG_NF_CONNTRACK_SANE_MODULE) struct nf_ct_sane_master ct_sane_info; +#endif +#if defined(CONFIG_NF_CONNTRACK_SIP) || defined(CONFIG_NF_CONNTRACK_SIP_MODULE) struct nf_ct_sip_master ct_sip_info; +#endif }; #include -- cgit v1.2.3 From 76a2d3bcfcc86e2a8044258515b86492a37631a3 Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Mon, 15 Nov 2010 11:59:03 +0100 Subject: netfilter: nf_nat: don't use atomic bit operation As we own the conntrack and the others can't see it until we confirm it, we don't need to use atomic bit operation on ct->status. Signed-off-by: Changli Gao Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_nat_core.h | 4 ++-- net/ipv4/netfilter/nf_nat_core.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_nat_core.h b/include/net/netfilter/nf_nat_core.h index 33602ab66190..5aec85c29979 100644 --- a/include/net/netfilter/nf_nat_core.h +++ b/include/net/netfilter/nf_nat_core.h @@ -21,9 +21,9 @@ static inline int nf_nat_initialized(struct nf_conn *ct, enum nf_nat_manip_type manip) { if (manip == IP_NAT_MANIP_SRC) - return test_bit(IPS_SRC_NAT_DONE_BIT, &ct->status); + return ct->status & IPS_SRC_NAT_DONE_BIT; else - return test_bit(IPS_DST_NAT_DONE_BIT, &ct->status); + return ct->status & IPS_DST_NAT_DONE_BIT; } struct nlattr; diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c index c04787ce1a71..ab877acb22a1 100644 --- a/net/ipv4/netfilter/nf_nat_core.c +++ b/net/ipv4/netfilter/nf_nat_core.c @@ -323,9 +323,9 @@ nf_nat_setup_info(struct nf_conn *ct, /* It's done. */ if (maniptype == IP_NAT_MANIP_DST) - set_bit(IPS_DST_NAT_DONE_BIT, &ct->status); + ct->status |= IPS_DST_NAT_DONE_BIT; else - set_bit(IPS_SRC_NAT_DONE_BIT, &ct->status); + ct->status |= IPS_SRC_NAT_DONE_BIT; return NF_ACCEPT; } -- cgit v1.2.3 From e0e76c83becc7536e8371e560504d836d34fcf7d Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Mon, 15 Nov 2010 12:23:24 +0100 Subject: netfilter: ct_extend: define NF_CT_EXT_* as needed Less IDs make nf_ct_ext smaller. Signed-off-by: Changli Gao Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_conntrack_ecache.h | 8 ++++++++ include/net/netfilter/nf_conntrack_extend.h | 6 ++++++ include/net/netfilter/nf_nat.h | 4 ++++ 3 files changed, 18 insertions(+) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h index 96ba5f7dcab6..f596b60d6d75 100644 --- a/include/net/netfilter/nf_conntrack_ecache.h +++ b/include/net/netfilter/nf_conntrack_ecache.h @@ -23,12 +23,17 @@ struct nf_conntrack_ecache { static inline struct nf_conntrack_ecache * nf_ct_ecache_find(const struct nf_conn *ct) { +#ifdef CONFIG_NF_CONNTRACK_EVENTS return nf_ct_ext_find(ct, NF_CT_EXT_ECACHE); +#else + return NULL; +#endif } static inline struct nf_conntrack_ecache * nf_ct_ecache_ext_add(struct nf_conn *ct, u16 ctmask, u16 expmask, gfp_t gfp) { +#ifdef CONFIG_NF_CONNTRACK_EVENTS struct net *net = nf_ct_net(ct); struct nf_conntrack_ecache *e; @@ -45,6 +50,9 @@ nf_ct_ecache_ext_add(struct nf_conn *ct, u16 ctmask, u16 expmask, gfp_t gfp) e->expmask = expmask; } return e; +#else + return NULL; +#endif }; #ifdef CONFIG_NF_CONNTRACK_EVENTS diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 0772d296dfdb..1a9f96db3798 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -7,10 +7,16 @@ enum nf_ct_ext_id { NF_CT_EXT_HELPER, +#if defined(CONFIG_NF_NAT) || defined(CONFIG_NF_NAT_MODULE) NF_CT_EXT_NAT, +#endif NF_CT_EXT_ACCT, +#ifdef CONFIG_NF_CONNTRACK_EVENTS NF_CT_EXT_ECACHE, +#endif +#ifdef CONFIG_NF_CONNTRACK_ZONES NF_CT_EXT_ZONE, +#endif NF_CT_EXT_NUM, }; diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h index f5f09f032a90..e966092a36f1 100644 --- a/include/net/netfilter/nf_nat.h +++ b/include/net/netfilter/nf_nat.h @@ -84,7 +84,11 @@ extern int nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple, static inline struct nf_conn_nat *nfct_nat(const struct nf_conn *ct) { +#if defined(CONFIG_NF_NAT) || defined(CONFIG_NF_NAT_MODULE) return nf_ct_ext_find(ct, NF_CT_EXT_NAT); +#else + return NULL; +#endif } #else /* !__KERNEL__: iptables wants this to compile. */ -- cgit v1.2.3 From 03c0e5bb34c9755ae4d955c97fba40b24e9c7fe7 Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Mon, 15 Nov 2010 12:27:27 +0100 Subject: netfilter: nf_nat: define nat_pptp_info as needed Signed-off-by: Changli Gao Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_nat.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h index e966092a36f1..aff80b190c12 100644 --- a/include/net/netfilter/nf_nat.h +++ b/include/net/netfilter/nf_nat.h @@ -56,7 +56,9 @@ struct nf_nat_multi_range_compat { /* per conntrack: nat application helper private data */ union nf_conntrack_nat_help { /* insert nat helper private data here */ +#if defined(CONFIG_NF_NAT_PPTP) || defined(CONFIG_NF_NAT_PPTP_MODULE) struct nf_nat_pptp nat_pptp_info; +#endif }; struct nf_conn; -- cgit v1.2.3 From 0e60ebe04c51807db972d03665651ae6b5c26d7e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 15 Nov 2010 18:17:21 +0100 Subject: netfilter: add __rcu annotations Add some __rcu annotations and use helpers to reduce number of sparse warnings (CONFIG_SPARSE_RCU_POINTER=y) Signed-off-by: Eric Dumazet Signed-off-by: Patrick McHardy --- include/linux/netfilter.h | 6 +++--- include/net/netfilter/nf_conntrack_ecache.h | 4 ++-- include/net/netfilter/nf_conntrack_l3proto.h | 2 +- net/netfilter/core.c | 4 ++-- net/netfilter/nf_conntrack_expect.c | 6 +++--- net/netfilter/nf_conntrack_proto.c | 20 +++++++++++++++----- net/netfilter/nf_conntrack_standalone.c | 9 ++++++--- net/netfilter/nf_log.c | 6 ++++-- net/netfilter/nf_queue.c | 18 ++++++++++++++---- net/netfilter/nfnetlink_log.c | 6 +++--- 10 files changed, 53 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 89341c32631a..928a35ec21c7 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -265,7 +265,7 @@ struct nf_afinfo { int route_key_size; }; -extern const struct nf_afinfo *nf_afinfo[NFPROTO_NUMPROTO]; +extern const struct nf_afinfo __rcu *nf_afinfo[NFPROTO_NUMPROTO]; static inline const struct nf_afinfo *nf_get_afinfo(unsigned short family) { return rcu_dereference(nf_afinfo[family]); @@ -355,9 +355,9 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family) #endif /*CONFIG_NETFILTER*/ #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) -extern void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *); +extern void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *) __rcu; extern void nf_ct_attach(struct sk_buff *, struct sk_buff *); -extern void (*nf_ct_destroy)(struct nf_conntrack *); +extern void (*nf_ct_destroy)(struct nf_conntrack *) __rcu; #else static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} #endif diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h index f596b60d6d75..8fdb04b8cce0 100644 --- a/include/net/netfilter/nf_conntrack_ecache.h +++ b/include/net/netfilter/nf_conntrack_ecache.h @@ -67,7 +67,7 @@ struct nf_ct_event_notifier { int (*fcn)(unsigned int events, struct nf_ct_event *item); }; -extern struct nf_ct_event_notifier *nf_conntrack_event_cb; +extern struct nf_ct_event_notifier __rcu *nf_conntrack_event_cb; extern int nf_conntrack_register_notifier(struct nf_ct_event_notifier *nb); extern void nf_conntrack_unregister_notifier(struct nf_ct_event_notifier *nb); @@ -167,7 +167,7 @@ struct nf_exp_event_notifier { int (*fcn)(unsigned int events, struct nf_exp_event *item); }; -extern struct nf_exp_event_notifier *nf_expect_event_cb; +extern struct nf_exp_event_notifier __rcu *nf_expect_event_cb; extern int nf_ct_expect_register_notifier(struct nf_exp_event_notifier *nb); extern void nf_ct_expect_unregister_notifier(struct nf_exp_event_notifier *nb); diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index a7547611e8f1..e8010f445ae1 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -73,7 +73,7 @@ struct nf_conntrack_l3proto { struct module *me; }; -extern struct nf_conntrack_l3proto *nf_ct_l3protos[AF_MAX]; +extern struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[AF_MAX]; /* Protocol registration. */ extern int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto); diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 85dabb86be6f..5faec4fd8193 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -212,7 +212,7 @@ EXPORT_SYMBOL(skb_make_writable); /* This does not belong here, but locally generated errors need it if connection tracking in use: without this, connection may not be in hash table, and hence manufactured ICMP or RST packets will not be associated with it. */ -void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *); +void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *) __rcu __read_mostly; EXPORT_SYMBOL(ip_ct_attach); void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) @@ -229,7 +229,7 @@ void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) } EXPORT_SYMBOL(nf_ct_attach); -void (*nf_ct_destroy)(struct nf_conntrack *); +void (*nf_ct_destroy)(struct nf_conntrack *) __rcu __read_mostly; EXPORT_SYMBOL(nf_ct_destroy); void nf_conntrack_destroy(struct nf_conntrack *nfct) diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 46e8966912b1..cab196cf428c 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -482,7 +482,7 @@ static struct hlist_node *ct_expect_get_first(struct seq_file *seq) struct hlist_node *n; for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) { - n = rcu_dereference(net->ct.expect_hash[st->bucket].first); + n = rcu_dereference(hlist_first_rcu(&net->ct.expect_hash[st->bucket])); if (n) return n; } @@ -495,11 +495,11 @@ static struct hlist_node *ct_expect_get_next(struct seq_file *seq, struct net *net = seq_file_net(seq); struct ct_expect_iter_state *st = seq->private; - head = rcu_dereference(head->next); + head = rcu_dereference(hlist_next_rcu(head)); while (head == NULL) { if (++st->bucket >= nf_ct_expect_hsize) return NULL; - head = rcu_dereference(net->ct.expect_hash[st->bucket].first); + head = rcu_dereference(hlist_first_rcu(&net->ct.expect_hash[st->bucket])); } return head; } diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index dc7bb74110df..03b56a0fff30 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -166,6 +166,7 @@ static void nf_ct_l3proto_unregister_sysctl(struct nf_conntrack_l3proto *l3proto int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto) { int ret = 0; + struct nf_conntrack_l3proto *old; if (proto->l3proto >= AF_MAX) return -EBUSY; @@ -174,7 +175,9 @@ int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto) return -EINVAL; mutex_lock(&nf_ct_proto_mutex); - if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_l3proto_generic) { + old = rcu_dereference_protected(nf_ct_l3protos[proto->l3proto], + lockdep_is_held(&nf_ct_proto_mutex)); + if (old != &nf_conntrack_l3proto_generic) { ret = -EBUSY; goto out_unlock; } @@ -201,7 +204,9 @@ void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto) BUG_ON(proto->l3proto >= AF_MAX); mutex_lock(&nf_ct_proto_mutex); - BUG_ON(nf_ct_l3protos[proto->l3proto] != proto); + BUG_ON(rcu_dereference_protected(nf_ct_l3protos[proto->l3proto], + lockdep_is_held(&nf_ct_proto_mutex) + ) != proto); rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], &nf_conntrack_l3proto_generic); nf_ct_l3proto_unregister_sysctl(proto); @@ -299,8 +304,10 @@ int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto) smp_wmb(); nf_ct_protos[l4proto->l3proto] = proto_array; - } else if (nf_ct_protos[l4proto->l3proto][l4proto->l4proto] != - &nf_conntrack_l4proto_generic) { + } else if (rcu_dereference_protected( + nf_ct_protos[l4proto->l3proto][l4proto->l4proto], + lockdep_is_held(&nf_ct_proto_mutex) + ) != &nf_conntrack_l4proto_generic) { ret = -EBUSY; goto out_unlock; } @@ -331,7 +338,10 @@ void nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *l4proto) BUG_ON(l4proto->l3proto >= PF_MAX); mutex_lock(&nf_ct_proto_mutex); - BUG_ON(nf_ct_protos[l4proto->l3proto][l4proto->l4proto] != l4proto); + BUG_ON(rcu_dereference_protected( + nf_ct_protos[l4proto->l3proto][l4proto->l4proto], + lockdep_is_held(&nf_ct_proto_mutex) + ) != l4proto); rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], &nf_conntrack_l4proto_generic); nf_ct_l4proto_unregister_sysctl(l4proto); diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 0fb65705b44b..328f1d2a51f8 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -29,6 +29,7 @@ #include #include #include +#include MODULE_LICENSE("GPL"); @@ -56,7 +57,7 @@ static struct hlist_nulls_node *ct_get_first(struct seq_file *seq) for (st->bucket = 0; st->bucket < net->ct.htable_size; st->bucket++) { - n = rcu_dereference(net->ct.hash[st->bucket].first); + n = rcu_dereference(hlist_nulls_first_rcu(&net->ct.hash[st->bucket])); if (!is_a_nulls(n)) return n; } @@ -69,13 +70,15 @@ static struct hlist_nulls_node *ct_get_next(struct seq_file *seq, struct net *net = seq_file_net(seq); struct ct_iter_state *st = seq->private; - head = rcu_dereference(head->next); + head = rcu_dereference(hlist_nulls_next_rcu(head)); while (is_a_nulls(head)) { if (likely(get_nulls_value(head) == st->bucket)) { if (++st->bucket >= net->ct.htable_size) return NULL; } - head = rcu_dereference(net->ct.hash[st->bucket].first); + head = rcu_dereference( + hlist_nulls_first_rcu( + &net->ct.hash[st->bucket])); } return head; } diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index b07393eab88e..20c775cff2a8 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -161,7 +161,8 @@ static int seq_show(struct seq_file *s, void *v) struct nf_logger *t; int ret; - logger = nf_loggers[*pos]; + logger = rcu_dereference_protected(nf_loggers[*pos], + lockdep_is_held(&nf_log_mutex)); if (!logger) ret = seq_printf(s, "%2lld NONE (", *pos); @@ -249,7 +250,8 @@ static int nf_log_proc_dostring(ctl_table *table, int write, mutex_unlock(&nf_log_mutex); } else { mutex_lock(&nf_log_mutex); - logger = nf_loggers[tindex]; + logger = rcu_dereference_protected(nf_loggers[tindex], + lockdep_is_held(&nf_log_mutex)); if (!logger) table->data = "NONE"; else diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 74aebed5bd28..1876f7411561 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -27,14 +27,17 @@ static DEFINE_MUTEX(queue_handler_mutex); int nf_register_queue_handler(u_int8_t pf, const struct nf_queue_handler *qh) { int ret; + const struct nf_queue_handler *old; if (pf >= ARRAY_SIZE(queue_handler)) return -EINVAL; mutex_lock(&queue_handler_mutex); - if (queue_handler[pf] == qh) + old = rcu_dereference_protected(queue_handler[pf], + lockdep_is_held(&queue_handler_mutex)); + if (old == qh) ret = -EEXIST; - else if (queue_handler[pf]) + else if (old) ret = -EBUSY; else { rcu_assign_pointer(queue_handler[pf], qh); @@ -49,11 +52,15 @@ EXPORT_SYMBOL(nf_register_queue_handler); /* The caller must flush their queue before this */ int nf_unregister_queue_handler(u_int8_t pf, const struct nf_queue_handler *qh) { + const struct nf_queue_handler *old; + if (pf >= ARRAY_SIZE(queue_handler)) return -EINVAL; mutex_lock(&queue_handler_mutex); - if (queue_handler[pf] && queue_handler[pf] != qh) { + old = rcu_dereference_protected(queue_handler[pf], + lockdep_is_held(&queue_handler_mutex)); + if (old && old != qh) { mutex_unlock(&queue_handler_mutex); return -EINVAL; } @@ -73,7 +80,10 @@ void nf_unregister_queue_handlers(const struct nf_queue_handler *qh) mutex_lock(&queue_handler_mutex); for (pf = 0; pf < ARRAY_SIZE(queue_handler); pf++) { - if (queue_handler[pf] == qh) + if (rcu_dereference_protected( + queue_handler[pf], + lockdep_is_held(&queue_handler_mutex) + ) == qh) rcu_assign_pointer(queue_handler[pf], NULL); } mutex_unlock(&queue_handler_mutex); diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 6a1572b0ab41..91592da504b9 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -874,19 +874,19 @@ static struct hlist_node *get_first(struct iter_state *st) for (st->bucket = 0; st->bucket < INSTANCE_BUCKETS; st->bucket++) { if (!hlist_empty(&instance_table[st->bucket])) - return rcu_dereference_bh(instance_table[st->bucket].first); + return rcu_dereference_bh(hlist_first_rcu(&instance_table[st->bucket])); } return NULL; } static struct hlist_node *get_next(struct iter_state *st, struct hlist_node *h) { - h = rcu_dereference_bh(h->next); + h = rcu_dereference_bh(hlist_next_rcu(h)); while (!h) { if (++st->bucket >= INSTANCE_BUCKETS) return NULL; - h = rcu_dereference_bh(instance_table[st->bucket].first); + h = rcu_dereference_bh(hlist_first_rcu(&instance_table[st->bucket])); } return h; } -- cgit v1.2.3 From e9e5eee8733739f13a204132b502494b3f494f3b Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Mon, 8 Nov 2010 20:05:57 +0900 Subject: IPVS: Add persistence engine to connection entry The dest of a connection may not exist if it has been created as the result of connection synchronisation. But in order for connection entries for templates with persistence engine data created through connection synchronisation to be valid access to the persistence engine pointer is required. So add the persistence engine to the connection itself. Signed-off-by: Simon Horman --- include/net/ip_vs.h | 16 ++++++++++++++-- net/netfilter/ipvs/ip_vs_conn.c | 19 ++++++++++--------- net/netfilter/ipvs/ip_vs_ctl.c | 4 ++-- net/netfilter/ipvs/ip_vs_pe.c | 14 ++++---------- 4 files changed, 30 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index b7bbd6c28cfa..be2b5690f892 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -422,6 +422,7 @@ struct ip_vs_conn { struct ip_vs_seq in_seq; /* incoming seq. struct */ struct ip_vs_seq out_seq; /* outgoing seq. struct */ + const struct ip_vs_pe *pe; char *pe_data; __u8 pe_data_len; }; @@ -814,8 +815,19 @@ void ip_vs_bind_pe(struct ip_vs_service *svc, struct ip_vs_pe *pe); void ip_vs_unbind_pe(struct ip_vs_service *svc); int register_ip_vs_pe(struct ip_vs_pe *pe); int unregister_ip_vs_pe(struct ip_vs_pe *pe); -extern struct ip_vs_pe *ip_vs_pe_get(const char *name); -extern void ip_vs_pe_put(struct ip_vs_pe *pe); +struct ip_vs_pe *ip_vs_pe_getbyname(const char *name); + +static inline void ip_vs_pe_get(const struct ip_vs_pe *pe) +{ + if (pe && pe->module) + __module_get(pe->module); +} + +static inline void ip_vs_pe_put(const struct ip_vs_pe *pe) +{ + if (pe && pe->module) + module_put(pe->module); +} /* * IPVS protocol functions (from ip_vs_proto.c) diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index e9adecdc8ca4..64a9ca314100 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -176,8 +176,8 @@ static unsigned int ip_vs_conn_hashkey_conn(const struct ip_vs_conn *cp) ip_vs_conn_fill_param(cp->af, cp->protocol, &cp->caddr, cp->cport, NULL, 0, &p); - if (cp->dest && cp->dest->svc->pe) { - p.pe = cp->dest->svc->pe; + if (cp->pe) { + p.pe = cp->pe; p.pe_data = cp->pe_data; p.pe_data_len = cp->pe_data_len; } @@ -765,6 +765,7 @@ static void ip_vs_conn_expire(unsigned long data) if (cp->flags & IP_VS_CONN_F_NFCT) ip_vs_conn_drop_conntrack(cp); + ip_vs_pe_put(cp->pe); kfree(cp->pe_data); if (unlikely(cp->app != NULL)) ip_vs_unbind_app(cp); @@ -826,7 +827,9 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, &cp->daddr, daddr); cp->dport = dport; cp->flags = flags; - if (flags & IP_VS_CONN_F_TEMPLATE && p->pe_data) { + if (flags & IP_VS_CONN_F_TEMPLATE && p->pe) { + ip_vs_pe_get(p->pe); + cp->pe = p->pe; cp->pe_data = p->pe_data; cp->pe_data_len = p->pe_data_len; } @@ -958,15 +961,13 @@ static int ip_vs_conn_seq_show(struct seq_file *seq, void *v) char pe_data[IP_VS_PENAME_MAXLEN + IP_VS_PEDATA_MAXLEN + 3]; size_t len = 0; - if (cp->dest && cp->pe_data && - cp->dest->svc->pe->show_pe_data) { + if (cp->pe_data) { pe_data[0] = ' '; - len = strlen(cp->dest->svc->pe->name); - memcpy(pe_data + 1, cp->dest->svc->pe->name, len); + len = strlen(cp->pe->name); + memcpy(pe_data + 1, cp->pe->name, len); pe_data[len + 1] = ' '; len += 2; - len += cp->dest->svc->pe->show_pe_data(cp, - pe_data + len); + len += cp->pe->show_pe_data(cp, pe_data + len); } pe_data[len] = '\0'; diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 5f5daa30b0af..3e92558dfcc2 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1139,7 +1139,7 @@ ip_vs_add_service(struct ip_vs_service_user_kern *u, } if (u->pe_name && *u->pe_name) { - pe = ip_vs_pe_get(u->pe_name); + pe = ip_vs_pe_getbyname(u->pe_name); if (pe == NULL) { pr_info("persistence engine module ip_vs_pe_%s " "not found\n", u->pe_name); @@ -1250,7 +1250,7 @@ ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user_kern *u) old_sched = sched; if (u->pe_name && *u->pe_name) { - pe = ip_vs_pe_get(u->pe_name); + pe = ip_vs_pe_getbyname(u->pe_name); if (pe == NULL) { pr_info("persistence engine module ip_vs_pe_%s " "not found\n", u->pe_name); diff --git a/net/netfilter/ipvs/ip_vs_pe.c b/net/netfilter/ipvs/ip_vs_pe.c index 3414af70ee12..e99f920b93d1 100644 --- a/net/netfilter/ipvs/ip_vs_pe.c +++ b/net/netfilter/ipvs/ip_vs_pe.c @@ -30,7 +30,7 @@ void ip_vs_unbind_pe(struct ip_vs_service *svc) /* Get pe in the pe list by name */ static struct ip_vs_pe * -ip_vs_pe_getbyname(const char *pe_name) +__ip_vs_pe_getbyname(const char *pe_name) { struct ip_vs_pe *pe; @@ -60,28 +60,22 @@ ip_vs_pe_getbyname(const char *pe_name) } /* Lookup pe and try to load it if it doesn't exist */ -struct ip_vs_pe *ip_vs_pe_get(const char *name) +struct ip_vs_pe *ip_vs_pe_getbyname(const char *name) { struct ip_vs_pe *pe; /* Search for the pe by name */ - pe = ip_vs_pe_getbyname(name); + pe = __ip_vs_pe_getbyname(name); /* If pe not found, load the module and search again */ if (!pe) { request_module("ip_vs_pe_%s", name); - pe = ip_vs_pe_getbyname(name); + pe = __ip_vs_pe_getbyname(name); } return pe; } -void ip_vs_pe_put(struct ip_vs_pe *pe) -{ - if (pe && pe->module) - module_put(pe->module); -} - /* Register a pe in the pe list */ int register_ip_vs_pe(struct ip_vs_pe *pe) { -- cgit v1.2.3 From d494262b8a0f3507b62104a565849124abe29827 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Tue, 9 Nov 2010 09:33:15 +0900 Subject: IPVS: Make the cp argument to ip_vs_sync_conn() static Acked-by: Hans Schillstrom Signed-off-by: Simon Horman --- include/net/ip_vs.h | 2 +- net/netfilter/ipvs/ip_vs_sync.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index be2b5690f892..d5a32e47f9d9 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -916,7 +916,7 @@ extern char ip_vs_master_mcast_ifn[IP_VS_IFNAME_MAXLEN]; extern char ip_vs_backup_mcast_ifn[IP_VS_IFNAME_MAXLEN]; extern int start_sync_thread(int state, char *mcast_ifn, __u8 syncid); extern int stop_sync_thread(int state); -extern void ip_vs_sync_conn(struct ip_vs_conn *cp); +extern void ip_vs_sync_conn(const struct ip_vs_conn *cp); /* diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index ab85aedea17e..a4dccbc4f1bb 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -236,7 +236,7 @@ get_curr_sync_buff(unsigned long time) * Add an ip_vs_conn information into the current sync_buff. * Called by ip_vs_in. */ -void ip_vs_sync_conn(struct ip_vs_conn *cp) +void ip_vs_sync_conn(const struct ip_vs_conn *cp) { struct ip_vs_sync_mesg *m; struct ip_vs_sync_conn *s; -- cgit v1.2.3 From 0e051e683ba4acb4e67c272c6a89707d974099d1 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Fri, 19 Nov 2010 14:25:07 +0100 Subject: IPVS: Backup, Prepare for transferring firewall marks (fwmark) to the backup daemon. One struct will have fwmark added: * ip_vs_conn ip_vs_conn_new() and ip_vs_find_dest() will have an extra param - fwmark The effects of that, is in this patch. Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 6 ++++-- net/netfilter/ipvs/ip_vs_conn.c | 5 +++-- net/netfilter/ipvs/ip_vs_core.c | 8 ++++---- net/netfilter/ipvs/ip_vs_ctl.c | 4 ++-- net/netfilter/ipvs/ip_vs_ftp.c | 5 +++-- net/netfilter/ipvs/ip_vs_sync.c | 4 ++-- 6 files changed, 18 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index d5a32e47f9d9..890f01c215e9 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -382,6 +382,7 @@ struct ip_vs_conn { union nf_inet_addr vaddr; /* virtual address */ union nf_inet_addr daddr; /* destination address */ volatile __u32 flags; /* status flags */ + __u32 fwmark; /* Fire wall mark from skb */ __be16 cport; __be16 vport; __be16 dport; @@ -720,7 +721,7 @@ extern void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport); struct ip_vs_conn *ip_vs_conn_new(const struct ip_vs_conn_param *p, const union nf_inet_addr *daddr, __be16 dport, unsigned flags, - struct ip_vs_dest *dest); + struct ip_vs_dest *dest, __u32 fwmark); extern void ip_vs_conn_expire_now(struct ip_vs_conn *cp); extern const char * ip_vs_state_name(__u16 proto, int state); @@ -901,7 +902,8 @@ extern int ip_vs_control_init(void); extern void ip_vs_control_cleanup(void); extern struct ip_vs_dest * ip_vs_find_dest(int af, const union nf_inet_addr *daddr, __be16 dport, - const union nf_inet_addr *vaddr, __be16 vport, __u16 protocol); + const union nf_inet_addr *vaddr, __be16 vport, __u16 protocol, + __u32 fwmark); extern struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp); diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 7615f9e3d955..66e4662925d5 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -613,7 +613,7 @@ struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp) if ((cp) && (!cp->dest)) { dest = ip_vs_find_dest(cp->af, &cp->daddr, cp->dport, &cp->vaddr, cp->vport, - cp->protocol); + cp->protocol, cp->fwmark); ip_vs_bind_dest(cp, dest); return dest; } else @@ -803,7 +803,7 @@ void ip_vs_conn_expire_now(struct ip_vs_conn *cp) struct ip_vs_conn * ip_vs_conn_new(const struct ip_vs_conn_param *p, const union nf_inet_addr *daddr, __be16 dport, unsigned flags, - struct ip_vs_dest *dest) + struct ip_vs_dest *dest, __u32 fwmark) { struct ip_vs_conn *cp; struct ip_vs_protocol *pp = ip_vs_proto_get(p->protocol); @@ -827,6 +827,7 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, &cp->daddr, daddr); cp->dport = dport; cp->flags = flags; + cp->fwmark = fwmark; if (flags & IP_VS_CONN_F_TEMPLATE && p->pe) { ip_vs_pe_get(p->pe); cp->pe = p->pe; diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index b4e51e9c5a04..e2bb3cd41c07 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -293,7 +293,7 @@ ip_vs_sched_persist(struct ip_vs_service *svc, * and thus param.pe_data will be destroyed * when the template expires */ ct = ip_vs_conn_new(¶m, &dest->addr, dport, - IP_VS_CONN_F_TEMPLATE, dest); + IP_VS_CONN_F_TEMPLATE, dest, skb->mark); if (ct == NULL) { kfree(param.pe_data); return NULL; @@ -319,7 +319,7 @@ ip_vs_sched_persist(struct ip_vs_service *svc, */ ip_vs_conn_fill_param(svc->af, iph.protocol, &iph.saddr, ports[0], &iph.daddr, ports[1], ¶m); - cp = ip_vs_conn_new(¶m, &dest->addr, dport, flags, dest); + cp = ip_vs_conn_new(¶m, &dest->addr, dport, flags, dest, skb->mark); if (cp == NULL) { ip_vs_conn_put(ct); return NULL; @@ -423,7 +423,7 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb, pptr[0], &iph.daddr, pptr[1], &p); cp = ip_vs_conn_new(&p, &dest->addr, dest->port ? dest->port : pptr[1], - flags, dest); + flags, dest, skb->mark); if (!cp) return NULL; } @@ -489,7 +489,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, &iph.daddr, pptr[1], &p); cp = ip_vs_conn_new(&p, &daddr, 0, IP_VS_CONN_F_BYPASS | flags, - NULL); + NULL, skb->mark); if (!cp) return NF_DROP; } diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 3e92558dfcc2..a5bd00279047 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -657,12 +657,12 @@ ip_vs_lookup_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr, struct ip_vs_dest *ip_vs_find_dest(int af, const union nf_inet_addr *daddr, __be16 dport, const union nf_inet_addr *vaddr, - __be16 vport, __u16 protocol) + __be16 vport, __u16 protocol, __u32 fwmark) { struct ip_vs_dest *dest; struct ip_vs_service *svc; - svc = ip_vs_service_get(af, 0, protocol, vaddr, vport); + svc = ip_vs_service_get(af, fwmark, protocol, vaddr, vport); if (!svc) return NULL; dest = ip_vs_lookup_dest(svc, daddr, dport); diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index 75455000ad1c..84aef65b37d1 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -208,7 +208,7 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, n_cp = ip_vs_conn_new(&p, &from, port, IP_VS_CONN_F_NO_CPORT | IP_VS_CONN_F_NFCT, - cp->dest); + cp->dest, skb->mark); if (!n_cp) return 0; @@ -365,7 +365,8 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, if (!n_cp) { n_cp = ip_vs_conn_new(&p, &cp->daddr, htons(ntohs(cp->dport)-1), - IP_VS_CONN_F_NFCT, cp->dest); + IP_VS_CONN_F_NFCT, cp->dest, + skb->mark); if (!n_cp) return 0; diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 3897d6bf3b29..47eed672dc08 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -404,7 +404,7 @@ static void ip_vs_process_message(char *buffer, const size_t buflen) s->dport, (union nf_inet_addr *)&s->vaddr, s->vport, - s->protocol); + s->protocol, 0); /* Set the approprite ativity flag */ if (s->protocol == IPPROTO_TCP) { if (state != IP_VS_TCP_S_ESTABLISHED) @@ -419,7 +419,7 @@ static void ip_vs_process_message(char *buffer, const size_t buflen) } cp = ip_vs_conn_new(¶m, (union nf_inet_addr *)&s->daddr, - s->dport, flags, dest); + s->dport, flags, dest, 0); if (dest) atomic_dec(&dest->refcnt); if (!cp) { -- cgit v1.2.3 From fe5e7a1efb664df0280f10377813d7099fb7eb0f Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Fri, 19 Nov 2010 14:25:12 +0100 Subject: IPVS: Backup, Adding Version 1 receive capability Functionality improvements * flags changed from 16 to 32 bits * fwmark added (32 bits) * timeout in sec. added (32 bits) * pe data added (Variable length) * IPv6 capabilities (3x16 bytes for addr.) * Version and type in every conn msg. ip_vs_process_message() now handles Version 1 messages and will call ip_vs_process_message_v0() for version 0 messages. ip_vs_proc_conn() is common for both version, and handles the update of connection hash. ip_vs_conn_fill_param_sync() - Version 1 messages only ip_vs_conn_fill_param_sync_v0() - Version 0 messages only Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/linux/ip_vs.h | 8 + include/net/ip_vs.h | 1 + net/netfilter/ipvs/ip_vs_pe.c | 5 +- net/netfilter/ipvs/ip_vs_sync.c | 549 +++++++++++++++++++++++++++++++--------- 4 files changed, 440 insertions(+), 123 deletions(-) (limited to 'include') diff --git a/include/linux/ip_vs.h b/include/linux/ip_vs.h index 5f43a3b2e3ad..4deb3834d62c 100644 --- a/include/linux/ip_vs.h +++ b/include/linux/ip_vs.h @@ -89,6 +89,14 @@ #define IP_VS_CONN_F_TEMPLATE 0x1000 /* template, not connection */ #define IP_VS_CONN_F_ONE_PACKET 0x2000 /* forward only one packet */ +#define IP_VS_CONN_F_BACKUP_MASK (IP_VS_CONN_F_FWD_MASK | \ + IP_VS_CONN_F_NOOUTPUT | \ + IP_VS_CONN_F_INACTIVE | \ + IP_VS_CONN_F_SEQ_MASK | \ + IP_VS_CONN_F_NO_CPORT | \ + IP_VS_CONN_F_TEMPLATE \ + ) + /* Flags that are not sent to backup server start from bit 16 */ #define IP_VS_CONN_F_NFCT (1 << 16) /* use netfilter conntrack */ diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 890f01c215e9..4069484df7bb 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -817,6 +817,7 @@ void ip_vs_unbind_pe(struct ip_vs_service *svc); int register_ip_vs_pe(struct ip_vs_pe *pe); int unregister_ip_vs_pe(struct ip_vs_pe *pe); struct ip_vs_pe *ip_vs_pe_getbyname(const char *name); +struct ip_vs_pe *__ip_vs_pe_getbyname(const char *pe_name); static inline void ip_vs_pe_get(const struct ip_vs_pe *pe) { diff --git a/net/netfilter/ipvs/ip_vs_pe.c b/net/netfilter/ipvs/ip_vs_pe.c index e99f920b93d1..5cf859ccb31b 100644 --- a/net/netfilter/ipvs/ip_vs_pe.c +++ b/net/netfilter/ipvs/ip_vs_pe.c @@ -29,12 +29,11 @@ void ip_vs_unbind_pe(struct ip_vs_service *svc) } /* Get pe in the pe list by name */ -static struct ip_vs_pe * -__ip_vs_pe_getbyname(const char *pe_name) +struct ip_vs_pe *__ip_vs_pe_getbyname(const char *pe_name) { struct ip_vs_pe *pe; - IP_VS_DBG(2, "%s(): pe_name \"%s\"\n", __func__, + IP_VS_DBG(10, "%s(): pe_name \"%s\"\n", __func__, pe_name); spin_lock_bh(&ip_vs_pe_lock); diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 566482f227fa..e071508901d1 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -35,6 +35,8 @@ #include #include +#include /* Used for ntoh_seq and hton_seq */ + #include #include @@ -286,6 +288,16 @@ static struct sockaddr_in mcast_addr = { .sin_addr.s_addr = cpu_to_be32(IP_VS_SYNC_GROUP), }; +/* + * Copy of struct ip_vs_seq + * From unaligned network order to aligned host order + */ +static void ntoh_seq(struct ip_vs_seq *no, struct ip_vs_seq *ho) +{ + ho->init_seq = get_unaligned_be32(&no->init_seq); + ho->delta = get_unaligned_be32(&no->delta); + ho->previous_delta = get_unaligned_be32(&no->previous_delta); +} static inline struct ip_vs_sync_buff *sb_dequeue(void) { @@ -418,59 +430,186 @@ void ip_vs_sync_conn(const struct ip_vs_conn *cp) ip_vs_sync_conn(cp->control); } +/* + * fill_param used by version 1 + */ static inline int -ip_vs_conn_fill_param_sync(int af, int protocol, - const union nf_inet_addr *caddr, __be16 cport, - const union nf_inet_addr *vaddr, __be16 vport, - struct ip_vs_conn_param *p) +ip_vs_conn_fill_param_sync(int af, union ip_vs_sync_conn *sc, + struct ip_vs_conn_param *p, + __u8 *pe_data, unsigned int pe_data_len, + __u8 *pe_name, unsigned int pe_name_len) { - /* XXX: Need to take into account persistence engine */ - ip_vs_conn_fill_param(af, protocol, caddr, cport, vaddr, vport, p); +#ifdef CONFIG_IP_VS_IPV6 + if (af == AF_INET6) + ip_vs_conn_fill_param(af, sc->v6.protocol, + (const union nf_inet_addr *)&sc->v6.caddr, + sc->v6.cport, + (const union nf_inet_addr *)&sc->v6.vaddr, + sc->v6.vport, p); + else +#endif + ip_vs_conn_fill_param(af, sc->v4.protocol, + (const union nf_inet_addr *)&sc->v4.caddr, + sc->v4.cport, + (const union nf_inet_addr *)&sc->v4.vaddr, + sc->v4.vport, p); + /* Handle pe data */ + if (pe_data_len) { + if (pe_name_len) { + char buff[IP_VS_PENAME_MAXLEN+1]; + + memcpy(buff, pe_name, pe_name_len); + buff[pe_name_len]=0; + p->pe = __ip_vs_pe_getbyname(buff); + if (!p->pe) { + IP_VS_DBG(3, "BACKUP, no %s engine found/loaded\n", buff); + return 1; + } + } else { + IP_VS_ERR_RL("BACKUP, Invalid PE parameters\n"); + return 1; + } + + p->pe_data = kmalloc(pe_data_len, GFP_ATOMIC); + if (!p->pe_data) { + if (p->pe->module) + module_put(p->pe->module); + return -ENOMEM; + } + memcpy(p->pe_data, pe_data, pe_data_len); + p->pe_data_len = pe_data_len; + } return 0; } /* - * Process received multicast message and create the corresponding - * ip_vs_conn entries. + * Connection Add / Update. + * Common for version 0 and 1 reception of backup sync_conns. + * Param: ... + * timeout is in sec. + */ +static void ip_vs_proc_conn(struct ip_vs_conn_param *param, unsigned flags, + unsigned state, unsigned protocol, unsigned type, + const union nf_inet_addr *daddr, __be16 dport, + unsigned long timeout, __u32 fwmark, + struct ip_vs_sync_conn_options *opt, + struct ip_vs_protocol *pp) +{ + struct ip_vs_dest *dest; + struct ip_vs_conn *cp; + + + if (!(flags & IP_VS_CONN_F_TEMPLATE)) + cp = ip_vs_conn_in_get(param); + else + cp = ip_vs_ct_in_get(param); + + if (cp && param->pe_data) /* Free pe_data */ + kfree(param->pe_data); + if (!cp) { + /* + * Find the appropriate destination for the connection. + * If it is not found the connection will remain unbound + * but still handled. + */ + dest = ip_vs_find_dest(type, daddr, dport, param->vaddr, + param->vport, protocol, fwmark); + + /* Set the approprite ativity flag */ + if (protocol == IPPROTO_TCP) { + if (state != IP_VS_TCP_S_ESTABLISHED) + flags |= IP_VS_CONN_F_INACTIVE; + else + flags &= ~IP_VS_CONN_F_INACTIVE; + } else if (protocol == IPPROTO_SCTP) { + if (state != IP_VS_SCTP_S_ESTABLISHED) + flags |= IP_VS_CONN_F_INACTIVE; + else + flags &= ~IP_VS_CONN_F_INACTIVE; + } + cp = ip_vs_conn_new(param, daddr, dport, flags, dest, fwmark); + if (dest) + atomic_dec(&dest->refcnt); + if (!cp) { + if (param->pe_data) + kfree(param->pe_data); + IP_VS_DBG(2, "BACKUP, add new conn. failed\n"); + return; + } + } else if (!cp->dest) { + dest = ip_vs_try_bind_dest(cp); + if (dest) + atomic_dec(&dest->refcnt); + } else if ((cp->dest) && (cp->protocol == IPPROTO_TCP) && + (cp->state != state)) { + /* update active/inactive flag for the connection */ + dest = cp->dest; + if (!(cp->flags & IP_VS_CONN_F_INACTIVE) && + (state != IP_VS_TCP_S_ESTABLISHED)) { + atomic_dec(&dest->activeconns); + atomic_inc(&dest->inactconns); + cp->flags |= IP_VS_CONN_F_INACTIVE; + } else if ((cp->flags & IP_VS_CONN_F_INACTIVE) && + (state == IP_VS_TCP_S_ESTABLISHED)) { + atomic_inc(&dest->activeconns); + atomic_dec(&dest->inactconns); + cp->flags &= ~IP_VS_CONN_F_INACTIVE; + } + } else if ((cp->dest) && (cp->protocol == IPPROTO_SCTP) && + (cp->state != state)) { + dest = cp->dest; + if (!(cp->flags & IP_VS_CONN_F_INACTIVE) && + (state != IP_VS_SCTP_S_ESTABLISHED)) { + atomic_dec(&dest->activeconns); + atomic_inc(&dest->inactconns); + cp->flags &= ~IP_VS_CONN_F_INACTIVE; + } + } + + if (opt) + memcpy(&cp->in_seq, opt, sizeof(*opt)); + atomic_set(&cp->in_pkts, sysctl_ip_vs_sync_threshold[0]); + cp->state = state; + cp->old_state = cp->state; + /* + * For Ver 0 messages style + * - Not possible to recover the right timeout for templates + * - can not find the right fwmark + * virtual service. If needed, we can do it for + * non-fwmark persistent services. + * Ver 1 messages style. + * - No problem. + */ + if (timeout) { + if (timeout > MAX_SCHEDULE_TIMEOUT / HZ) + timeout = MAX_SCHEDULE_TIMEOUT / HZ; + cp->timeout = timeout*HZ; + } else if (!(flags & IP_VS_CONN_F_TEMPLATE) && pp->timeout_table) + cp->timeout = pp->timeout_table[state]; + else + cp->timeout = (3*60*HZ); + ip_vs_conn_put(cp); +} + +/* + * Process received multicast message for Version 0 */ -static void ip_vs_process_message(char *buffer, const size_t buflen) +static void ip_vs_process_message_v0(const char *buffer, const size_t buflen) { struct ip_vs_sync_mesg *m = (struct ip_vs_sync_mesg *)buffer; struct ip_vs_sync_conn_v0 *s; struct ip_vs_sync_conn_options *opt; - struct ip_vs_conn *cp; struct ip_vs_protocol *pp; - struct ip_vs_dest *dest; struct ip_vs_conn_param param; char *p; int i; - if (buflen < sizeof(struct ip_vs_sync_mesg)) { - IP_VS_ERR_RL("sync message header too short\n"); - return; - } - - /* Convert size back to host byte order */ - m->size = ntohs(m->size); - - if (buflen != m->size) { - IP_VS_ERR_RL("bogus sync message size\n"); - return; - } - - /* SyncID sanity check */ - if (ip_vs_backup_syncid != 0 && m->syncid != ip_vs_backup_syncid) { - IP_VS_DBG(7, "Ignoring incoming msg with syncid = %d\n", - m->syncid); - return; - } - p = (char *)buffer + sizeof(struct ip_vs_sync_mesg); for (i=0; inr_conns; i++) { unsigned flags, state; if (p + SIMPLE_CONN_SIZE > buffer+buflen) { - IP_VS_ERR_RL("bogus conn in sync message\n"); + IP_VS_ERR_RL("BACKUP v0, bogus conn\n"); return; } s = (struct ip_vs_sync_conn_v0 *) p; @@ -480,7 +619,7 @@ static void ip_vs_process_message(char *buffer, const size_t buflen) opt = (struct ip_vs_sync_conn_options *)&s[1]; p += FULL_CONN_SIZE; if (p > buffer+buflen) { - IP_VS_ERR_RL("bogus conn options in sync message\n"); + IP_VS_ERR_RL("BACKUP v0, Dropping buffer bogus conn options\n"); return; } } else { @@ -492,12 +631,12 @@ static void ip_vs_process_message(char *buffer, const size_t buflen) if (!(flags & IP_VS_CONN_F_TEMPLATE)) { pp = ip_vs_proto_get(s->protocol); if (!pp) { - IP_VS_ERR_RL("Unsupported protocol %u in sync msg\n", + IP_VS_DBG(2, "BACKUP v0, Unsupported protocol %u\n", s->protocol); continue; } if (state >= pp->num_states) { - IP_VS_DBG(2, "Invalid %s state %u in sync msg\n", + IP_VS_DBG(2, "BACKUP v0, Invalid %s state %u\n", pp->name, state); continue; } @@ -505,103 +644,273 @@ static void ip_vs_process_message(char *buffer, const size_t buflen) /* protocol in templates is not used for state/timeout */ pp = NULL; if (state > 0) { - IP_VS_DBG(2, "Invalid template state %u in sync msg\n", + IP_VS_DBG(2, "BACKUP v0, Invalid template state %u\n", state); state = 0; } } - if (ip_vs_conn_fill_param_sync(AF_INET, s->protocol, - (union nf_inet_addr *)&s->caddr, - s->cport, - (union nf_inet_addr *)&s->vaddr, - s->vport, ¶m)) { - pr_err("ip_vs_conn_fill_param_sync failed"); - return; + ip_vs_conn_fill_param(AF_INET, s->protocol, + (const union nf_inet_addr *)&s->caddr, + s->cport, + (const union nf_inet_addr *)&s->vaddr, + s->vport, ¶m); + + /* Send timeout as Zero */ + ip_vs_proc_conn(¶m, flags, state, s->protocol, AF_INET, + (union nf_inet_addr *)&s->daddr, s->dport, + 0, 0, opt, pp); + } +} + +/* + * Handle options + */ +static inline int ip_vs_proc_seqopt(__u8 *p, unsigned int plen, + __u32 *opt_flags, + struct ip_vs_sync_conn_options *opt) +{ + struct ip_vs_sync_conn_options *topt; + + topt = (struct ip_vs_sync_conn_options *)p; + + if (plen != sizeof(struct ip_vs_sync_conn_options)) { + IP_VS_DBG(2, "BACKUP, bogus conn options length\n"); + return -EINVAL; + } + if (*opt_flags & IPVS_OPT_F_SEQ_DATA) { + IP_VS_DBG(2, "BACKUP, conn options found twice\n"); + return -EINVAL; + } + ntoh_seq(&topt->in_seq, &opt->in_seq); + ntoh_seq(&topt->out_seq, &opt->out_seq); + *opt_flags |= IPVS_OPT_F_SEQ_DATA; + return 0; +} + +static int ip_vs_proc_str(__u8 *p, unsigned int plen, unsigned int *data_len, + __u8 **data, unsigned int maxlen, + __u32 *opt_flags, __u32 flag) +{ + if (plen > maxlen) { + IP_VS_DBG(2, "BACKUP, bogus par.data len > %d\n", maxlen); + return -EINVAL; + } + if (*opt_flags & flag) { + IP_VS_DBG(2, "BACKUP, Par.data found twice 0x%x\n", flag); + return -EINVAL; + } + *data_len = plen; + *data = p; + *opt_flags |= flag; + return 0; +} +/* + * Process a Version 1 sync. connection + */ +static inline int ip_vs_proc_sync_conn(__u8 *p, __u8 *msg_end) +{ + struct ip_vs_sync_conn_options opt; + union ip_vs_sync_conn *s; + struct ip_vs_protocol *pp; + struct ip_vs_conn_param param; + __u32 flags; + unsigned int af, state, pe_data_len=0, pe_name_len=0; + __u8 *pe_data=NULL, *pe_name=NULL; + __u32 opt_flags=0; + int retc=0; + + s = (union ip_vs_sync_conn *) p; + + if (s->v6.type & STYPE_F_INET6) { +#ifdef CONFIG_IP_VS_IPV6 + af = AF_INET6; + p += sizeof(struct ip_vs_sync_v6); +#else + IP_VS_DBG(3,"BACKUP, IPv6 msg received, and IPVS is not compiled for IPv6\n"); + retc = 10; + goto out; +#endif + } else if (!s->v4.type) { + af = AF_INET; + p += sizeof(struct ip_vs_sync_v4); + } else { + return -10; + } + if (p > msg_end) + return -20; + + /* Process optional params check Type & Len. */ + while (p < msg_end) { + int ptype; + int plen; + + if (p+2 > msg_end) + return -30; + ptype = *(p++); + plen = *(p++); + + if (!plen || ((p + plen) > msg_end)) + return -40; + /* Handle seq option p = param data */ + switch (ptype & ~IPVS_OPT_F_PARAM) { + case IPVS_OPT_SEQ_DATA: + if (ip_vs_proc_seqopt(p, plen, &opt_flags, &opt)) + return -50; + break; + + case IPVS_OPT_PE_DATA: + if (ip_vs_proc_str(p, plen, &pe_data_len, &pe_data, + IP_VS_PEDATA_MAXLEN, &opt_flags, + IPVS_OPT_F_PE_DATA)) + return -60; + break; + + case IPVS_OPT_PE_NAME: + if (ip_vs_proc_str(p, plen,&pe_name_len, &pe_name, + IP_VS_PENAME_MAXLEN, &opt_flags, + IPVS_OPT_F_PE_NAME)) + return -70; + break; + + default: + /* Param data mandatory ? */ + if (!(ptype & IPVS_OPT_F_PARAM)) { + IP_VS_DBG(3, "BACKUP, Unknown mandatory param %d found\n", + ptype & ~IPVS_OPT_F_PARAM); + retc = 20; + goto out; + } } - if (!(flags & IP_VS_CONN_F_TEMPLATE)) - cp = ip_vs_conn_in_get(¶m); - else - cp = ip_vs_ct_in_get(¶m); - if (!cp) { - /* - * Find the appropriate destination for the connection. - * If it is not found the connection will remain unbound - * but still handled. - */ - dest = ip_vs_find_dest(AF_INET, - (union nf_inet_addr *)&s->daddr, - s->dport, - (union nf_inet_addr *)&s->vaddr, - s->vport, - s->protocol, 0); - /* Set the approprite ativity flag */ - if (s->protocol == IPPROTO_TCP) { - if (state != IP_VS_TCP_S_ESTABLISHED) - flags |= IP_VS_CONN_F_INACTIVE; - else - flags &= ~IP_VS_CONN_F_INACTIVE; - } else if (s->protocol == IPPROTO_SCTP) { - if (state != IP_VS_SCTP_S_ESTABLISHED) - flags |= IP_VS_CONN_F_INACTIVE; - else - flags &= ~IP_VS_CONN_F_INACTIVE; + p += plen; /* Next option */ + } + + /* Get flags and Mask off unsupported */ + flags = ntohl(s->v4.flags) & IP_VS_CONN_F_BACKUP_MASK; + flags |= IP_VS_CONN_F_SYNC; + state = ntohs(s->v4.state); + + if (!(flags & IP_VS_CONN_F_TEMPLATE)) { + pp = ip_vs_proto_get(s->v4.protocol); + if (!pp) { + IP_VS_DBG(3,"BACKUP, Unsupported protocol %u\n", + s->v4.protocol); + retc = 30; + goto out; + } + if (state >= pp->num_states) { + IP_VS_DBG(3, "BACKUP, Invalid %s state %u\n", + pp->name, state); + retc = 40; + goto out; + } + } else { + /* protocol in templates is not used for state/timeout */ + pp = NULL; + if (state > 0) { + IP_VS_DBG(3, "BACKUP, Invalid template state %u\n", + state); + state = 0; + } + } + if (ip_vs_conn_fill_param_sync(af, s, ¶m, + pe_data, pe_data_len, + pe_name, pe_name_len)) { + retc = 50; + goto out; + } + /* If only IPv4, just silent skip IPv6 */ + if (af == AF_INET) + ip_vs_proc_conn(¶m, flags, state, s->v4.protocol, af, + (union nf_inet_addr *)&s->v4.daddr, s->v4.dport, + ntohl(s->v4.timeout), ntohl(s->v4.fwmark), + (opt_flags & IPVS_OPT_F_SEQ_DATA ? &opt : NULL), + pp); +#ifdef CONFIG_IP_VS_IPV6 + else + ip_vs_proc_conn(¶m, flags, state, s->v6.protocol, af, + (union nf_inet_addr *)&s->v6.daddr, s->v6.dport, + ntohl(s->v6.timeout), ntohl(s->v6.fwmark), + (opt_flags & IPVS_OPT_F_SEQ_DATA ? &opt : NULL), + pp); +#endif + return 0; + /* Error exit */ +out: + IP_VS_DBG(2, "BACKUP, Single msg dropped err:%d\n", retc); + return retc; + +} +/* + * Process received multicast message and create the corresponding + * ip_vs_conn entries. + * Handles Version 0 & 1 + */ +static void ip_vs_process_message(__u8 *buffer, const size_t buflen) +{ + struct ip_vs_sync_mesg_v2 *m2 = (struct ip_vs_sync_mesg_v2 *)buffer; + __u8 *p, *msg_end; + unsigned int i, nr_conns; + + if (buflen < sizeof(struct ip_vs_sync_mesg)) { + IP_VS_DBG(2, "BACKUP, message header too short\n"); + return; + } + /* Convert size back to host byte order */ + m2->size = ntohs(m2->size); + + if (buflen != m2->size) { + IP_VS_DBG(2, "BACKUP, bogus message size\n"); + return; + } + /* SyncID sanity check */ + if (ip_vs_backup_syncid != 0 && m2->syncid != ip_vs_backup_syncid) { + IP_VS_DBG(7, "BACKUP, Ignoring syncid = %d\n", m2->syncid); + return; + } + /* Handle version 1 message */ + if ((m2->version == SYNC_PROTO_VER) && (m2->reserved == 0) + && (m2->spare == 0)) { + + msg_end = buffer + sizeof(struct ip_vs_sync_mesg_v2); + nr_conns = m2->nr_conns; + + for (i=0; iv4) > buffer+buflen) { + IP_VS_ERR_RL("BACKUP, Dropping buffer, to small\n"); + return; } - cp = ip_vs_conn_new(¶m, - (union nf_inet_addr *)&s->daddr, - s->dport, flags, dest, 0); - if (dest) - atomic_dec(&dest->refcnt); - if (!cp) { - pr_err("ip_vs_conn_new failed\n"); + s = (union ip_vs_sync_conn *)p; + size = ntohs(s->v4.ver_size) & SVER_MASK; + msg_end = p + size; + /* Basic sanity checks */ + if (msg_end > buffer+buflen) { + IP_VS_ERR_RL("BACKUP, Dropping buffer, msg > buffer\n"); return; } - } else if (!cp->dest) { - dest = ip_vs_try_bind_dest(cp); - if (dest) - atomic_dec(&dest->refcnt); - } else if ((cp->dest) && (cp->protocol == IPPROTO_TCP) && - (cp->state != state)) { - /* update active/inactive flag for the connection */ - dest = cp->dest; - if (!(cp->flags & IP_VS_CONN_F_INACTIVE) && - (state != IP_VS_TCP_S_ESTABLISHED)) { - atomic_dec(&dest->activeconns); - atomic_inc(&dest->inactconns); - cp->flags |= IP_VS_CONN_F_INACTIVE; - } else if ((cp->flags & IP_VS_CONN_F_INACTIVE) && - (state == IP_VS_TCP_S_ESTABLISHED)) { - atomic_inc(&dest->activeconns); - atomic_dec(&dest->inactconns); - cp->flags &= ~IP_VS_CONN_F_INACTIVE; + if (ntohs(s->v4.ver_size) >> SVER_SHIFT) { + IP_VS_ERR_RL("BACKUP, Dropping buffer, Unknown version %d\n", + ntohs(s->v4.ver_size) >> SVER_SHIFT); + return; } - } else if ((cp->dest) && (cp->protocol == IPPROTO_SCTP) && - (cp->state != state)) { - dest = cp->dest; - if (!(cp->flags & IP_VS_CONN_F_INACTIVE) && - (state != IP_VS_SCTP_S_ESTABLISHED)) { - atomic_dec(&dest->activeconns); - atomic_inc(&dest->inactconns); - cp->flags &= ~IP_VS_CONN_F_INACTIVE; + /* Process a single sync_conn */ + if ((retc=ip_vs_proc_sync_conn(p, msg_end)) < 0) { + IP_VS_ERR_RL("BACKUP, Dropping buffer, Err: %d in decoding\n", + retc); + return; } + /* Make sure we have 32 bit alignment */ + msg_end = p + ((size + 3) & ~3); } - - if (opt) - memcpy(&cp->in_seq, opt, sizeof(*opt)); - atomic_set(&cp->in_pkts, sysctl_ip_vs_sync_threshold[0]); - cp->state = state; - cp->old_state = cp->state; - /* - * We can not recover the right timeout for templates - * in all cases, we can not find the right fwmark - * virtual service. If needed, we can do it for - * non-fwmark persistent services. - */ - if (!(flags & IP_VS_CONN_F_TEMPLATE) && pp->timeout_table) - cp->timeout = pp->timeout_table[state]; - else - cp->timeout = (3*60*HZ); - ip_vs_conn_put(cp); + } else { + /* Old type of message */ + ip_vs_process_message_v0(buffer, buflen); + return; } } -- cgit v1.2.3 From 986a075795339c5ea1122ce9290dfd5504252eb0 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Fri, 19 Nov 2010 14:25:13 +0100 Subject: IPVS: Backup, Change sending to Version 1 format Enable sending and removal of version 0 sending Affected functions, ip_vs_sync_buff_create() ip_vs_sync_conn() ip_vs_core.c removal of IPv4 check. *v5 Just check cp->pe_data_len in ip_vs_sync_conn Check if padding needed before adding a new sync_conn to the buffer, i.e. avoid sending padding at the end. *v4 moved sanity check and pe_name_len after sloop. use cp->pe instead of cp->dest->svc->pe real length in each sync_conn, not padded length however total size of a sync_msg includes padding. *v3 Sending ip_vs_sync_conn_options in network order. Sending Templates for ONE_PACKET conn. Renaming of ip_vs_sync_mesg to ip_vs_sync_mesg_v0 Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 2 +- net/netfilter/ipvs/ip_vs_core.c | 13 ++- net/netfilter/ipvs/ip_vs_sync.c | 189 +++++++++++++++++++++++++++++++--------- 3 files changed, 156 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 4069484df7bb..a715f3db179a 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -919,7 +919,7 @@ extern char ip_vs_master_mcast_ifn[IP_VS_IFNAME_MAXLEN]; extern char ip_vs_backup_mcast_ifn[IP_VS_IFNAME_MAXLEN]; extern int start_sync_thread(int state, char *mcast_ifn, __u8 syncid); extern int stop_sync_thread(int state); -extern void ip_vs_sync_conn(const struct ip_vs_conn *cp); +extern void ip_vs_sync_conn(struct ip_vs_conn *cp); /* diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 3445da6e8c95..5287771d0647 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1560,9 +1560,15 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) * * Sync connection if it is about to close to * encorage the standby servers to update the connections timeout + * + * For ONE_PKT let ip_vs_sync_conn() do the filter work. */ - pkts = atomic_add_return(1, &cp->in_pkts); - if (af == AF_INET && (ip_vs_sync_state & IP_VS_STATE_MASTER) && + if (cp->flags & IP_VS_CONN_F_ONE_PACKET) + pkts = sysctl_ip_vs_sync_threshold[0]; + else + pkts = atomic_add_return(1, &cp->in_pkts); + + if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && cp->protocol == IPPROTO_SCTP) { if ((cp->state == IP_VS_SCTP_S_ESTABLISHED && (pkts % sysctl_ip_vs_sync_threshold[1] @@ -1577,8 +1583,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) } /* Keep this block last: TCP and others with pp->num_states <= 1 */ - else if (af == AF_INET && - (ip_vs_sync_state & IP_VS_STATE_MASTER) && + else if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && (((cp->protocol != IPPROTO_TCP || cp->state == IP_VS_TCP_S_ESTABLISHED) && (pkts % sysctl_ip_vs_sync_threshold[1] diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index e071508901d1..df5abf0e25af 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -226,7 +226,7 @@ struct ip_vs_sync_thread_data { #define MAX_CONNS_PER_SYNCBUFF 255 /* nr_conns in ip_vs_sync_mesg is 8 bit */ /* Version 0 header */ -struct ip_vs_sync_mesg { +struct ip_vs_sync_mesg_v0 { __u8 nr_conns; __u8 syncid; __u16 size; @@ -235,7 +235,7 @@ struct ip_vs_sync_mesg { }; /* Version 1 header */ -struct ip_vs_sync_mesg_v2 { +struct ip_vs_sync_mesg { __u8 reserved; /* must be zero */ __u8 syncid; __u16 size; @@ -299,6 +299,17 @@ static void ntoh_seq(struct ip_vs_seq *no, struct ip_vs_seq *ho) ho->previous_delta = get_unaligned_be32(&no->previous_delta); } +/* + * Copy of struct ip_vs_seq + * From Aligned host order to unaligned network order + */ +static void hton_seq(struct ip_vs_seq *ho, struct ip_vs_seq *no) +{ + put_unaligned_be32(ho->init_seq, &no->init_seq); + put_unaligned_be32(ho->delta, &no->delta); + put_unaligned_be32(ho->previous_delta, &no->previous_delta); +} + static inline struct ip_vs_sync_buff *sb_dequeue(void) { struct ip_vs_sync_buff *sb; @@ -317,6 +328,9 @@ static inline struct ip_vs_sync_buff *sb_dequeue(void) return sb; } +/* + * Create a new sync buffer for Version 1 proto. + */ static inline struct ip_vs_sync_buff * ip_vs_sync_buff_create(void) { struct ip_vs_sync_buff *sb; @@ -328,11 +342,15 @@ static inline struct ip_vs_sync_buff * ip_vs_sync_buff_create(void) kfree(sb); return NULL; } - sb->mesg->nr_conns = 0; + sb->mesg->reserved = 0; /* old nr_conns i.e. must be zeo now */ + sb->mesg->version = SYNC_PROTO_VER; sb->mesg->syncid = ip_vs_master_syncid; - sb->mesg->size = 4; - sb->head = (unsigned char *)sb->mesg + 4; + sb->mesg->size = sizeof(struct ip_vs_sync_mesg); + sb->mesg->nr_conns = 0; + sb->mesg->spare = 0; + sb->head = (unsigned char *)sb->mesg + sizeof(struct ip_vs_sync_mesg); sb->end = (unsigned char *)sb->mesg + sync_send_mesg_maxlen; + sb->firstuse = jiffies; return sb; } @@ -373,18 +391,60 @@ get_curr_sync_buff(unsigned long time) return sb; } - /* * Add an ip_vs_conn information into the current sync_buff. * Called by ip_vs_in. + * Sending Version 1 messages */ -void ip_vs_sync_conn(const struct ip_vs_conn *cp) +void ip_vs_sync_conn(struct ip_vs_conn *cp) { struct ip_vs_sync_mesg *m; - struct ip_vs_sync_conn_v0 *s; - int len; + union ip_vs_sync_conn *s; + __u8 *p; + unsigned int len, pe_name_len, pad; + + /* Do not sync ONE PACKET */ + if (cp->flags & IP_VS_CONN_F_ONE_PACKET) + goto control; +sloop: + /* Sanity checks */ + pe_name_len = 0; + if (cp->pe_data_len) { + if (!cp->pe_data || !cp->dest) { + IP_VS_ERR_RL("SYNC, connection pe_data invalid\n"); + return; + } + pe_name_len = strnlen(cp->pe->name, IP_VS_PENAME_MAXLEN); + } spin_lock(&curr_sb_lock); + +#ifdef CONFIG_IP_VS_IPV6 + if (cp->af == AF_INET6) + len = sizeof(struct ip_vs_sync_v6); + else +#endif + len = sizeof(struct ip_vs_sync_v4); + + if (cp->flags & IP_VS_CONN_F_SEQ_MASK) + len += sizeof(struct ip_vs_sync_conn_options) + 2; + + if (cp->pe_data_len) + len += cp->pe_data_len + 2; /* + Param hdr field */ + if (pe_name_len) + len += pe_name_len + 2; + + /* check if there is a space for this one */ + pad = 0; + if (curr_sb) { + pad = (4 - (size_t)curr_sb->head) & 3; + if (curr_sb->head + len + pad > curr_sb->end) { + sb_queue_tail(curr_sb); + curr_sb = NULL; + pad = 0; + } + } + if (!curr_sb) { if (!(curr_sb=ip_vs_sync_buff_create())) { spin_unlock(&curr_sb_lock); @@ -393,41 +453,84 @@ void ip_vs_sync_conn(const struct ip_vs_conn *cp) } } - len = (cp->flags & IP_VS_CONN_F_SEQ_MASK) ? FULL_CONN_SIZE : - SIMPLE_CONN_SIZE; m = curr_sb->mesg; - s = (struct ip_vs_sync_conn_v0 *)curr_sb->head; - - /* copy members */ - s->protocol = cp->protocol; - s->cport = cp->cport; - s->vport = cp->vport; - s->dport = cp->dport; - s->caddr = cp->caddr.ip; - s->vaddr = cp->vaddr.ip; - s->daddr = cp->daddr.ip; - s->flags = htons(cp->flags & ~IP_VS_CONN_F_HASHED); - s->state = htons(cp->state); - if (cp->flags & IP_VS_CONN_F_SEQ_MASK) { - struct ip_vs_sync_conn_options *opt = - (struct ip_vs_sync_conn_options *)&s[1]; - memcpy(opt, &cp->in_seq, sizeof(*opt)); - } - + p = curr_sb->head; + curr_sb->head += pad + len; + m->size += pad + len; + /* Add ev. padding from prev. sync_conn */ + while (pad--) + *(p++) = 0; + + s = (union ip_vs_sync_conn *)p; + + /* Set message type & copy members */ + s->v4.type = (cp->af == AF_INET6 ? STYPE_F_INET6 : 0); + s->v4.ver_size = htons(len & SVER_MASK); /* Version 0 */ + s->v4.flags = htonl(cp->flags & ~IP_VS_CONN_F_HASHED); + s->v4.state = htons(cp->state); + s->v4.protocol = cp->protocol; + s->v4.cport = cp->cport; + s->v4.vport = cp->vport; + s->v4.dport = cp->dport; + s->v4.fwmark = htonl(cp->fwmark); + s->v4.timeout = htonl(cp->timeout / HZ); m->nr_conns++; - m->size += len; - curr_sb->head += len; - /* check if there is a space for next one */ - if (curr_sb->head+FULL_CONN_SIZE > curr_sb->end) { - sb_queue_tail(curr_sb); - curr_sb = NULL; +#ifdef CONFIG_IP_VS_IPV6 + if (cp->af == AF_INET6) { + p += sizeof(struct ip_vs_sync_v6); + ipv6_addr_copy(&s->v6.caddr, &cp->caddr.in6); + ipv6_addr_copy(&s->v6.vaddr, &cp->vaddr.in6); + ipv6_addr_copy(&s->v6.daddr, &cp->daddr.in6); + } else +#endif + { + p += sizeof(struct ip_vs_sync_v4); /* options ptr */ + s->v4.caddr = cp->caddr.ip; + s->v4.vaddr = cp->vaddr.ip; + s->v4.daddr = cp->daddr.ip; + } + if (cp->flags & IP_VS_CONN_F_SEQ_MASK) { + *(p++) = IPVS_OPT_SEQ_DATA; + *(p++) = sizeof(struct ip_vs_sync_conn_options); + hton_seq((struct ip_vs_seq *)p, &cp->in_seq); + p += sizeof(struct ip_vs_seq); + hton_seq((struct ip_vs_seq *)p, &cp->out_seq); + p += sizeof(struct ip_vs_seq); } + /* Handle pe data */ + if (cp->pe_data_len && cp->pe_data) { + *(p++) = IPVS_OPT_PE_DATA; + *(p++) = cp->pe_data_len; + memcpy(p, cp->pe_data, cp->pe_data_len); + p += cp->pe_data_len; + if (pe_name_len) { + /* Add PE_NAME */ + *(p++) = IPVS_OPT_PE_NAME; + *(p++) = pe_name_len; + memcpy(p, cp->pe->name, pe_name_len); + p += pe_name_len; + } + } + spin_unlock(&curr_sb_lock); +control: /* synchronize its controller if it has */ - if (cp->control) - ip_vs_sync_conn(cp->control); + cp = cp->control; + if (!cp) + return; + /* + * Reduce sync rate for templates + * i.e only increment in_pkts for Templates. + */ + if (cp->flags & IP_VS_CONN_F_TEMPLATE) { + int pkts = atomic_add_return(1, &cp->in_pkts); + + if (pkts % sysctl_ip_vs_sync_threshold[1] != 1) + return; + } + goto sloop; } /* @@ -596,7 +699,7 @@ static void ip_vs_proc_conn(struct ip_vs_conn_param *param, unsigned flags, */ static void ip_vs_process_message_v0(const char *buffer, const size_t buflen) { - struct ip_vs_sync_mesg *m = (struct ip_vs_sync_mesg *)buffer; + struct ip_vs_sync_mesg_v0 *m = (struct ip_vs_sync_mesg_v0 *)buffer; struct ip_vs_sync_conn_v0 *s; struct ip_vs_sync_conn_options *opt; struct ip_vs_protocol *pp; @@ -604,7 +707,7 @@ static void ip_vs_process_message_v0(const char *buffer, const size_t buflen) char *p; int i; - p = (char *)buffer + sizeof(struct ip_vs_sync_mesg); + p = (char *)buffer + sizeof(struct ip_vs_sync_mesg_v0); for (i=0; inr_conns; i++) { unsigned flags, state; @@ -848,11 +951,11 @@ out: */ static void ip_vs_process_message(__u8 *buffer, const size_t buflen) { - struct ip_vs_sync_mesg_v2 *m2 = (struct ip_vs_sync_mesg_v2 *)buffer; + struct ip_vs_sync_mesg *m2 = (struct ip_vs_sync_mesg *)buffer; __u8 *p, *msg_end; - unsigned int i, nr_conns; + int i, nr_conns; - if (buflen < sizeof(struct ip_vs_sync_mesg)) { + if (buflen < sizeof(struct ip_vs_sync_mesg_v0)) { IP_VS_DBG(2, "BACKUP, message header too short\n"); return; } @@ -872,7 +975,7 @@ static void ip_vs_process_message(__u8 *buffer, const size_t buflen) if ((m2->version == SYNC_PROTO_VER) && (m2->reserved == 0) && (m2->spare == 0)) { - msg_end = buffer + sizeof(struct ip_vs_sync_mesg_v2); + msg_end = buffer + sizeof(struct ip_vs_sync_mesg); nr_conns = m2->nr_conns; for (i=0; i Date: Fri, 19 Nov 2010 14:25:14 +0100 Subject: IPVS: Backup, adding version 0 sending capabilities This patch adds a sysclt net.ipv4.vs.sync_version that can be used to send sync msg in version 0 or 1 format. sync_version value is logical, Value 1 (default) New version 0 Plain old version Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 2 + net/netfilter/ipvs/ip_vs_ctl.c | 28 ++++++++- net/netfilter/ipvs/ip_vs_sync.c | 134 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index a715f3db179a..d858264217ba 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -883,7 +883,9 @@ extern int sysctl_ip_vs_conntrack; extern int sysctl_ip_vs_snat_reroute; extern struct ip_vs_stats ip_vs_stats; extern const struct ctl_path net_vs_ctl_path[]; +extern int sysctl_ip_vs_sync_ver; +extern void ip_vs_sync_switch_mode(int mode); extern struct ip_vs_service * ip_vs_service_get(int af, __u32 fwmark, __u16 protocol, const union nf_inet_addr *vaddr, __be16 vport); diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index a5bd00279047..d12a13c497ba 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -92,7 +92,7 @@ int sysctl_ip_vs_nat_icmp_send = 0; int sysctl_ip_vs_conntrack; #endif int sysctl_ip_vs_snat_reroute = 1; - +int sysctl_ip_vs_sync_ver = 1; /* Default version of sync proto */ #ifdef CONFIG_IP_VS_DEBUG static int sysctl_ip_vs_debug_level = 0; @@ -1536,6 +1536,25 @@ proc_do_sync_threshold(ctl_table *table, int write, return rc; } +static int +proc_do_sync_mode(ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int *valp = table->data; + int val = *valp; + int rc; + + rc = proc_dointvec(table, write, buffer, lenp, ppos); + if (write && (*valp != val)) { + if ((*valp < 0) || (*valp > 1)) { + /* Restore the correct value */ + *valp = val; + } else { + ip_vs_sync_switch_mode(val); + } + } + return rc; +} /* * IPVS sysctl table (under the /proc/sys/net/ipv4/vs/) @@ -1602,6 +1621,13 @@ static struct ctl_table vs_vars[] = { .mode = 0644, .proc_handler = &proc_dointvec, }, + { + .procname = "sync_version", + .data = &sysctl_ip_vs_sync_ver, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_do_sync_mode, + }, #if 0 { .procname = "timeout_established", diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index df5abf0e25af..c1c167ab73ee 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -5,6 +5,18 @@ * high-performance and highly available server based on a * cluster of servers. * + * Version 1, is capable of handling both version 0 and 1 messages. + * Version 0 is the plain old format. + * Note Version 0 receivers will just drop Ver 1 messages. + * Version 1 is capable of handle IPv6, Persistence data, + * time-outs, and firewall marks. + * In ver.1 "ip_vs_sync_conn_options" will be sent in netw. order. + * Ver. 0 can be turned on by sysctl -w net.ipv4.vs.sync_version=0 + * + * Definitions Message: is a complete datagram + * Sync_conn: is a part of a Message + * Param Data is an option to a Sync_conn. + * * Authors: Wensong Zhang * * ip_vs_sync: sync connection info from master load balancer to backups @@ -15,6 +27,8 @@ * Alexandre Cassen : Added SyncID support for incoming sync * messages filtering. * Justin Ossevoort : Fix endian problem on sync message size. + * Hans Schillstrom : Added Version 1: i.e. IPv6, + * Persistence support, fwmark and time-out. */ #define KMSG_COMPONENT "IPVS" @@ -391,6 +405,121 @@ get_curr_sync_buff(unsigned long time) return sb; } +/* + * Switch mode from sending version 0 or 1 + * - must handle sync_buf + */ +void ip_vs_sync_switch_mode(int mode) { + + if (!ip_vs_sync_state & IP_VS_STATE_MASTER) + return; + if (mode == sysctl_ip_vs_sync_ver || !curr_sb) + return; + + spin_lock_bh(&curr_sb_lock); + /* Buffer empty ? then let buf_create do the job */ + if ( curr_sb->mesg->size <= sizeof(struct ip_vs_sync_mesg)) { + kfree(curr_sb); + curr_sb = NULL; + } else { + spin_lock_bh(&ip_vs_sync_lock); + if (ip_vs_sync_state & IP_VS_STATE_MASTER) + list_add_tail(&curr_sb->list, &ip_vs_sync_queue); + else + ip_vs_sync_buff_release(curr_sb); + spin_unlock_bh(&ip_vs_sync_lock); + } + spin_unlock_bh(&curr_sb_lock); +} + +/* + * Create a new sync buffer for Version 0 proto. + */ +static inline struct ip_vs_sync_buff * ip_vs_sync_buff_create_v0(void) +{ + struct ip_vs_sync_buff *sb; + struct ip_vs_sync_mesg_v0 *mesg; + + if (!(sb=kmalloc(sizeof(struct ip_vs_sync_buff), GFP_ATOMIC))) + return NULL; + + if (!(sb->mesg=kmalloc(sync_send_mesg_maxlen, GFP_ATOMIC))) { + kfree(sb); + return NULL; + } + mesg = (struct ip_vs_sync_mesg_v0 *)sb->mesg; + mesg->nr_conns = 0; + mesg->syncid = ip_vs_master_syncid; + mesg->size = 4; + sb->head = (unsigned char *)mesg + 4; + sb->end = (unsigned char *)mesg + sync_send_mesg_maxlen; + sb->firstuse = jiffies; + return sb; +} + +/* + * Version 0 , could be switched in by sys_ctl. + * Add an ip_vs_conn information into the current sync_buff. + */ +void ip_vs_sync_conn_v0(struct ip_vs_conn *cp) +{ + struct ip_vs_sync_mesg_v0 *m; + struct ip_vs_sync_conn_v0 *s; + int len; + + if (unlikely(cp->af != AF_INET)) + return; + /* Do not sync ONE PACKET */ + if (cp->flags & IP_VS_CONN_F_ONE_PACKET) + return; + + spin_lock(&curr_sb_lock); + if (!curr_sb) { + if (!(curr_sb=ip_vs_sync_buff_create_v0())) { + spin_unlock(&curr_sb_lock); + pr_err("ip_vs_sync_buff_create failed.\n"); + return; + } + } + + len = (cp->flags & IP_VS_CONN_F_SEQ_MASK) ? FULL_CONN_SIZE : + SIMPLE_CONN_SIZE; + m = (struct ip_vs_sync_mesg_v0 *)curr_sb->mesg; + s = (struct ip_vs_sync_conn_v0 *)curr_sb->head; + + /* copy members */ + s->reserved = 0; + s->protocol = cp->protocol; + s->cport = cp->cport; + s->vport = cp->vport; + s->dport = cp->dport; + s->caddr = cp->caddr.ip; + s->vaddr = cp->vaddr.ip; + s->daddr = cp->daddr.ip; + s->flags = htons(cp->flags & ~IP_VS_CONN_F_HASHED); + s->state = htons(cp->state); + if (cp->flags & IP_VS_CONN_F_SEQ_MASK) { + struct ip_vs_sync_conn_options *opt = + (struct ip_vs_sync_conn_options *)&s[1]; + memcpy(opt, &cp->in_seq, sizeof(*opt)); + } + + m->nr_conns++; + m->size += len; + curr_sb->head += len; + + /* check if there is a space for next one */ + if (curr_sb->head + FULL_CONN_SIZE > curr_sb->end) { + sb_queue_tail(curr_sb); + curr_sb = NULL; + } + spin_unlock(&curr_sb_lock); + + /* synchronize its controller if it has */ + if (cp->control) + ip_vs_sync_conn(cp->control); +} + /* * Add an ip_vs_conn information into the current sync_buff. * Called by ip_vs_in. @@ -403,6 +532,11 @@ void ip_vs_sync_conn(struct ip_vs_conn *cp) __u8 *p; unsigned int len, pe_name_len, pad; + /* Handle old version of the protocol */ + if (sysctl_ip_vs_sync_ver == 0) { + ip_vs_sync_conn_v0(cp); + return; + } /* Do not sync ONE PACKET */ if (cp->flags & IP_VS_CONN_F_ONE_PACKET) goto control; -- cgit v1.2.3 From f1c722295e029eace7960fc687efd5afd67dc555 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 15 Dec 2010 22:58:53 +0100 Subject: netfilter: xtables: use guarded types We are supposed to use the kernel's own types in userspace exports. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter/xt_CT.h | 10 +++++----- include/linux/netfilter/xt_TCPOPTSTRIP.h | 2 +- include/linux/netfilter/xt_TPROXY.h | 8 ++++---- include/linux/netfilter/xt_cluster.h | 8 ++++---- include/linux/netfilter/xt_quota.h | 6 +++--- include/linux/netfilter/xt_time.h | 14 +++++++------- include/linux/netfilter/xt_u32.h | 16 ++++++++-------- 7 files changed, 32 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/xt_CT.h b/include/linux/netfilter/xt_CT.h index 1b564106891d..fbf4c5658554 100644 --- a/include/linux/netfilter/xt_CT.h +++ b/include/linux/netfilter/xt_CT.h @@ -4,11 +4,11 @@ #define XT_CT_NOTRACK 0x1 struct xt_ct_target_info { - u_int16_t flags; - u_int16_t zone; - u_int32_t ct_events; - u_int32_t exp_events; - char helper[16]; + __u16 flags; + __u16 zone; + __u32 ct_events; + __u32 exp_events; + char helper[16]; /* Used internally by the kernel */ struct nf_conn *ct __attribute__((aligned(8))); diff --git a/include/linux/netfilter/xt_TCPOPTSTRIP.h b/include/linux/netfilter/xt_TCPOPTSTRIP.h index 2db543214ff5..342ef14b1761 100644 --- a/include/linux/netfilter/xt_TCPOPTSTRIP.h +++ b/include/linux/netfilter/xt_TCPOPTSTRIP.h @@ -7,7 +7,7 @@ (((1U << (idx & 31)) & bmap[(idx) >> 5]) != 0) struct xt_tcpoptstrip_target_info { - u_int32_t strip_bmap[8]; + __u32 strip_bmap[8]; }; #endif /* _XT_TCPOPTSTRIP_H */ diff --git a/include/linux/netfilter/xt_TPROXY.h b/include/linux/netfilter/xt_TPROXY.h index 3f3d69361289..8097e0b4c15e 100644 --- a/include/linux/netfilter/xt_TPROXY.h +++ b/include/linux/netfilter/xt_TPROXY.h @@ -5,15 +5,15 @@ * redirection. We can get rid of that whenever we get support for * mutliple targets in the same rule. */ struct xt_tproxy_target_info { - u_int32_t mark_mask; - u_int32_t mark_value; + __u32 mark_mask; + __u32 mark_value; __be32 laddr; __be16 lport; }; struct xt_tproxy_target_info_v1 { - u_int32_t mark_mask; - u_int32_t mark_value; + __u32 mark_mask; + __u32 mark_value; union nf_inet_addr laddr; __be16 lport; }; diff --git a/include/linux/netfilter/xt_cluster.h b/include/linux/netfilter/xt_cluster.h index 886682656f09..66cfa3c782ac 100644 --- a/include/linux/netfilter/xt_cluster.h +++ b/include/linux/netfilter/xt_cluster.h @@ -6,10 +6,10 @@ enum xt_cluster_flags { }; struct xt_cluster_match_info { - u_int32_t total_nodes; - u_int32_t node_mask; - u_int32_t hash_seed; - u_int32_t flags; + __u32 total_nodes; + __u32 node_mask; + __u32 hash_seed; + __u32 flags; }; #define XT_CLUSTER_NODES_MAX 32 diff --git a/include/linux/netfilter/xt_quota.h b/include/linux/netfilter/xt_quota.h index b0d28c659ab7..8bda65f0bc92 100644 --- a/include/linux/netfilter/xt_quota.h +++ b/include/linux/netfilter/xt_quota.h @@ -9,9 +9,9 @@ enum xt_quota_flags { struct xt_quota_priv; struct xt_quota_info { - u_int32_t flags; - u_int32_t pad; - aligned_u64 quota; + __u32 flags; + __u32 pad; + aligned_u64 quota; /* Used internally by the kernel */ struct xt_quota_priv *master; diff --git a/include/linux/netfilter/xt_time.h b/include/linux/netfilter/xt_time.h index 14b6df412c9f..b8bd4568efdb 100644 --- a/include/linux/netfilter/xt_time.h +++ b/include/linux/netfilter/xt_time.h @@ -2,13 +2,13 @@ #define _XT_TIME_H 1 struct xt_time_info { - u_int32_t date_start; - u_int32_t date_stop; - u_int32_t daytime_start; - u_int32_t daytime_stop; - u_int32_t monthdays_match; - u_int8_t weekdays_match; - u_int8_t flags; + __u32 date_start; + __u32 date_stop; + __u32 daytime_start; + __u32 daytime_stop; + __u32 monthdays_match; + __u8 weekdays_match; + __u8 flags; }; enum { diff --git a/include/linux/netfilter/xt_u32.h b/include/linux/netfilter/xt_u32.h index 9947f56cdbdd..e8c3d8722bae 100644 --- a/include/linux/netfilter/xt_u32.h +++ b/include/linux/netfilter/xt_u32.h @@ -9,13 +9,13 @@ enum xt_u32_ops { }; struct xt_u32_location_element { - u_int32_t number; - u_int8_t nextop; + __u32 number; + __u8 nextop; }; struct xt_u32_value_element { - u_int32_t min; - u_int32_t max; + __u32 min; + __u32 max; }; /* @@ -27,14 +27,14 @@ struct xt_u32_value_element { struct xt_u32_test { struct xt_u32_location_element location[XT_U32_MAXSIZE+1]; struct xt_u32_value_element value[XT_U32_MAXSIZE+1]; - u_int8_t nnums; - u_int8_t nvalues; + __u8 nnums; + __u8 nvalues; }; struct xt_u32 { struct xt_u32_test tests[XT_U32_MAXSIZE+1]; - u_int8_t ntests; - u_int8_t invert; + __u8 ntests; + __u8 invert; }; #endif /* _XT_U32_H */ -- cgit v1.2.3 From ae90bdeaeac6b964b7a1e853a90a19f358a9ac20 Mon Sep 17 00:00:00 2001 From: KOVACS Krisztian Date: Wed, 15 Dec 2010 23:53:41 +0100 Subject: netfilter: fix compilation when conntrack is disabled but tproxy is enabled The IPv6 tproxy patches split IPv6 defragmentation off of conntrack, but failed to update the #ifdef stanzas guarding the defragmentation related fields and code in skbuff and conntrack related code in nf_defrag_ipv6.c. This patch adds the required #ifdefs so that IPv6 tproxy can truly be used without connection tracking. Original report: http://marc.info/?l=linux-netdev&m=129010118516341&w=2 Reported-by: Randy Dunlap Signed-off-by: KOVACS Krisztian Acked-by: Randy Dunlap Signed-off-by: Patrick McHardy --- include/linux/skbuff.h | 15 +++++++++++++++ include/net/netfilter/ipv6/nf_conntrack_ipv6.h | 10 ---------- include/net/netfilter/ipv6/nf_defrag_ipv6.h | 10 ++++++++++ net/core/skbuff.c | 2 ++ net/ipv6/netfilter/nf_defrag_ipv6_hooks.c | 8 +++++++- 5 files changed, 34 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index e6ba898de61c..4f2db79a2abb 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -255,6 +255,11 @@ typedef unsigned int sk_buff_data_t; typedef unsigned char *sk_buff_data_t; #endif +#if defined(CONFIG_NF_DEFRAG_IPV4) || defined(CONFIG_NF_DEFRAG_IPV4_MODULE) || \ + defined(CONFIG_NF_DEFRAG_IPV6) || defined(CONFIG_NF_DEFRAG_IPV6_MODULE) +#define NET_SKBUFF_NF_DEFRAG_NEEDED 1 +#endif + /** * struct sk_buff - socket buffer * @next: Next buffer in list @@ -362,6 +367,8 @@ struct sk_buff { void (*destructor)(struct sk_buff *skb); #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) struct nf_conntrack *nfct; +#endif +#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED struct sk_buff *nfct_reasm; #endif #ifdef CONFIG_BRIDGE_NETFILTER @@ -2051,6 +2058,8 @@ static inline void nf_conntrack_get(struct nf_conntrack *nfct) if (nfct) atomic_inc(&nfct->use); } +#endif +#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED static inline void nf_conntrack_get_reasm(struct sk_buff *skb) { if (skb) @@ -2079,6 +2088,8 @@ static inline void nf_reset(struct sk_buff *skb) #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) nf_conntrack_put(skb->nfct); skb->nfct = NULL; +#endif +#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED nf_conntrack_put_reasm(skb->nfct_reasm); skb->nfct_reasm = NULL; #endif @@ -2095,6 +2106,8 @@ static inline void __nf_copy(struct sk_buff *dst, const struct sk_buff *src) dst->nfct = src->nfct; nf_conntrack_get(src->nfct); dst->nfctinfo = src->nfctinfo; +#endif +#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED dst->nfct_reasm = src->nfct_reasm; nf_conntrack_get_reasm(src->nfct_reasm); #endif @@ -2108,6 +2121,8 @@ static inline void nf_copy(struct sk_buff *dst, const struct sk_buff *src) { #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) nf_conntrack_put(dst->nfct); +#endif +#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED nf_conntrack_put_reasm(dst->nfct_reasm); #endif #ifdef CONFIG_BRIDGE_NETFILTER diff --git a/include/net/netfilter/ipv6/nf_conntrack_ipv6.h b/include/net/netfilter/ipv6/nf_conntrack_ipv6.h index 1ee717eb5b09..a4c993685795 100644 --- a/include/net/netfilter/ipv6/nf_conntrack_ipv6.h +++ b/include/net/netfilter/ipv6/nf_conntrack_ipv6.h @@ -7,16 +7,6 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6; -extern int nf_ct_frag6_init(void); -extern void nf_ct_frag6_cleanup(void); -extern struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user); -extern void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb, - struct net_device *in, - struct net_device *out, - int (*okfn)(struct sk_buff *)); - -struct inet_frags_ctl; - #include extern struct ctl_table nf_ct_ipv6_sysctl_table[]; diff --git a/include/net/netfilter/ipv6/nf_defrag_ipv6.h b/include/net/netfilter/ipv6/nf_defrag_ipv6.h index 94dd54d76b48..fd79c9a1779d 100644 --- a/include/net/netfilter/ipv6/nf_defrag_ipv6.h +++ b/include/net/netfilter/ipv6/nf_defrag_ipv6.h @@ -3,4 +3,14 @@ extern void nf_defrag_ipv6_enable(void); +extern int nf_ct_frag6_init(void); +extern void nf_ct_frag6_cleanup(void); +extern struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user); +extern void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb, + struct net_device *in, + struct net_device *out, + int (*okfn)(struct sk_buff *)); + +struct inet_frags_ctl; + #endif /* _NF_DEFRAG_IPV6_H */ diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 104f8444754a..74ebf4b5258a 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -380,6 +380,8 @@ static void skb_release_head_state(struct sk_buff *skb) } #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) nf_conntrack_put(skb->nfct); +#endif +#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED nf_conntrack_put_reasm(skb->nfct_reasm); #endif #ifdef CONFIG_BRIDGE_NETFILTER diff --git a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c index 99abfb53bab9..97c5b21b9674 100644 --- a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c +++ b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c @@ -19,13 +19,15 @@ #include #include +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) #include #include #include #include #include -#include #include +#endif +#include #include static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum, @@ -33,8 +35,10 @@ static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum, { u16 zone = NF_CT_DEFAULT_ZONE; +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) if (skb->nfct) zone = nf_ct_zone((struct nf_conn *)skb->nfct); +#endif #ifdef CONFIG_BRIDGE_NETFILTER if (skb->nf_bridge && @@ -56,9 +60,11 @@ static unsigned int ipv6_defrag(unsigned int hooknum, { struct sk_buff *reasm; +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) /* Previously seen (loopback)? */ if (skb->nfct && !nf_ct_is_template((struct nf_conn *)skb->nfct)) return NF_ACCEPT; +#endif reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(hooknum, skb)); /* queued */ -- cgit v1.2.3 From ca01d6dd2d7a2652000307520777538740efc286 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Tue, 28 Dec 2010 14:25:21 -0800 Subject: pstore: new filesystem interface to platform persistent storage Some platforms have a small amount of non-volatile storage that can be used to store information useful to diagnose the cause of a system crash. This is the generic part of a file system interface that presents information from the crash as a series of files in /dev/pstore. Once the information has been seen, the underlying storage is freed by deleting the files. Signed-off-by: Tony Luck --- Documentation/ABI/testing/pstore | 35 ++++ Documentation/ABI/testing/sysfs-fs-pstore | 7 + fs/Kconfig | 1 + fs/Makefile | 1 + fs/pstore/Kconfig | 13 ++ fs/pstore/Makefile | 7 + fs/pstore/inode.c | 280 ++++++++++++++++++++++++++++++ fs/pstore/internal.h | 7 + fs/pstore/platform.c | 202 +++++++++++++++++++++ include/linux/magic.h | 1 + include/linux/pstore.h | 60 +++++++ 11 files changed, 614 insertions(+) create mode 100644 Documentation/ABI/testing/pstore create mode 100644 Documentation/ABI/testing/sysfs-fs-pstore create mode 100644 fs/pstore/Kconfig create mode 100644 fs/pstore/Makefile create mode 100644 fs/pstore/inode.c create mode 100644 fs/pstore/internal.h create mode 100644 fs/pstore/platform.c create mode 100644 include/linux/pstore.h (limited to 'include') diff --git a/Documentation/ABI/testing/pstore b/Documentation/ABI/testing/pstore new file mode 100644 index 000000000000..f1fb2a004264 --- /dev/null +++ b/Documentation/ABI/testing/pstore @@ -0,0 +1,35 @@ +Where: /dev/pstore/... +Date: January 2011 +Kernel Version: 2.6.38 +Contact: tony.luck@intel.com +Description: Generic interface to platform dependent persistent storage. + + Platforms that provide a mechanism to preserve some data + across system reboots can register with this driver to + provide a generic interface to show records captured in + the dying moments. In the case of a panic the last part + of the console log is captured, but other interesting + data can also be saved. + + # mount -t pstore - /dev/pstore + + $ ls -l /dev/pstore + total 0 + -r--r--r-- 1 root root 7896 Nov 30 15:38 dmesg-erst-1 + + Different users of this interface will result in different + filename prefixes. Currently two are defined: + + "dmesg" - saved console log + "mce" - architecture dependent data from fatal h/w error + + Once the information in a file has been read, removing + the file will signal to the underlying persistent storage + device that it can reclaim the space for later re-use. + + $ rm /dev/pstore/dmesg-erst-1 + + The expectation is that all files in /dev/pstore + will be saved elsewhere and erased from persistent store + soon after boot to free up space ready for the next + catastrophe. diff --git a/Documentation/ABI/testing/sysfs-fs-pstore b/Documentation/ABI/testing/sysfs-fs-pstore new file mode 100644 index 000000000000..8e659d854805 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-fs-pstore @@ -0,0 +1,7 @@ +What: /sys/fs/pstore/kmsg_bytes +Date: January 2011 +Kernel Version: 2.6.38 +Contact: "Tony Luck" +Description: + Controls amount of console log that will be saved + to persistent store on oops/panic. diff --git a/fs/Kconfig b/fs/Kconfig index 771f457402d4..2bbe47fec1ec 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -188,6 +188,7 @@ source "fs/omfs/Kconfig" source "fs/hpfs/Kconfig" source "fs/qnx4/Kconfig" source "fs/romfs/Kconfig" +source "fs/pstore/Kconfig" source "fs/sysv/Kconfig" source "fs/ufs/Kconfig" source "fs/exofs/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index a7f7cef0c0c8..db71a5b21a4f 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -121,3 +121,4 @@ obj-$(CONFIG_BTRFS_FS) += btrfs/ obj-$(CONFIG_GFS2_FS) += gfs2/ obj-$(CONFIG_EXOFS_FS) += exofs/ obj-$(CONFIG_CEPH_FS) += ceph/ +obj-$(CONFIG_PSTORE) += pstore/ diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig new file mode 100644 index 000000000000..867d0ac026ce --- /dev/null +++ b/fs/pstore/Kconfig @@ -0,0 +1,13 @@ +config PSTORE + bool "Persistant store support" + default n + help + This option enables generic access to platform level + persistent storage via "pstore" filesystem that can + be mounted as /dev/pstore. Only useful if you have + a platform level driver that registers with pstore to + provide the data, so you probably should just go say "Y" + (or "M") to a platform specific persistent store driver + (e.g. ACPI_APEI on X86) which will select this for you. + If you don't have a platform persistent store driver, + say N. diff --git a/fs/pstore/Makefile b/fs/pstore/Makefile new file mode 100644 index 000000000000..760f4bce7d1d --- /dev/null +++ b/fs/pstore/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the linux pstorefs routines. +# + +obj-y += pstore.o + +pstore-objs += inode.o platform.o diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c new file mode 100644 index 000000000000..0e806aafe857 --- /dev/null +++ b/fs/pstore/inode.c @@ -0,0 +1,280 @@ +/* + * Persistent Storage - ramfs parts. + * + * Copyright (C) 2010 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +#define PSTORE_NAMELEN 64 + +struct pstore_private { + u64 id; + int (*erase)(u64); +}; + +#define pstore_get_inode ramfs_get_inode + +/* + * When a file is unlinked from our file system we call the + * platform driver to erase the record from persistent store. + */ +static int pstore_unlink(struct inode *dir, struct dentry *dentry) +{ + struct pstore_private *p = dentry->d_inode->i_private; + + p->erase(p->id); + kfree(p); + + return simple_unlink(dir, dentry); +} + +static const struct inode_operations pstore_dir_inode_operations = { + .lookup = simple_lookup, + .unlink = pstore_unlink, +}; + +static const struct super_operations pstore_ops = { + .statfs = simple_statfs, + .drop_inode = generic_delete_inode, + .show_options = generic_show_options, +}; + +static struct super_block *pstore_sb; +static struct vfsmount *pstore_mnt; + +int pstore_is_mounted(void) +{ + return pstore_mnt != NULL; +} + +/* + * Set up a file structure as if we had opened this file and + * write our data to it. + */ +static int pstore_writefile(struct inode *inode, struct dentry *dentry, + char *data, size_t size) +{ + struct file f; + ssize_t n; + mm_segment_t old_fs = get_fs(); + + memset(&f, '0', sizeof f); + f.f_mapping = inode->i_mapping; + f.f_path.dentry = dentry; + f.f_path.mnt = pstore_mnt; + f.f_pos = 0; + f.f_op = inode->i_fop; + set_fs(KERNEL_DS); + n = do_sync_write(&f, data, size, &f.f_pos); + set_fs(old_fs); + + fsnotify_modify(&f); + + return n == size; +} + +/* + * Make a regular file in the root directory of our file system. + * Load it up with "size" bytes of data from "buf". + * Set the mtime & ctime to the date that this record was originally stored. + */ +int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, + char *data, size_t size, + struct timespec time, int (*erase)(u64)) +{ + struct dentry *root = pstore_sb->s_root; + struct dentry *dentry; + struct inode *inode; + int rc; + char name[PSTORE_NAMELEN]; + struct pstore_private *private; + + rc = -ENOMEM; + inode = pstore_get_inode(pstore_sb, root->d_inode, S_IFREG | 0444, 0); + if (!inode) + goto fail; + inode->i_uid = inode->i_gid = 0; + private = kmalloc(sizeof *private, GFP_KERNEL); + if (!private) + goto fail_alloc; + private->id = id; + private->erase = erase; + + switch (type) { + case PSTORE_TYPE_DMESG: + sprintf(name, "dmesg-%s-%lld", psname, id); + break; + case PSTORE_TYPE_MCE: + sprintf(name, "mce-%s-%lld", psname, id); + break; + case PSTORE_TYPE_UNKNOWN: + sprintf(name, "unknown-%s-%lld", psname, id); + break; + default: + sprintf(name, "type%d-%s-%lld", type, psname, id); + break; + } + + mutex_lock(&root->d_inode->i_mutex); + + rc = -ENOSPC; + dentry = d_alloc_name(root, name); + if (IS_ERR(dentry)) + goto fail_lockedalloc; + + d_add(dentry, inode); + + mutex_unlock(&root->d_inode->i_mutex); + + if (!pstore_writefile(inode, dentry, data, size)) + goto fail_write; + + inode->i_private = private; + + if (time.tv_sec) + inode->i_mtime = inode->i_ctime = time; + + return 0; + +fail_write: + kfree(private); + inode->i_nlink--; + mutex_lock(&root->d_inode->i_mutex); + d_delete(dentry); + dput(dentry); + mutex_unlock(&root->d_inode->i_mutex); + goto fail; + +fail_lockedalloc: + mutex_unlock(&root->d_inode->i_mutex); + kfree(private); +fail_alloc: + iput(inode); + +fail: + return rc; +} + +int pstore_fill_super(struct super_block *sb, void *data, int silent) +{ + struct inode *inode = NULL; + struct dentry *root; + int err; + + save_mount_options(sb, data); + + pstore_sb = sb; + + sb->s_maxbytes = MAX_LFS_FILESIZE; + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = PSTOREFS_MAGIC; + sb->s_op = &pstore_ops; + sb->s_time_gran = 1; + + inode = pstore_get_inode(sb, NULL, S_IFDIR | 0755, 0); + if (!inode) { + err = -ENOMEM; + goto fail; + } + /* override ramfs "dir" options so we catch unlink(2) */ + inode->i_op = &pstore_dir_inode_operations; + + root = d_alloc_root(inode); + sb->s_root = root; + if (!root) { + err = -ENOMEM; + goto fail; + } + + pstore_get_records(); + + return 0; +fail: + iput(inode); + return err; +} + +static int pstore_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) +{ + struct dentry *root; + + root = mount_nodev(fs_type, flags, data, pstore_fill_super); + if (IS_ERR(root)) + return -ENOMEM; + + mnt->mnt_root = root; + mnt->mnt_sb = root->d_sb; + pstore_mnt = mnt; + + return 0; +} + +static void pstore_kill_sb(struct super_block *sb) +{ + kill_litter_super(sb); + pstore_sb = NULL; + pstore_mnt = NULL; +} + +static struct file_system_type pstore_fs_type = { + .name = "pstore", + .get_sb = pstore_get_sb, + .kill_sb = pstore_kill_sb, +}; + +static int __init init_pstore_fs(void) +{ + int ret = 0; + struct kobject *pstorefs_kobj; + + pstorefs_kobj = kobject_create_and_add("pstore", fs_kobj); + if (!pstorefs_kobj) + return -ENOMEM; + + sysfs_create_file(pstorefs_kobj, &pstore_kmsg_bytes_attr.attr); + + ret = register_filesystem(&pstore_fs_type); + + if (ret) { + sysfs_remove_file(pstorefs_kobj, &pstore_kmsg_bytes_attr.attr); + kobject_put(pstorefs_kobj); + } + + return ret; +} +module_init(init_pstore_fs) + +MODULE_AUTHOR("Tony Luck "); +MODULE_LICENSE("GPL"); diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h new file mode 100644 index 000000000000..76c26d2fab29 --- /dev/null +++ b/fs/pstore/internal.h @@ -0,0 +1,7 @@ +extern void pstore_get_records(void); +extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id, + char *data, size_t size, + struct timespec time, int (*erase)(u64)); +extern int pstore_is_mounted(void); + +extern struct kobj_attribute pstore_kmsg_bytes_attr; diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c new file mode 100644 index 000000000000..705fdf8abf6e --- /dev/null +++ b/fs/pstore/platform.c @@ -0,0 +1,202 @@ +/* + * Persistent Storage - platform driver interface parts. + * + * Copyright (C) 2010 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +/* + * pstore_lock just protects "psinfo" during + * calls to pstore_register() + */ +static DEFINE_SPINLOCK(pstore_lock); +static struct pstore_info *psinfo; + +/* How much of the console log to snapshot. /sys/fs/pstore/kmsg_bytes */ +static unsigned long kmsg_bytes = 10240; + +static ssize_t b_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%lu\n", kmsg_bytes); +} + +static ssize_t b_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + return (sscanf(buf, "%lu", &kmsg_bytes) > 0) ? count : 0; +} + +struct kobj_attribute pstore_kmsg_bytes_attr = + __ATTR(kmsg_bytes, S_IRUGO | S_IWUSR, b_show, b_store); + +/* Tag each group of saved records with a sequence number */ +static int oopscount; + +/* + * callback from kmsg_dump. (s2,l2) has the most recently + * written bytes, older bytes are in (s1,l1). Save as much + * as we can from the end of the buffer. + */ +static void pstore_dump(struct kmsg_dumper *dumper, + enum kmsg_dump_reason reason, + const char *s1, unsigned long l1, + const char *s2, unsigned long l2) +{ + unsigned long s1_start, s2_start; + unsigned long l1_cpy, l2_cpy; + unsigned long size, total = 0; + char *dst; + u64 id; + int hsize, part = 1; + + mutex_lock(&psinfo->buf_mutex); + oopscount++; + while (total < kmsg_bytes) { + dst = psinfo->buf; + hsize = sprintf(dst, "Oops#%d Part%d\n", oopscount, part++); + size = psinfo->bufsize - hsize; + dst += hsize; + + l2_cpy = min(l2, size); + l1_cpy = min(l1, size - l2_cpy); + + if (l1_cpy + l2_cpy == 0) + break; + + s2_start = l2 - l2_cpy; + s1_start = l1 - l1_cpy; + + memcpy(dst, s1 + s1_start, l1_cpy); + memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy); + + id = psinfo->write(PSTORE_TYPE_DMESG, hsize + l1_cpy + l2_cpy); + if (pstore_is_mounted()) + pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, + psinfo->buf, hsize + l1_cpy + l2_cpy, + CURRENT_TIME, psinfo->erase); + l1 -= l1_cpy; + l2 -= l2_cpy; + total += l1_cpy + l2_cpy; + } + mutex_unlock(&psinfo->buf_mutex); +} + +static struct kmsg_dumper pstore_dumper = { + .dump = pstore_dump, +}; + +/* + * platform specific persistent storage driver registers with + * us here. If pstore is already mounted, call the platform + * read function right away to populate the file system. If not + * then the pstore mount code will call us later to fill out + * the file system. + * + * Register with kmsg_dump to save last part of console log on panic. + */ +int pstore_register(struct pstore_info *psi) +{ + struct module *owner = psi->owner; + + spin_lock(&pstore_lock); + if (psinfo) { + spin_unlock(&pstore_lock); + return -EBUSY; + } + psinfo = psi; + spin_unlock(&pstore_lock); + + if (owner && !try_module_get(owner)) { + psinfo = NULL; + return -EINVAL; + } + + if (pstore_is_mounted()) + pstore_get_records(); + + kmsg_dump_register(&pstore_dumper); + + return 0; +} +EXPORT_SYMBOL_GPL(pstore_register); + +/* + * Read all the records from the persistent store. Create and + * file files in our filesystem. + */ +void pstore_get_records(void) +{ + struct pstore_info *psi = psinfo; + size_t size; + u64 id; + enum pstore_type_id type; + struct timespec time; + int failed = 0; + + if (!psi) + return; + + mutex_lock(&psinfo->buf_mutex); + while ((size = psi->read(&id, &type, &time)) > 0) { + if (pstore_mkfile(type, psi->name, id, psi->buf, size, + time, psi->erase)) + failed++; + } + mutex_unlock(&psinfo->buf_mutex); + + if (failed) + printk(KERN_WARNING "pstore: failed to load %d record(s) from '%s'\n", + failed, psi->name); +} + +/* + * Call platform driver to write a record to the + * persistent store. + */ +int pstore_write(enum pstore_type_id type, char *buf, size_t size) +{ + u64 id; + + if (!psinfo) + return -ENODEV; + + if (size > psinfo->bufsize) + return -EFBIG; + + mutex_lock(&psinfo->buf_mutex); + memcpy(psinfo->buf, buf, size); + id = psinfo->write(type, size); + if (pstore_is_mounted()) + pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, psinfo->buf, + size, CURRENT_TIME, psinfo->erase); + mutex_unlock(&psinfo->buf_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(pstore_write); diff --git a/include/linux/magic.h b/include/linux/magic.h index ff690d05f129..e87fd5ac3e5e 100644 --- a/include/linux/magic.h +++ b/include/linux/magic.h @@ -26,6 +26,7 @@ #define ISOFS_SUPER_MAGIC 0x9660 #define JFFS2_SUPER_MAGIC 0x72b6 #define ANON_INODE_FS_MAGIC 0x09041934 +#define PSTOREFS_MAGIC 0x6165676C #define MINIX_SUPER_MAGIC 0x137F /* original minix fs */ #define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */ diff --git a/include/linux/pstore.h b/include/linux/pstore.h new file mode 100644 index 000000000000..41977737bb7d --- /dev/null +++ b/include/linux/pstore.h @@ -0,0 +1,60 @@ +/* + * Persistent Storage - pstore.h + * + * Copyright (C) 2010 Intel Corporation + * + * This code is the generic layer to export data records from platform + * level persistent storage via a file system. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _LINUX_PSTORE_H +#define _LINUX_PSTORE_H + +/* types */ +enum pstore_type_id { + PSTORE_TYPE_DMESG = 0, + PSTORE_TYPE_MCE = 1, + PSTORE_TYPE_UNKNOWN = 255 +}; + +struct pstore_info { + struct module *owner; + char *name; + struct mutex buf_mutex; /* serialize access to 'buf' */ + char *buf; + size_t bufsize; + size_t (*read)(u64 *id, enum pstore_type_id *type, + struct timespec *time); + u64 (*write)(enum pstore_type_id type, size_t size); + int (*erase)(u64 id); +}; + +#ifdef CONFIG_PSTORE +extern int pstore_register(struct pstore_info *); +extern int pstore_write(enum pstore_type_id type, char *buf, size_t size); +#else +static inline int +pstore_register(struct pstore_info *psi) +{ + return -ENODEV; +} +static inline int +pstore_write(enum pstore_type_id type, char *buf, size_t size) +{ + return -ENODEV; +} +#endif + +#endif /*_LINUX_PSTORE_H*/ -- cgit v1.2.3 From faff4bb067d15a3bc0dde8c50cbc1a7075e314de Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 7 Jan 2011 22:36:11 -0700 Subject: ASoC: Export debugfs root dentry A couple Tegra ASoC drivers will create debugfs entries. Mark requested these by under debugfs/asoc/ not just debugfs/. To enable this, export the dentry representing debugfs/asoc/. Also, rename debugfs_root -> asoc_debugfs_root now it's exported to prevent potential symbol name clashes. Signed-off-by: Stephen Warren Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc.h | 4 ++++ sound/soc/soc-core.c | 19 ++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 74921f20a1d8..96aadbba85b2 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -756,4 +756,8 @@ static inline void *snd_soc_pcm_get_drvdata(struct snd_soc_pcm_runtime *rtd) #include +#ifdef CONFIG_DEBUG_FS +extern struct dentry *asoc_debugfs_root; +#endif + #endif diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4274435853df..0484e504b589 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -48,7 +48,8 @@ static DEFINE_MUTEX(pcm_mutex); static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); #ifdef CONFIG_DEBUG_FS -static struct dentry *debugfs_root; +struct dentry *asoc_debugfs_root; +EXPORT_SYMBOL_GPL(asoc_debugfs_root); #endif static DEFINE_MUTEX(client_mutex); @@ -360,7 +361,7 @@ static const struct file_operations platform_list_fops = { static void soc_init_card_debugfs(struct snd_soc_card *card) { card->debugfs_card_root = debugfs_create_dir(card->name, - debugfs_root); + asoc_debugfs_root); if (!card->debugfs_card_root) { dev_warn(card->dev, "ASoC: Failed to create codec debugfs directory\n"); @@ -3583,22 +3584,22 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_codec); static int __init snd_soc_init(void) { #ifdef CONFIG_DEBUG_FS - debugfs_root = debugfs_create_dir("asoc", NULL); - if (IS_ERR(debugfs_root) || !debugfs_root) { + asoc_debugfs_root = debugfs_create_dir("asoc", NULL); + if (IS_ERR(asoc_debugfs_root) || !asoc_debugfs_root) { printk(KERN_WARNING "ASoC: Failed to create debugfs directory\n"); - debugfs_root = NULL; + asoc_debugfs_root = NULL; } - if (!debugfs_create_file("codecs", 0444, debugfs_root, NULL, + if (!debugfs_create_file("codecs", 0444, asoc_debugfs_root, NULL, &codec_list_fops)) pr_warn("ASoC: Failed to create CODEC list debugfs file\n"); - if (!debugfs_create_file("dais", 0444, debugfs_root, NULL, + if (!debugfs_create_file("dais", 0444, asoc_debugfs_root, NULL, &dai_list_fops)) pr_warn("ASoC: Failed to create DAI list debugfs file\n"); - if (!debugfs_create_file("platforms", 0444, debugfs_root, NULL, + if (!debugfs_create_file("platforms", 0444, asoc_debugfs_root, NULL, &platform_list_fops)) pr_warn("ASoC: Failed to create platform list debugfs file\n"); #endif @@ -3610,7 +3611,7 @@ module_init(snd_soc_init); static void __exit snd_soc_exit(void) { #ifdef CONFIG_DEBUG_FS - debugfs_remove_recursive(debugfs_root); + debugfs_remove_recursive(asoc_debugfs_root); #endif platform_driver_unregister(&soc_driver); } -- cgit v1.2.3 From 8a9dab1a555e3f2088c68cae792dfd7e854e65e4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 10 Jan 2011 22:25:21 +0000 Subject: ASoC: Update name of debugfs root symbol to snd_soc_ Everything else is using snd_soc_ so we should use it here too. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 2 +- sound/soc/soc-core.c | 20 ++++++++++---------- sound/soc/tegra/tegra_das.c | 5 +++-- sound/soc/tegra/tegra_i2s.c | 2 +- 4 files changed, 15 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 96aadbba85b2..c477058ff98a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -757,7 +757,7 @@ static inline void *snd_soc_pcm_get_drvdata(struct snd_soc_pcm_runtime *rtd) #include #ifdef CONFIG_DEBUG_FS -extern struct dentry *asoc_debugfs_root; +extern struct dentry *snd_soc_debugfs_root; #endif #endif diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0484e504b589..2ff708a41119 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -48,8 +48,8 @@ static DEFINE_MUTEX(pcm_mutex); static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); #ifdef CONFIG_DEBUG_FS -struct dentry *asoc_debugfs_root; -EXPORT_SYMBOL_GPL(asoc_debugfs_root); +struct dentry *snd_soc_debugfs_root; +EXPORT_SYMBOL_GPL(snd_soc_debugfs_root); #endif static DEFINE_MUTEX(client_mutex); @@ -361,7 +361,7 @@ static const struct file_operations platform_list_fops = { static void soc_init_card_debugfs(struct snd_soc_card *card) { card->debugfs_card_root = debugfs_create_dir(card->name, - asoc_debugfs_root); + snd_soc_debugfs_root); if (!card->debugfs_card_root) { dev_warn(card->dev, "ASoC: Failed to create codec debugfs directory\n"); @@ -3584,22 +3584,22 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_codec); static int __init snd_soc_init(void) { #ifdef CONFIG_DEBUG_FS - asoc_debugfs_root = debugfs_create_dir("asoc", NULL); - if (IS_ERR(asoc_debugfs_root) || !asoc_debugfs_root) { + snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL); + if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) { printk(KERN_WARNING "ASoC: Failed to create debugfs directory\n"); - asoc_debugfs_root = NULL; + snd_soc_debugfs_root = NULL; } - if (!debugfs_create_file("codecs", 0444, asoc_debugfs_root, NULL, + if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL, &codec_list_fops)) pr_warn("ASoC: Failed to create CODEC list debugfs file\n"); - if (!debugfs_create_file("dais", 0444, asoc_debugfs_root, NULL, + if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL, &dai_list_fops)) pr_warn("ASoC: Failed to create DAI list debugfs file\n"); - if (!debugfs_create_file("platforms", 0444, asoc_debugfs_root, NULL, + if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL, &platform_list_fops)) pr_warn("ASoC: Failed to create platform list debugfs file\n"); #endif @@ -3611,7 +3611,7 @@ module_init(snd_soc_init); static void __exit snd_soc_exit(void) { #ifdef CONFIG_DEBUG_FS - debugfs_remove_recursive(asoc_debugfs_root); + debugfs_remove_recursive(snd_soc_debugfs_root); #endif platform_driver_unregister(&soc_driver); } diff --git a/sound/soc/tegra/tegra_das.c b/sound/soc/tegra/tegra_das.c index 796d36d5188a..01eb9c9301de 100644 --- a/sound/soc/tegra/tegra_das.c +++ b/sound/soc/tegra/tegra_das.c @@ -144,8 +144,9 @@ static const struct file_operations tegra_das_debug_fops = { static void tegra_das_debug_add(struct tegra_das *das) { - das->debug = debugfs_create_file(DRV_NAME, S_IRUGO, asoc_debugfs_root, - das, &tegra_das_debug_fops); + das->debug = debugfs_create_file(DRV_NAME, S_IRUGO, + snd_soc_debugfs_root, das, + &tegra_das_debug_fops); } static void tegra_das_debug_remove(struct tegra_das *das) diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c index 9b7a22af52a6..1730509c8ac2 100644 --- a/sound/soc/tegra/tegra_i2s.c +++ b/sound/soc/tegra/tegra_i2s.c @@ -104,7 +104,7 @@ static void tegra_i2s_debug_add(struct tegra_i2s *i2s, int id) char name[] = DRV_NAME ".0"; snprintf(name, sizeof(name), DRV_NAME".%1d", id); - i2s->debug = debugfs_create_file(name, S_IRUGO, asoc_debugfs_root, + i2s->debug = debugfs_create_file(name, S_IRUGO, snd_soc_debugfs_root, i2s, &tegra_i2s_debug_fops); } -- cgit v1.2.3 From aea170a099793abcd0e6de46b947458073204241 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Wed, 12 Jan 2011 10:38:58 +0000 Subject: ASoC: soc-cache: Add reg_size as a member to snd_soc_codec Simplify the use of reg_size, by calculating it once and storing it in the codec structure for later reference. The value of reg_size is reg_cache_size * reg_word_size. Signed-off-by: Dimitris Papastamos Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-cache.c | 26 ++++++++------------------ sound/soc/soc-core.c | 1 + 3 files changed, 10 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index c477058ff98a..d609232da82a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -459,6 +459,7 @@ struct snd_soc_codec { struct list_head card_list; int num_dai; enum snd_soc_compress_type compress_type; + size_t reg_size; /* reg_cache_size * reg_word_size */ /* runtime */ struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 19b29fb3ca4b..b2e333f5a388 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -1100,34 +1100,28 @@ static inline int snd_soc_lzo_get_blkindex(struct snd_soc_codec *codec, unsigned int reg) { const struct snd_soc_codec_driver *codec_drv; - size_t reg_size; codec_drv = codec->driver; - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; return (reg * codec_drv->reg_word_size) / - DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()); + DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()); } static inline int snd_soc_lzo_get_blkpos(struct snd_soc_codec *codec, unsigned int reg) { const struct snd_soc_codec_driver *codec_drv; - size_t reg_size; codec_drv = codec->driver; - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; - return reg % (DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()) / + return reg % (DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()) / codec_drv->reg_word_size); } static inline int snd_soc_lzo_get_blksize(struct snd_soc_codec *codec) { const struct snd_soc_codec_driver *codec_drv; - size_t reg_size; codec_drv = codec->driver; - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; - return DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()); + return DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()); } static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec) @@ -1287,7 +1281,7 @@ static int snd_soc_lzo_cache_exit(struct snd_soc_codec *codec) static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) { struct snd_soc_lzo_ctx **lzo_blocks; - size_t reg_size, bmp_size; + size_t bmp_size; const struct snd_soc_codec_driver *codec_drv; int ret, tofree, i, blksize, blkcount; const char *p, *end; @@ -1295,7 +1289,6 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) ret = 0; codec_drv = codec->driver; - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; /* * If we have not been given a default register cache @@ -1307,8 +1300,7 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) tofree = 1; if (!codec->reg_def_copy) { - codec->reg_def_copy = kzalloc(reg_size, - GFP_KERNEL); + codec->reg_def_copy = kzalloc(codec->reg_size, GFP_KERNEL); if (!codec->reg_def_copy) return -ENOMEM; } @@ -1356,7 +1348,7 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) blksize = snd_soc_lzo_get_blksize(codec); p = codec->reg_def_copy; - end = codec->reg_def_copy + reg_size; + end = codec->reg_def_copy + codec->reg_size; /* compress the register map and fill the lzo blocks */ for (i = 0; i < blkcount; ++i, p += blksize) { lzo_blocks[i]->src = p; @@ -1441,16 +1433,14 @@ static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec) static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) { const struct snd_soc_codec_driver *codec_drv; - size_t reg_size; codec_drv = codec->driver; - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; if (codec->reg_def_copy) codec->reg_cache = kmemdup(codec->reg_def_copy, - reg_size, GFP_KERNEL); + codec->reg_size, GFP_KERNEL); else - codec->reg_cache = kzalloc(reg_size, GFP_KERNEL); + codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL); if (!codec->reg_cache) return -ENOMEM; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 04475c18003c..cbac50b69c39 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3500,6 +3500,7 @@ int snd_soc_register_codec(struct device *dev, /* allocate CODEC register cache */ if (codec_drv->reg_cache_size && codec_drv->reg_word_size) { reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; + codec->reg_size = reg_size; /* it is necessary to make a copy of the default register cache * because in the case of using a compression type that requires * the default register cache to be marked as __devinitconst the -- cgit v1.2.3 From 61b1ab4583e275af216c8454b9256de680499b19 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:42 +0100 Subject: IPVS: netns, add basic init per netns. Preparation for network name-space init, in this stage some empty functions exists. In most files there is a check if it is root ns i.e. init_net if (!net_eq(net, &init_net)) return ... this will be removed by the last patch, when enabling name-space. *v3 ip_vs_conn.c merge error corrected. net_ipvs #ifdef removed as sugested by Jan Engelhardt [ horms@verge.net.au: Removed whitespace-change-only hunks ] Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 11 +++++++ include/net/net_namespace.h | 2 ++ include/net/netns/ip_vs.h | 25 ++++++++++++++++ net/netfilter/ipvs/ip_vs_app.c | 28 +++++++++++++++--- net/netfilter/ipvs/ip_vs_conn.c | 34 ++++++++++++++++++---- net/netfilter/ipvs/ip_vs_core.c | 63 ++++++++++++++++++++++++++++++++++++++-- net/netfilter/ipvs/ip_vs_ctl.c | 49 +++++++++++++++++++++++++------ net/netfilter/ipvs/ip_vs_est.c | 20 ++++++++++++- net/netfilter/ipvs/ip_vs_ftp.c | 34 +++++++++++++++++++--- net/netfilter/ipvs/ip_vs_lblc.c | 37 +++++++++++++++++++++-- net/netfilter/ipvs/ip_vs_lblcr.c | 38 +++++++++++++++++++++--- net/netfilter/ipvs/ip_vs_proto.c | 19 ++++++++++++ net/netfilter/ipvs/ip_vs_sync.c | 27 +++++++++++++++++ 13 files changed, 354 insertions(+), 33 deletions(-) create mode 100644 include/net/netns/ip_vs.h (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index d858264217ba..c1c2ece3ed94 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -28,6 +28,15 @@ #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) #include #endif +#include /* Netw namespace */ + +/* + * Generic access of ipvs struct + */ +static inline struct netns_ipvs *net_ipvs(struct net* net) +{ + return net->ipvs; +} /* Connections' size value needed by ip_vs_ctl.c */ extern int ip_vs_conn_tab_size; @@ -922,6 +931,8 @@ extern char ip_vs_backup_mcast_ifn[IP_VS_IFNAME_MAXLEN]; extern int start_sync_thread(int state, char *mcast_ifn, __u8 syncid); extern int stop_sync_thread(int state); extern void ip_vs_sync_conn(struct ip_vs_conn *cp); +extern int ip_vs_sync_init(void); +extern void ip_vs_sync_cleanup(void); /* diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 1bf812b21fb7..b3b4a34cb2cc 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -20,6 +20,7 @@ #include #endif #include +#include struct proc_dir_entry; struct net_device; @@ -94,6 +95,7 @@ struct net { #ifdef CONFIG_XFRM struct netns_xfrm xfrm; #endif + struct netns_ipvs *ipvs; }; diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h new file mode 100644 index 000000000000..12fe84087cec --- /dev/null +++ b/include/net/netns/ip_vs.h @@ -0,0 +1,25 @@ +/* + * IP Virtual Server + * Data structure for network namspace + * + */ + +#ifndef IP_VS_H_ +#define IP_VS_H_ + +#include +#include +#include +#include +#include +#include + +struct ip_vs_stats; +struct ip_vs_sync_buff; +struct ctl_table_header; + +struct netns_ipvs { + int gen; /* Generation */ +}; + +#endif /* IP_VS_H_ */ diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index a475edee0912..40b09ccc4896 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -569,15 +569,35 @@ static const struct file_operations ip_vs_app_fops = { }; #endif -int __init ip_vs_app_init(void) +static int __net_init __ip_vs_app_init(struct net *net) { - /* we will replace it with proc_net_ipvs_create() soon */ - proc_net_fops_create(&init_net, "ip_vs_app", 0, &ip_vs_app_fops); + if (!net_eq(net, &init_net)) /* netns not enabled yet */ + return -EPERM; + + proc_net_fops_create(net, "ip_vs_app", 0, &ip_vs_app_fops); return 0; } +static void __net_exit __ip_vs_app_cleanup(struct net *net) +{ + proc_net_remove(net, "ip_vs_app"); +} + +static struct pernet_operations ip_vs_app_ops = { + .init = __ip_vs_app_init, + .exit = __ip_vs_app_cleanup, +}; + +int __init ip_vs_app_init(void) +{ + int rv; + + rv = register_pernet_subsys(&ip_vs_app_ops); + return rv; +} + void ip_vs_app_cleanup(void) { - proc_net_remove(&init_net, "ip_vs_app"); + unregister_pernet_subsys(&ip_vs_app_ops); } diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 66e4662925d5..7c1b502f8d8d 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1201,11 +1201,36 @@ static void ip_vs_conn_flush(void) goto flush_again; } } +/* + * per netns init and exit + */ +int __net_init __ip_vs_conn_init(struct net *net) +{ + if (!net_eq(net, &init_net)) /* netns not enabled yet */ + return -EPERM; + proc_net_fops_create(net, "ip_vs_conn", 0, &ip_vs_conn_fops); + proc_net_fops_create(net, "ip_vs_conn_sync", 0, &ip_vs_conn_sync_fops); + return 0; +} + +static void __net_exit __ip_vs_conn_cleanup(struct net *net) +{ + if (!net_eq(net, &init_net)) /* netns not enabled yet */ + return; + + proc_net_remove(net, "ip_vs_conn"); + proc_net_remove(net, "ip_vs_conn_sync"); +} +static struct pernet_operations ipvs_conn_ops = { + .init = __ip_vs_conn_init, + .exit = __ip_vs_conn_cleanup, +}; int __init ip_vs_conn_init(void) { int idx; + int retc; /* Compute size and mask */ ip_vs_conn_tab_size = 1 << ip_vs_conn_tab_bits; @@ -1243,24 +1268,21 @@ int __init ip_vs_conn_init(void) rwlock_init(&__ip_vs_conntbl_lock_array[idx].l); } - proc_net_fops_create(&init_net, "ip_vs_conn", 0, &ip_vs_conn_fops); - proc_net_fops_create(&init_net, "ip_vs_conn_sync", 0, &ip_vs_conn_sync_fops); + retc = register_pernet_subsys(&ipvs_conn_ops); /* calculate the random value for connection hash */ get_random_bytes(&ip_vs_conn_rnd, sizeof(ip_vs_conn_rnd)); - return 0; + return retc; } - void ip_vs_conn_cleanup(void) { + unregister_pernet_subsys(&ipvs_conn_ops); /* flush all the connection entries first */ ip_vs_conn_flush(); /* Release the empty cache */ kmem_cache_destroy(ip_vs_conn_cachep); - proc_net_remove(&init_net, "ip_vs_conn"); - proc_net_remove(&init_net, "ip_vs_conn_sync"); vfree(ip_vs_conn_tab); } diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 5287771d0647..206f40c548d7 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -41,6 +41,7 @@ #include /* for icmp_send */ #include #include +#include /* net_generic() */ #include #include @@ -68,6 +69,12 @@ EXPORT_SYMBOL(ip_vs_conn_put); EXPORT_SYMBOL(ip_vs_get_debug_level); #endif +int ip_vs_net_id __read_mostly; +#ifdef IP_VS_GENERIC_NETNS +EXPORT_SYMBOL(ip_vs_net_id); +#endif +/* netns cnt used for uniqueness */ +static atomic_t ipvs_netns_cnt = ATOMIC_INIT(0); /* ID used in ICMP lookups */ #define icmp_id(icmph) (((icmph)->un).echo.id) @@ -1813,6 +1820,44 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = { #endif }; +/* + * Initialize IP Virtual Server netns mem. + */ +static int __net_init __ip_vs_init(struct net *net) +{ + struct netns_ipvs *ipvs; + + if (!net_eq(net, &init_net)) { + pr_err("The final patch for enabling netns is missing\n"); + return -EPERM; + } + ipvs = net_generic(net, ip_vs_net_id); + if (ipvs == NULL) { + pr_err("%s(): no memory.\n", __func__); + return -ENOMEM; + } + /* Counters used for creating unique names */ + ipvs->gen = atomic_read(&ipvs_netns_cnt); + atomic_inc(&ipvs_netns_cnt); + net->ipvs = ipvs; + printk(KERN_INFO "IPVS: Creating netns size=%lu id=%d\n", + sizeof(struct netns_ipvs), ipvs->gen); + return 0; +} + +static void __net_exit __ip_vs_cleanup(struct net *net) +{ + struct netns_ipvs *ipvs = net_ipvs(net); + + IP_VS_DBG(10, "ipvs netns %d released\n", ipvs->gen); +} + +static struct pernet_operations ipvs_core_ops = { + .init = __ip_vs_init, + .exit = __ip_vs_cleanup, + .id = &ip_vs_net_id, + .size = sizeof(struct netns_ipvs), +}; /* * Initialize IP Virtual Server @@ -1821,8 +1866,11 @@ static int __init ip_vs_init(void) { int ret; - ip_vs_estimator_init(); + ret = register_pernet_subsys(&ipvs_core_ops); /* Alloc ip_vs struct */ + if (ret < 0) + return ret; + ip_vs_estimator_init(); ret = ip_vs_control_init(); if (ret < 0) { pr_err("can't setup control.\n"); @@ -1843,15 +1891,23 @@ static int __init ip_vs_init(void) goto cleanup_app; } + ret = ip_vs_sync_init(); + if (ret < 0) { + pr_err("can't setup sync data.\n"); + goto cleanup_conn; + } + ret = nf_register_hooks(ip_vs_ops, ARRAY_SIZE(ip_vs_ops)); if (ret < 0) { pr_err("can't register hooks.\n"); - goto cleanup_conn; + goto cleanup_sync; } pr_info("ipvs loaded.\n"); return ret; +cleanup_sync: + ip_vs_sync_cleanup(); cleanup_conn: ip_vs_conn_cleanup(); cleanup_app: @@ -1861,17 +1917,20 @@ static int __init ip_vs_init(void) ip_vs_control_cleanup(); cleanup_estimator: ip_vs_estimator_cleanup(); + unregister_pernet_subsys(&ipvs_core_ops); /* free ip_vs struct */ return ret; } static void __exit ip_vs_cleanup(void) { nf_unregister_hooks(ip_vs_ops, ARRAY_SIZE(ip_vs_ops)); + ip_vs_sync_cleanup(); ip_vs_conn_cleanup(); ip_vs_app_cleanup(); ip_vs_protocol_cleanup(); ip_vs_control_cleanup(); ip_vs_estimator_cleanup(); + unregister_pernet_subsys(&ipvs_core_ops); /* free ip_vs struct */ pr_info("ipvs unloaded.\n"); } diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index ca49e928f302..ceeef4352d34 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -3406,6 +3406,42 @@ static void ip_vs_genl_unregister(void) /* End of Generic Netlink interface definitions */ +/* + * per netns intit/exit func. + */ +int __net_init __ip_vs_control_init(struct net *net) +{ + if (!net_eq(net, &init_net)) /* netns not enabled yet */ + return -EPERM; + + proc_net_fops_create(net, "ip_vs", 0, &ip_vs_info_fops); + proc_net_fops_create(net, "ip_vs_stats", 0, &ip_vs_stats_fops); + sysctl_header = register_net_sysctl_table(net, net_vs_ctl_path, + vs_vars); + if (sysctl_header == NULL) + goto err_reg; + ip_vs_new_estimator(&ip_vs_stats); + return 0; + +err_reg: + return -ENOMEM; +} + +static void __net_exit __ip_vs_control_cleanup(struct net *net) +{ + if (!net_eq(net, &init_net)) /* netns not enabled yet */ + return; + + ip_vs_kill_estimator(&ip_vs_stats); + unregister_net_sysctl_table(sysctl_header); + proc_net_remove(net, "ip_vs_stats"); + proc_net_remove(net, "ip_vs"); +} + +static struct pernet_operations ipvs_control_ops = { + .init = __ip_vs_control_init, + .exit = __ip_vs_control_cleanup, +}; int __init ip_vs_control_init(void) { @@ -3437,12 +3473,9 @@ int __init ip_vs_control_init(void) return ret; } - proc_net_fops_create(&init_net, "ip_vs", 0, &ip_vs_info_fops); - proc_net_fops_create(&init_net, "ip_vs_stats",0, &ip_vs_stats_fops); - - sysctl_header = register_sysctl_paths(net_vs_ctl_path, vs_vars); - - ip_vs_new_estimator(&ip_vs_stats); + ret = register_pernet_subsys(&ipvs_control_ops); + if (ret) + return ret; /* Hook the defense timer */ schedule_delayed_work(&defense_work, DEFENSE_TIMER_PERIOD); @@ -3459,9 +3492,7 @@ void ip_vs_control_cleanup(void) cancel_delayed_work_sync(&defense_work); cancel_work_sync(&defense_work.work); ip_vs_kill_estimator(&ip_vs_stats); - unregister_sysctl_table(sysctl_header); - proc_net_remove(&init_net, "ip_vs_stats"); - proc_net_remove(&init_net, "ip_vs"); + unregister_pernet_subsys(&ipvs_control_ops); ip_vs_genl_unregister(); nf_unregister_sockopt(&ip_vs_sockopts); LeaveFunction(2); diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index ff28801962e0..7417a0c1408b 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -157,13 +157,31 @@ void ip_vs_zero_estimator(struct ip_vs_stats *stats) est->outbps = 0; } +static int __net_init __ip_vs_estimator_init(struct net *net) +{ + if (!net_eq(net, &init_net)) /* netns not enabled yet */ + return -EPERM; + + return 0; +} + +static struct pernet_operations ip_vs_app_ops = { + .init = __ip_vs_estimator_init, +}; + int __init ip_vs_estimator_init(void) { + int rv; + + rv = register_pernet_subsys(&ip_vs_app_ops); + if (rv < 0) + return rv; mod_timer(&est_timer, jiffies + 2 * HZ); - return 0; + return rv; } void ip_vs_estimator_cleanup(void) { del_timer_sync(&est_timer); + unregister_pernet_subsys(&ip_vs_app_ops); } diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index 84aef65b37d1..0e762f322aa3 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -399,15 +399,17 @@ static struct ip_vs_app ip_vs_ftp = { .pkt_in = ip_vs_ftp_in, }; - /* - * ip_vs_ftp initialization + * per netns ip_vs_ftp initialization */ -static int __init ip_vs_ftp_init(void) +static int __net_init __ip_vs_ftp_init(struct net *net) { int i, ret; struct ip_vs_app *app = &ip_vs_ftp; + if (!net_eq(net, &init_net)) /* netns not enabled yet */ + return -EPERM; + ret = register_ip_vs_app(app); if (ret) return ret; @@ -427,14 +429,38 @@ static int __init ip_vs_ftp_init(void) return ret; } +/* + * netns exit + */ +static void __ip_vs_ftp_exit(struct net *net) +{ + struct ip_vs_app *app = &ip_vs_ftp; + + if (!net_eq(net, &init_net)) /* netns not enabled yet */ + return; + + unregister_ip_vs_app(app); +} + +static struct pernet_operations ip_vs_ftp_ops = { + .init = __ip_vs_ftp_init, + .exit = __ip_vs_ftp_exit, +}; +int __init ip_vs_ftp_init(void) +{ + int rv; + + rv = register_pernet_subsys(&ip_vs_ftp_ops); + return rv; +} /* * ip_vs_ftp finish. */ static void __exit ip_vs_ftp_exit(void) { - unregister_ip_vs_app(&ip_vs_ftp); + unregister_pernet_subsys(&ip_vs_ftp_ops); } diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 9323f8944199..84278fb4e055 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -543,23 +543,54 @@ static struct ip_vs_scheduler ip_vs_lblc_scheduler = .schedule = ip_vs_lblc_schedule, }; +/* + * per netns init. + */ +static int __net_init __ip_vs_lblc_init(struct net *net) +{ + if (!net_eq(net, &init_net)) /* netns not enabled yet */ + return -EPERM; + + sysctl_header = register_net_sysctl_table(net, net_vs_ctl_path, + vs_vars_table); + if (!sysctl_header) + return -ENOMEM; + + return 0; +} + +static void __net_exit __ip_vs_lblc_exit(struct net *net) +{ + if (!net_eq(net, &init_net)) /* netns not enabled yet */ + return; + + unregister_net_sysctl_table(sysctl_header); +} + +static struct pernet_operations ip_vs_lblc_ops = { + .init = __ip_vs_lblc_init, + .exit = __ip_vs_lblc_exit, +}; static int __init ip_vs_lblc_init(void) { int ret; - sysctl_header = register_sysctl_paths(net_vs_ctl_path, vs_vars_table); + ret = register_pernet_subsys(&ip_vs_lblc_ops); + if (ret) + return ret; + ret = register_ip_vs_scheduler(&ip_vs_lblc_scheduler); if (ret) - unregister_sysctl_table(sysctl_header); + unregister_pernet_subsys(&ip_vs_lblc_ops); return ret; } static void __exit ip_vs_lblc_cleanup(void) { - unregister_sysctl_table(sysctl_header); unregister_ip_vs_scheduler(&ip_vs_lblc_scheduler); + unregister_pernet_subsys(&ip_vs_lblc_ops); } diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index dbeed8ea421a..7c7396a6acbf 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -744,23 +744,53 @@ static struct ip_vs_scheduler ip_vs_lblcr_scheduler = .schedule = ip_vs_lblcr_schedule, }; +/* + * per netns init. + */ +static int __net_init __ip_vs_lblcr_init(struct net *net) +{ + if (!net_eq(net, &init_net)) /* netns not enabled yet */ + return -EPERM; + + sysctl_header = register_net_sysctl_table(net, net_vs_ctl_path, + vs_vars_table); + if (!sysctl_header) + return -ENOMEM; + + return 0; +} + +static void __net_exit __ip_vs_lblcr_exit(struct net *net) +{ + if (!net_eq(net, &init_net)) /* netns not enabled yet */ + return; + + unregister_net_sysctl_table(sysctl_header); +} + +static struct pernet_operations ip_vs_lblcr_ops = { + .init = __ip_vs_lblcr_init, + .exit = __ip_vs_lblcr_exit, +}; static int __init ip_vs_lblcr_init(void) { int ret; - sysctl_header = register_sysctl_paths(net_vs_ctl_path, vs_vars_table); + ret = register_pernet_subsys(&ip_vs_lblcr_ops); + if (ret) + return ret; + ret = register_ip_vs_scheduler(&ip_vs_lblcr_scheduler); if (ret) - unregister_sysctl_table(sysctl_header); + unregister_pernet_subsys(&ip_vs_lblcr_ops); return ret; } - static void __exit ip_vs_lblcr_cleanup(void) { - unregister_sysctl_table(sysctl_header); unregister_ip_vs_scheduler(&ip_vs_lblcr_scheduler); + unregister_pernet_subsys(&ip_vs_lblcr_ops); } diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index c53998390877..45392942d0e7 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c @@ -236,6 +236,23 @@ ip_vs_tcpudp_debug_packet(int af, struct ip_vs_protocol *pp, ip_vs_tcpudp_debug_packet_v4(pp, skb, offset, msg); } +/* + * per network name-space init + */ +static int __net_init __ip_vs_protocol_init(struct net *net) +{ + return 0; +} + +static void __net_exit __ip_vs_protocol_cleanup(struct net *net) +{ + /* empty */ +} + +static struct pernet_operations ipvs_proto_ops = { + .init = __ip_vs_protocol_init, + .exit = __ip_vs_protocol_cleanup, +}; int __init ip_vs_protocol_init(void) { @@ -265,6 +282,7 @@ int __init ip_vs_protocol_init(void) REGISTER_PROTOCOL(&ip_vs_protocol_esp); #endif pr_info("Registered protocols (%s)\n", &protocols[2]); + return register_pernet_subsys(&ipvs_proto_ops); return 0; } @@ -275,6 +293,7 @@ void ip_vs_protocol_cleanup(void) struct ip_vs_protocol *pp; int i; + unregister_pernet_subsys(&ipvs_proto_ops); /* unregister all the ipvs protocols */ for (i = 0; i < IP_VS_PROTO_TAB_SIZE; i++) { while ((pp = ip_vs_proto_table[i]) != NULL) diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index c1c167ab73ee..3668739a6d06 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -1639,3 +1639,30 @@ int stop_sync_thread(int state) return 0; } + +/* + * Initialize data struct for each netns + */ +static int __net_init __ip_vs_sync_init(struct net *net) +{ + return 0; +} + +static void __ip_vs_sync_cleanup(struct net *net) +{ +} +static struct pernet_operations ipvs_sync_ops = { + .init = __ip_vs_sync_init, + .exit = __ip_vs_sync_cleanup, +}; + + +int __init ip_vs_sync_init(void) +{ + return register_pernet_subsys(&ipvs_sync_ops); +} + +void __exit ip_vs_sync_cleanup(void) +{ + unregister_pernet_subsys(&ipvs_sync_ops); +} -- cgit v1.2.3 From fc723250c9cb046cc19833a2b1c4309bbf59ac36 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:43 +0100 Subject: IPVS: netns to services part 1 Services hash tables got netns ptr a hash arg, While Real Servers (rs) has been moved to ipvs struct. Two new inline functions added to get net ptr from skb. Since ip_vs is called from different contexts there is two places to dig for the net ptr skb->dev or skb->sk this is handled in skb_net() and skb_sknet() Global functions, ip_vs_service_get() ip_vs_lookup_real_service() etc have got struct net *net as first param. If possible get net ptr skb etc, - if not &init_net is used at this early stage of patching. ip_vs_ctl.c procfs not ready for netns yet. *v3 Comments by Julian - __ip_vs_service_find and __ip_vs_svc_fwm_find are fast path, net_eq(svc->net, net) so the check is at the end now. - net = skb_net(skb) in ip_vs_out moved after check for skb_dst. Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 64 +++++++++- include/net/netns/ip_vs.h | 8 ++ net/netfilter/ipvs/ip_vs_conn.c | 2 +- net/netfilter/ipvs/ip_vs_core.c | 4 +- net/netfilter/ipvs/ip_vs_ctl.c | 232 +++++++++++++++++++--------------- net/netfilter/ipvs/ip_vs_proto_sctp.c | 5 +- net/netfilter/ipvs/ip_vs_proto_tcp.c | 7 +- net/netfilter/ipvs/ip_vs_proto_udp.c | 5 +- net/netfilter/ipvs/ip_vs_sync.c | 2 +- 9 files changed, 214 insertions(+), 115 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index c1c2ece3ed94..d551e0d8fd9a 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -37,6 +37,59 @@ static inline struct netns_ipvs *net_ipvs(struct net* net) { return net->ipvs; } +/* + * Get net ptr from skb in traffic cases + * use skb_sknet when call is from userland (ioctl or netlink) + */ +static inline struct net *skb_net(struct sk_buff *skb) +{ +#ifdef CONFIG_NET_NS +#ifdef CONFIG_IP_VS_DEBUG + /* + * This is used for debug only. + * Start with the most likely hit + * End with BUG + */ + if (likely(skb->dev && skb->dev->nd_net)) + return dev_net(skb->dev); + if (skb_dst(skb)->dev) + return dev_net(skb_dst(skb)->dev); + WARN(skb->sk, "Maybe skb_sknet should be used in %s() at line:%d\n", + __func__, __LINE__); + if (likely(skb->sk && skb->sk->sk_net)) + return sock_net(skb->sk); + pr_err("There is no net ptr to find in the skb in %s() line:%d\n", + __func__, __LINE__); + BUG(); +#else + return dev_net(skb->dev ? : skb_dst(skb)->dev); +#endif +#else + return &init_net; +#endif +} + +static inline struct net *skb_sknet(struct sk_buff *skb) +{ +#ifdef CONFIG_NET_NS +#ifdef CONFIG_IP_VS_DEBUG + /* Start with the most likely hit */ + if (likely(skb->sk && skb->sk->sk_net)) + return sock_net(skb->sk); + WARN(skb->dev, "Maybe skb_net should be used instead in %s() line:%d\n", + __func__, __LINE__); + if (likely(skb->dev && skb->dev->nd_net)) + return dev_net(skb->dev); + pr_err("There is no net ptr to find in the skb in %s() line:%d\n", + __func__, __LINE__); + BUG(); +#else + return sock_net(skb->sk); +#endif +#else + return &init_net; +#endif +} /* Connections' size value needed by ip_vs_ctl.c */ extern int ip_vs_conn_tab_size; @@ -496,6 +549,7 @@ struct ip_vs_service { unsigned flags; /* service status flags */ unsigned timeout; /* persistent timeout in ticks */ __be32 netmask; /* grouping granularity */ + struct net *net; struct list_head destinations; /* real server d-linked list */ __u32 num_dests; /* number of servers */ @@ -896,7 +950,7 @@ extern int sysctl_ip_vs_sync_ver; extern void ip_vs_sync_switch_mode(int mode); extern struct ip_vs_service * -ip_vs_service_get(int af, __u32 fwmark, __u16 protocol, +ip_vs_service_get(struct net *net, int af, __u32 fwmark, __u16 protocol, const union nf_inet_addr *vaddr, __be16 vport); static inline void ip_vs_service_put(struct ip_vs_service *svc) @@ -905,7 +959,7 @@ static inline void ip_vs_service_put(struct ip_vs_service *svc) } extern struct ip_vs_dest * -ip_vs_lookup_real_service(int af, __u16 protocol, +ip_vs_lookup_real_service(struct net *net, int af, __u16 protocol, const union nf_inet_addr *daddr, __be16 dport); extern int ip_vs_use_count_inc(void); @@ -913,9 +967,9 @@ extern void ip_vs_use_count_dec(void); extern int ip_vs_control_init(void); extern void ip_vs_control_cleanup(void); extern struct ip_vs_dest * -ip_vs_find_dest(int af, const union nf_inet_addr *daddr, __be16 dport, - const union nf_inet_addr *vaddr, __be16 vport, __u16 protocol, - __u32 fwmark); +ip_vs_find_dest(struct net *net, int af, const union nf_inet_addr *daddr, + __be16 dport, const union nf_inet_addr *vaddr, __be16 vport, + __u16 protocol, __u32 fwmark); extern struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp); diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h index 12fe84087cec..5b87d22a39fb 100644 --- a/include/net/netns/ip_vs.h +++ b/include/net/netns/ip_vs.h @@ -20,6 +20,14 @@ struct ctl_table_header; struct netns_ipvs { int gen; /* Generation */ + /* + * Hash table: for real service lookups + */ + #define IP_VS_RTAB_BITS 4 + #define IP_VS_RTAB_SIZE (1 << IP_VS_RTAB_BITS) + #define IP_VS_RTAB_MASK (IP_VS_RTAB_SIZE - 1) + + struct list_head rs_table[IP_VS_RTAB_SIZE]; }; #endif /* IP_VS_H_ */ diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 7c1b502f8d8d..7a0e79e3ad0f 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -611,7 +611,7 @@ struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp) struct ip_vs_dest *dest; if ((cp) && (!cp->dest)) { - dest = ip_vs_find_dest(cp->af, &cp->daddr, cp->dport, + dest = ip_vs_find_dest(&init_net, cp->af, &cp->daddr, cp->dport, &cp->vaddr, cp->vport, cp->protocol, cp->fwmark); ip_vs_bind_dest(cp, dest); diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 206f40c548d7..d0616ea1eebf 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1031,6 +1031,7 @@ drop: static unsigned int ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) { + struct net *net = NULL; struct ip_vs_iphdr iph; struct ip_vs_protocol *pp; struct ip_vs_conn *cp; @@ -1054,6 +1055,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) if (unlikely(!skb_dst(skb))) return NF_ACCEPT; + net = skb_net(skb); ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) { @@ -1119,7 +1121,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) sizeof(_ports), _ports); if (pptr == NULL) return NF_ACCEPT; /* Not for me */ - if (ip_vs_lookup_real_service(af, iph.protocol, + if (ip_vs_lookup_real_service(net, af, iph.protocol, &iph.saddr, pptr[0])) { /* diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index ceeef4352d34..2d7c96bd2114 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -287,15 +287,6 @@ static struct list_head ip_vs_svc_table[IP_VS_SVC_TAB_SIZE]; /* the service table hashed by fwmark */ static struct list_head ip_vs_svc_fwm_table[IP_VS_SVC_TAB_SIZE]; -/* - * Hash table: for real service lookups - */ -#define IP_VS_RTAB_BITS 4 -#define IP_VS_RTAB_SIZE (1 << IP_VS_RTAB_BITS) -#define IP_VS_RTAB_MASK (IP_VS_RTAB_SIZE - 1) - -static struct list_head ip_vs_rtable[IP_VS_RTAB_SIZE]; - /* * Trash for destinations */ @@ -311,9 +302,9 @@ static atomic_t ip_vs_nullsvc_counter = ATOMIC_INIT(0); /* * Returns hash value for virtual service */ -static __inline__ unsigned -ip_vs_svc_hashkey(int af, unsigned proto, const union nf_inet_addr *addr, - __be16 port) +static inline unsigned +ip_vs_svc_hashkey(struct net *net, int af, unsigned proto, + const union nf_inet_addr *addr, __be16 port) { register unsigned porth = ntohs(port); __be32 addr_fold = addr->ip; @@ -323,6 +314,7 @@ ip_vs_svc_hashkey(int af, unsigned proto, const union nf_inet_addr *addr, addr_fold = addr->ip6[0]^addr->ip6[1]^ addr->ip6[2]^addr->ip6[3]; #endif + addr_fold ^= ((size_t)net>>8); return (proto^ntohl(addr_fold)^(porth>>IP_VS_SVC_TAB_BITS)^porth) & IP_VS_SVC_TAB_MASK; @@ -331,13 +323,13 @@ ip_vs_svc_hashkey(int af, unsigned proto, const union nf_inet_addr *addr, /* * Returns hash value of fwmark for virtual service lookup */ -static __inline__ unsigned ip_vs_svc_fwm_hashkey(__u32 fwmark) +static inline unsigned ip_vs_svc_fwm_hashkey(struct net *net, __u32 fwmark) { - return fwmark & IP_VS_SVC_TAB_MASK; + return (((size_t)net>>8) ^ fwmark) & IP_VS_SVC_TAB_MASK; } /* - * Hashes a service in the ip_vs_svc_table by + * Hashes a service in the ip_vs_svc_table by * or in the ip_vs_svc_fwm_table by fwmark. * Should be called with locked tables. */ @@ -353,16 +345,16 @@ static int ip_vs_svc_hash(struct ip_vs_service *svc) if (svc->fwmark == 0) { /* - * Hash it by in ip_vs_svc_table + * Hash it by in ip_vs_svc_table */ - hash = ip_vs_svc_hashkey(svc->af, svc->protocol, &svc->addr, - svc->port); + hash = ip_vs_svc_hashkey(svc->net, svc->af, svc->protocol, + &svc->addr, svc->port); list_add(&svc->s_list, &ip_vs_svc_table[hash]); } else { /* - * Hash it by fwmark in ip_vs_svc_fwm_table + * Hash it by fwmark in svc_fwm_table */ - hash = ip_vs_svc_fwm_hashkey(svc->fwmark); + hash = ip_vs_svc_fwm_hashkey(svc->net, svc->fwmark); list_add(&svc->f_list, &ip_vs_svc_fwm_table[hash]); } @@ -374,7 +366,7 @@ static int ip_vs_svc_hash(struct ip_vs_service *svc) /* - * Unhashes a service from ip_vs_svc_table/ip_vs_svc_fwm_table. + * Unhashes a service from svc_table / svc_fwm_table. * Should be called with locked tables. */ static int ip_vs_svc_unhash(struct ip_vs_service *svc) @@ -386,10 +378,10 @@ static int ip_vs_svc_unhash(struct ip_vs_service *svc) } if (svc->fwmark == 0) { - /* Remove it from the ip_vs_svc_table table */ + /* Remove it from the svc_table table */ list_del(&svc->s_list); } else { - /* Remove it from the ip_vs_svc_fwm_table table */ + /* Remove it from the svc_fwm_table table */ list_del(&svc->f_list); } @@ -400,23 +392,24 @@ static int ip_vs_svc_unhash(struct ip_vs_service *svc) /* - * Get service by {proto,addr,port} in the service table. + * Get service by {netns, proto,addr,port} in the service table. */ static inline struct ip_vs_service * -__ip_vs_service_find(int af, __u16 protocol, const union nf_inet_addr *vaddr, - __be16 vport) +__ip_vs_service_find(struct net *net, int af, __u16 protocol, + const union nf_inet_addr *vaddr, __be16 vport) { unsigned hash; struct ip_vs_service *svc; /* Check for "full" addressed entries */ - hash = ip_vs_svc_hashkey(af, protocol, vaddr, vport); + hash = ip_vs_svc_hashkey(net, af, protocol, vaddr, vport); list_for_each_entry(svc, &ip_vs_svc_table[hash], s_list){ if ((svc->af == af) && ip_vs_addr_equal(af, &svc->addr, vaddr) && (svc->port == vport) - && (svc->protocol == protocol)) { + && (svc->protocol == protocol) + && net_eq(svc->net, net)) { /* HIT */ return svc; } @@ -430,16 +423,17 @@ __ip_vs_service_find(int af, __u16 protocol, const union nf_inet_addr *vaddr, * Get service by {fwmark} in the service table. */ static inline struct ip_vs_service * -__ip_vs_svc_fwm_find(int af, __u32 fwmark) +__ip_vs_svc_fwm_find(struct net *net, int af, __u32 fwmark) { unsigned hash; struct ip_vs_service *svc; /* Check for fwmark addressed entries */ - hash = ip_vs_svc_fwm_hashkey(fwmark); + hash = ip_vs_svc_fwm_hashkey(net, fwmark); list_for_each_entry(svc, &ip_vs_svc_fwm_table[hash], f_list) { - if (svc->fwmark == fwmark && svc->af == af) { + if (svc->fwmark == fwmark && svc->af == af + && net_eq(svc->net, net)) { /* HIT */ return svc; } @@ -449,7 +443,7 @@ __ip_vs_svc_fwm_find(int af, __u32 fwmark) } struct ip_vs_service * -ip_vs_service_get(int af, __u32 fwmark, __u16 protocol, +ip_vs_service_get(struct net *net, int af, __u32 fwmark, __u16 protocol, const union nf_inet_addr *vaddr, __be16 vport) { struct ip_vs_service *svc; @@ -459,14 +453,15 @@ ip_vs_service_get(int af, __u32 fwmark, __u16 protocol, /* * Check the table hashed by fwmark first */ - if (fwmark && (svc = __ip_vs_svc_fwm_find(af, fwmark))) + svc = __ip_vs_svc_fwm_find(net, af, fwmark); + if (fwmark && svc) goto out; /* * Check the table hashed by * for "full" addressed entries */ - svc = __ip_vs_service_find(af, protocol, vaddr, vport); + svc = __ip_vs_service_find(net, af, protocol, vaddr, vport); if (svc == NULL && protocol == IPPROTO_TCP @@ -476,7 +471,7 @@ ip_vs_service_get(int af, __u32 fwmark, __u16 protocol, * Check if ftp service entry exists, the packet * might belong to FTP data connections. */ - svc = __ip_vs_service_find(af, protocol, vaddr, FTPPORT); + svc = __ip_vs_service_find(net, af, protocol, vaddr, FTPPORT); } if (svc == NULL @@ -484,7 +479,7 @@ ip_vs_service_get(int af, __u32 fwmark, __u16 protocol, /* * Check if the catch-all port (port zero) exists */ - svc = __ip_vs_service_find(af, protocol, vaddr, 0); + svc = __ip_vs_service_find(net, af, protocol, vaddr, 0); } out: @@ -545,10 +540,10 @@ static inline unsigned ip_vs_rs_hashkey(int af, } /* - * Hashes ip_vs_dest in ip_vs_rtable by . + * Hashes ip_vs_dest in rs_table by . * should be called with locked tables. */ -static int ip_vs_rs_hash(struct ip_vs_dest *dest) +static int ip_vs_rs_hash(struct netns_ipvs *ipvs, struct ip_vs_dest *dest) { unsigned hash; @@ -562,19 +557,19 @@ static int ip_vs_rs_hash(struct ip_vs_dest *dest) */ hash = ip_vs_rs_hashkey(dest->af, &dest->addr, dest->port); - list_add(&dest->d_list, &ip_vs_rtable[hash]); + list_add(&dest->d_list, &ipvs->rs_table[hash]); return 1; } /* - * UNhashes ip_vs_dest from ip_vs_rtable. + * UNhashes ip_vs_dest from rs_table. * should be called with locked tables. */ static int ip_vs_rs_unhash(struct ip_vs_dest *dest) { /* - * Remove it from the ip_vs_rtable table. + * Remove it from the rs_table table. */ if (!list_empty(&dest->d_list)) { list_del(&dest->d_list); @@ -588,10 +583,11 @@ static int ip_vs_rs_unhash(struct ip_vs_dest *dest) * Lookup real service by in the real service table. */ struct ip_vs_dest * -ip_vs_lookup_real_service(int af, __u16 protocol, +ip_vs_lookup_real_service(struct net *net, int af, __u16 protocol, const union nf_inet_addr *daddr, __be16 dport) { + struct netns_ipvs *ipvs = net_ipvs(net); unsigned hash; struct ip_vs_dest *dest; @@ -602,7 +598,7 @@ ip_vs_lookup_real_service(int af, __u16 protocol, hash = ip_vs_rs_hashkey(af, daddr, dport); read_lock(&__ip_vs_rs_lock); - list_for_each_entry(dest, &ip_vs_rtable[hash], d_list) { + list_for_each_entry(dest, &ipvs->rs_table[hash], d_list) { if ((dest->af == af) && ip_vs_addr_equal(af, &dest->addr, daddr) && (dest->port == dport) @@ -652,7 +648,8 @@ ip_vs_lookup_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr, * ip_vs_lookup_real_service() looked promissing, but * seems not working as expected. */ -struct ip_vs_dest *ip_vs_find_dest(int af, const union nf_inet_addr *daddr, +struct ip_vs_dest *ip_vs_find_dest(struct net *net, int af, + const union nf_inet_addr *daddr, __be16 dport, const union nf_inet_addr *vaddr, __be16 vport, __u16 protocol, __u32 fwmark) @@ -660,7 +657,7 @@ struct ip_vs_dest *ip_vs_find_dest(int af, const union nf_inet_addr *daddr, struct ip_vs_dest *dest; struct ip_vs_service *svc; - svc = ip_vs_service_get(af, fwmark, protocol, vaddr, vport); + svc = ip_vs_service_get(net, af, fwmark, protocol, vaddr, vport); if (!svc) return NULL; dest = ip_vs_lookup_dest(svc, daddr, dport); @@ -768,6 +765,7 @@ static void __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, struct ip_vs_dest_user_kern *udest, int add) { + struct netns_ipvs *ipvs = net_ipvs(svc->net); int conn_flags; /* set the weight and the flags */ @@ -780,11 +778,11 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, conn_flags |= IP_VS_CONN_F_NOOUTPUT; } else { /* - * Put the real service in ip_vs_rtable if not present. + * Put the real service in rs_table if not present. * For now only for NAT! */ write_lock_bh(&__ip_vs_rs_lock); - ip_vs_rs_hash(dest); + ip_vs_rs_hash(ipvs, dest); write_unlock_bh(&__ip_vs_rs_lock); } atomic_set(&dest->conn_flags, conn_flags); @@ -1117,7 +1115,7 @@ ip_vs_del_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) * Add a service into the service hash table */ static int -ip_vs_add_service(struct ip_vs_service_user_kern *u, +ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u, struct ip_vs_service **svc_p) { int ret = 0; @@ -1172,6 +1170,7 @@ ip_vs_add_service(struct ip_vs_service_user_kern *u, svc->flags = u->flags; svc->timeout = u->timeout * HZ; svc->netmask = u->netmask; + svc->net = net; INIT_LIST_HEAD(&svc->destinations); rwlock_init(&svc->sched_lock); @@ -1428,17 +1427,19 @@ static int ip_vs_del_service(struct ip_vs_service *svc) /* * Flush all the virtual services */ -static int ip_vs_flush(void) +static int ip_vs_flush(struct net *net) { int idx; struct ip_vs_service *svc, *nxt; /* - * Flush the service table hashed by + * Flush the service table hashed by */ for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { - list_for_each_entry_safe(svc, nxt, &ip_vs_svc_table[idx], s_list) { - ip_vs_unlink_service(svc); + list_for_each_entry_safe(svc, nxt, &ip_vs_svc_table[idx], + s_list) { + if (net_eq(svc->net, net)) + ip_vs_unlink_service(svc); } } @@ -1448,7 +1449,8 @@ static int ip_vs_flush(void) for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { list_for_each_entry_safe(svc, nxt, &ip_vs_svc_fwm_table[idx], f_list) { - ip_vs_unlink_service(svc); + if (net_eq(svc->net, net)) + ip_vs_unlink_service(svc); } } @@ -1472,20 +1474,22 @@ static int ip_vs_zero_service(struct ip_vs_service *svc) return 0; } -static int ip_vs_zero_all(void) +static int ip_vs_zero_all(struct net *net) { int idx; struct ip_vs_service *svc; for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) { - ip_vs_zero_service(svc); + if (net_eq(svc->net, net)) + ip_vs_zero_service(svc); } } for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) { - ip_vs_zero_service(svc); + if (net_eq(svc->net, net)) + ip_vs_zero_service(svc); } } @@ -1763,6 +1767,7 @@ static struct ctl_table_header * sysctl_header; #ifdef CONFIG_PROC_FS struct ip_vs_iter { + struct seq_net_private p; /* Do not move this, netns depends upon it*/ struct list_head *table; int bucket; }; @@ -1789,6 +1794,7 @@ static inline const char *ip_vs_fwd_name(unsigned flags) /* Get the Nth entry in the two lists */ static struct ip_vs_service *ip_vs_info_array(struct seq_file *seq, loff_t pos) { + struct net *net = seq_file_net(seq); struct ip_vs_iter *iter = seq->private; int idx; struct ip_vs_service *svc; @@ -1796,7 +1802,7 @@ static struct ip_vs_service *ip_vs_info_array(struct seq_file *seq, loff_t pos) /* look in hash by protocol */ for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) { - if (pos-- == 0){ + if (net_eq(svc->net, net) && pos-- == 0) { iter->table = ip_vs_svc_table; iter->bucket = idx; return svc; @@ -1807,7 +1813,7 @@ static struct ip_vs_service *ip_vs_info_array(struct seq_file *seq, loff_t pos) /* keep looking in fwmark */ for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) { - if (pos-- == 0) { + if (net_eq(svc->net, net) && pos-- == 0) { iter->table = ip_vs_svc_fwm_table; iter->bucket = idx; return svc; @@ -1961,7 +1967,7 @@ static const struct seq_operations ip_vs_info_seq_ops = { static int ip_vs_info_open(struct inode *inode, struct file *file) { - return seq_open_private(file, &ip_vs_info_seq_ops, + return seq_open_net(inode, file, &ip_vs_info_seq_ops, sizeof(struct ip_vs_iter)); } @@ -2011,7 +2017,7 @@ static int ip_vs_stats_show(struct seq_file *seq, void *v) static int ip_vs_stats_seq_open(struct inode *inode, struct file *file) { - return single_open(file, ip_vs_stats_show, NULL); + return single_open_net(inode, file, ip_vs_stats_show); } static const struct file_operations ip_vs_stats_fops = { @@ -2113,6 +2119,7 @@ static void ip_vs_copy_udest_compat(struct ip_vs_dest_user_kern *udest, static int do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) { + struct net *net = sock_net(sk); int ret; unsigned char arg[MAX_ARG_LEN]; struct ip_vs_service_user *usvc_compat; @@ -2147,7 +2154,7 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) if (cmd == IP_VS_SO_SET_FLUSH) { /* Flush the virtual service */ - ret = ip_vs_flush(); + ret = ip_vs_flush(net); goto out_unlock; } else if (cmd == IP_VS_SO_SET_TIMEOUT) { /* Set timeout values for (tcp tcpfin udp) */ @@ -2174,7 +2181,7 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) if (cmd == IP_VS_SO_SET_ZERO) { /* if no service address is set, zero counters in all */ if (!usvc.fwmark && !usvc.addr.ip && !usvc.port) { - ret = ip_vs_zero_all(); + ret = ip_vs_zero_all(net); goto out_unlock; } } @@ -2191,10 +2198,10 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) /* Lookup the exact service by or fwmark */ if (usvc.fwmark == 0) - svc = __ip_vs_service_find(usvc.af, usvc.protocol, + svc = __ip_vs_service_find(net, usvc.af, usvc.protocol, &usvc.addr, usvc.port); else - svc = __ip_vs_svc_fwm_find(usvc.af, usvc.fwmark); + svc = __ip_vs_svc_fwm_find(net, usvc.af, usvc.fwmark); if (cmd != IP_VS_SO_SET_ADD && (svc == NULL || svc->protocol != usvc.protocol)) { @@ -2207,7 +2214,7 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) if (svc != NULL) ret = -EEXIST; else - ret = ip_vs_add_service(&usvc, &svc); + ret = ip_vs_add_service(net, &usvc, &svc); break; case IP_VS_SO_SET_EDIT: ret = ip_vs_edit_service(svc, &usvc); @@ -2267,7 +2274,8 @@ ip_vs_copy_service(struct ip_vs_service_entry *dst, struct ip_vs_service *src) } static inline int -__ip_vs_get_service_entries(const struct ip_vs_get_services *get, +__ip_vs_get_service_entries(struct net *net, + const struct ip_vs_get_services *get, struct ip_vs_get_services __user *uptr) { int idx, count=0; @@ -2278,7 +2286,7 @@ __ip_vs_get_service_entries(const struct ip_vs_get_services *get, for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) { /* Only expose IPv4 entries to old interface */ - if (svc->af != AF_INET) + if (svc->af != AF_INET || !net_eq(svc->net, net)) continue; if (count >= get->num_services) @@ -2297,7 +2305,7 @@ __ip_vs_get_service_entries(const struct ip_vs_get_services *get, for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) { /* Only expose IPv4 entries to old interface */ - if (svc->af != AF_INET) + if (svc->af != AF_INET || !net_eq(svc->net, net)) continue; if (count >= get->num_services) @@ -2317,7 +2325,7 @@ __ip_vs_get_service_entries(const struct ip_vs_get_services *get, } static inline int -__ip_vs_get_dest_entries(const struct ip_vs_get_dests *get, +__ip_vs_get_dest_entries(struct net *net, const struct ip_vs_get_dests *get, struct ip_vs_get_dests __user *uptr) { struct ip_vs_service *svc; @@ -2325,9 +2333,9 @@ __ip_vs_get_dest_entries(const struct ip_vs_get_dests *get, int ret = 0; if (get->fwmark) - svc = __ip_vs_svc_fwm_find(AF_INET, get->fwmark); + svc = __ip_vs_svc_fwm_find(net, AF_INET, get->fwmark); else - svc = __ip_vs_service_find(AF_INET, get->protocol, &addr, + svc = __ip_vs_service_find(net, AF_INET, get->protocol, &addr, get->port); if (svc) { @@ -2401,7 +2409,9 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) unsigned char arg[128]; int ret = 0; unsigned int copylen; + struct net *net = sock_net(sk); + BUG_ON(!net); if (!capable(CAP_NET_ADMIN)) return -EPERM; @@ -2463,7 +2473,7 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) ret = -EINVAL; goto out; } - ret = __ip_vs_get_service_entries(get, user); + ret = __ip_vs_get_service_entries(net, get, user); } break; @@ -2476,10 +2486,11 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) entry = (struct ip_vs_service_entry *)arg; addr.ip = entry->addr; if (entry->fwmark) - svc = __ip_vs_svc_fwm_find(AF_INET, entry->fwmark); + svc = __ip_vs_svc_fwm_find(net, AF_INET, entry->fwmark); else - svc = __ip_vs_service_find(AF_INET, entry->protocol, - &addr, entry->port); + svc = __ip_vs_service_find(net, AF_INET, + entry->protocol, &addr, + entry->port); if (svc) { ip_vs_copy_service(entry, svc); if (copy_to_user(user, entry, sizeof(*entry)) != 0) @@ -2502,7 +2513,7 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) ret = -EINVAL; goto out; } - ret = __ip_vs_get_dest_entries(get, user); + ret = __ip_vs_get_dest_entries(net, get, user); } break; @@ -2722,11 +2733,12 @@ static int ip_vs_genl_dump_services(struct sk_buff *skb, int idx = 0, i; int start = cb->args[0]; struct ip_vs_service *svc; + struct net *net = skb_sknet(skb); mutex_lock(&__ip_vs_mutex); for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) { list_for_each_entry(svc, &ip_vs_svc_table[i], s_list) { - if (++idx <= start) + if (++idx <= start || !net_eq(svc->net, net)) continue; if (ip_vs_genl_dump_service(skb, svc, cb) < 0) { idx--; @@ -2737,7 +2749,7 @@ static int ip_vs_genl_dump_services(struct sk_buff *skb, for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) { list_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) { - if (++idx <= start) + if (++idx <= start || !net_eq(svc->net, net)) continue; if (ip_vs_genl_dump_service(skb, svc, cb) < 0) { idx--; @@ -2753,7 +2765,8 @@ nla_put_failure: return skb->len; } -static int ip_vs_genl_parse_service(struct ip_vs_service_user_kern *usvc, +static int ip_vs_genl_parse_service(struct net *net, + struct ip_vs_service_user_kern *usvc, struct nlattr *nla, int full_entry, struct ip_vs_service **ret_svc) { @@ -2796,9 +2809,9 @@ static int ip_vs_genl_parse_service(struct ip_vs_service_user_kern *usvc, } if (usvc->fwmark) - svc = __ip_vs_svc_fwm_find(usvc->af, usvc->fwmark); + svc = __ip_vs_svc_fwm_find(net, usvc->af, usvc->fwmark); else - svc = __ip_vs_service_find(usvc->af, usvc->protocol, + svc = __ip_vs_service_find(net, usvc->af, usvc->protocol, &usvc->addr, usvc->port); *ret_svc = svc; @@ -2835,13 +2848,14 @@ static int ip_vs_genl_parse_service(struct ip_vs_service_user_kern *usvc, return 0; } -static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla) +static struct ip_vs_service *ip_vs_genl_find_service(struct net *net, + struct nlattr *nla) { struct ip_vs_service_user_kern usvc; struct ip_vs_service *svc; int ret; - ret = ip_vs_genl_parse_service(&usvc, nla, 0, &svc); + ret = ip_vs_genl_parse_service(net, &usvc, nla, 0, &svc); return ret ? ERR_PTR(ret) : svc; } @@ -2909,6 +2923,7 @@ static int ip_vs_genl_dump_dests(struct sk_buff *skb, struct ip_vs_service *svc; struct ip_vs_dest *dest; struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1]; + struct net *net; mutex_lock(&__ip_vs_mutex); @@ -2917,7 +2932,8 @@ static int ip_vs_genl_dump_dests(struct sk_buff *skb, IPVS_CMD_ATTR_MAX, ip_vs_cmd_policy)) goto out_err; - svc = ip_vs_genl_find_service(attrs[IPVS_CMD_ATTR_SERVICE]); + net = skb_sknet(skb); + svc = ip_vs_genl_find_service(net, attrs[IPVS_CMD_ATTR_SERVICE]); if (IS_ERR(svc) || svc == NULL) goto out_err; @@ -3102,13 +3118,15 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) struct ip_vs_dest_user_kern udest; int ret = 0, cmd; int need_full_svc = 0, need_full_dest = 0; + struct net *net; + net = skb_sknet(skb); cmd = info->genlhdr->cmd; mutex_lock(&__ip_vs_mutex); if (cmd == IPVS_CMD_FLUSH) { - ret = ip_vs_flush(); + ret = ip_vs_flush(net); goto out; } else if (cmd == IPVS_CMD_SET_CONFIG) { ret = ip_vs_genl_set_config(info->attrs); @@ -3133,7 +3151,7 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) goto out; } else if (cmd == IPVS_CMD_ZERO && !info->attrs[IPVS_CMD_ATTR_SERVICE]) { - ret = ip_vs_zero_all(); + ret = ip_vs_zero_all(net); goto out; } @@ -3143,7 +3161,7 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) if (cmd == IPVS_CMD_NEW_SERVICE || cmd == IPVS_CMD_SET_SERVICE) need_full_svc = 1; - ret = ip_vs_genl_parse_service(&usvc, + ret = ip_vs_genl_parse_service(net, &usvc, info->attrs[IPVS_CMD_ATTR_SERVICE], need_full_svc, &svc); if (ret) @@ -3173,7 +3191,7 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) switch (cmd) { case IPVS_CMD_NEW_SERVICE: if (svc == NULL) - ret = ip_vs_add_service(&usvc, &svc); + ret = ip_vs_add_service(net, &usvc, &svc); else ret = -EEXIST; break; @@ -3211,7 +3229,9 @@ static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info) struct sk_buff *msg; void *reply; int ret, cmd, reply_cmd; + struct net *net; + net = skb_sknet(skb); cmd = info->genlhdr->cmd; if (cmd == IPVS_CMD_GET_SERVICE) @@ -3240,7 +3260,8 @@ static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info) { struct ip_vs_service *svc; - svc = ip_vs_genl_find_service(info->attrs[IPVS_CMD_ATTR_SERVICE]); + svc = ip_vs_genl_find_service(net, + info->attrs[IPVS_CMD_ATTR_SERVICE]); if (IS_ERR(svc)) { ret = PTR_ERR(svc); goto out_err; @@ -3411,9 +3432,15 @@ static void ip_vs_genl_unregister(void) */ int __net_init __ip_vs_control_init(struct net *net) { + int idx; + struct netns_ipvs *ipvs = net_ipvs(net); + if (!net_eq(net, &init_net)) /* netns not enabled yet */ return -EPERM; + for (idx = 0; idx < IP_VS_RTAB_SIZE; idx++) + INIT_LIST_HEAD(&ipvs->rs_table[idx]); + proc_net_fops_create(net, "ip_vs", 0, &ip_vs_info_fops); proc_net_fops_create(net, "ip_vs_stats", 0, &ip_vs_stats_fops); sysctl_header = register_net_sysctl_table(net, net_vs_ctl_path, @@ -3445,43 +3472,48 @@ static struct pernet_operations ipvs_control_ops = { int __init ip_vs_control_init(void) { - int ret; int idx; + int ret; EnterFunction(2); - /* Initialize ip_vs_svc_table, ip_vs_svc_fwm_table, ip_vs_rtable */ + /* Initialize svc_table, ip_vs_svc_fwm_table, rs_table */ for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { INIT_LIST_HEAD(&ip_vs_svc_table[idx]); INIT_LIST_HEAD(&ip_vs_svc_fwm_table[idx]); } - for(idx = 0; idx < IP_VS_RTAB_SIZE; idx++) { - INIT_LIST_HEAD(&ip_vs_rtable[idx]); + + ret = register_pernet_subsys(&ipvs_control_ops); + if (ret) { + pr_err("cannot register namespace.\n"); + goto err; } - smp_wmb(); + + smp_wmb(); /* Do we really need it now ? */ ret = nf_register_sockopt(&ip_vs_sockopts); if (ret) { pr_err("cannot register sockopt.\n"); - return ret; + goto err_net; } ret = ip_vs_genl_register(); if (ret) { pr_err("cannot register Generic Netlink interface.\n"); nf_unregister_sockopt(&ip_vs_sockopts); - return ret; + goto err_net; } - ret = register_pernet_subsys(&ipvs_control_ops); - if (ret) - return ret; - /* Hook the defense timer */ schedule_delayed_work(&defense_work, DEFENSE_TIMER_PERIOD); LeaveFunction(2); return 0; + +err_net: + unregister_pernet_subsys(&ipvs_control_ops); +err: + return ret; } diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index a315159983ad..521b827083fe 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -12,6 +12,7 @@ static int sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, int *verdict, struct ip_vs_conn **cpp) { + struct net *net; struct ip_vs_service *svc; sctp_chunkhdr_t _schunkh, *sch; sctp_sctphdr_t *sh, _sctph; @@ -27,9 +28,9 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, sizeof(_schunkh), &_schunkh); if (sch == NULL) return 0; - + net = skb_net(skb); if ((sch->type == SCTP_CID_INIT) && - (svc = ip_vs_service_get(af, skb->mark, iph.protocol, + (svc = ip_vs_service_get(net, af, skb->mark, iph.protocol, &iph.daddr, sh->dest))) { int ignored; diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 1cdab12abfef..c175d3166263 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -31,6 +31,7 @@ static int tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, int *verdict, struct ip_vs_conn **cpp) { + struct net *net; struct ip_vs_service *svc; struct tcphdr _tcph, *th; struct ip_vs_iphdr iph; @@ -42,11 +43,11 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, *verdict = NF_DROP; return 0; } - + net = skb_net(skb); /* No !th->ack check to allow scheduling on SYN+ACK for Active FTP */ if (th->syn && - (svc = ip_vs_service_get(af, skb->mark, iph.protocol, &iph.daddr, - th->dest))) { + (svc = ip_vs_service_get(net, af, skb->mark, iph.protocol, + &iph.daddr, th->dest))) { int ignored; if (ip_vs_todrop()) { diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index cd398de010cc..5ab54f648654 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -31,6 +31,7 @@ static int udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, int *verdict, struct ip_vs_conn **cpp) { + struct net *net; struct ip_vs_service *svc; struct udphdr _udph, *uh; struct ip_vs_iphdr iph; @@ -42,8 +43,8 @@ udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, *verdict = NF_DROP; return 0; } - - svc = ip_vs_service_get(af, skb->mark, iph.protocol, + net = skb_net(skb); + svc = ip_vs_service_get(net, af, skb->mark, iph.protocol, &iph.daddr, uh->dest); if (svc) { int ignored; diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 3668739a6d06..662aa2c22a05 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -749,7 +749,7 @@ static void ip_vs_proc_conn(struct ip_vs_conn_param *param, unsigned flags, * If it is not found the connection will remain unbound * but still handled. */ - dest = ip_vs_find_dest(type, daddr, dport, param->vaddr, + dest = ip_vs_find_dest(&init_net, type, daddr, dport, param->vaddr, param->vport, protocol, fwmark); /* Set the approprite ativity flag */ -- cgit v1.2.3 From d0a1eef9c38218af20c809b2220a960b7ed81a36 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:44 +0100 Subject: IPVS: netns awarness to lblcr sheduler var sysctl_ip_vs_lblcr_expiration moved to ipvs struct as sysctl_lblcr_expiration procfs updated to handle this. Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/netns/ip_vs.h | 5 ++++ net/netfilter/ipvs/ip_vs_lblcr.c | 54 ++++++++++++++++++++++++++-------------- 2 files changed, 41 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h index 5b87d22a39fb..51a92ee1b167 100644 --- a/include/net/netns/ip_vs.h +++ b/include/net/netns/ip_vs.h @@ -28,6 +28,11 @@ struct netns_ipvs { #define IP_VS_RTAB_MASK (IP_VS_RTAB_SIZE - 1) struct list_head rs_table[IP_VS_RTAB_SIZE]; + + /* ip_vs_lblcr */ + int sysctl_lblcr_expiration; + struct ctl_table_header *lblcr_ctl_header; + struct ctl_table *lblcr_ctl_table; }; #endif /* IP_VS_H_ */ diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index 7c7396a6acbf..61ae8cfcf0b4 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -70,8 +70,6 @@ * entries that haven't been touched for a day. */ #define COUNT_FOR_FULL_EXPIRATION 30 -static int sysctl_ip_vs_lblcr_expiration = 24*60*60*HZ; - /* * for IPVS lblcr entry hash table @@ -296,7 +294,7 @@ struct ip_vs_lblcr_table { static ctl_table vs_vars_table[] = { { .procname = "lblcr_expiration", - .data = &sysctl_ip_vs_lblcr_expiration, + .data = NULL, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, @@ -304,8 +302,6 @@ static ctl_table vs_vars_table[] = { { } }; -static struct ctl_table_header * sysctl_header; - static inline void ip_vs_lblcr_free(struct ip_vs_lblcr_entry *en) { list_del(&en->list); @@ -425,14 +421,15 @@ static inline void ip_vs_lblcr_full_check(struct ip_vs_service *svc) unsigned long now = jiffies; int i, j; struct ip_vs_lblcr_entry *en, *nxt; + struct netns_ipvs *ipvs = net_ipvs(svc->net); for (i=0, j=tbl->rover; isched_lock); list_for_each_entry_safe(en, nxt, &tbl->bucket[j], list) { - if (time_after(en->lastuse+sysctl_ip_vs_lblcr_expiration, - now)) + if (time_after(en->lastuse + + ipvs->sysctl_lblcr_expiration, now)) continue; ip_vs_lblcr_free(en); @@ -664,6 +661,7 @@ ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) read_lock(&svc->sched_lock); en = ip_vs_lblcr_get(svc->af, tbl, &iph.daddr); if (en) { + struct netns_ipvs *ipvs = net_ipvs(svc->net); /* We only hold a read lock, but this is atomic */ en->lastuse = jiffies; @@ -675,7 +673,7 @@ ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) /* More than one destination + enough time passed by, cleanup */ if (atomic_read(&en->set.size) > 1 && time_after(jiffies, en->set.lastmod + - sysctl_ip_vs_lblcr_expiration)) { + ipvs->sysctl_lblcr_expiration)) { struct ip_vs_dest *m; write_lock(&en->set.lock); @@ -749,23 +747,43 @@ static struct ip_vs_scheduler ip_vs_lblcr_scheduler = */ static int __net_init __ip_vs_lblcr_init(struct net *net) { - if (!net_eq(net, &init_net)) /* netns not enabled yet */ - return -EPERM; - - sysctl_header = register_net_sysctl_table(net, net_vs_ctl_path, - vs_vars_table); - if (!sysctl_header) - return -ENOMEM; + struct netns_ipvs *ipvs = net_ipvs(net); + + if (!net_eq(net, &init_net)) { + ipvs->lblcr_ctl_table = kmemdup(vs_vars_table, + sizeof(vs_vars_table), + GFP_KERNEL); + if (ipvs->lblcr_ctl_table == NULL) + goto err_dup; + } else + ipvs->lblcr_ctl_table = vs_vars_table; + ipvs->sysctl_lblcr_expiration = 24*60*60*HZ; + ipvs->lblcr_ctl_table[0].data = &ipvs->sysctl_lblcr_expiration; + + ipvs->lblcr_ctl_header = + register_net_sysctl_table(net, net_vs_ctl_path, + ipvs->lblcr_ctl_table); + if (!ipvs->lblcr_ctl_header) + goto err_reg; return 0; + +err_reg: + if (!net_eq(net, &init_net)) + kfree(ipvs->lblcr_ctl_table); + +err_dup: + return -ENOMEM; } static void __net_exit __ip_vs_lblcr_exit(struct net *net) { - if (!net_eq(net, &init_net)) /* netns not enabled yet */ - return; + struct netns_ipvs *ipvs = net_ipvs(net); + + unregister_net_sysctl_table(ipvs->lblcr_ctl_header); - unregister_net_sysctl_table(sysctl_header); + if (!net_eq(net, &init_net)) + kfree(ipvs->lblcr_ctl_table); } static struct pernet_operations ip_vs_lblcr_ops = { -- cgit v1.2.3 From b6e885ddb903e681b7cbb4e68ad775154660e1f4 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:45 +0100 Subject: IPVS: netns awarness to lblc sheduler var sysctl_ip_vs_lblc_expiration moved to ipvs struct as sysctl_lblc_expiration procfs updated to handle this. Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/netns/ip_vs.h | 4 ++++ net/netfilter/ipvs/ip_vs_lblc.c | 50 ++++++++++++++++++++++++++++------------- 2 files changed, 38 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h index 51a92ee1b167..d14581cc4fe0 100644 --- a/include/net/netns/ip_vs.h +++ b/include/net/netns/ip_vs.h @@ -29,6 +29,10 @@ struct netns_ipvs { struct list_head rs_table[IP_VS_RTAB_SIZE]; + /* ip_vs_lblc */ + int sysctl_lblc_expiration; + struct ctl_table_header *lblc_ctl_header; + struct ctl_table *lblc_ctl_table; /* ip_vs_lblcr */ int sysctl_lblcr_expiration; struct ctl_table_header *lblcr_ctl_header; diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 84278fb4e055..d5bec3371871 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -70,7 +70,6 @@ * entries that haven't been touched for a day. */ #define COUNT_FOR_FULL_EXPIRATION 30 -static int sysctl_ip_vs_lblc_expiration = 24*60*60*HZ; /* @@ -117,7 +116,7 @@ struct ip_vs_lblc_table { static ctl_table vs_vars_table[] = { { .procname = "lblc_expiration", - .data = &sysctl_ip_vs_lblc_expiration, + .data = NULL, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, @@ -125,8 +124,6 @@ static ctl_table vs_vars_table[] = { { } }; -static struct ctl_table_header * sysctl_header; - static inline void ip_vs_lblc_free(struct ip_vs_lblc_entry *en) { list_del(&en->list); @@ -248,6 +245,7 @@ static inline void ip_vs_lblc_full_check(struct ip_vs_service *svc) struct ip_vs_lblc_entry *en, *nxt; unsigned long now = jiffies; int i, j; + struct netns_ipvs *ipvs = net_ipvs(svc->net); for (i=0, j=tbl->rover; isched_lock); list_for_each_entry_safe(en, nxt, &tbl->bucket[j], list) { if (time_before(now, - en->lastuse + sysctl_ip_vs_lblc_expiration)) + en->lastuse + + ipvs->sysctl_lblc_expiration)) continue; ip_vs_lblc_free(en); @@ -548,23 +547,43 @@ static struct ip_vs_scheduler ip_vs_lblc_scheduler = */ static int __net_init __ip_vs_lblc_init(struct net *net) { - if (!net_eq(net, &init_net)) /* netns not enabled yet */ - return -EPERM; - - sysctl_header = register_net_sysctl_table(net, net_vs_ctl_path, - vs_vars_table); - if (!sysctl_header) - return -ENOMEM; + struct netns_ipvs *ipvs = net_ipvs(net); + + if (!net_eq(net, &init_net)) { + ipvs->lblc_ctl_table = kmemdup(vs_vars_table, + sizeof(vs_vars_table), + GFP_KERNEL); + if (ipvs->lblc_ctl_table == NULL) + goto err_dup; + } else + ipvs->lblc_ctl_table = vs_vars_table; + ipvs->sysctl_lblc_expiration = 24*60*60*HZ; + ipvs->lblc_ctl_table[0].data = &ipvs->sysctl_lblc_expiration; + + ipvs->lblc_ctl_header = + register_net_sysctl_table(net, net_vs_ctl_path, + ipvs->lblc_ctl_table); + if (!ipvs->lblc_ctl_header) + goto err_reg; return 0; + +err_reg: + if (!net_eq(net, &init_net)) + kfree(ipvs->lblc_ctl_table); + +err_dup: + return -ENOMEM; } static void __net_exit __ip_vs_lblc_exit(struct net *net) { - if (!net_eq(net, &init_net)) /* netns not enabled yet */ - return; + struct netns_ipvs *ipvs = net_ipvs(net); + + unregister_net_sysctl_table(ipvs->lblc_ctl_header); - unregister_net_sysctl_table(sysctl_header); + if (!net_eq(net, &init_net)) + kfree(ipvs->lblc_ctl_table); } static struct pernet_operations ip_vs_lblc_ops = { @@ -586,7 +605,6 @@ static int __init ip_vs_lblc_init(void) return ret; } - static void __exit ip_vs_lblc_cleanup(void) { unregister_ip_vs_scheduler(&ip_vs_lblc_scheduler); -- cgit v1.2.3 From 252c64103237f1841088f0f29b4f084b1c774546 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:46 +0100 Subject: IPVS: netns, prepare protocol Add support for protocol data per name-space. in struct ip_vs_protocol, appcnt will be removed when all protos are modified for network name-space. This patch causes warnings of unused functions, they will be used when next patch will be applied. Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 20 +++++++++++- include/net/netns/ip_vs.h | 3 ++ net/netfilter/ipvs/ip_vs_proto.c | 66 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index d551e0d8fd9a..88d4e40b538a 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -352,6 +352,7 @@ struct iphdr; struct ip_vs_conn; struct ip_vs_app; struct sk_buff; +struct ip_vs_proto_data; struct ip_vs_protocol { struct ip_vs_protocol *next; @@ -366,6 +367,10 @@ struct ip_vs_protocol { void (*exit)(struct ip_vs_protocol *pp); + void (*init_netns)(struct net *net, struct ip_vs_proto_data *pd); + + void (*exit_netns)(struct net *net, struct ip_vs_proto_data *pd); + int (*conn_schedule)(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, int *verdict, struct ip_vs_conn **cpp); @@ -417,7 +422,20 @@ struct ip_vs_protocol { int (*set_state_timeout)(struct ip_vs_protocol *pp, char *sname, int to); }; -extern struct ip_vs_protocol * ip_vs_proto_get(unsigned short proto); +/* + * protocol data per netns + */ +struct ip_vs_proto_data { + struct ip_vs_proto_data *next; + struct ip_vs_protocol *pp; + int *timeout_table; /* protocol timeout table */ + atomic_t appcnt; /* counter of proto app incs. */ + struct tcp_states_t *tcp_state_table; +}; + +extern struct ip_vs_protocol *ip_vs_proto_get(unsigned short proto); +extern struct ip_vs_proto_data *ip_vs_proto_data_get(struct net *net, + unsigned short proto); struct ip_vs_conn_param { const union nf_inet_addr *caddr; diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h index d14581cc4fe0..6f4e089b8db2 100644 --- a/include/net/netns/ip_vs.h +++ b/include/net/netns/ip_vs.h @@ -28,6 +28,9 @@ struct netns_ipvs { #define IP_VS_RTAB_MASK (IP_VS_RTAB_SIZE - 1) struct list_head rs_table[IP_VS_RTAB_SIZE]; + /* ip_vs_proto */ + #define IP_VS_PROTO_TAB_SIZE 32 /* must be power of 2 */ + struct ip_vs_proto_data *proto_data_table[IP_VS_PROTO_TAB_SIZE]; /* ip_vs_lblc */ int sysctl_lblc_expiration; diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index 45392942d0e7..576e29648c53 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c @@ -60,6 +60,31 @@ static int __used __init register_ip_vs_protocol(struct ip_vs_protocol *pp) return 0; } +/* + * register an ipvs protocols netns related data + */ +static int +register_ip_vs_proto_netns(struct net *net, struct ip_vs_protocol *pp) +{ + struct netns_ipvs *ipvs = net_ipvs(net); + unsigned hash = IP_VS_PROTO_HASH(pp->protocol); + struct ip_vs_proto_data *pd = + kzalloc(sizeof(struct ip_vs_proto_data), GFP_ATOMIC); + + if (!pd) { + pr_err("%s(): no memory.\n", __func__); + return -ENOMEM; + } + pd->pp = pp; /* For speed issues */ + pd->next = ipvs->proto_data_table[hash]; + ipvs->proto_data_table[hash] = pd; + atomic_set(&pd->appcnt, 0); /* Init app counter */ + + if (pp->init_netns != NULL) + pp->init_netns(net, pd); + + return 0; +} /* * unregister an ipvs protocol @@ -82,6 +107,29 @@ static int unregister_ip_vs_protocol(struct ip_vs_protocol *pp) return -ESRCH; } +/* + * unregister an ipvs protocols netns data + */ +static int +unregister_ip_vs_proto_netns(struct net *net, struct ip_vs_proto_data *pd) +{ + struct netns_ipvs *ipvs = net_ipvs(net); + struct ip_vs_proto_data **pd_p; + unsigned hash = IP_VS_PROTO_HASH(pd->pp->protocol); + + pd_p = &ipvs->proto_data_table[hash]; + for (; *pd_p; pd_p = &(*pd_p)->next) { + if (*pd_p == pd) { + *pd_p = pd->next; + if (pd->pp->exit_netns != NULL) + pd->pp->exit_netns(net, pd); + kfree(pd); + return 0; + } + } + + return -ESRCH; +} /* * get ip_vs_protocol object by its proto. @@ -100,6 +148,24 @@ struct ip_vs_protocol * ip_vs_proto_get(unsigned short proto) } EXPORT_SYMBOL(ip_vs_proto_get); +/* + * get ip_vs_protocol object data by netns and proto + */ +struct ip_vs_proto_data * +ip_vs_proto_data_get(struct net *net, unsigned short proto) +{ + struct netns_ipvs *ipvs = net_ipvs(net); + struct ip_vs_proto_data *pd; + unsigned hash = IP_VS_PROTO_HASH(proto); + + for (pd = ipvs->proto_data_table[hash]; pd; pd = pd->next) { + if (pd->pp->protocol == proto) + return pd; + } + + return NULL; +} +EXPORT_SYMBOL(ip_vs_proto_data_get); /* * Propagate event for state change to all protocols -- cgit v1.2.3 From 4a85b96c08ef84076f84e87280223a4301988ed9 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:47 +0100 Subject: IPVS: netns preparation for proto_tcp In this phase (one), all local vars will be moved to ipvs struct. Remaining work, add param struct net *net to a couple of functions that is common for all protos and use all ip_vs_proto_data *v3 Removed unused function as sugested by Simon Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 2 +- include/net/netns/ip_vs.h | 8 +++ net/netfilter/ipvs/ip_vs_ftp.c | 8 ++- net/netfilter/ipvs/ip_vs_proto.c | 13 ++++- net/netfilter/ipvs/ip_vs_proto_tcp.c | 97 +++++++++++++++++++----------------- 5 files changed, 79 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 88d4e40b538a..3c45a00cdc3e 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -807,7 +807,7 @@ extern void ip_vs_conn_expire_now(struct ip_vs_conn *cp); extern const char * ip_vs_state_name(__u16 proto, int state); -extern void ip_vs_tcp_conn_listen(struct ip_vs_conn *cp); +extern void ip_vs_tcp_conn_listen(struct net *net, struct ip_vs_conn *cp); extern int ip_vs_check_template(struct ip_vs_conn *ct); extern void ip_vs_random_dropentry(void); extern int ip_vs_conn_init(void); diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h index 6f4e089b8db2..ac77363647ab 100644 --- a/include/net/netns/ip_vs.h +++ b/include/net/netns/ip_vs.h @@ -31,6 +31,14 @@ struct netns_ipvs { /* ip_vs_proto */ #define IP_VS_PROTO_TAB_SIZE 32 /* must be power of 2 */ struct ip_vs_proto_data *proto_data_table[IP_VS_PROTO_TAB_SIZE]; + /* ip_vs_proto_tcp */ +#ifdef CONFIG_IP_VS_PROTO_TCP + #define TCP_APP_TAB_BITS 4 + #define TCP_APP_TAB_SIZE (1 << TCP_APP_TAB_BITS) + #define TCP_APP_TAB_MASK (TCP_APP_TAB_SIZE - 1) + struct list_head tcp_apps[TCP_APP_TAB_SIZE]; + spinlock_t tcp_app_lock; +#endif /* ip_vs_lblc */ int sysctl_lblc_expiration; diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index 0e762f322aa3..b38ae941f677 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -157,6 +157,7 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, int ret = 0; enum ip_conntrack_info ctinfo; struct nf_conn *ct; + struct net *net; #ifdef CONFIG_IP_VS_IPV6 /* This application helper doesn't work with IPv6 yet, @@ -257,8 +258,9 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, * would be adjusted twice. */ + net = skb_net(skb); cp->app_data = NULL; - ip_vs_tcp_conn_listen(n_cp); + ip_vs_tcp_conn_listen(net, n_cp); ip_vs_conn_put(n_cp); return ret; } @@ -287,6 +289,7 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, union nf_inet_addr to; __be16 port; struct ip_vs_conn *n_cp; + struct net *net; #ifdef CONFIG_IP_VS_IPV6 /* This application helper doesn't work with IPv6 yet, @@ -378,7 +381,8 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, /* * Move tunnel to listen state */ - ip_vs_tcp_conn_listen(n_cp); + net = skb_net(skb); + ip_vs_tcp_conn_listen(net, n_cp); ip_vs_conn_put(n_cp); return 1; diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index 576e29648c53..320c6a65f370 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c @@ -307,12 +307,23 @@ ip_vs_tcpudp_debug_packet(int af, struct ip_vs_protocol *pp, */ static int __net_init __ip_vs_protocol_init(struct net *net) { +#ifdef CONFIG_IP_VS_PROTO_TCP + register_ip_vs_proto_netns(net, &ip_vs_protocol_tcp); +#endif return 0; } static void __net_exit __ip_vs_protocol_cleanup(struct net *net) { - /* empty */ + struct netns_ipvs *ipvs = net_ipvs(net); + struct ip_vs_proto_data *pd; + int i; + + /* unregister all the ipvs proto data for this netns */ + for (i = 0; i < IP_VS_PROTO_TAB_SIZE; i++) { + while ((pd = ipvs->proto_data_table[i]) != NULL) + unregister_ip_vs_proto_netns(net, pd); + } } static struct pernet_operations ipvs_proto_ops = { diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index c175d3166263..9d9df3d61093 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -9,8 +9,12 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Changes: + * Changes: Hans Schillstrom * + * Network name space (netns) aware. + * Global data moved to netns i.e struct netns_ipvs + * tcp_timeouts table has copy per netns in a hash table per + * protocol ip_vs_proto_data and is handled by netns */ #define KMSG_COMPONENT "IPVS" @@ -345,7 +349,7 @@ static const int tcp_state_off[IP_VS_DIR_LAST] = { /* * Timeout table[state] */ -static int tcp_timeouts[IP_VS_TCP_S_LAST+1] = { +static const int tcp_timeouts[IP_VS_TCP_S_LAST+1] = { [IP_VS_TCP_S_NONE] = 2*HZ, [IP_VS_TCP_S_ESTABLISHED] = 15*60*HZ, [IP_VS_TCP_S_SYN_SENT] = 2*60*HZ, @@ -460,13 +464,6 @@ static void tcp_timeout_change(struct ip_vs_protocol *pp, int flags) tcp_state_table = (on? tcp_states_dos : tcp_states); } -static int -tcp_set_state_timeout(struct ip_vs_protocol *pp, char *sname, int to) -{ - return ip_vs_set_state_timeout(pp->timeout_table, IP_VS_TCP_S_LAST, - tcp_state_name_table, sname, to); -} - static inline int tcp_state_idx(struct tcphdr *th) { if (th->rst) @@ -487,6 +484,7 @@ set_tcp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int state_idx; int new_state = IP_VS_TCP_S_CLOSE; int state_off = tcp_state_off[direction]; + struct ip_vs_proto_data *pd; /* Temp fix */ /* * Update state offset to INPUT_ONLY if necessary @@ -542,10 +540,13 @@ set_tcp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp, } } - cp->timeout = pp->timeout_table[cp->state = new_state]; + pd = ip_vs_proto_data_get(&init_net, pp->protocol); + if (likely(pd)) + cp->timeout = pd->timeout_table[cp->state = new_state]; + else /* What to do ? */ + cp->timeout = tcp_timeouts[cp->state = new_state]; } - /* * Handle state transitions */ @@ -573,17 +574,6 @@ tcp_state_transition(struct ip_vs_conn *cp, int direction, return 1; } - -/* - * Hash table for TCP application incarnations - */ -#define TCP_APP_TAB_BITS 4 -#define TCP_APP_TAB_SIZE (1 << TCP_APP_TAB_BITS) -#define TCP_APP_TAB_MASK (TCP_APP_TAB_SIZE - 1) - -static struct list_head tcp_apps[TCP_APP_TAB_SIZE]; -static DEFINE_SPINLOCK(tcp_app_lock); - static inline __u16 tcp_app_hashkey(__be16 port) { return (((__force u16)port >> TCP_APP_TAB_BITS) ^ (__force u16)port) @@ -597,21 +587,23 @@ static int tcp_register_app(struct ip_vs_app *inc) __u16 hash; __be16 port = inc->port; int ret = 0; + struct netns_ipvs *ipvs = net_ipvs(&init_net); + struct ip_vs_proto_data *pd = ip_vs_proto_data_get(&init_net, IPPROTO_TCP); hash = tcp_app_hashkey(port); - spin_lock_bh(&tcp_app_lock); - list_for_each_entry(i, &tcp_apps[hash], p_list) { + spin_lock_bh(&ipvs->tcp_app_lock); + list_for_each_entry(i, &ipvs->tcp_apps[hash], p_list) { if (i->port == port) { ret = -EEXIST; goto out; } } - list_add(&inc->p_list, &tcp_apps[hash]); - atomic_inc(&ip_vs_protocol_tcp.appcnt); + list_add(&inc->p_list, &ipvs->tcp_apps[hash]); + atomic_inc(&pd->pp->appcnt); out: - spin_unlock_bh(&tcp_app_lock); + spin_unlock_bh(&ipvs->tcp_app_lock); return ret; } @@ -619,16 +611,20 @@ static int tcp_register_app(struct ip_vs_app *inc) static void tcp_unregister_app(struct ip_vs_app *inc) { - spin_lock_bh(&tcp_app_lock); - atomic_dec(&ip_vs_protocol_tcp.appcnt); + struct netns_ipvs *ipvs = net_ipvs(&init_net); + struct ip_vs_proto_data *pd = ip_vs_proto_data_get(&init_net, IPPROTO_TCP); + + spin_lock_bh(&ipvs->tcp_app_lock); + atomic_dec(&pd->pp->appcnt); list_del(&inc->p_list); - spin_unlock_bh(&tcp_app_lock); + spin_unlock_bh(&ipvs->tcp_app_lock); } static int tcp_app_conn_bind(struct ip_vs_conn *cp) { + struct netns_ipvs *ipvs = net_ipvs(&init_net); int hash; struct ip_vs_app *inc; int result = 0; @@ -640,12 +636,12 @@ tcp_app_conn_bind(struct ip_vs_conn *cp) /* Lookup application incarnations and bind the right one */ hash = tcp_app_hashkey(cp->vport); - spin_lock(&tcp_app_lock); - list_for_each_entry(inc, &tcp_apps[hash], p_list) { + spin_lock(&ipvs->tcp_app_lock); + list_for_each_entry(inc, &ipvs->tcp_apps[hash], p_list) { if (inc->port == cp->vport) { if (unlikely(!ip_vs_app_inc_get(inc))) break; - spin_unlock(&tcp_app_lock); + spin_unlock(&ipvs->tcp_app_lock); IP_VS_DBG_BUF(9, "%s(): Binding conn %s:%u->" "%s:%u to app %s on port %u\n", @@ -662,7 +658,7 @@ tcp_app_conn_bind(struct ip_vs_conn *cp) goto out; } } - spin_unlock(&tcp_app_lock); + spin_unlock(&ipvs->tcp_app_lock); out: return result; @@ -672,24 +668,34 @@ tcp_app_conn_bind(struct ip_vs_conn *cp) /* * Set LISTEN timeout. (ip_vs_conn_put will setup timer) */ -void ip_vs_tcp_conn_listen(struct ip_vs_conn *cp) +void ip_vs_tcp_conn_listen(struct net *net, struct ip_vs_conn *cp) { + struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_TCP); + spin_lock(&cp->lock); cp->state = IP_VS_TCP_S_LISTEN; - cp->timeout = ip_vs_protocol_tcp.timeout_table[IP_VS_TCP_S_LISTEN]; + cp->timeout = (pd ? pd->timeout_table[IP_VS_TCP_S_LISTEN] + : tcp_timeouts[IP_VS_TCP_S_LISTEN]); spin_unlock(&cp->lock); } - -static void ip_vs_tcp_init(struct ip_vs_protocol *pp) +/* --------------------------------------------- + * timeouts is netns related now. + * --------------------------------------------- + */ +static void __ip_vs_tcp_init(struct net *net, struct ip_vs_proto_data *pd) { - IP_VS_INIT_HASH_TABLE(tcp_apps); - pp->timeout_table = tcp_timeouts; -} + struct netns_ipvs *ipvs = net_ipvs(net); + ip_vs_init_hash_table(ipvs->tcp_apps, TCP_APP_TAB_SIZE); + spin_lock_init(&ipvs->tcp_app_lock); + pd->timeout_table = ip_vs_create_timeout_table((int *)tcp_timeouts, + sizeof(tcp_timeouts)); +} -static void ip_vs_tcp_exit(struct ip_vs_protocol *pp) +static void __ip_vs_tcp_exit(struct net *net, struct ip_vs_proto_data *pd) { + kfree(pd->timeout_table); } @@ -699,8 +705,10 @@ struct ip_vs_protocol ip_vs_protocol_tcp = { .num_states = IP_VS_TCP_S_LAST, .dont_defrag = 0, .appcnt = ATOMIC_INIT(0), - .init = ip_vs_tcp_init, - .exit = ip_vs_tcp_exit, + .init = NULL, + .exit = NULL, + .init_netns = __ip_vs_tcp_init, + .exit_netns = __ip_vs_tcp_exit, .register_app = tcp_register_app, .unregister_app = tcp_unregister_app, .conn_schedule = tcp_conn_schedule, @@ -714,5 +722,4 @@ struct ip_vs_protocol ip_vs_protocol_tcp = { .app_conn_bind = tcp_app_conn_bind, .debug_packet = ip_vs_tcpudp_debug_packet, .timeout_change = tcp_timeout_change, - .set_state_timeout = tcp_set_state_timeout, }; -- cgit v1.2.3 From 78b16bde104cc74bedbf462b0ebed2990f35ff6b Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:48 +0100 Subject: IPVS: netns preparation for proto_udp In this phase (one), all local vars will be moved to ipvs struct. Remaining work, add param struct net *net to a couple of functions that is common for all protos and use ip_vs_proto_data *v3 Removed unused function set_state_timeout() Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/netns/ip_vs.h | 8 ++++ net/netfilter/ipvs/ip_vs_proto.c | 3 ++ net/netfilter/ipvs/ip_vs_proto_udp.c | 86 ++++++++++++++++++------------------ 3 files changed, 54 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h index ac77363647ab..62b1448d3795 100644 --- a/include/net/netns/ip_vs.h +++ b/include/net/netns/ip_vs.h @@ -39,6 +39,14 @@ struct netns_ipvs { struct list_head tcp_apps[TCP_APP_TAB_SIZE]; spinlock_t tcp_app_lock; #endif + /* ip_vs_proto_udp */ +#ifdef CONFIG_IP_VS_PROTO_UDP + #define UDP_APP_TAB_BITS 4 + #define UDP_APP_TAB_SIZE (1 << UDP_APP_TAB_BITS) + #define UDP_APP_TAB_MASK (UDP_APP_TAB_SIZE - 1) + struct list_head udp_apps[UDP_APP_TAB_SIZE]; + spinlock_t udp_app_lock; +#endif /* ip_vs_lblc */ int sysctl_lblc_expiration; diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index 320c6a65f370..cdc414238fcb 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c @@ -309,6 +309,9 @@ static int __net_init __ip_vs_protocol_init(struct net *net) { #ifdef CONFIG_IP_VS_PROTO_TCP register_ip_vs_proto_netns(net, &ip_vs_protocol_tcp); +#endif +#ifdef CONFIG_IP_VS_PROTO_UDP + register_ip_vs_proto_netns(net, &ip_vs_protocol_udp); #endif return 0; } diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index 5ab54f648654..71a4721a8f8a 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -9,7 +9,8 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Changes: + * Changes: Hans Schillstrom + * Network name space (netns) aware. * */ @@ -345,19 +346,6 @@ udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp) return 1; } - -/* - * Note: the caller guarantees that only one of register_app, - * unregister_app or app_conn_bind is called each time. - */ - -#define UDP_APP_TAB_BITS 4 -#define UDP_APP_TAB_SIZE (1 << UDP_APP_TAB_BITS) -#define UDP_APP_TAB_MASK (UDP_APP_TAB_SIZE - 1) - -static struct list_head udp_apps[UDP_APP_TAB_SIZE]; -static DEFINE_SPINLOCK(udp_app_lock); - static inline __u16 udp_app_hashkey(__be16 port) { return (((__force u16)port >> UDP_APP_TAB_BITS) ^ (__force u16)port) @@ -371,22 +359,24 @@ static int udp_register_app(struct ip_vs_app *inc) __u16 hash; __be16 port = inc->port; int ret = 0; + struct netns_ipvs *ipvs = net_ipvs(&init_net); + struct ip_vs_proto_data *pd = ip_vs_proto_data_get(&init_net, IPPROTO_UDP); hash = udp_app_hashkey(port); - spin_lock_bh(&udp_app_lock); - list_for_each_entry(i, &udp_apps[hash], p_list) { + spin_lock_bh(&ipvs->udp_app_lock); + list_for_each_entry(i, &ipvs->udp_apps[hash], p_list) { if (i->port == port) { ret = -EEXIST; goto out; } } - list_add(&inc->p_list, &udp_apps[hash]); - atomic_inc(&ip_vs_protocol_udp.appcnt); + list_add(&inc->p_list, &ipvs->udp_apps[hash]); + atomic_inc(&pd->pp->appcnt); out: - spin_unlock_bh(&udp_app_lock); + spin_unlock_bh(&ipvs->udp_app_lock); return ret; } @@ -394,15 +384,19 @@ static int udp_register_app(struct ip_vs_app *inc) static void udp_unregister_app(struct ip_vs_app *inc) { - spin_lock_bh(&udp_app_lock); - atomic_dec(&ip_vs_protocol_udp.appcnt); + struct ip_vs_proto_data *pd = ip_vs_proto_data_get(&init_net, IPPROTO_UDP); + struct netns_ipvs *ipvs = net_ipvs(&init_net); + + spin_lock_bh(&ipvs->udp_app_lock); + atomic_dec(&pd->pp->appcnt); list_del(&inc->p_list); - spin_unlock_bh(&udp_app_lock); + spin_unlock_bh(&ipvs->udp_app_lock); } static int udp_app_conn_bind(struct ip_vs_conn *cp) { + struct netns_ipvs *ipvs = net_ipvs(&init_net); int hash; struct ip_vs_app *inc; int result = 0; @@ -414,12 +408,12 @@ 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); - spin_lock(&udp_app_lock); - list_for_each_entry(inc, &udp_apps[hash], p_list) { + spin_lock(&ipvs->udp_app_lock); + list_for_each_entry(inc, &ipvs->udp_apps[hash], p_list) { if (inc->port == cp->vport) { if (unlikely(!ip_vs_app_inc_get(inc))) break; - spin_unlock(&udp_app_lock); + spin_unlock(&ipvs->udp_app_lock); IP_VS_DBG_BUF(9, "%s(): Binding conn %s:%u->" "%s:%u to app %s on port %u\n", @@ -436,14 +430,14 @@ static int udp_app_conn_bind(struct ip_vs_conn *cp) goto out; } } - spin_unlock(&udp_app_lock); + spin_unlock(&ipvs->udp_app_lock); out: return result; } -static int udp_timeouts[IP_VS_UDP_S_LAST+1] = { +static const int udp_timeouts[IP_VS_UDP_S_LAST+1] = { [IP_VS_UDP_S_NORMAL] = 5*60*HZ, [IP_VS_UDP_S_LAST] = 2*HZ, }; @@ -453,14 +447,6 @@ static const char *const udp_state_name_table[IP_VS_UDP_S_LAST+1] = { [IP_VS_UDP_S_LAST] = "BUG!", }; - -static int -udp_set_state_timeout(struct ip_vs_protocol *pp, char *sname, int to) -{ - return ip_vs_set_state_timeout(pp->timeout_table, IP_VS_UDP_S_LAST, - udp_state_name_table, sname, to); -} - static const char * udp_state_name(int state) { if (state >= IP_VS_UDP_S_LAST) @@ -473,18 +459,31 @@ udp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, struct ip_vs_protocol *pp) { - cp->timeout = pp->timeout_table[IP_VS_UDP_S_NORMAL]; + struct ip_vs_proto_data *pd; /* Temp fix, pp will be replaced by pd */ + + pd = ip_vs_proto_data_get(&init_net, IPPROTO_UDP); + if (unlikely(!pd)) { + pr_err("UDP no ns data\n"); + return 0; + } + + cp->timeout = pd->timeout_table[IP_VS_UDP_S_NORMAL]; return 1; } -static void udp_init(struct ip_vs_protocol *pp) +static void __udp_init(struct net *net, struct ip_vs_proto_data *pd) { - IP_VS_INIT_HASH_TABLE(udp_apps); - pp->timeout_table = udp_timeouts; + struct netns_ipvs *ipvs = net_ipvs(net); + + ip_vs_init_hash_table(ipvs->udp_apps, UDP_APP_TAB_SIZE); + spin_lock_init(&ipvs->udp_app_lock); + pd->timeout_table = ip_vs_create_timeout_table((int *)udp_timeouts, + sizeof(udp_timeouts)); } -static void udp_exit(struct ip_vs_protocol *pp) +static void __udp_exit(struct net *net, struct ip_vs_proto_data *pd) { + kfree(pd->timeout_table); } @@ -493,8 +492,10 @@ struct ip_vs_protocol ip_vs_protocol_udp = { .protocol = IPPROTO_UDP, .num_states = IP_VS_UDP_S_LAST, .dont_defrag = 0, - .init = udp_init, - .exit = udp_exit, + .init = NULL, + .exit = NULL, + .init_netns = __udp_init, + .exit_netns = __udp_exit, .conn_schedule = udp_conn_schedule, .conn_in_get = ip_vs_conn_in_get_proto, .conn_out_get = ip_vs_conn_out_get_proto, @@ -508,5 +509,4 @@ struct ip_vs_protocol ip_vs_protocol_udp = { .app_conn_bind = udp_app_conn_bind, .debug_packet = ip_vs_tcpudp_debug_packet, .timeout_change = NULL, - .set_state_timeout = udp_set_state_timeout, }; -- cgit v1.2.3 From 9d934878e7870fbbbd8eaed2e467552536877def Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:49 +0100 Subject: IPVS: netns preparation for proto_sctp In this phase (one), all local vars will be moved to ipvs struct. Remaining work, add param struct net *net to a couple of functions that is common for all protos and use ip_vs_proto_data *v3 Removed unuset function set_state_timeout() Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/netns/ip_vs.h | 9 +++ net/netfilter/ipvs/ip_vs_proto.c | 3 + net/netfilter/ipvs/ip_vs_proto_sctp.c | 121 ++++++++++++++++------------------ 3 files changed, 70 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h index 62b1448d3795..58bd3fd85a97 100644 --- a/include/net/netns/ip_vs.h +++ b/include/net/netns/ip_vs.h @@ -47,6 +47,15 @@ struct netns_ipvs { struct list_head udp_apps[UDP_APP_TAB_SIZE]; spinlock_t udp_app_lock; #endif + /* ip_vs_proto_sctp */ +#ifdef CONFIG_IP_VS_PROTO_SCTP + #define SCTP_APP_TAB_BITS 4 + #define SCTP_APP_TAB_SIZE (1 << SCTP_APP_TAB_BITS) + #define SCTP_APP_TAB_MASK (SCTP_APP_TAB_SIZE - 1) + /* Hash table for SCTP application incarnations */ + struct list_head sctp_apps[SCTP_APP_TAB_SIZE]; + spinlock_t sctp_app_lock; +#endif /* ip_vs_lblc */ int sysctl_lblc_expiration; diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index cdc414238fcb..001b2f825043 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c @@ -312,6 +312,9 @@ static int __net_init __ip_vs_protocol_init(struct net *net) #endif #ifdef CONFIG_IP_VS_PROTO_UDP register_ip_vs_proto_netns(net, &ip_vs_protocol_udp); +#endif +#ifdef CONFIG_IP_VS_PROTO_SCTP + register_ip_vs_proto_netns(net, &ip_vs_protocol_sctp); #endif return 0; } diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 521b827083fe..f826dd1e4630 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -862,7 +862,7 @@ static struct ipvs_sctp_nextstate /* * Timeout table[state] */ -static int sctp_timeouts[IP_VS_SCTP_S_LAST + 1] = { +static const int sctp_timeouts[IP_VS_SCTP_S_LAST + 1] = { [IP_VS_SCTP_S_NONE] = 2 * HZ, [IP_VS_SCTP_S_INIT_CLI] = 1 * 60 * HZ, [IP_VS_SCTP_S_INIT_SER] = 1 * 60 * HZ, @@ -906,18 +906,6 @@ static const char *sctp_state_name(int state) return "?"; } -static void sctp_timeout_change(struct ip_vs_protocol *pp, int flags) -{ -} - -static int -sctp_set_state_timeout(struct ip_vs_protocol *pp, char *sname, int to) -{ - -return ip_vs_set_state_timeout(pp->timeout_table, IP_VS_SCTP_S_LAST, - sctp_state_name_table, sname, to); -} - static inline int set_sctp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int direction, const struct sk_buff *skb) @@ -926,6 +914,7 @@ set_sctp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp, unsigned char chunk_type; int event, next_state; int ihl; + struct ip_vs_proto_data *pd; #ifdef CONFIG_IP_VS_IPV6 ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr); @@ -1001,10 +990,13 @@ set_sctp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp, } } } + pd = ip_vs_proto_data_get(&init_net, pp->protocol); /* tmp fix */ + if (likely(pd)) + cp->timeout = pd->timeout_table[cp->state = next_state]; + else /* What to do ? */ + cp->timeout = sctp_timeouts[cp->state = next_state]; - cp->timeout = pp->timeout_table[cp->state = next_state]; - - return 1; + return 1; } static int @@ -1020,16 +1012,6 @@ sctp_state_transition(struct ip_vs_conn *cp, int direction, return ret; } -/* - * Hash table for SCTP application incarnations - */ -#define SCTP_APP_TAB_BITS 4 -#define SCTP_APP_TAB_SIZE (1 << SCTP_APP_TAB_BITS) -#define SCTP_APP_TAB_MASK (SCTP_APP_TAB_SIZE - 1) - -static struct list_head sctp_apps[SCTP_APP_TAB_SIZE]; -static DEFINE_SPINLOCK(sctp_app_lock); - static inline __u16 sctp_app_hashkey(__be16 port) { return (((__force u16)port >> SCTP_APP_TAB_BITS) ^ (__force u16)port) @@ -1042,34 +1024,40 @@ static int sctp_register_app(struct ip_vs_app *inc) __u16 hash; __be16 port = inc->port; int ret = 0; + struct netns_ipvs *ipvs = net_ipvs(&init_net); + struct ip_vs_proto_data *pd = ip_vs_proto_data_get(&init_net, IPPROTO_SCTP); hash = sctp_app_hashkey(port); - spin_lock_bh(&sctp_app_lock); - list_for_each_entry(i, &sctp_apps[hash], p_list) { + spin_lock_bh(&ipvs->sctp_app_lock); + list_for_each_entry(i, &ipvs->sctp_apps[hash], p_list) { if (i->port == port) { ret = -EEXIST; goto out; } } - list_add(&inc->p_list, &sctp_apps[hash]); - atomic_inc(&ip_vs_protocol_sctp.appcnt); + list_add(&inc->p_list, &ipvs->sctp_apps[hash]); + atomic_inc(&pd->pp->appcnt); out: - spin_unlock_bh(&sctp_app_lock); + spin_unlock_bh(&ipvs->sctp_app_lock); return ret; } static void sctp_unregister_app(struct ip_vs_app *inc) { - spin_lock_bh(&sctp_app_lock); - atomic_dec(&ip_vs_protocol_sctp.appcnt); + struct netns_ipvs *ipvs = net_ipvs(&init_net); + struct ip_vs_proto_data *pd = ip_vs_proto_data_get(&init_net, IPPROTO_SCTP); + + spin_lock_bh(&ipvs->sctp_app_lock); + atomic_dec(&pd->pp->appcnt); list_del(&inc->p_list); - spin_unlock_bh(&sctp_app_lock); + spin_unlock_bh(&ipvs->sctp_app_lock); } static int sctp_app_conn_bind(struct ip_vs_conn *cp) { + struct netns_ipvs *ipvs = net_ipvs(&init_net); int hash; struct ip_vs_app *inc; int result = 0; @@ -1080,12 +1068,12 @@ 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); - spin_lock(&sctp_app_lock); - list_for_each_entry(inc, &sctp_apps[hash], p_list) { + spin_lock(&ipvs->sctp_app_lock); + list_for_each_entry(inc, &ipvs->sctp_apps[hash], p_list) { if (inc->port == cp->vport) { if (unlikely(!ip_vs_app_inc_get(inc))) break; - spin_unlock(&sctp_app_lock); + spin_unlock(&ipvs->sctp_app_lock); IP_VS_DBG_BUF(9, "%s: Binding conn %s:%u->" "%s:%u to app %s on port %u\n", @@ -1101,43 +1089,50 @@ static int sctp_app_conn_bind(struct ip_vs_conn *cp) goto out; } } - spin_unlock(&sctp_app_lock); + spin_unlock(&ipvs->sctp_app_lock); out: return result; } -static void ip_vs_sctp_init(struct ip_vs_protocol *pp) +/* --------------------------------------------- + * timeouts is netns related now. + * --------------------------------------------- + */ +static void __ip_vs_sctp_init(struct net *net, struct ip_vs_proto_data *pd) { - IP_VS_INIT_HASH_TABLE(sctp_apps); - pp->timeout_table = sctp_timeouts; -} + struct netns_ipvs *ipvs = net_ipvs(net); + ip_vs_init_hash_table(ipvs->sctp_apps, SCTP_APP_TAB_SIZE); + spin_lock_init(&ipvs->tcp_app_lock); + pd->timeout_table = ip_vs_create_timeout_table((int *)sctp_timeouts, + sizeof(sctp_timeouts)); +} -static void ip_vs_sctp_exit(struct ip_vs_protocol *pp) +static void __ip_vs_sctp_exit(struct net *net, struct ip_vs_proto_data *pd) { - + kfree(pd->timeout_table); } struct ip_vs_protocol ip_vs_protocol_sctp = { - .name = "SCTP", - .protocol = IPPROTO_SCTP, - .num_states = IP_VS_SCTP_S_LAST, - .dont_defrag = 0, - .appcnt = ATOMIC_INIT(0), - .init = ip_vs_sctp_init, - .exit = ip_vs_sctp_exit, - .register_app = sctp_register_app, + .name = "SCTP", + .protocol = IPPROTO_SCTP, + .num_states = IP_VS_SCTP_S_LAST, + .dont_defrag = 0, + .init = NULL, + .exit = NULL, + .init_netns = __ip_vs_sctp_init, + .exit_netns = __ip_vs_sctp_exit, + .register_app = sctp_register_app, .unregister_app = sctp_unregister_app, - .conn_schedule = sctp_conn_schedule, - .conn_in_get = ip_vs_conn_in_get_proto, - .conn_out_get = ip_vs_conn_out_get_proto, - .snat_handler = sctp_snat_handler, - .dnat_handler = sctp_dnat_handler, - .csum_check = sctp_csum_check, - .state_name = sctp_state_name, + .conn_schedule = sctp_conn_schedule, + .conn_in_get = ip_vs_conn_in_get_proto, + .conn_out_get = ip_vs_conn_out_get_proto, + .snat_handler = sctp_snat_handler, + .dnat_handler = sctp_dnat_handler, + .csum_check = sctp_csum_check, + .state_name = sctp_state_name, .state_transition = sctp_state_transition, - .app_conn_bind = sctp_app_conn_bind, - .debug_packet = ip_vs_tcpudp_debug_packet, - .timeout_change = sctp_timeout_change, - .set_state_timeout = sctp_set_state_timeout, + .app_conn_bind = sctp_app_conn_bind, + .debug_packet = ip_vs_tcpudp_debug_packet, + .timeout_change = NULL, }; -- cgit v1.2.3 From 9330419d9aa4f97df412ac9be9fc0388c67dd315 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:51 +0100 Subject: IPVS: netns, use ip_vs_proto_data as param. ip_vs_protocol *pp is replaced by ip_vs_proto_data *pd in function call in ip_vs_protocol struct i.e. :, - timeout_change() - state_transition() ip_vs_protocol_timeout_change() got ipvs as param, due to above and a upcoming patch - defence work Most of this changes are triggered by Julians comment: "tcp_timeout_change should work with the new struct ip_vs_proto_data so that tcp_state_table will go to pd->state_table and set_tcp_state will get pd instead of pp" *v3 Mostly comments from Julian The pp -> pd conversion should start from functions like ip_vs_out() that use pp = ip_vs_proto_get(iph.protocol), now they should use ip_vs_proto_data_get(net, iph.protocol). conn_in_get() and conn_out_get() unused param *pp, removed. *v4 ip_vs_protocol_timeout_change() walk the proto_data path. Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 18 +++----- net/netfilter/ipvs/ip_vs_conn.c | 2 - net/netfilter/ipvs/ip_vs_core.c | 77 ++++++++++++++++++++------------- net/netfilter/ipvs/ip_vs_ctl.c | 55 ++++++++++++++--------- net/netfilter/ipvs/ip_vs_proto.c | 21 ++++++--- net/netfilter/ipvs/ip_vs_proto_ah_esp.c | 10 ++--- net/netfilter/ipvs/ip_vs_proto_sctp.c | 16 +++---- net/netfilter/ipvs/ip_vs_proto_tcp.c | 27 +++++------- net/netfilter/ipvs/ip_vs_proto_udp.c | 11 ++--- net/netfilter/xt_ipvs.c | 2 +- 10 files changed, 129 insertions(+), 110 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 3c45a00cdc3e..464ea365ca07 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -372,13 +372,12 @@ struct ip_vs_protocol { void (*exit_netns)(struct net *net, struct ip_vs_proto_data *pd); int (*conn_schedule)(int af, struct sk_buff *skb, - struct ip_vs_protocol *pp, + struct ip_vs_proto_data *pd, int *verdict, struct ip_vs_conn **cpp); struct ip_vs_conn * (*conn_in_get)(int af, const struct sk_buff *skb, - struct ip_vs_protocol *pp, const struct ip_vs_iphdr *iph, unsigned int proto_off, int inverse); @@ -386,7 +385,6 @@ struct ip_vs_protocol { struct ip_vs_conn * (*conn_out_get)(int af, const struct sk_buff *skb, - struct ip_vs_protocol *pp, const struct ip_vs_iphdr *iph, unsigned int proto_off, int inverse); @@ -404,7 +402,7 @@ struct ip_vs_protocol { int (*state_transition)(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_protocol *pp); + struct ip_vs_proto_data *pd); int (*register_app)(struct ip_vs_app *inc); @@ -417,9 +415,7 @@ struct ip_vs_protocol { int offset, const char *msg); - void (*timeout_change)(struct ip_vs_protocol *pp, int flags); - - int (*set_state_timeout)(struct ip_vs_protocol *pp, char *sname, int to); + void (*timeout_change)(struct ip_vs_proto_data *pd, int flags); }; /* @@ -778,7 +774,6 @@ struct ip_vs_conn *ip_vs_conn_in_get(const struct ip_vs_conn_param *p); struct ip_vs_conn *ip_vs_ct_in_get(const struct ip_vs_conn_param *p); struct ip_vs_conn * ip_vs_conn_in_get_proto(int af, const struct sk_buff *skb, - struct ip_vs_protocol *pp, const struct ip_vs_iphdr *iph, unsigned int proto_off, int inverse); @@ -786,7 +781,6 @@ struct ip_vs_conn * ip_vs_conn_in_get_proto(int af, const struct sk_buff *skb, struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p); struct ip_vs_conn * ip_vs_conn_out_get_proto(int af, const struct sk_buff *skb, - struct ip_vs_protocol *pp, const struct ip_vs_iphdr *iph, unsigned int proto_off, int inverse); @@ -917,7 +911,7 @@ static inline void ip_vs_pe_put(const struct ip_vs_pe *pe) */ extern int ip_vs_protocol_init(void); extern void ip_vs_protocol_cleanup(void); -extern void ip_vs_protocol_timeout_change(int flags); +extern void ip_vs_protocol_timeout_change(struct netns_ipvs *ipvs, int flags); extern int *ip_vs_create_timeout_table(int *table, int size); extern int ip_vs_set_state_timeout(int *table, int num, const char *const *names, @@ -947,9 +941,9 @@ extern struct ip_vs_scheduler *ip_vs_scheduler_get(const char *sched_name); extern void ip_vs_scheduler_put(struct ip_vs_scheduler *scheduler); extern struct ip_vs_conn * ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb, - struct ip_vs_protocol *pp, int *ignored); + struct ip_vs_proto_data *pd, int *ignored); extern int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, - struct ip_vs_protocol *pp); + struct ip_vs_proto_data *pd); /* diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 7a0e79e3ad0f..a7aba6a4697e 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -329,7 +329,6 @@ ip_vs_conn_fill_param_proto(int af, const struct sk_buff *skb, struct ip_vs_conn * ip_vs_conn_in_get_proto(int af, const struct sk_buff *skb, - struct ip_vs_protocol *pp, const struct ip_vs_iphdr *iph, unsigned int proto_off, int inverse) { @@ -428,7 +427,6 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p) struct ip_vs_conn * ip_vs_conn_out_get_proto(int af, const struct sk_buff *skb, - struct ip_vs_protocol *pp, const struct ip_vs_iphdr *iph, unsigned int proto_off, int inverse) { diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index d0616ea1eebf..9317affc5ea1 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -177,11 +177,11 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) static inline int ip_vs_set_state(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_protocol *pp) + struct ip_vs_proto_data *pd) { - if (unlikely(!pp->state_transition)) + if (unlikely(!pd->pp->state_transition)) return 0; - return pp->state_transition(cp, direction, skb, pp); + return pd->pp->state_transition(cp, direction, skb, pd); } static inline int @@ -378,8 +378,9 @@ ip_vs_sched_persist(struct ip_vs_service *svc, */ struct ip_vs_conn * ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb, - struct ip_vs_protocol *pp, int *ignored) + struct ip_vs_proto_data *pd, int *ignored) { + struct ip_vs_protocol *pp = pd->pp; struct ip_vs_conn *cp = NULL; struct ip_vs_iphdr iph; struct ip_vs_dest *dest; @@ -408,7 +409,7 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb, * Do not schedule replies from local real server. */ if ((!skb->dev || skb->dev->flags & IFF_LOOPBACK) && - (cp = pp->conn_in_get(svc->af, skb, pp, &iph, iph.len, 1))) { + (cp = pp->conn_in_get(svc->af, skb, &iph, iph.len, 1))) { IP_VS_DBG_PKT(12, svc->af, pp, skb, 0, "Not scheduling reply for existing connection"); __ip_vs_conn_put(cp); @@ -479,11 +480,12 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb, * no destination is available for a new connection. */ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, - struct ip_vs_protocol *pp) + struct ip_vs_proto_data *pd) { __be16 _ports[2], *pptr; struct ip_vs_iphdr iph; int unicast; + ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph); pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports); @@ -530,10 +532,10 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, ip_vs_in_stats(cp, skb); /* set state */ - cs = ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pp); + cs = ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); /* transmit the first SYN packet */ - ret = cp->packet_xmit(skb, cp, pp); + ret = cp->packet_xmit(skb, cp, pd->pp); /* do not touch skb anymore */ atomic_inc(&cp->in_pkts); @@ -840,7 +842,7 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related, ip_vs_fill_iphdr(AF_INET, cih, &ciph); /* The embedded headers contain source and dest in reverse order */ - cp = pp->conn_out_get(AF_INET, skb, pp, &ciph, offset, 1); + cp = pp->conn_out_get(AF_INET, skb, &ciph, offset, 1); if (!cp) return NF_ACCEPT; @@ -917,7 +919,7 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related, ip_vs_fill_iphdr(AF_INET6, cih, &ciph); /* The embedded headers contain source and dest in reverse order */ - cp = pp->conn_out_get(AF_INET6, skb, pp, &ciph, offset, 1); + cp = pp->conn_out_get(AF_INET6, skb, &ciph, offset, 1); if (!cp) return NF_ACCEPT; @@ -956,9 +958,11 @@ static inline int is_tcp_reset(const struct sk_buff *skb, int nh_len) * Used for NAT and local client. */ static unsigned int -handle_response(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, int ihl) { + struct ip_vs_protocol *pp = pd->pp; + IP_VS_DBG_PKT(11, af, pp, skb, 0, "Outgoing packet"); if (!skb_make_writable(skb, ihl)) @@ -1007,7 +1011,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, IP_VS_DBG_PKT(10, af, pp, skb, 0, "After SNAT"); ip_vs_out_stats(cp, skb); - ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pp); + ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd); skb->ipvs_property = 1; if (!(cp->flags & IP_VS_CONN_F_NFCT)) ip_vs_notrack(skb); @@ -1034,6 +1038,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) struct net *net = NULL; struct ip_vs_iphdr iph; struct ip_vs_protocol *pp; + struct ip_vs_proto_data *pd; struct ip_vs_conn *cp; EnterFunction(11); @@ -1079,9 +1084,10 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); } - pp = ip_vs_proto_get(iph.protocol); - if (unlikely(!pp)) + pd = ip_vs_proto_data_get(net, iph.protocol); + if (unlikely(!pd)) return NF_ACCEPT; + pp = pd->pp; /* reassemble IP fragments */ #ifdef CONFIG_IP_VS_IPV6 @@ -1107,10 +1113,10 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) /* * Check if the packet belongs to an existing entry */ - cp = pp->conn_out_get(af, skb, pp, &iph, iph.len, 0); + cp = pp->conn_out_get(af, skb, &iph, iph.len, 0); if (likely(cp)) - return handle_response(af, skb, pp, cp, iph.len); + return handle_response(af, skb, pd, cp, iph.len); if (sysctl_ip_vs_nat_icmp_send && (pp->protocol == IPPROTO_TCP || pp->protocol == IPPROTO_UDP || @@ -1236,12 +1242,14 @@ ip_vs_local_reply6(unsigned int hooknum, struct sk_buff *skb, static int ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) { + struct net *net = NULL; struct iphdr *iph; struct icmphdr _icmph, *ic; struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ struct ip_vs_iphdr ciph; struct ip_vs_conn *cp; struct ip_vs_protocol *pp; + struct ip_vs_proto_data *pd; unsigned int offset, ihl, verdict; union nf_inet_addr snet; @@ -1283,9 +1291,11 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) if (cih == NULL) return NF_ACCEPT; /* The packet looks wrong, ignore */ - pp = ip_vs_proto_get(cih->protocol); - if (!pp) + net = skb_net(skb); + pd = ip_vs_proto_data_get(net, cih->protocol); + if (!pd) return NF_ACCEPT; + pp = pd->pp; /* Is the embedded protocol header present? */ if (unlikely(cih->frag_off & htons(IP_OFFSET) && @@ -1299,10 +1309,10 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) ip_vs_fill_iphdr(AF_INET, cih, &ciph); /* The embedded headers contain source and dest in reverse order */ - cp = pp->conn_in_get(AF_INET, skb, pp, &ciph, offset, 1); + cp = pp->conn_in_get(AF_INET, skb, &ciph, offset, 1); if (!cp) { /* The packet could also belong to a local client */ - cp = pp->conn_out_get(AF_INET, skb, pp, &ciph, offset, 1); + cp = pp->conn_out_get(AF_INET, skb, &ciph, offset, 1); if (cp) { snet.ip = iph->saddr; return handle_response_icmp(AF_INET, skb, &snet, @@ -1346,6 +1356,7 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) static int ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) { + struct net *net = NULL; struct ipv6hdr *iph; struct icmp6hdr _icmph, *ic; struct ipv6hdr _ciph, *cih; /* The ip header contained @@ -1353,6 +1364,7 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) struct ip_vs_iphdr ciph; struct ip_vs_conn *cp; struct ip_vs_protocol *pp; + struct ip_vs_proto_data *pd; unsigned int offset, verdict; union nf_inet_addr snet; struct rt6_info *rt; @@ -1395,9 +1407,11 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) if (cih == NULL) return NF_ACCEPT; /* The packet looks wrong, ignore */ - pp = ip_vs_proto_get(cih->nexthdr); - if (!pp) + net = skb_net(skb); + pd = ip_vs_proto_data_get(net, cih->nexthdr); + if (!pd) return NF_ACCEPT; + pp = pd->pp; /* Is the embedded protocol header present? */ /* TODO: we don't support fragmentation at the moment anyways */ @@ -1411,10 +1425,10 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) ip_vs_fill_iphdr(AF_INET6, cih, &ciph); /* The embedded headers contain source and dest in reverse order */ - cp = pp->conn_in_get(AF_INET6, skb, pp, &ciph, offset, 1); + cp = pp->conn_in_get(AF_INET6, skb, &ciph, offset, 1); if (!cp) { /* The packet could also belong to a local client */ - cp = pp->conn_out_get(AF_INET6, skb, pp, &ciph, offset, 1); + cp = pp->conn_out_get(AF_INET6, skb, &ciph, offset, 1); if (cp) { ipv6_addr_copy(&snet.in6, &iph->saddr); return handle_response_icmp(AF_INET6, skb, &snet, @@ -1457,8 +1471,10 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) static unsigned int ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) { + struct net *net = NULL; struct ip_vs_iphdr iph; struct ip_vs_protocol *pp; + struct ip_vs_proto_data *pd; struct ip_vs_conn *cp; int ret, restart, pkts; @@ -1514,20 +1530,21 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); } + net = skb_net(skb); /* Protocol supported? */ - pp = ip_vs_proto_get(iph.protocol); - if (unlikely(!pp)) + pd = ip_vs_proto_data_get(net, iph.protocol); + if (unlikely(!pd)) return NF_ACCEPT; - + pp = pd->pp; /* * Check if the packet belongs to an existing connection entry */ - cp = pp->conn_in_get(af, skb, pp, &iph, iph.len, 0); + cp = pp->conn_in_get(af, skb, &iph, iph.len, 0); if (unlikely(!cp)) { int v; - if (!pp->conn_schedule(af, skb, pp, &v, &cp)) + if (!pp->conn_schedule(af, skb, pd, &v, &cp)) return v; } @@ -1555,7 +1572,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) } ip_vs_in_stats(cp, skb); - restart = ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pp); + restart = ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); if (cp->packet_xmit) ret = cp->packet_xmit(skb, cp, pp); /* do not touch skb anymore */ diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 2d7c96bd2114..88474f1e828a 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -38,6 +38,7 @@ #include #include +#include #include #ifdef CONFIG_IP_VS_IPV6 #include @@ -125,7 +126,7 @@ static int __ip_vs_addr_is_local_v6(const struct in6_addr *addr) * update_defense_level is called from keventd and from sysctl, * so it needs to protect itself from softirqs */ -static void update_defense_level(void) +static void update_defense_level(struct netns_ipvs *ipvs) { struct sysinfo i; static int old_secure_tcp = 0; @@ -239,7 +240,8 @@ static void update_defense_level(void) } old_secure_tcp = sysctl_ip_vs_secure_tcp; if (to_change >= 0) - ip_vs_protocol_timeout_change(sysctl_ip_vs_secure_tcp>1); + ip_vs_protocol_timeout_change(ipvs, + sysctl_ip_vs_secure_tcp > 1); spin_unlock(&ip_vs_securetcp_lock); local_bh_enable(); @@ -255,7 +257,10 @@ static DECLARE_DELAYED_WORK(defense_work, defense_work_handler); static void defense_work_handler(struct work_struct *work) { - update_defense_level(); + struct net *net = &init_net; + struct netns_ipvs *ipvs = net_ipvs(net); + + update_defense_level(ipvs); if (atomic_read(&ip_vs_dropentry)) ip_vs_random_dropentry(); @@ -1502,6 +1507,7 @@ static int proc_do_defense_mode(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { + struct net *net = current->nsproxy->net_ns; int *valp = table->data; int val = *valp; int rc; @@ -1512,7 +1518,7 @@ proc_do_defense_mode(ctl_table *table, int write, /* Restore the correct value */ *valp = val; } else { - update_defense_level(); + update_defense_level(net_ipvs(net)); } } return rc; @@ -2033,8 +2039,10 @@ static const struct file_operations ip_vs_stats_fops = { /* * Set timeout values for tcp tcpfin udp in the timeout_table. */ -static int ip_vs_set_timeout(struct ip_vs_timeout_user *u) +static int ip_vs_set_timeout(struct net *net, struct ip_vs_timeout_user *u) { + struct ip_vs_proto_data *pd; + IP_VS_DBG(2, "Setting timeout tcp:%d tcpfin:%d udp:%d\n", u->tcp_timeout, u->tcp_fin_timeout, @@ -2042,19 +2050,22 @@ static int ip_vs_set_timeout(struct ip_vs_timeout_user *u) #ifdef CONFIG_IP_VS_PROTO_TCP if (u->tcp_timeout) { - ip_vs_protocol_tcp.timeout_table[IP_VS_TCP_S_ESTABLISHED] + pd = ip_vs_proto_data_get(net, IPPROTO_TCP); + pd->timeout_table[IP_VS_TCP_S_ESTABLISHED] = u->tcp_timeout * HZ; } if (u->tcp_fin_timeout) { - ip_vs_protocol_tcp.timeout_table[IP_VS_TCP_S_FIN_WAIT] + pd = ip_vs_proto_data_get(net, IPPROTO_TCP); + pd->timeout_table[IP_VS_TCP_S_FIN_WAIT] = u->tcp_fin_timeout * HZ; } #endif #ifdef CONFIG_IP_VS_PROTO_UDP if (u->udp_timeout) { - ip_vs_protocol_udp.timeout_table[IP_VS_UDP_S_NORMAL] + pd = ip_vs_proto_data_get(net, IPPROTO_UDP); + pd->timeout_table[IP_VS_UDP_S_NORMAL] = u->udp_timeout * HZ; } #endif @@ -2158,7 +2169,7 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) goto out_unlock; } else if (cmd == IP_VS_SO_SET_TIMEOUT) { /* Set timeout values for (tcp tcpfin udp) */ - ret = ip_vs_set_timeout((struct ip_vs_timeout_user *)arg); + ret = ip_vs_set_timeout(net, (struct ip_vs_timeout_user *)arg); goto out_unlock; } else if (cmd == IP_VS_SO_SET_STARTDAEMON) { struct ip_vs_daemon_user *dm = (struct ip_vs_daemon_user *)arg; @@ -2370,17 +2381,19 @@ __ip_vs_get_dest_entries(struct net *net, const struct ip_vs_get_dests *get, } static inline void -__ip_vs_get_timeouts(struct ip_vs_timeout_user *u) +__ip_vs_get_timeouts(struct net *net, struct ip_vs_timeout_user *u) { + struct ip_vs_proto_data *pd; + #ifdef CONFIG_IP_VS_PROTO_TCP - u->tcp_timeout = - ip_vs_protocol_tcp.timeout_table[IP_VS_TCP_S_ESTABLISHED] / HZ; - u->tcp_fin_timeout = - ip_vs_protocol_tcp.timeout_table[IP_VS_TCP_S_FIN_WAIT] / HZ; + pd = ip_vs_proto_data_get(net, IPPROTO_TCP); + u->tcp_timeout = pd->timeout_table[IP_VS_TCP_S_ESTABLISHED] / HZ; + u->tcp_fin_timeout = pd->timeout_table[IP_VS_TCP_S_FIN_WAIT] / HZ; #endif #ifdef CONFIG_IP_VS_PROTO_UDP + pd = ip_vs_proto_data_get(net, IPPROTO_UDP); u->udp_timeout = - ip_vs_protocol_udp.timeout_table[IP_VS_UDP_S_NORMAL] / HZ; + pd->timeout_table[IP_VS_UDP_S_NORMAL] / HZ; #endif } @@ -2521,7 +2534,7 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) { struct ip_vs_timeout_user t; - __ip_vs_get_timeouts(&t); + __ip_vs_get_timeouts(net, &t); if (copy_to_user(user, &t, sizeof(t)) != 0) ret = -EFAULT; } @@ -3092,11 +3105,11 @@ static int ip_vs_genl_del_daemon(struct nlattr **attrs) return stop_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE])); } -static int ip_vs_genl_set_config(struct nlattr **attrs) +static int ip_vs_genl_set_config(struct net *net, struct nlattr **attrs) { struct ip_vs_timeout_user t; - __ip_vs_get_timeouts(&t); + __ip_vs_get_timeouts(net, &t); if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]) t.tcp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]); @@ -3108,7 +3121,7 @@ static int ip_vs_genl_set_config(struct nlattr **attrs) if (attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]) t.udp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]); - return ip_vs_set_timeout(&t); + return ip_vs_set_timeout(net, &t); } static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) @@ -3129,7 +3142,7 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) ret = ip_vs_flush(net); goto out; } else if (cmd == IPVS_CMD_SET_CONFIG) { - ret = ip_vs_genl_set_config(info->attrs); + ret = ip_vs_genl_set_config(net, info->attrs); goto out; } else if (cmd == IPVS_CMD_NEW_DAEMON || cmd == IPVS_CMD_DEL_DAEMON) { @@ -3281,7 +3294,7 @@ static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info) { struct ip_vs_timeout_user t; - __ip_vs_get_timeouts(&t); + __ip_vs_get_timeouts(net, &t); #ifdef CONFIG_IP_VS_PROTO_TCP NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP, t.tcp_timeout); NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP_FIN, diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index 9f609d4d5d58..6ac986cdcff3 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c @@ -152,9 +152,8 @@ EXPORT_SYMBOL(ip_vs_proto_get); * get ip_vs_protocol object data by netns and proto */ struct ip_vs_proto_data * -ip_vs_proto_data_get(struct net *net, unsigned short proto) +__ipvs_proto_data_get(struct netns_ipvs *ipvs, unsigned short proto) { - struct netns_ipvs *ipvs = net_ipvs(net); struct ip_vs_proto_data *pd; unsigned hash = IP_VS_PROTO_HASH(proto); @@ -165,20 +164,28 @@ ip_vs_proto_data_get(struct net *net, unsigned short proto) return NULL; } + +struct ip_vs_proto_data * +ip_vs_proto_data_get(struct net *net, unsigned short proto) +{ + struct netns_ipvs *ipvs = net_ipvs(net); + + return __ipvs_proto_data_get(ipvs, proto); +} EXPORT_SYMBOL(ip_vs_proto_data_get); /* * Propagate event for state change to all protocols */ -void ip_vs_protocol_timeout_change(int flags) +void ip_vs_protocol_timeout_change(struct netns_ipvs *ipvs, int flags) { - struct ip_vs_protocol *pp; + struct ip_vs_proto_data *pd; int i; for (i = 0; i < IP_VS_PROTO_TAB_SIZE; i++) { - for (pp = ip_vs_proto_table[i]; pp; pp = pp->next) { - if (pp->timeout_change) - pp->timeout_change(pp, flags); + for (pd = ipvs->proto_data_table[i]; pd; pd = pd->next) { + if (pd->pp->timeout_change) + pd->pp->timeout_change(pd, flags); } } } diff --git a/net/netfilter/ipvs/ip_vs_proto_ah_esp.c b/net/netfilter/ipvs/ip_vs_proto_ah_esp.c index b8b37fafc988..28039cbfcff4 100644 --- a/net/netfilter/ipvs/ip_vs_proto_ah_esp.c +++ b/net/netfilter/ipvs/ip_vs_proto_ah_esp.c @@ -55,7 +55,7 @@ ah_esp_conn_fill_param_proto(int af, const struct ip_vs_iphdr *iph, } static struct ip_vs_conn * -ah_esp_conn_in_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp, +ah_esp_conn_in_get(int af, const struct sk_buff *skb, const struct ip_vs_iphdr *iph, unsigned int proto_off, int inverse) { @@ -72,7 +72,7 @@ ah_esp_conn_in_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp, IP_VS_DBG_BUF(12, "Unknown ISAKMP entry for outin packet " "%s%s %s->%s\n", inverse ? "ICMP+" : "", - pp->name, + ip_vs_proto_get(iph->protocol)->name, IP_VS_DBG_ADDR(af, &iph->saddr), IP_VS_DBG_ADDR(af, &iph->daddr)); } @@ -83,7 +83,6 @@ ah_esp_conn_in_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp, static struct ip_vs_conn * ah_esp_conn_out_get(int af, const struct sk_buff *skb, - struct ip_vs_protocol *pp, const struct ip_vs_iphdr *iph, unsigned int proto_off, int inverse) @@ -97,7 +96,7 @@ ah_esp_conn_out_get(int af, const struct sk_buff *skb, IP_VS_DBG_BUF(12, "Unknown ISAKMP entry for inout packet " "%s%s %s->%s\n", inverse ? "ICMP+" : "", - pp->name, + ip_vs_proto_get(iph->protocol)->name, IP_VS_DBG_ADDR(af, &iph->saddr), IP_VS_DBG_ADDR(af, &iph->daddr)); } @@ -107,7 +106,7 @@ ah_esp_conn_out_get(int af, const struct sk_buff *skb, static int -ah_esp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +ah_esp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, int *verdict, struct ip_vs_conn **cpp) { /* @@ -137,7 +136,6 @@ struct ip_vs_protocol ip_vs_protocol_ah = { .app_conn_bind = NULL, .debug_packet = ip_vs_tcpudp_debug_packet, .timeout_change = NULL, /* ISAKMP */ - .set_state_timeout = NULL, }; #endif diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index f826dd1e4630..19bc37976ea7 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -9,7 +9,7 @@ #include static int -sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, int *verdict, struct ip_vs_conn **cpp) { struct net *net; @@ -47,10 +47,10 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, * Let the virtual server select a real server for the * incoming connection, and create a connection entry. */ - *cpp = ip_vs_schedule(svc, skb, pp, &ignored); + *cpp = ip_vs_schedule(svc, skb, pd, &ignored); if (!*cpp && ignored <= 0) { if (!ignored) - *verdict = ip_vs_leave(svc, skb, pp); + *verdict = ip_vs_leave(svc, skb, pd); else { ip_vs_service_put(svc); *verdict = NF_DROP; @@ -907,14 +907,13 @@ static const char *sctp_state_name(int state) } static inline int -set_sctp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp, +set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, int direction, const struct sk_buff *skb) { sctp_chunkhdr_t _sctpch, *sch; unsigned char chunk_type; int event, next_state; int ihl; - struct ip_vs_proto_data *pd; #ifdef CONFIG_IP_VS_IPV6 ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr); @@ -966,7 +965,7 @@ set_sctp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp, IP_VS_DBG_BUF(8, "%s %s %s:%d->" "%s:%d state: %s->%s conn->refcnt:%d\n", - pp->name, + pd->pp->name, ((direction == IP_VS_DIR_OUTPUT) ? "output " : "input "), IP_VS_DBG_ADDR(cp->af, &cp->daddr), @@ -990,7 +989,6 @@ set_sctp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp, } } } - pd = ip_vs_proto_data_get(&init_net, pp->protocol); /* tmp fix */ if (likely(pd)) cp->timeout = pd->timeout_table[cp->state = next_state]; else /* What to do ? */ @@ -1001,12 +999,12 @@ set_sctp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp, static int sctp_state_transition(struct ip_vs_conn *cp, int direction, - const struct sk_buff *skb, struct ip_vs_protocol *pp) + const struct sk_buff *skb, struct ip_vs_proto_data *pd) { int ret = 0; spin_lock(&cp->lock); - ret = set_sctp_state(pp, cp, direction, skb); + ret = set_sctp_state(pd, cp, direction, skb); spin_unlock(&cp->lock); return ret; diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 9d9df3d61093..d7c245532798 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -32,7 +32,7 @@ #include static int -tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, int *verdict, struct ip_vs_conn **cpp) { struct net *net; @@ -68,10 +68,10 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, * Let the virtual server select a real server for the * incoming connection, and create a connection entry. */ - *cpp = ip_vs_schedule(svc, skb, pp, &ignored); + *cpp = ip_vs_schedule(svc, skb, pd, &ignored); if (!*cpp && ignored <= 0) { if (!ignored) - *verdict = ip_vs_leave(svc, skb, pp); + *verdict = ip_vs_leave(svc, skb, pd); else { ip_vs_service_put(svc); *verdict = NF_DROP; @@ -448,10 +448,7 @@ static struct tcp_states_t tcp_states_dos [] = { /*rst*/ {{sCL, sCL, sCL, sSR, sCL, sCL, sCL, sCL, sLA, sLI, sCL }}, }; -static struct tcp_states_t *tcp_state_table = tcp_states; - - -static void tcp_timeout_change(struct ip_vs_protocol *pp, int flags) +static void tcp_timeout_change(struct ip_vs_proto_data *pd, int flags) { int on = (flags & 1); /* secure_tcp */ @@ -461,7 +458,7 @@ static void tcp_timeout_change(struct ip_vs_protocol *pp, int flags) ** for most if not for all of the applications. Something ** like "capabilities" (flags) for each object. */ - tcp_state_table = (on? tcp_states_dos : tcp_states); + pd->tcp_state_table = (on ? tcp_states_dos : tcp_states); } static inline int tcp_state_idx(struct tcphdr *th) @@ -478,13 +475,12 @@ static inline int tcp_state_idx(struct tcphdr *th) } static inline void -set_tcp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp, +set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, int direction, struct tcphdr *th) { int state_idx; int new_state = IP_VS_TCP_S_CLOSE; int state_off = tcp_state_off[direction]; - struct ip_vs_proto_data *pd; /* Temp fix */ /* * Update state offset to INPUT_ONLY if necessary @@ -502,7 +498,8 @@ set_tcp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp, goto tcp_state_out; } - new_state = tcp_state_table[state_off+state_idx].next_state[cp->state]; + new_state = + pd->tcp_state_table[state_off+state_idx].next_state[cp->state]; tcp_state_out: if (new_state != cp->state) { @@ -510,7 +507,7 @@ set_tcp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp, IP_VS_DBG_BUF(8, "%s %s [%c%c%c%c] %s:%d->" "%s:%d state: %s->%s conn->refcnt:%d\n", - pp->name, + pd->pp->name, ((state_off == TCP_DIR_OUTPUT) ? "output " : "input "), th->syn ? 'S' : '.', @@ -540,7 +537,6 @@ set_tcp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp, } } - pd = ip_vs_proto_data_get(&init_net, pp->protocol); if (likely(pd)) cp->timeout = pd->timeout_table[cp->state = new_state]; else /* What to do ? */ @@ -553,7 +549,7 @@ set_tcp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp, static int tcp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_protocol *pp) + struct ip_vs_proto_data *pd) { struct tcphdr _tcph, *th; @@ -568,7 +564,7 @@ tcp_state_transition(struct ip_vs_conn *cp, int direction, return 0; spin_lock(&cp->lock); - set_tcp_state(pp, cp, direction, th); + set_tcp_state(pd, cp, direction, th); spin_unlock(&cp->lock); return 1; @@ -691,6 +687,7 @@ static void __ip_vs_tcp_init(struct net *net, struct ip_vs_proto_data *pd) spin_lock_init(&ipvs->tcp_app_lock); pd->timeout_table = ip_vs_create_timeout_table((int *)tcp_timeouts, sizeof(tcp_timeouts)); + pd->tcp_state_table = tcp_states; } static void __ip_vs_tcp_exit(struct net *net, struct ip_vs_proto_data *pd) diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index 71a4721a8f8a..aa85df2f14a0 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -29,7 +29,7 @@ #include static int -udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, int *verdict, struct ip_vs_conn **cpp) { struct net *net; @@ -64,10 +64,10 @@ udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, * Let the virtual server select a real server for the * incoming connection, and create a connection entry. */ - *cpp = ip_vs_schedule(svc, skb, pp, &ignored); + *cpp = ip_vs_schedule(svc, skb, pd, &ignored); if (!*cpp && ignored <= 0) { if (!ignored) - *verdict = ip_vs_leave(svc, skb, pp); + *verdict = ip_vs_leave(svc, skb, pd); else { ip_vs_service_put(svc); *verdict = NF_DROP; @@ -457,11 +457,8 @@ static const char * udp_state_name(int state) static int udp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_protocol *pp) + struct ip_vs_proto_data *pd) { - struct ip_vs_proto_data *pd; /* Temp fix, pp will be replaced by pd */ - - pd = ip_vs_proto_data_get(&init_net, IPPROTO_UDP); if (unlikely(!pd)) { pr_err("UDP no ns data\n"); return 0; diff --git a/net/netfilter/xt_ipvs.c b/net/netfilter/xt_ipvs.c index 9127a3d8aa35..bb10b0717f1b 100644 --- a/net/netfilter/xt_ipvs.c +++ b/net/netfilter/xt_ipvs.c @@ -85,7 +85,7 @@ ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par) /* * Check if the packet belongs to an existing entry */ - cp = pp->conn_out_get(family, skb, pp, &iph, iph.len, 1 /* inverse */); + cp = pp->conn_out_get(family, skb, &iph, iph.len, 1 /* inverse */); if (unlikely(cp == NULL)) { match = false; goto out; -- cgit v1.2.3 From 9bbac6a904d0816dae58b454692c54d6773cc20d Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:52 +0100 Subject: IPVS: netns, common protocol changes and use of appcnt. appcnt and timeout_table moved from struct ip_vs_protocol to ip_vs proto_data. struct net *net added as first param to - register_app() - unregister_app() - app_conn_bind() - ip_vs_conn_new() [horms@verge.net.au: removed cosmetic-change-only hunk] Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 2 -- net/netfilter/ipvs/ip_vs_conn.c | 6 ++-- net/netfilter/ipvs/ip_vs_proto_sctp.c | 4 +-- net/netfilter/ipvs/ip_vs_proto_tcp.c | 5 ++-- net/netfilter/ipvs/ip_vs_proto_udp.c | 4 +-- net/netfilter/ipvs/ip_vs_sync.c | 55 +++++++++++++++++++---------------- 6 files changed, 39 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 464ea365ca07..cc6ae621a9b5 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -360,8 +360,6 @@ struct ip_vs_protocol { u16 protocol; u16 num_states; int dont_defrag; - atomic_t appcnt; /* counter of proto app incs */ - int *timeout_table; /* protocol timeout table */ void (*init)(struct ip_vs_protocol *pp); diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index a7aba6a4697e..b2024c942345 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -804,7 +804,7 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, struct ip_vs_dest *dest, __u32 fwmark) { struct ip_vs_conn *cp; - struct ip_vs_protocol *pp = ip_vs_proto_get(p->protocol); + struct ip_vs_proto_data *pd = ip_vs_proto_data_get(&init_net, p->protocol); cp = kmem_cache_zalloc(ip_vs_conn_cachep, GFP_ATOMIC); if (cp == NULL) { @@ -863,8 +863,8 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, #endif ip_vs_bind_xmit(cp); - if (unlikely(pp && atomic_read(&pp->appcnt))) - ip_vs_bind_app(cp, pp); + if (unlikely(pd && atomic_read(&pd->appcnt))) + ip_vs_bind_app(cp, pd->pp); /* * Allow conntrack to be preserved. By default, conntrack diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 19bc37976ea7..0f14f793318a 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -1035,7 +1035,7 @@ static int sctp_register_app(struct ip_vs_app *inc) } } list_add(&inc->p_list, &ipvs->sctp_apps[hash]); - atomic_inc(&pd->pp->appcnt); + atomic_inc(&pd->appcnt); out: spin_unlock_bh(&ipvs->sctp_app_lock); @@ -1048,7 +1048,7 @@ static void sctp_unregister_app(struct ip_vs_app *inc) struct ip_vs_proto_data *pd = ip_vs_proto_data_get(&init_net, IPPROTO_SCTP); spin_lock_bh(&ipvs->sctp_app_lock); - atomic_dec(&pd->pp->appcnt); + atomic_dec(&pd->appcnt); list_del(&inc->p_list); spin_unlock_bh(&ipvs->sctp_app_lock); } diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index d7c245532798..290b3803d8ce 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -596,7 +596,7 @@ static int tcp_register_app(struct ip_vs_app *inc) } } list_add(&inc->p_list, &ipvs->tcp_apps[hash]); - atomic_inc(&pd->pp->appcnt); + atomic_inc(&pd->appcnt); out: spin_unlock_bh(&ipvs->tcp_app_lock); @@ -611,7 +611,7 @@ tcp_unregister_app(struct ip_vs_app *inc) struct ip_vs_proto_data *pd = ip_vs_proto_data_get(&init_net, IPPROTO_TCP); spin_lock_bh(&ipvs->tcp_app_lock); - atomic_dec(&pd->pp->appcnt); + atomic_dec(&pd->appcnt); list_del(&inc->p_list); spin_unlock_bh(&ipvs->tcp_app_lock); } @@ -701,7 +701,6 @@ struct ip_vs_protocol ip_vs_protocol_tcp = { .protocol = IPPROTO_TCP, .num_states = IP_VS_TCP_S_LAST, .dont_defrag = 0, - .appcnt = ATOMIC_INIT(0), .init = NULL, .exit = NULL, .init_netns = __ip_vs_tcp_init, diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index aa85df2f14a0..3719837a8fdc 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -373,7 +373,7 @@ static int udp_register_app(struct ip_vs_app *inc) } } list_add(&inc->p_list, &ipvs->udp_apps[hash]); - atomic_inc(&pd->pp->appcnt); + atomic_inc(&pd->appcnt); out: spin_unlock_bh(&ipvs->udp_app_lock); @@ -388,7 +388,7 @@ udp_unregister_app(struct ip_vs_app *inc) struct netns_ipvs *ipvs = net_ipvs(&init_net); spin_lock_bh(&ipvs->udp_app_lock); - atomic_dec(&pd->pp->appcnt); + atomic_dec(&pd->appcnt); list_del(&inc->p_list); spin_unlock_bh(&ipvs->udp_app_lock); } diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 662aa2c22a05..6831e8fac8db 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -725,17 +725,16 @@ ip_vs_conn_fill_param_sync(int af, union ip_vs_sync_conn *sc, * Param: ... * timeout is in sec. */ -static void ip_vs_proc_conn(struct ip_vs_conn_param *param, unsigned flags, - unsigned state, unsigned protocol, unsigned type, +static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param, + unsigned int flags, unsigned int state, + unsigned int protocol, unsigned int type, const union nf_inet_addr *daddr, __be16 dport, unsigned long timeout, __u32 fwmark, - struct ip_vs_sync_conn_options *opt, - struct ip_vs_protocol *pp) + struct ip_vs_sync_conn_options *opt) { struct ip_vs_dest *dest; struct ip_vs_conn *cp; - if (!(flags & IP_VS_CONN_F_TEMPLATE)) cp = ip_vs_conn_in_get(param); else @@ -821,17 +820,23 @@ static void ip_vs_proc_conn(struct ip_vs_conn_param *param, unsigned flags, if (timeout > MAX_SCHEDULE_TIMEOUT / HZ) timeout = MAX_SCHEDULE_TIMEOUT / HZ; cp->timeout = timeout*HZ; - } else if (!(flags & IP_VS_CONN_F_TEMPLATE) && pp->timeout_table) - cp->timeout = pp->timeout_table[state]; - else - cp->timeout = (3*60*HZ); + } else { + struct ip_vs_proto_data *pd; + + pd = ip_vs_proto_data_get(net, protocol); + if (!(flags & IP_VS_CONN_F_TEMPLATE) && pd && pd->timeout_table) + cp->timeout = pd->timeout_table[state]; + else + cp->timeout = (3*60*HZ); + } ip_vs_conn_put(cp); } /* * Process received multicast message for Version 0 */ -static void ip_vs_process_message_v0(const char *buffer, const size_t buflen) +static void ip_vs_process_message_v0(struct net *net, const char *buffer, + const size_t buflen) { struct ip_vs_sync_mesg_v0 *m = (struct ip_vs_sync_mesg_v0 *)buffer; struct ip_vs_sync_conn_v0 *s; @@ -879,7 +884,6 @@ static void ip_vs_process_message_v0(const char *buffer, const size_t buflen) } } else { /* protocol in templates is not used for state/timeout */ - pp = NULL; if (state > 0) { IP_VS_DBG(2, "BACKUP v0, Invalid template state %u\n", state); @@ -894,9 +898,9 @@ static void ip_vs_process_message_v0(const char *buffer, const size_t buflen) s->vport, ¶m); /* Send timeout as Zero */ - ip_vs_proc_conn(¶m, flags, state, s->protocol, AF_INET, + ip_vs_proc_conn(net, ¶m, flags, state, s->protocol, AF_INET, (union nf_inet_addr *)&s->daddr, s->dport, - 0, 0, opt, pp); + 0, 0, opt); } } @@ -945,7 +949,7 @@ static int ip_vs_proc_str(__u8 *p, unsigned int plen, unsigned int *data_len, /* * Process a Version 1 sync. connection */ -static inline int ip_vs_proc_sync_conn(__u8 *p, __u8 *msg_end) +static inline int ip_vs_proc_sync_conn(struct net *net, __u8 *p, __u8 *msg_end) { struct ip_vs_sync_conn_options opt; union ip_vs_sync_conn *s; @@ -1043,7 +1047,6 @@ static inline int ip_vs_proc_sync_conn(__u8 *p, __u8 *msg_end) } } else { /* protocol in templates is not used for state/timeout */ - pp = NULL; if (state > 0) { IP_VS_DBG(3, "BACKUP, Invalid template state %u\n", state); @@ -1058,18 +1061,18 @@ static inline int ip_vs_proc_sync_conn(__u8 *p, __u8 *msg_end) } /* If only IPv4, just silent skip IPv6 */ if (af == AF_INET) - ip_vs_proc_conn(¶m, flags, state, s->v4.protocol, af, + ip_vs_proc_conn(net, ¶m, flags, state, s->v4.protocol, af, (union nf_inet_addr *)&s->v4.daddr, s->v4.dport, ntohl(s->v4.timeout), ntohl(s->v4.fwmark), - (opt_flags & IPVS_OPT_F_SEQ_DATA ? &opt : NULL), - pp); + (opt_flags & IPVS_OPT_F_SEQ_DATA ? &opt : NULL) + ); #ifdef CONFIG_IP_VS_IPV6 else - ip_vs_proc_conn(¶m, flags, state, s->v6.protocol, af, + ip_vs_proc_conn(net, ¶m, flags, state, s->v6.protocol, af, (union nf_inet_addr *)&s->v6.daddr, s->v6.dport, ntohl(s->v6.timeout), ntohl(s->v6.fwmark), - (opt_flags & IPVS_OPT_F_SEQ_DATA ? &opt : NULL), - pp); + (opt_flags & IPVS_OPT_F_SEQ_DATA ? &opt : NULL) + ); #endif return 0; /* Error exit */ @@ -1083,7 +1086,8 @@ out: * ip_vs_conn entries. * Handles Version 0 & 1 */ -static void ip_vs_process_message(__u8 *buffer, const size_t buflen) +static void ip_vs_process_message(struct net *net, __u8 *buffer, + const size_t buflen) { struct ip_vs_sync_mesg *m2 = (struct ip_vs_sync_mesg *)buffer; __u8 *p, *msg_end; @@ -1136,7 +1140,8 @@ static void ip_vs_process_message(__u8 *buffer, const size_t buflen) return; } /* Process a single sync_conn */ - if ((retc=ip_vs_proc_sync_conn(p, msg_end)) < 0) { + retc = ip_vs_proc_sync_conn(net, p, msg_end); + if (retc < 0) { IP_VS_ERR_RL("BACKUP, Dropping buffer, Err: %d in decoding\n", retc); return; @@ -1146,7 +1151,7 @@ static void ip_vs_process_message(__u8 *buffer, const size_t buflen) } } else { /* Old type of message */ - ip_vs_process_message_v0(buffer, buflen); + ip_vs_process_message_v0(net, buffer, buflen); return; } } @@ -1500,7 +1505,7 @@ static int sync_thread_backup(void *data) /* disable bottom half, because it accesses the data shared by softirq while getting/creating conns */ local_bh_disable(); - ip_vs_process_message(tinfo->buf, len); + ip_vs_process_message(&init_net, tinfo->buf, len); local_bh_enable(); } } -- cgit v1.2.3 From ab8a5e8408c3df2d654611bffc3aaf04f418b266 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:53 +0100 Subject: IPVS: netns awareness to ip_vs_app All variables moved to struct ipvs, most external changes fixed (i.e. init_net removed) in ip_vs_protocol param struct net *net added to: - register_app() - unregister_app() This affected almost all proto_xxx.c files Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 12 +++--- include/net/netns/ip_vs.h | 5 +++ net/netfilter/ipvs/ip_vs_app.c | 73 +++++++++++++++++++++-------------- net/netfilter/ipvs/ip_vs_ftp.c | 8 ++-- net/netfilter/ipvs/ip_vs_proto_sctp.c | 12 +++--- net/netfilter/ipvs/ip_vs_proto_tcp.c | 12 +++--- net/netfilter/ipvs/ip_vs_proto_udp.c | 12 +++--- 7 files changed, 76 insertions(+), 58 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index cc6ae621a9b5..0cdd8ce454c2 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -402,9 +402,9 @@ struct ip_vs_protocol { const struct sk_buff *skb, struct ip_vs_proto_data *pd); - int (*register_app)(struct ip_vs_app *inc); + int (*register_app)(struct net *net, struct ip_vs_app *inc); - void (*unregister_app)(struct ip_vs_app *inc); + void (*unregister_app)(struct net *net, struct ip_vs_app *inc); int (*app_conn_bind)(struct ip_vs_conn *cp); @@ -871,12 +871,12 @@ ip_vs_control_add(struct ip_vs_conn *cp, struct ip_vs_conn *ctl_cp) * (from ip_vs_app.c) */ #define IP_VS_APP_MAX_PORTS 8 -extern int register_ip_vs_app(struct ip_vs_app *app); -extern void unregister_ip_vs_app(struct ip_vs_app *app); +extern int register_ip_vs_app(struct net *net, struct ip_vs_app *app); +extern void unregister_ip_vs_app(struct net *net, struct ip_vs_app *app); extern int ip_vs_bind_app(struct ip_vs_conn *cp, struct ip_vs_protocol *pp); extern void ip_vs_unbind_app(struct ip_vs_conn *cp); -extern int -register_ip_vs_app_inc(struct ip_vs_app *app, __u16 proto, __u16 port); +extern int register_ip_vs_app_inc(struct net *net, struct ip_vs_app *app, + __u16 proto, __u16 port); extern int ip_vs_app_inc_get(struct ip_vs_app *inc); extern void ip_vs_app_inc_put(struct ip_vs_app *inc); diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h index 58bd3fd85a97..03f7fe1bede6 100644 --- a/include/net/netns/ip_vs.h +++ b/include/net/netns/ip_vs.h @@ -28,6 +28,11 @@ struct netns_ipvs { #define IP_VS_RTAB_MASK (IP_VS_RTAB_SIZE - 1) struct list_head rs_table[IP_VS_RTAB_SIZE]; + /* ip_vs_app */ + struct list_head app_list; + struct mutex app_mutex; + struct lock_class_key app_key; /* mutex debuging */ + /* ip_vs_proto */ #define IP_VS_PROTO_TAB_SIZE 32 /* must be power of 2 */ struct ip_vs_proto_data *proto_data_table[IP_VS_PROTO_TAB_SIZE]; diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index 40b09ccc4896..286f46594e0e 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -43,11 +43,6 @@ EXPORT_SYMBOL(register_ip_vs_app); EXPORT_SYMBOL(unregister_ip_vs_app); EXPORT_SYMBOL(register_ip_vs_app_inc); -/* ipvs application list head */ -static LIST_HEAD(ip_vs_app_list); -static DEFINE_MUTEX(__ip_vs_app_mutex); - - /* * Get an ip_vs_app object */ @@ -67,7 +62,8 @@ static inline void ip_vs_app_put(struct ip_vs_app *app) * Allocate/initialize app incarnation and register it in proto apps. */ static int -ip_vs_app_inc_new(struct ip_vs_app *app, __u16 proto, __u16 port) +ip_vs_app_inc_new(struct net *net, struct ip_vs_app *app, __u16 proto, + __u16 port) { struct ip_vs_protocol *pp; struct ip_vs_app *inc; @@ -98,7 +94,7 @@ ip_vs_app_inc_new(struct ip_vs_app *app, __u16 proto, __u16 port) } } - ret = pp->register_app(inc); + ret = pp->register_app(net, inc); if (ret) goto out; @@ -119,7 +115,7 @@ ip_vs_app_inc_new(struct ip_vs_app *app, __u16 proto, __u16 port) * Release app incarnation */ static void -ip_vs_app_inc_release(struct ip_vs_app *inc) +ip_vs_app_inc_release(struct net *net, struct ip_vs_app *inc) { struct ip_vs_protocol *pp; @@ -127,7 +123,7 @@ ip_vs_app_inc_release(struct ip_vs_app *inc) return; if (pp->unregister_app) - pp->unregister_app(inc); + pp->unregister_app(net, inc); IP_VS_DBG(9, "%s App %s:%u unregistered\n", pp->name, inc->name, ntohs(inc->port)); @@ -168,15 +164,17 @@ void ip_vs_app_inc_put(struct ip_vs_app *inc) * Register an application incarnation in protocol applications */ int -register_ip_vs_app_inc(struct ip_vs_app *app, __u16 proto, __u16 port) +register_ip_vs_app_inc(struct net *net, struct ip_vs_app *app, __u16 proto, + __u16 port) { + struct netns_ipvs *ipvs = net_ipvs(net); int result; - mutex_lock(&__ip_vs_app_mutex); + mutex_lock(&ipvs->app_mutex); - result = ip_vs_app_inc_new(app, proto, port); + result = ip_vs_app_inc_new(net, app, proto, port); - mutex_unlock(&__ip_vs_app_mutex); + mutex_unlock(&ipvs->app_mutex); return result; } @@ -185,16 +183,17 @@ register_ip_vs_app_inc(struct ip_vs_app *app, __u16 proto, __u16 port) /* * ip_vs_app registration routine */ -int register_ip_vs_app(struct ip_vs_app *app) +int register_ip_vs_app(struct net *net, struct ip_vs_app *app) { + struct netns_ipvs *ipvs = net_ipvs(net); /* increase the module use count */ ip_vs_use_count_inc(); - mutex_lock(&__ip_vs_app_mutex); + mutex_lock(&ipvs->app_mutex); - list_add(&app->a_list, &ip_vs_app_list); + list_add(&app->a_list, &ipvs->app_list); - mutex_unlock(&__ip_vs_app_mutex); + mutex_unlock(&ipvs->app_mutex); return 0; } @@ -204,19 +203,20 @@ int register_ip_vs_app(struct ip_vs_app *app) * ip_vs_app unregistration routine * We are sure there are no app incarnations attached to services */ -void unregister_ip_vs_app(struct ip_vs_app *app) +void unregister_ip_vs_app(struct net *net, struct ip_vs_app *app) { + struct netns_ipvs *ipvs = net_ipvs(net); struct ip_vs_app *inc, *nxt; - mutex_lock(&__ip_vs_app_mutex); + mutex_lock(&ipvs->app_mutex); list_for_each_entry_safe(inc, nxt, &app->incs_list, a_list) { - ip_vs_app_inc_release(inc); + ip_vs_app_inc_release(net, inc); } list_del(&app->a_list); - mutex_unlock(&__ip_vs_app_mutex); + mutex_unlock(&ipvs->app_mutex); /* decrease the module use count */ ip_vs_use_count_dec(); @@ -226,7 +226,8 @@ void unregister_ip_vs_app(struct ip_vs_app *app) /* * Bind ip_vs_conn to its ip_vs_app (called by cp constructor) */ -int ip_vs_bind_app(struct ip_vs_conn *cp, struct ip_vs_protocol *pp) +int ip_vs_bind_app(struct ip_vs_conn *cp, + struct ip_vs_protocol *pp) { return pp->app_conn_bind(cp); } @@ -481,11 +482,11 @@ int ip_vs_app_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb) * /proc/net/ip_vs_app entry function */ -static struct ip_vs_app *ip_vs_app_idx(loff_t pos) +static struct ip_vs_app *ip_vs_app_idx(struct netns_ipvs *ipvs, loff_t pos) { struct ip_vs_app *app, *inc; - list_for_each_entry(app, &ip_vs_app_list, a_list) { + list_for_each_entry(app, &ipvs->app_list, a_list) { list_for_each_entry(inc, &app->incs_list, a_list) { if (pos-- == 0) return inc; @@ -497,19 +498,24 @@ static struct ip_vs_app *ip_vs_app_idx(loff_t pos) static void *ip_vs_app_seq_start(struct seq_file *seq, loff_t *pos) { - mutex_lock(&__ip_vs_app_mutex); + struct net *net = seq_file_net(seq); + struct netns_ipvs *ipvs = net_ipvs(net); + + mutex_lock(&ipvs->app_mutex); - return *pos ? ip_vs_app_idx(*pos - 1) : SEQ_START_TOKEN; + return *pos ? ip_vs_app_idx(ipvs, *pos - 1) : SEQ_START_TOKEN; } static void *ip_vs_app_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct ip_vs_app *inc, *app; struct list_head *e; + struct net *net = seq_file_net(seq); + struct netns_ipvs *ipvs = net_ipvs(net); ++*pos; if (v == SEQ_START_TOKEN) - return ip_vs_app_idx(0); + return ip_vs_app_idx(ipvs, 0); inc = v; app = inc->app; @@ -518,7 +524,7 @@ static void *ip_vs_app_seq_next(struct seq_file *seq, void *v, loff_t *pos) return list_entry(e, struct ip_vs_app, a_list); /* go on to next application */ - for (e = app->a_list.next; e != &ip_vs_app_list; e = e->next) { + for (e = app->a_list.next; e != &ipvs->app_list; e = e->next) { app = list_entry(e, struct ip_vs_app, a_list); list_for_each_entry(inc, &app->incs_list, a_list) { return inc; @@ -529,7 +535,9 @@ static void *ip_vs_app_seq_next(struct seq_file *seq, void *v, loff_t *pos) static void ip_vs_app_seq_stop(struct seq_file *seq, void *v) { - mutex_unlock(&__ip_vs_app_mutex); + struct netns_ipvs *ipvs = net_ipvs(seq_file_net(seq)); + + mutex_unlock(&ipvs->app_mutex); } static int ip_vs_app_seq_show(struct seq_file *seq, void *v) @@ -557,7 +565,8 @@ static const struct seq_operations ip_vs_app_seq_ops = { static int ip_vs_app_open(struct inode *inode, struct file *file) { - return seq_open(file, &ip_vs_app_seq_ops); + return seq_open_net(inode, file, &ip_vs_app_seq_ops, + sizeof(struct seq_net_private)); } static const struct file_operations ip_vs_app_fops = { @@ -571,9 +580,13 @@ static const struct file_operations ip_vs_app_fops = { static int __net_init __ip_vs_app_init(struct net *net) { + struct netns_ipvs *ipvs = net_ipvs(net); + if (!net_eq(net, &init_net)) /* netns not enabled yet */ return -EPERM; + INIT_LIST_HEAD(&ipvs->app_list); + __mutex_init(&ipvs->app_mutex, "ipvs->app_mutex", &ipvs->app_key); proc_net_fops_create(net, "ip_vs_app", 0, &ip_vs_app_fops); return 0; } diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index b38ae941f677..77b0036dcb73 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -414,14 +414,14 @@ static int __net_init __ip_vs_ftp_init(struct net *net) if (!net_eq(net, &init_net)) /* netns not enabled yet */ return -EPERM; - ret = register_ip_vs_app(app); + ret = register_ip_vs_app(net, app); if (ret) return ret; for (i=0; iprotocol, ports[i]); + ret = register_ip_vs_app_inc(net, app, app->protocol, ports[i]); if (ret) break; pr_info("%s: loaded support on port[%d] = %d\n", @@ -429,7 +429,7 @@ static int __net_init __ip_vs_ftp_init(struct net *net) } if (ret) - unregister_ip_vs_app(app); + unregister_ip_vs_app(net, app); return ret; } @@ -443,7 +443,7 @@ static void __ip_vs_ftp_exit(struct net *net) if (!net_eq(net, &init_net)) /* netns not enabled yet */ return; - unregister_ip_vs_app(app); + unregister_ip_vs_app(net, app); } static struct pernet_operations ip_vs_ftp_ops = { diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 0f14f793318a..569e77bf08c4 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -1016,14 +1016,14 @@ static inline __u16 sctp_app_hashkey(__be16 port) & SCTP_APP_TAB_MASK; } -static int sctp_register_app(struct ip_vs_app *inc) +static int sctp_register_app(struct net *net, struct ip_vs_app *inc) { struct ip_vs_app *i; __u16 hash; __be16 port = inc->port; int ret = 0; - struct netns_ipvs *ipvs = net_ipvs(&init_net); - struct ip_vs_proto_data *pd = ip_vs_proto_data_get(&init_net, IPPROTO_SCTP); + struct netns_ipvs *ipvs = net_ipvs(net); + struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_SCTP); hash = sctp_app_hashkey(port); @@ -1042,10 +1042,10 @@ out: return ret; } -static void sctp_unregister_app(struct ip_vs_app *inc) +static void sctp_unregister_app(struct net *net, struct ip_vs_app *inc) { - struct netns_ipvs *ipvs = net_ipvs(&init_net); - struct ip_vs_proto_data *pd = ip_vs_proto_data_get(&init_net, IPPROTO_SCTP); + struct netns_ipvs *ipvs = net_ipvs(net); + struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_SCTP); spin_lock_bh(&ipvs->sctp_app_lock); atomic_dec(&pd->appcnt); diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 290b3803d8ce..757aaaf083bb 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -577,14 +577,14 @@ static inline __u16 tcp_app_hashkey(__be16 port) } -static int tcp_register_app(struct ip_vs_app *inc) +static int tcp_register_app(struct net *net, struct ip_vs_app *inc) { struct ip_vs_app *i; __u16 hash; __be16 port = inc->port; int ret = 0; - struct netns_ipvs *ipvs = net_ipvs(&init_net); - struct ip_vs_proto_data *pd = ip_vs_proto_data_get(&init_net, IPPROTO_TCP); + struct netns_ipvs *ipvs = net_ipvs(net); + struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_TCP); hash = tcp_app_hashkey(port); @@ -605,10 +605,10 @@ static int tcp_register_app(struct ip_vs_app *inc) static void -tcp_unregister_app(struct ip_vs_app *inc) +tcp_unregister_app(struct net *net, struct ip_vs_app *inc) { - struct netns_ipvs *ipvs = net_ipvs(&init_net); - struct ip_vs_proto_data *pd = ip_vs_proto_data_get(&init_net, IPPROTO_TCP); + struct netns_ipvs *ipvs = net_ipvs(net); + struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_TCP); spin_lock_bh(&ipvs->tcp_app_lock); atomic_dec(&pd->appcnt); diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index 3719837a8fdc..1dc394100fa8 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -353,14 +353,14 @@ static inline __u16 udp_app_hashkey(__be16 port) } -static int udp_register_app(struct ip_vs_app *inc) +static int udp_register_app(struct net *net, struct ip_vs_app *inc) { struct ip_vs_app *i; __u16 hash; __be16 port = inc->port; int ret = 0; - struct netns_ipvs *ipvs = net_ipvs(&init_net); - struct ip_vs_proto_data *pd = ip_vs_proto_data_get(&init_net, IPPROTO_UDP); + struct netns_ipvs *ipvs = net_ipvs(net); + struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_UDP); hash = udp_app_hashkey(port); @@ -382,10 +382,10 @@ static int udp_register_app(struct ip_vs_app *inc) static void -udp_unregister_app(struct ip_vs_app *inc) +udp_unregister_app(struct net *net, struct ip_vs_app *inc) { - struct ip_vs_proto_data *pd = ip_vs_proto_data_get(&init_net, IPPROTO_UDP); - struct netns_ipvs *ipvs = net_ipvs(&init_net); + struct ip_vs_proto_data *pd = ip_vs_proto_data_get(net, IPPROTO_UDP); + struct netns_ipvs *ipvs = net_ipvs(net); spin_lock_bh(&ipvs->udp_app_lock); atomic_dec(&pd->appcnt); -- cgit v1.2.3 From 29c2026fd4980c144d9c746dc1565060f08e5796 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:54 +0100 Subject: IPVS: netns awareness to ip_vs_est All variables moved to struct ipvs, most external changes fixed (i.e. init_net removed) *v3 timer per ns instead of a common timer in estimator. Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 4 +- include/net/netns/ip_vs.h | 4 ++ net/netfilter/ipvs/ip_vs_ctl.c | 20 +++++----- net/netfilter/ipvs/ip_vs_est.c | 86 +++++++++++++++++++++++------------------- 4 files changed, 64 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 0cdd8ce454c2..c08927bb1728 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -1004,8 +1004,8 @@ extern void ip_vs_sync_cleanup(void); */ extern int ip_vs_estimator_init(void); extern void ip_vs_estimator_cleanup(void); -extern void ip_vs_new_estimator(struct ip_vs_stats *stats); -extern void ip_vs_kill_estimator(struct ip_vs_stats *stats); +extern void ip_vs_new_estimator(struct net *net, struct ip_vs_stats *stats); +extern void ip_vs_kill_estimator(struct net *net, struct ip_vs_stats *stats); extern void ip_vs_zero_estimator(struct ip_vs_stats *stats); /* diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h index 03f7fe1bede6..db0240198339 100644 --- a/include/net/netns/ip_vs.h +++ b/include/net/netns/ip_vs.h @@ -70,6 +70,10 @@ struct netns_ipvs { int sysctl_lblcr_expiration; struct ctl_table_header *lblcr_ctl_header; struct ctl_table *lblcr_ctl_table; + /* ip_vs_est */ + struct list_head est_list; /* estimator list */ + spinlock_t est_lock; + struct timer_list est_timer; /* Estimation timer */ }; #endif /* IP_VS_H_ */ diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 88474f1e828a..c89beb8eafbb 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -816,7 +816,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, spin_unlock(&dest->dst_lock); if (add) - ip_vs_new_estimator(&dest->stats); + ip_vs_new_estimator(svc->net, &dest->stats); write_lock_bh(&__ip_vs_svc_lock); @@ -1009,9 +1009,9 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) /* * Delete a destination (must be already unlinked from the service) */ -static void __ip_vs_del_dest(struct ip_vs_dest *dest) +static void __ip_vs_del_dest(struct net *net, struct ip_vs_dest *dest) { - ip_vs_kill_estimator(&dest->stats); + ip_vs_kill_estimator(net, &dest->stats); /* * Remove it from the d-linked list with the real services. @@ -1080,6 +1080,7 @@ static int ip_vs_del_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) { struct ip_vs_dest *dest; + struct net *net = svc->net; __be16 dport = udest->port; EnterFunction(2); @@ -1108,7 +1109,7 @@ ip_vs_del_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) /* * Delete the destination */ - __ip_vs_del_dest(dest); + __ip_vs_del_dest(net, dest); LeaveFunction(2); @@ -1197,7 +1198,7 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u, else if (svc->port == 0) atomic_inc(&ip_vs_nullsvc_counter); - ip_vs_new_estimator(&svc->stats); + ip_vs_new_estimator(net, &svc->stats); /* Count only IPv4 services for old get/setsockopt interface */ if (svc->af == AF_INET) @@ -1345,7 +1346,7 @@ static void __ip_vs_del_service(struct ip_vs_service *svc) if (svc->af == AF_INET) ip_vs_num_services--; - ip_vs_kill_estimator(&svc->stats); + ip_vs_kill_estimator(svc->net, &svc->stats); /* Unbind scheduler */ old_sched = svc->scheduler; @@ -1368,7 +1369,7 @@ static void __ip_vs_del_service(struct ip_vs_service *svc) */ list_for_each_entry_safe(dest, nxt, &svc->destinations, n_list) { __ip_vs_unlink_dest(svc, dest, 0); - __ip_vs_del_dest(dest); + __ip_vs_del_dest(svc->net, dest); } /* @@ -3460,7 +3461,7 @@ int __net_init __ip_vs_control_init(struct net *net) vs_vars); if (sysctl_header == NULL) goto err_reg; - ip_vs_new_estimator(&ip_vs_stats); + ip_vs_new_estimator(net, &ip_vs_stats); return 0; err_reg: @@ -3472,7 +3473,7 @@ static void __net_exit __ip_vs_control_cleanup(struct net *net) if (!net_eq(net, &init_net)) /* netns not enabled yet */ return; - ip_vs_kill_estimator(&ip_vs_stats); + ip_vs_kill_estimator(net, &ip_vs_stats); unregister_net_sysctl_table(sysctl_header); proc_net_remove(net, "ip_vs_stats"); proc_net_remove(net, "ip_vs"); @@ -3536,7 +3537,6 @@ void ip_vs_control_cleanup(void) ip_vs_trash_cleanup(); cancel_delayed_work_sync(&defense_work); cancel_work_sync(&defense_work.work); - ip_vs_kill_estimator(&ip_vs_stats); unregister_pernet_subsys(&ipvs_control_ops); ip_vs_genl_unregister(); nf_unregister_sockopt(&ip_vs_sockopts); diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index 7417a0c1408b..07d839bef537 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -8,8 +8,12 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Changes: - * + * Changes: Hans Schillstrom + * Network name space (netns) aware. + * Global data moved to netns i.e struct netns_ipvs + * Affected data: est_list and est_lock. + * estimation_timer() runs with timer per netns. + * get_stats()) do the per cpu summing. */ #define KMSG_COMPONENT "IPVS" @@ -48,12 +52,6 @@ */ -static void estimation_timer(unsigned long arg); - -static LIST_HEAD(est_list); -static DEFINE_SPINLOCK(est_lock); -static DEFINE_TIMER(est_timer, estimation_timer, 0, 0); - static void estimation_timer(unsigned long arg) { struct ip_vs_estimator *e; @@ -62,9 +60,12 @@ static void estimation_timer(unsigned long arg) u32 n_inpkts, n_outpkts; u64 n_inbytes, n_outbytes; u32 rate; + struct net *net = (struct net *)arg; + struct netns_ipvs *ipvs; - spin_lock(&est_lock); - list_for_each_entry(e, &est_list, list) { + ipvs = net_ipvs(net); + spin_lock(&ipvs->est_lock); + list_for_each_entry(e, &ipvs->est_list, list) { s = container_of(e, struct ip_vs_stats, est); spin_lock(&s->lock); @@ -75,38 +76,39 @@ static void estimation_timer(unsigned long arg) n_outbytes = s->ustats.outbytes; /* scaled by 2^10, but divided 2 seconds */ - rate = (n_conns - e->last_conns)<<9; + rate = (n_conns - e->last_conns) << 9; e->last_conns = n_conns; - e->cps += ((long)rate - (long)e->cps)>>2; - s->ustats.cps = (e->cps+0x1FF)>>10; + e->cps += ((long)rate - (long)e->cps) >> 2; + s->ustats.cps = (e->cps + 0x1FF) >> 10; - rate = (n_inpkts - e->last_inpkts)<<9; + rate = (n_inpkts - e->last_inpkts) << 9; e->last_inpkts = n_inpkts; - e->inpps += ((long)rate - (long)e->inpps)>>2; - s->ustats.inpps = (e->inpps+0x1FF)>>10; + e->inpps += ((long)rate - (long)e->inpps) >> 2; + s->ustats.inpps = (e->inpps + 0x1FF) >> 10; - rate = (n_outpkts - e->last_outpkts)<<9; + rate = (n_outpkts - e->last_outpkts) << 9; e->last_outpkts = n_outpkts; - e->outpps += ((long)rate - (long)e->outpps)>>2; - s->ustats.outpps = (e->outpps+0x1FF)>>10; + e->outpps += ((long)rate - (long)e->outpps) >> 2; + s->ustats.outpps = (e->outpps + 0x1FF) >> 10; - rate = (n_inbytes - e->last_inbytes)<<4; + rate = (n_inbytes - e->last_inbytes) << 4; e->last_inbytes = n_inbytes; - e->inbps += ((long)rate - (long)e->inbps)>>2; - s->ustats.inbps = (e->inbps+0xF)>>5; + e->inbps += ((long)rate - (long)e->inbps) >> 2; + s->ustats.inbps = (e->inbps + 0xF) >> 5; - rate = (n_outbytes - e->last_outbytes)<<4; + rate = (n_outbytes - e->last_outbytes) << 4; e->last_outbytes = n_outbytes; - e->outbps += ((long)rate - (long)e->outbps)>>2; - s->ustats.outbps = (e->outbps+0xF)>>5; + e->outbps += ((long)rate - (long)e->outbps) >> 2; + s->ustats.outbps = (e->outbps + 0xF) >> 5; spin_unlock(&s->lock); } - spin_unlock(&est_lock); - mod_timer(&est_timer, jiffies + 2*HZ); + spin_unlock(&ipvs->est_lock); + mod_timer(&ipvs->est_timer, jiffies + 2*HZ); } -void ip_vs_new_estimator(struct ip_vs_stats *stats) +void ip_vs_new_estimator(struct net *net, struct ip_vs_stats *stats) { + struct netns_ipvs *ipvs = net_ipvs(net); struct ip_vs_estimator *est = &stats->est; INIT_LIST_HEAD(&est->list); @@ -126,18 +128,19 @@ void ip_vs_new_estimator(struct ip_vs_stats *stats) est->last_outbytes = stats->ustats.outbytes; est->outbps = stats->ustats.outbps<<5; - spin_lock_bh(&est_lock); - list_add(&est->list, &est_list); - spin_unlock_bh(&est_lock); + spin_lock_bh(&ipvs->est_lock); + list_add(&est->list, &ipvs->est_list); + spin_unlock_bh(&ipvs->est_lock); } -void ip_vs_kill_estimator(struct ip_vs_stats *stats) +void ip_vs_kill_estimator(struct net *net, struct ip_vs_stats *stats) { + struct netns_ipvs *ipvs = net_ipvs(net); struct ip_vs_estimator *est = &stats->est; - spin_lock_bh(&est_lock); + spin_lock_bh(&ipvs->est_lock); list_del(&est->list); - spin_unlock_bh(&est_lock); + spin_unlock_bh(&ipvs->est_lock); } void ip_vs_zero_estimator(struct ip_vs_stats *stats) @@ -159,14 +162,25 @@ void ip_vs_zero_estimator(struct ip_vs_stats *stats) static int __net_init __ip_vs_estimator_init(struct net *net) { + struct netns_ipvs *ipvs = net_ipvs(net); + if (!net_eq(net, &init_net)) /* netns not enabled yet */ return -EPERM; + INIT_LIST_HEAD(&ipvs->est_list); + spin_lock_init(&ipvs->est_lock); + setup_timer(&ipvs->est_timer, estimation_timer, (unsigned long)net); + mod_timer(&ipvs->est_timer, jiffies + 2 * HZ); return 0; } +static void __net_exit __ip_vs_estimator_exit(struct net *net) +{ + del_timer_sync(&net_ipvs(net)->est_timer); +} static struct pernet_operations ip_vs_app_ops = { .init = __ip_vs_estimator_init, + .exit = __ip_vs_estimator_exit, }; int __init ip_vs_estimator_init(void) @@ -174,14 +188,10 @@ int __init ip_vs_estimator_init(void) int rv; rv = register_pernet_subsys(&ip_vs_app_ops); - if (rv < 0) - return rv; - mod_timer(&est_timer, jiffies + 2 * HZ); return rv; } void ip_vs_estimator_cleanup(void) { - del_timer_sync(&est_timer); unregister_pernet_subsys(&ip_vs_app_ops); } -- cgit v1.2.3 From f131315fa272d337dfca7dad2f033ff5296dad65 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:55 +0100 Subject: IPVS: netns awareness to ip_vs_sync All global variables moved to struct ipvs, most external changes fixed (i.e. init_net removed) in sync_buf create + 4 replaced by sizeof(struct..) Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 14 +- include/net/netns/ip_vs.h | 16 ++ net/netfilter/ipvs/ip_vs_core.c | 15 +- net/netfilter/ipvs/ip_vs_ctl.c | 52 ++++--- net/netfilter/ipvs/ip_vs_sync.c | 334 +++++++++++++++++++++------------------- 5 files changed, 240 insertions(+), 191 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index c08927bb1728..4265b5e00c94 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -958,7 +958,7 @@ extern struct ip_vs_stats ip_vs_stats; extern const struct ctl_path net_vs_ctl_path[]; extern int sysctl_ip_vs_sync_ver; -extern void ip_vs_sync_switch_mode(int mode); +extern void ip_vs_sync_switch_mode(struct net *net, int mode); extern struct ip_vs_service * ip_vs_service_get(struct net *net, int af, __u32 fwmark, __u16 protocol, const union nf_inet_addr *vaddr, __be16 vport); @@ -987,14 +987,10 @@ extern struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp); * IPVS sync daemon data and function prototypes * (from ip_vs_sync.c) */ -extern volatile int ip_vs_sync_state; -extern volatile int ip_vs_master_syncid; -extern volatile int ip_vs_backup_syncid; -extern char ip_vs_master_mcast_ifn[IP_VS_IFNAME_MAXLEN]; -extern char ip_vs_backup_mcast_ifn[IP_VS_IFNAME_MAXLEN]; -extern int start_sync_thread(int state, char *mcast_ifn, __u8 syncid); -extern int stop_sync_thread(int state); -extern void ip_vs_sync_conn(struct ip_vs_conn *cp); +extern int start_sync_thread(struct net *net, int state, char *mcast_ifn, + __u8 syncid); +extern int stop_sync_thread(struct net *net, int state); +extern void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp); extern int ip_vs_sync_init(void); extern void ip_vs_sync_cleanup(void); diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h index db0240198339..aba78f3c8341 100644 --- a/include/net/netns/ip_vs.h +++ b/include/net/netns/ip_vs.h @@ -74,6 +74,22 @@ struct netns_ipvs { struct list_head est_list; /* estimator list */ spinlock_t est_lock; struct timer_list est_timer; /* Estimation timer */ + /* ip_vs_sync */ + struct list_head sync_queue; + spinlock_t sync_lock; + struct ip_vs_sync_buff *sync_buff; + spinlock_t sync_buff_lock; + struct sockaddr_in sync_mcast_addr; + struct task_struct *master_thread; + struct task_struct *backup_thread; + int send_mesg_maxlen; + int recv_mesg_maxlen; + volatile int sync_state; + volatile int master_syncid; + volatile int backup_syncid; + /* multicast interface name */ + char master_mcast_ifn[IP_VS_IFNAME_MAXLEN]; + char backup_mcast_ifn[IP_VS_IFNAME_MAXLEN]; }; #endif /* IP_VS_H_ */ diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 9317affc5ea1..5531d569aa5e 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1471,12 +1471,13 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) static unsigned int ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) { - struct net *net = NULL; + struct net *net; struct ip_vs_iphdr iph; struct ip_vs_protocol *pp; struct ip_vs_proto_data *pd; struct ip_vs_conn *cp; int ret, restart, pkts; + struct netns_ipvs *ipvs; /* Already marked as IPVS request or reply? */ if (skb->ipvs_property) @@ -1556,7 +1557,8 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) } IP_VS_DBG_PKT(11, af, pp, skb, 0, "Incoming packet"); - + net = skb_net(skb); + ipvs = net_ipvs(net); /* Check the server status */ if (cp->dest && !(cp->dest->flags & IP_VS_DEST_F_AVAILABLE)) { /* the destination server is not available */ @@ -1589,12 +1591,13 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) * * For ONE_PKT let ip_vs_sync_conn() do the filter work. */ + if (cp->flags & IP_VS_CONN_F_ONE_PACKET) pkts = sysctl_ip_vs_sync_threshold[0]; else pkts = atomic_add_return(1, &cp->in_pkts); - if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && + if ((ipvs->sync_state & IP_VS_STATE_MASTER) && cp->protocol == IPPROTO_SCTP) { if ((cp->state == IP_VS_SCTP_S_ESTABLISHED && (pkts % sysctl_ip_vs_sync_threshold[1] @@ -1603,13 +1606,13 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) ((cp->state == IP_VS_SCTP_S_CLOSED) || (cp->state == IP_VS_SCTP_S_SHUT_ACK_CLI) || (cp->state == IP_VS_SCTP_S_SHUT_ACK_SER)))) { - ip_vs_sync_conn(cp); + ip_vs_sync_conn(net, cp); goto out; } } /* Keep this block last: TCP and others with pp->num_states <= 1 */ - else if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && + else if ((ipvs->sync_state & IP_VS_STATE_MASTER) && (((cp->protocol != IPPROTO_TCP || cp->state == IP_VS_TCP_S_ESTABLISHED) && (pkts % sysctl_ip_vs_sync_threshold[1] @@ -1619,7 +1622,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) (cp->state == IP_VS_TCP_S_CLOSE) || (cp->state == IP_VS_TCP_S_CLOSE_WAIT) || (cp->state == IP_VS_TCP_S_TIME_WAIT))))) - ip_vs_sync_conn(cp); + ip_vs_sync_conn(net, cp); out: cp->old_state = cp->state; diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index c89beb8eafbb..03f86312b4bb 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1559,7 +1559,8 @@ proc_do_sync_mode(ctl_table *table, int write, /* Restore the correct value */ *valp = val; } else { - ip_vs_sync_switch_mode(val); + struct net *net = current->nsproxy->net_ns; + ip_vs_sync_switch_mode(net, val); } } return rc; @@ -2174,11 +2175,12 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) goto out_unlock; } else if (cmd == IP_VS_SO_SET_STARTDAEMON) { struct ip_vs_daemon_user *dm = (struct ip_vs_daemon_user *)arg; - ret = start_sync_thread(dm->state, dm->mcast_ifn, dm->syncid); + ret = start_sync_thread(net, dm->state, dm->mcast_ifn, + dm->syncid); goto out_unlock; } else if (cmd == IP_VS_SO_SET_STOPDAEMON) { struct ip_vs_daemon_user *dm = (struct ip_vs_daemon_user *)arg; - ret = stop_sync_thread(dm->state); + ret = stop_sync_thread(net, dm->state); goto out_unlock; } @@ -2424,6 +2426,7 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) int ret = 0; unsigned int copylen; struct net *net = sock_net(sk); + struct netns_ipvs *ipvs = net_ipvs(net); BUG_ON(!net); if (!capable(CAP_NET_ADMIN)) @@ -2546,15 +2549,17 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) struct ip_vs_daemon_user d[2]; memset(&d, 0, sizeof(d)); - if (ip_vs_sync_state & IP_VS_STATE_MASTER) { + if (ipvs->sync_state & IP_VS_STATE_MASTER) { d[0].state = IP_VS_STATE_MASTER; - strlcpy(d[0].mcast_ifn, ip_vs_master_mcast_ifn, sizeof(d[0].mcast_ifn)); - d[0].syncid = ip_vs_master_syncid; + strlcpy(d[0].mcast_ifn, ipvs->master_mcast_ifn, + sizeof(d[0].mcast_ifn)); + d[0].syncid = ipvs->master_syncid; } - if (ip_vs_sync_state & IP_VS_STATE_BACKUP) { + if (ipvs->sync_state & IP_VS_STATE_BACKUP) { d[1].state = IP_VS_STATE_BACKUP; - strlcpy(d[1].mcast_ifn, ip_vs_backup_mcast_ifn, sizeof(d[1].mcast_ifn)); - d[1].syncid = ip_vs_backup_syncid; + strlcpy(d[1].mcast_ifn, ipvs->backup_mcast_ifn, + sizeof(d[1].mcast_ifn)); + d[1].syncid = ipvs->backup_syncid; } if (copy_to_user(user, &d, sizeof(d)) != 0) ret = -EFAULT; @@ -3061,20 +3066,23 @@ nla_put_failure: static int ip_vs_genl_dump_daemons(struct sk_buff *skb, struct netlink_callback *cb) { + struct net *net = skb_net(skb); + struct netns_ipvs *ipvs = net_ipvs(net); + mutex_lock(&__ip_vs_mutex); - if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && !cb->args[0]) { + if ((ipvs->sync_state & IP_VS_STATE_MASTER) && !cb->args[0]) { if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_MASTER, - ip_vs_master_mcast_ifn, - ip_vs_master_syncid, cb) < 0) + ipvs->master_mcast_ifn, + ipvs->master_syncid, cb) < 0) goto nla_put_failure; cb->args[0] = 1; } - if ((ip_vs_sync_state & IP_VS_STATE_BACKUP) && !cb->args[1]) { + if ((ipvs->sync_state & IP_VS_STATE_BACKUP) && !cb->args[1]) { if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_BACKUP, - ip_vs_backup_mcast_ifn, - ip_vs_backup_syncid, cb) < 0) + ipvs->backup_mcast_ifn, + ipvs->backup_syncid, cb) < 0) goto nla_put_failure; cb->args[1] = 1; @@ -3086,24 +3094,26 @@ nla_put_failure: return skb->len; } -static int ip_vs_genl_new_daemon(struct nlattr **attrs) +static int ip_vs_genl_new_daemon(struct net *net, struct nlattr **attrs) { if (!(attrs[IPVS_DAEMON_ATTR_STATE] && attrs[IPVS_DAEMON_ATTR_MCAST_IFN] && attrs[IPVS_DAEMON_ATTR_SYNC_ID])) return -EINVAL; - return start_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]), + return start_sync_thread(net, + nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]), nla_data(attrs[IPVS_DAEMON_ATTR_MCAST_IFN]), nla_get_u32(attrs[IPVS_DAEMON_ATTR_SYNC_ID])); } -static int ip_vs_genl_del_daemon(struct nlattr **attrs) +static int ip_vs_genl_del_daemon(struct net *net, struct nlattr **attrs) { if (!attrs[IPVS_DAEMON_ATTR_STATE]) return -EINVAL; - return stop_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE])); + return stop_sync_thread(net, + nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE])); } static int ip_vs_genl_set_config(struct net *net, struct nlattr **attrs) @@ -3159,9 +3169,9 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) } if (cmd == IPVS_CMD_NEW_DAEMON) - ret = ip_vs_genl_new_daemon(daemon_attrs); + ret = ip_vs_genl_new_daemon(net, daemon_attrs); else - ret = ip_vs_genl_del_daemon(daemon_attrs); + ret = ip_vs_genl_del_daemon(net, daemon_attrs); goto out; } else if (cmd == IPVS_CMD_ZERO && !info->attrs[IPVS_CMD_ATTR_SERVICE]) { diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 6831e8fac8db..c29e73d686fb 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -192,6 +192,7 @@ union ip_vs_sync_conn { #define IPVS_OPT_F_PARAM (1 << (IPVS_OPT_PARAM-1)) struct ip_vs_sync_thread_data { + struct net *net; struct socket *sock; char *buf; }; @@ -259,10 +260,6 @@ struct ip_vs_sync_mesg { /* ip_vs_sync_conn entries start here */ }; -/* the maximum length of sync (sending/receiving) message */ -static int sync_send_mesg_maxlen; -static int sync_recv_mesg_maxlen; - struct ip_vs_sync_buff { struct list_head list; unsigned long firstuse; @@ -273,28 +270,6 @@ struct ip_vs_sync_buff { unsigned char *end; }; - -/* the sync_buff list head and the lock */ -static LIST_HEAD(ip_vs_sync_queue); -static DEFINE_SPINLOCK(ip_vs_sync_lock); - -/* current sync_buff for accepting new conn entries */ -static struct ip_vs_sync_buff *curr_sb = NULL; -static DEFINE_SPINLOCK(curr_sb_lock); - -/* ipvs sync daemon state */ -volatile int ip_vs_sync_state = IP_VS_STATE_NONE; -volatile int ip_vs_master_syncid = 0; -volatile int ip_vs_backup_syncid = 0; - -/* multicast interface name */ -char ip_vs_master_mcast_ifn[IP_VS_IFNAME_MAXLEN]; -char ip_vs_backup_mcast_ifn[IP_VS_IFNAME_MAXLEN]; - -/* sync daemon tasks */ -static struct task_struct *sync_master_thread; -static struct task_struct *sync_backup_thread; - /* multicast addr */ static struct sockaddr_in mcast_addr = { .sin_family = AF_INET, @@ -324,20 +299,20 @@ static void hton_seq(struct ip_vs_seq *ho, struct ip_vs_seq *no) put_unaligned_be32(ho->previous_delta, &no->previous_delta); } -static inline struct ip_vs_sync_buff *sb_dequeue(void) +static inline struct ip_vs_sync_buff *sb_dequeue(struct netns_ipvs *ipvs) { struct ip_vs_sync_buff *sb; - spin_lock_bh(&ip_vs_sync_lock); - if (list_empty(&ip_vs_sync_queue)) { + spin_lock_bh(&ipvs->sync_lock); + if (list_empty(&ipvs->sync_queue)) { sb = NULL; } else { - sb = list_entry(ip_vs_sync_queue.next, + sb = list_entry(ipvs->sync_queue.next, struct ip_vs_sync_buff, list); list_del(&sb->list); } - spin_unlock_bh(&ip_vs_sync_lock); + spin_unlock_bh(&ipvs->sync_lock); return sb; } @@ -345,25 +320,27 @@ static inline struct ip_vs_sync_buff *sb_dequeue(void) /* * Create a new sync buffer for Version 1 proto. */ -static inline struct ip_vs_sync_buff * ip_vs_sync_buff_create(void) +static inline struct ip_vs_sync_buff * +ip_vs_sync_buff_create(struct netns_ipvs *ipvs) { struct ip_vs_sync_buff *sb; if (!(sb=kmalloc(sizeof(struct ip_vs_sync_buff), GFP_ATOMIC))) return NULL; - if (!(sb->mesg=kmalloc(sync_send_mesg_maxlen, GFP_ATOMIC))) { + sb->mesg = kmalloc(ipvs->send_mesg_maxlen, GFP_ATOMIC); + if (!sb->mesg) { kfree(sb); return NULL; } sb->mesg->reserved = 0; /* old nr_conns i.e. must be zeo now */ sb->mesg->version = SYNC_PROTO_VER; - sb->mesg->syncid = ip_vs_master_syncid; + sb->mesg->syncid = ipvs->master_syncid; sb->mesg->size = sizeof(struct ip_vs_sync_mesg); sb->mesg->nr_conns = 0; sb->mesg->spare = 0; sb->head = (unsigned char *)sb->mesg + sizeof(struct ip_vs_sync_mesg); - sb->end = (unsigned char *)sb->mesg + sync_send_mesg_maxlen; + sb->end = (unsigned char *)sb->mesg + ipvs->send_mesg_maxlen; sb->firstuse = jiffies; return sb; @@ -375,14 +352,16 @@ static inline void ip_vs_sync_buff_release(struct ip_vs_sync_buff *sb) kfree(sb); } -static inline void sb_queue_tail(struct ip_vs_sync_buff *sb) +static inline void sb_queue_tail(struct netns_ipvs *ipvs) { - spin_lock(&ip_vs_sync_lock); - if (ip_vs_sync_state & IP_VS_STATE_MASTER) - list_add_tail(&sb->list, &ip_vs_sync_queue); + struct ip_vs_sync_buff *sb = ipvs->sync_buff; + + spin_lock(&ipvs->sync_lock); + if (ipvs->sync_state & IP_VS_STATE_MASTER) + list_add_tail(&sb->list, &ipvs->sync_queue); else ip_vs_sync_buff_release(sb); - spin_unlock(&ip_vs_sync_lock); + spin_unlock(&ipvs->sync_lock); } /* @@ -390,18 +369,18 @@ static inline void sb_queue_tail(struct ip_vs_sync_buff *sb) * than the specified time or the specified time is zero. */ static inline struct ip_vs_sync_buff * -get_curr_sync_buff(unsigned long time) +get_curr_sync_buff(struct netns_ipvs *ipvs, unsigned long time) { struct ip_vs_sync_buff *sb; - spin_lock_bh(&curr_sb_lock); - if (curr_sb && (time == 0 || - time_before(jiffies - curr_sb->firstuse, time))) { - sb = curr_sb; - curr_sb = NULL; + spin_lock_bh(&ipvs->sync_buff_lock); + if (ipvs->sync_buff && (time == 0 || + time_before(jiffies - ipvs->sync_buff->firstuse, time))) { + sb = ipvs->sync_buff; + ipvs->sync_buff = NULL; } else sb = NULL; - spin_unlock_bh(&curr_sb_lock); + spin_unlock_bh(&ipvs->sync_buff_lock); return sb; } @@ -409,33 +388,37 @@ get_curr_sync_buff(unsigned long time) * Switch mode from sending version 0 or 1 * - must handle sync_buf */ -void ip_vs_sync_switch_mode(int mode) { +void ip_vs_sync_switch_mode(struct net *net, int mode) +{ + struct netns_ipvs *ipvs = net_ipvs(net); - if (!ip_vs_sync_state & IP_VS_STATE_MASTER) + if (!ipvs->sync_state & IP_VS_STATE_MASTER) return; - if (mode == sysctl_ip_vs_sync_ver || !curr_sb) + if (mode == sysctl_ip_vs_sync_ver || !ipvs->sync_buff) return; - spin_lock_bh(&curr_sb_lock); + spin_lock_bh(&ipvs->sync_buff_lock); /* Buffer empty ? then let buf_create do the job */ - if ( curr_sb->mesg->size <= sizeof(struct ip_vs_sync_mesg)) { - kfree(curr_sb); - curr_sb = NULL; + if (ipvs->sync_buff->mesg->size <= sizeof(struct ip_vs_sync_mesg)) { + kfree(ipvs->sync_buff); + ipvs->sync_buff = NULL; } else { - spin_lock_bh(&ip_vs_sync_lock); - if (ip_vs_sync_state & IP_VS_STATE_MASTER) - list_add_tail(&curr_sb->list, &ip_vs_sync_queue); + spin_lock_bh(&ipvs->sync_lock); + if (ipvs->sync_state & IP_VS_STATE_MASTER) + list_add_tail(&ipvs->sync_buff->list, + &ipvs->sync_queue); else - ip_vs_sync_buff_release(curr_sb); - spin_unlock_bh(&ip_vs_sync_lock); + ip_vs_sync_buff_release(ipvs->sync_buff); + spin_unlock_bh(&ipvs->sync_lock); } - spin_unlock_bh(&curr_sb_lock); + spin_unlock_bh(&ipvs->sync_buff_lock); } /* * Create a new sync buffer for Version 0 proto. */ -static inline struct ip_vs_sync_buff * ip_vs_sync_buff_create_v0(void) +static inline struct ip_vs_sync_buff * +ip_vs_sync_buff_create_v0(struct netns_ipvs *ipvs) { struct ip_vs_sync_buff *sb; struct ip_vs_sync_mesg_v0 *mesg; @@ -443,16 +426,17 @@ static inline struct ip_vs_sync_buff * ip_vs_sync_buff_create_v0(void) if (!(sb=kmalloc(sizeof(struct ip_vs_sync_buff), GFP_ATOMIC))) return NULL; - if (!(sb->mesg=kmalloc(sync_send_mesg_maxlen, GFP_ATOMIC))) { + sb->mesg = kmalloc(ipvs->send_mesg_maxlen, GFP_ATOMIC); + if (!sb->mesg) { kfree(sb); return NULL; } mesg = (struct ip_vs_sync_mesg_v0 *)sb->mesg; mesg->nr_conns = 0; - mesg->syncid = ip_vs_master_syncid; - mesg->size = 4; - sb->head = (unsigned char *)mesg + 4; - sb->end = (unsigned char *)mesg + sync_send_mesg_maxlen; + mesg->syncid = ipvs->master_syncid; + mesg->size = sizeof(struct ip_vs_sync_mesg_v0); + sb->head = (unsigned char *)mesg + sizeof(struct ip_vs_sync_mesg_v0); + sb->end = (unsigned char *)mesg + ipvs->send_mesg_maxlen; sb->firstuse = jiffies; return sb; } @@ -461,8 +445,9 @@ static inline struct ip_vs_sync_buff * ip_vs_sync_buff_create_v0(void) * Version 0 , could be switched in by sys_ctl. * Add an ip_vs_conn information into the current sync_buff. */ -void ip_vs_sync_conn_v0(struct ip_vs_conn *cp) +void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp) { + struct netns_ipvs *ipvs = net_ipvs(net); struct ip_vs_sync_mesg_v0 *m; struct ip_vs_sync_conn_v0 *s; int len; @@ -473,10 +458,12 @@ void ip_vs_sync_conn_v0(struct ip_vs_conn *cp) if (cp->flags & IP_VS_CONN_F_ONE_PACKET) return; - spin_lock(&curr_sb_lock); - if (!curr_sb) { - if (!(curr_sb=ip_vs_sync_buff_create_v0())) { - spin_unlock(&curr_sb_lock); + spin_lock(&ipvs->sync_buff_lock); + if (!ipvs->sync_buff) { + ipvs->sync_buff = + ip_vs_sync_buff_create_v0(ipvs); + if (!ipvs->sync_buff) { + spin_unlock(&ipvs->sync_buff_lock); pr_err("ip_vs_sync_buff_create failed.\n"); return; } @@ -484,8 +471,8 @@ void ip_vs_sync_conn_v0(struct ip_vs_conn *cp) len = (cp->flags & IP_VS_CONN_F_SEQ_MASK) ? FULL_CONN_SIZE : SIMPLE_CONN_SIZE; - m = (struct ip_vs_sync_mesg_v0 *)curr_sb->mesg; - s = (struct ip_vs_sync_conn_v0 *)curr_sb->head; + m = (struct ip_vs_sync_mesg_v0 *)ipvs->sync_buff->mesg; + s = (struct ip_vs_sync_conn_v0 *)ipvs->sync_buff->head; /* copy members */ s->reserved = 0; @@ -506,18 +493,18 @@ void ip_vs_sync_conn_v0(struct ip_vs_conn *cp) m->nr_conns++; m->size += len; - curr_sb->head += len; + ipvs->sync_buff->head += len; /* check if there is a space for next one */ - if (curr_sb->head + FULL_CONN_SIZE > curr_sb->end) { - sb_queue_tail(curr_sb); - curr_sb = NULL; + if (ipvs->sync_buff->head + FULL_CONN_SIZE > ipvs->sync_buff->end) { + sb_queue_tail(ipvs); + ipvs->sync_buff = NULL; } - spin_unlock(&curr_sb_lock); + spin_unlock(&ipvs->sync_buff_lock); /* synchronize its controller if it has */ if (cp->control) - ip_vs_sync_conn(cp->control); + ip_vs_sync_conn(net, cp->control); } /* @@ -525,8 +512,9 @@ void ip_vs_sync_conn_v0(struct ip_vs_conn *cp) * Called by ip_vs_in. * Sending Version 1 messages */ -void ip_vs_sync_conn(struct ip_vs_conn *cp) +void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp) { + struct netns_ipvs *ipvs = net_ipvs(net); struct ip_vs_sync_mesg *m; union ip_vs_sync_conn *s; __u8 *p; @@ -534,7 +522,7 @@ void ip_vs_sync_conn(struct ip_vs_conn *cp) /* Handle old version of the protocol */ if (sysctl_ip_vs_sync_ver == 0) { - ip_vs_sync_conn_v0(cp); + ip_vs_sync_conn_v0(net, cp); return; } /* Do not sync ONE PACKET */ @@ -551,7 +539,7 @@ sloop: pe_name_len = strnlen(cp->pe->name, IP_VS_PENAME_MAXLEN); } - spin_lock(&curr_sb_lock); + spin_lock(&ipvs->sync_buff_lock); #ifdef CONFIG_IP_VS_IPV6 if (cp->af == AF_INET6) @@ -570,26 +558,27 @@ sloop: /* check if there is a space for this one */ pad = 0; - if (curr_sb) { - pad = (4 - (size_t)curr_sb->head) & 3; - if (curr_sb->head + len + pad > curr_sb->end) { - sb_queue_tail(curr_sb); - curr_sb = NULL; + if (ipvs->sync_buff) { + pad = (4 - (size_t)ipvs->sync_buff->head) & 3; + if (ipvs->sync_buff->head + len + pad > ipvs->sync_buff->end) { + sb_queue_tail(ipvs); + ipvs->sync_buff = NULL; pad = 0; } } - if (!curr_sb) { - if (!(curr_sb=ip_vs_sync_buff_create())) { - spin_unlock(&curr_sb_lock); + if (!ipvs->sync_buff) { + ipvs->sync_buff = ip_vs_sync_buff_create(ipvs); + if (!ipvs->sync_buff) { + spin_unlock(&ipvs->sync_buff_lock); pr_err("ip_vs_sync_buff_create failed.\n"); return; } } - m = curr_sb->mesg; - p = curr_sb->head; - curr_sb->head += pad + len; + m = ipvs->sync_buff->mesg; + p = ipvs->sync_buff->head; + ipvs->sync_buff->head += pad + len; m->size += pad + len; /* Add ev. padding from prev. sync_conn */ while (pad--) @@ -647,7 +636,7 @@ sloop: } } - spin_unlock(&curr_sb_lock); + spin_unlock(&ipvs->sync_buff_lock); control: /* synchronize its controller if it has */ @@ -699,7 +688,8 @@ ip_vs_conn_fill_param_sync(int af, union ip_vs_sync_conn *sc, buff[pe_name_len]=0; p->pe = __ip_vs_pe_getbyname(buff); if (!p->pe) { - IP_VS_DBG(3, "BACKUP, no %s engine found/loaded\n", buff); + IP_VS_DBG(3, "BACKUP, no %s engine found/loaded\n", + buff); return 1; } } else { @@ -748,7 +738,7 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param, * If it is not found the connection will remain unbound * but still handled. */ - dest = ip_vs_find_dest(&init_net, type, daddr, dport, param->vaddr, + dest = ip_vs_find_dest(net, type, daddr, dport, param->vaddr, param->vport, protocol, fwmark); /* Set the approprite ativity flag */ @@ -1089,6 +1079,7 @@ out: static void ip_vs_process_message(struct net *net, __u8 *buffer, const size_t buflen) { + struct netns_ipvs *ipvs = net_ipvs(net); struct ip_vs_sync_mesg *m2 = (struct ip_vs_sync_mesg *)buffer; __u8 *p, *msg_end; int i, nr_conns; @@ -1105,7 +1096,7 @@ static void ip_vs_process_message(struct net *net, __u8 *buffer, return; } /* SyncID sanity check */ - if (ip_vs_backup_syncid != 0 && m2->syncid != ip_vs_backup_syncid) { + if (ipvs->backup_syncid != 0 && m2->syncid != ipvs->backup_syncid) { IP_VS_DBG(7, "BACKUP, Ignoring syncid = %d\n", m2->syncid); return; } @@ -1190,8 +1181,10 @@ static int set_mcast_if(struct sock *sk, char *ifname) { struct net_device *dev; struct inet_sock *inet = inet_sk(sk); + struct net *net = sock_net(sk); - if ((dev = __dev_get_by_name(&init_net, ifname)) == NULL) + dev = __dev_get_by_name(net, ifname); + if (!dev) return -ENODEV; if (sk->sk_bound_dev_if && dev->ifindex != sk->sk_bound_dev_if) @@ -1210,30 +1203,33 @@ static int set_mcast_if(struct sock *sk, char *ifname) * Set the maximum length of sync message according to the * specified interface's MTU. */ -static int set_sync_mesg_maxlen(int sync_state) +static int set_sync_mesg_maxlen(struct net *net, int sync_state) { + struct netns_ipvs *ipvs = net_ipvs(net); struct net_device *dev; int num; if (sync_state == IP_VS_STATE_MASTER) { - if ((dev = __dev_get_by_name(&init_net, ip_vs_master_mcast_ifn)) == NULL) + dev = __dev_get_by_name(net, ipvs->master_mcast_ifn); + if (!dev) return -ENODEV; num = (dev->mtu - sizeof(struct iphdr) - sizeof(struct udphdr) - SYNC_MESG_HEADER_LEN - 20) / SIMPLE_CONN_SIZE; - sync_send_mesg_maxlen = SYNC_MESG_HEADER_LEN + + ipvs->send_mesg_maxlen = SYNC_MESG_HEADER_LEN + SIMPLE_CONN_SIZE * min(num, MAX_CONNS_PER_SYNCBUFF); IP_VS_DBG(7, "setting the maximum length of sync sending " - "message %d.\n", sync_send_mesg_maxlen); + "message %d.\n", ipvs->send_mesg_maxlen); } else if (sync_state == IP_VS_STATE_BACKUP) { - if ((dev = __dev_get_by_name(&init_net, ip_vs_backup_mcast_ifn)) == NULL) + dev = __dev_get_by_name(net, ipvs->backup_mcast_ifn); + if (!dev) return -ENODEV; - sync_recv_mesg_maxlen = dev->mtu - + ipvs->recv_mesg_maxlen = dev->mtu - sizeof(struct iphdr) - sizeof(struct udphdr); IP_VS_DBG(7, "setting the maximum length of sync receiving " - "message %d.\n", sync_recv_mesg_maxlen); + "message %d.\n", ipvs->recv_mesg_maxlen); } return 0; @@ -1248,6 +1244,7 @@ static int set_sync_mesg_maxlen(int sync_state) static int join_mcast_group(struct sock *sk, struct in_addr *addr, char *ifname) { + struct net *net = sock_net(sk); struct ip_mreqn mreq; struct net_device *dev; int ret; @@ -1255,7 +1252,8 @@ join_mcast_group(struct sock *sk, struct in_addr *addr, char *ifname) memset(&mreq, 0, sizeof(mreq)); memcpy(&mreq.imr_multiaddr, addr, sizeof(struct in_addr)); - if ((dev = __dev_get_by_name(&init_net, ifname)) == NULL) + dev = __dev_get_by_name(net, ifname); + if (!dev) return -ENODEV; if (sk->sk_bound_dev_if && dev->ifindex != sk->sk_bound_dev_if) return -EINVAL; @@ -1272,11 +1270,13 @@ join_mcast_group(struct sock *sk, struct in_addr *addr, char *ifname) static int bind_mcastif_addr(struct socket *sock, char *ifname) { + struct net *net = sock_net(sock->sk); struct net_device *dev; __be32 addr; struct sockaddr_in sin; - if ((dev = __dev_get_by_name(&init_net, ifname)) == NULL) + dev = __dev_get_by_name(net, ifname); + if (!dev) return -ENODEV; addr = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE); @@ -1298,8 +1298,9 @@ static int bind_mcastif_addr(struct socket *sock, char *ifname) /* * Set up sending multicast socket over UDP */ -static struct socket * make_send_sock(void) +static struct socket *make_send_sock(struct net *net) { + struct netns_ipvs *ipvs = net_ipvs(net); struct socket *sock; int result; @@ -1310,7 +1311,7 @@ static struct socket * make_send_sock(void) return ERR_PTR(result); } - result = set_mcast_if(sock->sk, ip_vs_master_mcast_ifn); + result = set_mcast_if(sock->sk, ipvs->master_mcast_ifn); if (result < 0) { pr_err("Error setting outbound mcast interface\n"); goto error; @@ -1319,7 +1320,7 @@ static struct socket * make_send_sock(void) set_mcast_loop(sock->sk, 0); set_mcast_ttl(sock->sk, 1); - result = bind_mcastif_addr(sock, ip_vs_master_mcast_ifn); + result = bind_mcastif_addr(sock, ipvs->master_mcast_ifn); if (result < 0) { pr_err("Error binding address of the mcast interface\n"); goto error; @@ -1343,8 +1344,9 @@ static struct socket * make_send_sock(void) /* * Set up receiving multicast socket over UDP */ -static struct socket * make_receive_sock(void) +static struct socket *make_receive_sock(struct net *net) { + struct netns_ipvs *ipvs = net_ipvs(net); struct socket *sock; int result; @@ -1368,7 +1370,7 @@ static struct socket * make_receive_sock(void) /* join the multicast group */ result = join_mcast_group(sock->sk, (struct in_addr *) &mcast_addr.sin_addr, - ip_vs_backup_mcast_ifn); + ipvs->backup_mcast_ifn); if (result < 0) { pr_err("Error joining to the multicast group\n"); goto error; @@ -1439,20 +1441,21 @@ ip_vs_receive(struct socket *sock, char *buffer, const size_t buflen) static int sync_thread_master(void *data) { struct ip_vs_sync_thread_data *tinfo = data; + struct netns_ipvs *ipvs = net_ipvs(tinfo->net); struct ip_vs_sync_buff *sb; pr_info("sync thread started: state = MASTER, mcast_ifn = %s, " "syncid = %d\n", - ip_vs_master_mcast_ifn, ip_vs_master_syncid); + ipvs->master_mcast_ifn, ipvs->master_syncid); while (!kthread_should_stop()) { - while ((sb = sb_dequeue())) { + while ((sb = sb_dequeue(ipvs))) { ip_vs_send_sync_msg(tinfo->sock, sb->mesg); ip_vs_sync_buff_release(sb); } - /* check if entries stay in curr_sb for 2 seconds */ - sb = get_curr_sync_buff(2 * HZ); + /* check if entries stay in ipvs->sync_buff for 2 seconds */ + sb = get_curr_sync_buff(ipvs, 2 * HZ); if (sb) { ip_vs_send_sync_msg(tinfo->sock, sb->mesg); ip_vs_sync_buff_release(sb); @@ -1462,14 +1465,13 @@ static int sync_thread_master(void *data) } /* clean up the sync_buff queue */ - while ((sb=sb_dequeue())) { + while ((sb = sb_dequeue(ipvs))) ip_vs_sync_buff_release(sb); - } /* clean up the current sync_buff */ - if ((sb = get_curr_sync_buff(0))) { + sb = get_curr_sync_buff(ipvs, 0); + if (sb) ip_vs_sync_buff_release(sb); - } /* release the sending multicast socket */ sock_release(tinfo->sock); @@ -1482,11 +1484,12 @@ static int sync_thread_master(void *data) static int sync_thread_backup(void *data) { struct ip_vs_sync_thread_data *tinfo = data; + struct netns_ipvs *ipvs = net_ipvs(tinfo->net); int len; pr_info("sync thread started: state = BACKUP, mcast_ifn = %s, " "syncid = %d\n", - ip_vs_backup_mcast_ifn, ip_vs_backup_syncid); + ipvs->backup_mcast_ifn, ipvs->backup_syncid); while (!kthread_should_stop()) { wait_event_interruptible(*sk_sleep(tinfo->sock->sk), @@ -1496,7 +1499,7 @@ static int sync_thread_backup(void *data) /* do we have data now? */ while (!skb_queue_empty(&(tinfo->sock->sk->sk_receive_queue))) { len = ip_vs_receive(tinfo->sock, tinfo->buf, - sync_recv_mesg_maxlen); + ipvs->recv_mesg_maxlen); if (len <= 0) { pr_err("receiving message error\n"); break; @@ -1505,7 +1508,7 @@ static int sync_thread_backup(void *data) /* disable bottom half, because it accesses the data shared by softirq while getting/creating conns */ local_bh_disable(); - ip_vs_process_message(&init_net, tinfo->buf, len); + ip_vs_process_message(tinfo->net, tinfo->buf, len); local_bh_enable(); } } @@ -1519,11 +1522,12 @@ static int sync_thread_backup(void *data) } -int start_sync_thread(int state, char *mcast_ifn, __u8 syncid) +int start_sync_thread(struct net *net, int state, char *mcast_ifn, __u8 syncid) { struct ip_vs_sync_thread_data *tinfo; struct task_struct **realtask, *task; struct socket *sock; + struct netns_ipvs *ipvs = net_ipvs(net); char *name, *buf = NULL; int (*threadfn)(void *data); int result = -ENOMEM; @@ -1533,27 +1537,27 @@ int start_sync_thread(int state, char *mcast_ifn, __u8 syncid) sizeof(struct ip_vs_sync_conn_v0)); if (state == IP_VS_STATE_MASTER) { - if (sync_master_thread) + if (ipvs->master_thread) return -EEXIST; - strlcpy(ip_vs_master_mcast_ifn, mcast_ifn, - sizeof(ip_vs_master_mcast_ifn)); - ip_vs_master_syncid = syncid; - realtask = &sync_master_thread; - name = "ipvs_syncmaster"; + strlcpy(ipvs->master_mcast_ifn, mcast_ifn, + sizeof(ipvs->master_mcast_ifn)); + ipvs->master_syncid = syncid; + realtask = &ipvs->master_thread; + name = "ipvs_master:%d"; threadfn = sync_thread_master; - sock = make_send_sock(); + sock = make_send_sock(net); } else if (state == IP_VS_STATE_BACKUP) { - if (sync_backup_thread) + if (ipvs->backup_thread) return -EEXIST; - strlcpy(ip_vs_backup_mcast_ifn, mcast_ifn, - sizeof(ip_vs_backup_mcast_ifn)); - ip_vs_backup_syncid = syncid; - realtask = &sync_backup_thread; - name = "ipvs_syncbackup"; + strlcpy(ipvs->backup_mcast_ifn, mcast_ifn, + sizeof(ipvs->backup_mcast_ifn)); + ipvs->backup_syncid = syncid; + realtask = &ipvs->backup_thread; + name = "ipvs_backup:%d"; threadfn = sync_thread_backup; - sock = make_receive_sock(); + sock = make_receive_sock(net); } else { return -EINVAL; } @@ -1563,9 +1567,9 @@ int start_sync_thread(int state, char *mcast_ifn, __u8 syncid) goto out; } - set_sync_mesg_maxlen(state); + set_sync_mesg_maxlen(net, state); if (state == IP_VS_STATE_BACKUP) { - buf = kmalloc(sync_recv_mesg_maxlen, GFP_KERNEL); + buf = kmalloc(ipvs->recv_mesg_maxlen, GFP_KERNEL); if (!buf) goto outsocket; } @@ -1574,10 +1578,11 @@ int start_sync_thread(int state, char *mcast_ifn, __u8 syncid) if (!tinfo) goto outbuf; + tinfo->net = net; tinfo->sock = sock; tinfo->buf = buf; - task = kthread_run(threadfn, tinfo, name); + task = kthread_run(threadfn, tinfo, name, ipvs->gen); if (IS_ERR(task)) { result = PTR_ERR(task); goto outtinfo; @@ -1585,7 +1590,7 @@ int start_sync_thread(int state, char *mcast_ifn, __u8 syncid) /* mark as active */ *realtask = task; - ip_vs_sync_state |= state; + ipvs->sync_state |= state; /* increase the module use count */ ip_vs_use_count_inc(); @@ -1603,16 +1608,18 @@ out: } -int stop_sync_thread(int state) +int stop_sync_thread(struct net *net, int state) { + struct netns_ipvs *ipvs = net_ipvs(net); + IP_VS_DBG(7, "%s(): pid %d\n", __func__, task_pid_nr(current)); if (state == IP_VS_STATE_MASTER) { - if (!sync_master_thread) + if (!ipvs->master_thread) return -ESRCH; pr_info("stopping master sync thread %d ...\n", - task_pid_nr(sync_master_thread)); + task_pid_nr(ipvs->master_thread)); /* * The lock synchronizes with sb_queue_tail(), so that we don't @@ -1620,21 +1627,21 @@ int stop_sync_thread(int state) * progress of stopping the master sync daemon. */ - spin_lock_bh(&ip_vs_sync_lock); - ip_vs_sync_state &= ~IP_VS_STATE_MASTER; - spin_unlock_bh(&ip_vs_sync_lock); - kthread_stop(sync_master_thread); - sync_master_thread = NULL; + spin_lock_bh(&ipvs->sync_lock); + ipvs->sync_state &= ~IP_VS_STATE_MASTER; + spin_unlock_bh(&ipvs->sync_lock); + kthread_stop(ipvs->master_thread); + ipvs->master_thread = NULL; } else if (state == IP_VS_STATE_BACKUP) { - if (!sync_backup_thread) + if (!ipvs->backup_thread) return -ESRCH; pr_info("stopping backup sync thread %d ...\n", - task_pid_nr(sync_backup_thread)); + task_pid_nr(ipvs->backup_thread)); - ip_vs_sync_state &= ~IP_VS_STATE_BACKUP; - kthread_stop(sync_backup_thread); - sync_backup_thread = NULL; + ipvs->sync_state &= ~IP_VS_STATE_BACKUP; + kthread_stop(ipvs->backup_thread); + ipvs->backup_thread = NULL; } else { return -EINVAL; } @@ -1650,12 +1657,29 @@ int stop_sync_thread(int state) */ static int __net_init __ip_vs_sync_init(struct net *net) { + struct netns_ipvs *ipvs = net_ipvs(net); + + if (!net_eq(net, &init_net)) /* netns not enabled yet */ + return -EPERM; + + INIT_LIST_HEAD(&ipvs->sync_queue); + spin_lock_init(&ipvs->sync_lock); + spin_lock_init(&ipvs->sync_buff_lock); + + ipvs->sync_mcast_addr.sin_family = AF_INET; + ipvs->sync_mcast_addr.sin_port = cpu_to_be16(IP_VS_SYNC_PORT); + ipvs->sync_mcast_addr.sin_addr.s_addr = cpu_to_be32(IP_VS_SYNC_GROUP); return 0; } static void __ip_vs_sync_cleanup(struct net *net) { + if (!net_eq(net, &init_net)) /* netns not enabled yet */ + return; + stop_sync_thread(net, IP_VS_STATE_MASTER); + stop_sync_thread(net, IP_VS_STATE_BACKUP); } + static struct pernet_operations ipvs_sync_ops = { .init = __ip_vs_sync_init, .exit = __ip_vs_sync_cleanup, -- cgit v1.2.3 From b17fc9963f837ef1acfe36e193108fb16ed58647 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:56 +0100 Subject: IPVS: netns, ip_vs_stats and its procfs The statistic counter locks for every packet are now removed, and that statistic is now per CPU, i.e. no locks needed. However summing is made in ip_vs_est into ip_vs_stats struct which is moved to ipvs struc. procfs, ip_vs_stats now have a "per cpu" count and a grand total. A new function seq_file_single_net() in ip_vs.h created for handling of single_open_net() since it does not place net ptr in a struct, like others. /var/lib/lxc # cat /proc/net/ip_vs_stats_percpu Total Incoming Outgoing Incoming Outgoing CPU Conns Packets Packets Bytes Bytes 0 0 3 1 9D 34 1 0 1 2 49 70 2 0 1 2 34 76 3 1 2 2 70 74 ~ 1 7 7 18A 18E Conns/s Pkts/s Pkts/s Bytes/s Bytes/s 0 0 0 0 0 *v3 ip_vs_stats reamains as before, instead ip_vs_stats_percpu is added. u64 seq lock added *v4 Bug correction inbytes and outbytes as own vars.. per_cpu counter for all stats now as suggested by Julian. [horms@verge.net.au: removed whitespace-change-only hunk] Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 51 ++++++++++++++- include/net/netns/ip_vs.h | 4 ++ net/netfilter/ipvs/ip_vs_core.c | 89 ++++++++++++++------------ net/netfilter/ipvs/ip_vs_ctl.c | 134 ++++++++++++++++++++++++++++++++++------ net/netfilter/ipvs/ip_vs_est.c | 39 ++++++++++++ 5 files changed, 256 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 4265b5e00c94..605d5db81a39 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -90,6 +90,18 @@ static inline struct net *skb_sknet(struct sk_buff *skb) return &init_net; #endif } +/* + * This one needed for single_open_net since net is stored directly in + * private not as a struct i.e. seq_file_net cant be used. + */ +static inline struct net *seq_file_single_net(struct seq_file *seq) +{ +#ifdef CONFIG_NET_NS + return (struct net *)seq->private; +#else + return &init_net; +#endif +} /* Connections' size value needed by ip_vs_ctl.c */ extern int ip_vs_conn_tab_size; @@ -320,6 +332,23 @@ struct ip_vs_seq { before last resized pkt */ }; +/* + * counters per cpu + */ +struct ip_vs_counters { + __u32 conns; /* connections scheduled */ + __u32 inpkts; /* incoming packets */ + __u32 outpkts; /* outgoing packets */ + __u64 inbytes; /* incoming bytes */ + __u64 outbytes; /* outgoing bytes */ +}; +/* + * Stats per cpu + */ +struct ip_vs_cpu_stats { + struct ip_vs_counters ustats; + struct u64_stats_sync syncp; +}; /* * IPVS statistics objects @@ -341,12 +370,28 @@ struct ip_vs_estimator { }; struct ip_vs_stats { - struct ip_vs_stats_user ustats; /* statistics */ + struct ip_vs_stats_user ustats; /* statistics */ struct ip_vs_estimator est; /* estimator */ - - spinlock_t lock; /* spin lock */ + struct ip_vs_cpu_stats *cpustats; /* per cpu counters */ + spinlock_t lock; /* spin lock */ }; +/* + * Helper Macros for per cpu + * ipvs->tot_stats->ustats.count + */ +#define IPVS_STAT_INC(ipvs, count) \ + __this_cpu_inc((ipvs)->ustats->count) + +#define IPVS_STAT_ADD(ipvs, count, value) \ + do {\ + write_seqcount_begin(per_cpu_ptr((ipvs)->ustats_seq, \ + raw_smp_processor_id())); \ + __this_cpu_add((ipvs)->ustats->count, value); \ + write_seqcount_end(per_cpu_ptr((ipvs)->ustats_seq, \ + raw_smp_processor_id())); \ + } while (0) + struct dst_entry; struct iphdr; struct ip_vs_conn; diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h index aba78f3c8341..bd1dad872178 100644 --- a/include/net/netns/ip_vs.h +++ b/include/net/netns/ip_vs.h @@ -61,6 +61,10 @@ struct netns_ipvs { struct list_head sctp_apps[SCTP_APP_TAB_SIZE]; spinlock_t sctp_app_lock; #endif + /* ip_vs_ctl */ + struct ip_vs_stats *tot_stats; /* Statistics & est. */ + struct ip_vs_cpu_stats __percpu *cpustats; /* Stats per cpu */ + seqcount_t *ustats_seq; /* u64 read retry */ /* ip_vs_lblc */ int sysctl_lblc_expiration; diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 5531d569aa5e..7e6a2a046bf5 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -115,21 +115,28 @@ static inline void ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb) { struct ip_vs_dest *dest = cp->dest; + struct netns_ipvs *ipvs = net_ipvs(skb_net(skb)); + if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { - spin_lock(&dest->stats.lock); - dest->stats.ustats.inpkts++; - dest->stats.ustats.inbytes += skb->len; - spin_unlock(&dest->stats.lock); - - spin_lock(&dest->svc->stats.lock); - dest->svc->stats.ustats.inpkts++; - dest->svc->stats.ustats.inbytes += skb->len; - spin_unlock(&dest->svc->stats.lock); - - spin_lock(&ip_vs_stats.lock); - ip_vs_stats.ustats.inpkts++; - ip_vs_stats.ustats.inbytes += skb->len; - spin_unlock(&ip_vs_stats.lock); + struct ip_vs_cpu_stats *s; + + s = this_cpu_ptr(dest->stats.cpustats); + s->ustats.inpkts++; + u64_stats_update_begin(&s->syncp); + s->ustats.inbytes += skb->len; + u64_stats_update_end(&s->syncp); + + s = this_cpu_ptr(dest->svc->stats.cpustats); + s->ustats.inpkts++; + u64_stats_update_begin(&s->syncp); + s->ustats.inbytes += skb->len; + u64_stats_update_end(&s->syncp); + + s = this_cpu_ptr(ipvs->cpustats); + s->ustats.inpkts++; + u64_stats_update_begin(&s->syncp); + s->ustats.inbytes += skb->len; + u64_stats_update_end(&s->syncp); } } @@ -138,21 +145,28 @@ static inline void ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb) { struct ip_vs_dest *dest = cp->dest; + struct netns_ipvs *ipvs = net_ipvs(skb_net(skb)); + if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { - spin_lock(&dest->stats.lock); - dest->stats.ustats.outpkts++; - dest->stats.ustats.outbytes += skb->len; - spin_unlock(&dest->stats.lock); - - spin_lock(&dest->svc->stats.lock); - dest->svc->stats.ustats.outpkts++; - dest->svc->stats.ustats.outbytes += skb->len; - spin_unlock(&dest->svc->stats.lock); - - spin_lock(&ip_vs_stats.lock); - ip_vs_stats.ustats.outpkts++; - ip_vs_stats.ustats.outbytes += skb->len; - spin_unlock(&ip_vs_stats.lock); + struct ip_vs_cpu_stats *s; + + s = this_cpu_ptr(dest->stats.cpustats); + s->ustats.outpkts++; + u64_stats_update_begin(&s->syncp); + s->ustats.outbytes += skb->len; + u64_stats_update_end(&s->syncp); + + s = this_cpu_ptr(dest->svc->stats.cpustats); + s->ustats.outpkts++; + u64_stats_update_begin(&s->syncp); + s->ustats.outbytes += skb->len; + u64_stats_update_end(&s->syncp); + + s = this_cpu_ptr(ipvs->cpustats); + s->ustats.outpkts++; + u64_stats_update_begin(&s->syncp); + s->ustats.outbytes += skb->len; + u64_stats_update_end(&s->syncp); } } @@ -160,17 +174,17 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb) static inline void ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) { - spin_lock(&cp->dest->stats.lock); - cp->dest->stats.ustats.conns++; - spin_unlock(&cp->dest->stats.lock); + struct netns_ipvs *ipvs = net_ipvs(svc->net); + struct ip_vs_cpu_stats *s; - spin_lock(&svc->stats.lock); - svc->stats.ustats.conns++; - spin_unlock(&svc->stats.lock); + s = this_cpu_ptr(cp->dest->stats.cpustats); + s->ustats.conns++; - spin_lock(&ip_vs_stats.lock); - ip_vs_stats.ustats.conns++; - spin_unlock(&ip_vs_stats.lock); + s = this_cpu_ptr(svc->stats.cpustats); + s->ustats.conns++; + + s = this_cpu_ptr(ipvs->cpustats); + s->ustats.conns++; } @@ -1841,7 +1855,6 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = { }, #endif }; - /* * Initialize IP Virtual Server netns mem. */ diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 03f86312b4bb..cbd58c60e1bf 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -257,8 +257,7 @@ static DECLARE_DELAYED_WORK(defense_work, defense_work_handler); static void defense_work_handler(struct work_struct *work) { - struct net *net = &init_net; - struct netns_ipvs *ipvs = net_ipvs(net); + struct netns_ipvs *ipvs = net_ipvs(&init_net); update_defense_level(ipvs); if (atomic_read(&ip_vs_dropentry)) @@ -519,6 +518,7 @@ __ip_vs_unbind_svc(struct ip_vs_dest *dest) svc->fwmark, IP_VS_DBG_ADDR(svc->af, &svc->addr), ntohs(svc->port), atomic_read(&svc->usecnt)); + free_percpu(svc->stats.cpustats); kfree(svc); } } @@ -722,6 +722,7 @@ ip_vs_trash_get_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr, list_del(&dest->n_list); ip_vs_dst_reset(dest); __ip_vs_unbind_svc(dest); + free_percpu(dest->stats.cpustats); kfree(dest); } } @@ -747,6 +748,7 @@ static void ip_vs_trash_cleanup(void) list_del(&dest->n_list); ip_vs_dst_reset(dest); __ip_vs_unbind_svc(dest); + free_percpu(dest->stats.cpustats); kfree(dest); } } @@ -868,6 +870,11 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest, pr_err("%s(): no memory.\n", __func__); return -ENOMEM; } + dest->stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats); + if (!dest->stats.cpustats) { + pr_err("%s() alloc_percpu failed\n", __func__); + goto err_alloc; + } dest->af = svc->af; dest->protocol = svc->protocol; @@ -891,6 +898,10 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest, LeaveFunction(2); return 0; + +err_alloc: + kfree(dest); + return -ENOMEM; } @@ -1037,6 +1048,7 @@ static void __ip_vs_del_dest(struct net *net, struct ip_vs_dest *dest) and only one user context can update virtual service at a time, so the operation here is OK */ atomic_dec(&dest->svc->refcnt); + free_percpu(dest->stats.cpustats); kfree(dest); } else { IP_VS_DBG_BUF(3, "Moving dest %s:%u into trash, " @@ -1163,6 +1175,11 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u, ret = -ENOMEM; goto out_err; } + svc->stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats); + if (!svc->stats.cpustats) { + pr_err("%s() alloc_percpu failed\n", __func__); + goto out_err; + } /* I'm the first user of the service */ atomic_set(&svc->usecnt, 0); @@ -1212,6 +1229,7 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u, *svc_p = svc; return 0; + out_err: if (svc != NULL) { ip_vs_unbind_scheduler(svc); @@ -1220,6 +1238,8 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u, ip_vs_app_inc_put(svc->inc); local_bh_enable(); } + if (svc->stats.cpustats) + free_percpu(svc->stats.cpustats); kfree(svc); } ip_vs_scheduler_put(sched); @@ -1388,6 +1408,7 @@ static void __ip_vs_del_service(struct ip_vs_service *svc) svc->fwmark, IP_VS_DBG_ADDR(svc->af, &svc->addr), ntohs(svc->port), atomic_read(&svc->usecnt)); + free_percpu(svc->stats.cpustats); kfree(svc); } @@ -1499,7 +1520,7 @@ static int ip_vs_zero_all(struct net *net) } } - ip_vs_zero_stats(&ip_vs_stats); + ip_vs_zero_stats(net_ipvs(net)->tot_stats); return 0; } @@ -1989,13 +2010,11 @@ static const struct file_operations ip_vs_info_fops = { #endif -struct ip_vs_stats ip_vs_stats = { - .lock = __SPIN_LOCK_UNLOCKED(ip_vs_stats.lock), -}; - #ifdef CONFIG_PROC_FS static int ip_vs_stats_show(struct seq_file *seq, void *v) { + struct net *net = seq_file_single_net(seq); + struct ip_vs_stats *tot_stats = net_ipvs(net)->tot_stats; /* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ seq_puts(seq, @@ -2003,22 +2022,22 @@ static int ip_vs_stats_show(struct seq_file *seq, void *v) seq_printf(seq, " Conns Packets Packets Bytes Bytes\n"); - spin_lock_bh(&ip_vs_stats.lock); - seq_printf(seq, "%8X %8X %8X %16LX %16LX\n\n", ip_vs_stats.ustats.conns, - ip_vs_stats.ustats.inpkts, ip_vs_stats.ustats.outpkts, - (unsigned long long) ip_vs_stats.ustats.inbytes, - (unsigned long long) ip_vs_stats.ustats.outbytes); + spin_lock_bh(&tot_stats->lock); + seq_printf(seq, "%8X %8X %8X %16LX %16LX\n\n", tot_stats->ustats.conns, + tot_stats->ustats.inpkts, tot_stats->ustats.outpkts, + (unsigned long long) tot_stats->ustats.inbytes, + (unsigned long long) tot_stats->ustats.outbytes); /* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ seq_puts(seq, " Conns/s Pkts/s Pkts/s Bytes/s Bytes/s\n"); seq_printf(seq,"%8X %8X %8X %16X %16X\n", - ip_vs_stats.ustats.cps, - ip_vs_stats.ustats.inpps, - ip_vs_stats.ustats.outpps, - ip_vs_stats.ustats.inbps, - ip_vs_stats.ustats.outbps); - spin_unlock_bh(&ip_vs_stats.lock); + tot_stats->ustats.cps, + tot_stats->ustats.inpps, + tot_stats->ustats.outpps, + tot_stats->ustats.inbps, + tot_stats->ustats.outbps); + spin_unlock_bh(&tot_stats->lock); return 0; } @@ -2036,6 +2055,59 @@ static const struct file_operations ip_vs_stats_fops = { .release = single_release, }; +static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v) +{ + struct net *net = seq_file_single_net(seq); + struct ip_vs_stats *tot_stats = net_ipvs(net)->tot_stats; + int i; + +/* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ + seq_puts(seq, + " Total Incoming Outgoing Incoming Outgoing\n"); + seq_printf(seq, + "CPU Conns Packets Packets Bytes Bytes\n"); + + for_each_possible_cpu(i) { + struct ip_vs_cpu_stats *u = per_cpu_ptr(net->ipvs->cpustats, i); + seq_printf(seq, "%3X %8X %8X %8X %16LX %16LX\n", + i, u->ustats.conns, u->ustats.inpkts, + u->ustats.outpkts, (__u64)u->ustats.inbytes, + (__u64)u->ustats.outbytes); + } + + spin_lock_bh(&tot_stats->lock); + seq_printf(seq, " ~ %8X %8X %8X %16LX %16LX\n\n", + tot_stats->ustats.conns, tot_stats->ustats.inpkts, + tot_stats->ustats.outpkts, + (unsigned long long) tot_stats->ustats.inbytes, + (unsigned long long) tot_stats->ustats.outbytes); + +/* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ + seq_puts(seq, + " Conns/s Pkts/s Pkts/s Bytes/s Bytes/s\n"); + seq_printf(seq, " %8X %8X %8X %16X %16X\n", + tot_stats->ustats.cps, + tot_stats->ustats.inpps, + tot_stats->ustats.outpps, + tot_stats->ustats.inbps, + tot_stats->ustats.outbps); + spin_unlock_bh(&tot_stats->lock); + + return 0; +} + +static int ip_vs_stats_percpu_seq_open(struct inode *inode, struct file *file) +{ + return single_open_net(inode, file, ip_vs_stats_percpu_show); +} + +static const struct file_operations ip_vs_stats_percpu_fops = { + .owner = THIS_MODULE, + .open = ip_vs_stats_percpu_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; #endif /* @@ -3461,32 +3533,54 @@ int __net_init __ip_vs_control_init(struct net *net) if (!net_eq(net, &init_net)) /* netns not enabled yet */ return -EPERM; + /* procfs stats */ + ipvs->tot_stats = kzalloc(sizeof(struct ip_vs_stats), GFP_KERNEL); + if (ipvs->tot_stats == NULL) { + pr_err("%s(): no memory.\n", __func__); + return -ENOMEM; + } + ipvs->cpustats = alloc_percpu(struct ip_vs_cpu_stats); + if (!ipvs->cpustats) { + pr_err("%s() alloc_percpu failed\n", __func__); + goto err_alloc; + } + spin_lock_init(&ipvs->tot_stats->lock); for (idx = 0; idx < IP_VS_RTAB_SIZE; idx++) INIT_LIST_HEAD(&ipvs->rs_table[idx]); proc_net_fops_create(net, "ip_vs", 0, &ip_vs_info_fops); proc_net_fops_create(net, "ip_vs_stats", 0, &ip_vs_stats_fops); + proc_net_fops_create(net, "ip_vs_stats_percpu", 0, + &ip_vs_stats_percpu_fops); sysctl_header = register_net_sysctl_table(net, net_vs_ctl_path, vs_vars); if (sysctl_header == NULL) goto err_reg; - ip_vs_new_estimator(net, &ip_vs_stats); + ip_vs_new_estimator(net, ipvs->tot_stats); return 0; err_reg: + free_percpu(ipvs->cpustats); +err_alloc: + kfree(ipvs->tot_stats); return -ENOMEM; } static void __net_exit __ip_vs_control_cleanup(struct net *net) { + struct netns_ipvs *ipvs = net_ipvs(net); + if (!net_eq(net, &init_net)) /* netns not enabled yet */ return; - ip_vs_kill_estimator(net, &ip_vs_stats); + ip_vs_kill_estimator(net, ipvs->tot_stats); unregister_net_sysctl_table(sysctl_header); + proc_net_remove(net, "ip_vs_stats_percpu"); proc_net_remove(net, "ip_vs_stats"); proc_net_remove(net, "ip_vs"); + free_percpu(ipvs->cpustats); + kfree(ipvs->tot_stats); } static struct pernet_operations ipvs_control_ops = { diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index 07d839bef537..d13616b138cd 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -52,6 +52,43 @@ */ +/* + * Make a summary from each cpu + */ +static void ip_vs_read_cpu_stats(struct ip_vs_stats_user *sum, + struct ip_vs_cpu_stats *stats) +{ + int i; + + for_each_possible_cpu(i) { + struct ip_vs_cpu_stats *s = per_cpu_ptr(stats, i); + unsigned int start; + __u64 inbytes, outbytes; + if (i) { + sum->conns += s->ustats.conns; + sum->inpkts += s->ustats.inpkts; + sum->outpkts += s->ustats.outpkts; + do { + start = u64_stats_fetch_begin_bh(&s->syncp); + inbytes = s->ustats.inbytes; + outbytes = s->ustats.outbytes; + } while (u64_stats_fetch_retry_bh(&s->syncp, start)); + sum->inbytes += inbytes; + sum->outbytes += outbytes; + } else { + sum->conns = s->ustats.conns; + sum->inpkts = s->ustats.inpkts; + sum->outpkts = s->ustats.outpkts; + do { + start = u64_stats_fetch_begin_bh(&s->syncp); + sum->inbytes = s->ustats.inbytes; + sum->outbytes = s->ustats.outbytes; + } while (u64_stats_fetch_retry_bh(&s->syncp, start)); + } + } +} + + static void estimation_timer(unsigned long arg) { struct ip_vs_estimator *e; @@ -64,10 +101,12 @@ static void estimation_timer(unsigned long arg) struct netns_ipvs *ipvs; ipvs = net_ipvs(net); + ip_vs_read_cpu_stats(&ipvs->tot_stats->ustats, ipvs->cpustats); spin_lock(&ipvs->est_lock); list_for_each_entry(e, &ipvs->est_list, list) { s = container_of(e, struct ip_vs_stats, est); + ip_vs_read_cpu_stats(&s->ustats, s->cpustats); spin_lock(&s->lock); n_conns = s->ustats.conns; n_inpkts = s->ustats.inpkts; -- cgit v1.2.3 From 6e67e586e7289c144d5a189d6e0fa7141d025746 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:57 +0100 Subject: IPVS: netns, connection hash got net as param. Connection hash table is now name space aware. i.e. net ptr >> 8 is xor:ed to the hash, and this is the first param to be compared. The net struct is 0xa40 in size ( a little bit smaller for 32 bit arch:s) and cache-line aligned, so a ptr >> 5 might be a more clever solution ? All lookups where net is compared uses net_eq() which returns 1 when netns is disabled, and the compiler seems to do something clever in that case. ip_vs_conn_fill_param() have *net as first param now. Three new inlines added to keep conn struct smaller when names space is disabled. - ip_vs_conn_net() - ip_vs_conn_net_set() - ip_vs_conn_net_eq() *v3 moved net compare to the end in "fast path" Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 53 +++++++++++---- include/net/netns/ip_vs.h | 2 + net/netfilter/ipvs/ip_vs_conn.c | 112 ++++++++++++++++++++------------ net/netfilter/ipvs/ip_vs_core.c | 15 +++-- net/netfilter/ipvs/ip_vs_ftp.c | 14 ++-- net/netfilter/ipvs/ip_vs_nfct.c | 6 +- net/netfilter/ipvs/ip_vs_proto_ah_esp.c | 15 +++-- net/netfilter/ipvs/ip_vs_proto_sctp.c | 2 +- net/netfilter/ipvs/ip_vs_proto_tcp.c | 2 +- net/netfilter/ipvs/ip_vs_proto_udp.c | 2 +- net/netfilter/ipvs/ip_vs_sync.c | 13 ++-- 11 files changed, 153 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 605d5db81a39..f82c0ffdee74 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -477,6 +477,7 @@ extern struct ip_vs_proto_data *ip_vs_proto_data_get(struct net *net, unsigned short proto); struct ip_vs_conn_param { + struct net *net; const union nf_inet_addr *caddr; const union nf_inet_addr *vaddr; __be16 cport; @@ -494,17 +495,19 @@ struct ip_vs_conn_param { */ struct ip_vs_conn { struct list_head c_list; /* hashed list heads */ - +#ifdef CONFIG_NET_NS + struct net *net; /* Name space */ +#endif /* Protocol, addresses and port numbers */ - u16 af; /* address family */ - union nf_inet_addr caddr; /* client address */ - union nf_inet_addr vaddr; /* virtual address */ - union nf_inet_addr daddr; /* destination address */ - volatile __u32 flags; /* status flags */ - __u32 fwmark; /* Fire wall mark from skb */ - __be16 cport; - __be16 vport; - __be16 dport; + u16 af; /* address family */ + __be16 cport; + __be16 vport; + __be16 dport; + __u32 fwmark; /* Fire wall mark from skb */ + union nf_inet_addr caddr; /* client address */ + union nf_inet_addr vaddr; /* virtual address */ + union nf_inet_addr daddr; /* destination address */ + volatile __u32 flags; /* status flags */ __u16 protocol; /* Which protocol (TCP/UDP) */ /* counter and timer */ @@ -547,6 +550,33 @@ struct ip_vs_conn { __u8 pe_data_len; }; +/* + * To save some memory in conn table when name space is disabled. + */ +static inline struct net *ip_vs_conn_net(const struct ip_vs_conn *cp) +{ +#ifdef CONFIG_NET_NS + return cp->net; +#else + return &init_net; +#endif +} +static inline void ip_vs_conn_net_set(struct ip_vs_conn *cp, struct net *net) +{ +#ifdef CONFIG_NET_NS + cp->net = net; +#endif +} + +static inline int ip_vs_conn_net_eq(const struct ip_vs_conn *cp, + struct net *net) +{ +#ifdef CONFIG_NET_NS + return cp->net == net; +#else + return 1; +#endif +} /* * Extended internal versions of struct ip_vs_service_user and @@ -796,13 +826,14 @@ enum { IP_VS_DIR_LAST, }; -static inline void ip_vs_conn_fill_param(int af, int protocol, +static inline void ip_vs_conn_fill_param(struct net *net, int af, int protocol, const union nf_inet_addr *caddr, __be16 cport, const union nf_inet_addr *vaddr, __be16 vport, struct ip_vs_conn_param *p) { + p->net = net; p->af = af; p->protocol = protocol; p->caddr = caddr; diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h index bd1dad872178..1acfb334e69b 100644 --- a/include/net/netns/ip_vs.h +++ b/include/net/netns/ip_vs.h @@ -66,6 +66,8 @@ struct netns_ipvs { struct ip_vs_cpu_stats __percpu *cpustats; /* Stats per cpu */ seqcount_t *ustats_seq; /* u64 read retry */ + /* ip_vs_conn */ + atomic_t conn_count; /* connection counter */ /* ip_vs_lblc */ int sysctl_lblc_expiration; struct ctl_table_header *lblc_ctl_header; diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index b2024c942345..0d5e4feabc1b 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -64,9 +64,6 @@ static struct list_head *ip_vs_conn_tab __read_mostly; /* SLAB cache for IPVS connections */ static struct kmem_cache *ip_vs_conn_cachep __read_mostly; -/* counter for current IPVS connections */ -static atomic_t ip_vs_conn_count = ATOMIC_INIT(0); - /* counter for no client port connections */ static atomic_t ip_vs_conn_no_cport_cnt = ATOMIC_INIT(0); @@ -76,7 +73,7 @@ static unsigned int ip_vs_conn_rnd __read_mostly; /* * Fine locking granularity for big connection hash table */ -#define CT_LOCKARRAY_BITS 4 +#define CT_LOCKARRAY_BITS 5 #define CT_LOCKARRAY_SIZE (1<>8)) & ip_vs_conn_tab_mask; #endif - return jhash_3words((__force u32)addr->ip, (__force u32)port, proto, - ip_vs_conn_rnd) - & ip_vs_conn_tab_mask; + return (jhash_3words((__force u32)addr->ip, (__force u32)port, proto, + ip_vs_conn_rnd) ^ + ((size_t)net>>8)) & ip_vs_conn_tab_mask; } static unsigned int ip_vs_conn_hashkey_param(const struct ip_vs_conn_param *p, @@ -166,15 +163,15 @@ static unsigned int ip_vs_conn_hashkey_param(const struct ip_vs_conn_param *p, port = p->vport; } - return ip_vs_conn_hashkey(p->af, p->protocol, addr, port); + return ip_vs_conn_hashkey(p->net, p->af, p->protocol, addr, port); } static unsigned int ip_vs_conn_hashkey_conn(const struct ip_vs_conn *cp) { struct ip_vs_conn_param p; - ip_vs_conn_fill_param(cp->af, cp->protocol, &cp->caddr, cp->cport, - NULL, 0, &p); + ip_vs_conn_fill_param(ip_vs_conn_net(cp), cp->af, cp->protocol, + &cp->caddr, cp->cport, NULL, 0, &p); if (cp->pe) { p.pe = cp->pe; @@ -186,7 +183,7 @@ static unsigned int ip_vs_conn_hashkey_conn(const struct ip_vs_conn *cp) } /* - * Hashes ip_vs_conn in ip_vs_conn_tab by proto,addr,port. + * Hashes ip_vs_conn in ip_vs_conn_tab by netns,proto,addr,port. * returns bool success. */ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp) @@ -269,11 +266,12 @@ __ip_vs_conn_in_get(const struct ip_vs_conn_param *p) list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { if (cp->af == p->af && + p->cport == cp->cport && p->vport == cp->vport && ip_vs_addr_equal(p->af, p->caddr, &cp->caddr) && ip_vs_addr_equal(p->af, p->vaddr, &cp->vaddr) && - p->cport == cp->cport && p->vport == cp->vport && ((!p->cport) ^ (!(cp->flags & IP_VS_CONN_F_NO_CPORT))) && - p->protocol == cp->protocol) { + p->protocol == cp->protocol && + ip_vs_conn_net_eq(cp, p->net)) { /* HIT */ atomic_inc(&cp->refcnt); ct_read_unlock(hash); @@ -313,17 +311,18 @@ ip_vs_conn_fill_param_proto(int af, const struct sk_buff *skb, struct ip_vs_conn_param *p) { __be16 _ports[2], *pptr; + struct net *net = skb_net(skb); pptr = skb_header_pointer(skb, proto_off, sizeof(_ports), _ports); if (pptr == NULL) return 1; if (likely(!inverse)) - ip_vs_conn_fill_param(af, iph->protocol, &iph->saddr, pptr[0], - &iph->daddr, pptr[1], p); + ip_vs_conn_fill_param(net, af, iph->protocol, &iph->saddr, + pptr[0], &iph->daddr, pptr[1], p); else - ip_vs_conn_fill_param(af, iph->protocol, &iph->daddr, pptr[1], - &iph->saddr, pptr[0], p); + ip_vs_conn_fill_param(net, af, iph->protocol, &iph->daddr, + pptr[1], &iph->saddr, pptr[0], p); return 0; } @@ -352,6 +351,8 @@ struct ip_vs_conn *ip_vs_ct_in_get(const struct ip_vs_conn_param *p) ct_read_lock(hash); list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { + if (!ip_vs_conn_net_eq(cp, p->net)) + continue; if (p->pe_data && p->pe->ct_match) { if (p->pe == cp->pe && p->pe->ct_match(p, cp)) goto out; @@ -403,10 +404,11 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p) list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { if (cp->af == p->af && + p->vport == cp->cport && p->cport == cp->dport && ip_vs_addr_equal(p->af, p->vaddr, &cp->caddr) && ip_vs_addr_equal(p->af, p->caddr, &cp->daddr) && - p->vport == cp->cport && p->cport == cp->dport && - p->protocol == cp->protocol) { + p->protocol == cp->protocol && + ip_vs_conn_net_eq(cp, p->net)) { /* HIT */ atomic_inc(&cp->refcnt); ret = cp; @@ -609,8 +611,8 @@ struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp) struct ip_vs_dest *dest; if ((cp) && (!cp->dest)) { - dest = ip_vs_find_dest(&init_net, cp->af, &cp->daddr, cp->dport, - &cp->vaddr, cp->vport, + dest = ip_vs_find_dest(ip_vs_conn_net(cp), cp->af, &cp->daddr, + cp->dport, &cp->vaddr, cp->vport, cp->protocol, cp->fwmark); ip_vs_bind_dest(cp, dest); return dest; @@ -728,6 +730,7 @@ int ip_vs_check_template(struct ip_vs_conn *ct) static void ip_vs_conn_expire(unsigned long data) { struct ip_vs_conn *cp = (struct ip_vs_conn *)data; + struct netns_ipvs *ipvs = net_ipvs(ip_vs_conn_net(cp)); cp->timeout = 60*HZ; @@ -770,7 +773,7 @@ static void ip_vs_conn_expire(unsigned long data) ip_vs_unbind_dest(cp); if (cp->flags & IP_VS_CONN_F_NO_CPORT) atomic_dec(&ip_vs_conn_no_cport_cnt); - atomic_dec(&ip_vs_conn_count); + atomic_dec(&ipvs->conn_count); kmem_cache_free(ip_vs_conn_cachep, cp); return; @@ -804,7 +807,9 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, struct ip_vs_dest *dest, __u32 fwmark) { struct ip_vs_conn *cp; - struct ip_vs_proto_data *pd = ip_vs_proto_data_get(&init_net, p->protocol); + struct netns_ipvs *ipvs = net_ipvs(p->net); + struct ip_vs_proto_data *pd = ip_vs_proto_data_get(p->net, + p->protocol); cp = kmem_cache_zalloc(ip_vs_conn_cachep, GFP_ATOMIC); if (cp == NULL) { @@ -814,6 +819,7 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, INIT_LIST_HEAD(&cp->c_list); setup_timer(&cp->timer, ip_vs_conn_expire, (unsigned long)cp); + ip_vs_conn_net_set(cp, p->net); cp->af = p->af; cp->protocol = p->protocol; ip_vs_addr_copy(p->af, &cp->caddr, p->caddr); @@ -844,7 +850,7 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, atomic_set(&cp->n_control, 0); atomic_set(&cp->in_pkts, 0); - atomic_inc(&ip_vs_conn_count); + atomic_inc(&ipvs->conn_count); if (flags & IP_VS_CONN_F_NO_CPORT) atomic_inc(&ip_vs_conn_no_cport_cnt); @@ -886,17 +892,22 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, * /proc/net/ip_vs_conn entries */ #ifdef CONFIG_PROC_FS +struct ip_vs_iter_state { + struct seq_net_private p; + struct list_head *l; +}; static void *ip_vs_conn_array(struct seq_file *seq, loff_t pos) { int idx; struct ip_vs_conn *cp; + struct ip_vs_iter_state *iter = seq->private; for (idx = 0; idx < ip_vs_conn_tab_size; idx++) { ct_read_lock_bh(idx); list_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) { if (pos-- == 0) { - seq->private = &ip_vs_conn_tab[idx]; + iter->l = &ip_vs_conn_tab[idx]; return cp; } } @@ -908,14 +919,17 @@ static void *ip_vs_conn_array(struct seq_file *seq, loff_t pos) static void *ip_vs_conn_seq_start(struct seq_file *seq, loff_t *pos) { - seq->private = NULL; + struct ip_vs_iter_state *iter = seq->private; + + iter->l = NULL; return *pos ? ip_vs_conn_array(seq, *pos - 1) :SEQ_START_TOKEN; } static void *ip_vs_conn_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct ip_vs_conn *cp = v; - struct list_head *e, *l = seq->private; + struct ip_vs_iter_state *iter = seq->private; + struct list_head *e, *l = iter->l; int idx; ++*pos; @@ -932,18 +946,19 @@ static void *ip_vs_conn_seq_next(struct seq_file *seq, void *v, loff_t *pos) while (++idx < ip_vs_conn_tab_size) { ct_read_lock_bh(idx); list_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) { - seq->private = &ip_vs_conn_tab[idx]; + iter->l = &ip_vs_conn_tab[idx]; return cp; } ct_read_unlock_bh(idx); } - seq->private = NULL; + iter->l = NULL; return NULL; } static void ip_vs_conn_seq_stop(struct seq_file *seq, void *v) { - struct list_head *l = seq->private; + struct ip_vs_iter_state *iter = seq->private; + struct list_head *l = iter->l; if (l) ct_read_unlock_bh(l - ip_vs_conn_tab); @@ -957,9 +972,12 @@ static int ip_vs_conn_seq_show(struct seq_file *seq, void *v) "Pro FromIP FPrt ToIP TPrt DestIP DPrt State Expires PEName PEData\n"); else { const struct ip_vs_conn *cp = v; + struct net *net = seq_file_net(seq); char pe_data[IP_VS_PENAME_MAXLEN + IP_VS_PEDATA_MAXLEN + 3]; size_t len = 0; + if (!ip_vs_conn_net_eq(cp, net)) + return 0; if (cp->pe_data) { pe_data[0] = ' '; len = strlen(cp->pe->name); @@ -1004,7 +1022,8 @@ static const struct seq_operations ip_vs_conn_seq_ops = { static int ip_vs_conn_open(struct inode *inode, struct file *file) { - return seq_open(file, &ip_vs_conn_seq_ops); + return seq_open_net(inode, file, &ip_vs_conn_seq_ops, + sizeof(struct ip_vs_iter_state)); } static const struct file_operations ip_vs_conn_fops = { @@ -1031,6 +1050,10 @@ static int ip_vs_conn_sync_seq_show(struct seq_file *seq, void *v) "Pro FromIP FPrt ToIP TPrt DestIP DPrt State Origin Expires\n"); else { const struct ip_vs_conn *cp = v; + struct net *net = seq_file_net(seq); + + if (!ip_vs_conn_net_eq(cp, net)) + return 0; #ifdef CONFIG_IP_VS_IPV6 if (cp->af == AF_INET6) @@ -1067,7 +1090,8 @@ static const struct seq_operations ip_vs_conn_sync_seq_ops = { static int ip_vs_conn_sync_open(struct inode *inode, struct file *file) { - return seq_open(file, &ip_vs_conn_sync_seq_ops); + return seq_open_net(inode, file, &ip_vs_conn_sync_seq_ops, + sizeof(struct ip_vs_iter_state)); } static const struct file_operations ip_vs_conn_sync_fops = { @@ -1168,10 +1192,11 @@ void ip_vs_random_dropentry(void) /* * Flush all the connection entries in the ip_vs_conn_tab */ -static void ip_vs_conn_flush(void) +static void ip_vs_conn_flush(struct net *net) { int idx; struct ip_vs_conn *cp; + struct netns_ipvs *ipvs = net_ipvs(net); flush_again: for (idx = 0; idx < ip_vs_conn_tab_size; idx++) { @@ -1181,7 +1206,8 @@ static void ip_vs_conn_flush(void) ct_write_lock_bh(idx); list_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) { - + if (!ip_vs_conn_net_eq(cp, net)) + continue; IP_VS_DBG(4, "del connection\n"); ip_vs_conn_expire_now(cp); if (cp->control) { @@ -1194,7 +1220,7 @@ static void ip_vs_conn_flush(void) /* the counter may be not NULL, because maybe some conn entries are run by slow timer handler or unhashed but still referred */ - if (atomic_read(&ip_vs_conn_count) != 0) { + if (atomic_read(&ipvs->conn_count) != 0) { schedule(); goto flush_again; } @@ -1204,8 +1230,11 @@ static void ip_vs_conn_flush(void) */ int __net_init __ip_vs_conn_init(struct net *net) { + struct netns_ipvs *ipvs = net_ipvs(net); + if (!net_eq(net, &init_net)) /* netns not enabled yet */ return -EPERM; + atomic_set(&ipvs->conn_count, 0); proc_net_fops_create(net, "ip_vs_conn", 0, &ip_vs_conn_fops); proc_net_fops_create(net, "ip_vs_conn_sync", 0, &ip_vs_conn_sync_fops); @@ -1217,6 +1246,8 @@ static void __net_exit __ip_vs_conn_cleanup(struct net *net) if (!net_eq(net, &init_net)) /* netns not enabled yet */ return; + /* flush all the connection entries first */ + ip_vs_conn_flush(net); proc_net_remove(net, "ip_vs_conn"); proc_net_remove(net, "ip_vs_conn_sync"); } @@ -1277,9 +1308,6 @@ int __init ip_vs_conn_init(void) void ip_vs_conn_cleanup(void) { unregister_pernet_subsys(&ipvs_conn_ops); - /* flush all the connection entries first */ - ip_vs_conn_flush(); - /* Release the empty cache */ kmem_cache_destroy(ip_vs_conn_cachep); vfree(ip_vs_conn_tab); diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 7e6a2a046bf5..7205b49c56c1 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -205,7 +205,8 @@ ip_vs_conn_fill_param_persist(const struct ip_vs_service *svc, const union nf_inet_addr *vaddr, __be16 vport, struct ip_vs_conn_param *p) { - ip_vs_conn_fill_param(svc->af, protocol, caddr, cport, vaddr, vport, p); + ip_vs_conn_fill_param(svc->net, svc->af, protocol, caddr, cport, vaddr, + vport, p); p->pe = svc->pe; if (p->pe && p->pe->fill_param) return p->pe->fill_param(p, skb); @@ -348,8 +349,8 @@ ip_vs_sched_persist(struct ip_vs_service *svc, /* * Create a new connection according to the template */ - ip_vs_conn_fill_param(svc->af, iph.protocol, &iph.saddr, src_port, - &iph.daddr, dst_port, ¶m); + ip_vs_conn_fill_param(svc->net, svc->af, iph.protocol, &iph.saddr, + src_port, &iph.daddr, dst_port, ¶m); cp = ip_vs_conn_new(¶m, &dest->addr, dport, flags, dest, skb->mark); if (cp == NULL) { @@ -464,8 +465,10 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb, */ { struct ip_vs_conn_param p; - ip_vs_conn_fill_param(svc->af, iph.protocol, &iph.saddr, - pptr[0], &iph.daddr, pptr[1], &p); + + ip_vs_conn_fill_param(svc->net, svc->af, iph.protocol, + &iph.saddr, pptr[0], &iph.daddr, pptr[1], + &p); cp = ip_vs_conn_new(&p, &dest->addr, dest->port ? dest->port : pptr[1], flags, dest, skb->mark); @@ -532,7 +535,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, IP_VS_DBG(6, "%s(): create a cache_bypass entry\n", __func__); { struct ip_vs_conn_param p; - ip_vs_conn_fill_param(svc->af, iph.protocol, + ip_vs_conn_fill_param(svc->net, svc->af, iph.protocol, &iph.saddr, pptr[0], &iph.daddr, pptr[1], &p); cp = ip_vs_conn_new(&p, &daddr, 0, diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index 77b0036dcb73..6a04f9ab9d0d 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -198,13 +198,15 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, */ { struct ip_vs_conn_param p; - ip_vs_conn_fill_param(AF_INET, iph->protocol, - &from, port, &cp->caddr, 0, &p); + ip_vs_conn_fill_param(ip_vs_conn_net(cp), AF_INET, + iph->protocol, &from, port, + &cp->caddr, 0, &p); n_cp = ip_vs_conn_out_get(&p); } if (!n_cp) { struct ip_vs_conn_param p; - ip_vs_conn_fill_param(AF_INET, IPPROTO_TCP, &cp->caddr, + ip_vs_conn_fill_param(ip_vs_conn_net(cp), + AF_INET, IPPROTO_TCP, &cp->caddr, 0, &cp->vaddr, port, &p); n_cp = ip_vs_conn_new(&p, &from, port, IP_VS_CONN_F_NO_CPORT | @@ -361,9 +363,9 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, { struct ip_vs_conn_param p; - ip_vs_conn_fill_param(AF_INET, iph->protocol, &to, port, - &cp->vaddr, htons(ntohs(cp->vport)-1), - &p); + ip_vs_conn_fill_param(ip_vs_conn_net(cp), AF_INET, + iph->protocol, &to, port, &cp->vaddr, + htons(ntohs(cp->vport)-1), &p); n_cp = ip_vs_conn_in_get(&p); if (!n_cp) { n_cp = ip_vs_conn_new(&p, &cp->daddr, diff --git a/net/netfilter/ipvs/ip_vs_nfct.c b/net/netfilter/ipvs/ip_vs_nfct.c index 4680647cd450..f454c80df0a7 100644 --- a/net/netfilter/ipvs/ip_vs_nfct.c +++ b/net/netfilter/ipvs/ip_vs_nfct.c @@ -141,6 +141,7 @@ static void ip_vs_nfct_expect_callback(struct nf_conn *ct, struct nf_conntrack_tuple *orig, new_reply; struct ip_vs_conn *cp; struct ip_vs_conn_param p; + struct net *net = nf_ct_net(ct); if (exp->tuple.src.l3num != PF_INET) return; @@ -155,7 +156,7 @@ static void ip_vs_nfct_expect_callback(struct nf_conn *ct, /* RS->CLIENT */ orig = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; - ip_vs_conn_fill_param(exp->tuple.src.l3num, orig->dst.protonum, + ip_vs_conn_fill_param(net, exp->tuple.src.l3num, orig->dst.protonum, &orig->src.u3, orig->src.u.tcp.port, &orig->dst.u3, orig->dst.u.tcp.port, &p); cp = ip_vs_conn_out_get(&p); @@ -268,7 +269,8 @@ void ip_vs_conn_drop_conntrack(struct ip_vs_conn *cp) " for conn " FMT_CONN "\n", __func__, ARG_TUPLE(&tuple), ARG_CONN(cp)); - h = nf_conntrack_find_get(&init_net, NF_CT_DEFAULT_ZONE, &tuple); + h = nf_conntrack_find_get(ip_vs_conn_net(cp), NF_CT_DEFAULT_ZONE, + &tuple); if (h) { ct = nf_ct_tuplehash_to_ctrack(h); /* Show what happens instead of calling nf_ct_kill() */ diff --git a/net/netfilter/ipvs/ip_vs_proto_ah_esp.c b/net/netfilter/ipvs/ip_vs_proto_ah_esp.c index 28039cbfcff4..5b8eb8b12c3e 100644 --- a/net/netfilter/ipvs/ip_vs_proto_ah_esp.c +++ b/net/netfilter/ipvs/ip_vs_proto_ah_esp.c @@ -41,15 +41,16 @@ struct isakmp_hdr { #define PORT_ISAKMP 500 static void -ah_esp_conn_fill_param_proto(int af, const struct ip_vs_iphdr *iph, - int inverse, struct ip_vs_conn_param *p) +ah_esp_conn_fill_param_proto(struct net *net, int af, + const struct ip_vs_iphdr *iph, int inverse, + struct ip_vs_conn_param *p) { if (likely(!inverse)) - ip_vs_conn_fill_param(af, IPPROTO_UDP, + ip_vs_conn_fill_param(net, af, IPPROTO_UDP, &iph->saddr, htons(PORT_ISAKMP), &iph->daddr, htons(PORT_ISAKMP), p); else - ip_vs_conn_fill_param(af, IPPROTO_UDP, + ip_vs_conn_fill_param(net, af, IPPROTO_UDP, &iph->daddr, htons(PORT_ISAKMP), &iph->saddr, htons(PORT_ISAKMP), p); } @@ -61,8 +62,9 @@ ah_esp_conn_in_get(int af, const struct sk_buff *skb, { struct ip_vs_conn *cp; struct ip_vs_conn_param p; + struct net *net = skb_net(skb); - ah_esp_conn_fill_param_proto(af, iph, inverse, &p); + ah_esp_conn_fill_param_proto(net, af, iph, inverse, &p); cp = ip_vs_conn_in_get(&p); if (!cp) { /* @@ -89,8 +91,9 @@ ah_esp_conn_out_get(int af, const struct sk_buff *skb, { struct ip_vs_conn *cp; struct ip_vs_conn_param p; + struct net *net = skb_net(skb); - ah_esp_conn_fill_param_proto(af, iph, inverse, &p); + ah_esp_conn_fill_param_proto(net, af, iph, inverse, &p); cp = ip_vs_conn_out_get(&p); if (!cp) { IP_VS_DBG_BUF(12, "Unknown ISAKMP entry for inout packet " diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 569e77bf08c4..550365a690c7 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -1055,7 +1055,7 @@ static void sctp_unregister_app(struct net *net, struct ip_vs_app *inc) static int sctp_app_conn_bind(struct ip_vs_conn *cp) { - struct netns_ipvs *ipvs = net_ipvs(&init_net); + struct netns_ipvs *ipvs = net_ipvs(ip_vs_conn_net(cp)); int hash; struct ip_vs_app *inc; int result = 0; diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 757aaaf083bb..d8b3f9f15826 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -620,7 +620,7 @@ tcp_unregister_app(struct net *net, struct ip_vs_app *inc) static int tcp_app_conn_bind(struct ip_vs_conn *cp) { - struct netns_ipvs *ipvs = net_ipvs(&init_net); + struct netns_ipvs *ipvs = net_ipvs(ip_vs_conn_net(cp)); int hash; struct ip_vs_app *inc; int result = 0; diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index 1dc394100fa8..581157bbded5 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -396,7 +396,7 @@ udp_unregister_app(struct net *net, struct ip_vs_app *inc) static int udp_app_conn_bind(struct ip_vs_conn *cp) { - struct netns_ipvs *ipvs = net_ipvs(&init_net); + struct netns_ipvs *ipvs = net_ipvs(ip_vs_conn_net(cp)); int hash; struct ip_vs_app *inc; int result = 0; diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index c29e73d686fb..f85e47daecc3 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -660,21 +660,21 @@ control: * fill_param used by version 1 */ static inline int -ip_vs_conn_fill_param_sync(int af, union ip_vs_sync_conn *sc, +ip_vs_conn_fill_param_sync(struct net *net, int af, union ip_vs_sync_conn *sc, struct ip_vs_conn_param *p, __u8 *pe_data, unsigned int pe_data_len, __u8 *pe_name, unsigned int pe_name_len) { #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) - ip_vs_conn_fill_param(af, sc->v6.protocol, + ip_vs_conn_fill_param(net, af, sc->v6.protocol, (const union nf_inet_addr *)&sc->v6.caddr, sc->v6.cport, (const union nf_inet_addr *)&sc->v6.vaddr, sc->v6.vport, p); else #endif - ip_vs_conn_fill_param(af, sc->v4.protocol, + ip_vs_conn_fill_param(net, af, sc->v4.protocol, (const union nf_inet_addr *)&sc->v4.caddr, sc->v4.cport, (const union nf_inet_addr *)&sc->v4.vaddr, @@ -881,7 +881,7 @@ static void ip_vs_process_message_v0(struct net *net, const char *buffer, } } - ip_vs_conn_fill_param(AF_INET, s->protocol, + ip_vs_conn_fill_param(net, AF_INET, s->protocol, (const union nf_inet_addr *)&s->caddr, s->cport, (const union nf_inet_addr *)&s->vaddr, @@ -1043,9 +1043,8 @@ static inline int ip_vs_proc_sync_conn(struct net *net, __u8 *p, __u8 *msg_end) state = 0; } } - if (ip_vs_conn_fill_param_sync(af, s, ¶m, - pe_data, pe_data_len, - pe_name, pe_name_len)) { + if (ip_vs_conn_fill_param_sync(net, af, s, ¶m, pe_data, + pe_data_len, pe_name, pe_name_len)) { retc = 50; goto out; } -- cgit v1.2.3 From a0840e2e165a370ca24a59545e564e9881a55891 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:58 +0100 Subject: IPVS: netns, ip_vs_ctl local vars moved to ipvs struct. Moving global vars to ipvs struct, except for svc table lock. Next patch for ctl will be drop-rate handling. *v3 __ip_vs_mutex remains global ip_vs_conntrack_enabled(struct netns_ipvs *ipvs) Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 27 ++-- include/net/netns/ip_vs.h | 37 ++++- net/netfilter/ipvs/ip_vs_conn.c | 7 +- net/netfilter/ipvs/ip_vs_core.c | 34 ++-- net/netfilter/ipvs/ip_vs_ctl.c | 291 ++++++++++++++++++---------------- net/netfilter/ipvs/ip_vs_proto_sctp.c | 2 +- net/netfilter/ipvs/ip_vs_proto_tcp.c | 2 +- net/netfilter/ipvs/ip_vs_proto_udp.c | 2 +- net/netfilter/ipvs/ip_vs_sync.c | 9 +- 9 files changed, 230 insertions(+), 181 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index f82c0ffdee74..af9acf44e40a 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -41,7 +41,7 @@ static inline struct netns_ipvs *net_ipvs(struct net* net) * Get net ptr from skb in traffic cases * use skb_sknet when call is from userland (ioctl or netlink) */ -static inline struct net *skb_net(struct sk_buff *skb) +static inline struct net *skb_net(const struct sk_buff *skb) { #ifdef CONFIG_NET_NS #ifdef CONFIG_IP_VS_DEBUG @@ -69,7 +69,7 @@ static inline struct net *skb_net(struct sk_buff *skb) #endif } -static inline struct net *skb_sknet(struct sk_buff *skb) +static inline struct net *skb_sknet(const struct sk_buff *skb) { #ifdef CONFIG_NET_NS #ifdef CONFIG_IP_VS_DEBUG @@ -1023,13 +1023,6 @@ extern int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, /* * IPVS control data and functions (from ip_vs_ctl.c) */ -extern int sysctl_ip_vs_cache_bypass; -extern int sysctl_ip_vs_expire_nodest_conn; -extern int sysctl_ip_vs_expire_quiescent_template; -extern int sysctl_ip_vs_sync_threshold[2]; -extern int sysctl_ip_vs_nat_icmp_send; -extern int sysctl_ip_vs_conntrack; -extern int sysctl_ip_vs_snat_reroute; extern struct ip_vs_stats ip_vs_stats; extern const struct ctl_path net_vs_ctl_path[]; extern int sysctl_ip_vs_sync_ver; @@ -1119,11 +1112,13 @@ extern int ip_vs_icmp_xmit_v6 extern int ip_vs_drop_rate; extern int ip_vs_drop_counter; -static __inline__ int ip_vs_todrop(void) +static inline int ip_vs_todrop(struct netns_ipvs *ipvs) { - if (!ip_vs_drop_rate) return 0; - if (--ip_vs_drop_counter > 0) return 0; - ip_vs_drop_counter = ip_vs_drop_rate; + if (!ipvs->drop_rate) + return 0; + if (--ipvs->drop_counter > 0) + return 0; + ipvs->drop_counter = ipvs->drop_rate; return 1; } @@ -1211,9 +1206,9 @@ static inline void ip_vs_notrack(struct sk_buff *skb) * Netfilter connection tracking * (from ip_vs_nfct.c) */ -static inline int ip_vs_conntrack_enabled(void) +static inline int ip_vs_conntrack_enabled(struct netns_ipvs *ipvs) { - return sysctl_ip_vs_conntrack; + return ipvs->sysctl_conntrack; } extern void ip_vs_update_conntrack(struct sk_buff *skb, struct ip_vs_conn *cp, @@ -1226,7 +1221,7 @@ extern void ip_vs_conn_drop_conntrack(struct ip_vs_conn *cp); #else -static inline int ip_vs_conntrack_enabled(void) +static inline int ip_vs_conntrack_enabled(struct netns_ipvs *ipvs) { return 0; } diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h index 1acfb334e69b..c4b1abf258e4 100644 --- a/include/net/netns/ip_vs.h +++ b/include/net/netns/ip_vs.h @@ -61,13 +61,46 @@ struct netns_ipvs { struct list_head sctp_apps[SCTP_APP_TAB_SIZE]; spinlock_t sctp_app_lock; #endif + /* ip_vs_conn */ + atomic_t conn_count; /* connection counter */ + /* ip_vs_ctl */ struct ip_vs_stats *tot_stats; /* Statistics & est. */ struct ip_vs_cpu_stats __percpu *cpustats; /* Stats per cpu */ seqcount_t *ustats_seq; /* u64 read retry */ - /* ip_vs_conn */ - atomic_t conn_count; /* connection counter */ + int num_services; /* no of virtual services */ + /* 1/rate drop and drop-entry variables */ + int drop_rate; + int drop_counter; + atomic_t dropentry; + /* locks in ctl.c */ + spinlock_t dropentry_lock; /* drop entry handling */ + spinlock_t droppacket_lock; /* drop packet handling */ + spinlock_t securetcp_lock; /* state and timeout tables */ + rwlock_t rs_lock; /* real services table */ + /* semaphore for IPVS sockopts. And, [gs]etsockopt may sleep. */ + struct lock_class_key ctl_key; /* ctl_mutex debuging */ + /* sys-ctl struct */ + struct ctl_table_header *sysctl_hdr; + struct ctl_table *sysctl_tbl; + /* sysctl variables */ + int sysctl_amemthresh; + int sysctl_am_droprate; + int sysctl_drop_entry; + int sysctl_drop_packet; + int sysctl_secure_tcp; +#ifdef CONFIG_IP_VS_NFCT + int sysctl_conntrack; +#endif + int sysctl_snat_reroute; + int sysctl_sync_ver; + int sysctl_cache_bypass; + int sysctl_expire_nodest_conn; + int sysctl_expire_quiescent_template; + int sysctl_sync_threshold[2]; + int sysctl_nat_icmp_send; + /* ip_vs_lblc */ int sysctl_lblc_expiration; struct ctl_table_header *lblc_ctl_header; diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 0d5e4feabc1b..5ba205a4d79c 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -686,13 +686,14 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp) int ip_vs_check_template(struct ip_vs_conn *ct) { struct ip_vs_dest *dest = ct->dest; + struct netns_ipvs *ipvs = net_ipvs(ip_vs_conn_net(ct)); /* * Checking the dest server status. */ if ((dest == NULL) || !(dest->flags & IP_VS_DEST_F_AVAILABLE) || - (sysctl_ip_vs_expire_quiescent_template && + (ipvs->sysctl_expire_quiescent_template && (atomic_read(&dest->weight) == 0))) { IP_VS_DBG_BUF(9, "check_template: dest not available for " "protocol %s s:%s:%d v:%s:%d " @@ -879,7 +880,7 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, * IP_VS_CONN_F_ONE_PACKET too. */ - if (ip_vs_conntrack_enabled()) + if (ip_vs_conntrack_enabled(ipvs)) cp->flags |= IP_VS_CONN_F_NFCT; /* Hash it in the ip_vs_conn_tab finally */ @@ -1198,7 +1199,7 @@ static void ip_vs_conn_flush(struct net *net) struct ip_vs_conn *cp; struct netns_ipvs *ipvs = net_ipvs(net); - flush_again: +flush_again: for (idx = 0; idx < ip_vs_conn_tab_size; idx++) { /* * Lock is actually needed in this loop. diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 7205b49c56c1..a7c59a722af3 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -499,6 +499,7 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb, int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, struct ip_vs_proto_data *pd) { + struct netns_ipvs *ipvs; __be16 _ports[2], *pptr; struct ip_vs_iphdr iph; int unicast; @@ -521,7 +522,8 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, /* if it is fwmark-based service, the cache_bypass sysctl is up and the destination is a non-local unicast, then create a cache_bypass connection entry */ - if (sysctl_ip_vs_cache_bypass && svc->fwmark && unicast) { + ipvs = net_ipvs(skb_net(skb)); + if (ipvs->sysctl_cache_bypass && svc->fwmark && unicast) { int ret, cs; struct ip_vs_conn *cp; unsigned int flags = (svc->flags & IP_VS_SVC_F_ONEPACKET && @@ -733,6 +735,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, unsigned int offset, unsigned int ihl) { + struct netns_ipvs *ipvs; unsigned int verdict = NF_DROP; if (IP_VS_FWD_METHOD(cp) != 0) { @@ -754,6 +757,8 @@ static int handle_response_icmp(int af, struct sk_buff *skb, if (!skb_make_writable(skb, offset)) goto out; + ipvs = net_ipvs(skb_net(skb)); + #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) ip_vs_nat_icmp_v6(skb, pp, cp, 1); @@ -763,11 +768,11 @@ static int handle_response_icmp(int af, struct sk_buff *skb, #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) { - if (sysctl_ip_vs_snat_reroute && ip6_route_me_harder(skb) != 0) + if (ipvs->sysctl_snat_reroute && ip6_route_me_harder(skb) != 0) goto out; } else #endif - if ((sysctl_ip_vs_snat_reroute || + if ((ipvs->sysctl_snat_reroute || skb_rtable(skb)->rt_flags & RTCF_LOCAL) && ip_route_me_harder(skb, RTN_LOCAL) != 0) goto out; @@ -979,6 +984,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, int ihl) { struct ip_vs_protocol *pp = pd->pp; + struct netns_ipvs *ipvs; IP_VS_DBG_PKT(11, af, pp, skb, 0, "Outgoing packet"); @@ -1014,13 +1020,15 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, * if it came from this machine itself. So re-compute * the routing information. */ + ipvs = net_ipvs(skb_net(skb)); + #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) { - if (sysctl_ip_vs_snat_reroute && ip6_route_me_harder(skb) != 0) + if (ipvs->sysctl_snat_reroute && ip6_route_me_harder(skb) != 0) goto drop; } else #endif - if ((sysctl_ip_vs_snat_reroute || + if ((ipvs->sysctl_snat_reroute || skb_rtable(skb)->rt_flags & RTCF_LOCAL) && ip_route_me_harder(skb, RTN_LOCAL) != 0) goto drop; @@ -1057,6 +1065,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) struct ip_vs_protocol *pp; struct ip_vs_proto_data *pd; struct ip_vs_conn *cp; + struct netns_ipvs *ipvs; EnterFunction(11); @@ -1131,10 +1140,11 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) * Check if the packet belongs to an existing entry */ cp = pp->conn_out_get(af, skb, &iph, iph.len, 0); + ipvs = net_ipvs(net); if (likely(cp)) return handle_response(af, skb, pd, cp, iph.len); - if (sysctl_ip_vs_nat_icmp_send && + if (ipvs->sysctl_nat_icmp_send && (pp->protocol == IPPROTO_TCP || pp->protocol == IPPROTO_UDP || pp->protocol == IPPROTO_SCTP)) { @@ -1580,7 +1590,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) if (cp->dest && !(cp->dest->flags & IP_VS_DEST_F_AVAILABLE)) { /* the destination server is not available */ - if (sysctl_ip_vs_expire_nodest_conn) { + if (ipvs->sysctl_expire_nodest_conn) { /* try to expire the connection immediately */ ip_vs_conn_expire_now(cp); } @@ -1610,15 +1620,15 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) */ if (cp->flags & IP_VS_CONN_F_ONE_PACKET) - pkts = sysctl_ip_vs_sync_threshold[0]; + pkts = ipvs->sysctl_sync_threshold[0]; else pkts = atomic_add_return(1, &cp->in_pkts); if ((ipvs->sync_state & IP_VS_STATE_MASTER) && cp->protocol == IPPROTO_SCTP) { if ((cp->state == IP_VS_SCTP_S_ESTABLISHED && - (pkts % sysctl_ip_vs_sync_threshold[1] - == sysctl_ip_vs_sync_threshold[0])) || + (pkts % ipvs->sysctl_sync_threshold[1] + == ipvs->sysctl_sync_threshold[0])) || (cp->old_state != cp->state && ((cp->state == IP_VS_SCTP_S_CLOSED) || (cp->state == IP_VS_SCTP_S_SHUT_ACK_CLI) || @@ -1632,8 +1642,8 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) else if ((ipvs->sync_state & IP_VS_STATE_MASTER) && (((cp->protocol != IPPROTO_TCP || cp->state == IP_VS_TCP_S_ESTABLISHED) && - (pkts % sysctl_ip_vs_sync_threshold[1] - == sysctl_ip_vs_sync_threshold[0])) || + (pkts % ipvs->sysctl_sync_threshold[1] + == ipvs->sysctl_sync_threshold[0])) || ((cp->protocol == IPPROTO_TCP) && (cp->old_state != cp->state) && ((cp->state == IP_VS_TCP_S_FIN_WAIT) || (cp->state == IP_VS_TCP_S_CLOSE) || diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index cbd58c60e1bf..183ac18bded5 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -58,42 +58,7 @@ static DEFINE_MUTEX(__ip_vs_mutex); /* lock for service table */ static DEFINE_RWLOCK(__ip_vs_svc_lock); -/* lock for table with the real services */ -static DEFINE_RWLOCK(__ip_vs_rs_lock); - -/* lock for state and timeout tables */ -static DEFINE_SPINLOCK(ip_vs_securetcp_lock); - -/* lock for drop entry handling */ -static DEFINE_SPINLOCK(__ip_vs_dropentry_lock); - -/* lock for drop packet handling */ -static DEFINE_SPINLOCK(__ip_vs_droppacket_lock); - -/* 1/rate drop and drop-entry variables */ -int ip_vs_drop_rate = 0; -int ip_vs_drop_counter = 0; -static atomic_t ip_vs_dropentry = ATOMIC_INIT(0); - -/* number of virtual services */ -static int ip_vs_num_services = 0; - /* sysctl variables */ -static int sysctl_ip_vs_drop_entry = 0; -static int sysctl_ip_vs_drop_packet = 0; -static int sysctl_ip_vs_secure_tcp = 0; -static int sysctl_ip_vs_amemthresh = 1024; -static int sysctl_ip_vs_am_droprate = 10; -int sysctl_ip_vs_cache_bypass = 0; -int sysctl_ip_vs_expire_nodest_conn = 0; -int sysctl_ip_vs_expire_quiescent_template = 0; -int sysctl_ip_vs_sync_threshold[2] = { 3, 50 }; -int sysctl_ip_vs_nat_icmp_send = 0; -#ifdef CONFIG_IP_VS_NFCT -int sysctl_ip_vs_conntrack; -#endif -int sysctl_ip_vs_snat_reroute = 1; -int sysctl_ip_vs_sync_ver = 1; /* Default version of sync proto */ #ifdef CONFIG_IP_VS_DEBUG static int sysctl_ip_vs_debug_level = 0; @@ -142,73 +107,73 @@ static void update_defense_level(struct netns_ipvs *ipvs) /* si_swapinfo(&i); */ /* availmem = availmem - (i.totalswap - i.freeswap); */ - nomem = (availmem < sysctl_ip_vs_amemthresh); + nomem = (availmem < ipvs->sysctl_amemthresh); local_bh_disable(); /* drop_entry */ - spin_lock(&__ip_vs_dropentry_lock); - switch (sysctl_ip_vs_drop_entry) { + spin_lock(&ipvs->dropentry_lock); + switch (ipvs->sysctl_drop_entry) { case 0: - atomic_set(&ip_vs_dropentry, 0); + atomic_set(&ipvs->dropentry, 0); break; case 1: if (nomem) { - atomic_set(&ip_vs_dropentry, 1); - sysctl_ip_vs_drop_entry = 2; + atomic_set(&ipvs->dropentry, 1); + ipvs->sysctl_drop_entry = 2; } else { - atomic_set(&ip_vs_dropentry, 0); + atomic_set(&ipvs->dropentry, 0); } break; case 2: if (nomem) { - atomic_set(&ip_vs_dropentry, 1); + atomic_set(&ipvs->dropentry, 1); } else { - atomic_set(&ip_vs_dropentry, 0); - sysctl_ip_vs_drop_entry = 1; + atomic_set(&ipvs->dropentry, 0); + ipvs->sysctl_drop_entry = 1; }; break; case 3: - atomic_set(&ip_vs_dropentry, 1); + atomic_set(&ipvs->dropentry, 1); break; } - spin_unlock(&__ip_vs_dropentry_lock); + spin_unlock(&ipvs->dropentry_lock); /* drop_packet */ - spin_lock(&__ip_vs_droppacket_lock); - switch (sysctl_ip_vs_drop_packet) { + spin_lock(&ipvs->droppacket_lock); + switch (ipvs->sysctl_drop_packet) { case 0: - ip_vs_drop_rate = 0; + ipvs->drop_rate = 0; break; case 1: if (nomem) { - ip_vs_drop_rate = ip_vs_drop_counter - = sysctl_ip_vs_amemthresh / - (sysctl_ip_vs_amemthresh-availmem); - sysctl_ip_vs_drop_packet = 2; + ipvs->drop_rate = ipvs->drop_counter + = ipvs->sysctl_amemthresh / + (ipvs->sysctl_amemthresh-availmem); + ipvs->sysctl_drop_packet = 2; } else { - ip_vs_drop_rate = 0; + ipvs->drop_rate = 0; } break; case 2: if (nomem) { - ip_vs_drop_rate = ip_vs_drop_counter - = sysctl_ip_vs_amemthresh / - (sysctl_ip_vs_amemthresh-availmem); + ipvs->drop_rate = ipvs->drop_counter + = ipvs->sysctl_amemthresh / + (ipvs->sysctl_amemthresh-availmem); } else { - ip_vs_drop_rate = 0; - sysctl_ip_vs_drop_packet = 1; + ipvs->drop_rate = 0; + ipvs->sysctl_drop_packet = 1; } break; case 3: - ip_vs_drop_rate = sysctl_ip_vs_am_droprate; + ipvs->drop_rate = ipvs->sysctl_am_droprate; break; } - spin_unlock(&__ip_vs_droppacket_lock); + spin_unlock(&ipvs->droppacket_lock); /* secure_tcp */ - spin_lock(&ip_vs_securetcp_lock); - switch (sysctl_ip_vs_secure_tcp) { + spin_lock(&ipvs->securetcp_lock); + switch (ipvs->sysctl_secure_tcp) { case 0: if (old_secure_tcp >= 2) to_change = 0; @@ -217,7 +182,7 @@ static void update_defense_level(struct netns_ipvs *ipvs) if (nomem) { if (old_secure_tcp < 2) to_change = 1; - sysctl_ip_vs_secure_tcp = 2; + ipvs->sysctl_secure_tcp = 2; } else { if (old_secure_tcp >= 2) to_change = 0; @@ -230,7 +195,7 @@ static void update_defense_level(struct netns_ipvs *ipvs) } else { if (old_secure_tcp >= 2) to_change = 0; - sysctl_ip_vs_secure_tcp = 1; + ipvs->sysctl_secure_tcp = 1; } break; case 3: @@ -238,11 +203,11 @@ static void update_defense_level(struct netns_ipvs *ipvs) to_change = 1; break; } - old_secure_tcp = sysctl_ip_vs_secure_tcp; + old_secure_tcp = ipvs->sysctl_secure_tcp; if (to_change >= 0) ip_vs_protocol_timeout_change(ipvs, - sysctl_ip_vs_secure_tcp > 1); - spin_unlock(&ip_vs_securetcp_lock); + ipvs->sysctl_secure_tcp > 1); + spin_unlock(&ipvs->securetcp_lock); local_bh_enable(); } @@ -260,7 +225,7 @@ static void defense_work_handler(struct work_struct *work) struct netns_ipvs *ipvs = net_ipvs(&init_net); update_defense_level(ipvs); - if (atomic_read(&ip_vs_dropentry)) + if (atomic_read(&ipvs->dropentry)) ip_vs_random_dropentry(); schedule_delayed_work(&defense_work, DEFENSE_TIMER_PERIOD); @@ -602,7 +567,7 @@ ip_vs_lookup_real_service(struct net *net, int af, __u16 protocol, */ hash = ip_vs_rs_hashkey(af, daddr, dport); - read_lock(&__ip_vs_rs_lock); + read_lock(&ipvs->rs_lock); list_for_each_entry(dest, &ipvs->rs_table[hash], d_list) { if ((dest->af == af) && ip_vs_addr_equal(af, &dest->addr, daddr) @@ -610,11 +575,11 @@ ip_vs_lookup_real_service(struct net *net, int af, __u16 protocol, && ((dest->protocol == protocol) || dest->vfwmark)) { /* HIT */ - read_unlock(&__ip_vs_rs_lock); + read_unlock(&ipvs->rs_lock); return dest; } } - read_unlock(&__ip_vs_rs_lock); + read_unlock(&ipvs->rs_lock); return NULL; } @@ -788,9 +753,9 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, * Put the real service in rs_table if not present. * For now only for NAT! */ - write_lock_bh(&__ip_vs_rs_lock); + write_lock_bh(&ipvs->rs_lock); ip_vs_rs_hash(ipvs, dest); - write_unlock_bh(&__ip_vs_rs_lock); + write_unlock_bh(&ipvs->rs_lock); } atomic_set(&dest->conn_flags, conn_flags); @@ -1022,14 +987,16 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) */ static void __ip_vs_del_dest(struct net *net, struct ip_vs_dest *dest) { + struct netns_ipvs *ipvs = net_ipvs(net); + ip_vs_kill_estimator(net, &dest->stats); /* * Remove it from the d-linked list with the real services. */ - write_lock_bh(&__ip_vs_rs_lock); + write_lock_bh(&ipvs->rs_lock); ip_vs_rs_unhash(dest); - write_unlock_bh(&__ip_vs_rs_lock); + write_unlock_bh(&ipvs->rs_lock); /* * Decrease the refcnt of the dest, and free the dest @@ -1092,7 +1059,6 @@ static int ip_vs_del_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) { struct ip_vs_dest *dest; - struct net *net = svc->net; __be16 dport = udest->port; EnterFunction(2); @@ -1121,7 +1087,7 @@ ip_vs_del_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) /* * Delete the destination */ - __ip_vs_del_dest(net, dest); + __ip_vs_del_dest(svc->net, dest); LeaveFunction(2); @@ -1140,6 +1106,7 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u, struct ip_vs_scheduler *sched = NULL; struct ip_vs_pe *pe = NULL; struct ip_vs_service *svc = NULL; + struct netns_ipvs *ipvs = net_ipvs(net); /* increase the module use count */ ip_vs_use_count_inc(); @@ -1219,7 +1186,7 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u, /* Count only IPv4 services for old get/setsockopt interface */ if (svc->af == AF_INET) - ip_vs_num_services++; + ipvs->num_services++; /* Hash the service into the service table */ write_lock_bh(&__ip_vs_svc_lock); @@ -1359,12 +1326,13 @@ static void __ip_vs_del_service(struct ip_vs_service *svc) struct ip_vs_dest *dest, *nxt; struct ip_vs_scheduler *old_sched; struct ip_vs_pe *old_pe; + struct netns_ipvs *ipvs = net_ipvs(svc->net); pr_info("%s: enter\n", __func__); /* Count only IPv4 services for old get/setsockopt interface */ if (svc->af == AF_INET) - ip_vs_num_services--; + ipvs->num_services--; ip_vs_kill_estimator(svc->net, &svc->stats); @@ -1589,42 +1557,31 @@ proc_do_sync_mode(ctl_table *table, int write, /* * IPVS sysctl table (under the /proc/sys/net/ipv4/vs/) + * Do not change order or insert new entries without + * align with netns init in __ip_vs_control_init() */ static struct ctl_table vs_vars[] = { { .procname = "amemthresh", - .data = &sysctl_ip_vs_amemthresh, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, -#ifdef CONFIG_IP_VS_DEBUG - { - .procname = "debug_level", - .data = &sysctl_ip_vs_debug_level, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec, }, -#endif { .procname = "am_droprate", - .data = &sysctl_ip_vs_am_droprate, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec, }, { .procname = "drop_entry", - .data = &sysctl_ip_vs_drop_entry, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_do_defense_mode, }, { .procname = "drop_packet", - .data = &sysctl_ip_vs_drop_packet, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_do_defense_mode, @@ -1632,7 +1589,6 @@ static struct ctl_table vs_vars[] = { #ifdef CONFIG_IP_VS_NFCT { .procname = "conntrack", - .data = &sysctl_ip_vs_conntrack, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec, @@ -1640,25 +1596,62 @@ static struct ctl_table vs_vars[] = { #endif { .procname = "secure_tcp", - .data = &sysctl_ip_vs_secure_tcp, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_do_defense_mode, }, { .procname = "snat_reroute", - .data = &sysctl_ip_vs_snat_reroute, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec, }, { .procname = "sync_version", - .data = &sysctl_ip_vs_sync_ver, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_do_sync_mode, }, + { + .procname = "cache_bypass", + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { + .procname = "expire_nodest_conn", + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { + .procname = "expire_quiescent_template", + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { + .procname = "sync_threshold", + .maxlen = + sizeof(((struct netns_ipvs *)0)->sysctl_sync_threshold), + .mode = 0644, + .proc_handler = proc_do_sync_threshold, + }, + { + .procname = "nat_icmp_send", + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, +#ifdef CONFIG_IP_VS_DEBUG + { + .procname = "debug_level", + .data = &sysctl_ip_vs_debug_level, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, +#endif #if 0 { .procname = "timeout_established", @@ -1745,41 +1738,6 @@ static struct ctl_table vs_vars[] = { .proc_handler = proc_dointvec_jiffies, }, #endif - { - .procname = "cache_bypass", - .data = &sysctl_ip_vs_cache_bypass, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, - { - .procname = "expire_nodest_conn", - .data = &sysctl_ip_vs_expire_nodest_conn, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, - { - .procname = "expire_quiescent_template", - .data = &sysctl_ip_vs_expire_quiescent_template, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, - { - .procname = "sync_threshold", - .data = &sysctl_ip_vs_sync_threshold, - .maxlen = sizeof(sysctl_ip_vs_sync_threshold), - .mode = 0644, - .proc_handler = proc_do_sync_threshold, - }, - { - .procname = "nat_icmp_send", - .data = &sysctl_ip_vs_nat_icmp_send, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, { } }; @@ -1791,8 +1749,6 @@ const struct ctl_path net_vs_ctl_path[] = { }; EXPORT_SYMBOL_GPL(net_vs_ctl_path); -static struct ctl_table_header * sysctl_header; - #ifdef CONFIG_PROC_FS struct ip_vs_iter { @@ -2543,7 +2499,7 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) struct ip_vs_getinfo info; info.version = IP_VS_VERSION_CODE; info.size = ip_vs_conn_tab_size; - info.num_services = ip_vs_num_services; + info.num_services = ipvs->num_services; if (copy_to_user(user, &info, sizeof(info)) != 0) ret = -EFAULT; } @@ -3014,7 +2970,7 @@ static int ip_vs_genl_dump_dests(struct sk_buff *skb, struct ip_vs_service *svc; struct ip_vs_dest *dest; struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1]; - struct net *net; + struct net *net = skb_sknet(skb); mutex_lock(&__ip_vs_mutex); @@ -3023,7 +2979,7 @@ static int ip_vs_genl_dump_dests(struct sk_buff *skb, IPVS_CMD_ATTR_MAX, ip_vs_cmd_policy)) goto out_err; - net = skb_sknet(skb); + svc = ip_vs_genl_find_service(net, attrs[IPVS_CMD_ATTR_SERVICE]); if (IS_ERR(svc) || svc == NULL) goto out_err; @@ -3215,8 +3171,10 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) int ret = 0, cmd; int need_full_svc = 0, need_full_dest = 0; struct net *net; + struct netns_ipvs *ipvs; net = skb_sknet(skb); + ipvs = net_ipvs(net); cmd = info->genlhdr->cmd; mutex_lock(&__ip_vs_mutex); @@ -3326,8 +3284,10 @@ static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info) void *reply; int ret, cmd, reply_cmd; struct net *net; + struct netns_ipvs *ipvs; net = skb_sknet(skb); + ipvs = net_ipvs(net); cmd = info->genlhdr->cmd; if (cmd == IPVS_CMD_GET_SERVICE) @@ -3530,9 +3490,21 @@ int __net_init __ip_vs_control_init(struct net *net) { int idx; struct netns_ipvs *ipvs = net_ipvs(net); + struct ctl_table *tbl; if (!net_eq(net, &init_net)) /* netns not enabled yet */ return -EPERM; + + atomic_set(&ipvs->dropentry, 0); + spin_lock_init(&ipvs->dropentry_lock); + spin_lock_init(&ipvs->droppacket_lock); + spin_lock_init(&ipvs->securetcp_lock); + ipvs->rs_lock = __RW_LOCK_UNLOCKED(ipvs->rs_lock); + + /* Initialize rs_table */ + for (idx = 0; idx < IP_VS_RTAB_SIZE; idx++) + INIT_LIST_HEAD(&ipvs->rs_table[idx]); + /* procfs stats */ ipvs->tot_stats = kzalloc(sizeof(struct ip_vs_stats), GFP_KERNEL); if (ipvs->tot_stats == NULL) { @@ -3553,14 +3525,51 @@ int __net_init __ip_vs_control_init(struct net *net) proc_net_fops_create(net, "ip_vs_stats", 0, &ip_vs_stats_fops); proc_net_fops_create(net, "ip_vs_stats_percpu", 0, &ip_vs_stats_percpu_fops); - sysctl_header = register_net_sysctl_table(net, net_vs_ctl_path, + + if (!net_eq(net, &init_net)) { + tbl = kmemdup(vs_vars, sizeof(vs_vars), GFP_KERNEL); + if (tbl == NULL) + goto err_dup; + } else + tbl = vs_vars; + /* Initialize sysctl defaults */ + idx = 0; + ipvs->sysctl_amemthresh = 1024; + tbl[idx++].data = &ipvs->sysctl_amemthresh; + ipvs->sysctl_am_droprate = 10; + tbl[idx++].data = &ipvs->sysctl_am_droprate; + tbl[idx++].data = &ipvs->sysctl_drop_entry; + tbl[idx++].data = &ipvs->sysctl_drop_packet; +#ifdef CONFIG_IP_VS_NFCT + tbl[idx++].data = &ipvs->sysctl_conntrack; +#endif + tbl[idx++].data = &ipvs->sysctl_secure_tcp; + ipvs->sysctl_snat_reroute = 1; + tbl[idx++].data = &ipvs->sysctl_snat_reroute; + ipvs->sysctl_sync_ver = 1; + tbl[idx++].data = &ipvs->sysctl_sync_ver; + tbl[idx++].data = &ipvs->sysctl_cache_bypass; + tbl[idx++].data = &ipvs->sysctl_expire_nodest_conn; + tbl[idx++].data = &ipvs->sysctl_expire_quiescent_template; + ipvs->sysctl_sync_threshold[0] = 3; + ipvs->sysctl_sync_threshold[1] = 50; + tbl[idx].data = &ipvs->sysctl_sync_threshold; + tbl[idx++].maxlen = sizeof(ipvs->sysctl_sync_threshold); + tbl[idx++].data = &ipvs->sysctl_nat_icmp_send; + + + ipvs->sysctl_hdr = register_net_sysctl_table(net, net_vs_ctl_path, vs_vars); - if (sysctl_header == NULL) + if (ipvs->sysctl_hdr == NULL) goto err_reg; ip_vs_new_estimator(net, ipvs->tot_stats); + ipvs->sysctl_tbl = tbl; return 0; err_reg: + if (!net_eq(net, &init_net)) + kfree(tbl); +err_dup: free_percpu(ipvs->cpustats); err_alloc: kfree(ipvs->tot_stats); @@ -3575,7 +3584,7 @@ static void __net_exit __ip_vs_control_cleanup(struct net *net) return; ip_vs_kill_estimator(net, ipvs->tot_stats); - unregister_net_sysctl_table(sysctl_header); + unregister_net_sysctl_table(ipvs->sysctl_hdr); proc_net_remove(net, "ip_vs_stats_percpu"); proc_net_remove(net, "ip_vs_stats"); proc_net_remove(net, "ip_vs"); diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 550365a690c7..fb2d04ac5d4e 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -34,7 +34,7 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, &iph.daddr, sh->dest))) { int ignored; - if (ip_vs_todrop()) { + if (ip_vs_todrop(net_ipvs(net))) { /* * It seems that we are very loaded. * We have to drop this packet :( diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index d8b3f9f15826..c0cc341b840d 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -54,7 +54,7 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, &iph.daddr, th->dest))) { int ignored; - if (ip_vs_todrop()) { + if (ip_vs_todrop(net_ipvs(net))) { /* * It seems that we are very loaded. * We have to drop this packet :( diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index 581157bbded5..f1282cbe6fe3 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -50,7 +50,7 @@ udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, if (svc) { int ignored; - if (ip_vs_todrop()) { + if (ip_vs_todrop(net_ipvs(net))) { /* * It seems that we are very loaded. * We have to drop this packet :( diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index f85e47daecc3..b1780562c42b 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -394,7 +394,7 @@ void ip_vs_sync_switch_mode(struct net *net, int mode) if (!ipvs->sync_state & IP_VS_STATE_MASTER) return; - if (mode == sysctl_ip_vs_sync_ver || !ipvs->sync_buff) + if (mode == ipvs->sysctl_sync_ver || !ipvs->sync_buff) return; spin_lock_bh(&ipvs->sync_buff_lock); @@ -521,7 +521,7 @@ void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp) unsigned int len, pe_name_len, pad; /* Handle old version of the protocol */ - if (sysctl_ip_vs_sync_ver == 0) { + if (ipvs->sysctl_sync_ver == 0) { ip_vs_sync_conn_v0(net, cp); return; } @@ -650,7 +650,7 @@ control: if (cp->flags & IP_VS_CONN_F_TEMPLATE) { int pkts = atomic_add_return(1, &cp->in_pkts); - if (pkts % sysctl_ip_vs_sync_threshold[1] != 1) + if (pkts % ipvs->sysctl_sync_threshold[1] != 1) return; } goto sloop; @@ -724,6 +724,7 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param, { struct ip_vs_dest *dest; struct ip_vs_conn *cp; + struct netns_ipvs *ipvs = net_ipvs(net); if (!(flags & IP_VS_CONN_F_TEMPLATE)) cp = ip_vs_conn_in_get(param); @@ -794,7 +795,7 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param, if (opt) memcpy(&cp->in_seq, opt, sizeof(*opt)); - atomic_set(&cp->in_pkts, sysctl_ip_vs_sync_threshold[0]); + atomic_set(&cp->in_pkts, ipvs->sysctl_sync_threshold[0]); cp->state = state; cp->old_state = cp->state; /* -- cgit v1.2.3 From f6340ee0c6b9498ec918a7bb2f44e20abb8b2833 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:44:59 +0100 Subject: IPVS: netns, defense work timer. This patch makes defense work timer per name-space, A net ptr had to be added to the ipvs struct, since it's needed by defense_work_handler. [ horms@verge.net.au: Use cancel_delayed_work_sync() instead of cancel_rearming_delayed_work(). Found during merge conflict resoliution ] Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 2 +- include/net/netns/ip_vs.h | 3 +++ net/netfilter/ipvs/ip_vs_conn.c | 5 +++-- net/netfilter/ipvs/ip_vs_core.c | 1 + net/netfilter/ipvs/ip_vs_ctl.c | 20 +++++++++----------- 5 files changed, 17 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index af9acf44e40a..fbe660f95873 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -877,7 +877,7 @@ extern const char * ip_vs_state_name(__u16 proto, int state); extern void ip_vs_tcp_conn_listen(struct net *net, struct ip_vs_conn *cp); extern int ip_vs_check_template(struct ip_vs_conn *ct); -extern void ip_vs_random_dropentry(void); +extern void ip_vs_random_dropentry(struct net *net); extern int ip_vs_conn_init(void); extern void ip_vs_conn_cleanup(void); diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h index c4b1abf258e4..41332619142c 100644 --- a/include/net/netns/ip_vs.h +++ b/include/net/netns/ip_vs.h @@ -71,6 +71,7 @@ struct netns_ipvs { int num_services; /* no of virtual services */ /* 1/rate drop and drop-entry variables */ + struct delayed_work defense_work; /* Work handler */ int drop_rate; int drop_counter; atomic_t dropentry; @@ -129,6 +130,8 @@ struct netns_ipvs { /* multicast interface name */ char master_mcast_ifn[IP_VS_IFNAME_MAXLEN]; char backup_mcast_ifn[IP_VS_IFNAME_MAXLEN]; + /* net name space ptr */ + struct net *net; /* Needed by timer routines */ }; #endif /* IP_VS_H_ */ diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 5ba205a4d79c..28bdaf7c02f4 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1138,7 +1138,7 @@ static inline int todrop_entry(struct ip_vs_conn *cp) } /* Called from keventd and must protect itself from softirqs */ -void ip_vs_random_dropentry(void) +void ip_vs_random_dropentry(struct net *net) { int idx; struct ip_vs_conn *cp; @@ -1158,7 +1158,8 @@ void ip_vs_random_dropentry(void) if (cp->flags & IP_VS_CONN_F_TEMPLATE) /* connection template */ continue; - + if (!ip_vs_conn_net_eq(cp, net)) + continue; if (cp->protocol == IPPROTO_TCP) { switch(cp->state) { case IP_VS_TCP_S_SYN_RECV: diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index a7c59a722af3..bdda346a4f30 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1884,6 +1884,7 @@ static int __net_init __ip_vs_init(struct net *net) pr_err("%s(): no memory.\n", __func__); return -ENOMEM; } + ipvs->net = net; /* Counters used for creating unique names */ ipvs->gen = atomic_read(&ipvs_netns_cnt); atomic_inc(&ipvs_netns_cnt); diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 183ac18bded5..6a963d44df48 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -217,18 +217,16 @@ static void update_defense_level(struct netns_ipvs *ipvs) * Timer for checking the defense */ #define DEFENSE_TIMER_PERIOD 1*HZ -static void defense_work_handler(struct work_struct *work); -static DECLARE_DELAYED_WORK(defense_work, defense_work_handler); static void defense_work_handler(struct work_struct *work) { - struct netns_ipvs *ipvs = net_ipvs(&init_net); + struct netns_ipvs *ipvs = + container_of(work, struct netns_ipvs, defense_work.work); update_defense_level(ipvs); if (atomic_read(&ipvs->dropentry)) - ip_vs_random_dropentry(); - - schedule_delayed_work(&defense_work, DEFENSE_TIMER_PERIOD); + ip_vs_random_dropentry(ipvs->net); + schedule_delayed_work(&ipvs->defense_work, DEFENSE_TIMER_PERIOD); } int @@ -3564,6 +3562,9 @@ int __net_init __ip_vs_control_init(struct net *net) goto err_reg; ip_vs_new_estimator(net, ipvs->tot_stats); ipvs->sysctl_tbl = tbl; + /* Schedule defense work */ + INIT_DELAYED_WORK(&ipvs->defense_work, defense_work_handler); + schedule_delayed_work(&ipvs->defense_work, DEFENSE_TIMER_PERIOD); return 0; err_reg: @@ -3588,6 +3589,8 @@ static void __net_exit __ip_vs_control_cleanup(struct net *net) proc_net_remove(net, "ip_vs_stats_percpu"); proc_net_remove(net, "ip_vs_stats"); proc_net_remove(net, "ip_vs"); + cancel_delayed_work_sync(&ipvs->defense_work); + cancel_work_sync(&ipvs->defense_work.work); free_percpu(ipvs->cpustats); kfree(ipvs->tot_stats); } @@ -3631,9 +3634,6 @@ int __init ip_vs_control_init(void) goto err_net; } - /* Hook the defense timer */ - schedule_delayed_work(&defense_work, DEFENSE_TIMER_PERIOD); - LeaveFunction(2); return 0; @@ -3648,8 +3648,6 @@ void ip_vs_control_cleanup(void) { EnterFunction(2); ip_vs_trash_cleanup(); - cancel_delayed_work_sync(&defense_work); - cancel_work_sync(&defense_work.work); unregister_pernet_subsys(&ipvs_control_ops); ip_vs_genl_unregister(); nf_unregister_sockopt(&ip_vs_sockopts); -- cgit v1.2.3 From f2431e6e9255461eb1476340a89ad32ad4b38b03 Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:45:00 +0100 Subject: IPVS: netns, trash handling trash list per namspace, and reordering of some params in dst struct. [ horms@verge.net.au: Use cancel_delayed_work_sync() instead of cancel_rearming_delayed_work(). Found during merge conflict resoliution ] Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 4 ++-- include/net/netns/ip_vs.h | 3 +++ net/netfilter/ipvs/ip_vs_ctl.c | 23 +++++++++++------------ 3 files changed, 16 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index fbe660f95873..b23bea62f708 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -662,8 +662,8 @@ struct ip_vs_dest { struct list_head d_list; /* for table with all the dests */ u16 af; /* address family */ - union nf_inet_addr addr; /* IP address of the server */ __be16 port; /* port number of the server */ + union nf_inet_addr addr; /* IP address of the server */ volatile unsigned flags; /* dest status flags */ atomic_t conn_flags; /* flags to copy to conn */ atomic_t weight; /* server weight */ @@ -690,8 +690,8 @@ struct ip_vs_dest { /* for virtual service */ struct ip_vs_service *svc; /* service it belongs to */ __u16 protocol; /* which protocol (TCP/UDP) */ - union nf_inet_addr vaddr; /* virtual IP address */ __be16 vport; /* virtual port number */ + union nf_inet_addr vaddr; /* virtual IP address */ __u32 vfwmark; /* firewall mark of service */ }; diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h index 41332619142c..67ca1cf55af8 100644 --- a/include/net/netns/ip_vs.h +++ b/include/net/netns/ip_vs.h @@ -82,6 +82,9 @@ struct netns_ipvs { rwlock_t rs_lock; /* real services table */ /* semaphore for IPVS sockopts. And, [gs]etsockopt may sleep. */ struct lock_class_key ctl_key; /* ctl_mutex debuging */ + /* Trash for destinations */ + struct list_head dest_trash; + /* sys-ctl struct */ struct ctl_table_header *sysctl_hdr; struct ctl_table *sysctl_tbl; diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 6a963d44df48..442edf4be644 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -254,11 +254,6 @@ static struct list_head ip_vs_svc_table[IP_VS_SVC_TAB_SIZE]; /* the service table hashed by fwmark */ static struct list_head ip_vs_svc_fwm_table[IP_VS_SVC_TAB_SIZE]; -/* - * Trash for destinations - */ -static LIST_HEAD(ip_vs_dest_trash); - /* * FTP & NULL virtual service counters */ @@ -650,11 +645,12 @@ ip_vs_trash_get_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr, __be16 dport) { struct ip_vs_dest *dest, *nxt; + struct netns_ipvs *ipvs = net_ipvs(svc->net); /* * Find the destination in trash */ - list_for_each_entry_safe(dest, nxt, &ip_vs_dest_trash, n_list) { + list_for_each_entry_safe(dest, nxt, &ipvs->dest_trash, n_list) { IP_VS_DBG_BUF(3, "Destination %u/%s:%u still in trash, " "dest->refcnt=%d\n", dest->vfwmark, @@ -703,11 +699,12 @@ ip_vs_trash_get_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr, * are expired, and the refcnt of each destination in the trash must * be 1, so we simply release them here. */ -static void ip_vs_trash_cleanup(void) +static void ip_vs_trash_cleanup(struct net *net) { struct ip_vs_dest *dest, *nxt; + struct netns_ipvs *ipvs = net_ipvs(net); - list_for_each_entry_safe(dest, nxt, &ip_vs_dest_trash, n_list) { + list_for_each_entry_safe(dest, nxt, &ipvs->dest_trash, n_list) { list_del(&dest->n_list); ip_vs_dst_reset(dest); __ip_vs_unbind_svc(dest); @@ -1021,7 +1018,7 @@ static void __ip_vs_del_dest(struct net *net, struct ip_vs_dest *dest) IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port), atomic_read(&dest->refcnt)); - list_add(&dest->n_list, &ip_vs_dest_trash); + list_add(&dest->n_list, &ipvs->dest_trash); atomic_inc(&dest->refcnt); } } @@ -3503,6 +3500,8 @@ int __net_init __ip_vs_control_init(struct net *net) for (idx = 0; idx < IP_VS_RTAB_SIZE; idx++) INIT_LIST_HEAD(&ipvs->rs_table[idx]); + INIT_LIST_HEAD(&ipvs->dest_trash); + /* procfs stats */ ipvs->tot_stats = kzalloc(sizeof(struct ip_vs_stats), GFP_KERNEL); if (ipvs->tot_stats == NULL) { @@ -3584,13 +3583,14 @@ static void __net_exit __ip_vs_control_cleanup(struct net *net) if (!net_eq(net, &init_net)) /* netns not enabled yet */ return; + ip_vs_trash_cleanup(net); ip_vs_kill_estimator(net, ipvs->tot_stats); + cancel_delayed_work_sync(&ipvs->defense_work); + cancel_work_sync(&ipvs->defense_work.work); unregister_net_sysctl_table(ipvs->sysctl_hdr); proc_net_remove(net, "ip_vs_stats_percpu"); proc_net_remove(net, "ip_vs_stats"); proc_net_remove(net, "ip_vs"); - cancel_delayed_work_sync(&ipvs->defense_work); - cancel_work_sync(&ipvs->defense_work.work); free_percpu(ipvs->cpustats); kfree(ipvs->tot_stats); } @@ -3647,7 +3647,6 @@ err: void ip_vs_control_cleanup(void) { EnterFunction(2); - ip_vs_trash_cleanup(); unregister_pernet_subsys(&ipvs_control_ops); ip_vs_genl_unregister(); nf_unregister_sockopt(&ip_vs_sockopts); -- cgit v1.2.3 From 763f8d0ed4f1ce38b35cc0e05482b7799b82789b Mon Sep 17 00:00:00 2001 From: Hans Schillstrom Date: Mon, 3 Jan 2011 14:45:01 +0100 Subject: IPVS: netns, svc counters moved in ip_vs_ctl,c Last two global vars to be moved, ip_vs_ftpsvc_counter and ip_vs_nullsvc_counter. [horms@verge.net.au: removed whitespace-change-only hunk] Signed-off-by: Hans Schillstrom Acked-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/netns/ip_vs.h | 3 +++ net/netfilter/ipvs/ip_vs_ctl.c | 21 +++++++++------------ 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/netns/ip_vs.h b/include/net/netns/ip_vs.h index 67ca1cf55af8..259ebac904bf 100644 --- a/include/net/netns/ip_vs.h +++ b/include/net/netns/ip_vs.h @@ -84,6 +84,9 @@ struct netns_ipvs { struct lock_class_key ctl_key; /* ctl_mutex debuging */ /* Trash for destinations */ struct list_head dest_trash; + /* Service counters */ + atomic_t ftpsvc_counter; + atomic_t nullsvc_counter; /* sys-ctl struct */ struct ctl_table_header *sysctl_hdr; diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 442edf4be644..65f5de405ad2 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -254,12 +254,6 @@ static struct list_head ip_vs_svc_table[IP_VS_SVC_TAB_SIZE]; /* the service table hashed by fwmark */ static struct list_head ip_vs_svc_fwm_table[IP_VS_SVC_TAB_SIZE]; -/* - * FTP & NULL virtual service counters - */ -static atomic_t ip_vs_ftpsvc_counter = ATOMIC_INIT(0); -static atomic_t ip_vs_nullsvc_counter = ATOMIC_INIT(0); - /* * Returns hash value for virtual service @@ -409,6 +403,7 @@ ip_vs_service_get(struct net *net, int af, __u32 fwmark, __u16 protocol, const union nf_inet_addr *vaddr, __be16 vport) { struct ip_vs_service *svc; + struct netns_ipvs *ipvs = net_ipvs(net); read_lock(&__ip_vs_svc_lock); @@ -427,7 +422,7 @@ ip_vs_service_get(struct net *net, int af, __u32 fwmark, __u16 protocol, if (svc == NULL && protocol == IPPROTO_TCP - && atomic_read(&ip_vs_ftpsvc_counter) + && atomic_read(&ipvs->ftpsvc_counter) && (vport == FTPDATA || ntohs(vport) >= PROT_SOCK)) { /* * Check if ftp service entry exists, the packet @@ -437,7 +432,7 @@ ip_vs_service_get(struct net *net, int af, __u32 fwmark, __u16 protocol, } if (svc == NULL - && atomic_read(&ip_vs_nullsvc_counter)) { + && atomic_read(&ipvs->nullsvc_counter)) { /* * Check if the catch-all port (port zero) exists */ @@ -1173,9 +1168,9 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u, /* Update the virtual service counters */ if (svc->port == FTPPORT) - atomic_inc(&ip_vs_ftpsvc_counter); + atomic_inc(&ipvs->ftpsvc_counter); else if (svc->port == 0) - atomic_inc(&ip_vs_nullsvc_counter); + atomic_inc(&ipvs->nullsvc_counter); ip_vs_new_estimator(net, &svc->stats); @@ -1359,9 +1354,9 @@ static void __ip_vs_del_service(struct ip_vs_service *svc) * Update the virtual service counters */ if (svc->port == FTPPORT) - atomic_dec(&ip_vs_ftpsvc_counter); + atomic_dec(&ipvs->ftpsvc_counter); else if (svc->port == 0) - atomic_dec(&ip_vs_nullsvc_counter); + atomic_dec(&ipvs->nullsvc_counter); /* * Free the service if nobody refers to it @@ -3501,6 +3496,8 @@ int __net_init __ip_vs_control_init(struct net *net) INIT_LIST_HEAD(&ipvs->rs_table[idx]); INIT_LIST_HEAD(&ipvs->dest_trash); + atomic_set(&ipvs->ftpsvc_counter, 0); + atomic_set(&ipvs->nullsvc_counter, 0); /* procfs stats */ ipvs->tot_stats = kzalloc(sizeof(struct ip_vs_stats), GFP_KERNEL); -- cgit v1.2.3 From 5df15196a2bbf16ca4c6a797ec00ff36d0d5c179 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sun, 26 Dec 2010 10:22:22 +0100 Subject: netfilter: xt_comment: drop unneeded unsigned qualifier Since a string is stored, and not something like a MAC address that would rely on (un)signedness, drop the qualifier. Signed-off-by: Jan Engelhardt Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/xt_comment.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/netfilter/xt_comment.h b/include/linux/netfilter/xt_comment.h index eacfedc6b5d0..0ea5e79f5bd7 100644 --- a/include/linux/netfilter/xt_comment.h +++ b/include/linux/netfilter/xt_comment.h @@ -4,7 +4,7 @@ #define XT_MAX_COMMENT_LEN 256 struct xt_comment_info { - unsigned char comment[XT_MAX_COMMENT_LEN]; + char comment[XT_MAX_COMMENT_LEN]; }; #endif /* XT_COMMENT_H */ -- cgit v1.2.3 From b017900aac4a158b9bf7ffdcb8a369a91115b3e4 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 15 Dec 2010 09:46:26 +0100 Subject: netfilter: xt_conntrack: support matching on port ranges Add a new revision 3 that contains port ranges for all of origsrc, origdst, replsrc and repldst. The high ports are appended to the original v2 data structure to allow sharing most of the code with v1 and v2. Use of the revision specific port matching function is made dependant on par->match->revision. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/xt_conntrack.h | 15 +++++++ net/netfilter/xt_conntrack.c | 75 +++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/xt_conntrack.h b/include/linux/netfilter/xt_conntrack.h index 54f47a2f6152..74b904d8f99c 100644 --- a/include/linux/netfilter/xt_conntrack.h +++ b/include/linux/netfilter/xt_conntrack.h @@ -58,4 +58,19 @@ struct xt_conntrack_mtinfo2 { __u16 state_mask, status_mask; }; +struct xt_conntrack_mtinfo3 { + union nf_inet_addr origsrc_addr, origsrc_mask; + union nf_inet_addr origdst_addr, origdst_mask; + union nf_inet_addr replsrc_addr, replsrc_mask; + union nf_inet_addr repldst_addr, repldst_mask; + __u32 expires_min, expires_max; + __u16 l4proto; + __u16 origsrc_port, origdst_port; + __u16 replsrc_port, repldst_port; + __u16 match_flags, invert_flags; + __u16 state_mask, status_mask; + __u16 origsrc_port_high, origdst_port_high; + __u16 replsrc_port_high, repldst_port_high; +}; + #endif /*_XT_CONNTRACK_H*/ diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index e536710ad916..4ef1b63ad73f 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -112,6 +112,54 @@ ct_proto_port_check(const struct xt_conntrack_mtinfo2 *info, return true; } +static inline bool +port_match(u16 min, u16 max, u16 port, bool invert) +{ + return (port >= min && port <= max) ^ invert; +} + +static inline bool +ct_proto_port_check_v3(const struct xt_conntrack_mtinfo3 *info, + const struct nf_conn *ct) +{ + const struct nf_conntrack_tuple *tuple; + + tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + if ((info->match_flags & XT_CONNTRACK_PROTO) && + (nf_ct_protonum(ct) == info->l4proto) ^ + !(info->invert_flags & XT_CONNTRACK_PROTO)) + return false; + + /* Shortcut to match all recognized protocols by using ->src.all. */ + if ((info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) && + !port_match(info->origsrc_port, info->origsrc_port_high, + ntohs(tuple->src.u.all), + info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)) + return false; + + if ((info->match_flags & XT_CONNTRACK_ORIGDST_PORT) && + !port_match(info->origdst_port, info->origdst_port_high, + ntohs(tuple->dst.u.all), + info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)) + return false; + + tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; + + if ((info->match_flags & XT_CONNTRACK_REPLSRC_PORT) && + !port_match(info->replsrc_port, info->replsrc_port_high, + ntohs(tuple->src.u.all), + info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)) + return false; + + if ((info->match_flags & XT_CONNTRACK_REPLDST_PORT) && + !port_match(info->repldst_port, info->repldst_port_high, + ntohs(tuple->dst.u.all), + info->invert_flags & XT_CONNTRACK_REPLDST_PORT)) + return false; + + return true; +} + static bool conntrack_mt(const struct sk_buff *skb, struct xt_action_param *par, u16 state_mask, u16 status_mask) @@ -170,8 +218,13 @@ conntrack_mt(const struct sk_buff *skb, struct xt_action_param *par, !(info->invert_flags & XT_CONNTRACK_REPLDST)) return false; - if (!ct_proto_port_check(info, ct)) - return false; + if (par->match->revision != 3) { + if (!ct_proto_port_check(info, ct)) + return false; + } else { + if (!ct_proto_port_check_v3(par->matchinfo, ct)) + return false; + } if ((info->match_flags & XT_CONNTRACK_STATUS) && (!!(status_mask & ct->status) ^ @@ -207,6 +260,14 @@ conntrack_mt_v2(const struct sk_buff *skb, struct xt_action_param *par) return conntrack_mt(skb, par, info->state_mask, info->status_mask); } +static bool +conntrack_mt_v3(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_conntrack_mtinfo3 *info = par->matchinfo; + + return conntrack_mt(skb, par, info->state_mask, info->status_mask); +} + static int conntrack_mt_check(const struct xt_mtchk_param *par) { int ret; @@ -244,6 +305,16 @@ static struct xt_match conntrack_mt_reg[] __read_mostly = { .destroy = conntrack_mt_destroy, .me = THIS_MODULE, }, + { + .name = "conntrack", + .revision = 3, + .family = NFPROTO_UNSPEC, + .matchsize = sizeof(struct xt_conntrack_mtinfo3), + .match = conntrack_mt_v3, + .checkentry = conntrack_mt_check, + .destroy = conntrack_mt_destroy, + .me = THIS_MODULE, + }, }; static int __init conntrack_mt_init(void) -- cgit v1.2.3 From 255d0dc34068a976550ce555e153c0bfcfec7cc6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 18 Dec 2010 18:35:15 +0100 Subject: netfilter: x_table: speedup compat operations One iptables invocation with 135000 rules takes 35 seconds of cpu time on a recent server, using a 32bit distro and a 64bit kernel. We eventually trigger NMI/RCU watchdog. INFO: rcu_sched_state detected stall on CPU 3 (t=6000 jiffies) COMPAT mode has quadratic behavior and consume 16 bytes of memory per rule. Switch the xt_compat algos to use an array instead of list, and use a binary search to locate an offset in the sorted array. This halves memory need (8 bytes per rule), and removes quadratic behavior [ O(N*N) -> O(N*log2(N)) ] Time of iptables goes from 35 s to 150 ms. Signed-off-by: Eric Dumazet Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/x_tables.h | 3 +- net/bridge/netfilter/ebtables.c | 1 + net/ipv4/netfilter/arp_tables.c | 2 + net/ipv4/netfilter/ip_tables.c | 2 + net/ipv6/netfilter/ip6_tables.c | 2 + net/netfilter/x_tables.c | 82 ++++++++++++++++++++++---------------- 6 files changed, 57 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 742bec051440..0f04d985b41c 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -611,8 +611,9 @@ struct _compat_xt_align { extern void xt_compat_lock(u_int8_t af); extern void xt_compat_unlock(u_int8_t af); -extern int xt_compat_add_offset(u_int8_t af, unsigned int offset, short delta); +extern int xt_compat_add_offset(u_int8_t af, unsigned int offset, int delta); extern void xt_compat_flush_offsets(u_int8_t af); +extern void xt_compat_init_offsets(u_int8_t af, unsigned int number); extern int xt_compat_calc_jump(u_int8_t af, unsigned int offset); extern int xt_compat_match_offset(const struct xt_match *match); diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 16df0532d4b9..5f1825df9dca 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1764,6 +1764,7 @@ static int compat_table_info(const struct ebt_table_info *info, newinfo->entries_size = size; + xt_compat_init_offsets(AF_INET, info->nentries); return EBT_ENTRY_ITERATE(entries, size, compat_calc_entry, info, entries, newinfo); } diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 3fac340a28d5..47e5178b998b 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -883,6 +883,7 @@ static int compat_table_info(const struct xt_table_info *info, memcpy(newinfo, info, offsetof(struct xt_table_info, entries)); newinfo->initial_entries = 0; loc_cpu_entry = info->entries[raw_smp_processor_id()]; + xt_compat_init_offsets(NFPROTO_ARP, info->number); xt_entry_foreach(iter, loc_cpu_entry, info->size) { ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo); if (ret != 0) @@ -1350,6 +1351,7 @@ static int translate_compat_table(const char *name, duprintf("translate_compat_table: size %u\n", info->size); j = 0; xt_compat_lock(NFPROTO_ARP); + xt_compat_init_offsets(NFPROTO_ARP, number); /* Walk through entries, checking offsets. */ xt_entry_foreach(iter0, entry0, total_size) { ret = check_compat_entry_size_and_hooks(iter0, info, &size, diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index a846d633b3b6..c5a75d70970f 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -1080,6 +1080,7 @@ static int compat_table_info(const struct xt_table_info *info, memcpy(newinfo, info, offsetof(struct xt_table_info, entries)); newinfo->initial_entries = 0; loc_cpu_entry = info->entries[raw_smp_processor_id()]; + xt_compat_init_offsets(AF_INET, info->number); xt_entry_foreach(iter, loc_cpu_entry, info->size) { ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo); if (ret != 0) @@ -1681,6 +1682,7 @@ translate_compat_table(struct net *net, duprintf("translate_compat_table: size %u\n", info->size); j = 0; xt_compat_lock(AF_INET); + xt_compat_init_offsets(AF_INET, number); /* Walk through entries, checking offsets. */ xt_entry_foreach(iter0, entry0, total_size) { ret = check_compat_entry_size_and_hooks(iter0, info, &size, diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 455582384ece..0c9973a657fb 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -1093,6 +1093,7 @@ static int compat_table_info(const struct xt_table_info *info, memcpy(newinfo, info, offsetof(struct xt_table_info, entries)); newinfo->initial_entries = 0; loc_cpu_entry = info->entries[raw_smp_processor_id()]; + xt_compat_init_offsets(AF_INET6, info->number); xt_entry_foreach(iter, loc_cpu_entry, info->size) { ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo); if (ret != 0) @@ -1696,6 +1697,7 @@ translate_compat_table(struct net *net, duprintf("translate_compat_table: size %u\n", info->size); j = 0; xt_compat_lock(AF_INET6); + xt_compat_init_offsets(AF_INET6, number); /* Walk through entries, checking offsets. */ xt_entry_foreach(iter0, entry0, total_size) { ret = check_compat_entry_size_and_hooks(iter0, info, &size, diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 80463507420e..ee5de3af510a 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -38,9 +38,8 @@ MODULE_DESCRIPTION("{ip,ip6,arp,eb}_tables backend module"); #define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1)) struct compat_delta { - struct compat_delta *next; - unsigned int offset; - int delta; + unsigned int offset; /* offset in kernel */ + int delta; /* delta in 32bit user land */ }; struct xt_af { @@ -49,7 +48,9 @@ struct xt_af { struct list_head target; #ifdef CONFIG_COMPAT struct mutex compat_mutex; - struct compat_delta *compat_offsets; + struct compat_delta *compat_tab; + unsigned int number; /* number of slots in compat_tab[] */ + unsigned int cur; /* number of used slots in compat_tab[] */ #endif }; @@ -414,54 +415,67 @@ int xt_check_match(struct xt_mtchk_param *par, EXPORT_SYMBOL_GPL(xt_check_match); #ifdef CONFIG_COMPAT -int xt_compat_add_offset(u_int8_t af, unsigned int offset, short delta) +int xt_compat_add_offset(u_int8_t af, unsigned int offset, int delta) { - struct compat_delta *tmp; + struct xt_af *xp = &xt[af]; - tmp = kmalloc(sizeof(struct compat_delta), GFP_KERNEL); - if (!tmp) - return -ENOMEM; + if (!xp->compat_tab) { + if (!xp->number) + return -EINVAL; + xp->compat_tab = vmalloc(sizeof(struct compat_delta) * xp->number); + if (!xp->compat_tab) + return -ENOMEM; + xp->cur = 0; + } - tmp->offset = offset; - tmp->delta = delta; + if (xp->cur >= xp->number) + return -EINVAL; - if (xt[af].compat_offsets) { - tmp->next = xt[af].compat_offsets->next; - xt[af].compat_offsets->next = tmp; - } else { - xt[af].compat_offsets = tmp; - tmp->next = NULL; - } + if (xp->cur) + delta += xp->compat_tab[xp->cur - 1].delta; + xp->compat_tab[xp->cur].offset = offset; + xp->compat_tab[xp->cur].delta = delta; + xp->cur++; return 0; } EXPORT_SYMBOL_GPL(xt_compat_add_offset); void xt_compat_flush_offsets(u_int8_t af) { - struct compat_delta *tmp, *next; - - if (xt[af].compat_offsets) { - for (tmp = xt[af].compat_offsets; tmp; tmp = next) { - next = tmp->next; - kfree(tmp); - } - xt[af].compat_offsets = NULL; + if (xt[af].compat_tab) { + vfree(xt[af].compat_tab); + xt[af].compat_tab = NULL; + xt[af].number = 0; } } EXPORT_SYMBOL_GPL(xt_compat_flush_offsets); int xt_compat_calc_jump(u_int8_t af, unsigned int offset) { - struct compat_delta *tmp; - int delta; - - for (tmp = xt[af].compat_offsets, delta = 0; tmp; tmp = tmp->next) - if (tmp->offset < offset) - delta += tmp->delta; - return delta; + struct compat_delta *tmp = xt[af].compat_tab; + int mid, left = 0, right = xt[af].cur - 1; + + while (left <= right) { + mid = (left + right) >> 1; + if (offset > tmp[mid].offset) + left = mid + 1; + else if (offset < tmp[mid].offset) + right = mid - 1; + else + return mid ? tmp[mid - 1].delta : 0; + } + WARN_ON_ONCE(1); + return 0; } EXPORT_SYMBOL_GPL(xt_compat_calc_jump); +void xt_compat_init_offsets(u_int8_t af, unsigned int number) +{ + xt[af].number = number; + xt[af].cur = 0; +} +EXPORT_SYMBOL(xt_compat_init_offsets); + int xt_compat_match_offset(const struct xt_match *match) { u_int16_t csize = match->compatsize ? : match->matchsize; @@ -1337,7 +1351,7 @@ static int __init xt_init(void) mutex_init(&xt[i].mutex); #ifdef CONFIG_COMPAT mutex_init(&xt[i].compat_mutex); - xt[i].compat_offsets = NULL; + xt[i].compat_tab = NULL; #endif INIT_LIST_HEAD(&xt[i].target); INIT_LIST_HEAD(&xt[i].match); -- cgit v1.2.3 From 6faee60a4e82075853a437831768cc9e2e563e4e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 20 Dec 2010 15:57:47 +0100 Subject: netfilter: ebt_ip6: allow matching on ipv6-icmp types/codes To avoid adding a new match revision icmp type/code are stored in the sport/dport area. Signed-off-by: Florian Westphal Reviewed-by: Holger Eitzenberger Reviewed-by: Bart De Schuymer Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter_bridge/ebt_ip6.h | 15 ++++++++--- net/bridge/netfilter/ebt_ip6.c | 46 +++++++++++++++++++++++--------- 2 files changed, 46 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter_bridge/ebt_ip6.h b/include/linux/netfilter_bridge/ebt_ip6.h index e5de98701519..22af18a3c16b 100644 --- a/include/linux/netfilter_bridge/ebt_ip6.h +++ b/include/linux/netfilter_bridge/ebt_ip6.h @@ -18,8 +18,11 @@ #define EBT_IP6_PROTO 0x08 #define EBT_IP6_SPORT 0x10 #define EBT_IP6_DPORT 0x20 +#define EBT_IP6_ICMP6 0x40 + #define EBT_IP6_MASK (EBT_IP6_SOURCE | EBT_IP6_DEST | EBT_IP6_TCLASS |\ - EBT_IP6_PROTO | EBT_IP6_SPORT | EBT_IP6_DPORT) + EBT_IP6_PROTO | EBT_IP6_SPORT | EBT_IP6_DPORT | \ + EBT_IP6_ICMP6) #define EBT_IP6_MATCH "ip6" /* the same values are used for the invflags */ @@ -32,8 +35,14 @@ struct ebt_ip6_info { uint8_t protocol; uint8_t bitmask; uint8_t invflags; - uint16_t sport[2]; - uint16_t dport[2]; + union { + uint16_t sport[2]; + uint8_t icmpv6_type[2]; + }; + union { + uint16_t dport[2]; + uint8_t icmpv6_code[2]; + }; }; #endif diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c index 50a46afc2bcc..2ed0056a39a8 100644 --- a/net/bridge/netfilter/ebt_ip6.c +++ b/net/bridge/netfilter/ebt_ip6.c @@ -22,9 +22,15 @@ #include #include -struct tcpudphdr { - __be16 src; - __be16 dst; +union pkthdr { + struct { + __be16 src; + __be16 dst; + } tcpudphdr; + struct { + u8 type; + u8 code; + } icmphdr; }; static bool @@ -33,8 +39,8 @@ ebt_ip6_mt(const struct sk_buff *skb, struct xt_action_param *par) const struct ebt_ip6_info *info = par->matchinfo; const struct ipv6hdr *ih6; struct ipv6hdr _ip6h; - const struct tcpudphdr *pptr; - struct tcpudphdr _ports; + const union pkthdr *pptr; + union pkthdr _pkthdr; ih6 = skb_header_pointer(skb, 0, sizeof(_ip6h), &_ip6h); if (ih6 == NULL) @@ -56,26 +62,34 @@ ebt_ip6_mt(const struct sk_buff *skb, struct xt_action_param *par) return false; if (FWINV(info->protocol != nexthdr, EBT_IP6_PROTO)) return false; - if (!(info->bitmask & EBT_IP6_DPORT) && - !(info->bitmask & EBT_IP6_SPORT)) + if (!(info->bitmask & ( EBT_IP6_DPORT | + EBT_IP6_SPORT | EBT_IP6_ICMP6))) return true; - pptr = skb_header_pointer(skb, offset_ph, sizeof(_ports), - &_ports); + + /* min icmpv6 headersize is 4, so sizeof(_pkthdr) is ok. */ + pptr = skb_header_pointer(skb, offset_ph, sizeof(_pkthdr), + &_pkthdr); if (pptr == NULL) return false; if (info->bitmask & EBT_IP6_DPORT) { - u32 dst = ntohs(pptr->dst); + u16 dst = ntohs(pptr->tcpudphdr.dst); if (FWINV(dst < info->dport[0] || dst > info->dport[1], EBT_IP6_DPORT)) return false; } if (info->bitmask & EBT_IP6_SPORT) { - u32 src = ntohs(pptr->src); + u16 src = ntohs(pptr->tcpudphdr.src); if (FWINV(src < info->sport[0] || src > info->sport[1], EBT_IP6_SPORT)) return false; } - return true; + if ((info->bitmask & EBT_IP6_ICMP6) && + FWINV(pptr->icmphdr.type < info->icmpv6_type[0] || + pptr->icmphdr.type > info->icmpv6_type[1] || + pptr->icmphdr.code < info->icmpv6_code[0] || + pptr->icmphdr.code > info->icmpv6_code[1], + EBT_IP6_ICMP6)) + return false; } return true; } @@ -103,6 +117,14 @@ static int ebt_ip6_mt_check(const struct xt_mtchk_param *par) return -EINVAL; if (info->bitmask & EBT_IP6_SPORT && info->sport[0] > info->sport[1]) return -EINVAL; + if (info->bitmask & EBT_IP6_ICMP6) { + if ((info->invflags & EBT_IP6_PROTO) || + info->protocol != IPPROTO_ICMPV6) + return -EINVAL; + if (info->icmpv6_type[0] > info->icmpv6_type[1] || + info->icmpv6_code[0] > info->icmpv6_code[1]) + return -EINVAL; + } return 0; } -- cgit v1.2.3 From 066d16c3e8194677a9aaeb06a45e4014387d16f1 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Thu, 13 Jan 2011 12:20:36 +0000 Subject: ASoC: soc-cache: Add support for default readable()/volatile() functions For common scenarios, device drivers can provide a table of all the registers that are at least either readable/writable/volatile. The idea is that if a register lookup fails, all of its read/write/vol members will be zero and will be treated as default. This also reduces the size of the register access array. Signed-off-by: Dimitris Papastamos Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc.h | 22 ++++++++++++++++++++++ sound/soc/soc-cache.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index d609232da82a..b8acf99ac89d 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -276,6 +276,10 @@ int snd_soc_cache_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value); int snd_soc_cache_read(struct snd_soc_codec *codec, unsigned int reg, unsigned int *value); +int snd_soc_default_volatile_register(struct snd_soc_codec *codec, + unsigned int reg); +int snd_soc_default_readable_register(struct snd_soc_codec *codec, + unsigned int reg); /* Utility functions to get clock rates from various things */ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); @@ -366,6 +370,22 @@ int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol, int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +/** + * struct snd_soc_reg_access - Describes whether a given register is + * readable, writable or volatile. + * + * @reg: the register number + * @read: whether this register is readable + * @write: whether this register is writable + * @vol: whether this register is volatile + */ +struct snd_soc_reg_access { + u16 reg; + u16 read; + u16 write; + u16 vol; +}; + /** * struct snd_soc_jack_pin - Describes a pin to update based on jack detection * @@ -515,6 +535,8 @@ struct snd_soc_codec_driver { short reg_cache_step; short reg_word_size; const void *reg_cache_default; + short reg_access_size; + const struct snd_soc_reg_access *reg_access_default; enum snd_soc_compress_type compress_type; /* codec bias level */ diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 1a36b36c5baa..d97a59f6a249 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -1603,3 +1603,52 @@ int snd_soc_cache_sync(struct snd_soc_codec *codec) return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_cache_sync); + +static int snd_soc_get_reg_access_index(struct snd_soc_codec *codec, + unsigned int reg) +{ + const struct snd_soc_codec_driver *codec_drv; + unsigned int min, max, index; + + codec_drv = codec->driver; + min = 0; + max = codec_drv->reg_access_size - 1; + do { + index = (min + max) / 2; + if (codec_drv->reg_access_default[index].reg == reg) + return index; + if (codec_drv->reg_access_default[index].reg < reg) + min = index + 1; + else + max = index; + } while (min <= max); + return -1; +} + +int snd_soc_default_volatile_register(struct snd_soc_codec *codec, + unsigned int reg) +{ + int index; + + if (reg >= codec->driver->reg_cache_size) + return 1; + index = snd_soc_get_reg_access_index(codec, reg); + if (index < 0) + return 0; + return codec->driver->reg_access_default[index].vol; +} +EXPORT_SYMBOL_GPL(snd_soc_default_volatile_register); + +int snd_soc_default_readable_register(struct snd_soc_codec *codec, + unsigned int reg) +{ + int index; + + if (reg >= codec->driver->reg_cache_size) + return 1; + index = snd_soc_get_reg_access_index(codec, reg); + if (index < 0) + return 0; + return codec->driver->reg_access_default[index].read; +} +EXPORT_SYMBOL_GPL(snd_soc_default_readable_register); -- cgit v1.2.3 From d4754ec91c7b094298f0b2ba02327e6887671edc Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Thu, 13 Jan 2011 12:20:37 +0000 Subject: ASoC: Update users of readable_register()/volatile_register() Signed-off-by: Dimitris Papastamos Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc.h | 4 ++-- sound/soc/codecs/cs4270.c | 4 ++-- sound/soc/codecs/max98088.c | 2 +- sound/soc/codecs/wm8523.c | 2 +- sound/soc/codecs/wm8804.c | 2 +- sound/soc/codecs/wm8900.c | 2 +- sound/soc/codecs/wm8903.c | 2 +- sound/soc/codecs/wm8904.c | 2 +- sound/soc/codecs/wm8961.c | 2 +- sound/soc/codecs/wm8962.c | 4 ++-- sound/soc/codecs/wm8993.c | 2 +- sound/soc/codecs/wm8994.c | 10 +++++----- sound/soc/codecs/wm8995.c | 2 +- sound/soc/codecs/wm9081.c | 2 +- sound/soc/codecs/wm9090.c | 4 ++-- sound/soc/soc-core.c | 4 ++-- 16 files changed, 25 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index b8acf99ac89d..97d1832bb9df 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -529,8 +529,8 @@ struct snd_soc_codec_driver { int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); int (*display_register)(struct snd_soc_codec *, char *, size_t, unsigned int); - int (*volatile_register)(unsigned int); - int (*readable_register)(unsigned int); + int (*volatile_register)(struct snd_soc_codec *, unsigned int); + int (*readable_register)(struct snd_soc_codec *, unsigned int); short reg_cache_size; short reg_cache_step; short reg_word_size; diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 8b51245f2318..c0fccadaea9a 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -193,12 +193,12 @@ static struct cs4270_mode_ratios cs4270_mode_ratios[] = { /* The number of MCLK/LRCK ratios supported by the CS4270 */ #define NUM_MCLK_RATIOS ARRAY_SIZE(cs4270_mode_ratios) -static int cs4270_reg_is_readable(unsigned int reg) +static int cs4270_reg_is_readable(struct snd_soc_codec *codec, unsigned int reg) { return (reg >= CS4270_FIRSTREG) && (reg <= CS4270_LASTREG); } -static int cs4270_reg_is_volatile(unsigned int reg) +static int cs4270_reg_is_volatile(struct snd_soc_codec *codec, unsigned int reg) { /* Unreadable registers are considered volatile */ if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG)) diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 37133c40e762..b6ecc7e89673 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -608,7 +608,7 @@ static struct { { 0xFF, 0x00, 1 }, /* FF */ }; -static int max98088_volatile_register(unsigned int reg) +static int max98088_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { return max98088_access[reg].vol; } diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 5eb2f501ce32..83e86f077ee1 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -58,7 +58,7 @@ static const u16 wm8523_reg[WM8523_REGISTER_COUNT] = { 0x0000, /* R8 - ZERO_DETECT */ }; -static int wm8523_volatile_register(unsigned int reg) +static int wm8523_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8523_DEVICE_ID: diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index 6dae1b40c9f7..6785688f8806 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -175,7 +175,7 @@ static int txsrc_put(struct snd_kcontrol *kcontrol, return 0; } -static int wm8804_volatile(unsigned int reg) +static int wm8804_volatile(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8804_RST_DEVID1: diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index cd0959926d12..449ea09a193d 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -180,7 +180,7 @@ static const u16 wm8900_reg_defaults[WM8900_MAXREG] = { /* Remaining registers all zero */ }; -static int wm8900_volatile_register(unsigned int reg) +static int wm8900_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8900_REG_ID: diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 987476a5895f..a2a446cb1807 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -232,7 +232,7 @@ struct wm8903_priv { int mic_delay; }; -static int wm8903_volatile_register(unsigned int reg) +static int wm8903_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8903_SW_RESET_AND_ID: diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 9de44a4c05c0..17a8fe9b39b9 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -596,7 +596,7 @@ static struct { { 0x003F, 0x003F, 0 }, /* R248 - FLL NCO Test 1 */ }; -static int wm8904_volatile_register(unsigned int reg) +static int wm8904_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { return wm8904_access[reg].vol; } diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index 55252e7d02c9..cdee8103d09b 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -291,7 +291,7 @@ struct wm8961_priv { int sysclk; }; -static int wm8961_volatile_register(unsigned int reg) +static int wm8961_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8961_SOFTWARE_RESET: diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index b9cb1fcf8c92..7c02924beddf 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -1938,7 +1938,7 @@ static const struct wm8962_reg_access { [21139] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21139 - VSS_XTS32_0 */ }; -static int wm8962_volatile_register(unsigned int reg) +static int wm8962_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { if (wm8962_reg_access[reg].vol) return 1; @@ -1946,7 +1946,7 @@ static int wm8962_volatile_register(unsigned int reg) return 0; } -static int wm8962_readable_register(unsigned int reg) +static int wm8962_readable_register(struct snd_soc_codec *codec, unsigned int reg) { if (wm8962_reg_access[reg].read) return 1; diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 18c0d9ce7c32..379fa22c5b6c 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -242,7 +242,7 @@ struct wm8993_priv { int fll_src; }; -static int wm8993_volatile(unsigned int reg) +static int wm8993_volatile(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8993_SOFTWARE_RESET: diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 247a6a99feb8..0bb0bb40b842 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -109,7 +109,7 @@ struct wm8994_priv { struct wm8994_pdata *pdata; }; -static int wm8994_readable(unsigned int reg) +static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8994_GPIO_1: @@ -136,7 +136,7 @@ static int wm8994_readable(unsigned int reg) return wm8994_access_masks[reg].readable != 0; } -static int wm8994_volatile(unsigned int reg) +static int wm8994_volatile(struct snd_soc_codec *codec, unsigned int reg) { if (reg >= WM8994_CACHE_SIZE) return 1; @@ -164,7 +164,7 @@ static int wm8994_write(struct snd_soc_codec *codec, unsigned int reg, BUG_ON(reg > WM8994_MAX_REGISTER); - if (!wm8994_volatile(reg)) { + if (!wm8994_volatile(codec, reg)) { ret = snd_soc_cache_write(codec, reg, value); if (ret != 0) dev_err(codec->dev, "Cache write to %x failed: %d\n", @@ -182,7 +182,7 @@ static unsigned int wm8994_read(struct snd_soc_codec *codec, BUG_ON(reg > WM8994_MAX_REGISTER); - if (!wm8994_volatile(reg) && wm8994_readable(reg) && + if (!wm8994_volatile(codec, reg) && wm8994_readable(codec, reg) && reg < codec->driver->reg_cache_size) { ret = snd_soc_cache_read(codec, reg, &val); if (ret >= 0) @@ -2943,7 +2943,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) /* Read our current status back from the chip - we don't want to * reset as this may interfere with the GPIO or LDO operation. */ for (i = 0; i < WM8994_CACHE_SIZE; i++) { - if (!wm8994_readable(i) || wm8994_volatile(i)) + if (!wm8994_readable(codec, i) || wm8994_volatile(codec, i)) continue; ret = wm8994_reg_read(codec->control_data, i); diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index ac210ccebd4b..f0f678de489f 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -909,7 +909,7 @@ static const struct snd_soc_dapm_route wm8995_intercon[] = { { "SPK2R", NULL, "SPK2R Driver" } }; -static int wm8995_volatile(unsigned int reg) +static int wm8995_volatile(struct snd_soc_codec *codec, unsigned int reg) { /* out of bounds registers are generally considered * volatile to support register banks that are partially diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 43825b2102a5..5c224dd917d7 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -169,7 +169,7 @@ struct wm9081_priv { struct wm9081_retune_mobile_config *retune; }; -static int wm9081_volatile_register(unsigned int reg) +static int wm9081_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM9081_SOFTWARE_RESET: diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index a788c4297046..d40bfc9f8805 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -144,7 +144,7 @@ struct wm9090_priv { void *control_data; }; -static int wm9090_volatile(unsigned int reg) +static int wm9090_volatile(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM9090_SOFTWARE_RESET: @@ -518,7 +518,7 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec, for (i = 1; i < codec->driver->reg_cache_size; i++) { if (reg_cache[i] == wm9090_reg_defaults[i]) continue; - if (wm9090_volatile(i)) + if (wm9090_volatile(codec, i)) continue; ret = snd_soc_write(codec, i, reg_cache[i]); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index cbac50b69c39..b5e5758456bd 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -84,7 +84,7 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) count += sprintf(buf, "%s registers\n", codec->name); for (i = 0; i < codec->driver->reg_cache_size; i += step) { - if (codec->driver->readable_register && !codec->driver->readable_register(i)) + if (codec->driver->readable_register && !codec->driver->readable_register(codec, i)) continue; count += sprintf(buf + count, "%2x: ", i); @@ -2030,7 +2030,7 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg) { if (codec->driver->volatile_register) - return codec->driver->volatile_register(reg); + return codec->driver->volatile_register(codec, reg); else return 0; } -- cgit v1.2.3 From 1500b7b5ffaacb8199e0a61162f5d349fb19acbe Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Thu, 13 Jan 2011 12:20:38 +0000 Subject: ASoC: Automatically assign the default readable()/volatile() functions Ensure that all calls to readable_register()/volatile_register() go via the snd_soc_codec function pointers. If the default register access table has been given but no functions for handling readable()/volatile() registers, use the default ones provided by soc-cache. Signed-off-by: Dimitris Papastamos Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc.h | 2 ++ sound/soc/soc-core.c | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 97d1832bb9df..accb8a16c165 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -480,6 +480,8 @@ struct snd_soc_codec { int num_dai; enum snd_soc_compress_type compress_type; size_t reg_size; /* reg_cache_size * reg_word_size */ + int (*volatile_register)(struct snd_soc_codec *, unsigned int); + int (*readable_register)(struct snd_soc_codec *, unsigned int); /* runtime */ struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b5e5758456bd..30d76e8bc9df 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -84,7 +84,7 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) count += sprintf(buf, "%s registers\n", codec->name); for (i = 0; i < codec->driver->reg_cache_size; i += step) { - if (codec->driver->readable_register && !codec->driver->readable_register(codec, i)) + if (codec->readable_register && !codec->readable_register(codec, i)) continue; count += sprintf(buf + count, "%2x: ", i); @@ -2029,8 +2029,8 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) */ int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg) { - if (codec->driver->volatile_register) - return codec->driver->volatile_register(codec, reg); + if (codec->volatile_register) + return codec->volatile_register(codec, reg); else return 0; } @@ -3489,6 +3489,8 @@ int snd_soc_register_codec(struct device *dev, codec->write = codec_drv->write; codec->read = codec_drv->read; + codec->volatile_register = codec_drv->volatile_register; + codec->readable_register = codec_drv->readable_register; codec->dapm.bias_level = SND_SOC_BIAS_OFF; codec->dapm.dev = dev; codec->dapm.codec = codec; @@ -3517,6 +3519,13 @@ int snd_soc_register_codec(struct device *dev, } } + if (codec_drv->reg_access_size && codec_drv->reg_access_default) { + if (!codec->volatile_register) + codec->volatile_register = snd_soc_default_volatile_register; + if (!codec->readable_register) + codec->readable_register = snd_soc_default_readable_register; + } + for (i = 0; i < num_dai; i++) { fixup_codec_formats(&dai_drv[i].playback); fixup_codec_formats(&dai_drv[i].capture); -- cgit v1.2.3 From 4e10bda05d6c7d4aba509bbbab5ba748d54c702f Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 13 Jan 2011 22:48:52 +0530 Subject: ASoC: soc core add inline to handle card list initialzation Currently the soc_probe initializes the card hence it does the card list initialzation. But if machines directly register the card they would need to do these steps, so putting them as inline would save lot of code This patch adds an inline to do list initialzation Signed-off-by: Vinod Koul Signed-off-by: Harsha Priya Signed-off-by: Mark Brown --- include/sound/soc.h | 10 ++++++++++ sound/soc/soc-core.c | 7 +------ 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index accb8a16c165..541ddfaa1243 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -779,6 +779,16 @@ static inline void *snd_soc_pcm_get_drvdata(struct snd_soc_pcm_runtime *rtd) return dev_get_drvdata(&rtd->dev); } +static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card) +{ + INIT_LIST_HEAD(&card->dai_dev_list); + INIT_LIST_HEAD(&card->codec_dev_list); + INIT_LIST_HEAD(&card->platform_dev_list); + INIT_LIST_HEAD(&card->widgets); + INIT_LIST_HEAD(&card->paths); + INIT_LIST_HEAD(&card->dapm_list); +} + #include #ifdef CONFIG_DEBUG_FS diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 9c5e7cff3f01..83057127b2fa 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1872,12 +1872,7 @@ static int soc_probe(struct platform_device *pdev) /* Bodge while we unpick instantiation */ card->dev = &pdev->dev; - INIT_LIST_HEAD(&card->dai_dev_list); - INIT_LIST_HEAD(&card->codec_dev_list); - INIT_LIST_HEAD(&card->platform_dev_list); - INIT_LIST_HEAD(&card->widgets); - INIT_LIST_HEAD(&card->paths); - INIT_LIST_HEAD(&card->dapm_list); + snd_soc_initialize_card_lists(card); ret = snd_soc_register_card(card); if (ret != 0) { -- cgit v1.2.3 From c7066f70d9610df0b9406cc635fc09e86136e714 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 14 Jan 2011 13:36:42 +0100 Subject: netfilter: fix Kconfig dependencies Fix dependencies of netfilter realm match: it depends on NET_CLS_ROUTE, which itself depends on NET_SCHED; this dependency is missing from netfilter. Since matching on realms is also useful without having NET_SCHED enabled and the option really only controls whether the tclassid member is included in route and dst entries, rename the config option to IP_ROUTE_CLASSID and move it outside of traffic scheduling context to get rid of the NET_SCHED dependeny. Reported-by: Vladis Kletnieks Signed-off-by: Patrick McHardy --- include/net/dst.h | 2 +- include/net/ip_fib.h | 6 +++--- net/ipv4/Kconfig | 4 +++- net/ipv4/fib_rules.c | 10 +++++----- net/ipv4/fib_semantics.c | 14 +++++++------- net/ipv4/ip_input.c | 2 +- net/ipv4/route.c | 26 +++++++++++++------------- net/netfilter/Kconfig | 2 +- net/sched/Kconfig | 5 +---- net/sched/cls_flow.c | 2 +- net/sched/em_meta.c | 2 +- 11 files changed, 37 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index a5bd72646d65..6baba836ad8b 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -72,7 +72,7 @@ struct dst_entry { u32 metrics[RTAX_MAX]; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID __u32 tclassid; #else __u32 __pad2; diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 07bdb5e9e8ac..65d1fcdbc63b 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -55,7 +55,7 @@ struct fib_nh { int nh_weight; int nh_power; #endif -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID __u32 nh_tclassid; #endif int nh_oif; @@ -201,7 +201,7 @@ static inline int fib_lookup(struct net *net, const struct flowi *flp, extern int __net_init fib4_rules_init(struct net *net); extern void __net_exit fib4_rules_exit(struct net *net); -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID extern u32 fib_rules_tclass(struct fib_result *res); #endif @@ -235,7 +235,7 @@ extern struct fib_table *fib_hash_table(u32 id); static inline void fib_combine_itag(u32 *itag, struct fib_result *res) { -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID #ifdef CONFIG_IP_MULTIPLE_TABLES u32 rtag; #endif diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index 9e95d7fb6d5a..dcb2e18f6f8e 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -140,6 +140,9 @@ config IP_ROUTE_VERBOSE handled by the klogd daemon which is responsible for kernel messages ("man klogd"). +config IP_ROUTE_CLASSID + bool + config IP_PNP bool "IP: kernel level autoconfiguration" help @@ -655,4 +658,3 @@ config TCP_MD5SIG on the Internet. If unsure, say N. - diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 7981a24f5c7b..9cefe72029cf 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -41,12 +41,12 @@ struct fib4_rule { __be32 srcmask; __be32 dst; __be32 dstmask; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID u32 tclassid; #endif }; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID u32 fib_rules_tclass(struct fib_result *res) { return res->r ? ((struct fib4_rule *) res->r)->tclassid : 0; @@ -165,7 +165,7 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb, if (frh->dst_len) rule4->dst = nla_get_be32(tb[FRA_DST]); -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID if (tb[FRA_FLOW]) rule4->tclassid = nla_get_u32(tb[FRA_FLOW]); #endif @@ -195,7 +195,7 @@ static int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, if (frh->tos && (rule4->tos != frh->tos)) return 0; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID if (tb[FRA_FLOW] && (rule4->tclassid != nla_get_u32(tb[FRA_FLOW]))) return 0; #endif @@ -224,7 +224,7 @@ static int fib4_rule_fill(struct fib_rule *rule, struct sk_buff *skb, if (rule4->src_len) NLA_PUT_BE32(skb, FRA_SRC, rule4->src); -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID if (rule4->tclassid) NLA_PUT_U32(skb, FRA_FLOW, rule4->tclassid); #endif diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 3e0da3ef6116..a72c62d03106 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -200,7 +200,7 @@ static inline int nh_comp(const struct fib_info *fi, const struct fib_info *ofi) #ifdef CONFIG_IP_ROUTE_MULTIPATH nh->nh_weight != onh->nh_weight || #endif -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID nh->nh_tclassid != onh->nh_tclassid || #endif ((nh->nh_flags ^ onh->nh_flags) & ~RTNH_F_DEAD)) @@ -422,7 +422,7 @@ static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh, nla = nla_find(attrs, attrlen, RTA_GATEWAY); nexthop_nh->nh_gw = nla ? nla_get_be32(nla) : 0; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID nla = nla_find(attrs, attrlen, RTA_FLOW); nexthop_nh->nh_tclassid = nla ? nla_get_u32(nla) : 0; #endif @@ -476,7 +476,7 @@ int fib_nh_match(struct fib_config *cfg, struct fib_info *fi) nla = nla_find(attrs, attrlen, RTA_GATEWAY); if (nla && nla_get_be32(nla) != nh->nh_gw) return 1; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID nla = nla_find(attrs, attrlen, RTA_FLOW); if (nla && nla_get_u32(nla) != nh->nh_tclassid) return 1; @@ -783,7 +783,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg) goto err_inval; if (cfg->fc_gw && fi->fib_nh->nh_gw != cfg->fc_gw) goto err_inval; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID if (cfg->fc_flow && fi->fib_nh->nh_tclassid != cfg->fc_flow) goto err_inval; #endif @@ -796,7 +796,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg) nh->nh_oif = cfg->fc_oif; nh->nh_gw = cfg->fc_gw; nh->nh_flags = cfg->fc_flags; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID nh->nh_tclassid = cfg->fc_flow; #endif #ifdef CONFIG_IP_ROUTE_MULTIPATH @@ -1006,7 +1006,7 @@ int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, if (fi->fib_nh->nh_oif) NLA_PUT_U32(skb, RTA_OIF, fi->fib_nh->nh_oif); -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID if (fi->fib_nh[0].nh_tclassid) NLA_PUT_U32(skb, RTA_FLOW, fi->fib_nh[0].nh_tclassid); #endif @@ -1031,7 +1031,7 @@ int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, if (nh->nh_gw) NLA_PUT_BE32(skb, RTA_GATEWAY, nh->nh_gw); -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID if (nh->nh_tclassid) NLA_PUT_U32(skb, RTA_FLOW, nh->nh_tclassid); #endif diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index d859bcc26cb7..d7b2b0987a3b 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -340,7 +340,7 @@ static int ip_rcv_finish(struct sk_buff *skb) } } -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID if (unlikely(skb_dst(skb)->tclassid)) { struct ip_rt_acct *st = this_cpu_ptr(ip_rt_acct); u32 idx = skb_dst(skb)->tclassid; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 66610ea3c87b..f70ae1bccb8a 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -511,7 +511,7 @@ static const struct file_operations rt_cpu_seq_fops = { .release = seq_release, }; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID static int rt_acct_proc_show(struct seq_file *m, void *v) { struct ip_rt_acct *dst, *src; @@ -564,14 +564,14 @@ static int __net_init ip_rt_do_proc_init(struct net *net) if (!pde) goto err2; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID pde = proc_create("rt_acct", 0, net->proc_net, &rt_acct_proc_fops); if (!pde) goto err3; #endif return 0; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID err3: remove_proc_entry("rt_cache", net->proc_net_stat); #endif @@ -585,7 +585,7 @@ static void __net_exit ip_rt_do_proc_exit(struct net *net) { remove_proc_entry("rt_cache", net->proc_net_stat); remove_proc_entry("rt_cache", net->proc_net); -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID remove_proc_entry("rt_acct", net->proc_net); #endif } @@ -1784,7 +1784,7 @@ void ip_rt_get_source(u8 *addr, struct rtable *rt) memcpy(addr, &src, 4); } -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID static void set_class_tag(struct rtable *rt, u32 tag) { if (!(rt->dst.tclassid & 0xFFFF)) @@ -1811,7 +1811,7 @@ static void rt_set_nexthop(struct rtable *rt, struct fib_result *res, u32 itag) rt->dst.dev->mtu > 576) rt->dst.metrics[RTAX_MTU-1] = 576; } -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID rt->dst.tclassid = FIB_RES_NH(*res).nh_tclassid; #endif } else @@ -1827,7 +1827,7 @@ static void rt_set_nexthop(struct rtable *rt, struct fib_result *res, u32 itag) if (dst_metric(&rt->dst, RTAX_ADVMSS) > 65535 - 40) rt->dst.metrics[RTAX_ADVMSS-1] = 65535 - 40; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID #ifdef CONFIG_IP_MULTIPLE_TABLES set_class_tag(rt, fib_rules_tclass(res)); #endif @@ -1883,7 +1883,7 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, rth->fl.mark = skb->mark; rth->fl.fl4_src = saddr; rth->rt_src = saddr; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID rth->dst.tclassid = itag; #endif rth->rt_iif = @@ -2202,7 +2202,7 @@ local_input: rth->fl.mark = skb->mark; rth->fl.fl4_src = saddr; rth->rt_src = saddr; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID rth->dst.tclassid = itag; #endif rth->rt_iif = @@ -2820,7 +2820,7 @@ static int rt_fill_info(struct net *net, } if (rt->dst.dev) NLA_PUT_U32(skb, RTA_OIF, rt->dst.dev->ifindex); -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID if (rt->dst.tclassid) NLA_PUT_U32(skb, RTA_FLOW, rt->dst.tclassid); #endif @@ -3245,9 +3245,9 @@ static __net_initdata struct pernet_operations rt_genid_ops = { }; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID struct ip_rt_acct __percpu *ip_rt_acct __read_mostly; -#endif /* CONFIG_NET_CLS_ROUTE */ +#endif /* CONFIG_IP_ROUTE_CLASSID */ static __initdata unsigned long rhash_entries; static int __init set_rhash_entries(char *str) @@ -3263,7 +3263,7 @@ int __init ip_rt_init(void) { int rc = 0; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID ip_rt_acct = __alloc_percpu(256 * sizeof(struct ip_rt_acct), __alignof__(struct ip_rt_acct)); if (!ip_rt_acct) panic("IP: failed to allocate ip_rt_acct\n"); diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 1534f2b44caf..1b79353a5d29 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -886,7 +886,7 @@ config NETFILTER_XT_MATCH_RATEEST config NETFILTER_XT_MATCH_REALM tristate '"realm" match support' depends on NETFILTER_ADVANCED - select NET_CLS_ROUTE + select IP_ROUTE_CLASSID help This option adds a `realm' match, which allows you to use the realm key from the routing subsystem inside iptables. diff --git a/net/sched/Kconfig b/net/sched/Kconfig index a36270a994d7..4b753ef70bb7 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -243,7 +243,7 @@ config NET_CLS_TCINDEX config NET_CLS_ROUTE4 tristate "Routing decision (ROUTE)" - select NET_CLS_ROUTE + select IP_ROUTE_CLASSID select NET_CLS ---help--- If you say Y here, you will be able to classify packets @@ -252,9 +252,6 @@ config NET_CLS_ROUTE4 To compile this code as a module, choose M here: the module will be called cls_route. -config NET_CLS_ROUTE - bool - config NET_CLS_FW tristate "Netfilter mark (FW)" select NET_CLS diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 5b271a18bc3a..a3b293d22c66 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -276,7 +276,7 @@ fallback: static u32 flow_get_rtclassid(const struct sk_buff *skb) { -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID if (skb_dst(skb)) return skb_dst(skb)->tclassid; #endif diff --git a/net/sched/em_meta.c b/net/sched/em_meta.c index 34da5e29ea1a..0d66e58f26d8 100644 --- a/net/sched/em_meta.c +++ b/net/sched/em_meta.c @@ -255,7 +255,7 @@ META_COLLECTOR(int_rtclassid) if (unlikely(skb_dst(skb) == NULL)) *err = -1; else -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID dst->value = skb_dst(skb)->tclassid; #else dst->value = 0; -- cgit v1.2.3 From d862a6622e9db508d4b28cc7c5bc28bd548cc24e Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 14 Jan 2011 15:45:56 +0100 Subject: netfilter: nf_conntrack: use is_vmalloc_addr() Use is_vmalloc_addr() in nf_ct_free_hashtable() and get rid of the vmalloc flags to indicate that a hash table has been allocated using vmalloc(). Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_conntrack.h | 4 ++-- include/net/netns/conntrack.h | 2 -- include/net/netns/ipv4.h | 1 - net/ipv4/netfilter/nf_nat_core.c | 6 ++---- net/netfilter/nf_conntrack_core.c | 26 +++++++++----------------- net/netfilter/nf_conntrack_expect.c | 9 +++------ net/netfilter/nf_conntrack_helper.c | 10 +++------- 7 files changed, 19 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 2bc344c98215..d0d13378991e 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -202,9 +202,9 @@ extern void nf_ct_l3proto_module_put(unsigned short l3proto); * Allocate a hashtable of hlist_head (if nulls == 0), * or hlist_nulls_head (if nulls == 1) */ -extern void *nf_ct_alloc_hashtable(unsigned int *sizep, int *vmalloced, int nulls); +extern void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls); -extern void nf_ct_free_hashtable(void *hash, int vmalloced, unsigned int size); +extern void nf_ct_free_hashtable(void *hash, unsigned int size); extern struct nf_conntrack_tuple_hash * __nf_conntrack_find(struct net *net, u16 zone, diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index d4958d4c6574..5cf8a8c141aa 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -28,8 +28,6 @@ struct netns_ct { struct ctl_table_header *acct_sysctl_header; struct ctl_table_header *event_sysctl_header; #endif - int hash_vmalloc; - int expect_vmalloc; char *slabname; }; #endif diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index d68c3f121774..e2e2ef57eca2 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -43,7 +43,6 @@ struct netns_ipv4 { struct xt_table *nat_table; struct hlist_head *nat_bysource; unsigned int nat_htable_size; - int nat_vmalloced; #endif int sysctl_icmp_echo_ignore_all; diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c index eb55835a02c3..6972ceee99c6 100644 --- a/net/ipv4/netfilter/nf_nat_core.c +++ b/net/ipv4/netfilter/nf_nat_core.c @@ -682,8 +682,7 @@ static int __net_init nf_nat_net_init(struct net *net) { /* Leave them the same for the moment. */ net->ipv4.nat_htable_size = net->ct.htable_size; - net->ipv4.nat_bysource = nf_ct_alloc_hashtable(&net->ipv4.nat_htable_size, - &net->ipv4.nat_vmalloced, 0); + net->ipv4.nat_bysource = nf_ct_alloc_hashtable(&net->ipv4.nat_htable_size, 0); if (!net->ipv4.nat_bysource) return -ENOMEM; return 0; @@ -705,8 +704,7 @@ static void __net_exit nf_nat_net_exit(struct net *net) { nf_ct_iterate_cleanup(net, &clean_nat, NULL); synchronize_rcu(); - nf_ct_free_hashtable(net->ipv4.nat_bysource, net->ipv4.nat_vmalloced, - net->ipv4.nat_htable_size); + nf_ct_free_hashtable(net->ipv4.nat_bysource, net->ipv4.nat_htable_size); } static struct pernet_operations nf_nat_net_ops = { diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index e95ac42ef673..dc2ff2cd0a7e 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1202,9 +1202,9 @@ static int kill_all(struct nf_conn *i, void *data) return 1; } -void nf_ct_free_hashtable(void *hash, int vmalloced, unsigned int size) +void nf_ct_free_hashtable(void *hash, unsigned int size) { - if (vmalloced) + if (is_vmalloc_addr(hash)) vfree(hash); else free_pages((unsigned long)hash, @@ -1271,8 +1271,7 @@ static void nf_conntrack_cleanup_net(struct net *net) goto i_see_dead_people; } - nf_ct_free_hashtable(net->ct.hash, net->ct.hash_vmalloc, - net->ct.htable_size); + nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); nf_conntrack_ecache_fini(net); nf_conntrack_acct_fini(net); nf_conntrack_expect_fini(net); @@ -1301,21 +1300,18 @@ void nf_conntrack_cleanup(struct net *net) } } -void *nf_ct_alloc_hashtable(unsigned int *sizep, int *vmalloced, int nulls) +void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls) { struct hlist_nulls_head *hash; unsigned int nr_slots, i; size_t sz; - *vmalloced = 0; - BUILD_BUG_ON(sizeof(struct hlist_nulls_head) != sizeof(struct hlist_head)); nr_slots = *sizep = roundup(*sizep, PAGE_SIZE / sizeof(struct hlist_nulls_head)); sz = nr_slots * sizeof(struct hlist_nulls_head); hash = (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO, get_order(sz)); if (!hash) { - *vmalloced = 1; printk(KERN_WARNING "nf_conntrack: falling back to vmalloc.\n"); hash = __vmalloc(sz, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, PAGE_KERNEL); @@ -1331,7 +1327,7 @@ EXPORT_SYMBOL_GPL(nf_ct_alloc_hashtable); int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp) { - int i, bucket, vmalloced, old_vmalloced; + int i, bucket; unsigned int hashsize, old_size; struct hlist_nulls_head *hash, *old_hash; struct nf_conntrack_tuple_hash *h; @@ -1348,7 +1344,7 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp) if (!hashsize) return -EINVAL; - hash = nf_ct_alloc_hashtable(&hashsize, &vmalloced, 1); + hash = nf_ct_alloc_hashtable(&hashsize, 1); if (!hash) return -ENOMEM; @@ -1370,15 +1366,13 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp) } } old_size = init_net.ct.htable_size; - old_vmalloced = init_net.ct.hash_vmalloc; old_hash = init_net.ct.hash; init_net.ct.htable_size = nf_conntrack_htable_size = hashsize; - init_net.ct.hash_vmalloc = vmalloced; init_net.ct.hash = hash; spin_unlock_bh(&nf_conntrack_lock); - nf_ct_free_hashtable(old_hash, old_vmalloced, old_size); + nf_ct_free_hashtable(old_hash, old_size); return 0; } EXPORT_SYMBOL_GPL(nf_conntrack_set_hashsize); @@ -1491,8 +1485,7 @@ static int nf_conntrack_init_net(struct net *net) } net->ct.htable_size = nf_conntrack_htable_size; - net->ct.hash = nf_ct_alloc_hashtable(&net->ct.htable_size, - &net->ct.hash_vmalloc, 1); + net->ct.hash = nf_ct_alloc_hashtable(&net->ct.htable_size, 1); if (!net->ct.hash) { ret = -ENOMEM; printk(KERN_ERR "Unable to create nf_conntrack_hash\n"); @@ -1515,8 +1508,7 @@ err_ecache: err_acct: nf_conntrack_expect_fini(net); err_expect: - nf_ct_free_hashtable(net->ct.hash, net->ct.hash_vmalloc, - net->ct.htable_size); + nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); err_hash: kmem_cache_destroy(net->ct.nf_conntrack_cachep); err_cache: diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 4a9ed23180df..cd1e8e0970f2 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -639,8 +639,7 @@ int nf_conntrack_expect_init(struct net *net) } net->ct.expect_count = 0; - net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, - &net->ct.expect_vmalloc, 0); + net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, 0); if (net->ct.expect_hash == NULL) goto err1; @@ -662,8 +661,7 @@ err3: if (net_eq(net, &init_net)) kmem_cache_destroy(nf_ct_expect_cachep); err2: - nf_ct_free_hashtable(net->ct.expect_hash, net->ct.expect_vmalloc, - nf_ct_expect_hsize); + nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize); err1: return err; } @@ -675,6 +673,5 @@ void nf_conntrack_expect_fini(struct net *net) rcu_barrier(); /* Wait for call_rcu() before destroy */ kmem_cache_destroy(nf_ct_expect_cachep); } - nf_ct_free_hashtable(net->ct.expect_hash, net->ct.expect_vmalloc, - nf_ct_expect_hsize); + nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize); } diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 767bbe98a0f0..1bdfea357955 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -33,7 +33,6 @@ static DEFINE_MUTEX(nf_ct_helper_mutex); static struct hlist_head *nf_ct_helper_hash __read_mostly; static unsigned int nf_ct_helper_hsize __read_mostly; static unsigned int nf_ct_helper_count __read_mostly; -static int nf_ct_helper_vmalloc; /* Stupid hash, but collision free for the default registrations of the @@ -267,8 +266,7 @@ int nf_conntrack_helper_init(void) int err; nf_ct_helper_hsize = 1; /* gets rounded up to use one page */ - nf_ct_helper_hash = nf_ct_alloc_hashtable(&nf_ct_helper_hsize, - &nf_ct_helper_vmalloc, 0); + nf_ct_helper_hash = nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 0); if (!nf_ct_helper_hash) return -ENOMEM; @@ -279,14 +277,12 @@ int nf_conntrack_helper_init(void) return 0; err1: - nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_vmalloc, - nf_ct_helper_hsize); + nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize); return err; } void nf_conntrack_helper_fini(void) { nf_ct_extend_unregister(&helper_extend); - nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_vmalloc, - nf_ct_helper_hsize); + nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize); } -- cgit v1.2.3 From 43f393caec0362abe03c72799d3f342af3973070 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Sun, 16 Jan 2011 18:10:28 +0100 Subject: netfilter: audit target to record accepted/dropped packets This patch adds a new netfilter target which creates audit records for packets traversing a certain chain. It can be used to record packets which are rejected administraively as follows: -N AUDIT_DROP -A AUDIT_DROP -j AUDIT --type DROP -A AUDIT_DROP -j DROP a rule which would typically drop or reject a packet would then invoke the new chain to record packets before dropping them. -j AUDIT_DROP The module is protocol independant and works for iptables, ip6tables and ebtables. The following information is logged: - netfilter hook - packet length - incomming/outgoing interface - MAC src/dst/proto for ethernet packets - src/dst/protocol address for IPv4/IPv6 - src/dst port for TCP/UDP/UDPLITE - icmp type/code Cc: Patrick McHardy Cc: Eric Paris Cc: Al Viro Signed-off-by: Thomas Graf Signed-off-by: Patrick McHardy --- include/linux/audit.h | 1 + include/linux/netfilter/Kbuild | 1 + include/linux/netfilter/xt_AUDIT.h | 30 ++++++ net/netfilter/Kconfig | 10 ++ net/netfilter/Makefile | 1 + net/netfilter/xt_AUDIT.c | 204 +++++++++++++++++++++++++++++++++++++ 6 files changed, 247 insertions(+) create mode 100644 include/linux/netfilter/xt_AUDIT.h create mode 100644 net/netfilter/xt_AUDIT.c (limited to 'include') diff --git a/include/linux/audit.h b/include/linux/audit.h index 8b5c0620abf9..ae227dfcf9c6 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -103,6 +103,7 @@ #define AUDIT_BPRM_FCAPS 1321 /* Information about fcaps increasing perms */ #define AUDIT_CAPSET 1322 /* Record showing argument to sys_capset */ #define AUDIT_MMAP 1323 /* Record showing descriptor and flags in mmap */ +#define AUDIT_NETFILTER_PKT 1324 /* Packets traversing netfilter chains */ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index 9d40effe7ca7..9f11fbc377e2 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild @@ -9,6 +9,7 @@ header-y += nfnetlink_conntrack.h header-y += nfnetlink_log.h header-y += nfnetlink_queue.h header-y += x_tables.h +header-y += xt_AUDIT.h header-y += xt_CHECKSUM.h header-y += xt_CLASSIFY.h header-y += xt_CONNMARK.h diff --git a/include/linux/netfilter/xt_AUDIT.h b/include/linux/netfilter/xt_AUDIT.h new file mode 100644 index 000000000000..38751d2ea52b --- /dev/null +++ b/include/linux/netfilter/xt_AUDIT.h @@ -0,0 +1,30 @@ +/* + * Header file for iptables xt_AUDIT target + * + * (C) 2010-2011 Thomas Graf + * (C) 2010-2011 Red Hat, Inc. + * + * 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. + */ + +#ifndef _XT_AUDIT_TARGET_H +#define _XT_AUDIT_TARGET_H + +#include + +enum { + XT_AUDIT_TYPE_ACCEPT = 0, + XT_AUDIT_TYPE_DROP, + XT_AUDIT_TYPE_REJECT, + __XT_AUDIT_TYPE_MAX, +}; + +#define XT_AUDIT_TYPE_MAX (__XT_AUDIT_TYPE_MAX - 1) + +struct xt_audit_info { + __u8 type; /* XT_AUDIT_TYPE_* */ +}; + +#endif /* _XT_AUDIT_TARGET_H */ diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 1b79353a5d29..93918f022555 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -326,6 +326,16 @@ config NETFILTER_XT_CONNMARK comment "Xtables targets" +config NETFILTER_XT_TARGET_AUDIT + tristate "AUDIT target support" + depends on AUDIT + depends on NETFILTER_ADVANCED + ---help--- + This option adds a 'AUDIT' target, which can be used to create + audit records for packets dropped/accepted. + + To compileit as a module, choose M here. If unsure, say N. + config NETFILTER_XT_TARGET_CHECKSUM tristate "CHECKSUM target support" depends on IP_NF_MANGLE || IP6_NF_MANGLE diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 441050f31111..401d574bdfd2 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_NETFILTER_XT_MARK) += xt_mark.o obj-$(CONFIG_NETFILTER_XT_CONNMARK) += xt_connmark.o # targets +obj-$(CONFIG_NETFILTER_XT_TARGET_AUDIT) += xt_AUDIT.o obj-$(CONFIG_NETFILTER_XT_TARGET_CHECKSUM) += xt_CHECKSUM.o obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIFY) += xt_CLASSIFY.o obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o diff --git a/net/netfilter/xt_AUDIT.c b/net/netfilter/xt_AUDIT.c new file mode 100644 index 000000000000..81802d27346e --- /dev/null +++ b/net/netfilter/xt_AUDIT.c @@ -0,0 +1,204 @@ +/* + * Creates audit record for dropped/accepted packets + * + * (C) 2010-2011 Thomas Graf + * (C) 2010-2011 Red Hat, Inc. + * + * 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. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Thomas Graf "); +MODULE_DESCRIPTION("Xtables: creates audit records for dropped/accepted packets"); +MODULE_ALIAS("ipt_AUDIT"); +MODULE_ALIAS("ip6t_AUDIT"); +MODULE_ALIAS("ebt_AUDIT"); +MODULE_ALIAS("arpt_AUDIT"); + +static void audit_proto(struct audit_buffer *ab, struct sk_buff *skb, + unsigned int proto, unsigned int offset) +{ + switch (proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_UDPLITE: { + const __be16 *pptr; + __be16 _ports[2]; + + pptr = skb_header_pointer(skb, offset, sizeof(_ports), _ports); + if (pptr == NULL) { + audit_log_format(ab, " truncated=1"); + return; + } + + audit_log_format(ab, " sport=%hu dport=%hu", + ntohs(pptr[0]), ntohs(pptr[1])); + } + break; + + case IPPROTO_ICMP: + case IPPROTO_ICMPV6: { + const u8 *iptr; + u8 _ih[2]; + + iptr = skb_header_pointer(skb, offset, sizeof(_ih), &_ih); + if (iptr == NULL) { + audit_log_format(ab, " truncated=1"); + return; + } + + audit_log_format(ab, " icmptype=%hhu icmpcode=%hhu", + iptr[0], iptr[1]); + + } + break; + } +} + +static void audit_ip4(struct audit_buffer *ab, struct sk_buff *skb) +{ + struct iphdr _iph; + const struct iphdr *ih; + + ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); + if (!ih) { + audit_log_format(ab, " truncated=1"); + return; + } + + audit_log_format(ab, " saddr=%pI4 daddr=%pI4 ipid=%hu proto=%hhu", + &ih->saddr, &ih->daddr, ntohs(ih->id), ih->protocol); + + if (ntohs(ih->frag_off) & IP_OFFSET) { + audit_log_format(ab, " frag=1"); + return; + } + + audit_proto(ab, skb, ih->protocol, ih->ihl * 4); +} + +static void audit_ip6(struct audit_buffer *ab, struct sk_buff *skb) +{ + struct ipv6hdr _ip6h; + const struct ipv6hdr *ih; + u8 nexthdr; + int offset; + + ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_ip6h), &_ip6h); + if (!ih) { + audit_log_format(ab, " truncated=1"); + return; + } + + nexthdr = ih->nexthdr; + offset = ipv6_skip_exthdr(skb, skb_network_offset(skb) + sizeof(_ip6h), + &nexthdr); + + audit_log_format(ab, " saddr=%pI6c daddr=%pI6c proto=%hhu", + &ih->saddr, &ih->daddr, nexthdr); + + if (offset) + audit_proto(ab, skb, nexthdr, offset); +} + +static unsigned int +audit_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct xt_audit_info *info = par->targinfo; + struct audit_buffer *ab; + + ab = audit_log_start(NULL, GFP_ATOMIC, AUDIT_NETFILTER_PKT); + if (ab == NULL) + goto errout; + + audit_log_format(ab, "action=%hhu hook=%u len=%u inif=%s outif=%s", + info->type, par->hooknum, skb->len, + par->in ? par->in->name : "?", + par->out ? par->out->name : "?"); + + if (skb->mark) + audit_log_format(ab, " mark=%#x", skb->mark); + + if (skb->dev && skb->dev->type == ARPHRD_ETHER) { + audit_log_format(ab, " smac=%pM dmac=%pM macproto=0x%04x", + eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, + ntohs(eth_hdr(skb)->h_proto)); + + if (par->family == NFPROTO_BRIDGE) { + switch (eth_hdr(skb)->h_proto) { + case __constant_htons(ETH_P_IP): + audit_ip4(ab, skb); + break; + + case __constant_htons(ETH_P_IPV6): + audit_ip6(ab, skb); + break; + } + } + } + + switch (par->family) { + case NFPROTO_IPV4: + audit_ip4(ab, skb); + break; + + case NFPROTO_IPV6: + audit_ip6(ab, skb); + break; + } + + audit_log_end(ab); + +errout: + return XT_CONTINUE; +} + +static int audit_tg_check(const struct xt_tgchk_param *par) +{ + const struct xt_audit_info *info = par->targinfo; + + if (info->type > XT_AUDIT_TYPE_MAX) { + pr_info("Audit type out of range (valid range: 0..%hhu)\n", + XT_AUDIT_TYPE_MAX); + return -ERANGE; + } + + return 0; +} + +static struct xt_target audit_tg_reg __read_mostly = { + .name = "AUDIT", + .family = NFPROTO_UNSPEC, + .target = audit_tg, + .targetsize = sizeof(struct xt_audit_info), + .checkentry = audit_tg_check, + .me = THIS_MODULE, +}; + +static int __init audit_tg_init(void) +{ + return xt_register_target(&audit_tg_reg); +} + +static void __exit audit_tg_exit(void) +{ + xt_unregister_target(&audit_tg_reg); +} + +module_init(audit_tg_init); +module_exit(audit_tg_exit); -- cgit v1.2.3 From fbabf31e4d482149b5e2704eb0287cf9117bdcf3 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Sun, 16 Jan 2011 18:12:59 +0100 Subject: netfilter: create audit records for x_tables replaces The setsockopt() syscall to replace tables is already recorded in the audit logs. This patch stores additional information such as table name and netfilter protocol. Cc: Patrick McHardy Cc: Eric Paris Cc: Al Viro Signed-off-by: Thomas Graf Signed-off-by: Patrick McHardy --- include/linux/audit.h | 1 + net/netfilter/x_tables.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) (limited to 'include') diff --git a/include/linux/audit.h b/include/linux/audit.h index ae227dfcf9c6..32b5c62a5042 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -104,6 +104,7 @@ #define AUDIT_CAPSET 1322 /* Record showing argument to sys_capset */ #define AUDIT_MMAP 1323 /* Record showing descriptor and flags in mmap */ #define AUDIT_NETFILTER_PKT 1324 /* Packets traversing netfilter chains */ +#define AUDIT_NETFILTER_CFG 1325 /* Netfilter chain modifications */ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index ee5de3af510a..fbc2b72091e0 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -834,6 +835,21 @@ xt_replace_table(struct xt_table *table, */ local_bh_enable(); +#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); + } + } +#endif + return private; } EXPORT_SYMBOL_GPL(xt_replace_table); -- cgit v1.2.3 From 70a7ca34dbdcc6f0ed332baf2b308bab2871424a Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 14 Jan 2011 19:22:48 +0530 Subject: ASoC: soc core allow machine driver to register the card The machine driver can't register the card directly and need to do this thru soc-audio device creation This patch allows the register and unregister card to be directly called by machine drivers Signed-off-by: Vinod Koul Signed-off-by: Harsha Priya Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc.h | 2 ++ sound/soc/soc-core.c | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 541ddfaa1243..9952254974b3 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -258,6 +258,8 @@ enum snd_soc_compress_type { SND_SOC_RBTREE_COMPRESSION }; +int snd_soc_register_card(struct snd_soc_card *card); +int snd_soc_unregister_card(struct snd_soc_card *card); int snd_soc_register_platform(struct device *dev, struct snd_soc_platform_driver *platform_drv); void snd_soc_unregister_platform(struct device *dev); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 83057127b2fa..69117b686fdc 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -58,8 +58,6 @@ static LIST_HEAD(dai_list); static LIST_HEAD(platform_list); static LIST_HEAD(codec_list); -static int snd_soc_register_card(struct snd_soc_card *card); -static int snd_soc_unregister_card(struct snd_soc_card *card); static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num); /* @@ -1870,6 +1868,13 @@ static int soc_probe(struct platform_device *pdev) struct snd_soc_card *card = platform_get_drvdata(pdev); int ret = 0; + /* + * no card, so machine driver should be registering card + * we should not be here in that case so ret error + */ + if (!card) + return -EINVAL; + /* Bodge while we unpick instantiation */ card->dev = &pdev->dev; snd_soc_initialize_card_lists(card); @@ -3105,11 +3110,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); * * @card: Card to register * - * Note that currently this is an internal only function: it will be - * exposed to machine drivers after further backporting of ASoC v2 - * registration APIs. */ -static int snd_soc_register_card(struct snd_soc_card *card) +int snd_soc_register_card(struct snd_soc_card *card) { int i; @@ -3141,17 +3143,15 @@ static int snd_soc_register_card(struct snd_soc_card *card) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_register_card); /** * snd_soc_unregister_card - Unregister a card with the ASoC core * * @card: Card to unregister * - * Note that currently this is an internal only function: it will be - * exposed to machine drivers after further backporting of ASoC v2 - * registration APIs. */ -static int snd_soc_unregister_card(struct snd_soc_card *card) +int snd_soc_unregister_card(struct snd_soc_card *card) { if (card->instantiated) soc_cleanup_card_resources(card); @@ -3162,6 +3162,7 @@ static int snd_soc_unregister_card(struct snd_soc_card *card) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_unregister_card); /* * Simplify DAI link configuration by removing ".-1" from device names -- cgit v1.2.3 From 7898e1f8e9eb1bee88c92d636e0ab93f2cbe31c6 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Mon, 17 Jan 2011 08:05:27 -0800 Subject: Subject: [PATCH] Smack: mmap controls for library containment In the embedded world there are often situations where libraries are updated from a variety of sources, for a variety of reasons, and with any number of security characteristics. These differences might include privilege required for a given library provided interface to function properly, as occurs from time to time in graphics libraries. There are also cases where it is important to limit use of libraries based on the provider of the library and the security aware application may make choices based on that criteria. These issues are addressed by providing an additional Smack label that may optionally be assigned to an object, the SMACK64MMAP attribute. An mmap operation is allowed if there is no such attribute. If there is a SMACK64MMAP attribute the mmap is permitted only if a subject with that label has all of the access permitted a subject with the current task label. Security aware applications may from time to time wish to reduce their "privilege" to avoid accidental use of privilege. One case where this arises is the environment in which multiple sources provide libraries to perform the same functions. An application may know that it should eschew services made available from a particular vendor, or of a particular version. In support of this a secondary list of Smack rules has been added that is local to the task. This list is consulted only in the case where the global list has approved access. It can only further restrict access. Unlike the global last, if no entry is found on the local list access is granted. An application can add entries to its own list by writing to /smack/load-self. The changes appear large as they involve refactoring the list handling to accomodate there being more than one rule list. Signed-off-by: Casey Schaufler --- include/linux/xattr.h | 2 + security/smack/smack.h | 9 +- security/smack/smack_access.c | 52 ++++-- security/smack/smack_lsm.c | 269 +++++++++++++++++++++++++----- security/smack/smackfs.c | 370 +++++++++++++++++++++++++++++------------- 5 files changed, 524 insertions(+), 178 deletions(-) (limited to 'include') diff --git a/include/linux/xattr.h b/include/linux/xattr.h index e6131ef98d8f..6050783005bd 100644 --- a/include/linux/xattr.h +++ b/include/linux/xattr.h @@ -42,11 +42,13 @@ #define XATTR_SMACK_IPOUT "SMACK64IPOUT" #define XATTR_SMACK_EXEC "SMACK64EXEC" #define XATTR_SMACK_TRANSMUTE "SMACK64TRANSMUTE" +#define XATTR_SMACK_MMAP "SMACK64MMAP" #define XATTR_NAME_SMACK XATTR_SECURITY_PREFIX XATTR_SMACK_SUFFIX #define XATTR_NAME_SMACKIPIN XATTR_SECURITY_PREFIX XATTR_SMACK_IPIN #define XATTR_NAME_SMACKIPOUT XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT #define XATTR_NAME_SMACKEXEC XATTR_SECURITY_PREFIX XATTR_SMACK_EXEC #define XATTR_NAME_SMACKTRANSMUTE XATTR_SECURITY_PREFIX XATTR_SMACK_TRANSMUTE +#define XATTR_NAME_SMACKMMAP XATTR_SECURITY_PREFIX XATTR_SMACK_MMAP #define XATTR_CAPS_SUFFIX "capability" #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX diff --git a/security/smack/smack.h b/security/smack/smack.h index 129c4eb8ffb1..e365d455ceb6 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -52,13 +52,16 @@ struct socket_smack { struct inode_smack { char *smk_inode; /* label of the fso */ char *smk_task; /* label of the task */ + char *smk_mmap; /* label of the mmap domain */ struct mutex smk_lock; /* initialization lock */ int smk_flags; /* smack inode flags */ }; struct task_smack { - char *smk_task; /* label used for access control */ - char *smk_forked; /* label when forked */ + char *smk_task; /* label for access control */ + char *smk_forked; /* label when forked */ + struct list_head smk_rules; /* per task access rules */ + struct mutex smk_rules_lock; /* lock for the rules */ }; #define SMK_INODE_INSTANT 0x01 /* inode is instantiated */ @@ -202,7 +205,7 @@ struct inode_smack *new_inode_smack(char *); /* * These functions are in smack_access.c */ -int smk_access_entry(char *, char *); +int smk_access_entry(char *, char *, struct list_head *); int smk_access(char *, char *, int, struct smk_audit_info *); int smk_curacc(char *, u32, struct smk_audit_info *); int smack_to_cipso(const char *, struct smack_cipso *); diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 7ba8478f599e..86453db4333d 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -70,10 +70,11 @@ int log_policy = SMACK_AUDIT_DENIED; * smk_access_entry - look up matching access rule * @subject_label: a pointer to the subject's Smack label * @object_label: a pointer to the object's Smack label + * @rule_list: the list of rules to search * * This function looks up the subject/object pair in the - * access rule list and returns pointer to the matching rule if found, - * NULL otherwise. + * access rule list and returns the access mode. If no + * entry is found returns -ENOENT. * * NOTE: * Even though Smack labels are usually shared on smack_list @@ -85,13 +86,13 @@ int log_policy = SMACK_AUDIT_DENIED; * will be on the list, so checking the pointers may be a worthwhile * optimization. */ -int smk_access_entry(char *subject_label, char *object_label) +int smk_access_entry(char *subject_label, char *object_label, + struct list_head *rule_list) { - u32 may = MAY_NOT; + int may = -ENOENT; struct smack_rule *srp; - rcu_read_lock(); - list_for_each_entry_rcu(srp, &smack_rule_list, list) { + list_for_each_entry_rcu(srp, rule_list, list) { if (srp->smk_subject == subject_label || strcmp(srp->smk_subject, subject_label) == 0) { if (srp->smk_object == object_label || @@ -101,7 +102,6 @@ int smk_access_entry(char *subject_label, char *object_label) } } } - rcu_read_unlock(); return may; } @@ -129,7 +129,7 @@ int smk_access_entry(char *subject_label, char *object_label) int smk_access(char *subject_label, char *object_label, int request, struct smk_audit_info *a) { - u32 may = MAY_NOT; + int may = MAY_NOT; int rc = 0; /* @@ -181,13 +181,14 @@ int smk_access(char *subject_label, char *object_label, int request, * Beyond here an explicit relationship is required. * If the requested access is contained in the available * access (e.g. read is included in readwrite) it's - * good. - */ - may = smk_access_entry(subject_label, object_label); - /* - * This is a bit map operation. + * good. A negative response from smk_access_entry() + * indicates there is no entry for this pair. */ - if ((request & may) == request) + rcu_read_lock(); + may = smk_access_entry(subject_label, object_label, &smack_rule_list); + rcu_read_unlock(); + + if (may > 0 && (request & may) == request) goto out_audit; rc = -EACCES; @@ -212,12 +213,27 @@ out_audit: */ int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) { + struct task_smack *tsp = current_security(); + char *sp = smk_of_task(tsp); + int may; int rc; - char *sp = smk_of_current(); + /* + * Check the global rule list + */ rc = smk_access(sp, obj_label, mode, NULL); - if (rc == 0) - goto out_audit; + if (rc == 0) { + /* + * If there is an entry in the task's rule list + * it can further restrict access. + */ + may = smk_access_entry(sp, obj_label, &tsp->smk_rules); + if (may < 0) + goto out_audit; + if ((mode & may) == mode) + goto out_audit; + rc = -EACCES; + } /* * Return if a specific label has been designated as the @@ -228,7 +244,7 @@ int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) goto out_audit; if (capable(CAP_MAC_OVERRIDE)) - return 0; + rc = 0; out_audit: #ifdef CONFIG_AUDIT diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 533bf3255d7f..123a499ded37 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -84,6 +84,56 @@ struct inode_smack *new_inode_smack(char *smack) return isp; } +/** + * new_task_smack - allocate a task security blob + * @smack: a pointer to the Smack label to use in the blob + * + * Returns the new blob or NULL if there's no memory available + */ +static struct task_smack *new_task_smack(char *task, char *forked, gfp_t gfp) +{ + struct task_smack *tsp; + + tsp = kzalloc(sizeof(struct task_smack), gfp); + if (tsp == NULL) + return NULL; + + tsp->smk_task = task; + tsp->smk_forked = forked; + INIT_LIST_HEAD(&tsp->smk_rules); + mutex_init(&tsp->smk_rules_lock); + + return tsp; +} + +/** + * smk_copy_rules - copy a rule set + * @nhead - new rules header pointer + * @ohead - old rules header pointer + * + * Returns 0 on success, -ENOMEM on error + */ +static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead, + gfp_t gfp) +{ + struct smack_rule *nrp; + struct smack_rule *orp; + int rc = 0; + + INIT_LIST_HEAD(nhead); + + list_for_each_entry_rcu(orp, ohead, list) { + nrp = kzalloc(sizeof(struct smack_rule), gfp); + if (nrp == NULL) { + rc = -ENOMEM; + break; + } + *nrp = *orp; + list_add_rcu(&nrp->list, nhead); + } + return rc; +} + /* * LSM hooks. * We he, that is fun! @@ -102,23 +152,17 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) { int rc; struct smk_audit_info ad; - char *sp, *tsp; + char *tsp; rc = cap_ptrace_access_check(ctp, mode); if (rc != 0) return rc; - sp = smk_of_current(); tsp = smk_of_task(task_security(ctp)); smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, ctp); - /* we won't log here, because rc can be overriden */ - rc = smk_access(sp, tsp, MAY_READWRITE, NULL); - if (rc != 0 && capable(CAP_MAC_OVERRIDE)) - rc = 0; - - smack_log(sp, tsp, MAY_READWRITE, rc, &ad); + rc = smk_curacc(tsp, MAY_READWRITE, &ad); return rc; } @@ -134,23 +178,17 @@ static int smack_ptrace_traceme(struct task_struct *ptp) { int rc; struct smk_audit_info ad; - char *sp, *tsp; + char *tsp; rc = cap_ptrace_traceme(ptp); if (rc != 0) return rc; + tsp = smk_of_task(task_security(ptp)); smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, ptp); - sp = smk_of_current(); - tsp = smk_of_task(task_security(ptp)); - /* we won't log here, because rc can be overriden */ - rc = smk_access(tsp, sp, MAY_READWRITE, NULL); - if (rc != 0 && has_capability(ptp, CAP_MAC_OVERRIDE)) - rc = 0; - - smack_log(tsp, sp, MAY_READWRITE, rc, &ad); + rc = smk_curacc(tsp, MAY_READWRITE, &ad); return rc; } @@ -474,7 +512,7 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, { char *isp = smk_of_inode(inode); char *dsp = smk_of_inode(dir); - u32 may; + int may; if (name) { *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL); @@ -483,14 +521,17 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, } if (value) { - may = smk_access_entry(smk_of_current(), dsp); + rcu_read_lock(); + may = smk_access_entry(smk_of_current(), dsp, &smack_rule_list); + rcu_read_unlock(); /* * If the access rule allows transmutation and * the directory requests transmutation then * by all means transmute. */ - if (((may & MAY_TRANSMUTE) != 0) && smk_inode_transmutable(dir)) + if (may > 0 && ((may & MAY_TRANSMUTE) != 0) && + smk_inode_transmutable(dir)) isp = dsp; *value = kstrdup(isp, GFP_KERNEL); @@ -716,7 +757,8 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, if (strcmp(name, XATTR_NAME_SMACK) == 0 || strcmp(name, XATTR_NAME_SMACKIPIN) == 0 || strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 || - strcmp(name, XATTR_NAME_SMACKEXEC) == 0) { + strcmp(name, XATTR_NAME_SMACKEXEC) == 0 || + strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { if (!capable(CAP_MAC_ADMIN)) rc = -EPERM; /* @@ -773,6 +815,12 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, isp->smk_task = nsp; else isp->smk_task = smack_known_invalid.smk_known; + } else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { + nsp = smk_import(value, size); + if (nsp != NULL) + isp->smk_mmap = nsp; + else + isp->smk_mmap = smack_known_invalid.smk_known; } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) isp->smk_flags |= SMK_INODE_TRANSMUTE; @@ -815,7 +863,8 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) strcmp(name, XATTR_NAME_SMACKIPIN) == 0 || strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 || strcmp(name, XATTR_NAME_SMACKEXEC) == 0 || - strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) { + strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0 || + strcmp(name, XATTR_NAME_SMACKMMAP)) { if (!capable(CAP_MAC_ADMIN)) rc = -EPERM; } else @@ -829,6 +878,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) if (rc == 0) { isp = dentry->d_inode->i_security; isp->smk_task = NULL; + isp->smk_mmap = NULL; } return rc; @@ -1059,6 +1109,113 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd, return rc; } +/** + * smk_mmap_list_check - the mmap check + * @sub: subject label + * @obj: object label + * @access: access mode + * @local: the task specific rule list + * + * Returns 0 if acces is permitted, -EACCES otherwise + */ +static int smk_mmap_list_check(char *sub, char *obj, int access, + struct list_head *local) +{ + int may; + + /* + * If there is not a global rule that + * allows access say no. + */ + may = smk_access_entry(sub, obj, &smack_rule_list); + if (may == -ENOENT || (may & access) != access) + return -EACCES; + /* + * If there is a task local rule that + * denies access say no. + */ + may = smk_access_entry(sub, obj, local); + if (may != -ENOENT && (may & access) != access) + return -EACCES; + + return 0; +} + +/** + * smack_file_mmap : + * Check permissions for a mmap operation. The @file may be NULL, e.g. + * if mapping anonymous memory. + * @file contains the file structure for file to map (may be NULL). + * @reqprot contains the protection requested by the application. + * @prot contains the protection that will be applied by the kernel. + * @flags contains the operational flags. + * Return 0 if permission is granted. + */ +static int smack_file_mmap(struct file *file, + unsigned long reqprot, unsigned long prot, + unsigned long flags, unsigned long addr, + unsigned long addr_only) +{ + struct smack_rule *srp; + struct task_smack *tsp; + char *sp; + char *msmack; + struct inode_smack *isp; + struct dentry *dp; + int rc; + + /* do DAC check on address space usage */ + rc = cap_file_mmap(file, reqprot, prot, flags, addr, addr_only); + if (rc || addr_only) + return rc; + + if (file == NULL || file->f_dentry == NULL) + return 0; + + dp = file->f_dentry; + + if (dp->d_inode == NULL) + return 0; + + isp = dp->d_inode->i_security; + if (isp->smk_mmap == NULL) + return 0; + msmack = isp->smk_mmap; + + tsp = current_security(); + sp = smk_of_current(); + rc = 0; + + rcu_read_lock(); + /* + * For each Smack rule associated with the subject + * label verify that the SMACK64MMAP also has access + * to that rule's object label. + * + * Because neither of the labels comes + * from the networking code it is sufficient + * to compare pointers. + */ + list_for_each_entry_rcu(srp, &smack_rule_list, list) { + if (srp->smk_subject != sp) + continue; + /* + * Matching labels always allows access. + */ + if (msmack == srp->smk_object) + continue; + + rc = smk_mmap_list_check(msmack, srp->smk_object, + srp->smk_access, &tsp->smk_rules); + if (rc != 0) + break; + } + + rcu_read_unlock(); + + return rc; +} + /** * smack_file_set_fowner - set the file security blob value * @file: object in question @@ -1095,6 +1252,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, * struct fown_struct is never outside the context of a struct file */ file = container_of(fown, struct file, f_owner); + /* we don't log here as rc can be overriden */ rc = smk_access(file->f_security, tsp, MAY_WRITE, NULL); if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE)) @@ -1145,9 +1303,14 @@ static int smack_file_receive(struct file *file) */ static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp) { - cred->security = kzalloc(sizeof(struct task_smack), gfp); - if (cred->security == NULL) + struct task_smack *tsp; + + tsp = new_task_smack(NULL, NULL, gfp); + if (tsp == NULL) return -ENOMEM; + + cred->security = tsp; + return 0; } @@ -1156,13 +1319,24 @@ static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp) * smack_cred_free - "free" task-level security credentials * @cred: the credentials in question * - * Smack isn't using copies of blobs. Everyone - * points to an immutable list. The blobs never go away. - * There is no leak here. */ static void smack_cred_free(struct cred *cred) { - kfree(cred->security); + struct task_smack *tsp = cred->security; + struct smack_rule *rp; + struct list_head *l; + struct list_head *n; + + if (tsp == NULL) + return; + cred->security = NULL; + + list_for_each_safe(l, n, &tsp->smk_rules) { + rp = list_entry(l, struct smack_rule, list); + list_del(&rp->list); + kfree(rp); + } + kfree(tsp); } /** @@ -1178,13 +1352,16 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old, { struct task_smack *old_tsp = old->security; struct task_smack *new_tsp; + int rc; - new_tsp = kzalloc(sizeof(struct task_smack), gfp); + new_tsp = new_task_smack(old_tsp->smk_task, old_tsp->smk_task, gfp); if (new_tsp == NULL) return -ENOMEM; - new_tsp->smk_task = old_tsp->smk_task; - new_tsp->smk_forked = old_tsp->smk_task; + rc = smk_copy_rules(&new_tsp->smk_rules, &old_tsp->smk_rules, gfp); + if (rc != 0) + return rc; + new->security = new_tsp; return 0; } @@ -1203,6 +1380,11 @@ static void smack_cred_transfer(struct cred *new, const struct cred *old) new_tsp->smk_task = old_tsp->smk_task; new_tsp->smk_forked = old_tsp->smk_task; + mutex_init(&new_tsp->smk_rules_lock); + INIT_LIST_HEAD(&new_tsp->smk_rules); + + + /* cbs copy rule list */ } /** @@ -2419,6 +2601,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) } } isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp); + isp->smk_mmap = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp); dput(dp); break; @@ -2478,6 +2661,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) static int smack_setprocattr(struct task_struct *p, char *name, void *value, size_t size) { + int rc; struct task_smack *tsp; struct task_smack *oldtsp; struct cred *new; @@ -2513,13 +2697,16 @@ static int smack_setprocattr(struct task_struct *p, char *name, new = prepare_creds(); if (new == NULL) return -ENOMEM; - tsp = kzalloc(sizeof(struct task_smack), GFP_KERNEL); + + tsp = new_task_smack(newsmack, oldtsp->smk_forked, GFP_KERNEL); if (tsp == NULL) { kfree(new); return -ENOMEM; } - tsp->smk_task = newsmack; - tsp->smk_forked = oldtsp->smk_forked; + rc = smk_copy_rules(&tsp->smk_rules, &oldtsp->smk_rules, GFP_KERNEL); + if (rc != 0) + return rc; + new->security = tsp; commit_creds(new); return size; @@ -3221,6 +3408,7 @@ struct security_operations smack_ops = { .file_ioctl = smack_file_ioctl, .file_lock = smack_file_lock, .file_fcntl = smack_file_fcntl, + .file_mmap = smack_file_mmap, .file_set_fowner = smack_file_set_fowner, .file_send_sigiotask = smack_file_send_sigiotask, .file_receive = smack_file_receive, @@ -3334,23 +3522,20 @@ static __init int smack_init(void) struct cred *cred; struct task_smack *tsp; - tsp = kzalloc(sizeof(struct task_smack), GFP_KERNEL); + if (!security_module_enable(&smack_ops)) + return 0; + + tsp = new_task_smack(smack_known_floor.smk_known, + smack_known_floor.smk_known, GFP_KERNEL); if (tsp == NULL) return -ENOMEM; - if (!security_module_enable(&smack_ops)) { - kfree(tsp); - return 0; - } - printk(KERN_INFO "Smack: Initializing.\n"); /* * Set the security state for the initial task. */ cred = (struct cred *) current->cred; - tsp->smk_forked = smack_known_floor.smk_known; - tsp->smk_task = smack_known_floor.smk_known; cred->security = tsp; /* initialize the smack_know_list */ diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 362d5eda948b..90d1bbaaa6f3 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -43,6 +43,7 @@ enum smk_inos { SMK_NETLBLADDR = 8, /* single label hosts */ SMK_ONLYCAP = 9, /* the only "capable" label */ SMK_LOGGING = 10, /* logging */ + SMK_LOAD_SELF = 11, /* task specific rules */ }; /* @@ -135,104 +136,30 @@ static void smk_netlabel_audit_set(struct netlbl_audit *nap) #define SMK_NETLBLADDRMIN 9 #define SMK_NETLBLADDRMAX 42 -/* - * Seq_file read operations for /smack/load - */ - -static void *load_seq_start(struct seq_file *s, loff_t *pos) -{ - if (*pos == SEQ_READ_FINISHED) - return NULL; - if (list_empty(&smack_rule_list)) - return NULL; - return smack_rule_list.next; -} - -static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos) -{ - struct list_head *list = v; - - if (list_is_last(list, &smack_rule_list)) { - *pos = SEQ_READ_FINISHED; - return NULL; - } - return list->next; -} - -static int load_seq_show(struct seq_file *s, void *v) -{ - struct list_head *list = v; - struct smack_rule *srp = - list_entry(list, struct smack_rule, list); - - seq_printf(s, "%s %s", (char *)srp->smk_subject, - (char *)srp->smk_object); - - seq_putc(s, ' '); - - if (srp->smk_access & MAY_READ) - seq_putc(s, 'r'); - if (srp->smk_access & MAY_WRITE) - seq_putc(s, 'w'); - if (srp->smk_access & MAY_EXEC) - seq_putc(s, 'x'); - if (srp->smk_access & MAY_APPEND) - seq_putc(s, 'a'); - if (srp->smk_access & MAY_TRANSMUTE) - seq_putc(s, 't'); - if (srp->smk_access == 0) - seq_putc(s, '-'); - - seq_putc(s, '\n'); - - return 0; -} - -static void load_seq_stop(struct seq_file *s, void *v) -{ - /* No-op */ -} - -static const struct seq_operations load_seq_ops = { - .start = load_seq_start, - .next = load_seq_next, - .show = load_seq_show, - .stop = load_seq_stop, -}; - -/** - * smk_open_load - open() for /smack/load - * @inode: inode structure representing file - * @file: "load" file pointer - * - * For reading, use load_seq_* seq_file reading operations. - */ -static int smk_open_load(struct inode *inode, struct file *file) -{ - return seq_open(file, &load_seq_ops); -} - /** * smk_set_access - add a rule to the rule list * @srp: the new rule to add + * @rule_list: the list of rules + * @rule_lock: the rule list lock * * Looks through the current subject/object/access list for * the subject/object pair and replaces the access that was * there. If the pair isn't found add it with the specified * access. * + * Returns 1 if a rule was found to exist already, 0 if it is new * Returns 0 if nothing goes wrong or -ENOMEM if it fails * during the allocation of the new pair to add. */ -static int smk_set_access(struct smack_rule *srp) +static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, + struct mutex *rule_lock) { struct smack_rule *sp; - int ret = 0; - int found; - mutex_lock(&smack_list_lock); + int found = 0; - found = 0; - list_for_each_entry_rcu(sp, &smack_rule_list, list) { + mutex_lock(rule_lock); + + list_for_each_entry_rcu(sp, rule_list, list) { if (sp->smk_subject == srp->smk_subject && sp->smk_object == srp->smk_object) { found = 1; @@ -241,19 +168,21 @@ static int smk_set_access(struct smack_rule *srp) } } if (found == 0) - list_add_rcu(&srp->list, &smack_rule_list); + list_add_rcu(&srp->list, rule_list); - mutex_unlock(&smack_list_lock); + mutex_unlock(rule_lock); - return ret; + return found; } /** - * smk_write_load - write() for /smack/load + * smk_write_load_list - write() for any /smack/load * @file: file pointer, not actually used * @buf: where to get the data from * @count: bytes sent * @ppos: where to start - must be 0 + * @rule_list: the list of rules to write to + * @rule_lock: lock for the rule list * * Get one smack access rule from above. * The format is exactly: @@ -263,21 +192,19 @@ static int smk_set_access(struct smack_rule *srp) * * writes must be SMK_LABELLEN+SMK_LABELLEN+SMK_ACCESSLEN bytes. */ -static ssize_t smk_write_load(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) +static ssize_t smk_write_load_list(struct file *file, const char __user *buf, + size_t count, loff_t *ppos, + struct list_head *rule_list, + struct mutex *rule_lock) { struct smack_rule *rule; char *data; int rc = -EINVAL; /* - * Must have privilege. * No partial writes. * Enough data must be present. */ - if (!capable(CAP_MAC_ADMIN)) - return -EPERM; - if (*ppos != 0) return -EINVAL; /* @@ -372,11 +299,13 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, goto out_free_rule; } - rc = smk_set_access(rule); - - if (!rc) - rc = count; - goto out; + rc = count; + /* + * smk_set_access returns true if there was already a rule + * for the subject/object pair, and false if it was new. + */ + if (!smk_set_access(rule, rule_list, rule_lock)) + goto out; out_free_rule: kfree(rule); @@ -385,6 +314,108 @@ out: return rc; } + +/* + * Seq_file read operations for /smack/load + */ + +static void *load_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos == SEQ_READ_FINISHED) + return NULL; + if (list_empty(&smack_rule_list)) + return NULL; + return smack_rule_list.next; +} + +static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct list_head *list = v; + + if (list_is_last(list, &smack_rule_list)) { + *pos = SEQ_READ_FINISHED; + return NULL; + } + return list->next; +} + +static int load_seq_show(struct seq_file *s, void *v) +{ + struct list_head *list = v; + struct smack_rule *srp = + list_entry(list, struct smack_rule, list); + + seq_printf(s, "%s %s", (char *)srp->smk_subject, + (char *)srp->smk_object); + + seq_putc(s, ' '); + + if (srp->smk_access & MAY_READ) + seq_putc(s, 'r'); + if (srp->smk_access & MAY_WRITE) + seq_putc(s, 'w'); + if (srp->smk_access & MAY_EXEC) + seq_putc(s, 'x'); + if (srp->smk_access & MAY_APPEND) + seq_putc(s, 'a'); + if (srp->smk_access & MAY_TRANSMUTE) + seq_putc(s, 't'); + if (srp->smk_access == 0) + seq_putc(s, '-'); + + seq_putc(s, '\n'); + + return 0; +} + +static void load_seq_stop(struct seq_file *s, void *v) +{ + /* No-op */ +} + +static const struct seq_operations load_seq_ops = { + .start = load_seq_start, + .next = load_seq_next, + .show = load_seq_show, + .stop = load_seq_stop, +}; + +/** + * smk_open_load - open() for /smack/load + * @inode: inode structure representing file + * @file: "load" file pointer + * + * For reading, use load_seq_* seq_file reading operations. + */ +static int smk_open_load(struct inode *inode, struct file *file) +{ + return seq_open(file, &load_seq_ops); +} + +/** + * smk_write_load - write() for /smack/load + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start - must be 0 + * + */ +static ssize_t smk_write_load(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + + /* + * Must have privilege. + * No partial writes. + * Enough data must be present. + */ + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + + return smk_write_load_list(file, buf, count, ppos, &smack_rule_list, + &smack_list_lock); +} + static const struct file_operations smk_load_ops = { .open = smk_open_load, .read = seq_read, @@ -1288,6 +1319,112 @@ static const struct file_operations smk_logging_ops = { .write = smk_write_logging, .llseek = default_llseek, }; + +/* + * Seq_file read operations for /smack/load-self + */ + +static void *load_self_seq_start(struct seq_file *s, loff_t *pos) +{ + struct task_smack *tsp = current_security(); + + if (*pos == SEQ_READ_FINISHED) + return NULL; + if (list_empty(&tsp->smk_rules)) + return NULL; + return tsp->smk_rules.next; +} + +static void *load_self_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct task_smack *tsp = current_security(); + struct list_head *list = v; + + if (list_is_last(list, &tsp->smk_rules)) { + *pos = SEQ_READ_FINISHED; + return NULL; + } + return list->next; +} + +static int load_self_seq_show(struct seq_file *s, void *v) +{ + struct list_head *list = v; + struct smack_rule *srp = + list_entry(list, struct smack_rule, list); + + seq_printf(s, "%s %s", (char *)srp->smk_subject, + (char *)srp->smk_object); + + seq_putc(s, ' '); + + if (srp->smk_access & MAY_READ) + seq_putc(s, 'r'); + if (srp->smk_access & MAY_WRITE) + seq_putc(s, 'w'); + if (srp->smk_access & MAY_EXEC) + seq_putc(s, 'x'); + if (srp->smk_access & MAY_APPEND) + seq_putc(s, 'a'); + if (srp->smk_access & MAY_TRANSMUTE) + seq_putc(s, 't'); + if (srp->smk_access == 0) + seq_putc(s, '-'); + + seq_putc(s, '\n'); + + return 0; +} + +static void load_self_seq_stop(struct seq_file *s, void *v) +{ + /* No-op */ +} + +static const struct seq_operations load_self_seq_ops = { + .start = load_self_seq_start, + .next = load_self_seq_next, + .show = load_self_seq_show, + .stop = load_self_seq_stop, +}; + + +/** + * smk_open_load_self - open() for /smack/load-self + * @inode: inode structure representing file + * @file: "load" file pointer + * + * For reading, use load_seq_* seq_file reading operations. + */ +static int smk_open_load_self(struct inode *inode, struct file *file) +{ + return seq_open(file, &load_self_seq_ops); +} + +/** + * smk_write_load_self - write() for /smack/load-self + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start - must be 0 + * + */ +static ssize_t smk_write_load_self(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct task_smack *tsp = current_security(); + + return smk_write_load_list(file, buf, count, ppos, &tsp->smk_rules, + &tsp->smk_rules_lock); +} + +static const struct file_operations smk_load_self_ops = { + .open = smk_open_load_self, + .read = seq_read, + .llseek = seq_lseek, + .write = smk_write_load_self, + .release = seq_release, +}; /** * smk_fill_super - fill the /smackfs superblock * @sb: the empty superblock @@ -1304,23 +1441,26 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) struct inode *root_inode; static struct tree_descr smack_files[] = { - [SMK_LOAD] = - {"load", &smk_load_ops, S_IRUGO|S_IWUSR}, - [SMK_CIPSO] = - {"cipso", &smk_cipso_ops, S_IRUGO|S_IWUSR}, - [SMK_DOI] = - {"doi", &smk_doi_ops, S_IRUGO|S_IWUSR}, - [SMK_DIRECT] = - {"direct", &smk_direct_ops, S_IRUGO|S_IWUSR}, - [SMK_AMBIENT] = - {"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR}, - [SMK_NETLBLADDR] = - {"netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR}, - [SMK_ONLYCAP] = - {"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR}, - [SMK_LOGGING] = - {"logging", &smk_logging_ops, S_IRUGO|S_IWUSR}, - /* last one */ {""} + [SMK_LOAD] = { + "load", &smk_load_ops, S_IRUGO|S_IWUSR}, + [SMK_CIPSO] = { + "cipso", &smk_cipso_ops, S_IRUGO|S_IWUSR}, + [SMK_DOI] = { + "doi", &smk_doi_ops, S_IRUGO|S_IWUSR}, + [SMK_DIRECT] = { + "direct", &smk_direct_ops, S_IRUGO|S_IWUSR}, + [SMK_AMBIENT] = { + "ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR}, + [SMK_NETLBLADDR] = { + "netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR}, + [SMK_ONLYCAP] = { + "onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR}, + [SMK_LOGGING] = { + "logging", &smk_logging_ops, S_IRUGO|S_IWUSR}, + [SMK_LOAD_SELF] = { + "load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO}, + /* last one */ + {""} }; rc = simple_fill_super(sb, SMACK_MAGIC, smack_files); -- cgit v1.2.3 From 0260c1dccc6a1018f8cf2c4778dffb47fc5d1c4c Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 18 Jan 2011 07:33:09 +0100 Subject: netfilter: xtables: use __uXX guarded types for userspace exports Signed-off-by: Jan Engelhardt --- include/linux/netfilter_bridge/ebt_802_3.h | 24 ++++++++++++------------ include/linux/netfilter_bridge/ebt_among.h | 2 +- include/linux/netfilter_bridge/ebt_arp.h | 4 ++-- include/linux/netfilter_bridge/ebt_ip.h | 12 ++++++------ include/linux/netfilter_bridge/ebt_ip6.h | 16 ++++++++-------- include/linux/netfilter_bridge/ebt_limit.h | 8 ++++---- include/linux/netfilter_bridge/ebt_log.h | 6 +++--- include/linux/netfilter_bridge/ebt_mark_m.h | 4 ++-- include/linux/netfilter_bridge/ebt_nflog.h | 10 +++++----- include/linux/netfilter_bridge/ebt_pkttype.h | 4 ++-- include/linux/netfilter_bridge/ebt_stp.h | 24 ++++++++++++------------ include/linux/netfilter_bridge/ebt_ulog.h | 2 +- include/linux/netfilter_bridge/ebt_vlan.h | 8 ++++---- include/linux/netfilter_ipv4/ipt_CLUSTERIP.h | 14 +++++++------- include/linux/netfilter_ipv4/ipt_ECN.h | 6 +++--- include/linux/netfilter_ipv4/ipt_SAME.h | 6 +++--- include/linux/netfilter_ipv4/ipt_TTL.h | 4 ++-- include/linux/netfilter_ipv4/ipt_addrtype.h | 14 +++++++------- include/linux/netfilter_ipv4/ipt_ah.h | 4 ++-- include/linux/netfilter_ipv4/ipt_ecn.h | 8 ++++---- include/linux/netfilter_ipv4/ipt_ttl.h | 4 ++-- include/linux/netfilter_ipv6/ip6t_HL.h | 4 ++-- include/linux/netfilter_ipv6/ip6t_REJECT.h | 2 +- include/linux/netfilter_ipv6/ip6t_ah.h | 8 ++++---- include/linux/netfilter_ipv6/ip6t_frag.h | 8 ++++---- include/linux/netfilter_ipv6/ip6t_hl.h | 4 ++-- include/linux/netfilter_ipv6/ip6t_ipv6header.h | 6 +++--- include/linux/netfilter_ipv6/ip6t_mh.h | 4 ++-- include/linux/netfilter_ipv6/ip6t_opts.h | 10 +++++----- include/linux/netfilter_ipv6/ip6t_rt.h | 12 ++++++------ 30 files changed, 121 insertions(+), 121 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter_bridge/ebt_802_3.h b/include/linux/netfilter_bridge/ebt_802_3.h index c73ef0b18bdc..c427764f4444 100644 --- a/include/linux/netfilter_bridge/ebt_802_3.h +++ b/include/linux/netfilter_bridge/ebt_802_3.h @@ -24,24 +24,24 @@ /* ui has one byte ctrl, ni has two */ struct hdr_ui { - uint8_t dsap; - uint8_t ssap; - uint8_t ctrl; - uint8_t orig[3]; + __u8 dsap; + __u8 ssap; + __u8 ctrl; + __u8 orig[3]; __be16 type; }; struct hdr_ni { - uint8_t dsap; - uint8_t ssap; + __u8 dsap; + __u8 ssap; __be16 ctrl; - uint8_t orig[3]; + __u8 orig[3]; __be16 type; }; struct ebt_802_3_hdr { - uint8_t daddr[6]; - uint8_t saddr[6]; + __u8 daddr[6]; + __u8 saddr[6]; __be16 len; union { struct hdr_ui ui; @@ -59,10 +59,10 @@ static inline struct ebt_802_3_hdr *ebt_802_3_hdr(const struct sk_buff *skb) #endif struct ebt_802_3_info { - uint8_t sap; + __u8 sap; __be16 type; - uint8_t bitmask; - uint8_t invflags; + __u8 bitmask; + __u8 invflags; }; #endif diff --git a/include/linux/netfilter_bridge/ebt_among.h b/include/linux/netfilter_bridge/ebt_among.h index 0009558609a7..686c9619dbc0 100644 --- a/include/linux/netfilter_bridge/ebt_among.h +++ b/include/linux/netfilter_bridge/ebt_among.h @@ -30,7 +30,7 @@ */ struct ebt_mac_wormhash_tuple { - uint32_t cmp[2]; + __u32 cmp[2]; __be32 ip; }; diff --git a/include/linux/netfilter_bridge/ebt_arp.h b/include/linux/netfilter_bridge/ebt_arp.h index cbf4843b6b0f..e62b5af95869 100644 --- a/include/linux/netfilter_bridge/ebt_arp.h +++ b/include/linux/netfilter_bridge/ebt_arp.h @@ -27,8 +27,8 @@ struct ebt_arp_info unsigned char smmsk[ETH_ALEN]; unsigned char dmaddr[ETH_ALEN]; unsigned char dmmsk[ETH_ALEN]; - uint8_t bitmask; - uint8_t invflags; + __u8 bitmask; + __u8 invflags; }; #endif diff --git a/include/linux/netfilter_bridge/ebt_ip.h b/include/linux/netfilter_bridge/ebt_ip.h index 6a708fb92241..d99de58da2c7 100644 --- a/include/linux/netfilter_bridge/ebt_ip.h +++ b/include/linux/netfilter_bridge/ebt_ip.h @@ -31,12 +31,12 @@ struct ebt_ip_info { __be32 daddr; __be32 smsk; __be32 dmsk; - uint8_t tos; - uint8_t protocol; - uint8_t bitmask; - uint8_t invflags; - uint16_t sport[2]; - uint16_t dport[2]; + __u8 tos; + __u8 protocol; + __u8 bitmask; + __u8 invflags; + __u16 sport[2]; + __u16 dport[2]; }; #endif diff --git a/include/linux/netfilter_bridge/ebt_ip6.h b/include/linux/netfilter_bridge/ebt_ip6.h index 22af18a3c16b..998e9d5a6b60 100644 --- a/include/linux/netfilter_bridge/ebt_ip6.h +++ b/include/linux/netfilter_bridge/ebt_ip6.h @@ -31,17 +31,17 @@ struct ebt_ip6_info { struct in6_addr daddr; struct in6_addr smsk; struct in6_addr dmsk; - uint8_t tclass; - uint8_t protocol; - uint8_t bitmask; - uint8_t invflags; + __u8 tclass; + __u8 protocol; + __u8 bitmask; + __u8 invflags; union { - uint16_t sport[2]; - uint8_t icmpv6_type[2]; + __u16 sport[2]; + __u8 icmpv6_type[2]; }; union { - uint16_t dport[2]; - uint8_t icmpv6_code[2]; + __u16 dport[2]; + __u8 icmpv6_code[2]; }; }; diff --git a/include/linux/netfilter_bridge/ebt_limit.h b/include/linux/netfilter_bridge/ebt_limit.h index 4bf76b751676..721d51ffa513 100644 --- a/include/linux/netfilter_bridge/ebt_limit.h +++ b/include/linux/netfilter_bridge/ebt_limit.h @@ -10,13 +10,13 @@ seconds, or one every 59 hours. */ struct ebt_limit_info { - u_int32_t avg; /* Average secs between packets * scale */ - u_int32_t burst; /* Period multiplier for upper limit. */ + __u32 avg; /* Average secs between packets * scale */ + __u32 burst; /* Period multiplier for upper limit. */ /* Used internally by the kernel */ unsigned long prev; - u_int32_t credit; - u_int32_t credit_cap, cost; + __u32 credit; + __u32 credit_cap, cost; }; #endif diff --git a/include/linux/netfilter_bridge/ebt_log.h b/include/linux/netfilter_bridge/ebt_log.h index cc2cdfb764bc..564beb4946ea 100644 --- a/include/linux/netfilter_bridge/ebt_log.h +++ b/include/linux/netfilter_bridge/ebt_log.h @@ -10,9 +10,9 @@ #define EBT_LOG_WATCHER "log" struct ebt_log_info { - uint8_t loglevel; - uint8_t prefix[EBT_LOG_PREFIX_SIZE]; - uint32_t bitmask; + __u8 loglevel; + __u8 prefix[EBT_LOG_PREFIX_SIZE]; + __u32 bitmask; }; #endif diff --git a/include/linux/netfilter_bridge/ebt_mark_m.h b/include/linux/netfilter_bridge/ebt_mark_m.h index 9ceb10ec0ed6..97b96c4b8db4 100644 --- a/include/linux/netfilter_bridge/ebt_mark_m.h +++ b/include/linux/netfilter_bridge/ebt_mark_m.h @@ -6,8 +6,8 @@ #define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR) struct ebt_mark_m_info { unsigned long mark, mask; - uint8_t invert; - uint8_t bitmask; + __u8 invert; + __u8 bitmask; }; #define EBT_MARK_MATCH "mark_m" diff --git a/include/linux/netfilter_bridge/ebt_nflog.h b/include/linux/netfilter_bridge/ebt_nflog.h index 052817849b83..477315bc3537 100644 --- a/include/linux/netfilter_bridge/ebt_nflog.h +++ b/include/linux/netfilter_bridge/ebt_nflog.h @@ -10,11 +10,11 @@ #define EBT_NFLOG_DEFAULT_THRESHOLD 1 struct ebt_nflog_info { - u_int32_t len; - u_int16_t group; - u_int16_t threshold; - u_int16_t flags; - u_int16_t pad; + __u32 len; + __u16 group; + __u16 threshold; + __u16 flags; + __u16 pad; char prefix[EBT_NFLOG_PREFIX_SIZE]; }; diff --git a/include/linux/netfilter_bridge/ebt_pkttype.h b/include/linux/netfilter_bridge/ebt_pkttype.h index 51a799840931..7c0fb0fdcf14 100644 --- a/include/linux/netfilter_bridge/ebt_pkttype.h +++ b/include/linux/netfilter_bridge/ebt_pkttype.h @@ -2,8 +2,8 @@ #define __LINUX_BRIDGE_EBT_PKTTYPE_H struct ebt_pkttype_info { - uint8_t pkt_type; - uint8_t invert; + __u8 pkt_type; + __u8 invert; }; #define EBT_PKTTYPE_MATCH "pkttype" diff --git a/include/linux/netfilter_bridge/ebt_stp.h b/include/linux/netfilter_bridge/ebt_stp.h index e503a0aa2728..13a0bd49a92a 100644 --- a/include/linux/netfilter_bridge/ebt_stp.h +++ b/include/linux/netfilter_bridge/ebt_stp.h @@ -21,24 +21,24 @@ #define EBT_STP_MATCH "stp" struct ebt_stp_config_info { - uint8_t flags; - uint16_t root_priol, root_priou; + __u8 flags; + __u16 root_priol, root_priou; char root_addr[6], root_addrmsk[6]; - uint32_t root_costl, root_costu; - uint16_t sender_priol, sender_priou; + __u32 root_costl, root_costu; + __u16 sender_priol, sender_priou; char sender_addr[6], sender_addrmsk[6]; - uint16_t portl, portu; - uint16_t msg_agel, msg_ageu; - uint16_t max_agel, max_ageu; - uint16_t hello_timel, hello_timeu; - uint16_t forward_delayl, forward_delayu; + __u16 portl, portu; + __u16 msg_agel, msg_ageu; + __u16 max_agel, max_ageu; + __u16 hello_timel, hello_timeu; + __u16 forward_delayl, forward_delayu; }; struct ebt_stp_info { - uint8_t type; + __u8 type; struct ebt_stp_config_info config; - uint16_t bitmask; - uint16_t invflags; + __u16 bitmask; + __u16 invflags; }; #endif diff --git a/include/linux/netfilter_bridge/ebt_ulog.h b/include/linux/netfilter_bridge/ebt_ulog.h index b677e2671541..de35a51a7e46 100644 --- a/include/linux/netfilter_bridge/ebt_ulog.h +++ b/include/linux/netfilter_bridge/ebt_ulog.h @@ -10,7 +10,7 @@ #define EBT_ULOG_VERSION 1 struct ebt_ulog_info { - uint32_t nlgroup; + __u32 nlgroup; unsigned int cprange; unsigned int qthreshold; char prefix[EBT_ULOG_PREFIX_LEN]; diff --git a/include/linux/netfilter_bridge/ebt_vlan.h b/include/linux/netfilter_bridge/ebt_vlan.h index 1d98be4031e7..48dffc1dad36 100644 --- a/include/linux/netfilter_bridge/ebt_vlan.h +++ b/include/linux/netfilter_bridge/ebt_vlan.h @@ -8,12 +8,12 @@ #define EBT_VLAN_MATCH "vlan" struct ebt_vlan_info { - uint16_t id; /* VLAN ID {1-4095} */ - uint8_t prio; /* VLAN User Priority {0-7} */ + __u16 id; /* VLAN ID {1-4095} */ + __u8 prio; /* VLAN User Priority {0-7} */ __be16 encap; /* VLAN Encapsulated frame code {0-65535} */ - uint8_t bitmask; /* Args bitmask bit 1=1 - ID arg, + __u8 bitmask; /* Args bitmask bit 1=1 - ID arg, bit 2=1 User-Priority arg, bit 3=1 encap*/ - uint8_t invflags; /* Inverse bitmask bit 1=1 - inversed ID arg, + __u8 invflags; /* Inverse bitmask bit 1=1 - inversed ID arg, bit 2=1 - inversed Pirority arg */ }; diff --git a/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h b/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h index e5a3687c8a72..3114f06939ef 100644 --- a/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h +++ b/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h @@ -17,15 +17,15 @@ struct clusterip_config; struct ipt_clusterip_tgt_info { - u_int32_t flags; + __u32 flags; /* only relevant for new ones */ - u_int8_t clustermac[6]; - u_int16_t num_total_nodes; - u_int16_t num_local_nodes; - u_int16_t local_nodes[CLUSTERIP_MAX_NODES]; - u_int32_t hash_mode; - u_int32_t hash_initval; + __u8 clustermac[6]; + __u16 num_total_nodes; + __u16 num_local_nodes; + __u16 local_nodes[CLUSTERIP_MAX_NODES]; + __u32 hash_mode; + __u32 hash_initval; /* Used internally by the kernel */ struct clusterip_config *config; diff --git a/include/linux/netfilter_ipv4/ipt_ECN.h b/include/linux/netfilter_ipv4/ipt_ECN.h index 7ca45918ab8e..c6e3e01b75e0 100644 --- a/include/linux/netfilter_ipv4/ipt_ECN.h +++ b/include/linux/netfilter_ipv4/ipt_ECN.h @@ -19,11 +19,11 @@ #define IPT_ECN_OP_MASK 0xce struct ipt_ECN_info { - u_int8_t operation; /* bitset of operations */ - u_int8_t ip_ect; /* ECT codepoint of IPv4 header, pre-shifted */ + __u8 operation; /* bitset of operations */ + __u8 ip_ect; /* ECT codepoint of IPv4 header, pre-shifted */ union { struct { - u_int8_t ece:1, cwr:1; /* TCP ECT bits */ + __u8 ece:1, cwr:1; /* TCP ECT bits */ } tcp; } proto; }; diff --git a/include/linux/netfilter_ipv4/ipt_SAME.h b/include/linux/netfilter_ipv4/ipt_SAME.h index 2529660c5b38..fa0ebeca5d95 100644 --- a/include/linux/netfilter_ipv4/ipt_SAME.h +++ b/include/linux/netfilter_ipv4/ipt_SAME.h @@ -7,9 +7,9 @@ struct ipt_same_info { unsigned char info; - u_int32_t rangesize; - u_int32_t ipnum; - u_int32_t *iparray; + __u32 rangesize; + __u32 ipnum; + __u32 *iparray; /* hangs off end. */ struct nf_nat_range range[IPT_SAME_MAX_RANGE]; diff --git a/include/linux/netfilter_ipv4/ipt_TTL.h b/include/linux/netfilter_ipv4/ipt_TTL.h index ee6611edc112..f6250e422d5e 100644 --- a/include/linux/netfilter_ipv4/ipt_TTL.h +++ b/include/linux/netfilter_ipv4/ipt_TTL.h @@ -13,8 +13,8 @@ enum { #define IPT_TTL_MAXMODE IPT_TTL_DEC struct ipt_TTL_info { - u_int8_t mode; - u_int8_t ttl; + __u8 mode; + __u8 ttl; }; diff --git a/include/linux/netfilter_ipv4/ipt_addrtype.h b/include/linux/netfilter_ipv4/ipt_addrtype.h index 446de6aef983..f29c3cfcc240 100644 --- a/include/linux/netfilter_ipv4/ipt_addrtype.h +++ b/include/linux/netfilter_ipv4/ipt_addrtype.h @@ -9,17 +9,17 @@ enum { }; struct ipt_addrtype_info_v1 { - u_int16_t source; /* source-type mask */ - u_int16_t dest; /* dest-type mask */ - u_int32_t flags; + __u16 source; /* source-type mask */ + __u16 dest; /* dest-type mask */ + __u32 flags; }; /* revision 0 */ struct ipt_addrtype_info { - u_int16_t source; /* source-type mask */ - u_int16_t dest; /* dest-type mask */ - u_int32_t invert_source; - u_int32_t invert_dest; + __u16 source; /* source-type mask */ + __u16 dest; /* dest-type mask */ + __u32 invert_source; + __u32 invert_dest; }; #endif diff --git a/include/linux/netfilter_ipv4/ipt_ah.h b/include/linux/netfilter_ipv4/ipt_ah.h index 2e555b4d05e3..8fea283ee62a 100644 --- a/include/linux/netfilter_ipv4/ipt_ah.h +++ b/include/linux/netfilter_ipv4/ipt_ah.h @@ -2,8 +2,8 @@ #define _IPT_AH_H struct ipt_ah { - u_int32_t spis[2]; /* Security Parameter Index */ - u_int8_t invflags; /* Inverse flags */ + __u32 spis[2]; /* Security Parameter Index */ + __u8 invflags; /* Inverse flags */ }; diff --git a/include/linux/netfilter_ipv4/ipt_ecn.h b/include/linux/netfilter_ipv4/ipt_ecn.h index 9945baa4ccd7..78b98aa8784d 100644 --- a/include/linux/netfilter_ipv4/ipt_ecn.h +++ b/include/linux/netfilter_ipv4/ipt_ecn.h @@ -20,12 +20,12 @@ /* match info */ struct ipt_ecn_info { - u_int8_t operation; - u_int8_t invert; - u_int8_t ip_ect; + __u8 operation; + __u8 invert; + __u8 ip_ect; union { struct { - u_int8_t ect; + __u8 ect; } tcp; } proto; }; diff --git a/include/linux/netfilter_ipv4/ipt_ttl.h b/include/linux/netfilter_ipv4/ipt_ttl.h index ee24fd86a3aa..93d9a06689a3 100644 --- a/include/linux/netfilter_ipv4/ipt_ttl.h +++ b/include/linux/netfilter_ipv4/ipt_ttl.h @@ -13,8 +13,8 @@ enum { struct ipt_ttl_info { - u_int8_t mode; - u_int8_t ttl; + __u8 mode; + __u8 ttl; }; diff --git a/include/linux/netfilter_ipv6/ip6t_HL.h b/include/linux/netfilter_ipv6/ip6t_HL.h index afb7813d45ab..81cdaf0480e3 100644 --- a/include/linux/netfilter_ipv6/ip6t_HL.h +++ b/include/linux/netfilter_ipv6/ip6t_HL.h @@ -14,8 +14,8 @@ enum { #define IP6T_HL_MAXMODE IP6T_HL_DEC struct ip6t_HL_info { - u_int8_t mode; - u_int8_t hop_limit; + __u8 mode; + __u8 hop_limit; }; diff --git a/include/linux/netfilter_ipv6/ip6t_REJECT.h b/include/linux/netfilter_ipv6/ip6t_REJECT.h index 6be6504162bb..b999aa4e5969 100644 --- a/include/linux/netfilter_ipv6/ip6t_REJECT.h +++ b/include/linux/netfilter_ipv6/ip6t_REJECT.h @@ -12,7 +12,7 @@ enum ip6t_reject_with { }; struct ip6t_reject_info { - u_int32_t with; /* reject type */ + __u32 with; /* reject type */ }; #endif /*_IP6T_REJECT_H*/ diff --git a/include/linux/netfilter_ipv6/ip6t_ah.h b/include/linux/netfilter_ipv6/ip6t_ah.h index 17a745cfb2c7..a602c165edd1 100644 --- a/include/linux/netfilter_ipv6/ip6t_ah.h +++ b/include/linux/netfilter_ipv6/ip6t_ah.h @@ -2,10 +2,10 @@ #define _IP6T_AH_H struct ip6t_ah { - u_int32_t spis[2]; /* Security Parameter Index */ - u_int32_t hdrlen; /* Header Length */ - u_int8_t hdrres; /* Test of the Reserved Filed */ - u_int8_t invflags; /* Inverse flags */ + __u32 spis[2]; /* Security Parameter Index */ + __u32 hdrlen; /* Header Length */ + __u8 hdrres; /* Test of the Reserved Filed */ + __u8 invflags; /* Inverse flags */ }; #define IP6T_AH_SPI 0x01 diff --git a/include/linux/netfilter_ipv6/ip6t_frag.h b/include/linux/netfilter_ipv6/ip6t_frag.h index 3724d0850920..538b31ef5e3d 100644 --- a/include/linux/netfilter_ipv6/ip6t_frag.h +++ b/include/linux/netfilter_ipv6/ip6t_frag.h @@ -2,10 +2,10 @@ #define _IP6T_FRAG_H struct ip6t_frag { - u_int32_t ids[2]; /* Security Parameter Index */ - u_int32_t hdrlen; /* Header Length */ - u_int8_t flags; /* */ - u_int8_t invflags; /* Inverse flags */ + __u32 ids[2]; /* Security Parameter Index */ + __u32 hdrlen; /* Header Length */ + __u8 flags; /* */ + __u8 invflags; /* Inverse flags */ }; #define IP6T_FRAG_IDS 0x01 diff --git a/include/linux/netfilter_ipv6/ip6t_hl.h b/include/linux/netfilter_ipv6/ip6t_hl.h index 5ef91b8319a8..c6fddcb971da 100644 --- a/include/linux/netfilter_ipv6/ip6t_hl.h +++ b/include/linux/netfilter_ipv6/ip6t_hl.h @@ -14,8 +14,8 @@ enum { struct ip6t_hl_info { - u_int8_t mode; - u_int8_t hop_limit; + __u8 mode; + __u8 hop_limit; }; diff --git a/include/linux/netfilter_ipv6/ip6t_ipv6header.h b/include/linux/netfilter_ipv6/ip6t_ipv6header.h index 01dfd445596a..73d53bd3ff62 100644 --- a/include/linux/netfilter_ipv6/ip6t_ipv6header.h +++ b/include/linux/netfilter_ipv6/ip6t_ipv6header.h @@ -9,9 +9,9 @@ on whether they contain certain headers */ #define __IPV6HEADER_H struct ip6t_ipv6header_info { - u_int8_t matchflags; - u_int8_t invflags; - u_int8_t modeflag; + __u8 matchflags; + __u8 invflags; + __u8 modeflag; }; #define MASK_HOPOPTS 128 diff --git a/include/linux/netfilter_ipv6/ip6t_mh.h b/include/linux/netfilter_ipv6/ip6t_mh.h index 18549bca2d1f..98c8cf685eea 100644 --- a/include/linux/netfilter_ipv6/ip6t_mh.h +++ b/include/linux/netfilter_ipv6/ip6t_mh.h @@ -3,8 +3,8 @@ /* MH matching stuff */ struct ip6t_mh { - u_int8_t types[2]; /* MH type range */ - u_int8_t invflags; /* Inverse flags */ + __u8 types[2]; /* MH type range */ + __u8 invflags; /* Inverse flags */ }; /* Values for "invflags" field in struct ip6t_mh. */ diff --git a/include/linux/netfilter_ipv6/ip6t_opts.h b/include/linux/netfilter_ipv6/ip6t_opts.h index 62d89bcd9f9c..405d309cd741 100644 --- a/include/linux/netfilter_ipv6/ip6t_opts.h +++ b/include/linux/netfilter_ipv6/ip6t_opts.h @@ -4,11 +4,11 @@ #define IP6T_OPTS_OPTSNR 16 struct ip6t_opts { - u_int32_t hdrlen; /* Header Length */ - u_int8_t flags; /* */ - u_int8_t invflags; /* Inverse flags */ - u_int16_t opts[IP6T_OPTS_OPTSNR]; /* opts */ - u_int8_t optsnr; /* Nr of OPts */ + __u32 hdrlen; /* Header Length */ + __u8 flags; /* */ + __u8 invflags; /* Inverse flags */ + __u16 opts[IP6T_OPTS_OPTSNR]; /* opts */ + __u8 optsnr; /* Nr of OPts */ }; #define IP6T_OPTS_LEN 0x01 diff --git a/include/linux/netfilter_ipv6/ip6t_rt.h b/include/linux/netfilter_ipv6/ip6t_rt.h index ab91bfd2cd00..e8dad20acd37 100644 --- a/include/linux/netfilter_ipv6/ip6t_rt.h +++ b/include/linux/netfilter_ipv6/ip6t_rt.h @@ -6,13 +6,13 @@ #define IP6T_RT_HOPS 16 struct ip6t_rt { - u_int32_t rt_type; /* Routing Type */ - u_int32_t segsleft[2]; /* Segments Left */ - u_int32_t hdrlen; /* Header Length */ - u_int8_t flags; /* */ - u_int8_t invflags; /* Inverse flags */ + __u32 rt_type; /* Routing Type */ + __u32 segsleft[2]; /* Segments Left */ + __u32 hdrlen; /* Header Length */ + __u8 flags; /* */ + __u8 invflags; /* Inverse flags */ struct in6_addr addrs[IP6T_RT_HOPS]; /* Hops */ - u_int8_t addrnr; /* Nr of Addresses */ + __u8 addrnr; /* Nr of Addresses */ }; #define IP6T_RT_TYP 0x01 -- cgit v1.2.3 From 0b8ad876275c74e4bfb6ec3150793f3c0ecfcee2 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 18 Jan 2011 11:23:06 +0100 Subject: netfilter: xtables: add missing header files to export list Signed-off-by: Jan Engelhardt --- include/linux/netfilter/Kbuild | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index 9f11fbc377e2..fc4e0aa805a2 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild @@ -56,6 +56,8 @@ header-y += xt_rateest.h header-y += xt_realm.h header-y += xt_recent.h header-y += xt_sctp.h +header-y += xt_secmark.h +header-y += xt_socket.h header-y += xt_state.h header-y += xt_statistic.h header-y += xt_string.h -- cgit v1.2.3 From a7c2f4d7daf9bbea362763fa7353b1862a2487ad Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Tue, 18 Jan 2011 15:02:48 +0100 Subject: netfilter: nf_nat: fix conversion to non-atomic bit ops My previous patch (netfilter: nf_nat: don't use atomic bit operation) made a mistake when converting atomic_set to a normal bit 'or'. IPS_*_BIT should be replaced with IPS_*. Signed-off-by: Changli Gao Cc: Tim Gardner Cc: Eric Dumazet Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_nat_core.h | 4 ++-- net/ipv4/netfilter/nf_nat_core.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_nat_core.h b/include/net/netfilter/nf_nat_core.h index 5aec85c29979..3dc7b98effeb 100644 --- a/include/net/netfilter/nf_nat_core.h +++ b/include/net/netfilter/nf_nat_core.h @@ -21,9 +21,9 @@ static inline int nf_nat_initialized(struct nf_conn *ct, enum nf_nat_manip_type manip) { if (manip == IP_NAT_MANIP_SRC) - return ct->status & IPS_SRC_NAT_DONE_BIT; + return ct->status & IPS_SRC_NAT_DONE; else - return ct->status & IPS_DST_NAT_DONE_BIT; + return ct->status & IPS_DST_NAT_DONE; } struct nlattr; diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c index 6972ceee99c6..3002c0492fb0 100644 --- a/net/ipv4/netfilter/nf_nat_core.c +++ b/net/ipv4/netfilter/nf_nat_core.c @@ -323,9 +323,9 @@ nf_nat_setup_info(struct nf_conn *ct, /* It's done. */ if (maniptype == IP_NAT_MANIP_DST) - ct->status |= IPS_DST_NAT_DONE_BIT; + ct->status |= IPS_DST_NAT_DONE; else - ct->status |= IPS_SRC_NAT_DONE_BIT; + ct->status |= IPS_SRC_NAT_DONE; return NF_ACCEPT; } -- cgit v1.2.3 From f615df76ed862b7d3927ec5f55b805ca19be29d9 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 18 Jan 2011 15:52:14 +0100 Subject: netfilter: reduce NF_VERDICT_MASK to 0xff NF_VERDICT_MASK is currently 0xffff. This is because the upper 16 bits are used to store errno (for NF_DROP) or the queue number (NF_QUEUE verdict). As there are up to 0xffff different queues available, there is no more room to store additional flags. At the moment there are only 6 different verdicts, i.e. we can reduce NF_VERDICT_MASK to 0xff to allow storing additional flags in the 0xff00 space. NF_VERDICT_BITS would then be reduced to 8, but because the value is exported to userspace, this might cause breakage; e.g.: e.g. 'queuenr = (1 << NF_VERDICT_BITS) | NF_QUEUE' would now break. Thus, remove NF_VERDICT_BITS usage in the kernel and move the old value to the 'userspace compat' section. Signed-off-by: Florian Westphal Signed-off-by: Patrick McHardy --- include/linux/netfilter.h | 20 +++++++++++++++----- net/netfilter/core.c | 4 ++-- net/netfilter/nf_queue.c | 2 +- 3 files changed, 18 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 0ab7ca787b22..78b73cc10216 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -24,16 +24,19 @@ #define NF_MAX_VERDICT NF_STOP /* we overload the higher bits for encoding auxiliary data such as the queue - * number. Not nice, but better than additional function arguments. */ -#define NF_VERDICT_MASK 0x0000ffff -#define NF_VERDICT_BITS 16 + * number or errno values. Not nice, but better than additional function + * arguments. */ +#define NF_VERDICT_MASK 0x000000ff + +/* extra verdict flags have mask 0x0000ff00 */ +/* queue number (NF_QUEUE) or errno (NF_DROP) */ #define NF_VERDICT_QMASK 0xffff0000 #define NF_VERDICT_QBITS 16 -#define NF_QUEUE_NR(x) ((((x) << NF_VERDICT_BITS) & NF_VERDICT_QMASK) | NF_QUEUE) +#define NF_QUEUE_NR(x) ((((x) << 16) & NF_VERDICT_QMASK) | NF_QUEUE) -#define NF_DROP_ERR(x) (((-x) << NF_VERDICT_BITS) | NF_DROP) +#define NF_DROP_ERR(x) (((-x) << 16) | NF_DROP) /* only for userspace compatibility */ #ifndef __KERNEL__ @@ -41,6 +44,9 @@ <= 0x2000 is used for protocol-flags. */ #define NFC_UNKNOWN 0x4000 #define NFC_ALTERED 0x8000 + +/* NF_VERDICT_BITS should be 8 now, but userspace might break if this changes */ +#define NF_VERDICT_BITS 16 #endif enum nf_inet_hooks { @@ -72,6 +78,10 @@ union nf_inet_addr { #ifdef __KERNEL__ #ifdef CONFIG_NETFILTER +static inline int NF_DROP_GETERR(int verdict) +{ + return -(verdict >> NF_VERDICT_QBITS); +} static inline int nf_inet_addr_cmp(const union nf_inet_addr *a1, const union nf_inet_addr *a2) diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 0c5b796ef527..4d88e45b978e 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -175,12 +175,12 @@ next_hook: ret = 1; } else if ((verdict & NF_VERDICT_MASK) == NF_DROP) { kfree_skb(skb); - ret = -(verdict >> NF_VERDICT_BITS); + ret = NF_DROP_GETERR(verdict); if (ret == 0) ret = -EPERM; } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) { ret = nf_queue(skb, elem, pf, hook, indev, outdev, okfn, - verdict >> NF_VERDICT_BITS); + verdict >> NF_VERDICT_QBITS); if (ret < 0) { if (ret == -ECANCELED) goto next_hook; diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 5c4b730a2e68..ce1150d4a3f2 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -299,7 +299,7 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) case NF_QUEUE: err = __nf_queue(skb, elem, entry->pf, entry->hook, entry->indev, entry->outdev, entry->okfn, - verdict >> NF_VERDICT_BITS); + verdict >> NF_VERDICT_QBITS); if (err < 0) { if (err == -ECANCELED) goto next_hook; -- cgit v1.2.3 From 94b27cc36123069966616670c3653cd6873babe9 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 18 Jan 2011 16:08:30 +0100 Subject: netfilter: allow NFQUEUE bypass if no listener is available If an skb is to be NF_QUEUE'd, but no program has opened the queue, the packet is dropped. This adds a v2 target revision of xt_NFQUEUE that allows packets to continue through the ruleset instead. Because the actual queueing happens outside of the target context, the 'bypass' flag has to be communicated back to the netfilter core. Unfortunately the only choice to do this without adding a new function argument is to use the target function return value (i.e. the verdict). In the NF_QUEUE case, the upper 16bit already contain the queue number to use. The previous patch reduced NF_VERDICT_MASK to 0xff, i.e. we now have extra room for a new flag. If a hook issued a NF_QUEUE verdict, then the netfilter core will continue packet processing if the queueing hook returns -ESRCH (== "this queue does not exist") and the new NF_VERDICT_FLAG_QUEUE_BYPASS flag is set in the verdict value. Note: If the queue exists, but userspace does not consume packets fast enough, the skb will still be dropped. Signed-off-by: Florian Westphal Signed-off-by: Patrick McHardy --- include/linux/netfilter.h | 1 + include/linux/netfilter/xt_NFQUEUE.h | 6 ++++++ net/netfilter/core.c | 3 +++ net/netfilter/nf_queue.c | 7 ++++++- net/netfilter/xt_NFQUEUE.c | 28 +++++++++++++++++++++++++--- 5 files changed, 41 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 78b73cc10216..eeec00abb664 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -29,6 +29,7 @@ #define NF_VERDICT_MASK 0x000000ff /* extra verdict flags have mask 0x0000ff00 */ +#define NF_VERDICT_FLAG_QUEUE_BYPASS 0x00008000 /* queue number (NF_QUEUE) or errno (NF_DROP) */ #define NF_VERDICT_QMASK 0xffff0000 diff --git a/include/linux/netfilter/xt_NFQUEUE.h b/include/linux/netfilter/xt_NFQUEUE.h index 2584f4a777de..9eafdbbb401c 100644 --- a/include/linux/netfilter/xt_NFQUEUE.h +++ b/include/linux/netfilter/xt_NFQUEUE.h @@ -20,4 +20,10 @@ struct xt_NFQ_info_v1 { __u16 queues_total; }; +struct xt_NFQ_info_v2 { + __u16 queuenum; + __u16 queues_total; + __u16 bypass; +}; + #endif /* _XT_NFQ_TARGET_H */ diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 4d88e45b978e..1e00bf7d27c5 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -184,6 +184,9 @@ next_hook: if (ret < 0) { if (ret == -ECANCELED) goto next_hook; + if (ret == -ESRCH && + (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) + goto next_hook; kfree_skb(skb); } ret = 0; diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index ce1150d4a3f2..5ab22e2bbd7d 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -138,8 +138,10 @@ static int __nf_queue(struct sk_buff *skb, rcu_read_lock(); qh = rcu_dereference(queue_handler[pf]); - if (!qh) + if (!qh) { + status = -ESRCH; goto err_unlock; + } afinfo = nf_get_afinfo(pf); if (!afinfo) @@ -303,6 +305,9 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) if (err < 0) { if (err == -ECANCELED) goto next_hook; + if (err == -ESRCH && + (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) + goto next_hook; kfree_skb(skb); } break; diff --git a/net/netfilter/xt_NFQUEUE.c b/net/netfilter/xt_NFQUEUE.c index 39627706aac6..d4f4b5d66b20 100644 --- a/net/netfilter/xt_NFQUEUE.c +++ b/net/netfilter/xt_NFQUEUE.c @@ -83,9 +83,20 @@ nfqueue_tg_v1(struct sk_buff *skb, const struct xt_action_param *par) return NF_QUEUE_NR(queue); } -static int nfqueue_tg_v1_check(const struct xt_tgchk_param *par) +static unsigned int +nfqueue_tg_v2(struct sk_buff *skb, const struct xt_action_param *par) { - const struct xt_NFQ_info_v1 *info = par->targinfo; + const struct xt_NFQ_info_v2 *info = par->targinfo; + unsigned int ret = nfqueue_tg_v1(skb, par); + + if (info->bypass) + ret |= NF_VERDICT_FLAG_QUEUE_BYPASS; + return ret; +} + +static int nfqueue_tg_check(const struct xt_tgchk_param *par) +{ + const struct xt_NFQ_info_v2 *info = par->targinfo; u32 maxid; if (unlikely(!rnd_inited)) { @@ -102,6 +113,8 @@ static int nfqueue_tg_v1_check(const struct xt_tgchk_param *par) info->queues_total, maxid); return -ERANGE; } + if (par->target->revision == 2 && info->bypass > 1) + return -EINVAL; return 0; } @@ -117,11 +130,20 @@ static struct xt_target nfqueue_tg_reg[] __read_mostly = { .name = "NFQUEUE", .revision = 1, .family = NFPROTO_UNSPEC, - .checkentry = nfqueue_tg_v1_check, + .checkentry = nfqueue_tg_check, .target = nfqueue_tg_v1, .targetsize = sizeof(struct xt_NFQ_info_v1), .me = THIS_MODULE, }, + { + .name = "NFQUEUE", + .revision = 2, + .family = NFPROTO_UNSPEC, + .checkentry = nfqueue_tg_check, + .target = nfqueue_tg_v2, + .targetsize = sizeof(struct xt_NFQ_info_v2), + .me = THIS_MODULE, + }, }; static int __init nfqueue_tg_init(void) -- cgit v1.2.3 From 93557f53e1fbd9e2b6574ab0a9b5852628fde9e3 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 18 Jan 2011 18:12:24 +0100 Subject: netfilter: nf_conntrack: nf_conntrack snmp helper Adding support for SNMP broadcast connection tracking. The SNMP broadcast requests are now paired with the SNMP responses. Thus allowing using SNMP broadcasts with firewall enabled. Please refer to the following conversation: http://marc.info/?l=netfilter-devel&m=125992205006600&w=2 Patrick McHardy wrote: > > The best solution would be to add generic broadcast tracking, the > > use of expectations for this is a bit of abuse. > > The second best choice I guess would be to move the help() function > > to a shared module and generalize it so it can be used for both. This patch implements the "second best choice". Since the netbios-ns conntrack module uses the same helper functionality as the snmp, only one helper function is added for both snmp and netbios-ns modules into the new object - nf_conntrack_broadcast. Signed-off-by: Jiri Olsa Signed-off-by: Patrick McHardy --- include/linux/netfilter/nf_conntrack_snmp.h | 9 ++++ include/net/netfilter/nf_conntrack_helper.h | 6 +++ net/ipv4/netfilter/Kconfig | 3 +- net/ipv4/netfilter/nf_nat_snmp_basic.c | 9 ++-- net/netfilter/Kconfig | 19 +++++++ net/netfilter/Makefile | 2 + net/netfilter/nf_conntrack_broadcast.c | 82 +++++++++++++++++++++++++++++ net/netfilter/nf_conntrack_netbios_ns.c | 74 ++++---------------------- net/netfilter/nf_conntrack_snmp.c | 77 +++++++++++++++++++++++++++ 9 files changed, 211 insertions(+), 70 deletions(-) create mode 100644 include/linux/netfilter/nf_conntrack_snmp.h create mode 100644 net/netfilter/nf_conntrack_broadcast.c create mode 100644 net/netfilter/nf_conntrack_snmp.c (limited to 'include') diff --git a/include/linux/netfilter/nf_conntrack_snmp.h b/include/linux/netfilter/nf_conntrack_snmp.h new file mode 100644 index 000000000000..064bc63a5346 --- /dev/null +++ b/include/linux/netfilter/nf_conntrack_snmp.h @@ -0,0 +1,9 @@ +#ifndef _NF_CONNTRACK_SNMP_H +#define _NF_CONNTRACK_SNMP_H + +extern int (*nf_nat_snmp_hook)(struct sk_buff *skb, + unsigned int protoff, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo); + +#endif /* _NF_CONNTRACK_SNMP_H */ diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 32c305dbdab6..f1c1311adc2c 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -63,4 +63,10 @@ static inline struct nf_conn_help *nfct_help(const struct nf_conn *ct) extern int nf_conntrack_helper_init(void); extern void nf_conntrack_helper_fini(void); +extern int nf_conntrack_broadcast_help(struct sk_buff *skb, + unsigned int protoff, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int timeout); + #endif /*_NF_CONNTRACK_HELPER_H*/ diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index babd1a2bae5f..f926a310075d 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -206,8 +206,9 @@ config IP_NF_TARGET_REDIRECT config NF_NAT_SNMP_BASIC tristate "Basic SNMP-ALG support" - depends on NF_NAT + depends on NF_CONNTRACK_SNMP && NF_NAT depends on NETFILTER_ADVANCED + default NF_NAT && NF_CONNTRACK_SNMP ---help--- This module implements an Application Layer Gateway (ALG) for diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic.c b/net/ipv4/netfilter/nf_nat_snmp_basic.c index ee5f419d0a56..8812a02078ab 100644 --- a/net/ipv4/netfilter/nf_nat_snmp_basic.c +++ b/net/ipv4/netfilter/nf_nat_snmp_basic.c @@ -54,6 +54,7 @@ #include #include #include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("James Morris "); @@ -1310,9 +1311,9 @@ static int __init nf_nat_snmp_basic_init(void) { int ret = 0; - ret = nf_conntrack_helper_register(&snmp_helper); - if (ret < 0) - return ret; + BUG_ON(nf_nat_snmp_hook != NULL); + rcu_assign_pointer(nf_nat_snmp_hook, help); + ret = nf_conntrack_helper_register(&snmp_trap_helper); if (ret < 0) { nf_conntrack_helper_unregister(&snmp_helper); @@ -1323,7 +1324,7 @@ static int __init nf_nat_snmp_basic_init(void) static void __exit nf_nat_snmp_basic_fini(void) { - nf_conntrack_helper_unregister(&snmp_helper); + rcu_assign_pointer(nf_nat_snmp_hook, NULL); nf_conntrack_helper_unregister(&snmp_trap_helper); } diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index e2480bddbfd5..939b504604c2 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -185,9 +185,13 @@ config NF_CONNTRACK_IRC To compile it as a module, choose M here. If unsure, say N. +config NF_CONNTRACK_BROADCAST + tristate + config NF_CONNTRACK_NETBIOS_NS tristate "NetBIOS name service protocol support" depends on NETFILTER_ADVANCED + select NF_CONNTRACK_BROADCAST help NetBIOS name service requests are sent as broadcast messages from an unprivileged port and responded to with unicast messages to the @@ -204,6 +208,21 @@ config NF_CONNTRACK_NETBIOS_NS To compile it as a module, choose M here. If unsure, say N. +config NF_CONNTRACK_SNMP + tristate "SNMP service protocol support" + depends on NETFILTER_ADVANCED + select NF_CONNTRACK_BROADCAST + help + SNMP service requests are sent as broadcast messages from an + unprivileged port and responded to with unicast messages to the + same port. This make them hard to firewall properly because connection + tracking doesn't deal with broadcasts. This helper tracks locally + originating SNMP service requests and the corresponding + responses. It relies on correct IP address configuration, specifically + netmask and broadcast address. + + To compile it as a module, choose M here. If unsure, say N. + config NF_CONNTRACK_PPTP tristate "PPtP protocol support" depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 401d574bdfd2..2c2628de9d3f 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -28,7 +28,9 @@ obj-$(CONFIG_NF_CONNTRACK_AMANDA) += nf_conntrack_amanda.o obj-$(CONFIG_NF_CONNTRACK_FTP) += nf_conntrack_ftp.o obj-$(CONFIG_NF_CONNTRACK_H323) += nf_conntrack_h323.o obj-$(CONFIG_NF_CONNTRACK_IRC) += nf_conntrack_irc.o +obj-$(CONFIG_NF_CONNTRACK_BROADCAST) += nf_conntrack_broadcast.o obj-$(CONFIG_NF_CONNTRACK_NETBIOS_NS) += nf_conntrack_netbios_ns.o +obj-$(CONFIG_NF_CONNTRACK_SNMP) += nf_conntrack_snmp.o obj-$(CONFIG_NF_CONNTRACK_PPTP) += nf_conntrack_pptp.o obj-$(CONFIG_NF_CONNTRACK_SANE) += nf_conntrack_sane.o obj-$(CONFIG_NF_CONNTRACK_SIP) += nf_conntrack_sip.o diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c new file mode 100644 index 000000000000..4e99cca61612 --- /dev/null +++ b/net/netfilter/nf_conntrack_broadcast.c @@ -0,0 +1,82 @@ +/* + * broadcast connection tracking helper + * + * (c) 2005 Patrick McHardy + * + * 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 + +int nf_conntrack_broadcast_help(struct sk_buff *skb, + unsigned int protoff, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int timeout) +{ + struct nf_conntrack_expect *exp; + struct iphdr *iph = ip_hdr(skb); + struct rtable *rt = skb_rtable(skb); + struct in_device *in_dev; + struct nf_conn_help *help = nfct_help(ct); + __be32 mask = 0; + + /* we're only interested in locally generated packets */ + if (skb->sk == NULL) + goto out; + if (rt == NULL || !(rt->rt_flags & RTCF_BROADCAST)) + goto out; + 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) { + if (ifa->ifa_broadcast == iph->daddr) { + mask = ifa->ifa_mask; + break; + } + } endfor_ifa(in_dev); + } + rcu_read_unlock(); + + if (mask == 0) + goto out; + + exp = nf_ct_expect_alloc(ct); + if (exp == NULL) + goto out; + + exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; + exp->tuple.src.u.udp.port = help->helper->tuple.src.u.udp.port; + + exp->mask.src.u3.ip = mask; + exp->mask.src.u.udp.port = htons(0xFFFF); + + exp->expectfn = NULL; + exp->flags = NF_CT_EXPECT_PERMANENT; + exp->class = NF_CT_EXPECT_CLASS_DEFAULT; + exp->helper = NULL; + + nf_ct_expect_related(exp); + nf_ct_expect_put(exp); + + nf_ct_refresh(ct, skb, timeout * HZ); +out: + return NF_ACCEPT; +} +EXPORT_SYMBOL_GPL(nf_conntrack_broadcast_help); + +MODULE_LICENSE("GPL"); diff --git a/net/netfilter/nf_conntrack_netbios_ns.c b/net/netfilter/nf_conntrack_netbios_ns.c index aadde018a072..4c8f30a3d6d2 100644 --- a/net/netfilter/nf_conntrack_netbios_ns.c +++ b/net/netfilter/nf_conntrack_netbios_ns.c @@ -18,14 +18,7 @@ #include #include #include -#include -#include -#include -#include #include -#include -#include -#include #include #include @@ -40,75 +33,26 @@ MODULE_ALIAS("ip_conntrack_netbios_ns"); MODULE_ALIAS_NFCT_HELPER("netbios_ns"); static unsigned int timeout __read_mostly = 3; -module_param(timeout, uint, 0400); +module_param(timeout, uint, S_IRUSR); MODULE_PARM_DESC(timeout, "timeout for master connection/replies in seconds"); -static int help(struct sk_buff *skb, unsigned int protoff, - struct nf_conn *ct, enum ip_conntrack_info ctinfo) -{ - struct nf_conntrack_expect *exp; - struct iphdr *iph = ip_hdr(skb); - struct rtable *rt = skb_rtable(skb); - struct in_device *in_dev; - __be32 mask = 0; - - /* we're only interested in locally generated packets */ - if (skb->sk == NULL) - goto out; - if (rt == NULL || !(rt->rt_flags & RTCF_BROADCAST)) - goto out; - 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) { - if (ifa->ifa_broadcast == iph->daddr) { - mask = ifa->ifa_mask; - break; - } - } endfor_ifa(in_dev); - } - rcu_read_unlock(); - - if (mask == 0) - goto out; - - exp = nf_ct_expect_alloc(ct); - if (exp == NULL) - goto out; - - exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; - exp->tuple.src.u.udp.port = htons(NMBD_PORT); - - exp->mask.src.u3.ip = mask; - exp->mask.src.u.udp.port = htons(0xFFFF); - - exp->expectfn = NULL; - exp->flags = NF_CT_EXPECT_PERMANENT; - exp->class = NF_CT_EXPECT_CLASS_DEFAULT; - exp->helper = NULL; - - nf_ct_expect_related(exp); - nf_ct_expect_put(exp); - - nf_ct_refresh(ct, skb, timeout * HZ); -out: - return NF_ACCEPT; -} - static struct nf_conntrack_expect_policy exp_policy = { .max_expected = 1, }; +static int netbios_ns_help(struct sk_buff *skb, unsigned int protoff, + struct nf_conn *ct, enum ip_conntrack_info ctinfo) +{ + return nf_conntrack_broadcast_help(skb, protoff, ct, ctinfo, timeout); +} + static struct nf_conntrack_helper helper __read_mostly = { .name = "netbios-ns", - .tuple.src.l3num = AF_INET, + .tuple.src.l3num = NFPROTO_IPV4, .tuple.src.u.udp.port = cpu_to_be16(NMBD_PORT), .tuple.dst.protonum = IPPROTO_UDP, .me = THIS_MODULE, - .help = help, + .help = netbios_ns_help, .expect_policy = &exp_policy, }; diff --git a/net/netfilter/nf_conntrack_snmp.c b/net/netfilter/nf_conntrack_snmp.c new file mode 100644 index 000000000000..6e545e26289e --- /dev/null +++ b/net/netfilter/nf_conntrack_snmp.c @@ -0,0 +1,77 @@ +/* + * SNMP service broadcast connection tracking helper + * + * (c) 2011 Jiri Olsa + * + * 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 + +#define SNMP_PORT 161 + +MODULE_AUTHOR("Jiri Olsa "); +MODULE_DESCRIPTION("SNMP service broadcast connection tracking helper"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NFCT_HELPER("snmp"); + +static unsigned int timeout __read_mostly = 30; +module_param(timeout, uint, S_IRUSR); +MODULE_PARM_DESC(timeout, "timeout for master connection/replies in seconds"); + +int (*nf_nat_snmp_hook)(struct sk_buff *skb, + unsigned int protoff, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo); +EXPORT_SYMBOL_GPL(nf_nat_snmp_hook); + +static int snmp_conntrack_help(struct sk_buff *skb, unsigned int protoff, + struct nf_conn *ct, enum ip_conntrack_info ctinfo) +{ + typeof(nf_nat_snmp_hook) nf_nat_snmp; + + nf_conntrack_broadcast_help(skb, protoff, ct, ctinfo, timeout); + + nf_nat_snmp = rcu_dereference(nf_nat_snmp_hook); + if (nf_nat_snmp && ct->status & IPS_NAT_MASK) + return nf_nat_snmp(skb, protoff, ct, ctinfo); + + return NF_ACCEPT; +} + +static struct nf_conntrack_expect_policy exp_policy = { + .max_expected = 1, +}; + +static struct nf_conntrack_helper helper __read_mostly = { + .name = "snmp", + .tuple.src.l3num = NFPROTO_IPV4, + .tuple.src.u.udp.port = cpu_to_be16(SNMP_PORT), + .tuple.dst.protonum = IPPROTO_UDP, + .me = THIS_MODULE, + .help = snmp_conntrack_help, + .expect_policy = &exp_policy, +}; + +static int __init nf_conntrack_snmp_init(void) +{ + exp_policy.timeout = timeout; + return nf_conntrack_helper_register(&helper); +} + +static void __exit nf_conntrack_snmp_fini(void) +{ + nf_conntrack_helper_unregister(&helper); +} + +module_init(nf_conntrack_snmp_init); +module_exit(nf_conntrack_snmp_fini); -- cgit v1.2.3 From 7c86ad4a50ece39305b1be900df9a58645716602 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 19 Jan 2011 14:16:42 +0900 Subject: serial: sh-sci: Kill off unused clock string. Now that the clock string isn't used by the driver anymore, kill it off from the platform structure. Signed-off-by: Paul Mundt --- include/linux/serial_sci.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index 1630d9cae22a..f538132f9622 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -45,7 +45,6 @@ struct plat_sci_port { unsigned int irqs[SCIx_NR_IRQS]; /* ERI, RXI, TXI, BRI */ unsigned int type; /* SCI / SCIF / IRDA */ upf_t flags; /* UPF_* flags */ - char *clk; /* clock string */ unsigned int scbrr_algo_id; /* SCBRR calculation algo */ unsigned int scscr; /* SCSCR initialization */ -- cgit v1.2.3 From e735038f3848a720f84a819c8191ed2f6a1beed8 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 19 Jan 2011 14:18:06 +0900 Subject: serial: sh-sci: Kill off unused membase kludge. All users of the platform port data specify a mapbase where the driver later derives the membase from. Now that UPF flags are taken in to account for generic ioremapping we can kill off the port-specific membase clobbering and simply use the generic paths. This derives from a time when sh64 was not capable of using the generic ioremap implementation and had employed early bolted DTLB mappings for port access, which is no longer an issue. Signed-off-by: Paul Mundt --- drivers/serial/sh-sci.c | 4 ---- include/linux/serial_sci.h | 1 - 2 files changed, 5 deletions(-) (limited to 'include') diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 92c91c83edde..1d1e700c7a43 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -1619,9 +1619,6 @@ static void sci_config_port(struct uart_port *port, int flags) port->type = s->type; - if (port->membase) - return; - if (port->flags & UPF_IOREMAP) { port->membase = ioremap_nocache(port->mapbase, 0x40); @@ -1727,7 +1724,6 @@ static int __devinit sci_init_single(struct platform_device *dev, init_timer(&sci_port->break_timer); port->mapbase = p->mapbase; - port->membase = p->membase; port->irq = p->irqs[SCIx_TXI_IRQ]; port->flags = p->flags; diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index f538132f9622..789acf5b31d3 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -40,7 +40,6 @@ struct device; * Platform device specific platform_data struct */ struct plat_sci_port { - void __iomem *membase; /* io cookie */ unsigned long mapbase; /* resource base */ unsigned int irqs[SCIx_NR_IRQS]; /* ERI, RXI, TXI, BRI */ unsigned int type; /* SCI / SCIF / IRDA */ -- cgit v1.2.3 From 80f8f1027b99660897bdeaeae73002185d829906 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 18 Jan 2011 07:46:52 +0000 Subject: net: filter: dont block softirqs in sk_run_filter() Packet filter (BPF) doesnt need to disable softirqs, being fully re-entrant and lock-less. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/sock.h | 2 +- net/core/filter.c | 6 +++--- net/packet/af_packet.c | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index d884d268c704..ba6465bf7c7a 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1189,7 +1189,7 @@ extern void sk_filter_release_rcu(struct rcu_head *rcu); static inline void sk_filter_release(struct sk_filter *fp) { if (atomic_dec_and_test(&fp->refcnt)) - call_rcu_bh(&fp->rcu, sk_filter_release_rcu); + call_rcu(&fp->rcu, sk_filter_release_rcu); } static inline void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp) diff --git a/net/core/filter.c b/net/core/filter.c index afc58374ca96..232b1873bb28 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -142,14 +142,14 @@ int sk_filter(struct sock *sk, struct sk_buff *skb) if (err) return err; - rcu_read_lock_bh(); - filter = rcu_dereference_bh(sk->sk_filter); + rcu_read_lock(); + filter = rcu_dereference(sk->sk_filter); if (filter) { unsigned int pkt_len = sk_run_filter(skb, filter->insns); err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM; } - rcu_read_unlock_bh(); + rcu_read_unlock(); return err; } diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 91cb1d71f018..c3fc7b70a879 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -523,11 +523,11 @@ static inline unsigned int run_filter(const struct sk_buff *skb, { struct sk_filter *filter; - rcu_read_lock_bh(); - filter = rcu_dereference_bh(sk->sk_filter); + rcu_read_lock(); + filter = rcu_dereference(sk->sk_filter); if (filter != NULL) res = sk_run_filter(skb, filter->insns); - rcu_read_unlock_bh(); + rcu_read_unlock(); return res; } -- cgit v1.2.3 From 22cc83780e214f4f009384f9c1d0658629625d49 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 19 Jan 2011 14:37:14 +0900 Subject: serial: sh-sci: Provide a helper for muxed IRQs. All of the muxed IRQs presently populate the IRQ array verbosely, this simply provides a trivial helper to do it for them. Signed-off-by: Paul Mundt --- include/linux/serial_sci.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index 789acf5b31d3..01ffe7c56e5b 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -34,6 +34,14 @@ enum { SCIx_NR_IRQS, }; +#define SCIx_IRQ_MUXED(irq) \ +{ \ + [SCIx_ERI_IRQ] = (irq), \ + [SCIx_RXI_IRQ] = (irq), \ + [SCIx_TXI_IRQ] = (irq), \ + [SCIx_BRI_IRQ] = (irq), \ +} + struct device; /* -- cgit v1.2.3 From 27bd107525607e6a64c612ca3c43ca0dac4768b1 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 19 Jan 2011 15:37:31 +0900 Subject: serial: sh-sci: Kill off some DMA ifdeffery. There's nothing worth hiding under the ifdef in the platform DMA definitions, and we certainly don't want board code adding this in to their platform data definitions, so we always expose the slave rx/tx and device pointer members instead. Signed-off-by: Paul Mundt --- drivers/serial/sh-sci.c | 14 ++++++++------ include/linux/serial_sci.h | 6 ++---- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index dccc9822ab9b..5b3e9769ca21 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -1423,6 +1423,14 @@ static void sci_free_dma(struct uart_port *port) if (s->chan_rx) sci_rx_dma_release(s, false); } +#else +static inline void sci_request_dma(struct uart_port *port) +{ +} + +static inline void sci_free_dma(struct uart_port *port) +{ +} #endif static int sci_startup(struct uart_port *port) @@ -1435,9 +1443,7 @@ static int sci_startup(struct uart_port *port) s->enable(port); sci_request_irq(s); -#ifdef CONFIG_SERIAL_SH_SCI_DMA sci_request_dma(port); -#endif sci_start_tx(port); sci_start_rx(port); @@ -1452,9 +1458,7 @@ static void sci_shutdown(struct uart_port *port) sci_stop_rx(port); sci_stop_tx(port); -#ifdef CONFIG_SERIAL_SH_SCI_DMA sci_free_dma(port); -#endif sci_free_irq(s); if (s->disable) @@ -1726,11 +1730,9 @@ static int __devinit sci_init_single(struct platform_device *dev, */ port->irq = p->irqs[SCIx_TXI_IRQ]; -#ifdef CONFIG_SERIAL_SH_SCI_DMA if (p->dma_dev) dev_dbg(port->dev, "DMA device %p, tx %d, rx %d\n", p->dma_dev, p->dma_slave_tx, p->dma_slave_rx); -#endif return 0; } diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index 01ffe7c56e5b..a2afc9fbe186 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -58,10 +58,8 @@ struct plat_sci_port { struct device *dma_dev; -#ifdef CONFIG_SERIAL_SH_SCI_DMA - unsigned int dma_slave_tx; - unsigned int dma_slave_rx; -#endif + unsigned int dma_slave_tx; + unsigned int dma_slave_rx; }; #endif /* __LINUX_SERIAL_SCI_H */ -- cgit v1.2.3 From 20e4859dedfc7e7b620d1756b29f8483c5be5fcc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 15 Jan 2011 13:40:50 +0000 Subject: ASoC: Add support for sequencing within With larger devices there may be many widgets of the same type in series in an audio path. Allow drivers to specify an additional level of ordering within each widget type by adding a subsequence number to widgets and then splitting operations on widgets so that widgets of the same type but different sequence numbers are processed separately. A typical example would be a supply widget which requires that another widget be enabled to provide power or clocking. SND_SOC_DAPM_PGA_S() and SND_SOC_DAPM_SUPPLY_S() macros are provided allowing this to be used with PGAs and supplies as these are the most commonly affected widgets. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc-dapm.h | 13 +++++++++++++ sound/soc/soc-dapm.c | 11 ++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 8031769ac485..a3760c93a8a3 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -157,6 +157,18 @@ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} +/* additional sequencing control within an event type */ +#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, wcontrols, \ + wncontrols, wevent, wflags) \ +{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ + .event = wevent, .event_flags = wflags, .subseq = wsubseq} +#define SND_SOC_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, winvert, wevent, \ + wflags) \ +{ .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \ + .shift = wshift, .invert = winvert, .event = wevent, \ + .event_flags = wflags, .subseq = wsubseq} + /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ @@ -450,6 +462,7 @@ struct snd_soc_dapm_widget { unsigned char ext:1; /* has external widgets */ unsigned char force:1; /* force state */ unsigned char ignore_suspend:1; /* kept enabled over suspend */ + int subseq; /* sort within widget type */ int (*power_check)(struct snd_soc_dapm_widget *w); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 57e1c9f71149..eb7436c7acad 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -737,6 +737,12 @@ static int dapm_seq_compare(struct snd_soc_dapm_widget *a, if (sort[a->id] != sort[b->id]) return sort[a->id] - sort[b->id]; + if (a->subseq != b->subseq) { + if (power_up) + return a->subseq - b->subseq; + else + return b->subseq - a->subseq; + } if (a->reg != b->reg) return a->reg - b->reg; if (a->dapm != b->dapm) @@ -869,6 +875,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *w, *n; LIST_HEAD(pending); int cur_sort = -1; + int cur_subseq = -1; int cur_reg = SND_SOC_NOPM; struct snd_soc_dapm_context *cur_dapm = NULL; int ret; @@ -884,12 +891,13 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, /* Do we need to apply any queued changes? */ if (sort[w->id] != cur_sort || w->reg != cur_reg || - w->dapm != cur_dapm) { + w->dapm != cur_dapm || w->subseq != cur_subseq) { if (!list_empty(&pending)) dapm_seq_run_coalesced(cur_dapm, &pending); INIT_LIST_HEAD(&pending); cur_sort = -1; + cur_subseq = -1; cur_reg = SND_SOC_NOPM; cur_dapm = NULL; } @@ -934,6 +942,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, default: /* Queue it up for application */ cur_sort = sort[w->id]; + cur_subseq = w->subseq; cur_reg = w->reg; cur_dapm = w->dapm; list_move(&w->power_list, &pending); -- cgit v1.2.3 From 474b62d6eee733abdcd36f8e3e5ce504fbb9110b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 18 Jan 2011 16:14:44 +0000 Subject: ASoC: Provide per widget type callback when executing DAPM sequences Many modern devices have features such as DC servos which take time to start. Currently these are handled by per-widget events but this makes it difficult to paralleise operations on multiple widgets, meaning delays can end up being needlessly serialised. By providing a callback to drivers when all widgets of a given type have been handled during a DAPM sequence the core allows drivers to start operations separately and wait for them to complete much more simply. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc-dapm.h | 3 +++ include/sound/soc.h | 3 +++ sound/soc/soc-core.c | 1 + sound/soc/soc-dapm.c | 16 +++++++++++++++- 4 files changed, 22 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index a3760c93a8a3..6c9ae237814b 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -500,6 +500,9 @@ struct snd_soc_dapm_context { struct snd_soc_dapm_update *update; + void (*seq_notifier)(struct snd_soc_dapm_context *, + enum snd_soc_dapm_type); + struct device *dev; /* from parent - for debug */ struct snd_soc_codec *codec; /* parent codec */ struct snd_soc_card *card; /* parent card */ diff --git a/include/sound/soc.h b/include/sound/soc.h index 9952254974b3..d244f9013767 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -546,6 +546,9 @@ struct snd_soc_codec_driver { /* codec bias level */ int (*set_bias_level)(struct snd_soc_codec *, enum snd_soc_bias_level level); + + void (*seq_notifier)(struct snd_soc_dapm_context *, + enum snd_soc_dapm_type); }; /* SoC platform interface */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 9e68984423b2..b0e7689159c1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3496,6 +3496,7 @@ int snd_soc_register_codec(struct device *dev, codec->dapm.bias_level = SND_SOC_BIAS_OFF; codec->dapm.dev = dev; codec->dapm.codec = codec; + codec->dapm.seq_notifier = codec_drv->seq_notifier; codec->dev = dev; codec->driver = codec_drv; codec->num_dai = num_dai; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index eb7436c7acad..37b376f4c75d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -878,7 +878,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, int cur_subseq = -1; int cur_reg = SND_SOC_NOPM; struct snd_soc_dapm_context *cur_dapm = NULL; - int ret; + int ret, i; int *sort; if (power_up) @@ -895,6 +895,13 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, if (!list_empty(&pending)) dapm_seq_run_coalesced(cur_dapm, &pending); + if (cur_dapm && cur_dapm->seq_notifier) { + for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) + if (sort[i] == cur_sort) + cur_dapm->seq_notifier(cur_dapm, + i); + } + INIT_LIST_HEAD(&pending); cur_sort = -1; cur_subseq = -1; @@ -956,6 +963,13 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, if (!list_empty(&pending)) dapm_seq_run_coalesced(dapm, &pending); + + if (cur_dapm && cur_dapm->seq_notifier) { + for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) + if (sort[i] == cur_sort) + cur_dapm->seq_notifier(cur_dapm, + i); + } } static void dapm_widget_update(struct snd_soc_dapm_context *dapm) -- cgit v1.2.3 From a992ca2a0498edd22a88ac8c41570f536de29c9e Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 19 Jan 2011 16:00:07 +0100 Subject: netfilter: nf_conntrack_tstamp: add flow-based timestamp extension This patch adds flow-based timestamping for conntracks. This conntrack extension is disabled by default. Basically, we use two 64-bits variables to store the creation timestamp once the conntrack has been confirmed and the other to store the deletion time. This extension is disabled by default, to enable it, you have to: echo 1 > /proc/sys/net/netfilter/nf_conntrack_timestamp This patch allows to save memory for user-space flow-based loogers such as ulogd2. In short, ulogd2 does not need to keep a hashtable with the conntrack in user-space to know when they were created and destroyed, instead we use the kernel timestamp. If we want to have a sane IPFIX implementation in user-space, this nanosecs resolution timestamps are also useful. Other custom user-space applications can benefit from this via libnetfilter_conntrack. This patch modifies the /proc output to display the delta time in seconds since the flow start. You can also obtain the flow-start date by means of the conntrack-tools. Signed-off-by: Pablo Neira Ayuso Signed-off-by: Patrick McHardy --- include/linux/netfilter/nfnetlink_conntrack.h | 9 ++ include/net/netfilter/nf_conntrack_extend.h | 4 + include/net/netfilter/nf_conntrack_timestamp.h | 53 +++++++++++ include/net/netns/conntrack.h | 2 + net/netfilter/Kconfig | 11 +++ net/netfilter/Makefile | 1 + net/netfilter/nf_conntrack_core.c | 26 ++++++ net/netfilter/nf_conntrack_netlink.c | 46 +++++++++- net/netfilter/nf_conntrack_standalone.c | 41 +++++++++ net/netfilter/nf_conntrack_timestamp.c | 120 +++++++++++++++++++++++++ 10 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 include/net/netfilter/nf_conntrack_timestamp.h create mode 100644 net/netfilter/nf_conntrack_timestamp.c (limited to 'include') diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index 19711e3ffd42..debf1aefd753 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -42,6 +42,7 @@ enum ctattr_type { CTA_SECMARK, /* obsolete */ CTA_ZONE, CTA_SECCTX, + CTA_TIMESTAMP, __CTA_MAX }; #define CTA_MAX (__CTA_MAX - 1) @@ -127,6 +128,14 @@ enum ctattr_counters { }; #define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1) +enum ctattr_tstamp { + CTA_TIMESTAMP_UNSPEC, + CTA_TIMESTAMP_START, + CTA_TIMESTAMP_STOP, + __CTA_TIMESTAMP_MAX +}; +#define CTA_TIMESTAMP_MAX (__CTA_TIMESTAMP_MAX - 1) + enum ctattr_nat { CTA_NAT_UNSPEC, CTA_NAT_MINIP, diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 1a9f96db3798..2dcf31703acb 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -16,6 +16,9 @@ enum nf_ct_ext_id { #endif #ifdef CONFIG_NF_CONNTRACK_ZONES NF_CT_EXT_ZONE, +#endif +#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP + NF_CT_EXT_TSTAMP, #endif NF_CT_EXT_NUM, }; @@ -25,6 +28,7 @@ enum nf_ct_ext_id { #define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter #define NF_CT_EXT_ECACHE_TYPE struct nf_conntrack_ecache #define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone +#define NF_CT_EXT_TSTAMP_TYPE struct nf_conn_tstamp /* Extensions: optional stuff which isn't permanently in struct. */ struct nf_ct_ext { diff --git a/include/net/netfilter/nf_conntrack_timestamp.h b/include/net/netfilter/nf_conntrack_timestamp.h new file mode 100644 index 000000000000..f17dcb664e29 --- /dev/null +++ b/include/net/netfilter/nf_conntrack_timestamp.h @@ -0,0 +1,53 @@ +#ifndef _NF_CONNTRACK_TSTAMP_H +#define _NF_CONNTRACK_TSTAMP_H + +#include +#include +#include +#include +#include + +struct nf_conn_tstamp { + u_int64_t start; + u_int64_t stop; +}; + +static inline +struct nf_conn_tstamp *nf_conn_tstamp_find(const struct nf_conn *ct) +{ +#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP + return nf_ct_ext_find(ct, NF_CT_EXT_TSTAMP); +#else + return NULL; +#endif +} + +static inline +struct nf_conn_tstamp *nf_ct_tstamp_ext_add(struct nf_conn *ct, gfp_t gfp) +{ +#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP + struct net *net = nf_ct_net(ct); + + if (!net->ct.sysctl_tstamp) + return NULL; + + return nf_ct_ext_add(ct, NF_CT_EXT_TSTAMP, gfp); +#else + return NULL; +#endif +}; + +static inline bool nf_ct_tstamp_enabled(struct net *net) +{ + return net->ct.sysctl_tstamp != 0; +} + +static inline void nf_ct_set_tstamp(struct net *net, bool enable) +{ + net->ct.sysctl_tstamp = enable; +} + +extern int nf_conntrack_tstamp_init(struct net *net); +extern void nf_conntrack_tstamp_fini(struct net *net); + +#endif /* _NF_CONNTRACK_TSTAMP_H */ diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index 5cf8a8c141aa..341eb089349e 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -21,11 +21,13 @@ struct netns_ct { int sysctl_events; unsigned int sysctl_events_retry_timeout; int sysctl_acct; + int sysctl_tstamp; int sysctl_checksum; unsigned int sysctl_log_invalid; /* Log invalid packets */ #ifdef CONFIG_SYSCTL struct ctl_table_header *sysctl_header; struct ctl_table_header *acct_sysctl_header; + struct ctl_table_header *tstamp_sysctl_header; struct ctl_table_header *event_sysctl_header; #endif char *slabname; diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 939b504604c2..faf7412ea453 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -85,6 +85,17 @@ config NF_CONNTRACK_EVENTS If unsure, say `N'. +config NF_CONNTRACK_TIMESTAMP + bool 'Connection tracking timestamping' + depends on NETFILTER_ADVANCED + help + This option enables support for connection tracking timestamping. + This allows you to store the flow start-time and to obtain + the flow-stop time (once it has been destroyed) via Connection + tracking events. + + If unsure, say `N'. + config NF_CT_PROTO_DCCP tristate 'DCCP protocol connection tracking support (EXPERIMENTAL)' depends on EXPERIMENTAL diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 2c2628de9d3f..9ae6878a85b1 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -1,6 +1,7 @@ netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o nf_conntrack_acct.o +nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o obj-$(CONFIG_NETFILTER) = netfilter.o diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index f47ac67e1bfe..1909311c392a 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -282,6 +283,11 @@ EXPORT_SYMBOL_GPL(nf_ct_insert_dying_list); static void death_by_timeout(unsigned long ul_conntrack) { struct nf_conn *ct = (void *)ul_conntrack; + struct nf_conn_tstamp *tstamp; + + tstamp = nf_conn_tstamp_find(ct); + if (tstamp && tstamp->stop == 0) + tstamp->stop = ktime_to_ns(ktime_get_real()); if (!test_bit(IPS_DYING_BIT, &ct->status) && unlikely(nf_conntrack_event(IPCT_DESTROY, ct) < 0)) { @@ -419,6 +425,7 @@ __nf_conntrack_confirm(struct sk_buff *skb) struct nf_conntrack_tuple_hash *h; struct nf_conn *ct; struct nf_conn_help *help; + struct nf_conn_tstamp *tstamp; struct hlist_nulls_node *n; enum ip_conntrack_info ctinfo; struct net *net; @@ -488,6 +495,14 @@ __nf_conntrack_confirm(struct sk_buff *skb) atomic_inc(&ct->ct_general.use); ct->status |= IPS_CONFIRMED; + /* set conntrack timestamp, if enabled. */ + tstamp = nf_conn_tstamp_find(ct); + if (tstamp) { + if (skb->tstamp.tv64 == 0) + __net_timestamp((struct sk_buff *)skb); + + tstamp->start = ktime_to_ns(skb->tstamp); + } /* Since the lookup is lockless, hash insertion must be done after * starting the timer and setting the CONFIRMED bit. The RCU barriers * guarantee that no other CPU can find the conntrack before the above @@ -746,6 +761,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, } nf_ct_acct_ext_add(ct, GFP_ATOMIC); + nf_ct_tstamp_ext_add(ct, GFP_ATOMIC); ecache = tmpl ? nf_ct_ecache_find(tmpl) : NULL; nf_ct_ecache_ext_add(ct, ecache ? ecache->ctmask : 0, @@ -1186,6 +1202,11 @@ struct __nf_ct_flush_report { static int kill_report(struct nf_conn *i, void *data) { struct __nf_ct_flush_report *fr = (struct __nf_ct_flush_report *)data; + struct nf_conn_tstamp *tstamp; + + tstamp = nf_conn_tstamp_find(i); + if (tstamp && tstamp->stop == 0) + tstamp->stop = ktime_to_ns(ktime_get_real()); /* If we fail to deliver the event, death_by_timeout() will retry */ if (nf_conntrack_event_report(IPCT_DESTROY, i, @@ -1497,6 +1518,9 @@ static int nf_conntrack_init_net(struct net *net) ret = nf_conntrack_acct_init(net); if (ret < 0) goto err_acct; + ret = nf_conntrack_tstamp_init(net); + if (ret < 0) + goto err_tstamp; ret = nf_conntrack_ecache_init(net); if (ret < 0) goto err_ecache; @@ -1504,6 +1528,8 @@ static int nf_conntrack_init_net(struct net *net) return 0; err_ecache: + nf_conntrack_tstamp_fini(net); +err_tstamp: nf_conntrack_acct_fini(net); err_acct: nf_conntrack_expect_fini(net); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 9eabaa6f28a8..715d56c85475 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -42,6 +42,7 @@ #include #include #include +#include #ifdef CONFIG_NF_NAT_NEEDED #include #include @@ -230,6 +231,33 @@ nla_put_failure: return -1; } +static int +ctnetlink_dump_timestamp(struct sk_buff *skb, const struct nf_conn *ct) +{ + struct nlattr *nest_count; + const struct nf_conn_tstamp *tstamp; + + tstamp = nf_conn_tstamp_find(ct); + if (!tstamp) + return 0; + + nest_count = nla_nest_start(skb, CTA_TIMESTAMP | NLA_F_NESTED); + if (!nest_count) + goto nla_put_failure; + + NLA_PUT_BE64(skb, CTA_TIMESTAMP_START, cpu_to_be64(tstamp->start)); + if (tstamp->stop != 0) { + NLA_PUT_BE64(skb, CTA_TIMESTAMP_STOP, + cpu_to_be64(tstamp->stop)); + } + nla_nest_end(skb, nest_count); + + return 0; + +nla_put_failure: + return -1; +} + #ifdef CONFIG_NF_CONNTRACK_MARK static inline int ctnetlink_dump_mark(struct sk_buff *skb, const struct nf_conn *ct) @@ -404,6 +432,7 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq, ctnetlink_dump_timeout(skb, ct) < 0 || ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0 || + ctnetlink_dump_timestamp(skb, ct) < 0 || ctnetlink_dump_protoinfo(skb, ct) < 0 || ctnetlink_dump_helpinfo(skb, ct) < 0 || ctnetlink_dump_mark(skb, ct) < 0 || @@ -470,6 +499,18 @@ ctnetlink_secctx_size(const struct nf_conn *ct) #endif } +static inline size_t +ctnetlink_timestamp_size(const struct nf_conn *ct) +{ +#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP + if (!nf_ct_ext_exist(ct, NF_CT_EXT_TSTAMP)) + return 0; + return nla_total_size(0) + 2 * nla_total_size(sizeof(uint64_t)); +#else + return 0; +#endif +} + static inline size_t ctnetlink_nlmsg_size(const struct nf_conn *ct) { @@ -481,6 +522,7 @@ ctnetlink_nlmsg_size(const struct nf_conn *ct) + nla_total_size(sizeof(u_int32_t)) /* CTA_ID */ + nla_total_size(sizeof(u_int32_t)) /* CTA_STATUS */ + ctnetlink_counters_size(ct) + + ctnetlink_timestamp_size(ct) + nla_total_size(sizeof(u_int32_t)) /* CTA_TIMEOUT */ + nla_total_size(0) /* CTA_PROTOINFO */ + nla_total_size(0) /* CTA_HELP */ @@ -571,7 +613,8 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) if (events & (1 << IPCT_DESTROY)) { if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || - ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0) + ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0 || + ctnetlink_dump_timestamp(skb, ct) < 0) goto nla_put_failure; } else { if (ctnetlink_dump_timeout(skb, ct) < 0) @@ -1360,6 +1403,7 @@ ctnetlink_create_conntrack(struct net *net, u16 zone, } nf_ct_acct_ext_add(ct, GFP_ATOMIC); + nf_ct_tstamp_ext_add(ct, GFP_ATOMIC); nf_ct_ecache_ext_add(ct, 0, 0, GFP_ATOMIC); /* we must add conntrack extensions before confirmation. */ ct->status |= IPS_CONFIRMED; diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 8257bf643593..69107fd78d3e 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -29,6 +29,7 @@ #include #include #include +#include #include MODULE_LICENSE("GPL"); @@ -46,6 +47,7 @@ EXPORT_SYMBOL_GPL(print_tuple); struct ct_iter_state { struct seq_net_private p; unsigned int bucket; + u_int64_t time_now; }; static struct hlist_nulls_node *ct_get_first(struct seq_file *seq) @@ -96,6 +98,9 @@ static struct hlist_nulls_node *ct_get_idx(struct seq_file *seq, loff_t pos) static void *ct_seq_start(struct seq_file *seq, loff_t *pos) __acquires(RCU) { + struct ct_iter_state *st = seq->private; + + st->time_now = ktime_to_ns(ktime_get_real()); rcu_read_lock(); return ct_get_idx(seq, *pos); } @@ -135,6 +140,39 @@ static inline int ct_show_secctx(struct seq_file *s, const struct nf_conn *ct) } #endif +#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP +static u_int64_t ct_delta_time(u_int64_t time_now, const struct nf_conn *ct) +{ + struct nf_conn_tstamp *tstamp; + + tstamp = nf_conn_tstamp_find(ct); + if (tstamp) { + u_int64_t delta_time = time_now - tstamp->start; + return delta_time > 0 ? div_s64(delta_time, NSEC_PER_SEC) : 0; + } + return -1; +} + +static int ct_show_delta_time(struct seq_file *s, const struct nf_conn *ct) +{ + struct ct_iter_state *st = s->private; + u_int64_t delta_time; + + delta_time = ct_delta_time(st->time_now, ct); + if (delta_time < 0) + return 0; + + return seq_printf(s, "delta-time=%llu ", + (unsigned long long)delta_time); +} +#else +static inline int +ct_show_delta_time(struct seq_file *s, const struct nf_conn *ct) +{ + return 0; +} +#endif + /* return 0 on success, 1 in case of error */ static int ct_seq_show(struct seq_file *s, void *v) { @@ -203,6 +241,9 @@ static int ct_seq_show(struct seq_file *s, void *v) goto release; #endif + if (ct_show_delta_time(s, ct)) + goto release; + if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use))) goto release; diff --git a/net/netfilter/nf_conntrack_timestamp.c b/net/netfilter/nf_conntrack_timestamp.c new file mode 100644 index 000000000000..af7dd31af0a1 --- /dev/null +++ b/net/netfilter/nf_conntrack_timestamp.c @@ -0,0 +1,120 @@ +/* + * (C) 2010 Pablo Neira Ayuso + * + * 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 (or any later at your option). + */ + +#include +#include +#include +#include + +#include +#include +#include + +static int nf_ct_tstamp __read_mostly; + +module_param_named(tstamp, nf_ct_tstamp, bool, 0644); +MODULE_PARM_DESC(tstamp, "Enable connection tracking flow timestamping."); + +#ifdef CONFIG_SYSCTL +static struct ctl_table tstamp_sysctl_table[] = { + { + .procname = "nf_conntrack_timestamp", + .data = &init_net.ct.sysctl_tstamp, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + {} +}; +#endif /* CONFIG_SYSCTL */ + +static struct nf_ct_ext_type tstamp_extend __read_mostly = { + .len = sizeof(struct nf_conn_tstamp), + .align = __alignof__(struct nf_conn_tstamp), + .id = NF_CT_EXT_TSTAMP, +}; + +#ifdef CONFIG_SYSCTL +static int nf_conntrack_tstamp_init_sysctl(struct net *net) +{ + struct ctl_table *table; + + table = kmemdup(tstamp_sysctl_table, sizeof(tstamp_sysctl_table), + GFP_KERNEL); + if (!table) + goto out; + + table[0].data = &net->ct.sysctl_tstamp; + + net->ct.tstamp_sysctl_header = register_net_sysctl_table(net, + nf_net_netfilter_sysctl_path, table); + if (!net->ct.tstamp_sysctl_header) { + printk(KERN_ERR "nf_ct_tstamp: can't register to sysctl.\n"); + goto out_register; + } + return 0; + +out_register: + kfree(table); +out: + return -ENOMEM; +} + +static void nf_conntrack_tstamp_fini_sysctl(struct net *net) +{ + struct ctl_table *table; + + table = net->ct.tstamp_sysctl_header->ctl_table_arg; + unregister_net_sysctl_table(net->ct.tstamp_sysctl_header); + kfree(table); +} +#else +static int nf_conntrack_tstamp_init_sysctl(struct net *net) +{ + return 0; +} + +static void nf_conntrack_tstamp_fini_sysctl(struct net *net) +{ +} +#endif + +int nf_conntrack_tstamp_init(struct net *net) +{ + int ret; + + net->ct.sysctl_tstamp = nf_ct_tstamp; + + if (net_eq(net, &init_net)) { + ret = nf_ct_extend_register(&tstamp_extend); + if (ret < 0) { + printk(KERN_ERR "nf_ct_tstamp: Unable to register " + "extension\n"); + goto out_extend_register; + } + } + + ret = nf_conntrack_tstamp_init_sysctl(net); + if (ret < 0) + goto out_sysctl; + + return 0; + +out_sysctl: + if (net_eq(net, &init_net)) + nf_ct_extend_unregister(&tstamp_extend); +out_extend_register: + return ret; +} + +void nf_conntrack_tstamp_fini(struct net *net) +{ + nf_conntrack_tstamp_fini_sysctl(net); + if (net_eq(net, &init_net)) + nf_ct_extend_unregister(&tstamp_extend); +} -- cgit v1.2.3 From df6ba5d80d6c9b51471d5fa046c3c06988e5f62a Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 12 Jan 2011 15:26:30 +0200 Subject: mac80211: add hw configuration for max ampdu buffer size Some devices don't support the maximum AMDPU buffer size of 64, so we need to add an option to configure this in the hardware configuration. This value will be used in the ADDBA response instead of the value suggested in the request, if the latter is greater than the max supported. Signed-off-by: Luciano Coelho Tested-by: Juuso Oikarinen Signed-off-by: John W. Linville --- include/net/mac80211.h | 8 ++++++++ net/mac80211/agg-rx.c | 3 +++ net/mac80211/main.c | 1 + 3 files changed, 12 insertions(+) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 62c0ce2d1dc8..d024fc563e7b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1147,6 +1147,13 @@ enum ieee80211_hw_flags { * @napi_weight: weight used for NAPI polling. You must specify an * appropriate value here if a napi_poll operation is provided * by your driver. + + * @max_rx_aggregation_subframes: maximum buffer size (number of + * sub-frames) to be used for A-MPDU block ack receiver + * aggregation. + * This is only relevant if the device has restrictions on the + * number of subframes, if it relies on mac80211 to do reordering + * it shouldn't be set. */ struct ieee80211_hw { struct ieee80211_conf conf; @@ -1165,6 +1172,7 @@ struct ieee80211_hw { u8 max_rates; u8 max_report_rates; u8 max_rate_tries; + u8 max_rx_aggregation_subframes; }; /** diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index f138b195d657..002db5e86eb6 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -239,6 +239,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, buf_size = buf_size << sband->ht_cap.ampdu_factor; } + /* make sure the size doesn't exceed the maximum supported by the hw */ + if (buf_size > local->hw.max_rx_aggregation_subframes) + buf_size = local->hw.max_rx_aggregation_subframes; /* examine state machine */ mutex_lock(&sta->ampdu_mlme.mtx); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 485d36bc9a46..1c507c6972e6 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -552,6 +552,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, local->hw.queues = 1; local->hw.max_rates = 1; local->hw.max_report_rates = 0; + local->hw.max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; local->hw.conf.long_frame_max_tx_count = wiphy->retry_long; local->hw.conf.short_frame_max_tx_count = wiphy->retry_short; local->user_power_level = -1; -- cgit v1.2.3 From 0b01f030d38e00650e2db42da083d8647aad40a5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 18 Jan 2011 13:51:05 +0100 Subject: mac80211: track receiver's aggregation reorder buffer size The aggregation code currently doesn't implement the buffer size negotiation. It will always request a max buffer size (which is fine, if a little pointless, as the mac80211 code doesn't know and might just use 0 instead), but if the peer requests a smaller size it isn't possible to honour this request. In order to fix this, look at the buffer size in the addBA response frame, keep track of it and pass it to the driver in the ampdu_action callback when called with the IEEE80211_AMPDU_TX_OPERATIONAL action. That way the driver can limit the number of subframes in aggregates appropriately. Note that this doesn't fix any drivers apart from the addition of the new argument -- they all need to be updated separately to use this variable! Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ar9170/main.c | 3 ++- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 2 +- drivers/net/wireless/ath/ath9k/main.c | 2 +- drivers/net/wireless/ath/carl9170/main.c | 2 +- drivers/net/wireless/iwlwifi/iwl-agn.c | 3 ++- drivers/net/wireless/iwlwifi/iwl-agn.h | 3 ++- drivers/net/wireless/mac80211_hwsim.c | 3 ++- drivers/net/wireless/mwl8k.c | 3 ++- drivers/net/wireless/rt2x00/rt2800lib.c | 3 ++- drivers/net/wireless/rt2x00/rt2800lib.h | 3 ++- drivers/net/wireless/rtlwifi/core.c | 3 ++- include/net/mac80211.h | 7 ++++++- net/mac80211/agg-rx.c | 4 ++-- net/mac80211/agg-tx.c | 20 +++++++++++++++++--- net/mac80211/driver-ops.h | 6 +++--- net/mac80211/driver-trace.h | 11 +++++++---- net/mac80211/sta_info.h | 2 ++ 17 files changed, 56 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c index 32bf79e6a320..a9111e1161fd 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -1945,7 +1945,8 @@ static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue, static int ar9170_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn) + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) { switch (action) { case IEEE80211_AMPDU_RX_START: diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 187af5b4440d..f14f37d29f45 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1549,7 +1549,7 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, - u16 tid, u16 *ssn) + u16 tid, u16 *ssn, u8 buf_size) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_sta *ista; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 174c016ef89d..c03184e7bffe 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2165,7 +2165,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, - u16 tid, u16 *ssn) + u16 tid, u16 *ssn, u8 buf_size) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 870df8c42622..ecfb80b059d1 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1279,7 +1279,7 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, - u16 tid, u16 *ssn) + u16 tid, u16 *ssn, u8 buf_size) { struct ar9170 *ar = hw->priv; struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 36335b1b54d4..8b045a401d62 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -3393,7 +3393,8 @@ int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn) + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) { struct iwl_priv *priv = hw->priv; int ret = -EINVAL; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h index da303585f801..822221a97e80 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn.h @@ -349,7 +349,8 @@ void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw, int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn); + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size); int iwlagn_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 454f045ddff3..5d39b2840584 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -943,7 +943,8 @@ static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn) + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) { switch (action) { case IEEE80211_AMPDU_TX_START: diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 809f2bf27958..106b427d0064 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -4356,7 +4356,8 @@ static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx, static int mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn) + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) { switch (action) { case IEEE80211_AMPDU_RX_START: diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index a25be625ee90..f8ba01cbc6dd 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3533,7 +3533,8 @@ EXPORT_SYMBOL_GPL(rt2800_get_tsf); int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn) + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) { int ret = 0; diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h index e3c995a9dec4..3efafb78ff77 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/rt2x00/rt2800lib.h @@ -198,7 +198,8 @@ int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, u64 rt2800_get_tsf(struct ieee80211_hw *hw); int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn); + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size); int rt2800_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey); diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index d6a924a05654..25d2d667ffba 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -748,7 +748,8 @@ static void rtl_op_sta_notify(struct ieee80211_hw *hw, static int rtl_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 * ssn) + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) { struct rtl_priv *rtlpriv = rtl_priv(hw); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d024fc563e7b..5afe341b4010 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1731,6 +1731,10 @@ enum ieee80211_ampdu_mlme_action { * ieee80211_ampdu_mlme_action. Starting sequence number (@ssn) * is the first frame we expect to perform the action on. Notice * that TX/RX_STOP can pass NULL for this parameter. + * The @buf_size parameter is only valid when the action is set to + * %IEEE80211_AMPDU_TX_OPERATIONAL and indicates the peer's reorder + * buffer size (number of subframes) for this session -- aggregates + * containing more subframes than this may not be transmitted to the peer. * Returns a negative error code on failure. * The callback can sleep. * @@ -1833,7 +1837,8 @@ struct ieee80211_ops { int (*ampdu_action)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn); + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size); int (*get_survey)(struct ieee80211_hw *hw, int idx, struct survey_info *survey); void (*rfkill_poll)(struct ieee80211_hw *hw); diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 002db5e86eb6..1f51f4162426 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -76,7 +76,7 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, #endif /* CONFIG_MAC80211_HT_DEBUG */ if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP, - &sta->sta, tid, NULL)) + &sta->sta, tid, NULL, 0)) printk(KERN_DEBUG "HW problem - can not stop rx " "aggregation for tid %d\n", tid); @@ -297,7 +297,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, } ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START, - &sta->sta, tid, &start_seq_num); + &sta->sta, tid, &start_seq_num, 0); #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Rx A-MPDU request on tid %d result %d\n", tid, ret); #endif /* CONFIG_MAC80211_HT_DEBUG */ diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 9cc472c6a6a5..42f7c9007331 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -190,7 +190,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_TX_STOP, - &sta->sta, tid, NULL); + &sta->sta, tid, NULL, 0); /* HW shall not deny going back to legacy */ if (WARN_ON(ret)) { @@ -311,7 +311,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) start_seq_num = sta->tid_seq[tid] >> 4; ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START, - &sta->sta, tid, &start_seq_num); + &sta->sta, tid, &start_seq_num, 0); if (ret) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "BA request denied - HW unavailable for" @@ -487,7 +487,8 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_TX_OPERATIONAL, - &sta->sta, tid, NULL); + &sta->sta, tid, NULL, + sta->ampdu_mlme.tid_tx[tid]->buf_size); /* * synchronize with TX path, while splicing the TX path @@ -742,9 +743,11 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, { struct tid_ampdu_tx *tid_tx; u16 capab, tid; + u8 buf_size; capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab); tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; + buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6; mutex_lock(&sta->ampdu_mlme.mtx); @@ -767,12 +770,23 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, if (le16_to_cpu(mgmt->u.action.u.addba_resp.status) == WLAN_STATUS_SUCCESS) { + /* + * IEEE 802.11-2007 7.3.1.14: + * In an ADDBA Response frame, when the Status Code field + * is set to 0, the Buffer Size subfield is set to a value + * of at least 1. + */ + if (!buf_size) + goto out; + if (test_and_set_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) { /* ignore duplicate response */ goto out; } + tid_tx->buf_size = buf_size; + if (test_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state)) ieee80211_agg_tx_operational(local, sta, tid); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 98d589960a49..78af32d4bc58 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -382,17 +382,17 @@ static inline int drv_ampdu_action(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, - u16 *ssn) + u16 *ssn, u8 buf_size) { int ret = -EOPNOTSUPP; might_sleep(); - trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn); + trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size); if (local->ops->ampdu_action) ret = local->ops->ampdu_action(&local->hw, &sdata->vif, action, - sta, tid, ssn); + sta, tid, ssn, buf_size); trace_drv_return_int(local, ret); diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 49c84218b2f4..fbabbc2f181a 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -784,9 +784,9 @@ TRACE_EVENT(drv_ampdu_action, struct ieee80211_sub_if_data *sdata, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, - u16 *ssn), + u16 *ssn, u8 buf_size), - TP_ARGS(local, sdata, action, sta, tid, ssn), + TP_ARGS(local, sdata, action, sta, tid, ssn, buf_size), TP_STRUCT__entry( LOCAL_ENTRY @@ -794,6 +794,7 @@ TRACE_EVENT(drv_ampdu_action, __field(u32, action) __field(u16, tid) __field(u16, ssn) + __field(u8, buf_size) VIF_ENTRY ), @@ -804,11 +805,13 @@ TRACE_EVENT(drv_ampdu_action, __entry->action = action; __entry->tid = tid; __entry->ssn = ssn ? *ssn : 0; + __entry->buf_size = buf_size; ), TP_printk( - LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d", - LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action, __entry->tid + LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d buf:%d", + LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action, + __entry->tid, __entry->buf_size ) ); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index bbdd2a86a94b..ca0b69060ef7 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -82,6 +82,7 @@ enum ieee80211_sta_info_flags { * @state: session state (see above) * @stop_initiator: initiator of a session stop * @tx_stop: TX DelBA frame when stopping + * @buf_size: reorder buffer size at receiver * * This structure's lifetime is managed by RCU, assignments to * the array holding it must hold the aggregation mutex. @@ -101,6 +102,7 @@ struct tid_ampdu_tx { u8 dialog_token; u8 stop_initiator; bool tx_stop; + u8 buf_size; }; /** -- cgit v1.2.3 From 5dd36bc933e8be84f8369ac64505a2938f9ce036 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 18 Jan 2011 13:52:23 +0100 Subject: mac80211: allow advertising correct maximum aggregate size Currently, mac80211 always advertises that it may send up to 64 subframes in an aggregate. This is fine, since it's the max, but might as well be set to zero instead since it doesn't have any information. However, drivers might have that information, so allow them to set a variable giving it, which will then be used. The default of zero will be fine since to the peer that means we don't know and it will just use its own limit for the buffer size. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 5 +++++ net/mac80211/agg-tx.c | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 5afe341b4010..d6b0045788ce 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1154,6 +1154,10 @@ enum ieee80211_hw_flags { * This is only relevant if the device has restrictions on the * number of subframes, if it relies on mac80211 to do reordering * it shouldn't be set. + * + * @max_tx_aggregation_subframes: maximum number of subframes in an + * aggregate an HT driver will transmit, used by the peer as a + * hint to size its reorder buffer. */ struct ieee80211_hw { struct ieee80211_conf conf; @@ -1173,6 +1177,7 @@ struct ieee80211_hw { u8 max_report_rates; u8 max_rate_tries; u8 max_rx_aggregation_subframes; + u8 max_tx_aggregation_subframes; }; /** diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 42f7c9007331..63d852cb4ca2 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -342,7 +342,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) /* send AddBA request */ ieee80211_send_addba_request(sdata, sta->sta.addr, tid, tid_tx->dialog_token, start_seq_num, - 0x40, tid_tx->timeout); + local->hw.max_tx_aggregation_subframes, + tid_tx->timeout); } int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, -- cgit v1.2.3 From cc4fc022571376412986e27e08b0765e9cb2aafb Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 18 Jan 2011 17:32:40 +0100 Subject: netfilter: xtables: connlimit revision 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds destination address-based selection. The old "inverse" member is overloaded (memory-wise) with a new "flags" variable, similar to how J.Park did it with xt_string rev 1. Since revision 0 userspace only sets flag 0x1, no great changes are made to explicitly test for different revisions. Signed-off-by: Jan Engelhardt --- Documentation/feature-removal-schedule.txt | 7 +++++ include/linux/netfilter/xt_connlimit.h | 12 ++++++++ net/netfilter/xt_connlimit.c | 44 ++++++++++++++++++++---------- 3 files changed, 49 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 22f10818c2b3..45cc8044dad6 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -576,3 +576,10 @@ Why: The functions have been superceded by cancel_delayed_work_sync() Who: Tejun Heo ---------------------------- + +What: xt_connlimit rev 0 +When: 2012 +Who: Jan Engelhardt +Files: net/netfilter/xt_connlimit.c + +---------------------------- diff --git a/include/linux/netfilter/xt_connlimit.h b/include/linux/netfilter/xt_connlimit.h index 7e3284bcbd2b..8884efc605c7 100644 --- a/include/linux/netfilter/xt_connlimit.h +++ b/include/linux/netfilter/xt_connlimit.h @@ -3,6 +3,11 @@ struct xt_connlimit_data; +enum { + XT_CONNLIMIT_INVERT = 1 << 0, + XT_CONNLIMIT_DADDR = 1 << 1, +}; + struct xt_connlimit_info { union { union nf_inet_addr mask; @@ -14,6 +19,13 @@ struct xt_connlimit_info { #endif }; unsigned int limit, inverse; + union { + /* revision 0 */ + unsigned int inverse; + + /* revision 1 */ + __u32 flags; + }; /* Used internally by the kernel */ struct xt_connlimit_data *data __attribute__((aligned(8))); diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index 452bc16af56c..7fd3fd51f274 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -193,10 +193,12 @@ connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) if (par->family == NFPROTO_IPV6) { const struct ipv6hdr *iph = ipv6_hdr(skb); - memcpy(&addr.ip6, &iph->saddr, sizeof(iph->saddr)); + memcpy(&addr.ip6, (info->flags & XT_CONNLIMIT_DADDR) ? + &iph->daddr : &iph->saddr, sizeof(addr.ip6)); } else { const struct iphdr *iph = ip_hdr(skb); - addr.ip = iph->saddr; + addr.ip = (info->flags & XT_CONNLIMIT_DADDR) ? + iph->daddr : iph->saddr; } spin_lock_bh(&info->data->lock); @@ -208,7 +210,8 @@ connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) /* kmalloc failed, drop it entirely */ goto hotdrop; - return (connections > info->limit) ^ info->inverse; + return (connections > info->limit) ^ + !!(info->flags & XT_CONNLIMIT_INVERT); hotdrop: par->hotdrop = true; @@ -266,25 +269,38 @@ static void connlimit_mt_destroy(const struct xt_mtdtor_param *par) kfree(info->data); } -static struct xt_match connlimit_mt_reg __read_mostly = { - .name = "connlimit", - .revision = 0, - .family = NFPROTO_UNSPEC, - .checkentry = connlimit_mt_check, - .match = connlimit_mt, - .matchsize = sizeof(struct xt_connlimit_info), - .destroy = connlimit_mt_destroy, - .me = THIS_MODULE, +static struct xt_match connlimit_mt_reg[] __read_mostly = { + { + .name = "connlimit", + .revision = 0, + .family = NFPROTO_UNSPEC, + .checkentry = connlimit_mt_check, + .match = connlimit_mt, + .matchsize = sizeof(struct xt_connlimit_info), + .destroy = connlimit_mt_destroy, + .me = THIS_MODULE, + }, + { + .name = "connlimit", + .revision = 1, + .family = NFPROTO_UNSPEC, + .checkentry = connlimit_mt_check, + .match = connlimit_mt, + .matchsize = sizeof(struct xt_connlimit_info), + .destroy = connlimit_mt_destroy, + .me = THIS_MODULE, + }, }; static int __init connlimit_mt_init(void) { - return xt_register_match(&connlimit_mt_reg); + return xt_register_matches(connlimit_mt_reg, + ARRAY_SIZE(connlimit_mt_reg)); } static void __exit connlimit_mt_exit(void) { - xt_unregister_match(&connlimit_mt_reg); + xt_unregister_matches(connlimit_mt_reg, ARRAY_SIZE(connlimit_mt_reg)); } module_init(connlimit_mt_init); -- cgit v1.2.3 From cbda10fa97d72c7a1923be4426171aa90e8c6dab Mon Sep 17 00:00:00 2001 From: Vlad Dogaru Date: Thu, 13 Jan 2011 23:38:30 +0000 Subject: net_device: add support for network device groups Net devices can now be grouped, enabling simpler manipulation from userspace. This patch adds a group field to the net_device structure, as well as rtnetlink support to query and modify it. Signed-off-by: Vlad Dogaru Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/linux/if_link.h | 1 + include/linux/netdevice.h | 7 +++++++ net/core/dev.c | 12 ++++++++++++ net/core/rtnetlink.c | 6 ++++++ 4 files changed, 26 insertions(+) (limited to 'include') diff --git a/include/linux/if_link.h b/include/linux/if_link.h index 6485d2a89bec..f4a2e6b1b864 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h @@ -135,6 +135,7 @@ enum { IFLA_VF_PORTS, IFLA_PORT_SELF, IFLA_AF_SPEC, + IFLA_GROUP, /* Group the device belongs to */ __IFLA_MAX }; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d971346b0340..68a4627b74f5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -75,6 +75,9 @@ struct wireless_dev; #define NET_RX_SUCCESS 0 /* keep 'em coming, baby */ #define NET_RX_DROP 1 /* packet dropped */ +/* Initial net device group. All devices belong to group 0 by default. */ +#define INIT_NETDEV_GROUP 0 + /* * Transmit return codes: transmit return codes originate from three different * namespaces: @@ -1153,6 +1156,9 @@ struct net_device { /* phy device may attach itself for hardware timestamping */ struct phy_device *phydev; + + /* group the device belongs to */ + int group; }; #define to_net_dev(d) container_of(d, struct net_device, dev) @@ -1844,6 +1850,7 @@ extern int dev_set_alias(struct net_device *, const char *, size_t); extern int dev_change_net_namespace(struct net_device *, struct net *, const char *); extern int dev_set_mtu(struct net_device *, int); +extern void dev_set_group(struct net_device *, int); extern int dev_set_mac_address(struct net_device *, struct sockaddr *); extern int dev_hard_start_xmit(struct sk_buff *skb, diff --git a/net/core/dev.c b/net/core/dev.c index 7741507429f4..2b85d4ae981f 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4571,6 +4571,17 @@ int dev_set_mtu(struct net_device *dev, int new_mtu) } EXPORT_SYMBOL(dev_set_mtu); +/** + * dev_set_group - Change group this device belongs to + * @dev: device + * @new_group: group this device should belong to + */ +void dev_set_group(struct net_device *dev, int new_group) +{ + dev->group = new_group; +} +EXPORT_SYMBOL(dev_set_group); + /** * dev_set_mac_address - Change Media Access Control Address * @dev: device @@ -5678,6 +5689,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, dev->priv_flags = IFF_XMIT_DST_RELEASE; setup(dev); strcpy(dev->name, name); + dev->group = INIT_NETDEV_GROUP; return dev; free_pcpu: diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index a5f7535aab5b..09062b07bf7f 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -868,6 +868,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, netif_running(dev) ? dev->operstate : IF_OPER_DOWN); NLA_PUT_U8(skb, IFLA_LINKMODE, dev->link_mode); NLA_PUT_U32(skb, IFLA_MTU, dev->mtu); + NLA_PUT_U32(skb, IFLA_GROUP, dev->group); if (dev->ifindex != dev->iflink) NLA_PUT_U32(skb, IFLA_LINK, dev->iflink); @@ -1265,6 +1266,11 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, modified = 1; } + if (tb[IFLA_GROUP]) { + dev_set_group(dev, nla_get_u32(tb[IFLA_GROUP])); + modified = 1; + } + /* * Interface selected by interface index but interface * name provided implies that a name change has been -- cgit v1.2.3 From 4f57c087de9b46182545676d2c594120a20f2e58 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 17 Jan 2011 08:06:04 +0000 Subject: net: implement mechanism for HW based QOS This patch provides a mechanism for lower layer devices to steer traffic using skb->priority to tx queues. This allows for hardware based QOS schemes to use the default qdisc without incurring the penalties related to global state and the qdisc lock. While reliably receiving skbs on the correct tx ring to avoid head of line blocking resulting from shuffling in the LLD. Finally, all the goodness from txq caching and xps/rps can still be leveraged. Many drivers and hardware exist with the ability to implement QOS schemes in the hardware but currently these drivers tend to rely on firmware to reroute specific traffic, a driver specific select_queue or the queue_mapping action in the qdisc. By using select_queue for this drivers need to be updated for each and every traffic type and we lose the goodness of much of the upstream work. Firmware solutions are inherently inflexible. And finally if admins are expected to build a qdisc and filter rules to steer traffic this requires knowledge of how the hardware is currently configured. The number of tx queues and the queue offsets may change depending on resources. Also this approach incurs all the overhead of a qdisc with filters. With the mechanism in this patch users can set skb priority using expected methods ie setsockopt() or the stack can set the priority directly. Then the skb will be steered to the correct tx queues aligned with hardware QOS traffic classes. In the normal case with single traffic class and all queues in this class everything works as is until the LLD enables multiple tcs. To steer the skb we mask out the lower 4 bits of the priority and allow the hardware to configure upto 15 distinct classes of traffic. This is expected to be sufficient for most applications at any rate it is more then the 8021Q spec designates and is equal to the number of prio bands currently implemented in the default qdisc. This in conjunction with a userspace application such as lldpad can be used to implement 8021Q transmission selection algorithms one of these algorithms being the extended transmission selection algorithm currently being used for DCB. Signed-off-by: John Fastabend Signed-off-by: David S. Miller --- include/linux/netdevice.h | 68 +++++++++++++++++++++++++++++++++++++++++++++++ net/core/dev.c | 55 +++++++++++++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 68a4627b74f5..371fa8839d51 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -646,6 +646,14 @@ struct xps_dev_maps { (nr_cpu_ids * sizeof(struct xps_map *))) #endif /* CONFIG_XPS */ +#define TC_MAX_QUEUE 16 +#define TC_BITMASK 15 +/* HW offloaded queuing disciplines txq count and offset maps */ +struct netdev_tc_txq { + u16 count; + u16 offset; +}; + /* * This structure defines the management hooks for network devices. * The following hooks can be defined; unless noted otherwise, they are @@ -756,6 +764,11 @@ struct xps_dev_maps { * int (*ndo_set_vf_port)(struct net_device *dev, int vf, * struct nlattr *port[]); * int (*ndo_get_vf_port)(struct net_device *dev, int vf, struct sk_buff *skb); + * int (*ndo_setup_tc)(struct net_device *dev, u8 tc) + * Called to setup 'tc' number of traffic classes in the net device. This + * is always called from the stack with the rtnl lock held and netif tx + * queues stopped. This allows the netdevice to perform queue management + * safely. */ #define HAVE_NET_DEVICE_OPS struct net_device_ops { @@ -814,6 +827,7 @@ struct net_device_ops { struct nlattr *port[]); int (*ndo_get_vf_port)(struct net_device *dev, int vf, struct sk_buff *skb); + int (*ndo_setup_tc)(struct net_device *dev, u8 tc); #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) int (*ndo_fcoe_enable)(struct net_device *dev); int (*ndo_fcoe_disable)(struct net_device *dev); @@ -1146,6 +1160,9 @@ struct net_device { /* Data Center Bridging netlink ops */ const struct dcbnl_rtnl_ops *dcbnl_ops; #endif + u8 num_tc; + struct netdev_tc_txq tc_to_txq[TC_MAX_QUEUE]; + u8 prio_tc_map[TC_BITMASK + 1]; #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) /* max exchange id for FCoE LRO by ddp */ @@ -1164,6 +1181,57 @@ struct net_device { #define NETDEV_ALIGN 32 +static inline +int netdev_get_prio_tc_map(const struct net_device *dev, u32 prio) +{ + return dev->prio_tc_map[prio & TC_BITMASK]; +} + +static inline +int netdev_set_prio_tc_map(struct net_device *dev, u8 prio, u8 tc) +{ + if (tc >= dev->num_tc) + return -EINVAL; + + dev->prio_tc_map[prio & TC_BITMASK] = tc & TC_BITMASK; + return 0; +} + +static inline +void netdev_reset_tc(struct net_device *dev) +{ + dev->num_tc = 0; + memset(dev->tc_to_txq, 0, sizeof(dev->tc_to_txq)); + memset(dev->prio_tc_map, 0, sizeof(dev->prio_tc_map)); +} + +static inline +int netdev_set_tc_queue(struct net_device *dev, u8 tc, u16 count, u16 offset) +{ + if (tc >= dev->num_tc) + return -EINVAL; + + dev->tc_to_txq[tc].count = count; + dev->tc_to_txq[tc].offset = offset; + return 0; +} + +static inline +int netdev_set_num_tc(struct net_device *dev, u8 num_tc) +{ + if (num_tc > TC_MAX_QUEUE) + return -EINVAL; + + dev->num_tc = num_tc; + return 0; +} + +static inline +int netdev_get_num_tc(struct net_device *dev) +{ + return dev->num_tc; +} + static inline struct netdev_queue *netdev_get_tx_queue(const struct net_device *dev, unsigned int index) diff --git a/net/core/dev.c b/net/core/dev.c index 2b85d4ae981f..8b1d886ed23b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1593,6 +1593,48 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) rcu_read_unlock(); } +/* netif_setup_tc - Handle tc mappings on real_num_tx_queues change + * @dev: Network device + * @txq: number of queues available + * + * If real_num_tx_queues is changed the tc mappings may no longer be + * valid. To resolve this verify the tc mapping remains valid and if + * not NULL the mapping. With no priorities mapping to this + * offset/count pair it will no longer be used. In the worst case TC0 + * is invalid nothing can be done so disable priority mappings. If is + * expected that drivers will fix this mapping if they can before + * calling netif_set_real_num_tx_queues. + */ +void netif_setup_tc(struct net_device *dev, unsigned int txq) +{ + int i; + struct netdev_tc_txq *tc = &dev->tc_to_txq[0]; + + /* If TC0 is invalidated disable TC mapping */ + if (tc->offset + tc->count > txq) { + pr_warning("Number of in use tx queues changed " + "invalidating tc mappings. Priority " + "traffic classification disabled!\n"); + dev->num_tc = 0; + return; + } + + /* Invalidated prio to tc mappings set to TC0 */ + for (i = 1; i < TC_BITMASK + 1; i++) { + int q = netdev_get_prio_tc_map(dev, i); + + tc = &dev->tc_to_txq[q]; + if (tc->offset + tc->count > txq) { + pr_warning("Number of in use tx queues " + "changed. Priority %i to tc " + "mapping %i is no longer valid " + "setting map to 0\n", + i, q); + netdev_set_prio_tc_map(dev, i, 0); + } + } +} + /* * Routine to help set real_num_tx_queues. To avoid skbs mapped to queues * greater then real_num_tx_queues stale skbs on the qdisc must be flushed. @@ -1612,6 +1654,9 @@ int netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq) if (rc) return rc; + if (dev->num_tc) + netif_setup_tc(dev, txq); + if (txq < dev->real_num_tx_queues) qdisc_reset_all_tx_gt(dev, txq); } @@ -2161,6 +2206,8 @@ u16 __skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb, unsigned int num_tx_queues) { u32 hash; + u16 qoffset = 0; + u16 qcount = num_tx_queues; if (skb_rx_queue_recorded(skb)) { hash = skb_get_rx_queue(skb); @@ -2169,13 +2216,19 @@ u16 __skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb, return hash; } + if (dev->num_tc) { + u8 tc = netdev_get_prio_tc_map(dev, skb->priority); + qoffset = dev->tc_to_txq[tc].offset; + qcount = dev->tc_to_txq[tc].count; + } + if (skb->sk && skb->sk->sk_hash) hash = skb->sk->sk_hash; else hash = (__force u16) skb->protocol ^ skb->rxhash; hash = jhash_1word(hash, hashrnd); - return (u16) (((u64) hash * num_tx_queues) >> 32); + return (u16) (((u64) hash * qcount) >> 32) + qoffset; } EXPORT_SYMBOL(__skb_tx_hash); -- cgit v1.2.3 From b8970f0bfc78103cb74c66055de7379b15097840 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 17 Jan 2011 08:06:09 +0000 Subject: net_sched: implement a root container qdisc sch_mqprio This implements a mqprio queueing discipline that by default creates a pfifo_fast qdisc per tx queue and provides the needed configuration interface. Using the mqprio qdisc the number of tcs currently in use along with the range of queues alloted to each class can be configured. By default skbs are mapped to traffic classes using the skb priority. This mapping is configurable. Configurable parameters, struct tc_mqprio_qopt { __u8 num_tc; __u8 prio_tc_map[TC_BITMASK + 1]; __u8 hw; __u16 count[TC_MAX_QUEUE]; __u16 offset[TC_MAX_QUEUE]; }; Here the count/offset pairing give the queue alignment and the prio_tc_map gives the mapping from skb->priority to tc. The hw bit determines if the hardware should configure the count and offset values. If the hardware bit is set then the operation will fail if the hardware does not implement the ndo_setup_tc operation. This is to avoid undetermined states where the hardware may or may not control the queue mapping. Also minimal bounds checking is done on the count/offset to verify a queue does not exceed num_tx_queues and that queue ranges do not overlap. Otherwise it is left to user policy or hardware configuration to create useful mappings. It is expected that hardware QOS schemes can be implemented by creating appropriate mappings of queues in ndo_tc_setup(). One expected use case is drivers will use the ndo_setup_tc to map queue ranges onto 802.1Q traffic classes. This provides a generic mechanism to map network traffic onto these traffic classes and removes the need for lower layer drivers to know specifics about traffic types. Signed-off-by: John Fastabend Signed-off-by: David S. Miller --- include/linux/pkt_sched.h | 12 ++ net/sched/Kconfig | 12 ++ net/sched/Makefile | 1 + net/sched/sch_generic.c | 4 + net/sched/sch_mqprio.c | 417 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 446 insertions(+) create mode 100644 net/sched/sch_mqprio.c (limited to 'include') diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h index 2cfa4bc8dea6..776cd93d5f7b 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -481,4 +481,16 @@ struct tc_drr_stats { __u32 deficit; }; +/* MQPRIO */ +#define TC_QOPT_BITMASK 15 +#define TC_QOPT_MAX_QUEUE 16 + +struct tc_mqprio_qopt { + __u8 num_tc; + __u8 prio_tc_map[TC_QOPT_BITMASK + 1]; + __u8 hw; + __u16 count[TC_QOPT_MAX_QUEUE]; + __u16 offset[TC_QOPT_MAX_QUEUE]; +}; + #endif diff --git a/net/sched/Kconfig b/net/sched/Kconfig index f04d4a484d53..73431d4aa6ef 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -205,6 +205,18 @@ config NET_SCH_DRR If unsure, say N. +config NET_SCH_MQPRIO + tristate "Multi-queue priority scheduler (MQPRIO)" + help + Say Y here if you want to use the Multi-queue Priority scheduler. + This scheduler allows QOS to be offloaded on NICs that have support + for offloading QOS schedulers. + + To compile this driver as a module, choose M here: the module will + be called sch_mqprio. + + If unsure, say N. + config NET_SCH_INGRESS tristate "Ingress Qdisc" depends on NET_CLS_ACT diff --git a/net/sched/Makefile b/net/sched/Makefile index 960f5dba6304..26ce681a2c60 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_NET_SCH_MULTIQ) += sch_multiq.o obj-$(CONFIG_NET_SCH_ATM) += sch_atm.o obj-$(CONFIG_NET_SCH_NETEM) += sch_netem.o obj-$(CONFIG_NET_SCH_DRR) += sch_drr.o +obj-$(CONFIG_NET_SCH_MQPRIO) += sch_mqprio.o obj-$(CONFIG_NET_CLS_U32) += cls_u32.o obj-$(CONFIG_NET_CLS_ROUTE4) += cls_route.o obj-$(CONFIG_NET_CLS_FW) += cls_fw.o diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 34dc598440a2..723b27849a50 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -540,6 +540,7 @@ struct Qdisc_ops pfifo_fast_ops __read_mostly = { .dump = pfifo_fast_dump, .owner = THIS_MODULE, }; +EXPORT_SYMBOL(pfifo_fast_ops); struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, struct Qdisc_ops *ops) @@ -674,6 +675,7 @@ struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue, return oqdisc; } +EXPORT_SYMBOL(dev_graft_qdisc); static void attach_one_default_qdisc(struct net_device *dev, struct netdev_queue *dev_queue, @@ -761,6 +763,7 @@ void dev_activate(struct net_device *dev) dev_watchdog_up(dev); } } +EXPORT_SYMBOL(dev_activate); static void dev_deactivate_queue(struct net_device *dev, struct netdev_queue *dev_queue, @@ -840,6 +843,7 @@ void dev_deactivate(struct net_device *dev) list_add(&dev->unreg_list, &single); dev_deactivate_many(&single); } +EXPORT_SYMBOL(dev_deactivate); static void dev_init_scheduler_queue(struct net_device *dev, struct netdev_queue *dev_queue, diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c new file mode 100644 index 000000000000..8620c65f480a --- /dev/null +++ b/net/sched/sch_mqprio.c @@ -0,0 +1,417 @@ +/* + * net/sched/sch_mqprio.c + * + * Copyright (c) 2010 John Fastabend + * + * 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 +#include + +struct mqprio_sched { + struct Qdisc **qdiscs; + int hw_owned; +}; + +static void mqprio_destroy(struct Qdisc *sch) +{ + struct net_device *dev = qdisc_dev(sch); + struct mqprio_sched *priv = qdisc_priv(sch); + unsigned int ntx; + + if (!priv->qdiscs) + return; + + for (ntx = 0; ntx < dev->num_tx_queues && priv->qdiscs[ntx]; ntx++) + qdisc_destroy(priv->qdiscs[ntx]); + + if (priv->hw_owned && dev->netdev_ops->ndo_setup_tc) + dev->netdev_ops->ndo_setup_tc(dev, 0); + else + netdev_set_num_tc(dev, 0); + + kfree(priv->qdiscs); +} + +static int mqprio_parse_opt(struct net_device *dev, struct tc_mqprio_qopt *qopt) +{ + int i, j; + + /* Verify num_tc is not out of max range */ + if (qopt->num_tc > TC_MAX_QUEUE) + return -EINVAL; + + /* Verify priority mapping uses valid tcs */ + for (i = 0; i < TC_BITMASK + 1; i++) { + if (qopt->prio_tc_map[i] >= qopt->num_tc) + return -EINVAL; + } + + /* net_device does not support requested operation */ + if (qopt->hw && !dev->netdev_ops->ndo_setup_tc) + return -EINVAL; + + /* if hw owned qcount and qoffset are taken from LLD so + * no reason to verify them here + */ + if (qopt->hw) + return 0; + + for (i = 0; i < qopt->num_tc; i++) { + unsigned int last = qopt->offset[i] + qopt->count[i]; + + /* Verify the queue count is in tx range being equal to the + * real_num_tx_queues indicates the last queue is in use. + */ + if (qopt->offset[i] >= dev->real_num_tx_queues || + !qopt->count[i] || + last > dev->real_num_tx_queues) + return -EINVAL; + + /* Verify that the offset and counts do not overlap */ + for (j = i + 1; j < qopt->num_tc; j++) { + if (last > qopt->offset[j]) + return -EINVAL; + } + } + + return 0; +} + +static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) +{ + struct net_device *dev = qdisc_dev(sch); + struct mqprio_sched *priv = qdisc_priv(sch); + struct netdev_queue *dev_queue; + struct Qdisc *qdisc; + int i, err = -EOPNOTSUPP; + struct tc_mqprio_qopt *qopt = NULL; + + BUILD_BUG_ON(TC_MAX_QUEUE != TC_QOPT_MAX_QUEUE); + BUILD_BUG_ON(TC_BITMASK != TC_QOPT_BITMASK); + + if (sch->parent != TC_H_ROOT) + return -EOPNOTSUPP; + + if (!netif_is_multiqueue(dev)) + return -EOPNOTSUPP; + + if (nla_len(opt) < sizeof(*qopt)) + return -EINVAL; + + qopt = nla_data(opt); + if (mqprio_parse_opt(dev, qopt)) + return -EINVAL; + + /* pre-allocate qdisc, attachment can't fail */ + priv->qdiscs = kcalloc(dev->num_tx_queues, sizeof(priv->qdiscs[0]), + GFP_KERNEL); + if (priv->qdiscs == NULL) { + err = -ENOMEM; + goto err; + } + + for (i = 0; i < dev->num_tx_queues; i++) { + dev_queue = netdev_get_tx_queue(dev, i); + qdisc = qdisc_create_dflt(dev_queue, &pfifo_fast_ops, + TC_H_MAKE(TC_H_MAJ(sch->handle), + TC_H_MIN(i + 1))); + if (qdisc == NULL) { + err = -ENOMEM; + goto err; + } + qdisc->flags |= TCQ_F_CAN_BYPASS; + priv->qdiscs[i] = qdisc; + } + + /* If the mqprio options indicate that hardware should own + * the queue mapping then run ndo_setup_tc otherwise use the + * supplied and verified mapping + */ + if (qopt->hw) { + priv->hw_owned = 1; + err = dev->netdev_ops->ndo_setup_tc(dev, qopt->num_tc); + if (err) + goto err; + } else { + netdev_set_num_tc(dev, qopt->num_tc); + for (i = 0; i < qopt->num_tc; i++) + netdev_set_tc_queue(dev, i, + qopt->count[i], qopt->offset[i]); + } + + /* Always use supplied priority mappings */ + for (i = 0; i < TC_BITMASK + 1; i++) + netdev_set_prio_tc_map(dev, i, qopt->prio_tc_map[i]); + + sch->flags |= TCQ_F_MQROOT; + return 0; + +err: + mqprio_destroy(sch); + return err; +} + +static void mqprio_attach(struct Qdisc *sch) +{ + struct net_device *dev = qdisc_dev(sch); + struct mqprio_sched *priv = qdisc_priv(sch); + struct Qdisc *qdisc; + unsigned int ntx; + + /* Attach underlying qdisc */ + for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { + qdisc = priv->qdiscs[ntx]; + qdisc = dev_graft_qdisc(qdisc->dev_queue, qdisc); + if (qdisc) + qdisc_destroy(qdisc); + } + kfree(priv->qdiscs); + priv->qdiscs = NULL; +} + +static struct netdev_queue *mqprio_queue_get(struct Qdisc *sch, + unsigned long cl) +{ + struct net_device *dev = qdisc_dev(sch); + unsigned long ntx = cl - 1 - netdev_get_num_tc(dev); + + if (ntx >= dev->num_tx_queues) + return NULL; + return netdev_get_tx_queue(dev, ntx); +} + +static int mqprio_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new, + struct Qdisc **old) +{ + struct net_device *dev = qdisc_dev(sch); + struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl); + + if (!dev_queue) + return -EINVAL; + + if (dev->flags & IFF_UP) + dev_deactivate(dev); + + *old = dev_graft_qdisc(dev_queue, new); + + if (dev->flags & IFF_UP) + dev_activate(dev); + + return 0; +} + +static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb) +{ + struct net_device *dev = qdisc_dev(sch); + struct mqprio_sched *priv = qdisc_priv(sch); + unsigned char *b = skb_tail_pointer(skb); + struct tc_mqprio_qopt opt; + struct Qdisc *qdisc; + unsigned int i; + + sch->q.qlen = 0; + memset(&sch->bstats, 0, sizeof(sch->bstats)); + memset(&sch->qstats, 0, sizeof(sch->qstats)); + + for (i = 0; i < dev->num_tx_queues; i++) { + qdisc = netdev_get_tx_queue(dev, i)->qdisc; + spin_lock_bh(qdisc_lock(qdisc)); + sch->q.qlen += qdisc->q.qlen; + sch->bstats.bytes += qdisc->bstats.bytes; + sch->bstats.packets += qdisc->bstats.packets; + sch->qstats.qlen += qdisc->qstats.qlen; + sch->qstats.backlog += qdisc->qstats.backlog; + sch->qstats.drops += qdisc->qstats.drops; + sch->qstats.requeues += qdisc->qstats.requeues; + sch->qstats.overlimits += qdisc->qstats.overlimits; + spin_unlock_bh(qdisc_lock(qdisc)); + } + + opt.num_tc = netdev_get_num_tc(dev); + memcpy(opt.prio_tc_map, dev->prio_tc_map, sizeof(opt.prio_tc_map)); + opt.hw = priv->hw_owned; + + for (i = 0; i < netdev_get_num_tc(dev); i++) { + opt.count[i] = dev->tc_to_txq[i].count; + opt.offset[i] = dev->tc_to_txq[i].offset; + } + + NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); + + return skb->len; +nla_put_failure: + nlmsg_trim(skb, b); + return -1; +} + +static struct Qdisc *mqprio_leaf(struct Qdisc *sch, unsigned long cl) +{ + struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl); + + if (!dev_queue) + return NULL; + + return dev_queue->qdisc_sleeping; +} + +static unsigned long mqprio_get(struct Qdisc *sch, u32 classid) +{ + struct net_device *dev = qdisc_dev(sch); + unsigned int ntx = TC_H_MIN(classid); + + if (ntx > dev->num_tx_queues + netdev_get_num_tc(dev)) + return 0; + 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) +{ + struct net_device *dev = qdisc_dev(sch); + + if (cl <= netdev_get_num_tc(dev)) { + tcm->tcm_parent = TC_H_ROOT; + tcm->tcm_info = 0; + } else { + int i; + struct netdev_queue *dev_queue; + + dev_queue = mqprio_queue_get(sch, cl); + tcm->tcm_parent = 0; + for (i = 0; i < netdev_get_num_tc(dev); i++) { + struct netdev_tc_txq tc = dev->tc_to_txq[i]; + int q_idx = cl - netdev_get_num_tc(dev); + + if (q_idx > tc.offset && + q_idx <= tc.offset + tc.count) { + tcm->tcm_parent = + TC_H_MAKE(TC_H_MAJ(sch->handle), + TC_H_MIN(i + 1)); + break; + } + } + tcm->tcm_info = dev_queue->qdisc_sleeping->handle; + } + tcm->tcm_handle |= TC_H_MIN(cl); + return 0; +} + +static int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl, + struct gnet_dump *d) +{ + struct net_device *dev = qdisc_dev(sch); + + if (cl <= netdev_get_num_tc(dev)) { + int i; + struct Qdisc *qdisc; + struct gnet_stats_queue qstats = {0}; + struct gnet_stats_basic_packed bstats = {0}; + struct netdev_tc_txq tc = dev->tc_to_txq[cl - 1]; + + /* Drop lock here it will be reclaimed before touching + * statistics this is required because the d->lock we + * hold here is the look on dev_queue->qdisc_sleeping + * also acquired below. + */ + spin_unlock_bh(d->lock); + + for (i = tc.offset; i < tc.offset + tc.count; i++) { + qdisc = netdev_get_tx_queue(dev, i)->qdisc; + spin_lock_bh(qdisc_lock(qdisc)); + bstats.bytes += qdisc->bstats.bytes; + bstats.packets += qdisc->bstats.packets; + qstats.qlen += qdisc->qstats.qlen; + qstats.backlog += qdisc->qstats.backlog; + qstats.drops += qdisc->qstats.drops; + qstats.requeues += qdisc->qstats.requeues; + qstats.overlimits += qdisc->qstats.overlimits; + spin_unlock_bh(qdisc_lock(qdisc)); + } + /* Reclaim root sleeping lock before completing stats */ + spin_lock_bh(d->lock); + if (gnet_stats_copy_basic(d, &bstats) < 0 || + gnet_stats_copy_queue(d, &qstats) < 0) + return -1; + } else { + struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl); + + sch = dev_queue->qdisc_sleeping; + sch->qstats.qlen = sch->q.qlen; + if (gnet_stats_copy_basic(d, &sch->bstats) < 0 || + gnet_stats_copy_queue(d, &sch->qstats) < 0) + return -1; + } + return 0; +} + +static void mqprio_walk(struct Qdisc *sch, struct qdisc_walker *arg) +{ + struct net_device *dev = qdisc_dev(sch); + unsigned long ntx; + + if (arg->stop) + return; + + /* Walk hierarchy with a virtual class per tc */ + arg->count = arg->skip; + for (ntx = arg->skip; + ntx < dev->num_tx_queues + netdev_get_num_tc(dev); + ntx++) { + if (arg->fn(sch, ntx + 1, arg) < 0) { + arg->stop = 1; + break; + } + arg->count++; + } +} + +static const struct Qdisc_class_ops mqprio_class_ops = { + .graft = mqprio_graft, + .leaf = mqprio_leaf, + .get = mqprio_get, + .put = mqprio_put, + .walk = mqprio_walk, + .dump = mqprio_dump_class, + .dump_stats = mqprio_dump_class_stats, +}; + +struct Qdisc_ops mqprio_qdisc_ops __read_mostly = { + .cl_ops = &mqprio_class_ops, + .id = "mqprio", + .priv_size = sizeof(struct mqprio_sched), + .init = mqprio_init, + .destroy = mqprio_destroy, + .attach = mqprio_attach, + .dump = mqprio_dump, + .owner = THIS_MODULE, +}; + +static int __init mqprio_module_init(void) +{ + return register_qdisc(&mqprio_qdisc_ops); +} + +static void __exit mqprio_module_exit(void) +{ + unregister_qdisc(&mqprio_qdisc_ops); +} + +module_init(mqprio_module_init); +module_exit(mqprio_module_exit); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 5d8449286456659cdd0998e62d80df2d9e77e9e3 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Thu, 20 Jan 2011 08:48:15 +0100 Subject: netfilter: xtables: remove extraneous header that slipped in Commit 0b8ad87 (netfilter: xtables: add missing header files to export list) erroneously added this. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter/Kbuild | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index fc4e0aa805a2..89c0d1e20d72 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild @@ -56,7 +56,6 @@ header-y += xt_rateest.h header-y += xt_realm.h header-y += xt_recent.h header-y += xt_sctp.h -header-y += xt_secmark.h header-y += xt_socket.h header-y += xt_state.h header-y += xt_statistic.h -- cgit v1.2.3 From ba12b130a65840005770135a69199cb9adaf8c8f Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Thu, 20 Jan 2011 14:01:12 +0100 Subject: netfilter: xtables: remove duplicate member Accidentally missed removing the old out-of-union "inverse" member, which caused the struct size to change which then gives size mismatch warnings when using an old iptables. It is interesting to see that gcc did not warn about this before. (Filed http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47376 ) Signed-off-by: Jan Engelhardt --- include/linux/netfilter/xt_connlimit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/netfilter/xt_connlimit.h b/include/linux/netfilter/xt_connlimit.h index 8884efc605c7..ab1d3b57fff7 100644 --- a/include/linux/netfilter/xt_connlimit.h +++ b/include/linux/netfilter/xt_connlimit.h @@ -18,7 +18,7 @@ struct xt_connlimit_info { }; #endif }; - unsigned int limit, inverse; + unsigned int limit; union { /* revision 0 */ unsigned int inverse; -- cgit v1.2.3 From dad8e7aeeb83a26d267e757e4c1cf69591850477 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Wed, 19 Jan 2011 14:53:36 +0000 Subject: ASoC: soc-cache: Introduce the cache_bypass option This is primarily needed to avoid writing back to the cache whenever we are syncing the cache with the hardware. This gives a performance benefit especially for large register maps. Signed-off-by: Dimitris Papastamos Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-cache.c | 36 ++++++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index d244f9013767..c184f84a354c 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -488,6 +488,7 @@ struct snd_soc_codec { /* runtime */ struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ unsigned int active; + unsigned int cache_bypass:1; /* Suppress access to the cache */ unsigned int cache_only:1; /* Suppress writes to hardware */ unsigned int cache_sync:1; /* Cache needs to be synced to hardware */ unsigned int suspended:1; /* Codec is in suspend PM state */ diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index d97a59f6a249..1ebff9f12b4e 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -25,7 +25,8 @@ static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -49,7 +50,8 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, data[1] = value & 0x00ff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -106,7 +108,8 @@ static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec, unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -130,7 +133,8 @@ static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, data[1] = value & 0x00ff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -191,7 +195,8 @@ static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg, data[1] = value & 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -216,7 +221,8 @@ static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec, reg &= 0xff; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -271,7 +277,8 @@ static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg, data[2] = value & 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -295,7 +302,8 @@ static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec, unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -450,7 +458,8 @@ static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec, reg &= 0xff; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -476,7 +485,8 @@ static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg, reg &= 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -568,7 +578,8 @@ static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec, unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -595,7 +606,8 @@ static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg, data[3] = value & 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; -- cgit v1.2.3 From 06988b06935da7a210887e9d3f50f46f2faa4953 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Thu, 20 Jan 2011 17:50:17 +0100 Subject: netfilter: xtables: add missing header inclusions for headers_check Resolve these warnings on `make headers_check`: usr/include/linux/netfilter/xt_CT.h:7: found __[us]{8,16,32,64} type without #include ... Signed-off-by: Jan Engelhardt --- include/linux/netfilter/xt_CT.h | 2 ++ include/linux/netfilter/xt_TCPOPTSTRIP.h | 2 ++ include/linux/netfilter/xt_TPROXY.h | 2 ++ include/linux/netfilter/xt_cluster.h | 2 ++ include/linux/netfilter/xt_connlimit.h | 2 ++ include/linux/netfilter/xt_quota.h | 2 ++ include/linux/netfilter/xt_socket.h | 2 ++ include/linux/netfilter/xt_time.h | 2 ++ include/linux/netfilter/xt_u32.h | 2 ++ include/linux/netfilter_bridge/ebt_802_3.h | 2 ++ include/linux/netfilter_bridge/ebt_among.h | 2 ++ include/linux/netfilter_bridge/ebt_arp.h | 2 ++ include/linux/netfilter_bridge/ebt_ip.h | 2 ++ include/linux/netfilter_bridge/ebt_ip6.h | 2 ++ include/linux/netfilter_bridge/ebt_limit.h | 2 ++ include/linux/netfilter_bridge/ebt_log.h | 2 ++ include/linux/netfilter_bridge/ebt_mark_m.h | 2 ++ include/linux/netfilter_bridge/ebt_nflog.h | 2 ++ include/linux/netfilter_bridge/ebt_pkttype.h | 2 ++ include/linux/netfilter_bridge/ebt_stp.h | 2 ++ include/linux/netfilter_bridge/ebt_ulog.h | 2 ++ include/linux/netfilter_bridge/ebt_vlan.h | 2 ++ include/linux/netfilter_ipv4/ipt_CLUSTERIP.h | 2 ++ include/linux/netfilter_ipv4/ipt_ECN.h | 2 ++ include/linux/netfilter_ipv4/ipt_SAME.h | 2 ++ include/linux/netfilter_ipv4/ipt_TTL.h | 2 ++ include/linux/netfilter_ipv4/ipt_addrtype.h | 2 ++ include/linux/netfilter_ipv4/ipt_ah.h | 2 ++ include/linux/netfilter_ipv4/ipt_ecn.h | 2 ++ include/linux/netfilter_ipv4/ipt_ttl.h | 2 ++ include/linux/netfilter_ipv6/ip6t_HL.h | 2 ++ include/linux/netfilter_ipv6/ip6t_REJECT.h | 2 ++ include/linux/netfilter_ipv6/ip6t_ah.h | 2 ++ include/linux/netfilter_ipv6/ip6t_frag.h | 2 ++ include/linux/netfilter_ipv6/ip6t_hl.h | 2 ++ include/linux/netfilter_ipv6/ip6t_ipv6header.h | 2 ++ include/linux/netfilter_ipv6/ip6t_mh.h | 2 ++ include/linux/netfilter_ipv6/ip6t_opts.h | 2 ++ include/linux/netfilter_ipv6/ip6t_rt.h | 1 + 39 files changed, 77 insertions(+) (limited to 'include') diff --git a/include/linux/netfilter/xt_CT.h b/include/linux/netfilter/xt_CT.h index fbf4c5658554..b56e76811c04 100644 --- a/include/linux/netfilter/xt_CT.h +++ b/include/linux/netfilter/xt_CT.h @@ -1,6 +1,8 @@ #ifndef _XT_CT_H #define _XT_CT_H +#include + #define XT_CT_NOTRACK 0x1 struct xt_ct_target_info { diff --git a/include/linux/netfilter/xt_TCPOPTSTRIP.h b/include/linux/netfilter/xt_TCPOPTSTRIP.h index 342ef14b1761..7157318499c2 100644 --- a/include/linux/netfilter/xt_TCPOPTSTRIP.h +++ b/include/linux/netfilter/xt_TCPOPTSTRIP.h @@ -1,6 +1,8 @@ #ifndef _XT_TCPOPTSTRIP_H #define _XT_TCPOPTSTRIP_H +#include + #define tcpoptstrip_set_bit(bmap, idx) \ (bmap[(idx) >> 5] |= 1U << (idx & 31)) #define tcpoptstrip_test_bit(bmap, idx) \ diff --git a/include/linux/netfilter/xt_TPROXY.h b/include/linux/netfilter/xt_TPROXY.h index 8097e0b4c15e..902043c2073f 100644 --- a/include/linux/netfilter/xt_TPROXY.h +++ b/include/linux/netfilter/xt_TPROXY.h @@ -1,6 +1,8 @@ #ifndef _XT_TPROXY_H #define _XT_TPROXY_H +#include + /* TPROXY target is capable of marking the packet to perform * redirection. We can get rid of that whenever we get support for * mutliple targets in the same rule. */ diff --git a/include/linux/netfilter/xt_cluster.h b/include/linux/netfilter/xt_cluster.h index 66cfa3c782ac..9b883c8fbf54 100644 --- a/include/linux/netfilter/xt_cluster.h +++ b/include/linux/netfilter/xt_cluster.h @@ -1,6 +1,8 @@ #ifndef _XT_CLUSTER_MATCH_H #define _XT_CLUSTER_MATCH_H +#include + enum xt_cluster_flags { XT_CLUSTER_F_INV = (1 << 0) }; diff --git a/include/linux/netfilter/xt_connlimit.h b/include/linux/netfilter/xt_connlimit.h index ab1d3b57fff7..0ca66e97acbc 100644 --- a/include/linux/netfilter/xt_connlimit.h +++ b/include/linux/netfilter/xt_connlimit.h @@ -1,6 +1,8 @@ #ifndef _XT_CONNLIMIT_H #define _XT_CONNLIMIT_H +#include + struct xt_connlimit_data; enum { diff --git a/include/linux/netfilter/xt_quota.h b/include/linux/netfilter/xt_quota.h index 8bda65f0bc92..ca6e03e47a17 100644 --- a/include/linux/netfilter/xt_quota.h +++ b/include/linux/netfilter/xt_quota.h @@ -1,6 +1,8 @@ #ifndef _XT_QUOTA_H #define _XT_QUOTA_H +#include + enum xt_quota_flags { XT_QUOTA_INVERT = 0x1, }; diff --git a/include/linux/netfilter/xt_socket.h b/include/linux/netfilter/xt_socket.h index 6f475b8ff34b..26d7217bd4f1 100644 --- a/include/linux/netfilter/xt_socket.h +++ b/include/linux/netfilter/xt_socket.h @@ -1,6 +1,8 @@ #ifndef _XT_SOCKET_H #define _XT_SOCKET_H +#include + enum { XT_SOCKET_TRANSPARENT = 1 << 0, }; diff --git a/include/linux/netfilter/xt_time.h b/include/linux/netfilter/xt_time.h index b8bd4568efdb..7c37fac576c4 100644 --- a/include/linux/netfilter/xt_time.h +++ b/include/linux/netfilter/xt_time.h @@ -1,6 +1,8 @@ #ifndef _XT_TIME_H #define _XT_TIME_H 1 +#include + struct xt_time_info { __u32 date_start; __u32 date_stop; diff --git a/include/linux/netfilter/xt_u32.h b/include/linux/netfilter/xt_u32.h index e8c3d8722bae..04d1bfea03c2 100644 --- a/include/linux/netfilter/xt_u32.h +++ b/include/linux/netfilter/xt_u32.h @@ -1,6 +1,8 @@ #ifndef _XT_U32_H #define _XT_U32_H 1 +#include + enum xt_u32_ops { XT_U32_AND, XT_U32_LEFTSH, diff --git a/include/linux/netfilter_bridge/ebt_802_3.h b/include/linux/netfilter_bridge/ebt_802_3.h index c427764f4444..be5be1577a56 100644 --- a/include/linux/netfilter_bridge/ebt_802_3.h +++ b/include/linux/netfilter_bridge/ebt_802_3.h @@ -1,6 +1,8 @@ #ifndef __LINUX_BRIDGE_EBT_802_3_H #define __LINUX_BRIDGE_EBT_802_3_H +#include + #define EBT_802_3_SAP 0x01 #define EBT_802_3_TYPE 0x02 diff --git a/include/linux/netfilter_bridge/ebt_among.h b/include/linux/netfilter_bridge/ebt_among.h index 686c9619dbc0..bd4e3ad0b706 100644 --- a/include/linux/netfilter_bridge/ebt_among.h +++ b/include/linux/netfilter_bridge/ebt_among.h @@ -1,6 +1,8 @@ #ifndef __LINUX_BRIDGE_EBT_AMONG_H #define __LINUX_BRIDGE_EBT_AMONG_H +#include + #define EBT_AMONG_DST 0x01 #define EBT_AMONG_SRC 0x02 diff --git a/include/linux/netfilter_bridge/ebt_arp.h b/include/linux/netfilter_bridge/ebt_arp.h index e62b5af95869..522f3e427f49 100644 --- a/include/linux/netfilter_bridge/ebt_arp.h +++ b/include/linux/netfilter_bridge/ebt_arp.h @@ -1,6 +1,8 @@ #ifndef __LINUX_BRIDGE_EBT_ARP_H #define __LINUX_BRIDGE_EBT_ARP_H +#include + #define EBT_ARP_OPCODE 0x01 #define EBT_ARP_HTYPE 0x02 #define EBT_ARP_PTYPE 0x04 diff --git a/include/linux/netfilter_bridge/ebt_ip.h b/include/linux/netfilter_bridge/ebt_ip.h index d99de58da2c7..c4bbc41b0ea4 100644 --- a/include/linux/netfilter_bridge/ebt_ip.h +++ b/include/linux/netfilter_bridge/ebt_ip.h @@ -15,6 +15,8 @@ #ifndef __LINUX_BRIDGE_EBT_IP_H #define __LINUX_BRIDGE_EBT_IP_H +#include + #define EBT_IP_SOURCE 0x01 #define EBT_IP_DEST 0x02 #define EBT_IP_TOS 0x04 diff --git a/include/linux/netfilter_bridge/ebt_ip6.h b/include/linux/netfilter_bridge/ebt_ip6.h index 998e9d5a6b60..42b889682721 100644 --- a/include/linux/netfilter_bridge/ebt_ip6.h +++ b/include/linux/netfilter_bridge/ebt_ip6.h @@ -12,6 +12,8 @@ #ifndef __LINUX_BRIDGE_EBT_IP6_H #define __LINUX_BRIDGE_EBT_IP6_H +#include + #define EBT_IP6_SOURCE 0x01 #define EBT_IP6_DEST 0x02 #define EBT_IP6_TCLASS 0x04 diff --git a/include/linux/netfilter_bridge/ebt_limit.h b/include/linux/netfilter_bridge/ebt_limit.h index 721d51ffa513..66d80b30ba0e 100644 --- a/include/linux/netfilter_bridge/ebt_limit.h +++ b/include/linux/netfilter_bridge/ebt_limit.h @@ -1,6 +1,8 @@ #ifndef __LINUX_BRIDGE_EBT_LIMIT_H #define __LINUX_BRIDGE_EBT_LIMIT_H +#include + #define EBT_LIMIT_MATCH "limit" /* timings are in milliseconds. */ diff --git a/include/linux/netfilter_bridge/ebt_log.h b/include/linux/netfilter_bridge/ebt_log.h index 564beb4946ea..7e7f1d1fe494 100644 --- a/include/linux/netfilter_bridge/ebt_log.h +++ b/include/linux/netfilter_bridge/ebt_log.h @@ -1,6 +1,8 @@ #ifndef __LINUX_BRIDGE_EBT_LOG_H #define __LINUX_BRIDGE_EBT_LOG_H +#include + #define EBT_LOG_IP 0x01 /* if the frame is made by ip, log the ip information */ #define EBT_LOG_ARP 0x02 #define EBT_LOG_NFLOG 0x04 diff --git a/include/linux/netfilter_bridge/ebt_mark_m.h b/include/linux/netfilter_bridge/ebt_mark_m.h index 97b96c4b8db4..410f9e5a71d4 100644 --- a/include/linux/netfilter_bridge/ebt_mark_m.h +++ b/include/linux/netfilter_bridge/ebt_mark_m.h @@ -1,6 +1,8 @@ #ifndef __LINUX_BRIDGE_EBT_MARK_M_H #define __LINUX_BRIDGE_EBT_MARK_M_H +#include + #define EBT_MARK_AND 0x01 #define EBT_MARK_OR 0x02 #define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR) diff --git a/include/linux/netfilter_bridge/ebt_nflog.h b/include/linux/netfilter_bridge/ebt_nflog.h index 477315bc3537..df829fce9125 100644 --- a/include/linux/netfilter_bridge/ebt_nflog.h +++ b/include/linux/netfilter_bridge/ebt_nflog.h @@ -1,6 +1,8 @@ #ifndef __LINUX_BRIDGE_EBT_NFLOG_H #define __LINUX_BRIDGE_EBT_NFLOG_H +#include + #define EBT_NFLOG_MASK 0x0 #define EBT_NFLOG_PREFIX_SIZE 64 diff --git a/include/linux/netfilter_bridge/ebt_pkttype.h b/include/linux/netfilter_bridge/ebt_pkttype.h index 7c0fb0fdcf14..c241badcd036 100644 --- a/include/linux/netfilter_bridge/ebt_pkttype.h +++ b/include/linux/netfilter_bridge/ebt_pkttype.h @@ -1,6 +1,8 @@ #ifndef __LINUX_BRIDGE_EBT_PKTTYPE_H #define __LINUX_BRIDGE_EBT_PKTTYPE_H +#include + struct ebt_pkttype_info { __u8 pkt_type; __u8 invert; diff --git a/include/linux/netfilter_bridge/ebt_stp.h b/include/linux/netfilter_bridge/ebt_stp.h index 13a0bd49a92a..1025b9f5fb7d 100644 --- a/include/linux/netfilter_bridge/ebt_stp.h +++ b/include/linux/netfilter_bridge/ebt_stp.h @@ -1,6 +1,8 @@ #ifndef __LINUX_BRIDGE_EBT_STP_H #define __LINUX_BRIDGE_EBT_STP_H +#include + #define EBT_STP_TYPE 0x0001 #define EBT_STP_FLAGS 0x0002 diff --git a/include/linux/netfilter_bridge/ebt_ulog.h b/include/linux/netfilter_bridge/ebt_ulog.h index de35a51a7e46..89a6becb5269 100644 --- a/include/linux/netfilter_bridge/ebt_ulog.h +++ b/include/linux/netfilter_bridge/ebt_ulog.h @@ -1,6 +1,8 @@ #ifndef _EBT_ULOG_H #define _EBT_ULOG_H +#include + #define EBT_ULOG_DEFAULT_NLGROUP 0 #define EBT_ULOG_DEFAULT_QTHRESHOLD 1 #define EBT_ULOG_MAXNLGROUPS 32 /* hardcoded netlink max */ diff --git a/include/linux/netfilter_bridge/ebt_vlan.h b/include/linux/netfilter_bridge/ebt_vlan.h index 48dffc1dad36..967d1d5cf98d 100644 --- a/include/linux/netfilter_bridge/ebt_vlan.h +++ b/include/linux/netfilter_bridge/ebt_vlan.h @@ -1,6 +1,8 @@ #ifndef __LINUX_BRIDGE_EBT_VLAN_H #define __LINUX_BRIDGE_EBT_VLAN_H +#include + #define EBT_VLAN_ID 0x01 #define EBT_VLAN_PRIO 0x02 #define EBT_VLAN_ENCAP 0x04 diff --git a/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h b/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h index 3114f06939ef..c6a204c97047 100644 --- a/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h +++ b/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h @@ -1,6 +1,8 @@ #ifndef _IPT_CLUSTERIP_H_target #define _IPT_CLUSTERIP_H_target +#include + enum clusterip_hashmode { CLUSTERIP_HASHMODE_SIP = 0, CLUSTERIP_HASHMODE_SIP_SPT, diff --git a/include/linux/netfilter_ipv4/ipt_ECN.h b/include/linux/netfilter_ipv4/ipt_ECN.h index c6e3e01b75e0..bb88d5315a4d 100644 --- a/include/linux/netfilter_ipv4/ipt_ECN.h +++ b/include/linux/netfilter_ipv4/ipt_ECN.h @@ -8,6 +8,8 @@ */ #ifndef _IPT_ECN_TARGET_H #define _IPT_ECN_TARGET_H + +#include #include #define IPT_ECN_IP_MASK (~XT_DSCP_MASK) diff --git a/include/linux/netfilter_ipv4/ipt_SAME.h b/include/linux/netfilter_ipv4/ipt_SAME.h index fa0ebeca5d95..5bca78267afd 100644 --- a/include/linux/netfilter_ipv4/ipt_SAME.h +++ b/include/linux/netfilter_ipv4/ipt_SAME.h @@ -1,6 +1,8 @@ #ifndef _IPT_SAME_H #define _IPT_SAME_H +#include + #define IPT_SAME_MAX_RANGE 10 #define IPT_SAME_NODST 0x01 diff --git a/include/linux/netfilter_ipv4/ipt_TTL.h b/include/linux/netfilter_ipv4/ipt_TTL.h index f6250e422d5e..f6ac169d92f9 100644 --- a/include/linux/netfilter_ipv4/ipt_TTL.h +++ b/include/linux/netfilter_ipv4/ipt_TTL.h @@ -4,6 +4,8 @@ #ifndef _IPT_TTL_H #define _IPT_TTL_H +#include + enum { IPT_TTL_SET = 0, IPT_TTL_INC, diff --git a/include/linux/netfilter_ipv4/ipt_addrtype.h b/include/linux/netfilter_ipv4/ipt_addrtype.h index f29c3cfcc240..0da42237c8da 100644 --- a/include/linux/netfilter_ipv4/ipt_addrtype.h +++ b/include/linux/netfilter_ipv4/ipt_addrtype.h @@ -1,6 +1,8 @@ #ifndef _IPT_ADDRTYPE_H #define _IPT_ADDRTYPE_H +#include + enum { IPT_ADDRTYPE_INVERT_SOURCE = 0x0001, IPT_ADDRTYPE_INVERT_DEST = 0x0002, diff --git a/include/linux/netfilter_ipv4/ipt_ah.h b/include/linux/netfilter_ipv4/ipt_ah.h index 8fea283ee62a..4e02bb0119e3 100644 --- a/include/linux/netfilter_ipv4/ipt_ah.h +++ b/include/linux/netfilter_ipv4/ipt_ah.h @@ -1,6 +1,8 @@ #ifndef _IPT_AH_H #define _IPT_AH_H +#include + struct ipt_ah { __u32 spis[2]; /* Security Parameter Index */ __u8 invflags; /* Inverse flags */ diff --git a/include/linux/netfilter_ipv4/ipt_ecn.h b/include/linux/netfilter_ipv4/ipt_ecn.h index 78b98aa8784d..eabf95fb7d3e 100644 --- a/include/linux/netfilter_ipv4/ipt_ecn.h +++ b/include/linux/netfilter_ipv4/ipt_ecn.h @@ -8,6 +8,8 @@ */ #ifndef _IPT_ECN_H #define _IPT_ECN_H + +#include #include #define IPT_ECN_IP_MASK (~XT_DSCP_MASK) diff --git a/include/linux/netfilter_ipv4/ipt_ttl.h b/include/linux/netfilter_ipv4/ipt_ttl.h index 93d9a06689a3..37bee4442486 100644 --- a/include/linux/netfilter_ipv4/ipt_ttl.h +++ b/include/linux/netfilter_ipv4/ipt_ttl.h @@ -4,6 +4,8 @@ #ifndef _IPT_TTL_H #define _IPT_TTL_H +#include + enum { IPT_TTL_EQ = 0, /* equals */ IPT_TTL_NE, /* not equals */ diff --git a/include/linux/netfilter_ipv6/ip6t_HL.h b/include/linux/netfilter_ipv6/ip6t_HL.h index 81cdaf0480e3..ebd8ead1bb63 100644 --- a/include/linux/netfilter_ipv6/ip6t_HL.h +++ b/include/linux/netfilter_ipv6/ip6t_HL.h @@ -5,6 +5,8 @@ #ifndef _IP6T_HL_H #define _IP6T_HL_H +#include + enum { IP6T_HL_SET = 0, IP6T_HL_INC, diff --git a/include/linux/netfilter_ipv6/ip6t_REJECT.h b/include/linux/netfilter_ipv6/ip6t_REJECT.h index b999aa4e5969..205ed62e4605 100644 --- a/include/linux/netfilter_ipv6/ip6t_REJECT.h +++ b/include/linux/netfilter_ipv6/ip6t_REJECT.h @@ -1,6 +1,8 @@ #ifndef _IP6T_REJECT_H #define _IP6T_REJECT_H +#include + enum ip6t_reject_with { IP6T_ICMP6_NO_ROUTE, IP6T_ICMP6_ADM_PROHIBITED, diff --git a/include/linux/netfilter_ipv6/ip6t_ah.h b/include/linux/netfilter_ipv6/ip6t_ah.h index a602c165edd1..5da2b65cb3ad 100644 --- a/include/linux/netfilter_ipv6/ip6t_ah.h +++ b/include/linux/netfilter_ipv6/ip6t_ah.h @@ -1,6 +1,8 @@ #ifndef _IP6T_AH_H #define _IP6T_AH_H +#include + struct ip6t_ah { __u32 spis[2]; /* Security Parameter Index */ __u32 hdrlen; /* Header Length */ diff --git a/include/linux/netfilter_ipv6/ip6t_frag.h b/include/linux/netfilter_ipv6/ip6t_frag.h index 538b31ef5e3d..b47f61b9e082 100644 --- a/include/linux/netfilter_ipv6/ip6t_frag.h +++ b/include/linux/netfilter_ipv6/ip6t_frag.h @@ -1,6 +1,8 @@ #ifndef _IP6T_FRAG_H #define _IP6T_FRAG_H +#include + struct ip6t_frag { __u32 ids[2]; /* Security Parameter Index */ __u32 hdrlen; /* Header Length */ diff --git a/include/linux/netfilter_ipv6/ip6t_hl.h b/include/linux/netfilter_ipv6/ip6t_hl.h index c6fddcb971da..6e76dbc6c19a 100644 --- a/include/linux/netfilter_ipv6/ip6t_hl.h +++ b/include/linux/netfilter_ipv6/ip6t_hl.h @@ -5,6 +5,8 @@ #ifndef _IP6T_HL_H #define _IP6T_HL_H +#include + enum { IP6T_HL_EQ = 0, /* equals */ IP6T_HL_NE, /* not equals */ diff --git a/include/linux/netfilter_ipv6/ip6t_ipv6header.h b/include/linux/netfilter_ipv6/ip6t_ipv6header.h index 73d53bd3ff62..efae3a20c214 100644 --- a/include/linux/netfilter_ipv6/ip6t_ipv6header.h +++ b/include/linux/netfilter_ipv6/ip6t_ipv6header.h @@ -8,6 +8,8 @@ on whether they contain certain headers */ #ifndef __IPV6HEADER_H #define __IPV6HEADER_H +#include + struct ip6t_ipv6header_info { __u8 matchflags; __u8 invflags; diff --git a/include/linux/netfilter_ipv6/ip6t_mh.h b/include/linux/netfilter_ipv6/ip6t_mh.h index 98c8cf685eea..a7729a5025cd 100644 --- a/include/linux/netfilter_ipv6/ip6t_mh.h +++ b/include/linux/netfilter_ipv6/ip6t_mh.h @@ -1,6 +1,8 @@ #ifndef _IP6T_MH_H #define _IP6T_MH_H +#include + /* MH matching stuff */ struct ip6t_mh { __u8 types[2]; /* MH type range */ diff --git a/include/linux/netfilter_ipv6/ip6t_opts.h b/include/linux/netfilter_ipv6/ip6t_opts.h index 405d309cd741..17d419a811fd 100644 --- a/include/linux/netfilter_ipv6/ip6t_opts.h +++ b/include/linux/netfilter_ipv6/ip6t_opts.h @@ -1,6 +1,8 @@ #ifndef _IP6T_OPTS_H #define _IP6T_OPTS_H +#include + #define IP6T_OPTS_OPTSNR 16 struct ip6t_opts { diff --git a/include/linux/netfilter_ipv6/ip6t_rt.h b/include/linux/netfilter_ipv6/ip6t_rt.h index e8dad20acd37..7605a5ff81cd 100644 --- a/include/linux/netfilter_ipv6/ip6t_rt.h +++ b/include/linux/netfilter_ipv6/ip6t_rt.h @@ -1,6 +1,7 @@ #ifndef _IP6T_RT_H #define _IP6T_RT_H +#include /*#include */ #define IP6T_RT_HOPS 16 -- cgit v1.2.3 From 2f1e3176723d74ea2dd975e5be0ef6bb4fed2e2e Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 20 Jan 2011 20:46:52 +0100 Subject: netfilter: nf_conntrack: fix linker error with NF_CONNTRACK_TIMESTAMP=n net/built-in.o: In function `nf_conntrack_init_net': net/netfilter/nf_conntrack_core.c:1521: undefined reference to `nf_conntrack_tstamp_init' net/netfilter/nf_conntrack_core.c:1531: undefined reference to `nf_conntrack_tstamp_fini' Add dummy inline functions for the =n case to fix this. Reported-by: John Fastabend Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_conntrack_timestamp.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_timestamp.h b/include/net/netfilter/nf_conntrack_timestamp.h index f17dcb664e29..fc9c82b1f06b 100644 --- a/include/net/netfilter/nf_conntrack_timestamp.h +++ b/include/net/netfilter/nf_conntrack_timestamp.h @@ -47,7 +47,19 @@ static inline void nf_ct_set_tstamp(struct net *net, bool enable) net->ct.sysctl_tstamp = enable; } +#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP extern int nf_conntrack_tstamp_init(struct net *net); extern void nf_conntrack_tstamp_fini(struct net *net); +#else +static inline int nf_conntrack_tstamp_init(struct net *net) +{ + return 0; +} + +static inline void nf_conntrack_tstamp_fini(struct net *net) +{ + return; +} +#endif /* CONFIG_NF_CONNTRACK_TIMESTAMP */ #endif /* _NF_CONNTRACK_TSTAMP_H */ -- cgit v1.2.3 From a0a5b676401ee97a295dd9fcd7a374e883d48a8a Mon Sep 17 00:00:00 2001 From: Ky Srinivasan Date: Thu, 16 Dec 2010 18:35:00 -0700 Subject: Connector: Add an index to support key/value pair (KVP) functionality Added a connector index to support key value/pair (KVP) functionality for Linux guests hosted on a HyperV platform. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- include/linux/connector.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/connector.h b/include/linux/connector.h index 7e8ca75d2dad..2e9759cdd275 100644 --- a/include/linux/connector.h +++ b/include/linux/connector.h @@ -42,8 +42,9 @@ #define CN_VAL_DM_USERSPACE_LOG 0x1 #define CN_IDX_DRBD 0x8 #define CN_VAL_DRBD 0x1 +#define CN_KVP_IDX 0x9 /* HyperV KVP */ -#define CN_NETLINK_USERS 8 +#define CN_NETLINK_USERS 9 /* * Maximum connector's message size. -- cgit v1.2.3 From fd245a4adb5288eac37250875f237c40a20a1944 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 20 Jan 2011 05:27:16 +0000 Subject: net_sched: move TCQ_F_THROTTLED flag In commit 371121057607e (net: QDISC_STATE_RUNNING dont need atomic bit ops) I moved QDISC_STATE_RUNNING flag to __state container, located in the cache line containing qdisc lock and often dirtied fields. I now move TCQ_F_THROTTLED bit too, so that we let first cache line read mostly, and shared by all cpus. This should speedup HTB/CBQ for example. Not using test_bit()/__clear_bit()/__test_and_set_bit allows to use an "unsigned int" for __state container, reducing by 8 bytes Qdisc size. Introduce helpers to hide implementation details. Signed-off-by: Eric Dumazet CC: Patrick McHardy CC: Jesper Dangaard Brouer CC: Jarek Poplawski CC: Jamal Hadi Salim CC: Stephen Hemminger Signed-off-by: David S. Miller --- include/net/sch_generic.h | 38 ++++++++++++++++++++++++++++---------- net/sched/sch_api.c | 6 +++--- net/sched/sch_cbq.c | 6 +++--- net/sched/sch_hfsc.c | 2 +- net/sched/sch_htb.c | 4 ++-- net/sched/sch_netem.c | 2 +- net/sched/sch_tbf.c | 2 +- 7 files changed, 39 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index e9eee99d8b1f..f6345f55041c 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -31,7 +31,8 @@ enum qdisc_state_t { * following bits are only changed while qdisc lock is held */ enum qdisc___state_t { - __QDISC___STATE_RUNNING, + __QDISC___STATE_RUNNING = 1, + __QDISC___STATE_THROTTLED = 2, }; struct qdisc_size_table { @@ -46,10 +47,9 @@ struct Qdisc { struct sk_buff * (*dequeue)(struct Qdisc *dev); unsigned flags; #define TCQ_F_BUILTIN 1 -#define TCQ_F_THROTTLED 2 -#define TCQ_F_INGRESS 4 -#define TCQ_F_CAN_BYPASS 8 -#define TCQ_F_MQROOT 16 +#define TCQ_F_INGRESS 2 +#define TCQ_F_CAN_BYPASS 4 +#define TCQ_F_MQROOT 8 #define TCQ_F_WARN_NONWC (1 << 16) int padded; struct Qdisc_ops *ops; @@ -78,25 +78,43 @@ struct Qdisc { unsigned long state; struct sk_buff_head q; struct gnet_stats_basic_packed bstats; - unsigned long __state; + unsigned int __state; struct gnet_stats_queue qstats; struct rcu_head rcu_head; spinlock_t busylock; }; -static inline bool qdisc_is_running(struct Qdisc *qdisc) +static inline bool qdisc_is_running(const struct Qdisc *qdisc) { - return test_bit(__QDISC___STATE_RUNNING, &qdisc->__state); + return (qdisc->__state & __QDISC___STATE_RUNNING) ? true : false; } static inline bool qdisc_run_begin(struct Qdisc *qdisc) { - return !__test_and_set_bit(__QDISC___STATE_RUNNING, &qdisc->__state); + if (qdisc_is_running(qdisc)) + return false; + qdisc->__state |= __QDISC___STATE_RUNNING; + return true; } static inline void qdisc_run_end(struct Qdisc *qdisc) { - __clear_bit(__QDISC___STATE_RUNNING, &qdisc->__state); + qdisc->__state &= ~__QDISC___STATE_RUNNING; +} + +static inline bool qdisc_is_throttled(const struct Qdisc *qdisc) +{ + return (qdisc->__state & __QDISC___STATE_THROTTLED) ? true : false; +} + +static inline void qdisc_throttled(struct Qdisc *qdisc) +{ + qdisc->__state |= __QDISC___STATE_THROTTLED; +} + +static inline void qdisc_unthrottled(struct Qdisc *qdisc) +{ + qdisc->__state &= ~__QDISC___STATE_THROTTLED; } struct Qdisc_class_ops { diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 36ac0ec81ce0..374fcbef80e8 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -473,7 +473,7 @@ static enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer) struct qdisc_watchdog *wd = container_of(timer, struct qdisc_watchdog, timer); - wd->qdisc->flags &= ~TCQ_F_THROTTLED; + qdisc_unthrottled(wd->qdisc); __netif_schedule(qdisc_root(wd->qdisc)); return HRTIMER_NORESTART; @@ -495,7 +495,7 @@ void qdisc_watchdog_schedule(struct qdisc_watchdog *wd, psched_time_t expires) &qdisc_root_sleeping(wd->qdisc)->state)) return; - wd->qdisc->flags |= TCQ_F_THROTTLED; + qdisc_throttled(wd->qdisc); time = ktime_set(0, 0); time = ktime_add_ns(time, PSCHED_TICKS2NS(expires)); hrtimer_start(&wd->timer, time, HRTIMER_MODE_ABS); @@ -505,7 +505,7 @@ EXPORT_SYMBOL(qdisc_watchdog_schedule); void qdisc_watchdog_cancel(struct qdisc_watchdog *wd) { hrtimer_cancel(&wd->timer); - wd->qdisc->flags &= ~TCQ_F_THROTTLED; + qdisc_unthrottled(wd->qdisc); } EXPORT_SYMBOL(qdisc_watchdog_cancel); diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 4aaf44c95c52..25ed522b2891 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -351,7 +351,7 @@ cbq_mark_toplevel(struct cbq_sched_data *q, struct cbq_class *cl) { int toplevel = q->toplevel; - if (toplevel > cl->level && !(cl->q->flags & TCQ_F_THROTTLED)) { + if (toplevel > cl->level && !(qdisc_is_throttled(cl->q))) { psched_time_t now; psched_tdiff_t incr; @@ -625,7 +625,7 @@ static enum hrtimer_restart cbq_undelay(struct hrtimer *timer) hrtimer_start(&q->delay_timer, time, HRTIMER_MODE_ABS); } - sch->flags &= ~TCQ_F_THROTTLED; + qdisc_unthrottled(sch); __netif_schedule(qdisc_root(sch)); return HRTIMER_NORESTART; } @@ -974,7 +974,7 @@ cbq_dequeue(struct Qdisc *sch) skb = cbq_dequeue_1(sch); if (skb) { sch->q.qlen--; - sch->flags &= ~TCQ_F_THROTTLED; + qdisc_unthrottled(sch); return skb; } diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index dea4009615f9..b632d9251913 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1664,7 +1664,7 @@ hfsc_dequeue(struct Qdisc *sch) set_passive(cl); } - sch->flags &= ~TCQ_F_THROTTLED; + qdisc_unthrottled(sch); sch->q.qlen--; return skb; diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 3e86fd3a1b78..39db75cd8c17 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -865,7 +865,7 @@ static struct sk_buff *htb_dequeue(struct Qdisc *sch) /* try to dequeue direct packets as high prio (!) to minimize cpu work */ skb = __skb_dequeue(&q->direct_queue); if (skb != NULL) { - sch->flags &= ~TCQ_F_THROTTLED; + qdisc_unthrottled(sch); sch->q.qlen--; return skb; } @@ -901,7 +901,7 @@ static struct sk_buff *htb_dequeue(struct Qdisc *sch) skb = htb_dequeue_tree(q, prio, level); if (likely(skb != NULL)) { sch->q.qlen--; - sch->flags &= ~TCQ_F_THROTTLED; + qdisc_unthrottled(sch); goto fin; } } diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index c2bbbe60d544..c26ef3614f7e 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -266,7 +266,7 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch) struct netem_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; - if (sch->flags & TCQ_F_THROTTLED) + if (qdisc_is_throttled(sch)) return NULL; skb = q->qdisc->ops->peek(q->qdisc); diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index 475edfb69c22..86c016696977 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -185,7 +185,7 @@ static struct sk_buff *tbf_dequeue(struct Qdisc *sch) q->tokens = toks; q->ptokens = ptoks; sch->q.qlen--; - sch->flags &= ~TCQ_F_THROTTLED; + qdisc_unthrottled(sch); return skb; } -- cgit v1.2.3 From a2da570d62fcb9e8816f6920e1ec02c706b289fa Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 20 Jan 2011 03:48:19 +0000 Subject: net_sched: RCU conversion of stab This patch converts stab qdisc management to RCU, so that we can perform the qdisc_calculate_pkt_len() call before getting qdisc lock. This shortens the lock's held time in __dev_xmit_skb(). This permits more qdiscs to get TCQ_F_CAN_BYPASS status, avoiding lot of cache misses and so reducing latencies. Signed-off-by: Eric Dumazet CC: Patrick McHardy CC: Jesper Dangaard Brouer CC: Jarek Poplawski CC: Jamal Hadi Salim CC: Stephen Hemminger Signed-off-by: David S. Miller --- include/net/sch_generic.h | 21 +++++++++++++++------ net/core/dev.c | 8 +++++--- net/sched/sch_api.c | 26 +++++++++++++++++--------- net/sched/sch_generic.c | 2 +- 4 files changed, 38 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index f6345f55041c..d531baa2506a 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -36,6 +36,7 @@ enum qdisc___state_t { }; struct qdisc_size_table { + struct rcu_head rcu; struct list_head list; struct tc_sizespec szopts; int refcnt; @@ -53,7 +54,7 @@ struct Qdisc { #define TCQ_F_WARN_NONWC (1 << 16) int padded; struct Qdisc_ops *ops; - struct qdisc_size_table *stab; + struct qdisc_size_table __rcu *stab; struct list_head list; u32 handle; u32 parent; @@ -349,8 +350,8 @@ extern struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, struct Qdisc_ops *ops); extern struct Qdisc *qdisc_create_dflt(struct netdev_queue *dev_queue, struct Qdisc_ops *ops, u32 parentid); -extern void qdisc_calculate_pkt_len(struct sk_buff *skb, - struct qdisc_size_table *stab); +extern void __qdisc_calculate_pkt_len(struct sk_buff *skb, + const struct qdisc_size_table *stab); extern void tcf_destroy(struct tcf_proto *tp); extern void tcf_destroy_chain(struct tcf_proto **fl); @@ -429,12 +430,20 @@ enum net_xmit_qdisc_t { #define net_xmit_drop_count(e) (1) #endif -static inline int qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch) +static inline void qdisc_calculate_pkt_len(struct sk_buff *skb, + const struct Qdisc *sch) { #ifdef CONFIG_NET_SCHED - if (sch->stab) - qdisc_calculate_pkt_len(skb, sch->stab); + struct qdisc_size_table *stab = rcu_dereference_bh(sch->stab); + + if (stab) + __qdisc_calculate_pkt_len(skb, stab); #endif +} + +static inline int qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch) +{ + qdisc_calculate_pkt_len(skb, sch); return sch->enqueue(skb, sch); } diff --git a/net/core/dev.c b/net/core/dev.c index a4ccd47f3196..2730352d2ccc 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2325,15 +2325,18 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, struct netdev_queue *txq) { spinlock_t *root_lock = qdisc_lock(q); - bool contended = qdisc_is_running(q); + bool contended; int rc; + qdisc_skb_cb(skb)->pkt_len = skb->len; + qdisc_calculate_pkt_len(skb, q); /* * Heuristic to force contended enqueues to serialize on a * separate lock before trying to get qdisc main lock. * This permits __QDISC_STATE_RUNNING owner to get the lock more often * and dequeue packets faster. */ + contended = qdisc_is_running(q); if (unlikely(contended)) spin_lock(&q->busylock); @@ -2351,7 +2354,6 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, if (!(dev->priv_flags & IFF_XMIT_DST_RELEASE)) skb_dst_force(skb); - qdisc_skb_cb(skb)->pkt_len = skb->len; qdisc_bstats_update(q, skb); if (sch_direct_xmit(skb, q, dev, txq, root_lock)) { @@ -2366,7 +2368,7 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, rc = NET_XMIT_SUCCESS; } else { skb_dst_force(skb); - rc = qdisc_enqueue_root(skb, q); + rc = q->enqueue(skb, q) & NET_XMIT_MASK; if (qdisc_run_begin(q)) { if (unlikely(contended)) { spin_unlock(&q->busylock); diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 374fcbef80e8..150741579408 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -398,6 +398,11 @@ static struct qdisc_size_table *qdisc_get_stab(struct nlattr *opt) return stab; } +static void stab_kfree_rcu(struct rcu_head *head) +{ + kfree(container_of(head, struct qdisc_size_table, rcu)); +} + void qdisc_put_stab(struct qdisc_size_table *tab) { if (!tab) @@ -407,7 +412,7 @@ void qdisc_put_stab(struct qdisc_size_table *tab) if (--tab->refcnt == 0) { list_del(&tab->list); - kfree(tab); + call_rcu_bh(&tab->rcu, stab_kfree_rcu); } spin_unlock(&qdisc_stab_lock); @@ -430,7 +435,7 @@ nla_put_failure: return -1; } -void qdisc_calculate_pkt_len(struct sk_buff *skb, struct qdisc_size_table *stab) +void __qdisc_calculate_pkt_len(struct sk_buff *skb, const struct qdisc_size_table *stab) { int pkt_len, slot; @@ -456,7 +461,7 @@ out: pkt_len = 1; qdisc_skb_cb(skb)->pkt_len = pkt_len; } -EXPORT_SYMBOL(qdisc_calculate_pkt_len); +EXPORT_SYMBOL(__qdisc_calculate_pkt_len); void qdisc_warn_nonwc(char *txt, struct Qdisc *qdisc) { @@ -835,7 +840,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, err = PTR_ERR(stab); goto err_out4; } - sch->stab = stab; + rcu_assign_pointer(sch->stab, stab); } if (tca[TCA_RATE]) { spinlock_t *root_lock; @@ -875,7 +880,7 @@ err_out4: * Any broken qdiscs that would require a ops->reset() here? * The qdisc was never in action so it shouldn't be necessary. */ - qdisc_put_stab(sch->stab); + qdisc_put_stab(rtnl_dereference(sch->stab)); if (ops->destroy) ops->destroy(sch); goto err_out3; @@ -883,7 +888,7 @@ err_out4: static int qdisc_change(struct Qdisc *sch, struct nlattr **tca) { - struct qdisc_size_table *stab = NULL; + struct qdisc_size_table *ostab, *stab = NULL; int err = 0; if (tca[TCA_OPTIONS]) { @@ -900,8 +905,9 @@ static int qdisc_change(struct Qdisc *sch, struct nlattr **tca) return PTR_ERR(stab); } - qdisc_put_stab(sch->stab); - sch->stab = stab; + ostab = rtnl_dereference(sch->stab); + rcu_assign_pointer(sch->stab, stab); + qdisc_put_stab(ostab); if (tca[TCA_RATE]) { /* NB: ignores errors from replace_estimator @@ -1180,6 +1186,7 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, struct nlmsghdr *nlh; unsigned char *b = skb_tail_pointer(skb); struct gnet_dump d; + struct qdisc_size_table *stab; nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags); tcm = NLMSG_DATA(nlh); @@ -1195,7 +1202,8 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, goto nla_put_failure; q->qstats.qlen = q->q.qlen; - if (q->stab && qdisc_dump_stab(skb, q->stab) < 0) + 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, diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 2f1cb62130da..cc17e794c41e 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -632,7 +632,7 @@ void qdisc_destroy(struct Qdisc *qdisc) #ifdef CONFIG_NET_SCHED qdisc_list_del(qdisc); - qdisc_put_stab(qdisc->stab); + qdisc_put_stab(rtnl_dereference(qdisc->stab)); #endif gen_kill_estimator(&qdisc->bstats, &qdisc->rate_est); if (ops->reset) -- cgit v1.2.3 From d18046b3cd989c06d2ad8d615e57c3cf63c63b67 Mon Sep 17 00:00:00 2001 From: Shan Wei Date: Wed, 19 Jan 2011 23:12:54 +0000 Subject: dccp: clean up unused DCCP_STATE_MASK definition Remove unused DCCP_STATE_MASK macro. Signed-off-by: Shan Wei Acked-by: Gerrit Renker Signed-off-by: David S. Miller --- include/linux/dccp.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 010e2d87ed75..d638e85dc501 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -279,8 +279,6 @@ enum dccp_state { DCCP_MAX_STATES }; -#define DCCP_STATE_MASK 0x1f - enum { DCCPF_OPEN = TCPF_ESTABLISHED, DCCPF_REQUESTING = TCPF_SYN_SENT, -- cgit v1.2.3 From 686a2955531312dab77bb6f1e8602787d85e47d8 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 20 Jan 2011 22:47:32 -0800 Subject: net: Add safe reverse SKB queue walkers. Signed-off-by: David S. Miller --- include/linux/skbuff.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index bf221d65d9ad..6e946da9d1d6 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1801,6 +1801,15 @@ static inline int pskb_trim_rcsum(struct sk_buff *skb, unsigned int len) prefetch(skb->prev), (skb != (struct sk_buff *)(queue)); \ skb = skb->prev) +#define skb_queue_reverse_walk_safe(queue, skb, tmp) \ + for (skb = (queue)->prev, tmp = skb->prev; \ + skb != (struct sk_buff *)(queue); \ + skb = tmp, tmp = skb->prev) + +#define skb_queue_reverse_walk_from_safe(queue, skb, tmp) \ + for (tmp = skb->prev; \ + skb != (struct sk_buff *)(queue); \ + skb = tmp, tmp = skb->prev) static inline bool skb_has_frag_list(const struct sk_buff *skb) { -- cgit v1.2.3 From c9e358dfc4a8cb2227172ef77908c2e0ee17bcb9 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 21 Jan 2011 09:24:48 -0700 Subject: driver-core: remove conditionals around devicetree pointers Having conditional around the of_match_table and the of_node pointers turns out to make driver code use ugly #ifdef blocks. Drop the conditionals and remove the #ifdef blocks from the affected drivers. Also tidy up minor whitespace issues within the same hunks. Signed-off-by: Grant Likely Acked-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-ocores.c | 14 +++----------- drivers/i2c/i2c-core.c | 2 -- drivers/mmc/host/mmc_spi.c | 4 ---- drivers/net/ethoc.c | 8 +------- drivers/spi/pxa2xx_spi.c | 2 -- drivers/spi/pxa2xx_spi_pci.c | 2 -- drivers/spi/xilinx_spi.c | 6 ------ include/linux/device.h | 7 ++----- include/linux/i2c.h | 2 -- include/linux/of.h | 4 ++-- 10 files changed, 8 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index ef3bcb1ce864..c2ef5ce1f1ff 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -330,9 +330,7 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev) i2c->adap = ocores_adapter; i2c_set_adapdata(&i2c->adap, i2c); i2c->adap.dev.parent = &pdev->dev; -#ifdef CONFIG_OF i2c->adap.dev.of_node = pdev->dev.of_node; -#endif /* add i2c adapter to i2c tree */ ret = i2c_add_adapter(&i2c->adap); @@ -390,15 +388,11 @@ static int ocores_i2c_resume(struct platform_device *pdev) #define ocores_i2c_resume NULL #endif -#ifdef CONFIG_OF static struct of_device_id ocores_i2c_match[] = { - { - .compatible = "opencores,i2c-ocores", - }, - {}, + { .compatible = "opencores,i2c-ocores", }, + {}, }; MODULE_DEVICE_TABLE(of, ocores_i2c_match); -#endif /* work with hotplug and coldplug */ MODULE_ALIAS("platform:ocores-i2c"); @@ -411,9 +405,7 @@ static struct platform_driver ocores_i2c_driver = { .driver = { .owner = THIS_MODULE, .name = "ocores-i2c", -#ifdef CONFIG_OF - .of_match_table = ocores_i2c_match, -#endif + .of_match_table = ocores_i2c_match, }, }; diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index f0bd5bcdf563..045ba6efea48 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -537,9 +537,7 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) client->dev.parent = &client->adapter->dev; client->dev.bus = &i2c_bus_type; client->dev.type = &i2c_client_type; -#ifdef CONFIG_OF client->dev.of_node = info->of_node; -#endif dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap), client->addr); diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index fd877f633dd2..2f7fc0c5146f 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1516,21 +1516,17 @@ static int __devexit mmc_spi_remove(struct spi_device *spi) return 0; } -#if defined(CONFIG_OF) static struct of_device_id mmc_spi_of_match_table[] __devinitdata = { { .compatible = "mmc-spi-slot", }, {}, }; -#endif static struct spi_driver mmc_spi_driver = { .driver = { .name = "mmc_spi", .bus = &spi_bus_type, .owner = THIS_MODULE, -#if defined(CONFIG_OF) .of_match_table = mmc_spi_of_match_table, -#endif }, .probe = mmc_spi_probe, .remove = __devexit_p(mmc_spi_remove), diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index b79d7e1555d5..db0290f05bdf 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -1163,15 +1163,11 @@ static int ethoc_resume(struct platform_device *pdev) # define ethoc_resume NULL #endif -#ifdef CONFIG_OF static struct of_device_id ethoc_match[] = { - { - .compatible = "opencores,ethoc", - }, + { .compatible = "opencores,ethoc", }, {}, }; MODULE_DEVICE_TABLE(of, ethoc_match); -#endif static struct platform_driver ethoc_driver = { .probe = ethoc_probe, @@ -1181,9 +1177,7 @@ static struct platform_driver ethoc_driver = { .driver = { .name = "ethoc", .owner = THIS_MODULE, -#ifdef CONFIG_OF .of_match_table = ethoc_match, -#endif }, }; diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 95928833855b..a429b01d0285 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -1557,9 +1557,7 @@ static int __devinit pxa2xx_spi_probe(struct platform_device *pdev) drv_data->ssp = ssp; master->dev.parent = &pdev->dev; -#ifdef CONFIG_OF master->dev.of_node = pdev->dev.of_node; -#endif /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; diff --git a/drivers/spi/pxa2xx_spi_pci.c b/drivers/spi/pxa2xx_spi_pci.c index 351d8a375b57..b6589bb3a6c3 100644 --- a/drivers/spi/pxa2xx_spi_pci.c +++ b/drivers/spi/pxa2xx_spi_pci.c @@ -98,9 +98,7 @@ static int __devinit ce4100_spi_probe(struct pci_dev *dev, pdev->dev.parent = &dev->dev; pdev->dev.platform_data = &spi_info->spi_pdata; -#ifdef CONFIG_OF pdev->dev.of_node = dev->dev.of_node; -#endif pdev->dev.release = plat_dev_release; spi_pdata->num_chipselect = dev->devfn; diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index 7adaef62a991..4d2c75df886c 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -351,14 +351,12 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) return IRQ_HANDLED; } -#ifdef CONFIG_OF static const struct of_device_id xilinx_spi_of_match[] = { { .compatible = "xlnx,xps-spi-2.00.a", }, { .compatible = "xlnx,xps-spi-2.00.b", }, {} }; MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); -#endif struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, u32 irq, s16 bus_num, int num_cs, int little_endian, int bits_per_word) @@ -394,9 +392,7 @@ struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, master->bus_num = bus_num; master->num_chipselect = num_cs; -#ifdef CONFIG_OF master->dev.of_node = dev->of_node; -#endif xspi->mem = *mem; xspi->irq = irq; @@ -539,9 +535,7 @@ static struct platform_driver xilinx_spi_driver = { .driver = { .name = XILINX_SPI_NAME, .owner = THIS_MODULE, -#ifdef CONFIG_OF .of_match_table = xilinx_spi_of_match, -#endif }, }; diff --git a/include/linux/device.h b/include/linux/device.h index 1bf5cf0b4513..ca5d25225aab 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -128,9 +128,7 @@ struct device_driver { bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ -#if defined(CONFIG_OF) const struct of_device_id *of_match_table; -#endif int (*probe) (struct device *dev); int (*remove) (struct device *dev); @@ -441,9 +439,8 @@ struct device { override */ /* arch specific additions */ struct dev_archdata archdata; -#ifdef CONFIG_OF - struct device_node *of_node; -#endif + + struct device_node *of_node; /* associated device tree node */ dev_t devt; /* dev_t, creates the sysfs "dev" */ diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 903576df88dc..06a8d9c7de98 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -258,9 +258,7 @@ struct i2c_board_info { unsigned short addr; void *platform_data; struct dev_archdata *archdata; -#ifdef CONFIG_OF struct device_node *of_node; -#endif int irq; }; diff --git a/include/linux/of.h b/include/linux/of.h index cad7cf0ab278..d9dd664a6a9c 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -23,8 +23,6 @@ #include -#ifdef CONFIG_OF - typedef u32 phandle; typedef u32 ihandle; @@ -65,6 +63,8 @@ struct device_node { #endif }; +#ifdef CONFIG_OF + /* Pointer for first entry in chain of all nodes. */ extern struct device_node *allnodes; extern struct device_node *of_chosen; -- cgit v1.2.3 From 7cfe56172ac14d2031f1896ecb629033f71caafa Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 20 Jan 2011 13:52:08 -0700 Subject: ASoC: wm8903: Expose GPIOs through gpiolib Also, update platform_data GPIO handling to have an explicit "don't touch this pin" option. Add #defines for the GPIO pin functions. Signed-off-by: Stephen Warren Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/wm8903.h | 20 +++++++- sound/soc/codecs/wm8903.c | 126 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 144 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/wm8903.h b/include/sound/wm8903.h index b4a0db2307ef..86172cf4339f 100644 --- a/include/sound/wm8903.h +++ b/include/sound/wm8903.h @@ -36,6 +36,21 @@ #define WM8903_MICBIAS_ENA_SHIFT 0 /* MICBIAS_ENA */ #define WM8903_MICBIAS_ENA_WIDTH 1 /* MICBIAS_ENA */ +/* + * WM8903_GPn_FN values + * + * See datasheets for list of valid values per pin + */ +#define WM8903_GPn_FN_GPIO_OUTPUT 0 +#define WM8903_GPn_FN_BCLK 1 +#define WM8903_GPn_FN_IRQ_OUTPT 2 +#define WM8903_GPn_FN_GPIO_INPUT 3 +#define WM8903_GPn_FN_MICBIAS_CURRENT_DETECT 4 +#define WM8903_GPn_FN_MICBIAS_SHORT_DETECT 5 +#define WM8903_GPn_FN_DMIC_LR_CLK_OUTPUT 6 +#define WM8903_GPn_FN_FLL_LOCK_OUTPUT 8 +#define WM8903_GPn_FN_FLL_CLOCK_OUTPUT 9 + /* * R116 (0x74) - GPIO Control 1 */ @@ -231,6 +246,8 @@ #define WM8903_GP5_DB_SHIFT 0 /* GP5_DB */ #define WM8903_GP5_DB_WIDTH 1 /* GP5_DB */ +#define WM8903_NUM_GPIO 5 + struct wm8903_platform_data { bool irq_active_low; /* Set if IRQ active low, default high */ @@ -243,7 +260,8 @@ struct wm8903_platform_data { int micdet_delay; /* Delay after microphone detection (ms) */ - u32 gpio_cfg[5]; /* Default register values for GPIO pin mux */ + int gpio_base; + u32 gpio_cfg[WM8903_NUM_GPIO]; /* Default register values for GPIO pin mux */ }; #endif diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index a2a446cb1807..9c4f2c4febc2 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -2,6 +2,7 @@ * wm8903.c -- WM8903 ALSA SoC Audio driver * * Copyright 2008 Wolfson Microelectronics + * Copyright 2011 NVIDIA, Inc. * * Author: Mark Brown * @@ -19,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -213,6 +215,7 @@ static u16 wm8903_reg_defaults[] = { }; struct wm8903_priv { + struct snd_soc_codec *codec; int sysclk; int irq; @@ -230,6 +233,10 @@ struct wm8903_priv { int mic_short; int mic_last_report; int mic_delay; + +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio_chip; +#endif }; static int wm8903_volatile_register(struct snd_soc_codec *codec, unsigned int reg) @@ -1635,6 +1642,119 @@ static int wm8903_resume(struct snd_soc_codec *codec) return 0; } +#ifdef CONFIG_GPIOLIB +static inline struct wm8903_priv *gpio_to_wm8903(struct gpio_chip *chip) +{ + return container_of(chip, struct wm8903_priv, gpio_chip); +} + +static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + if (offset >= WM8903_NUM_GPIO) + return -EINVAL; + + return 0; +} + +static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + unsigned int mask, val; + + mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK; + val = (WM8903_GPn_FN_GPIO_INPUT << WM8903_GP1_FN_SHIFT) | + WM8903_GP1_DIR; + + return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + mask, val); +} + +static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + int reg; + + reg = snd_soc_read(codec, WM8903_GPIO_CONTROL_1 + offset); + + return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT; +} + +static int wm8903_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + unsigned int mask, val; + + mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK | WM8903_GP1_LVL_MASK; + val = (WM8903_GPn_FN_GPIO_OUTPUT << WM8903_GP1_FN_SHIFT) | + (value << WM8903_GP2_LVL_SHIFT); + + return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + mask, val); +} + +static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + + snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + WM8903_GP1_LVL_MASK, value << WM8903_GP1_LVL_SHIFT); +} + +static struct gpio_chip wm8903_template_chip = { + .label = "wm8903", + .owner = THIS_MODULE, + .request = wm8903_gpio_request, + .direction_input = wm8903_gpio_direction_in, + .get = wm8903_gpio_get, + .direction_output = wm8903_gpio_direction_out, + .set = wm8903_gpio_set, + .can_sleep = 1, +}; + +static void wm8903_init_gpio(struct snd_soc_codec *codec) +{ + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev); + int ret; + + wm8903->gpio_chip = wm8903_template_chip; + wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO; + wm8903->gpio_chip.dev = codec->dev; + + if (pdata && pdata->gpio_base) + wm8903->gpio_chip.base = pdata->gpio_base; + else + wm8903->gpio_chip.base = -1; + + ret = gpiochip_add(&wm8903->gpio_chip); + if (ret != 0) + dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret); +} + +static void wm8903_free_gpio(struct snd_soc_codec *codec) +{ + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = gpiochip_remove(&wm8903->gpio_chip); + if (ret != 0) + dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret); +} +#else +static void wm8903_init_gpio(struct snd_soc_codec *codec) +{ +} + +static void wm8903_free_gpio(struct snd_soc_codec *codec) +{ +} +#endif + static int wm8903_probe(struct snd_soc_codec *codec) { struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev); @@ -1643,6 +1763,7 @@ static int wm8903_probe(struct snd_soc_codec *codec) int trigger, irq_pol; u16 val; + wm8903->codec = codec; init_completion(&wm8903->wseq); ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); @@ -1667,7 +1788,7 @@ static int wm8903_probe(struct snd_soc_codec *codec) /* Set up GPIOs and microphone detection */ if (pdata) { for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) { - if (!pdata->gpio_cfg[i]) + if (pdata->gpio_cfg[i] == WM8903_GPIO_NO_CONFIG) continue; snd_soc_write(codec, WM8903_GPIO_CONTROL_1 + i, @@ -1749,12 +1870,15 @@ static int wm8903_probe(struct snd_soc_codec *codec) ARRAY_SIZE(wm8903_snd_controls)); wm8903_add_widgets(codec); + wm8903_init_gpio(codec); + return ret; } /* power down chip */ static int wm8903_remove(struct snd_soc_codec *codec) { + wm8903_free_gpio(codec); wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } -- cgit v1.2.3 From 67b22517d8e48a97e1d2ab10d095c538bbb2374c Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Wed, 19 Jan 2011 21:22:06 +0300 Subject: ASoC: CS4271 codec support Added support for CS4271 codec to ASoC. Signed-off-by: Alexander Sverdlin Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/cs4271.h | 25 ++ sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs4271.c | 630 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 661 insertions(+) create mode 100644 include/sound/cs4271.h create mode 100644 sound/soc/codecs/cs4271.c (limited to 'include') diff --git a/include/sound/cs4271.h b/include/sound/cs4271.h new file mode 100644 index 000000000000..16f8d325d3dc --- /dev/null +++ b/include/sound/cs4271.h @@ -0,0 +1,25 @@ +/* + * Definitions for CS4271 ASoC codec driver + * + * Copyright (c) 2010 Alexander Sverdlin + * + * 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. + */ + +#ifndef __CS4271_H +#define __CS4271_H + +struct cs4271_platform_data { + int gpio_nreset; /* GPIO driving Reset pin, if any */ + int gpio_disable; /* GPIO that disable serial bus, if any */ +}; + +#endif /* __CS4271_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a9cb2a04ad56..e239345a4d5d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -26,6 +26,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC select SND_SOC_CS42L51 if I2C select SND_SOC_CS4270 if I2C + select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI select SND_SOC_CX20442 select SND_SOC_DA7210 if I2C select SND_SOC_JZ4740_CODEC if SOC_JZ4740 @@ -157,6 +158,9 @@ config SND_SOC_CS4270_VD33_ERRATA bool depends on SND_SOC_CS4270 +config SND_SOC_CS4271 + tristate + config SND_SOC_CX20442 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 68e76af894b9..83b7accd7037 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -12,6 +12,7 @@ snd-soc-ak4671-objs := ak4671.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs4270-objs := cs4270.o +snd-soc-cs4271-objs := cs4271.o snd-soc-cx20442-objs := cx20442.o snd-soc-da7210-objs := da7210.o snd-soc-dmic-objs := dmic.o @@ -93,6 +94,7 @@ obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o +obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c new file mode 100644 index 000000000000..237ece3f1046 --- /dev/null +++ b/sound/soc/codecs/cs4271.c @@ -0,0 +1,630 @@ +/* + * CS4271 ASoC codec driver + * + * Copyright (c) 2010 Alexander Sverdlin + * + * 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. + * + * This driver support CS4271 codec being master or slave, working + * in control port mode, connected either via SPI or I2C. + * The data format accepted is I2S or left-justified. + * DAPM support not implemented. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* + * CS4271 registers + * High byte represents SPI chip address (0x10) + write command (0) + * Low byte - codec register address + */ +#define CS4271_MODE1 0x2001 /* Mode Control 1 */ +#define CS4271_DACCTL 0x2002 /* DAC Control */ +#define CS4271_DACVOL 0x2003 /* DAC Volume & Mixing Control */ +#define CS4271_VOLA 0x2004 /* DAC Channel A Volume Control */ +#define CS4271_VOLB 0x2005 /* DAC Channel B Volume Control */ +#define CS4271_ADCCTL 0x2006 /* ADC Control */ +#define CS4271_MODE2 0x2007 /* Mode Control 2 */ +#define CS4271_CHIPID 0x2008 /* Chip ID */ + +#define CS4271_FIRSTREG CS4271_MODE1 +#define CS4271_LASTREG CS4271_MODE2 +#define CS4271_NR_REGS ((CS4271_LASTREG & 0xFF) + 1) + +/* Bit masks for the CS4271 registers */ +#define CS4271_MODE1_MODE_MASK 0xC0 +#define CS4271_MODE1_MODE_1X 0x00 +#define CS4271_MODE1_MODE_2X 0x80 +#define CS4271_MODE1_MODE_4X 0xC0 + +#define CS4271_MODE1_DIV_MASK 0x30 +#define CS4271_MODE1_DIV_1 0x00 +#define CS4271_MODE1_DIV_15 0x10 +#define CS4271_MODE1_DIV_2 0x20 +#define CS4271_MODE1_DIV_3 0x30 + +#define CS4271_MODE1_MASTER 0x08 + +#define CS4271_MODE1_DAC_DIF_MASK 0x07 +#define CS4271_MODE1_DAC_DIF_LJ 0x00 +#define CS4271_MODE1_DAC_DIF_I2S 0x01 +#define CS4271_MODE1_DAC_DIF_RJ16 0x02 +#define CS4271_MODE1_DAC_DIF_RJ24 0x03 +#define CS4271_MODE1_DAC_DIF_RJ20 0x04 +#define CS4271_MODE1_DAC_DIF_RJ18 0x05 + +#define CS4271_DACCTL_AMUTE 0x80 +#define CS4271_DACCTL_IF_SLOW 0x40 + +#define CS4271_DACCTL_DEM_MASK 0x30 +#define CS4271_DACCTL_DEM_DIS 0x00 +#define CS4271_DACCTL_DEM_441 0x10 +#define CS4271_DACCTL_DEM_48 0x20 +#define CS4271_DACCTL_DEM_32 0x30 + +#define CS4271_DACCTL_SVRU 0x08 +#define CS4271_DACCTL_SRD 0x04 +#define CS4271_DACCTL_INVA 0x02 +#define CS4271_DACCTL_INVB 0x01 + +#define CS4271_DACVOL_BEQUA 0x40 +#define CS4271_DACVOL_SOFT 0x20 +#define CS4271_DACVOL_ZEROC 0x10 + +#define CS4271_DACVOL_ATAPI_MASK 0x0F +#define CS4271_DACVOL_ATAPI_M_M 0x00 +#define CS4271_DACVOL_ATAPI_M_BR 0x01 +#define CS4271_DACVOL_ATAPI_M_BL 0x02 +#define CS4271_DACVOL_ATAPI_M_BLR2 0x03 +#define CS4271_DACVOL_ATAPI_AR_M 0x04 +#define CS4271_DACVOL_ATAPI_AR_BR 0x05 +#define CS4271_DACVOL_ATAPI_AR_BL 0x06 +#define CS4271_DACVOL_ATAPI_AR_BLR2 0x07 +#define CS4271_DACVOL_ATAPI_AL_M 0x08 +#define CS4271_DACVOL_ATAPI_AL_BR 0x09 +#define CS4271_DACVOL_ATAPI_AL_BL 0x0A +#define CS4271_DACVOL_ATAPI_AL_BLR2 0x0B +#define CS4271_DACVOL_ATAPI_ALR2_M 0x0C +#define CS4271_DACVOL_ATAPI_ALR2_BR 0x0D +#define CS4271_DACVOL_ATAPI_ALR2_BL 0x0E +#define CS4271_DACVOL_ATAPI_ALR2_BLR2 0x0F + +#define CS4271_VOLA_MUTE 0x80 +#define CS4271_VOLA_VOL_MASK 0x7F +#define CS4271_VOLB_MUTE 0x80 +#define CS4271_VOLB_VOL_MASK 0x7F + +#define CS4271_ADCCTL_DITHER16 0x20 + +#define CS4271_ADCCTL_ADC_DIF_MASK 0x10 +#define CS4271_ADCCTL_ADC_DIF_LJ 0x00 +#define CS4271_ADCCTL_ADC_DIF_I2S 0x10 + +#define CS4271_ADCCTL_MUTEA 0x08 +#define CS4271_ADCCTL_MUTEB 0x04 +#define CS4271_ADCCTL_HPFDA 0x02 +#define CS4271_ADCCTL_HPFDB 0x01 + +#define CS4271_MODE2_LOOP 0x10 +#define CS4271_MODE2_MUTECAEQUB 0x08 +#define CS4271_MODE2_FREEZE 0x04 +#define CS4271_MODE2_CPEN 0x02 +#define CS4271_MODE2_PDN 0x01 + +#define CS4271_CHIPID_PART_MASK 0xF0 +#define CS4271_CHIPID_REV_MASK 0x0F + +/* + * Default CS4271 power-up configuration + * Array contains non-existing in hw register at address 0 + * Array do not include Chip ID, as codec driver does not use + * registers read operations at all + */ +static const u8 cs4271_dflt_reg[CS4271_NR_REGS] = { + 0, + 0, + CS4271_DACCTL_AMUTE, + CS4271_DACVOL_SOFT | CS4271_DACVOL_ATAPI_AL_BR, + 0, + 0, + 0, + 0, +}; + +struct cs4271_private { + /* SND_SOC_I2C or SND_SOC_SPI */ + enum snd_soc_control_type bus_type; + void *control_data; + unsigned int mclk; + bool master; + bool deemph; + /* Current sample rate for de-emphasis control */ + int rate; + /* GPIO driving Reset pin, if any */ + int gpio_nreset; + /* GPIO that disable serial bus, if any */ + int gpio_disable; +}; + +struct cs4271_clk_cfg { + unsigned int ratio; /* MCLK / sample rate */ + u8 speed_mode; /* codec speed mode: 1x, 2x, 4x */ + u8 mclk_master; /* ratio bit mask for Master mode */ + u8 mclk_slave; /* ratio bit mask for Slave mode */ +}; + +static struct cs4271_clk_cfg cs4271_clk_tab[] = { + {64, CS4271_MODE1_MODE_4X, CS4271_MODE1_DIV_1, CS4271_MODE1_DIV_1}, + {96, CS4271_MODE1_MODE_4X, CS4271_MODE1_DIV_15, CS4271_MODE1_DIV_1}, + {128, CS4271_MODE1_MODE_2X, CS4271_MODE1_DIV_1, CS4271_MODE1_DIV_1}, + {192, CS4271_MODE1_MODE_2X, CS4271_MODE1_DIV_15, CS4271_MODE1_DIV_1}, + {256, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_1, CS4271_MODE1_DIV_1}, + {384, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_15, CS4271_MODE1_DIV_1}, + {512, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_2, CS4271_MODE1_DIV_1}, + {768, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_3, CS4271_MODE1_DIV_3}, + {1024, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_3, CS4271_MODE1_DIV_3} +}; + +#define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab) + +/* + * @freq is the desired MCLK rate + * MCLK rate should (c) be the sample rate, multiplied by one of the + * ratios listed in cs4271_mclk_fs_ratios table + */ +static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + + cs4271->mclk = freq; + return 0; +} + +static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + unsigned int val = 0; + + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + cs4271->master = 0; + break; + case SND_SOC_DAIFMT_CBM_CFM: + cs4271->master = 1; + val |= CS4271_MODE1_MASTER; + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + val |= CS4271_MODE1_DAC_DIF_LJ; + snd_soc_update_bits(codec, CS4271_ADCCTL, + CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_LJ); + break; + case SND_SOC_DAIFMT_I2S: + val |= CS4271_MODE1_DAC_DIF_I2S; + snd_soc_update_bits(codec, CS4271_ADCCTL, + CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_I2S); + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + snd_soc_update_bits(codec, CS4271_MODE1, + CS4271_MODE1_DAC_DIF_MASK | CS4271_MODE1_MASTER, val); + + return 0; +} + +static int cs4271_deemph[] = {0, 44100, 48000, 32000}; + +static int cs4271_set_deemph(struct snd_soc_codec *codec) +{ + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + int i; + int val = CS4271_DACCTL_DEM_DIS; + + if (cs4271->deemph) { + /* Find closest de-emphasis freq */ + val = 1; + for (i = 2; i < ARRAY_SIZE(cs4271_deemph); i++) + if (abs(cs4271_deemph[i] - cs4271->rate) < + abs(cs4271_deemph[val] - cs4271->rate)) + val = i; + val <<= 4; + } + + return snd_soc_update_bits(codec, CS4271_DACCTL, + CS4271_DACCTL_DEM_MASK, val); +} + +static int cs4271_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = cs4271->deemph; + return 0; +} + +static int cs4271_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + + cs4271->deemph = ucontrol->value.enumerated.item[0]; + return cs4271_set_deemph(codec); +} + +static int cs4271_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + unsigned int i, ratio, val; + + cs4271->rate = params_rate(params); + ratio = cs4271->mclk / cs4271->rate; + for (i = 0; i < CS4171_NR_RATIOS; i++) + if (cs4271_clk_tab[i].ratio == ratio) + break; + + if ((i == CS4171_NR_RATIOS) || ((ratio == 1024) && cs4271->master)) { + dev_err(codec->dev, "Invalid sample rate\n"); + return -EINVAL; + } + + /* Configure DAC */ + val = cs4271_clk_tab[i].speed_mode; + + if (cs4271->master) + val |= cs4271_clk_tab[i].mclk_master; + else + val |= cs4271_clk_tab[i].mclk_slave; + + snd_soc_update_bits(codec, CS4271_MODE1, + CS4271_MODE1_MODE_MASK | CS4271_MODE1_DIV_MASK, val); + + return cs4271_set_deemph(codec); +} + +static int cs4271_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + int val_a = 0; + int val_b = 0; + + if (mute) { + val_a = CS4271_VOLA_MUTE; + val_b = CS4271_VOLB_MUTE; + } + + snd_soc_update_bits(codec, CS4271_VOLA, CS4271_VOLA_MUTE, val_a); + snd_soc_update_bits(codec, CS4271_VOLB, CS4271_VOLB_MUTE, val_b); + + return 0; +} + +/* CS4271 controls */ +static DECLARE_TLV_DB_SCALE(cs4271_dac_tlv, -12700, 100, 0); + +static const struct snd_kcontrol_new cs4271_snd_controls[] = { + SOC_DOUBLE_R_TLV("Master Playback Volume", CS4271_VOLA, CS4271_VOLB, + 0, 0x7F, 1, cs4271_dac_tlv), + SOC_SINGLE("Digital Loopback Switch", CS4271_MODE2, 4, 1, 0), + SOC_SINGLE("Soft Ramp Switch", CS4271_DACVOL, 5, 1, 0), + SOC_SINGLE("Zero Cross Switch", CS4271_DACVOL, 4, 1, 0), + SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0, + cs4271_get_deemph, cs4271_put_deemph), + SOC_SINGLE("Auto-Mute Switch", CS4271_DACCTL, 7, 1, 0), + SOC_SINGLE("Slow Roll Off Filter Switch", CS4271_DACCTL, 6, 1, 0), + SOC_SINGLE("Soft Volume Ramp-Up Switch", CS4271_DACCTL, 3, 1, 0), + SOC_SINGLE("Soft Ramp-Down Switch", CS4271_DACCTL, 2, 1, 0), + SOC_SINGLE("Left Channel Inversion Switch", CS4271_DACCTL, 1, 1, 0), + SOC_SINGLE("Right Channel Inversion Switch", CS4271_DACCTL, 0, 1, 0), + SOC_DOUBLE("Master Capture Switch", CS4271_ADCCTL, 3, 2, 1, 1), + SOC_SINGLE("Dither 16-Bit Data Switch", CS4271_ADCCTL, 5, 1, 0), + SOC_DOUBLE("High Pass Filter Switch", CS4271_ADCCTL, 1, 0, 1, 1), + SOC_DOUBLE_R("Master Playback Switch", CS4271_VOLA, CS4271_VOLB, + 7, 1, 1), +}; + +static struct snd_soc_dai_ops cs4271_dai_ops = { + .hw_params = cs4271_hw_params, + .set_sysclk = cs4271_set_dai_sysclk, + .set_fmt = cs4271_set_dai_fmt, + .digital_mute = cs4271_digital_mute, +}; + +struct snd_soc_dai_driver cs4271_dai = { + .name = "cs4271-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = CS4271_PCM_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = CS4271_PCM_FORMATS, + }, + .ops = &cs4271_dai_ops, + .symmetric_rates = 1, +}; + +#ifdef CONFIG_PM +static int cs4271_soc_suspend(struct snd_soc_codec *codec, pm_message_t mesg) +{ + /* Set power-down bit */ + snd_soc_update_bits(codec, CS4271_MODE2, 0, CS4271_MODE2_PDN); + return 0; +} + +static int cs4271_soc_resume(struct snd_soc_codec *codec) +{ + /* Restore codec state */ + snd_soc_cache_sync(codec); + /* then disable the power-down bit */ + snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0); + return 0; +} +#else +#define cs4271_soc_suspend NULL +#define cs4271_soc_resume NULL +#endif /* CONFIG_PM */ + +static int cs4271_probe(struct snd_soc_codec *codec) +{ + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + struct cs4271_platform_data *cs4271plat = codec->dev->platform_data; + int ret; + int gpio_nreset = -EINVAL; + int gpio_disable = -EINVAL; + + codec->control_data = cs4271->control_data; + + if (cs4271plat) { + if (gpio_is_valid(cs4271plat->gpio_nreset)) + gpio_nreset = cs4271plat->gpio_nreset; + if (gpio_is_valid(cs4271plat->gpio_disable)) + gpio_disable = cs4271plat->gpio_disable; + } + + if (gpio_disable >= 0) + if (gpio_request(gpio_disable, "CS4271 Disable")) + gpio_disable = -EINVAL; + if (gpio_disable >= 0) + gpio_direction_output(gpio_disable, 0); + + if (gpio_nreset >= 0) + if (gpio_request(gpio_nreset, "CS4271 Reset")) + gpio_nreset = -EINVAL; + if (gpio_nreset >= 0) { + /* Reset codec */ + gpio_direction_output(gpio_nreset, 0); + udelay(1); + gpio_set_value(gpio_nreset, 1); + /* Give the codec time to wake up */ + udelay(1); + } + + cs4271->gpio_nreset = gpio_nreset; + cs4271->gpio_disable = gpio_disable; + + /* + * In case of I2C, chip address specified in board data. + * So cache IO operations use 8 bit codec register address. + * In case of SPI, chip address and register address + * passed together as 16 bit value. + * Anyway, register address is masked with 0xFF inside + * soc-cache code. + */ + if (cs4271->bus_type == SND_SOC_SPI) + ret = snd_soc_codec_set_cache_io(codec, 16, 8, + cs4271->bus_type); + else + ret = snd_soc_codec_set_cache_io(codec, 8, 8, + cs4271->bus_type); + if (ret) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + snd_soc_update_bits(codec, CS4271_MODE2, 0, + CS4271_MODE2_PDN | CS4271_MODE2_CPEN); + snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0); + /* Power-up sequence requires 85 uS */ + udelay(85); + + return snd_soc_add_controls(codec, cs4271_snd_controls, + ARRAY_SIZE(cs4271_snd_controls)); +} + +static int cs4271_remove(struct snd_soc_codec *codec) +{ + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + int gpio_nreset, gpio_disable; + + gpio_nreset = cs4271->gpio_nreset; + gpio_disable = cs4271->gpio_disable; + + if (gpio_is_valid(gpio_nreset)) { + /* Set codec to the reset state */ + gpio_set_value(gpio_nreset, 0); + gpio_free(gpio_nreset); + } + + if (gpio_is_valid(gpio_disable)) + gpio_free(gpio_disable); + + return 0; +}; + +struct snd_soc_codec_driver soc_codec_dev_cs4271 = { + .probe = cs4271_probe, + .remove = cs4271_remove, + .suspend = cs4271_soc_suspend, + .resume = cs4271_soc_resume, + .reg_cache_default = cs4271_dflt_reg, + .reg_cache_size = ARRAY_SIZE(cs4271_dflt_reg), + .reg_word_size = sizeof(cs4271_dflt_reg[0]), + .compress_type = SND_SOC_FLAT_COMPRESSION, +}; + +#if defined(CONFIG_SPI_MASTER) +static int __devinit cs4271_spi_probe(struct spi_device *spi) +{ + struct cs4271_private *cs4271; + + cs4271 = devm_kzalloc(&spi->dev, sizeof(*cs4271), GFP_KERNEL); + if (!cs4271) + return -ENOMEM; + + spi_set_drvdata(spi, cs4271); + cs4271->control_data = spi; + cs4271->bus_type = SND_SOC_SPI; + + return snd_soc_register_codec(&spi->dev, &soc_codec_dev_cs4271, + &cs4271_dai, 1); +} + +static int __devexit cs4271_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static struct spi_driver cs4271_spi_driver = { + .driver = { + .name = "cs4271", + .owner = THIS_MODULE, + }, + .probe = cs4271_spi_probe, + .remove = __devexit_p(cs4271_spi_remove), +}; +#endif /* defined(CONFIG_SPI_MASTER) */ + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static struct i2c_device_id cs4271_i2c_id[] = { + {"cs4271", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id); + +static int __devinit cs4271_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cs4271_private *cs4271; + + cs4271 = devm_kzalloc(&client->dev, sizeof(*cs4271), GFP_KERNEL); + if (!cs4271) + return -ENOMEM; + + i2c_set_clientdata(client, cs4271); + cs4271->control_data = client; + cs4271->bus_type = SND_SOC_I2C; + + return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4271, + &cs4271_dai, 1); +} + +static int __devexit cs4271_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static struct i2c_driver cs4271_i2c_driver = { + .driver = { + .name = "cs4271", + .owner = THIS_MODULE, + }, + .id_table = cs4271_i2c_id, + .probe = cs4271_i2c_probe, + .remove = __devexit_p(cs4271_i2c_remove), +}; +#endif /* defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) */ + +/* + * We only register our serial bus driver here without + * assignment to particular chip. So if any of the below + * fails, there is some problem with I2C or SPI subsystem. + * In most cases this module will be compiled with support + * of only one serial bus. + */ +static int __init cs4271_modinit(void) +{ + int ret; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + ret = i2c_add_driver(&cs4271_i2c_driver); + if (ret) { + pr_err("Failed to register CS4271 I2C driver: %d\n", ret); + return ret; + } +#endif + +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&cs4271_spi_driver); + if (ret) { + pr_err("Failed to register CS4271 SPI driver: %d\n", ret); + return ret; + } +#endif + + return 0; +} +module_init(cs4271_modinit); + +static void __exit cs4271_modexit(void) +{ +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&cs4271_spi_driver); +#endif + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&cs4271_i2c_driver); +#endif +} +module_exit(cs4271_modexit); + +MODULE_AUTHOR("Alexander Sverdlin "); +MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From c358e640a669b528b32af5442c92b856de623e1c Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Fri, 21 Jan 2011 15:29:02 +0000 Subject: ASoC: soc-cache: Add trace event for snd_soc_cache_sync() This patch makes it easy to see when the syncing process begins and ends. You can also enable the snd_soc_reg_write tracepoint to see which registers are being synced. Signed-off-by: Dimitris Papastamos Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/trace/events/asoc.h | 25 +++++++++++++++++++++++++ sound/soc/soc-cache.c | 10 ++++++++++ 2 files changed, 35 insertions(+) (limited to 'include') diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index 186e84db4b54..ae973d2e27a1 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h @@ -229,6 +229,31 @@ TRACE_EVENT(snd_soc_jack_notify, TP_printk("jack=%s %x", __get_str(name), (int)__entry->val) ); +TRACE_EVENT(snd_soc_cache_sync, + + TP_PROTO(struct snd_soc_codec *codec, const char *type, + const char *status), + + TP_ARGS(codec, type, status), + + TP_STRUCT__entry( + __string( name, codec->name ) + __string( status, status ) + __string( type, type ) + __field( int, id ) + ), + + TP_fast_assign( + __assign_str(name, codec->name); + __assign_str(status, status); + __assign_str(type, type); + __entry->id = codec->id; + ), + + TP_printk("codec=%s.%d type=%s status=%s", __get_str(name), + (int)__entry->id, __get_str(type), __get_str(status)) +); + #endif /* _TRACE_ASOC_H */ /* This part must be outside protection */ diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index f83483963791..db66dc44add2 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -18,6 +18,8 @@ #include #include +#include + static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, unsigned int reg) { @@ -1601,18 +1603,26 @@ EXPORT_SYMBOL_GPL(snd_soc_cache_write); int snd_soc_cache_sync(struct snd_soc_codec *codec) { int ret; + const char *name; if (!codec->cache_sync) { return 0; } + if (codec->cache_ops->name) + name = codec->cache_ops->name; + else + name = "unknown"; + if (codec->cache_ops && codec->cache_ops->sync) { if (codec->cache_ops->name) dev_dbg(codec->dev, "Syncing %s cache for %s codec\n", codec->cache_ops->name, codec->name); + trace_snd_soc_cache_sync(codec, name, "start"); ret = codec->cache_ops->sync(codec); if (!ret) codec->cache_sync = 0; + trace_snd_soc_cache_sync(codec, name, "end"); return ret; } -- cgit v1.2.3 From 4d805f7b6607f6e547dc22e5d57c201e43d21c05 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 20 Jan 2011 11:46:02 +0900 Subject: ASoC: sh: fsi: Add snd_soc_dai_set_fmt support This patch add snd_soc_dai_ops :: set_fmt to FSI driver and select master/slave clock mode by snd_soc_dai_set_fmt on fsi-xxx.c instead of platform infomation code. This patch remove fsi_is_master function which is no longer needed. Signed-off-by: Kuninori Morimoto Acked-by: Liam Girdwood Acked-by: Paul Mundt Signed-off-by: Mark Brown --- arch/arm/mach-shmobile/board-ag5evm.c | 4 +-- arch/arm/mach-shmobile/board-ap4evb.c | 2 -- arch/arm/mach-shmobile/board-mackerel.c | 2 -- arch/sh/boards/mach-ecovec24/setup.c | 2 -- arch/sh/boards/mach-se/7724/setup.c | 2 -- include/sound/sh_fsi.h | 8 +---- sound/soc/sh/fsi-ak4642.c | 13 +++++--- sound/soc/sh/fsi-da7210.c | 12 ++++++-- sound/soc/sh/fsi-hdmi.c | 11 +++++++ sound/soc/sh/fsi.c | 54 +++++++++++++++++++-------------- 10 files changed, 63 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-shmobile/board-ag5evm.c b/arch/arm/mach-shmobile/board-ag5evm.c index c18a740a4159..9ee55e0fbeb1 100644 --- a/arch/arm/mach-shmobile/board-ag5evm.c +++ b/arch/arm/mach-shmobile/board-ag5evm.c @@ -119,9 +119,7 @@ static struct platform_device keysc_device = { /* FSI A */ static struct sh_fsi_platform_info fsi_info = { - .porta_flags = SH_FSI_OUT_SLAVE_MODE | - SH_FSI_IN_SLAVE_MODE | - SH_FSI_OFMT(I2S) | + .porta_flags = SH_FSI_OFMT(I2S) | SH_FSI_IFMT(I2S), }; diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c index 3cf0951caa2d..d503a74e30e4 100644 --- a/arch/arm/mach-shmobile/board-ap4evb.c +++ b/arch/arm/mach-shmobile/board-ap4evb.c @@ -674,8 +674,6 @@ static int fsi_set_rate(struct device *dev, int is_porta, int rate, int enable) static struct sh_fsi_platform_info fsi_info = { .porta_flags = SH_FSI_BRS_INV | - SH_FSI_OUT_SLAVE_MODE | - SH_FSI_IN_SLAVE_MODE | SH_FSI_OFMT(PCM) | SH_FSI_IFMT(PCM), diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c index 7b15d21f0f68..425962d5b29c 100644 --- a/arch/arm/mach-shmobile/board-mackerel.c +++ b/arch/arm/mach-shmobile/board-mackerel.c @@ -611,8 +611,6 @@ fsi_set_rate_end: static struct sh_fsi_platform_info fsi_info = { .porta_flags = SH_FSI_BRS_INV | - SH_FSI_OUT_SLAVE_MODE | - SH_FSI_IN_SLAVE_MODE | SH_FSI_OFMT(PCM) | SH_FSI_IFMT(PCM), diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index 33b662999fc6..037416f346cf 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -724,8 +724,6 @@ static struct platform_device camera_devices[] = { /* FSI */ static struct sh_fsi_platform_info fsi_info = { .portb_flags = SH_FSI_BRS_INV | - SH_FSI_OUT_SLAVE_MODE | - SH_FSI_IN_SLAVE_MODE | SH_FSI_OFMT(I2S) | SH_FSI_IFMT(I2S), }; diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index 527679394a25..b4aef05dd8b5 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -287,8 +287,6 @@ static struct platform_device ceu1_device = { /* change J20, J21, J22 pin to 1-2 connection to use slave mode */ static struct sh_fsi_platform_info fsi_info = { .porta_flags = SH_FSI_BRS_INV | - SH_FSI_OUT_SLAVE_MODE | - SH_FSI_IN_SLAVE_MODE | SH_FSI_OFMT(PCM) | SH_FSI_IFMT(PCM), }; diff --git a/include/sound/sh_fsi.h b/include/sound/sh_fsi.h index d79894192ae3..18e43279f70f 100644 --- a/include/sound/sh_fsi.h +++ b/include/sound/sh_fsi.h @@ -17,12 +17,11 @@ /* flags format - * 0xABCDEEFF + * 0xABC0EEFF * * A: channel size for TDM (input) * B: channel size for TDM (ooutput) * C: inversion - * D: mode * E: input format * F: output format */ @@ -46,11 +45,6 @@ #define SH_FSI_LRS_INV (1 << 22) #define SH_FSI_BRS_INV (1 << 23) -/* mode */ -#define SH_FSI_MODE_MASK 0x000F0000 -#define SH_FSI_IN_SLAVE_MODE (1 << 16) /* default master mode */ -#define SH_FSI_OUT_SLAVE_MODE (1 << 17) /* default master mode */ - /* DI format */ #define SH_FSI_FMT_MASK 0x000000FF #define SH_FSI_IFMT(x) (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 8) diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c index a722a4c661ff..ce058c749e6a 100644 --- a/sound/soc/sh/fsi-ak4642.c +++ b/sound/soc/sh/fsi-ak4642.c @@ -23,15 +23,20 @@ struct fsi_ak4642_data { static int fsi_ak4642_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; int ret; - ret = snd_soc_dai_set_fmt(dai, SND_SOC_DAIFMT_LEFT_J | - SND_SOC_DAIFMT_CBM_CFM); + ret = snd_soc_dai_set_fmt(codec, SND_SOC_DAIFMT_LEFT_J | + SND_SOC_DAIFMT_CBM_CFM); if (ret < 0) return ret; - ret = snd_soc_dai_set_sysclk(dai, 0, 11289600, 0); + ret = snd_soc_dai_set_sysclk(codec, 0, 11289600, 0); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBS_CFS); return ret; } diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c index e8df9da92f71..9b24ed466ab1 100644 --- a/sound/soc/sh/fsi-da7210.c +++ b/sound/soc/sh/fsi-da7210.c @@ -15,11 +15,19 @@ static int fsi_da7210_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; + int ret; - return snd_soc_dai_set_fmt(dai, + ret = snd_soc_dai_set_fmt(codec, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBS_CFS); + + return ret; } static struct snd_soc_dai_link fsi_da7210_dai = { diff --git a/sound/soc/sh/fsi-hdmi.c b/sound/soc/sh/fsi-hdmi.c index a52dd8ec71d3..96d8ce3f3211 100644 --- a/sound/soc/sh/fsi-hdmi.c +++ b/sound/soc/sh/fsi-hdmi.c @@ -12,6 +12,16 @@ #include #include +static int fsi_hdmi_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu = rtd->cpu_dai; + int ret; + + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBM_CFM); + + return ret; +} + static struct snd_soc_dai_link fsi_dai_link = { .name = "HDMI", .stream_name = "HDMI", @@ -19,6 +29,7 @@ static struct snd_soc_dai_link fsi_dai_link = { .codec_dai_name = "sh_mobile_hdmi-hifi", .platform_name = "sh_fsi2", .codec_name = "sh-mobile-hdmi", + .init = fsi_hdmi_dai_init, }; static struct snd_soc_card fsi_soc_card = { diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 1d0a16e80919..5f39f364effd 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -78,6 +78,8 @@ /* CKG1 */ #define ACKMD_MASK 0x00007000 #define BPFMD_MASK 0x00000700 +#define DIMD (1 << 4) +#define DOMD (1 << 0) /* A/B MST_CTLR */ #define BP (1 << 4) /* Fix the signal of Biphase output */ @@ -292,21 +294,6 @@ static inline struct fsi_stream *fsi_get_stream(struct fsi_priv *fsi, return is_play ? &fsi->playback : &fsi->capture; } -static int fsi_is_master_mode(struct fsi_priv *fsi, int is_play) -{ - u32 mode; - u32 flags = fsi_get_info_flags(fsi); - - mode = is_play ? SH_FSI_OUT_SLAVE_MODE : SH_FSI_IN_SLAVE_MODE; - - /* return - * 1 : master mode - * 0 : slave mode - */ - - return (mode & flags) != mode; -} - static u32 fsi_get_port_shift(struct fsi_priv *fsi, int is_play) { int is_porta = fsi_is_port_a(fsi); @@ -764,19 +751,11 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, u32 fmt; u32 data; int is_play = fsi_is_play(substream); - int is_master; io = fsi_get_stream(fsi, is_play); pm_runtime_get_sync(dai->dev); - /* CKG1 */ - data = is_play ? (1 << 0) : (1 << 4); - is_master = fsi_is_master_mode(fsi, is_play); - if (is_master) - fsi_reg_mask_set(fsi, CKG1, data, data); - else - fsi_reg_mask_set(fsi, CKG1, data, 0); /* clock inversion (CKG2) */ data = 0; @@ -893,6 +872,34 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } +static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai); + u32 data = 0; + int ret; + + pm_runtime_get_sync(dai->dev); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + data = DIMD | DOMD; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + ret = -EINVAL; + goto set_fmt_exit; + } + fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data); + ret = 0; + +set_fmt_exit: + pm_runtime_put_sync(dai->dev); + + return ret; +} + static int fsi_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -979,6 +986,7 @@ static struct snd_soc_dai_ops fsi_dai_ops = { .startup = fsi_dai_startup, .shutdown = fsi_dai_shutdown, .trigger = fsi_dai_trigger, + .set_fmt = fsi_dai_set_fmt, .hw_params = fsi_dai_hw_params, }; -- cgit v1.2.3 From 59eb21a6504731fc16db4cf9463065dd61093e08 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Mon, 17 Jan 2011 13:37:28 +0900 Subject: cfg80211: Extend channel to frequency mapping for 802.11j Extend channel to frequency mapping for 802.11j Japan 4.9GHz band, according to IEEE802.11 section 17.3.8.3.2 and Annex J. Because there are now overlapping channel numbers in the 2GHz and 5GHz band we can't map from channel to frequency without knowing the band. This is no problem as in most contexts we know the band. In places where we don't know the band (and WEXT compatibility) we assume the 2GHz band for channels below 14. This patch does not implement all channel to frequency mappings defined in 802.11, it's just an extension for 802.11j 20MHz channels. 5MHz and 10MHz channels as well as 802.11y channels have been omitted. The following drivers have been updated to reflect the API changes: iwl-3945, iwl-agn, iwmc3200wifi, libertas, mwl8k, rt2x00, wl1251, wl12xx. The drivers have been compile-tested only. Signed-off-by: Bruno Randolf Signed-off-by: Brian Prodoehl Acked-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-3945.c | 5 ++-- drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 5 ++-- drivers/net/wireless/iwlwifi/iwl-core.c | 3 ++- drivers/net/wireless/iwmc3200wifi/cfg80211.c | 3 ++- drivers/net/wireless/iwmc3200wifi/rx.c | 7 ++++-- drivers/net/wireless/libertas/cfg.c | 6 +++-- drivers/net/wireless/mwl8k.c | 6 +++-- drivers/net/wireless/rt2x00/rt2x00dev.c | 5 +++- drivers/net/wireless/wl1251/rx.c | 3 ++- drivers/net/wireless/wl12xx/rx.c | 2 +- include/net/cfg80211.h | 3 ++- net/mac80211/ibss.c | 3 ++- net/mac80211/mesh.c | 2 +- net/mac80211/mlme.c | 8 ++++--- net/mac80211/scan.c | 3 ++- net/wireless/reg.c | 6 ++--- net/wireless/util.c | 36 +++++++++++++++++----------- net/wireless/wext-compat.c | 5 +++- 18 files changed, 71 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index a9b852be4509..1d9dcd7e3b82 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -594,10 +594,11 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv, rx_status.flag = 0; rx_status.mactime = le64_to_cpu(rx_end->timestamp); - rx_status.freq = - ieee80211_channel_to_frequency(le16_to_cpu(rx_hdr->channel)); rx_status.band = (rx_hdr->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + rx_status.freq = + ieee80211_channel_to_frequency(le16_to_cpu(rx_hdr->channel), + rx_status.band); rx_status.rate_idx = iwl3945_hwrate_to_plcp_idx(rx_hdr->rate); if (rx_status.band == IEEE80211_BAND_5GHZ) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index 29dcda0bde65..c7d03874b380 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c @@ -1162,10 +1162,11 @@ void iwlagn_rx_reply_rx(struct iwl_priv *priv, /* rx_status carries information about the packet to mac80211 */ rx_status.mactime = le64_to_cpu(phy_res->timestamp); - rx_status.freq = - ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel)); rx_status.band = (phy_res->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + rx_status.freq = + ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel), + rx_status.band); rx_status.rate_idx = iwlagn_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band); rx_status.flag = 0; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index efbde1f1a8bf..a8d4a936a2e7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -227,7 +227,8 @@ int iwlcore_init_geos(struct iwl_priv *priv) geo_ch = &sband->channels[sband->n_channels++]; geo_ch->center_freq = - ieee80211_channel_to_frequency(ch->channel); + ieee80211_channel_to_frequency(ch->channel, + sband->band); geo_ch->max_power = ch->max_power_avg; geo_ch->max_antenna_gain = 0xff; geo_ch->hw_value = ch->channel; diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c index 5a4982271e96..ed57e4402800 100644 --- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c +++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c @@ -287,7 +287,8 @@ int iwm_cfg80211_inform_bss(struct iwm_priv *iwm) return -EINVAL; } - freq = ieee80211_channel_to_frequency(umac_bss->channel); + freq = ieee80211_channel_to_frequency(umac_bss->channel, + band->band); channel = ieee80211_get_channel(wiphy, freq); signal = umac_bss->rssi * 100; diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c index a944893ae3ca..9a57cf6a488f 100644 --- a/drivers/net/wireless/iwmc3200wifi/rx.c +++ b/drivers/net/wireless/iwmc3200wifi/rx.c @@ -543,7 +543,10 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf, switch (le32_to_cpu(complete->status)) { case UMAC_ASSOC_COMPLETE_SUCCESS: chan = ieee80211_get_channel(wiphy, - ieee80211_channel_to_frequency(complete->channel)); + ieee80211_channel_to_frequency(complete->channel, + complete->band == UMAC_BAND_2GHZ ? + IEEE80211_BAND_2GHZ : + IEEE80211_BAND_5GHZ)); if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) { /* Associated to a unallowed channel, disassociate. */ __iwm_invalidate_mlme_profile(iwm); @@ -841,7 +844,7 @@ static int iwm_mlme_update_bss_table(struct iwm_priv *iwm, u8 *buf, goto err; } - freq = ieee80211_channel_to_frequency(umac_bss->channel); + freq = ieee80211_channel_to_frequency(umac_bss->channel, band->band); channel = ieee80211_get_channel(wiphy, freq); signal = umac_bss->rssi * 100; diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 698a1f7694ed..30ef0351bfc4 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -607,7 +607,8 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy, /* No channel, no luck */ if (chan_no != -1) { struct wiphy *wiphy = priv->wdev->wiphy; - int freq = ieee80211_channel_to_frequency(chan_no); + int freq = ieee80211_channel_to_frequency(chan_no, + IEEE80211_BAND_2GHZ); struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq); @@ -1597,7 +1598,8 @@ static int lbs_get_survey(struct wiphy *wiphy, struct net_device *dev, lbs_deb_enter(LBS_DEB_CFG80211); survey->channel = ieee80211_get_channel(wiphy, - ieee80211_channel_to_frequency(priv->channel)); + ieee80211_channel_to_frequency(priv->channel, + IEEE80211_BAND_2GHZ)); ret = lbs_get_rssi(priv, &signal, &noise); if (ret == 0) { diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 106b427d0064..af4f2c64f242 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -906,7 +906,8 @@ mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status, } else { status->band = IEEE80211_BAND_2GHZ; } - status->freq = ieee80211_channel_to_frequency(rxd->channel); + status->freq = ieee80211_channel_to_frequency(rxd->channel, + status->band); *qos = rxd->qos_control; @@ -1013,7 +1014,8 @@ mwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status, } else { status->band = IEEE80211_BAND_2GHZ; } - status->freq = ieee80211_channel_to_frequency(rxd->channel); + status->freq = ieee80211_channel_to_frequency(rxd->channel, + status->band); *qos = rxd->qos_control; if ((rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DECRYPT_ERROR) && diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 9597a03242cc..31b7db05abd9 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -649,7 +649,10 @@ static void rt2x00lib_channel(struct ieee80211_channel *entry, const int channel, const int tx_power, const int value) { - entry->center_freq = ieee80211_channel_to_frequency(channel); + /* XXX: this assumption about the band is wrong for 802.11j */ + entry->band = channel <= 14 ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + entry->center_freq = ieee80211_channel_to_frequency(channel, + entry->band); entry->hw_value = value; entry->max_power = tx_power; entry->max_antenna_gain = 0xff; diff --git a/drivers/net/wireless/wl1251/rx.c b/drivers/net/wireless/wl1251/rx.c index efa53607d5c9..86eef456d7b2 100644 --- a/drivers/net/wireless/wl1251/rx.c +++ b/drivers/net/wireless/wl1251/rx.c @@ -78,7 +78,8 @@ static void wl1251_rx_status(struct wl1251 *wl, */ wl->noise = desc->rssi - desc->snr / 2; - status->freq = ieee80211_channel_to_frequency(desc->channel); + status->freq = ieee80211_channel_to_frequency(desc->channel, + status->band); status->flag |= RX_FLAG_TSFT; diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index 682304c30b81..ec8d843d41cf 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -76,7 +76,7 @@ static void wl1271_rx_status(struct wl1271 *wl, */ wl->noise = desc->rssi - (desc->snr >> 1); - status->freq = ieee80211_channel_to_frequency(desc->channel); + status->freq = ieee80211_channel_to_frequency(desc->channel, desc_band); if (desc->flags & WL1271_RX_DESC_ENCRYPT_MASK) { status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1322695beb52..679a0494b5f2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1790,8 +1790,9 @@ static inline void *wdev_priv(struct wireless_dev *wdev) /** * ieee80211_channel_to_frequency - convert channel number to frequency * @chan: channel number + * @band: band, necessary due to channel number overlap */ -extern int ieee80211_channel_to_frequency(int chan); +extern int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band); /** * ieee80211_frequency_to_channel - convert frequency to channel number diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 53c7077ffd4f..775fb63471c4 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -270,7 +270,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, enum ieee80211_band band = rx_status->band; if (elems->ds_params && elems->ds_params_len == 1) - freq = ieee80211_channel_to_frequency(elems->ds_params[0]); + freq = ieee80211_channel_to_frequency(elems->ds_params[0], + band); else freq = rx_status->freq; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 2563fd1ea998..2a57cc02c618 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -574,7 +574,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, &elems); if (elems.ds_params && elems.ds_params_len == 1) - freq = ieee80211_channel_to_frequency(elems.ds_params[0]); + freq = ieee80211_channel_to_frequency(elems.ds_params[0], band); else freq = rx_status->freq; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index eecbb1fcd2ab..32210695b8b6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -176,7 +176,7 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, /* check that channel matches the right operating channel */ if (local->hw.conf.channel->center_freq != - ieee80211_channel_to_frequency(hti->control_chan)) + ieee80211_channel_to_frequency(hti->control_chan, sband->band)) enable_ht = false; if (enable_ht) { @@ -429,7 +429,8 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, container_of((void *)bss, struct cfg80211_bss, priv); struct ieee80211_channel *new_ch; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num); + int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num, + cbss->channel->band); ASSERT_MGD_MTX(ifmgd); @@ -1519,7 +1520,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, } if (elems->ds_params && elems->ds_params_len == 1) - freq = ieee80211_channel_to_frequency(elems->ds_params[0]); + freq = ieee80211_channel_to_frequency(elems->ds_params[0], + rx_status->band); else freq = rx_status->freq; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index fb274db77e3c..1ef73be76b25 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -196,7 +196,8 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) ieee802_11_parse_elems(elements, skb->len - baselen, &elems); if (elems.ds_params && elems.ds_params_len == 1) - freq = ieee80211_channel_to_frequency(elems.ds_params[0]); + freq = ieee80211_channel_to_frequency(elems.ds_params[0], + rx_status->band); else freq = rx_status->freq; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 37693b6ef23a..c565689f0b9f 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1801,9 +1801,9 @@ void regulatory_hint_disconnect(void) static bool freq_is_chan_12_13_14(u16 freq) { - if (freq == ieee80211_channel_to_frequency(12) || - freq == ieee80211_channel_to_frequency(13) || - freq == ieee80211_channel_to_frequency(14)) + if (freq == ieee80211_channel_to_frequency(12, IEEE80211_BAND_2GHZ) || + freq == ieee80211_channel_to_frequency(13, IEEE80211_BAND_2GHZ) || + freq == ieee80211_channel_to_frequency(14, IEEE80211_BAND_2GHZ)) return true; return false; } diff --git a/net/wireless/util.c b/net/wireless/util.c index 7620ae2fcf18..4ed065d8bb51 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -29,29 +29,37 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband, } EXPORT_SYMBOL(ieee80211_get_response_rate); -int ieee80211_channel_to_frequency(int chan) +int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band) { - if (chan < 14) - return 2407 + chan * 5; - - if (chan == 14) - return 2484; - - /* FIXME: 802.11j 17.3.8.3.2 */ - return (chan + 1000) * 5; + /* see 802.11 17.3.8.3.2 and Annex J + * there are overlapping channel numbers in 5GHz and 2GHz bands */ + if (band == IEEE80211_BAND_5GHZ) { + if (chan >= 182 && chan <= 196) + return 4000 + chan * 5; + else + return 5000 + chan * 5; + } else { /* IEEE80211_BAND_2GHZ */ + if (chan == 14) + return 2484; + else if (chan < 14) + return 2407 + chan * 5; + else + return 0; /* not supported */ + } } EXPORT_SYMBOL(ieee80211_channel_to_frequency); int ieee80211_frequency_to_channel(int freq) { + /* see 802.11 17.3.8.3.2 and Annex J */ if (freq == 2484) return 14; - - if (freq < 2484) + else if (freq < 2484) return (freq - 2407) / 5; - - /* FIXME: 802.11j 17.3.8.3.2 */ - return freq/5 - 1000; + else if (freq >= 4910 && freq <= 4980) + return (freq - 4000) / 5; + else + return (freq - 5000) / 5; } EXPORT_SYMBOL(ieee80211_frequency_to_channel); diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 3e5dbd4e4cd5..7f1f4ec49041 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -267,9 +267,12 @@ int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq) * -EINVAL for impossible things. */ if (freq->e == 0) { + enum ieee80211_band band = IEEE80211_BAND_2GHZ; if (freq->m < 0) return 0; - return ieee80211_channel_to_frequency(freq->m); + if (freq->m > 14) + band = IEEE80211_BAND_5GHZ; + return ieee80211_channel_to_frequency(freq->m, band); } else { int i, div = 1000000; for (i = 0; i < freq->e; i++) -- cgit v1.2.3 From cd7eab44e9946c28d595abe3e9a43e945bc49141 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 19 Jan 2011 21:01:44 +0000 Subject: genirq: Add IRQ affinity notifiers When initiating I/O on a multiqueue and multi-IRQ device, we may want to select a queue for which the response will be handled on the same or a nearby CPU. This requires a reverse-map of IRQ affinity. Add a notification mechanism to support this. This is based closely on work by Thomas Gleixner . Signed-off-by: Ben Hutchings Cc: linux-net-drivers@solarflare.com Cc: Tom Herbert Cc: David Miller LKML-Reference: <1295470904.11126.84.camel@bwh-desktop> Signed-off-by: Thomas Gleixner --- include/linux/interrupt.h | 33 ++++++++++++++++++- include/linux/irqdesc.h | 3 ++ kernel/irq/manage.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 55e0d4253e49..63c5ad78e37c 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include @@ -240,6 +242,35 @@ extern int irq_can_set_affinity(unsigned int irq); extern int irq_select_affinity(unsigned int irq); extern int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m); + +/** + * struct irq_affinity_notify - context for notification of IRQ affinity changes + * @irq: Interrupt to which notification applies + * @kref: Reference count, for internal use + * @work: Work item, for internal use + * @notify: Function to be called on change. This will be + * called in process context. + * @release: Function to be called on release. This will be + * called in process context. Once registered, the + * structure must only be freed when this function is + * called or later. + */ +struct irq_affinity_notify { + unsigned int irq; + struct kref kref; + struct work_struct work; + void (*notify)(struct irq_affinity_notify *, const cpumask_t *mask); + void (*release)(struct kref *ref); +}; + +extern int +irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify); + +static inline void irq_run_affinity_notifiers(void) +{ + flush_scheduled_work(); +} + #else /* CONFIG_SMP */ static inline int irq_set_affinity(unsigned int irq, const struct cpumask *m) @@ -255,7 +286,7 @@ static inline int irq_can_set_affinity(unsigned int irq) static inline int irq_select_affinity(unsigned int irq) { return 0; } static inline int irq_set_affinity_hint(unsigned int irq, - const struct cpumask *m) + const struct cpumask *m) { return -EINVAL; } diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index c1a95b7b58de..bfef56dadddb 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h @@ -8,6 +8,7 @@ * For now it's included from */ +struct irq_affinity_notify; struct proc_dir_entry; struct timer_rand_state; /** @@ -24,6 +25,7 @@ struct timer_rand_state; * @last_unhandled: aging timer for unhandled count * @irqs_unhandled: stats field for spurious unhandled interrupts * @lock: locking for SMP + * @affinity_notify: context for notification of affinity changes * @pending_mask: pending rebalanced interrupts * @threads_active: number of irqaction threads currently running * @wait_for_threads: wait queue for sync_irq to wait for threaded handlers @@ -70,6 +72,7 @@ struct irq_desc { raw_spinlock_t lock; #ifdef CONFIG_SMP const struct cpumask *affinity_hint; + struct irq_affinity_notify *affinity_notify; #ifdef CONFIG_GENERIC_PENDING_IRQ cpumask_var_t pending_mask; #endif diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 0caa59f747dd..0587c5ceaed8 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -134,6 +134,10 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask) irq_set_thread_affinity(desc); } #endif + if (desc->affinity_notify) { + kref_get(&desc->affinity_notify->kref); + schedule_work(&desc->affinity_notify->work); + } desc->status |= IRQ_AFFINITY_SET; raw_spin_unlock_irqrestore(&desc->lock, flags); return 0; @@ -155,6 +159,79 @@ int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m) } EXPORT_SYMBOL_GPL(irq_set_affinity_hint); +static void irq_affinity_notify(struct work_struct *work) +{ + struct irq_affinity_notify *notify = + container_of(work, struct irq_affinity_notify, work); + struct irq_desc *desc = irq_to_desc(notify->irq); + cpumask_var_t cpumask; + unsigned long flags; + + if (!desc) + goto out; + + if (!alloc_cpumask_var(&cpumask, GFP_KERNEL)) + goto out; + + raw_spin_lock_irqsave(&desc->lock, flags); +#ifdef CONFIG_GENERIC_PENDING_IRQ + if (desc->status & IRQ_MOVE_PENDING) + cpumask_copy(cpumask, desc->pending_mask); + else +#endif + cpumask_copy(cpumask, desc->affinity); + raw_spin_unlock_irqrestore(&desc->lock, flags); + + notify->notify(notify, cpumask); + + free_cpumask_var(cpumask); +out: + kref_put(¬ify->kref, notify->release); +} + +/** + * irq_set_affinity_notifier - control notification of IRQ affinity changes + * @irq: Interrupt for which to enable/disable notification + * @notify: Context for notification, or %NULL to disable + * notification. Function pointers must be initialised; + * the other fields will be initialised by this function. + * + * Must be called in process context. Notification may only be enabled + * after the IRQ is allocated and must be disabled before the IRQ is + * freed using free_irq(). + */ +int +irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify) +{ + struct irq_desc *desc = irq_to_desc(irq); + struct irq_affinity_notify *old_notify; + unsigned long flags; + + /* The release function is promised process context */ + might_sleep(); + + if (!desc) + return -EINVAL; + + /* Complete initialisation of *notify */ + if (notify) { + notify->irq = irq; + kref_init(¬ify->kref); + INIT_WORK(¬ify->work, irq_affinity_notify); + } + + raw_spin_lock_irqsave(&desc->lock, flags); + old_notify = desc->affinity_notify; + desc->affinity_notify = notify; + raw_spin_unlock_irqrestore(&desc->lock, flags); + + if (old_notify) + kref_put(&old_notify->kref, old_notify->release); + + return 0; +} +EXPORT_SYMBOL_GPL(irq_set_affinity_notifier); + #ifndef CONFIG_AUTO_IRQ_AFFINITY /* * Generic version of the affinity autoselector. @@ -1004,6 +1081,11 @@ void free_irq(unsigned int irq, void *dev_id) if (!desc) return; +#ifdef CONFIG_SMP + if (WARN_ON(desc->affinity_notify)) + desc->affinity_notify = NULL; +#endif + chip_bus_lock(desc); kfree(__free_irq(irq, dev_id)); chip_bus_sync_unlock(desc); -- cgit v1.2.3 From e71084af58cf15e6043338500eeaf6281d0a62af Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sat, 22 Jan 2011 15:05:03 +0100 Subject: firewire: core: fix card->reset_jiffies overflow On a 32-bit machine with, e.g., HZ=1000, jiffies will overflow after about 50 days, so if there are between 25 and 50 days between bus resets, the card->reset_jiffies comparisons can get wrong results. To fix this, ensure that this timestamp always uses 64 bits. Signed-off-by: Clemens Ladisch Signed-off-by: "Stefan Richter" --- drivers/firewire/core-card.c | 5 +++-- drivers/firewire/core-cdev.c | 3 ++- drivers/firewire/core-device.c | 3 ++- drivers/firewire/core-topology.c | 2 +- include/linux/firewire.h | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 24ff35511e2b..3241dc4e1fc9 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -233,7 +233,7 @@ static void br_work(struct work_struct *work) /* Delay for 2s after last reset per IEEE 1394 clause 8.2.1. */ if (card->reset_jiffies != 0 && - time_is_after_jiffies(card->reset_jiffies + 2 * HZ)) { + time_before64(get_jiffies_64(), card->reset_jiffies + 2 * HZ)) { if (!schedule_delayed_work(&card->br_work, 2 * HZ)) fw_card_put(card); return; @@ -316,7 +316,8 @@ static void bm_work(struct work_struct *work) irm_id = card->irm_node->node_id; local_id = card->local_node->node_id; - grace = time_after(jiffies, card->reset_jiffies + DIV_ROUND_UP(HZ, 8)); + grace = time_after64(get_jiffies_64(), + card->reset_jiffies + DIV_ROUND_UP(HZ, 8)); if ((is_next_generation(generation, card->bm_generation) && !card->bm_abdicate) || diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index e0c13fb3ae22..62ac111af243 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1205,7 +1205,8 @@ static void iso_resource_work(struct work_struct *work) todo = r->todo; /* Allow 1000ms grace period for other reallocations. */ if (todo == ISO_RES_ALLOC && - time_is_after_jiffies(client->device->card->reset_jiffies + HZ)) { + time_before64(get_jiffies_64(), + client->device->card->reset_jiffies + HZ)) { schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3)); skip = true; } else { diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 6113b896e790..57461923bacf 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -747,7 +747,8 @@ static void fw_device_shutdown(struct work_struct *work) container_of(work, struct fw_device, work.work); int minor = MINOR(device->device.devt); - if (time_is_after_jiffies(device->card->reset_jiffies + SHUTDOWN_DELAY) + if (time_before64(get_jiffies_64(), + device->card->reset_jiffies + SHUTDOWN_DELAY) && !list_empty(&device->card->link)) { schedule_delayed_work(&device->work, SHUTDOWN_DELAY); return; diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index 09be1a635505..193ed9233144 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -545,7 +545,7 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, */ smp_wmb(); card->generation = generation; - card->reset_jiffies = jiffies; + card->reset_jiffies = get_jiffies_64(); card->bm_node_id = 0xffff; card->bm_abdicate = bm_abdicate; fw_schedule_bm_work(card, 0); diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 9a3f5f9383f6..b6d21d5a11a2 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -89,7 +89,7 @@ struct fw_card { int current_tlabel; u64 tlabel_mask; struct list_head transaction_list; - unsigned long reset_jiffies; + u64 reset_jiffies; u32 split_timeout_hi; u32 split_timeout_lo; -- cgit v1.2.3 From 63310467a3d1ed6a0460ec1f4268126cd1ceec2e Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Thu, 20 Jan 2011 11:12:26 -0600 Subject: mm: Remove support for kmem_cache_name() The last user was ext4 and Eric Sandeen removed the call in a recent patch. See the following URL for the discussion: http://marc.info/?l=linux-ext4&m=129546975702198&w=2 Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slab.h | 1 - mm/slab.c | 8 -------- mm/slob.c | 6 ------ mm/slub.c | 6 ------ 4 files changed, 21 deletions(-) (limited to 'include') diff --git a/include/linux/slab.h b/include/linux/slab.h index fa9086647eb7..ad4dd1c8d30a 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -105,7 +105,6 @@ void kmem_cache_destroy(struct kmem_cache *); int kmem_cache_shrink(struct kmem_cache *); void kmem_cache_free(struct kmem_cache *, void *); unsigned int kmem_cache_size(struct kmem_cache *); -const char *kmem_cache_name(struct kmem_cache *); /* * Please use this macro to create slab caches. Simply specify the diff --git a/mm/slab.c b/mm/slab.c index 37961d1f584f..4bab2d1a8291 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -2147,8 +2147,6 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp) * * @name must be valid until the cache is destroyed. This implies that * the module calling this has to destroy the cache before getting unloaded. - * Note that kmem_cache_name() is not guaranteed to return the same pointer, - * therefore applications must manage it themselves. * * The flags are * @@ -3840,12 +3838,6 @@ unsigned int kmem_cache_size(struct kmem_cache *cachep) } EXPORT_SYMBOL(kmem_cache_size); -const char *kmem_cache_name(struct kmem_cache *cachep) -{ - return cachep->name; -} -EXPORT_SYMBOL_GPL(kmem_cache_name); - /* * This initializes kmem_list3 or resizes various caches for all nodes. */ diff --git a/mm/slob.c b/mm/slob.c index 3588eaaef726..46e0aee33a23 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -666,12 +666,6 @@ unsigned int kmem_cache_size(struct kmem_cache *c) } EXPORT_SYMBOL(kmem_cache_size); -const char *kmem_cache_name(struct kmem_cache *c) -{ - return c->name; -} -EXPORT_SYMBOL(kmem_cache_name); - int kmem_cache_shrink(struct kmem_cache *d) { return 0; diff --git a/mm/slub.c b/mm/slub.c index e15aa7f193c9..d2f343a54bad 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2399,12 +2399,6 @@ unsigned int kmem_cache_size(struct kmem_cache *s) } EXPORT_SYMBOL(kmem_cache_size); -const char *kmem_cache_name(struct kmem_cache *s) -{ - return s->name; -} -EXPORT_SYMBOL(kmem_cache_name); - static void list_slab_objects(struct kmem_cache *s, struct page *page, const char *text) { -- cgit v1.2.3 From ac61c46f4f7665ab4548e90430c37b2529e16cff Mon Sep 17 00:00:00 2001 From: David Dillow Date: Sun, 16 Jan 2011 15:12:39 -0500 Subject: [SCSI] fix incorrect value of SCSI_MAX_SG_CHAIN_SEGMENTS due to include file ordering If the compiled object doesn't include linux/scatterlist.h before scsi/scsi.h, it will get an incorrect definition of SCSI_MAX_SG_CHAIN_SEGMENTS. Signed-off-by: David Dillow Cc: stable@kernel.org Signed-off-by: James Bottomley --- include/scsi/scsi.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index 648d23358038..b76d4006e36d 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -9,6 +9,7 @@ #define _SCSI_SCSI_H #include +#include struct scsi_cmnd; -- cgit v1.2.3 From 940d7faa4818f386fcdf1b7266ec7b62bf07a7d0 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 6 Jan 2011 15:38:24 -0500 Subject: [SCSI] scsi_dh: Use scsi_devinfo functions to do matching of device_handler tables. Previously we were using strncmp in order to avoid having to include whitespace in the devlist, but this means "HSV1000" matches a device list entry that says "HSV100", which is wrong. This patch changes scsi_dh.c to use scsi_devinfo's matching functions instead, since they handle these cases correctly. Signed-off-by: James Bottomley --- drivers/scsi/device_handler/scsi_dh.c | 112 +++++++++++----------------------- drivers/scsi/scsi_priv.h | 1 + include/scsi/scsi_device.h | 1 + 3 files changed, 39 insertions(+), 75 deletions(-) (limited to 'include') diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index b837c5b3c8f9..564e6ecd17c2 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c @@ -25,16 +25,9 @@ #include #include "../scsi_priv.h" -struct scsi_dh_devinfo_list { - struct list_head node; - char vendor[9]; - char model[17]; - struct scsi_device_handler *handler; -}; - static DEFINE_SPINLOCK(list_lock); static LIST_HEAD(scsi_dh_list); -static LIST_HEAD(scsi_dh_dev_list); +static int scsi_dh_list_idx = 1; static struct scsi_device_handler *get_device_handler(const char *name) { @@ -51,40 +44,18 @@ static struct scsi_device_handler *get_device_handler(const char *name) return found; } - -static struct scsi_device_handler * -scsi_dh_cache_lookup(struct scsi_device *sdev) +static struct scsi_device_handler *get_device_handler_by_idx(int idx) { - struct scsi_dh_devinfo_list *tmp; - struct scsi_device_handler *found_dh = NULL; + struct scsi_device_handler *tmp, *found = NULL; spin_lock(&list_lock); - list_for_each_entry(tmp, &scsi_dh_dev_list, node) { - if (!strncmp(sdev->vendor, tmp->vendor, strlen(tmp->vendor)) && - !strncmp(sdev->model, tmp->model, strlen(tmp->model))) { - found_dh = tmp->handler; + list_for_each_entry(tmp, &scsi_dh_list, list) { + if (tmp->idx == idx) { + found = tmp; break; } } spin_unlock(&list_lock); - - return found_dh; -} - -static int scsi_dh_handler_lookup(struct scsi_device_handler *scsi_dh, - struct scsi_device *sdev) -{ - int i, found = 0; - - for(i = 0; scsi_dh->devlist[i].vendor; i++) { - if (!strncmp(sdev->vendor, scsi_dh->devlist[i].vendor, - strlen(scsi_dh->devlist[i].vendor)) && - !strncmp(sdev->model, scsi_dh->devlist[i].model, - strlen(scsi_dh->devlist[i].model))) { - found = 1; - break; - } - } return found; } @@ -102,41 +73,14 @@ device_handler_match(struct scsi_device_handler *scsi_dh, struct scsi_device *sdev) { struct scsi_device_handler *found_dh = NULL; - struct scsi_dh_devinfo_list *tmp; + int idx; - found_dh = scsi_dh_cache_lookup(sdev); - if (found_dh) - return found_dh; + idx = scsi_get_device_flags_keyed(sdev, sdev->vendor, sdev->model, + SCSI_DEVINFO_DH); + found_dh = get_device_handler_by_idx(idx); - if (scsi_dh) { - if (scsi_dh_handler_lookup(scsi_dh, sdev)) - found_dh = scsi_dh; - } else { - struct scsi_device_handler *tmp_dh; - - spin_lock(&list_lock); - list_for_each_entry(tmp_dh, &scsi_dh_list, list) { - if (scsi_dh_handler_lookup(tmp_dh, sdev)) - found_dh = tmp_dh; - } - spin_unlock(&list_lock); - } - - if (found_dh) { /* If device is found, add it to the cache */ - tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); - if (tmp) { - strncpy(tmp->vendor, sdev->vendor, 8); - strncpy(tmp->model, sdev->model, 16); - tmp->vendor[8] = '\0'; - tmp->model[16] = '\0'; - tmp->handler = found_dh; - spin_lock(&list_lock); - list_add(&tmp->node, &scsi_dh_dev_list); - spin_unlock(&list_lock); - } else { - found_dh = NULL; - } - } + if (scsi_dh && found_dh != scsi_dh) + found_dh = NULL; return found_dh; } @@ -373,12 +317,25 @@ static int scsi_dh_notifier_remove(struct device *dev, void *data) */ int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) { + int i; + if (get_device_handler(scsi_dh->name)) return -EBUSY; spin_lock(&list_lock); + scsi_dh->idx = scsi_dh_list_idx++; list_add(&scsi_dh->list, &scsi_dh_list); spin_unlock(&list_lock); + + for (i = 0; scsi_dh->devlist[i].vendor; i++) { + scsi_dev_info_list_add_keyed(0, + scsi_dh->devlist[i].vendor, + scsi_dh->devlist[i].model, + NULL, + scsi_dh->idx, + SCSI_DEVINFO_DH); + } + bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add); printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name); @@ -395,7 +352,7 @@ EXPORT_SYMBOL_GPL(scsi_register_device_handler); */ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) { - struct scsi_dh_devinfo_list *tmp, *pos; + int i; if (!get_device_handler(scsi_dh->name)) return -ENODEV; @@ -403,14 +360,14 @@ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_remove); + for (i = 0; scsi_dh->devlist[i].vendor; i++) { + scsi_dev_info_list_del_keyed(scsi_dh->devlist[i].vendor, + scsi_dh->devlist[i].model, + SCSI_DEVINFO_DH); + } + spin_lock(&list_lock); list_del(&scsi_dh->list); - list_for_each_entry_safe(pos, tmp, &scsi_dh_dev_list, node) { - if (pos->handler == scsi_dh) { - list_del(&pos->node); - kfree(pos); - } - } spin_unlock(&list_lock); printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name); @@ -576,6 +533,10 @@ static int __init scsi_dh_init(void) { int r; + r = scsi_dev_info_add_list(SCSI_DEVINFO_DH, "SCSI Device Handler"); + if (r) + return r; + r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb); if (!r) @@ -590,6 +551,7 @@ static void __exit scsi_dh_exit(void) bus_for_each_dev(&scsi_bus_type, NULL, NULL, scsi_dh_sysfs_attr_remove); bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb); + scsi_dev_info_remove_list(SCSI_DEVINFO_DH); } module_init(scsi_dh_init); diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index ce9e0adc8df8..9868afdb24e2 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -45,6 +45,7 @@ static inline void scsi_log_completion(struct scsi_cmnd *cmd, int disposition) enum { SCSI_DEVINFO_GLOBAL = 0, SCSI_DEVINFO_SPI, + SCSI_DEVINFO_DH, }; extern int scsi_get_device_flags(struct scsi_device *sdev, diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 85867dcde335..f171c65dc5a8 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -184,6 +184,7 @@ typedef void (*activate_complete)(void *, int); struct scsi_device_handler { /* Used by the infrastructure */ struct list_head list; /* list of scsi_device_handlers */ + int idx; /* Filled by the hardware handler */ struct module *module; -- cgit v1.2.3 From c39649c331c70952700f99832b03f87e9d7f5b4b Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 19 Jan 2011 11:03:25 +0000 Subject: lib: cpu_rmap: CPU affinity reverse-mapping When initiating I/O on a multiqueue and multi-IRQ device, we may want to select a queue for which the response will be handled on the same or a nearby CPU. This requires a reverse-map of IRQ affinity. Add library functions to support a generic reverse-mapping from CPUs to objects with affinity and the specific case where the objects are IRQs. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/cpu_rmap.h | 73 +++++++++++++ lib/Kconfig | 4 + lib/Makefile | 2 + lib/cpu_rmap.c | 269 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 348 insertions(+) create mode 100644 include/linux/cpu_rmap.h create mode 100644 lib/cpu_rmap.c (limited to 'include') diff --git a/include/linux/cpu_rmap.h b/include/linux/cpu_rmap.h new file mode 100644 index 000000000000..473771a528c0 --- /dev/null +++ b/include/linux/cpu_rmap.h @@ -0,0 +1,73 @@ +/* + * cpu_rmap.c: CPU affinity reverse-map support + * Copyright 2011 Solarflare Communications Inc. + * + * 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, incorporated herein by reference. + */ + +#include +#include +#include + +/** + * struct cpu_rmap - CPU affinity reverse-map + * @size: Number of objects to be reverse-mapped + * @used: Number of objects added + * @obj: Pointer to array of object pointers + * @near: For each CPU, the index and distance to the nearest object, + * based on affinity masks + */ +struct cpu_rmap { + u16 size, used; + void **obj; + struct { + u16 index; + u16 dist; + } near[0]; +}; +#define CPU_RMAP_DIST_INF 0xffff + +extern struct cpu_rmap *alloc_cpu_rmap(unsigned int size, gfp_t flags); + +/** + * free_cpu_rmap - free CPU affinity reverse-map + * @rmap: Reverse-map allocated with alloc_cpu_rmap(), or %NULL + */ +static inline void free_cpu_rmap(struct cpu_rmap *rmap) +{ + kfree(rmap); +} + +extern int cpu_rmap_add(struct cpu_rmap *rmap, void *obj); +extern int cpu_rmap_update(struct cpu_rmap *rmap, u16 index, + const struct cpumask *affinity); + +static inline u16 cpu_rmap_lookup_index(struct cpu_rmap *rmap, unsigned int cpu) +{ + return rmap->near[cpu].index; +} + +static inline void *cpu_rmap_lookup_obj(struct cpu_rmap *rmap, unsigned int cpu) +{ + return rmap->obj[rmap->near[cpu].index]; +} + +#ifdef CONFIG_GENERIC_HARDIRQS + +/** + * alloc_irq_cpu_rmap - allocate CPU affinity reverse-map for IRQs + * @size: Number of objects to be mapped + * + * Must be called in process context. + */ +static inline struct cpu_rmap *alloc_irq_cpu_rmap(unsigned int size) +{ + return alloc_cpu_rmap(size, GFP_KERNEL); +} +extern void free_irq_cpu_rmap(struct cpu_rmap *rmap); + +extern int irq_cpu_rmap_add(struct cpu_rmap *rmap, int irq); + +#endif diff --git a/lib/Kconfig b/lib/Kconfig index 0ee67e08ad3e..8334342e0d05 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -201,6 +201,10 @@ config DISABLE_OBSOLETE_CPUMASK_FUNCTIONS bool "Disable obsolete cpumask functions" if DEBUG_PER_CPU_MAPS depends on EXPERIMENTAL && BROKEN +config CPU_RMAP + bool + depends on SMP + # # Netlink attribute parsing support is select'ed if needed # diff --git a/lib/Makefile b/lib/Makefile index cbb774f7d41d..b73ba01a818a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -110,6 +110,8 @@ obj-$(CONFIG_ATOMIC64_SELFTEST) += atomic64_test.o obj-$(CONFIG_AVERAGE) += average.o +obj-$(CONFIG_CPU_RMAP) += cpu_rmap.o + hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/cpu_rmap.c b/lib/cpu_rmap.c new file mode 100644 index 000000000000..987acfafeb83 --- /dev/null +++ b/lib/cpu_rmap.c @@ -0,0 +1,269 @@ +/* + * cpu_rmap.c: CPU affinity reverse-map support + * Copyright 2011 Solarflare Communications Inc. + * + * 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, incorporated herein by reference. + */ + +#include +#ifdef CONFIG_GENERIC_HARDIRQS +#include +#endif +#include + +/* + * These functions maintain a mapping from CPUs to some ordered set of + * objects with CPU affinities. This can be seen as a reverse-map of + * CPU affinity. However, we do not assume that the object affinities + * cover all CPUs in the system. For those CPUs not directly covered + * by object affinities, we attempt to find a nearest object based on + * CPU topology. + */ + +/** + * alloc_cpu_rmap - allocate CPU affinity reverse-map + * @size: Number of objects to be mapped + * @flags: Allocation flags e.g. %GFP_KERNEL + */ +struct cpu_rmap *alloc_cpu_rmap(unsigned int size, gfp_t flags) +{ + struct cpu_rmap *rmap; + unsigned int cpu; + size_t obj_offset; + + /* This is a silly number of objects, and we use u16 indices. */ + if (size > 0xffff) + return NULL; + + /* Offset of object pointer array from base structure */ + obj_offset = ALIGN(offsetof(struct cpu_rmap, near[nr_cpu_ids]), + sizeof(void *)); + + rmap = kzalloc(obj_offset + size * sizeof(rmap->obj[0]), flags); + if (!rmap) + return NULL; + + rmap->obj = (void **)((char *)rmap + obj_offset); + + /* Initially assign CPUs to objects on a rota, since we have + * no idea where the objects are. Use infinite distance, so + * any object with known distance is preferable. Include the + * CPUs that are not present/online, since we definitely want + * any newly-hotplugged CPUs to have some object assigned. + */ + for_each_possible_cpu(cpu) { + rmap->near[cpu].index = cpu % size; + rmap->near[cpu].dist = CPU_RMAP_DIST_INF; + } + + rmap->size = size; + return rmap; +} +EXPORT_SYMBOL(alloc_cpu_rmap); + +/* Reevaluate nearest object for given CPU, comparing with the given + * neighbours at the given distance. + */ +static bool cpu_rmap_copy_neigh(struct cpu_rmap *rmap, unsigned int cpu, + const struct cpumask *mask, u16 dist) +{ + int neigh; + + for_each_cpu(neigh, mask) { + if (rmap->near[cpu].dist > dist && + rmap->near[neigh].dist <= dist) { + rmap->near[cpu].index = rmap->near[neigh].index; + rmap->near[cpu].dist = dist; + return true; + } + } + return false; +} + +#ifdef DEBUG +static void debug_print_rmap(const struct cpu_rmap *rmap, const char *prefix) +{ + unsigned index; + unsigned int cpu; + + pr_info("cpu_rmap %p, %s:\n", rmap, prefix); + + for_each_possible_cpu(cpu) { + index = rmap->near[cpu].index; + pr_info("cpu %d -> obj %u (distance %u)\n", + cpu, index, rmap->near[cpu].dist); + } +} +#else +static inline void +debug_print_rmap(const struct cpu_rmap *rmap, const char *prefix) +{ +} +#endif + +/** + * cpu_rmap_add - add object to a rmap + * @rmap: CPU rmap allocated with alloc_cpu_rmap() + * @obj: Object to add to rmap + * + * Return index of object. + */ +int cpu_rmap_add(struct cpu_rmap *rmap, void *obj) +{ + u16 index; + + BUG_ON(rmap->used >= rmap->size); + index = rmap->used++; + rmap->obj[index] = obj; + return index; +} +EXPORT_SYMBOL(cpu_rmap_add); + +/** + * cpu_rmap_update - update CPU rmap following a change of object affinity + * @rmap: CPU rmap to update + * @index: Index of object whose affinity changed + * @affinity: New CPU affinity of object + */ +int cpu_rmap_update(struct cpu_rmap *rmap, u16 index, + const struct cpumask *affinity) +{ + cpumask_var_t update_mask; + unsigned int cpu; + + if (unlikely(!zalloc_cpumask_var(&update_mask, GFP_KERNEL))) + return -ENOMEM; + + /* Invalidate distance for all CPUs for which this used to be + * the nearest object. Mark those CPUs for update. + */ + for_each_online_cpu(cpu) { + if (rmap->near[cpu].index == index) { + rmap->near[cpu].dist = CPU_RMAP_DIST_INF; + cpumask_set_cpu(cpu, update_mask); + } + } + + debug_print_rmap(rmap, "after invalidating old distances"); + + /* Set distance to 0 for all CPUs in the new affinity mask. + * Mark all CPUs within their NUMA nodes for update. + */ + for_each_cpu(cpu, affinity) { + rmap->near[cpu].index = index; + rmap->near[cpu].dist = 0; + cpumask_or(update_mask, update_mask, + cpumask_of_node(cpu_to_node(cpu))); + } + + debug_print_rmap(rmap, "after updating neighbours"); + + /* Update distances based on topology */ + for_each_cpu(cpu, update_mask) { + if (cpu_rmap_copy_neigh(rmap, cpu, + topology_thread_cpumask(cpu), 1)) + continue; + if (cpu_rmap_copy_neigh(rmap, cpu, + topology_core_cpumask(cpu), 2)) + continue; + if (cpu_rmap_copy_neigh(rmap, cpu, + cpumask_of_node(cpu_to_node(cpu)), 3)) + continue; + /* We could continue into NUMA node distances, but for now + * we give up. + */ + } + + debug_print_rmap(rmap, "after copying neighbours"); + + free_cpumask_var(update_mask); + return 0; +} +EXPORT_SYMBOL(cpu_rmap_update); + +#ifdef CONFIG_GENERIC_HARDIRQS + +/* Glue between IRQ affinity notifiers and CPU rmaps */ + +struct irq_glue { + struct irq_affinity_notify notify; + struct cpu_rmap *rmap; + u16 index; +}; + +/** + * free_irq_cpu_rmap - free a CPU affinity reverse-map used for IRQs + * @rmap: Reverse-map allocated with alloc_irq_cpu_map(), or %NULL + * + * Must be called in process context, before freeing the IRQs, and + * without holding any locks required by global workqueue items. + */ +void free_irq_cpu_rmap(struct cpu_rmap *rmap) +{ + struct irq_glue *glue; + u16 index; + + if (!rmap) + return; + + for (index = 0; index < rmap->used; index++) { + glue = rmap->obj[index]; + irq_set_affinity_notifier(glue->notify.irq, NULL); + } + irq_run_affinity_notifiers(); + + kfree(rmap); +} +EXPORT_SYMBOL(free_irq_cpu_rmap); + +static void +irq_cpu_rmap_notify(struct irq_affinity_notify *notify, const cpumask_t *mask) +{ + struct irq_glue *glue = + container_of(notify, struct irq_glue, notify); + int rc; + + rc = cpu_rmap_update(glue->rmap, glue->index, mask); + if (rc) + pr_warning("irq_cpu_rmap_notify: update failed: %d\n", rc); +} + +static void irq_cpu_rmap_release(struct kref *ref) +{ + struct irq_glue *glue = + container_of(ref, struct irq_glue, notify.kref); + kfree(glue); +} + +/** + * irq_cpu_rmap_add - add an IRQ to a CPU affinity reverse-map + * @rmap: The reverse-map + * @irq: The IRQ number + * + * This adds an IRQ affinity notifier that will update the reverse-map + * automatically. + * + * Must be called in process context, after the IRQ is allocated but + * before it is bound with request_irq(). + */ +int irq_cpu_rmap_add(struct cpu_rmap *rmap, int irq) +{ + struct irq_glue *glue = kzalloc(sizeof(*glue), GFP_KERNEL); + int rc; + + if (!glue) + return -ENOMEM; + glue->notify.notify = irq_cpu_rmap_notify; + glue->notify.release = irq_cpu_rmap_release; + glue->rmap = rmap; + glue->index = cpu_rmap_add(rmap, glue); + rc = irq_set_affinity_notifier(irq, &glue->notify); + if (rc) + kfree(glue); + return rc; +} +EXPORT_SYMBOL(irq_cpu_rmap_add); + +#endif /* CONFIG_GENERIC_HARDIRQS */ -- cgit v1.2.3 From c445477d74ab3779d1386ab797fbb9b628eb9f64 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 19 Jan 2011 11:03:53 +0000 Subject: net: RPS: Enable hardware acceleration of RFS Allow drivers for multiqueue hardware with flow filter tables to accelerate RFS. The driver must: 1. Set net_device::rx_cpu_rmap to a cpu_rmap of the RX completion IRQs (in queue order). This will provide a mapping from CPUs to the queues for which completions are handled nearest to them. 2. Implement net_device_ops::ndo_rx_flow_steer. This operation adds or replaces a filter steering the given flow to the given RX queue, if possible. 3. Periodically remove filters for which rps_may_expire_flow() returns true. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/netdevice.h | 33 ++++++++++++++-- net/Kconfig | 6 +++ net/core/dev.c | 97 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 127 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 371fa8839d51..a335f2022690 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -554,14 +554,16 @@ struct rps_map { #define RPS_MAP_SIZE(_num) (sizeof(struct rps_map) + (_num * sizeof(u16))) /* - * The rps_dev_flow structure contains the mapping of a flow to a CPU and the - * tail pointer for that CPU's input queue at the time of last enqueue. + * The rps_dev_flow structure contains the mapping of a flow to a CPU, the + * tail pointer for that CPU's input queue at the time of last enqueue, and + * a hardware filter index. */ struct rps_dev_flow { u16 cpu; - u16 fill; + u16 filter; unsigned int last_qtail; }; +#define RPS_NO_FILTER 0xffff /* * The rps_dev_flow_table structure contains a table of flow mappings. @@ -611,6 +613,11 @@ static inline void rps_reset_sock_flow(struct rps_sock_flow_table *table, extern struct rps_sock_flow_table __rcu *rps_sock_flow_table; +#ifdef CONFIG_RFS_ACCEL +extern bool rps_may_expire_flow(struct net_device *dev, u16 rxq_index, + u32 flow_id, u16 filter_id); +#endif + /* This structure contains an instance of an RX queue. */ struct netdev_rx_queue { struct rps_map __rcu *rps_map; @@ -769,6 +776,13 @@ struct netdev_tc_txq { * is always called from the stack with the rtnl lock held and netif tx * queues stopped. This allows the netdevice to perform queue management * safely. + * + * RFS acceleration. + * int (*ndo_rx_flow_steer)(struct net_device *dev, const struct sk_buff *skb, + * u16 rxq_index, u32 flow_id); + * Set hardware filter for RFS. rxq_index is the target queue index; + * flow_id is a flow ID to be passed to rps_may_expire_flow() later. + * Return the filter ID on success, or a negative error code. */ #define HAVE_NET_DEVICE_OPS struct net_device_ops { @@ -842,6 +856,12 @@ struct net_device_ops { int (*ndo_fcoe_get_wwn)(struct net_device *dev, u64 *wwn, int type); #endif +#ifdef CONFIG_RFS_ACCEL + int (*ndo_rx_flow_steer)(struct net_device *dev, + const struct sk_buff *skb, + u16 rxq_index, + u32 flow_id); +#endif }; /* @@ -1056,6 +1076,13 @@ struct net_device { /* Number of RX queues currently active in device */ unsigned int real_num_rx_queues; + +#ifdef CONFIG_RFS_ACCEL + /* CPU reverse-mapping for RX completion interrupts, indexed + * by RX queue number. Assigned by driver. This must only be + * set if the ndo_rx_flow_steer operation is defined. */ + struct cpu_rmap *rx_cpu_rmap; +#endif #endif rx_handler_func_t __rcu *rx_handler; diff --git a/net/Kconfig b/net/Kconfig index 72840626284b..79cabf1ee68b 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -221,6 +221,12 @@ config RPS depends on SMP && SYSFS && USE_GENERIC_SMP_HELPERS default y +config RFS_ACCEL + boolean + depends on RPS && GENERIC_HARDIRQS + select CPU_RMAP + default y + config XPS boolean depends on SMP && SYSFS && USE_GENERIC_SMP_HELPERS diff --git a/net/core/dev.c b/net/core/dev.c index d162ba8d622d..aa761472f9e2 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -132,6 +132,7 @@ #include #include #include +#include #include "net-sysfs.h" @@ -2588,6 +2589,53 @@ EXPORT_SYMBOL(__skb_get_rxhash); struct rps_sock_flow_table __rcu *rps_sock_flow_table __read_mostly; EXPORT_SYMBOL(rps_sock_flow_table); +static struct rps_dev_flow * +set_rps_cpu(struct net_device *dev, struct sk_buff *skb, + struct rps_dev_flow *rflow, u16 next_cpu) +{ + u16 tcpu; + + tcpu = rflow->cpu = next_cpu; + if (tcpu != RPS_NO_CPU) { +#ifdef CONFIG_RFS_ACCEL + struct netdev_rx_queue *rxqueue; + struct rps_dev_flow_table *flow_table; + struct rps_dev_flow *old_rflow; + u32 flow_id; + u16 rxq_index; + int rc; + + /* Should we steer this flow to a different hardware queue? */ + if (!skb_rx_queue_recorded(skb) || !dev->rx_cpu_rmap) + goto out; + rxq_index = cpu_rmap_lookup_index(dev->rx_cpu_rmap, next_cpu); + if (rxq_index == skb_get_rx_queue(skb)) + goto out; + + rxqueue = dev->_rx + rxq_index; + flow_table = rcu_dereference(rxqueue->rps_flow_table); + if (!flow_table) + goto out; + flow_id = skb->rxhash & flow_table->mask; + rc = dev->netdev_ops->ndo_rx_flow_steer(dev, skb, + rxq_index, flow_id); + if (rc < 0) + goto out; + old_rflow = rflow; + rflow = &flow_table->flows[flow_id]; + rflow->cpu = next_cpu; + rflow->filter = rc; + if (old_rflow->filter == rflow->filter) + old_rflow->filter = RPS_NO_FILTER; + out: +#endif + rflow->last_qtail = + per_cpu(softnet_data, tcpu).input_queue_head; + } + + return rflow; +} + /* * get_rps_cpu is called from netif_receive_skb and returns the target * CPU from the RPS map of the receiving queue for a given skb. @@ -2658,12 +2706,9 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb, if (unlikely(tcpu != next_cpu) && (tcpu == RPS_NO_CPU || !cpu_online(tcpu) || ((int)(per_cpu(softnet_data, tcpu).input_queue_head - - rflow->last_qtail)) >= 0)) { - tcpu = rflow->cpu = next_cpu; - if (tcpu != RPS_NO_CPU) - rflow->last_qtail = per_cpu(softnet_data, - tcpu).input_queue_head; - } + rflow->last_qtail)) >= 0)) + rflow = set_rps_cpu(dev, skb, rflow, next_cpu); + if (tcpu != RPS_NO_CPU && cpu_online(tcpu)) { *rflowp = rflow; cpu = tcpu; @@ -2684,6 +2729,46 @@ done: return cpu; } +#ifdef CONFIG_RFS_ACCEL + +/** + * rps_may_expire_flow - check whether an RFS hardware filter may be removed + * @dev: Device on which the filter was set + * @rxq_index: RX queue index + * @flow_id: Flow ID passed to ndo_rx_flow_steer() + * @filter_id: Filter ID returned by ndo_rx_flow_steer() + * + * Drivers that implement ndo_rx_flow_steer() should periodically call + * this function for each installed filter and remove the filters for + * which it returns %true. + */ +bool rps_may_expire_flow(struct net_device *dev, u16 rxq_index, + u32 flow_id, u16 filter_id) +{ + struct netdev_rx_queue *rxqueue = dev->_rx + rxq_index; + struct rps_dev_flow_table *flow_table; + struct rps_dev_flow *rflow; + bool expire = true; + int cpu; + + rcu_read_lock(); + flow_table = rcu_dereference(rxqueue->rps_flow_table); + if (flow_table && flow_id <= flow_table->mask) { + rflow = &flow_table->flows[flow_id]; + cpu = ACCESS_ONCE(rflow->cpu); + if (rflow->filter == filter_id && cpu != RPS_NO_CPU && + ((int)(per_cpu(softnet_data, cpu).input_queue_head - + rflow->last_qtail) < + (int)(10 * flow_table->mask))) + expire = false; + } + rcu_read_unlock(); + return expire; +} +EXPORT_SYMBOL(rps_may_expire_flow); + +#endif /* CONFIG_RFS_ACCEL */ + /* Called from hardirq (IPI) context */ static void rps_trigger_softirq(void *data) { -- cgit v1.2.3 From 04ed3e741d0f133e02bed7fa5c98edba128f90e7 Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Mon, 24 Jan 2011 15:32:47 -0800 Subject: net: change netdev->features to u32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quoting Ben Hutchings: we presumably won't be defining features that can only be enabled on 64-bit architectures. Occurences found by `grep -r` on net/, drivers/net, include/ [ Move features and vlan_features next to each other in struct netdev, as per Eric Dumazet's suggestion -DaveM ] Signed-off-by: Michał Mirosław Signed-off-by: David S. Miller --- drivers/net/bnx2.c | 2 +- drivers/net/bonding/bond_main.c | 4 ++-- drivers/net/myri10ge/myri10ge.c | 4 ++-- drivers/net/sfc/ethtool.c | 4 ++-- drivers/net/sfc/net_driver.h | 2 +- drivers/net/tun.c | 2 +- include/linux/netdevice.h | 24 ++++++++++++------------ include/linux/skbuff.h | 2 +- include/net/protocol.h | 4 ++-- include/net/tcp.h | 2 +- include/net/udp.h | 2 +- net/8021q/vlan.c | 2 +- net/bridge/br_if.c | 2 +- net/bridge/br_private.h | 2 +- net/core/dev.c | 15 +++++++-------- net/core/ethtool.c | 2 +- net/core/net-sysfs.c | 2 +- net/core/skbuff.c | 4 ++-- net/ipv4/af_inet.c | 2 +- net/ipv4/tcp.c | 2 +- net/ipv4/udp.c | 2 +- net/ipv6/af_inet6.c | 2 +- net/ipv6/udp.c | 2 +- 23 files changed, 45 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index df99edf3464a..cab96fa4cd3a 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -8312,7 +8312,7 @@ static const struct net_device_ops bnx2_netdev_ops = { #endif }; -static void inline vlan_features_add(struct net_device *dev, unsigned long flags) +static void inline vlan_features_add(struct net_device *dev, u32 flags) { dev->vlan_features |= flags; } diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 163e0b06eaa5..7047b406b8ba 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1372,8 +1372,8 @@ static int bond_compute_features(struct bonding *bond) { struct slave *slave; struct net_device *bond_dev = bond->dev; - unsigned long features = bond_dev->features; - unsigned long vlan_features = 0; + u32 features = bond_dev->features; + u32 vlan_features = 0; unsigned short max_hard_header_len = max((u16)ETH_HLEN, bond_dev->hard_header_len); int i; diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index ea5cfe2c3a04..a7f2eed9a08a 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -253,7 +253,7 @@ struct myri10ge_priv { unsigned long serial_number; int vendor_specific_offset; int fw_multicast_support; - unsigned long features; + u32 features; u32 max_tso6; u32 read_dma; u32 write_dma; @@ -1776,7 +1776,7 @@ static int myri10ge_set_rx_csum(struct net_device *netdev, u32 csum_enabled) static int myri10ge_set_tso(struct net_device *netdev, u32 tso_enabled) { struct myri10ge_priv *mgp = netdev_priv(netdev); - unsigned long flags = mgp->features & (NETIF_F_TSO6 | NETIF_F_TSO); + u32 flags = mgp->features & (NETIF_F_TSO6 | NETIF_F_TSO); if (tso_enabled) netdev->features |= flags; diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index 0e8bb19ed60d..713969accdbd 100644 --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c @@ -502,7 +502,7 @@ static void efx_ethtool_get_stats(struct net_device *net_dev, static int efx_ethtool_set_tso(struct net_device *net_dev, u32 enable) { struct efx_nic *efx __attribute__ ((unused)) = netdev_priv(net_dev); - unsigned long features; + u32 features; features = NETIF_F_TSO; if (efx->type->offload_features & NETIF_F_V6_CSUM) @@ -519,7 +519,7 @@ static int efx_ethtool_set_tso(struct net_device *net_dev, u32 enable) static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable) { struct efx_nic *efx = netdev_priv(net_dev); - unsigned long features = efx->type->offload_features & NETIF_F_ALL_CSUM; + u32 features = efx->type->offload_features & NETIF_F_ALL_CSUM; if (enable) net_dev->features |= features; diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index 28df8665256a..c65270241d2d 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h @@ -906,7 +906,7 @@ struct efx_nic_type { unsigned int phys_addr_channels; unsigned int tx_dc_base; unsigned int rx_dc_base; - unsigned long offload_features; + u32 offload_features; u32 reset_world_flags; }; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index b100bd50a0d7..55786a0efc41 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1142,7 +1142,7 @@ static int tun_get_iff(struct net *net, struct tun_struct *tun, * privs required. */ static int set_offload(struct net_device *dev, unsigned long arg) { - unsigned int old_features, features; + u32 old_features, features; old_features = dev->features; /* Unset features, set them as we chew on the arg. */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a335f2022690..0de3c59720fa 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -914,7 +914,11 @@ struct net_device { struct list_head unreg_list; /* Net device features */ - unsigned long features; + u32 features; + + /* VLAN feature mask */ + u32 vlan_features; + #define NETIF_F_SG 1 /* Scatter/gather IO. */ #define NETIF_F_IP_CSUM 2 /* Can checksum TCP/UDP over IPv4. */ #define NETIF_F_NO_CSUM 4 /* Does not require checksum. F.e. loopack. */ @@ -1176,9 +1180,6 @@ struct net_device { /* rtnetlink link ops */ const struct rtnl_link_ops *rtnl_link_ops; - /* VLAN feature mask */ - unsigned long vlan_features; - /* for setting kernel sock attribute on TCP connection setup */ #define GSO_MAX_SIZE 65536 unsigned int gso_max_size; @@ -1401,7 +1402,7 @@ struct packet_type { struct packet_type *, struct net_device *); struct sk_buff *(*gso_segment)(struct sk_buff *skb, - int features); + u32 features); int (*gso_send_check)(struct sk_buff *skb); struct sk_buff **(*gro_receive)(struct sk_buff **head, struct sk_buff *skb); @@ -2370,7 +2371,7 @@ extern int netdev_tstamp_prequeue; extern int weight_p; extern int netdev_set_master(struct net_device *dev, struct net_device *master); extern int skb_checksum_help(struct sk_buff *skb); -extern struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features); +extern struct sk_buff *skb_gso_segment(struct sk_buff *skb, u32 features); #ifdef CONFIG_BUG extern void netdev_rx_csum_fault(struct net_device *dev); #else @@ -2397,22 +2398,21 @@ extern char *netdev_drivername(const struct net_device *dev, char *buffer, int l extern void linkwatch_run_queue(void); -unsigned long netdev_increment_features(unsigned long all, unsigned long one, - unsigned long mask); -unsigned long netdev_fix_features(unsigned long features, const char *name); +u32 netdev_increment_features(u32 all, u32 one, u32 mask); +u32 netdev_fix_features(u32 features, const char *name); void netif_stacked_transfer_operstate(const struct net_device *rootdev, struct net_device *dev); -int netif_skb_features(struct sk_buff *skb); +u32 netif_skb_features(struct sk_buff *skb); -static inline int net_gso_ok(int features, int gso_type) +static inline int net_gso_ok(u32 features, int gso_type) { int feature = gso_type << NETIF_F_GSO_SHIFT; return (features & feature) == feature; } -static inline int skb_gso_ok(struct sk_buff *skb, int features) +static inline int skb_gso_ok(struct sk_buff *skb, u32 features) { return net_gso_ok(features, skb_shinfo(skb)->gso_type) && (!skb_has_frag_list(skb) || (features & NETIF_F_FRAGLIST)); diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 6e946da9d1d6..31f02d0b46a7 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1877,7 +1877,7 @@ extern void skb_split(struct sk_buff *skb, extern int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen); -extern struct sk_buff *skb_segment(struct sk_buff *skb, int features); +extern struct sk_buff *skb_segment(struct sk_buff *skb, u32 features); static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, int len, void *buffer) diff --git a/include/net/protocol.h b/include/net/protocol.h index dc07495bce4c..6f7eb800974a 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -38,7 +38,7 @@ struct net_protocol { void (*err_handler)(struct sk_buff *skb, u32 info); int (*gso_send_check)(struct sk_buff *skb); struct sk_buff *(*gso_segment)(struct sk_buff *skb, - int features); + u32 features); struct sk_buff **(*gro_receive)(struct sk_buff **head, struct sk_buff *skb); int (*gro_complete)(struct sk_buff *skb); @@ -57,7 +57,7 @@ struct inet6_protocol { int (*gso_send_check)(struct sk_buff *skb); struct sk_buff *(*gso_segment)(struct sk_buff *skb, - int features); + u32 features); struct sk_buff **(*gro_receive)(struct sk_buff **head, struct sk_buff *skb); int (*gro_complete)(struct sk_buff *skb); diff --git a/include/net/tcp.h b/include/net/tcp.h index 38509f047382..917911165e3b 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1404,7 +1404,7 @@ extern struct request_sock_ops tcp6_request_sock_ops; extern void tcp_v4_destroy_sock(struct sock *sk); extern int tcp_v4_gso_send_check(struct sk_buff *skb); -extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features); +extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, u32 features); extern struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb); extern struct sk_buff **tcp4_gro_receive(struct sk_buff **head, diff --git a/include/net/udp.h b/include/net/udp.h index bb967dd59bf7..e82f3a8c0f8f 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -245,5 +245,5 @@ extern void udp4_proc_exit(void); extern void udp_init(void); extern int udp4_ufo_send_check(struct sk_buff *skb); -extern struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, int features); +extern struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, u32 features); #endif /* _UDP_H */ diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 6e64f7c6a2e9..7850412f52b7 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -327,7 +327,7 @@ static void vlan_sync_address(struct net_device *dev, static void vlan_transfer_features(struct net_device *dev, struct net_device *vlandev) { - unsigned long old_features = vlandev->features; + u32 old_features = vlandev->features; vlandev->features &= ~dev->vlan_features; vlandev->features |= dev->features & dev->vlan_features; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index d9d1e2bac1d6..52ce4a30f8b3 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -365,7 +365,7 @@ int br_min_mtu(const struct net_bridge *br) void br_features_recompute(struct net_bridge *br) { struct net_bridge_port *p; - unsigned long features, mask; + u32 features, mask; features = mask = br->feature_mask; if (list_empty(&br->port_list)) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 84aac7734bfc..9f22898c5359 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -182,7 +182,7 @@ struct net_bridge struct br_cpu_netstats __percpu *stats; spinlock_t hash_lock; struct hlist_head hash[BR_HASH_SIZE]; - unsigned long feature_mask; + u32 feature_mask; #ifdef CONFIG_BRIDGE_NETFILTER struct rtable fake_rtable; bool nf_call_iptables; diff --git a/net/core/dev.c b/net/core/dev.c index ad3741898584..7103f89fde0c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1858,7 +1858,7 @@ EXPORT_SYMBOL(skb_checksum_help); * It may return NULL if the skb requires no segmentation. This is * only possible when GSO is used for verifying header integrity. */ -struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features) +struct sk_buff *skb_gso_segment(struct sk_buff *skb, u32 features) { struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); struct packet_type *ptype; @@ -2046,7 +2046,7 @@ static bool can_checksum_protocol(unsigned long features, __be16 protocol) protocol == htons(ETH_P_FCOE))); } -static int harmonize_features(struct sk_buff *skb, __be16 protocol, int features) +static u32 harmonize_features(struct sk_buff *skb, __be16 protocol, u32 features) { if (!can_checksum_protocol(features, protocol)) { features &= ~NETIF_F_ALL_CSUM; @@ -2058,10 +2058,10 @@ static int harmonize_features(struct sk_buff *skb, __be16 protocol, int features return features; } -int netif_skb_features(struct sk_buff *skb) +u32 netif_skb_features(struct sk_buff *skb) { __be16 protocol = skb->protocol; - int features = skb->dev->features; + u32 features = skb->dev->features; if (protocol == htons(ETH_P_8021Q)) { struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; @@ -2106,7 +2106,7 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, int rc = NETDEV_TX_OK; if (likely(!skb->next)) { - int features; + u32 features; /* * If device doesnt need skb->dst, release it right now while @@ -5213,7 +5213,7 @@ static void rollback_registered(struct net_device *dev) rollback_registered_many(&single); } -unsigned long netdev_fix_features(unsigned long features, const char *name) +u32 netdev_fix_features(u32 features, const char *name) { /* Fix illegal checksum combinations */ if ((features & NETIF_F_HW_CSUM) && @@ -6143,8 +6143,7 @@ static int dev_cpu_callback(struct notifier_block *nfb, * @one to the master device with current feature set @all. Will not * enable anything that is off in @mask. Returns the new feature set. */ -unsigned long netdev_increment_features(unsigned long all, unsigned long one, - unsigned long mask) +u32 netdev_increment_features(u32 all, u32 one, u32 mask) { /* If device needs checksumming, downgrade to it. */ if (all & NETIF_F_NO_CSUM && !(one & NETIF_F_NO_CSUM)) diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 17741782a345..bd1af99e1122 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -1458,7 +1458,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) void __user *useraddr = ifr->ifr_data; u32 ethcmd; int rc; - unsigned long old_features; + u32 old_features; if (!dev || !netif_device_present(dev)) return -ENODEV; diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index e23c01be5a5b..81367ccf3306 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -99,7 +99,7 @@ NETDEVICE_SHOW(addr_assign_type, fmt_dec); NETDEVICE_SHOW(addr_len, fmt_dec); NETDEVICE_SHOW(iflink, fmt_dec); NETDEVICE_SHOW(ifindex, fmt_dec); -NETDEVICE_SHOW(features, fmt_long_hex); +NETDEVICE_SHOW(features, fmt_hex); NETDEVICE_SHOW(type, fmt_dec); NETDEVICE_SHOW(link_mode, fmt_dec); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index d31bb36ae0dc..436c4c439240 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2497,7 +2497,7 @@ EXPORT_SYMBOL_GPL(skb_pull_rcsum); * a pointer to the first in a list of new skbs for the segments. * In case of error it returns ERR_PTR(err). */ -struct sk_buff *skb_segment(struct sk_buff *skb, int features) +struct sk_buff *skb_segment(struct sk_buff *skb, u32 features) { struct sk_buff *segs = NULL; struct sk_buff *tail = NULL; @@ -2507,7 +2507,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features) unsigned int offset = doffset; unsigned int headroom; unsigned int len; - int sg = features & NETIF_F_SG; + int sg = !!(features & NETIF_F_SG); int nfrags = skb_shinfo(skb)->nr_frags; int err = -ENOMEM; int i = 0; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index f2b61107df6c..e5e2d9d64abb 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1215,7 +1215,7 @@ out: return err; } -static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features) +static struct sk_buff *inet_gso_segment(struct sk_buff *skb, u32 features) { struct sk_buff *segs = ERR_PTR(-EINVAL); struct iphdr *iph; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 6c11eece262c..f9867d2dbef4 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2653,7 +2653,7 @@ int compat_tcp_getsockopt(struct sock *sk, int level, int optname, EXPORT_SYMBOL(compat_tcp_getsockopt); #endif -struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features) +struct sk_buff *tcp_tso_segment(struct sk_buff *skb, u32 features) { struct sk_buff *segs = ERR_PTR(-EINVAL); struct tcphdr *th; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 8157b17959ee..d37baaa1dbe3 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2199,7 +2199,7 @@ int udp4_ufo_send_check(struct sk_buff *skb) return 0; } -struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, int features) +struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, u32 features) { struct sk_buff *segs = ERR_PTR(-EINVAL); unsigned int mss; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 978e80e2c4a8..3194aa909872 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -772,7 +772,7 @@ out: return err; } -static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) +static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, u32 features) { struct sk_buff *segs = ERR_PTR(-EINVAL); struct ipv6hdr *ipv6h; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 9a009c66c8a3..a419a787eb69 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1299,7 +1299,7 @@ static int udp6_ufo_send_check(struct sk_buff *skb) return 0; } -static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, int features) +static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, u32 features) { struct sk_buff *segs = ERR_PTR(-EINVAL); unsigned int mss; -- cgit v1.2.3 From acd1130e8793fb150fb522da8ec51675839eb4b1 Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Mon, 24 Jan 2011 15:45:15 -0800 Subject: net: reduce and unify printk level in netdev_fix_features() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce printk() levels to KERN_INFO in netdev_fix_features() as this will be used by ethtool and might spam dmesg unnecessarily. This converts the function to use netdev_info() instead of plain printk(). As a side effect, bonding and bridge devices will now log dropped features on every slave device change. Signed-off-by: Michał Mirosław Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 4 ++-- include/linux/netdevice.h | 2 +- net/bridge/br_if.c | 2 +- net/core/dev.c | 33 ++++++++++++--------------------- 4 files changed, 16 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 7047b406b8ba..1df9f0ea9184 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1400,8 +1400,8 @@ static int bond_compute_features(struct bonding *bond) done: features |= (bond_dev->features & BOND_VLAN_FEATURES); - bond_dev->features = netdev_fix_features(features, NULL); - bond_dev->vlan_features = netdev_fix_features(vlan_features, NULL); + bond_dev->features = netdev_fix_features(bond_dev, features); + bond_dev->vlan_features = netdev_fix_features(bond_dev, vlan_features); bond_dev->hard_header_len = max_hard_header_len; return 0; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0de3c59720fa..8858422c5c5d 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2399,7 +2399,7 @@ extern char *netdev_drivername(const struct net_device *dev, char *buffer, int l extern void linkwatch_run_queue(void); u32 netdev_increment_features(u32 all, u32 one, u32 mask); -u32 netdev_fix_features(u32 features, const char *name); +u32 netdev_fix_features(struct net_device *dev, u32 features); void netif_stacked_transfer_operstate(const struct net_device *rootdev, struct net_device *dev); diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 52ce4a30f8b3..2a6801d8b728 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -379,7 +379,7 @@ void br_features_recompute(struct net_bridge *br) } done: - br->dev->features = netdev_fix_features(features, NULL); + br->dev->features = netdev_fix_features(br->dev, features); } /* called with RTNL */ diff --git a/net/core/dev.c b/net/core/dev.c index 7103f89fde0c..1b4c07fe295f 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5213,58 +5213,49 @@ static void rollback_registered(struct net_device *dev) rollback_registered_many(&single); } -u32 netdev_fix_features(u32 features, const char *name) +u32 netdev_fix_features(struct net_device *dev, u32 features) { /* Fix illegal checksum combinations */ if ((features & NETIF_F_HW_CSUM) && (features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) { - if (name) - printk(KERN_NOTICE "%s: mixed HW and IP checksum settings.\n", - name); + netdev_info(dev, "mixed HW and IP checksum settings.\n"); features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM); } if ((features & NETIF_F_NO_CSUM) && (features & (NETIF_F_HW_CSUM|NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) { - if (name) - printk(KERN_NOTICE "%s: mixed no checksumming and other settings.\n", - name); + netdev_info(dev, "mixed no checksumming and other settings.\n"); features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM|NETIF_F_HW_CSUM); } /* Fix illegal SG+CSUM combinations. */ if ((features & NETIF_F_SG) && !(features & NETIF_F_ALL_CSUM)) { - if (name) - printk(KERN_NOTICE "%s: Dropping NETIF_F_SG since no " - "checksum feature.\n", name); + netdev_info(dev, + "Dropping NETIF_F_SG since no checksum feature.\n"); features &= ~NETIF_F_SG; } /* TSO requires that SG is present as well. */ if ((features & NETIF_F_TSO) && !(features & NETIF_F_SG)) { - if (name) - printk(KERN_NOTICE "%s: Dropping NETIF_F_TSO since no " - "SG feature.\n", name); + netdev_info(dev, "Dropping NETIF_F_TSO since no SG feature.\n"); features &= ~NETIF_F_TSO; } + /* UFO needs SG and checksumming */ if (features & NETIF_F_UFO) { /* maybe split UFO into V4 and V6? */ if (!((features & NETIF_F_GEN_CSUM) || (features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM)) == (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) { - if (name) - printk(KERN_ERR "%s: Dropping NETIF_F_UFO " - "since no checksum offload features.\n", - name); + netdev_info(dev, + "Dropping NETIF_F_UFO since no checksum offload features.\n"); features &= ~NETIF_F_UFO; } if (!(features & NETIF_F_SG)) { - if (name) - printk(KERN_ERR "%s: Dropping NETIF_F_UFO " - "since no NETIF_F_SG feature.\n", name); + netdev_info(dev, + "Dropping NETIF_F_UFO since no NETIF_F_SG feature.\n"); features &= ~NETIF_F_UFO; } } @@ -5407,7 +5398,7 @@ int register_netdevice(struct net_device *dev) if (dev->iflink == -1) dev->iflink = dev->ifindex; - dev->features = netdev_fix_features(dev->features, dev->name); + dev->features = netdev_fix_features(dev, dev->features); /* Enable software GSO if SG is supported. */ if (dev->features & NETIF_F_SG) -- cgit v1.2.3 From 414b4ff5eecff0097d09c4a7da12e435fd503692 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 25 Jan 2011 12:43:49 +0100 Subject: block: add REQ_FLUSH_SEQ rq == &q->flush_rq was used to determine whether a rq is part of a flush sequence, which worked because all requests in a flush sequence were sequenced using the single dedicated request. This is about to change, so introduce REQ_FLUSH_SEQ flag to distinguish flush sequence requests. This patch doesn't cause any behavior change. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/blk-core.c | 4 ++-- block/blk-flush.c | 1 + block/blk.h | 2 +- include/linux/blk_types.h | 2 ++ 4 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 4ce953f1b390..fc7d8ad76f44 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -136,7 +136,7 @@ static void req_bio_endio(struct request *rq, struct bio *bio, { struct request_queue *q = rq->q; - if (&q->flush_rq != rq) { + if (!(rq->cmd_flags & REQ_FLUSH_SEQ)) { if (error) clear_bit(BIO_UPTODATE, &bio->bi_flags); else if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) @@ -1789,7 +1789,7 @@ static void blk_account_io_done(struct request *req) * normal IO on queueing nor completion. Accounting the * containing request is enough. */ - if (blk_do_io_stat(req) && req != &req->q->flush_rq) { + if (blk_do_io_stat(req) && !(req->cmd_flags & REQ_FLUSH_SEQ)) { unsigned long duration = jiffies - req->start_time; const int rw = rq_data_dir(req); struct hd_struct *part; diff --git a/block/blk-flush.c b/block/blk-flush.c index 54b123d6563e..8592869bcbe7 100644 --- a/block/blk-flush.c +++ b/block/blk-flush.c @@ -130,6 +130,7 @@ static struct request *queue_next_fseq(struct request_queue *q) BUG(); } + rq->cmd_flags |= REQ_FLUSH_SEQ; elv_insert(q, rq, ELEVATOR_INSERT_FRONT); return rq; } diff --git a/block/blk.h b/block/blk.h index 2db8f32838e7..9d2ee8f4d9af 100644 --- a/block/blk.h +++ b/block/blk.h @@ -61,7 +61,7 @@ static inline struct request *__elv_next_request(struct request_queue *q) while (!list_empty(&q->queue_head)) { rq = list_entry_rq(q->queue_head.next); if (!(rq->cmd_flags & (REQ_FLUSH | REQ_FUA)) || - rq == &q->flush_rq) + (rq->cmd_flags & REQ_FLUSH_SEQ)) return rq; rq = blk_do_flush(q, rq); if (rq) diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 46ad5197537a..dddedfc0af81 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -148,6 +148,7 @@ enum rq_flag_bits { __REQ_ALLOCED, /* request came from our alloc pool */ __REQ_COPY_USER, /* contains copies of user pages */ __REQ_FLUSH, /* request for cache flush */ + __REQ_FLUSH_SEQ, /* request for flush sequence */ __REQ_IO_STAT, /* account I/O stat */ __REQ_MIXED_MERGE, /* merge of different types, fail separately */ __REQ_SECURE, /* secure discard (used with __REQ_DISCARD) */ @@ -188,6 +189,7 @@ enum rq_flag_bits { #define REQ_ALLOCED (1 << __REQ_ALLOCED) #define REQ_COPY_USER (1 << __REQ_COPY_USER) #define REQ_FLUSH (1 << __REQ_FLUSH) +#define REQ_FLUSH_SEQ (1 << __REQ_FLUSH_SEQ) #define REQ_IO_STAT (1 << __REQ_IO_STAT) #define REQ_MIXED_MERGE (1 << __REQ_MIXED_MERGE) #define REQ_SECURE (1 << __REQ_SECURE) -- cgit v1.2.3 From ae1b1539622fb46e51b4d13b3f9e5f4c713f86ae Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 25 Jan 2011 12:43:54 +0100 Subject: block: reimplement FLUSH/FUA to support merge The current FLUSH/FUA support has evolved from the implementation which had to perform queue draining. As such, sequencing is done queue-wide one flush request after another. However, with the draining requirement gone, there's no reason to keep the queue-wide sequential approach. This patch reimplements FLUSH/FUA support such that each FLUSH/FUA request is sequenced individually. The actual FLUSH execution is double buffered and whenever a request wants to execute one for either PRE or POSTFLUSH, it queues on the pending queue. Once certain conditions are met, a flush request is issued and on its completion all pending requests proceed to the next sequence. This allows arbitrary merging of different type of flushes. How they are merged can be primarily controlled and tuned by adjusting the above said 'conditions' used to determine when to issue the next flush. This is inspired by Darrick's patches to merge multiple zero-data flushes which helps workloads with highly concurrent fsync requests. * As flush requests are never put on the IO scheduler, request fields used for flush share space with rq->rb_node. rq->completion_data is moved out of the union. This increases the request size by one pointer. As rq->elevator_private* are used only by the iosched too, it is possible to reduce the request size further. However, to do that, we need to modify request allocation path such that iosched data is not allocated for flush requests. * FLUSH/FUA processing happens on insertion now instead of dispatch. - Comments updated as per Vivek and Mike. Signed-off-by: Tejun Heo Cc: "Darrick J. Wong" Cc: Shaohua Li Cc: Christoph Hellwig Cc: Vivek Goyal Cc: Mike Snitzer Signed-off-by: Jens Axboe --- block/blk-core.c | 10 +- block/blk-flush.c | 440 ++++++++++++++++++++++++++++++++--------------- block/blk.h | 12 +- block/elevator.c | 7 + include/linux/blkdev.h | 18 +- include/linux/elevator.h | 1 + 6 files changed, 332 insertions(+), 156 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 617bb9e40927..05746093b45e 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -134,8 +134,6 @@ EXPORT_SYMBOL(blk_rq_init); static void req_bio_endio(struct request *rq, struct bio *bio, unsigned int nbytes, int error) { - struct request_queue *q = rq->q; - if (error) clear_bit(BIO_UPTODATE, &bio->bi_flags); else if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) @@ -159,8 +157,6 @@ static void req_bio_endio(struct request *rq, struct bio *bio, /* don't actually finish bio if it's part of flush sequence */ if (bio->bi_size == 0 && !(rq->cmd_flags & REQ_FLUSH_SEQ)) bio_endio(bio, error); - else if (error && !q->flush_err) - q->flush_err = error; } void blk_dump_rq_flags(struct request *rq, char *msg) @@ -519,7 +515,9 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) init_timer(&q->unplug_timer); setup_timer(&q->timeout, blk_rq_timed_out_timer, (unsigned long) q); INIT_LIST_HEAD(&q->timeout_list); - INIT_LIST_HEAD(&q->pending_flushes); + INIT_LIST_HEAD(&q->flush_queue[0]); + INIT_LIST_HEAD(&q->flush_queue[1]); + INIT_LIST_HEAD(&q->flush_data_in_flight); INIT_WORK(&q->unplug_work, blk_unplug_work); kobject_init(&q->kobj, &blk_queue_ktype); @@ -1198,7 +1196,7 @@ static int __make_request(struct request_queue *q, struct bio *bio) spin_lock_irq(q->queue_lock); if (bio->bi_rw & (REQ_FLUSH | REQ_FUA)) { - where = ELEVATOR_INSERT_FRONT; + where = ELEVATOR_INSERT_FLUSH; goto get_rq; } diff --git a/block/blk-flush.c b/block/blk-flush.c index 8592869bcbe7..a867e3f524f3 100644 --- a/block/blk-flush.c +++ b/block/blk-flush.c @@ -1,6 +1,69 @@ /* * Functions to sequence FLUSH and FUA writes. + * + * Copyright (C) 2011 Max Planck Institute for Gravitational Physics + * Copyright (C) 2011 Tejun Heo + * + * This file is released under the GPLv2. + * + * REQ_{FLUSH|FUA} requests are decomposed to sequences consisted of three + * optional steps - PREFLUSH, DATA and POSTFLUSH - according to the request + * properties and hardware capability. + * + * If a request doesn't have data, only REQ_FLUSH makes sense, which + * indicates a simple flush request. If there is data, REQ_FLUSH indicates + * that the device cache should be flushed before the data is executed, and + * REQ_FUA means that the data must be on non-volatile media on request + * completion. + * + * If the device doesn't have writeback cache, FLUSH and FUA don't make any + * difference. The requests are either completed immediately if there's no + * data or executed as normal requests otherwise. + * + * If the device has writeback cache and supports FUA, REQ_FLUSH is + * translated to PREFLUSH but REQ_FUA is passed down directly with DATA. + * + * If the device has writeback cache and doesn't support FUA, REQ_FLUSH is + * translated to PREFLUSH and REQ_FUA to POSTFLUSH. + * + * The actual execution of flush is double buffered. Whenever a request + * needs to execute PRE or POSTFLUSH, it queues at + * q->flush_queue[q->flush_pending_idx]. Once certain criteria are met, a + * flush is issued and the pending_idx is toggled. When the flush + * completes, all the requests which were pending are proceeded to the next + * step. This allows arbitrary merging of different types of FLUSH/FUA + * requests. + * + * Currently, the following conditions are used to determine when to issue + * flush. + * + * C1. At any given time, only one flush shall be in progress. This makes + * double buffering sufficient. + * + * C2. Flush is deferred if any request is executing DATA of its sequence. + * This avoids issuing separate POSTFLUSHes for requests which shared + * PREFLUSH. + * + * C3. The second condition is ignored if there is a request which has + * waited longer than FLUSH_PENDING_TIMEOUT. This is to avoid + * starvation in the unlikely case where there are continuous stream of + * FUA (without FLUSH) requests. + * + * For devices which support FUA, it isn't clear whether C2 (and thus C3) + * is beneficial. + * + * Note that a sequenced FLUSH/FUA request with DATA is completed twice. + * Once while executing DATA and again after the whole sequence is + * complete. The first completion updates the contained bio but doesn't + * finish it so that the bio submitter is notified only after the whole + * sequence is complete. This is implemented by testing REQ_FLUSH_SEQ in + * req_bio_endio(). + * + * The above peculiarity requires that each FLUSH/FUA request has only one + * bio attached to it, which is guaranteed as they aren't allowed to be + * merged in the usual way. */ + #include #include #include @@ -11,185 +74,290 @@ /* FLUSH/FUA sequences */ enum { - QUEUE_FSEQ_STARTED = (1 << 0), /* flushing in progress */ - QUEUE_FSEQ_PREFLUSH = (1 << 1), /* pre-flushing in progress */ - QUEUE_FSEQ_DATA = (1 << 2), /* data write in progress */ - QUEUE_FSEQ_POSTFLUSH = (1 << 3), /* post-flushing in progress */ - QUEUE_FSEQ_DONE = (1 << 4), + REQ_FSEQ_PREFLUSH = (1 << 0), /* pre-flushing in progress */ + REQ_FSEQ_DATA = (1 << 1), /* data write in progress */ + REQ_FSEQ_POSTFLUSH = (1 << 2), /* post-flushing in progress */ + REQ_FSEQ_DONE = (1 << 3), + + REQ_FSEQ_ACTIONS = REQ_FSEQ_PREFLUSH | REQ_FSEQ_DATA | + REQ_FSEQ_POSTFLUSH, + + /* + * If flush has been pending longer than the following timeout, + * it's issued even if flush_data requests are still in flight. + */ + FLUSH_PENDING_TIMEOUT = 5 * HZ, }; -static struct request *queue_next_fseq(struct request_queue *q); +static bool blk_kick_flush(struct request_queue *q); -unsigned blk_flush_cur_seq(struct request_queue *q) +static unsigned int blk_flush_policy(unsigned int fflags, struct request *rq) { - if (!q->flush_seq) - return 0; - return 1 << ffz(q->flush_seq); + unsigned int policy = 0; + + if (fflags & REQ_FLUSH) { + if (rq->cmd_flags & REQ_FLUSH) + policy |= REQ_FSEQ_PREFLUSH; + if (blk_rq_sectors(rq)) + policy |= REQ_FSEQ_DATA; + if (!(fflags & REQ_FUA) && (rq->cmd_flags & REQ_FUA)) + policy |= REQ_FSEQ_POSTFLUSH; + } + return policy; } -static struct request *blk_flush_complete_seq(struct request_queue *q, - unsigned seq, int error) +static unsigned int blk_flush_cur_seq(struct request *rq) { - struct request *next_rq = NULL; - - if (error && !q->flush_err) - q->flush_err = error; - - BUG_ON(q->flush_seq & seq); - q->flush_seq |= seq; - - if (blk_flush_cur_seq(q) != QUEUE_FSEQ_DONE) { - /* not complete yet, queue the next flush sequence */ - next_rq = queue_next_fseq(q); - } else { - /* complete this flush request */ - __blk_end_request_all(q->orig_flush_rq, q->flush_err); - q->orig_flush_rq = NULL; - q->flush_seq = 0; - - /* dispatch the next flush if there's one */ - if (!list_empty(&q->pending_flushes)) { - next_rq = list_entry_rq(q->pending_flushes.next); - list_move(&next_rq->queuelist, &q->queue_head); - } - } - return next_rq; + return 1 << ffz(rq->flush.seq); } -static void blk_flush_complete_seq_end_io(struct request_queue *q, - unsigned seq, int error) +static void blk_flush_restore_request(struct request *rq) { - bool was_empty = elv_queue_empty(q); - struct request *next_rq; - - next_rq = blk_flush_complete_seq(q, seq, error); - /* - * Moving a request silently to empty queue_head may stall the - * queue. Kick the queue in those cases. + * After flush data completion, @rq->bio is %NULL but we need to + * complete the bio again. @rq->biotail is guaranteed to equal the + * original @rq->bio. Restore it. */ - if (was_empty && next_rq) - __blk_run_queue(q); + rq->bio = rq->biotail; + + /* make @rq a normal request */ + rq->cmd_flags &= ~REQ_FLUSH_SEQ; + rq->end_io = NULL; } -static void pre_flush_end_io(struct request *rq, int error) +/** + * blk_flush_complete_seq - complete flush sequence + * @rq: FLUSH/FUA request being sequenced + * @seq: sequences to complete (mask of %REQ_FSEQ_*, can be zero) + * @error: whether an error occurred + * + * @rq just completed @seq part of its flush sequence, record the + * completion and trigger the next step. + * + * CONTEXT: + * spin_lock_irq(q->queue_lock) + * + * RETURNS: + * %true if requests were added to the dispatch queue, %false otherwise. + */ +static bool blk_flush_complete_seq(struct request *rq, unsigned int seq, + int error) { - elv_completed_request(rq->q, rq); - blk_flush_complete_seq_end_io(rq->q, QUEUE_FSEQ_PREFLUSH, error); + struct request_queue *q = rq->q; + struct list_head *pending = &q->flush_queue[q->flush_pending_idx]; + bool queued = false; + + BUG_ON(rq->flush.seq & seq); + rq->flush.seq |= seq; + + if (likely(!error)) + seq = blk_flush_cur_seq(rq); + else + seq = REQ_FSEQ_DONE; + + switch (seq) { + case REQ_FSEQ_PREFLUSH: + case REQ_FSEQ_POSTFLUSH: + /* queue for flush */ + if (list_empty(pending)) + q->flush_pending_since = jiffies; + list_move_tail(&rq->flush.list, pending); + break; + + case REQ_FSEQ_DATA: + list_move_tail(&rq->flush.list, &q->flush_data_in_flight); + list_add(&rq->queuelist, &q->queue_head); + queued = true; + break; + + case REQ_FSEQ_DONE: + /* + * @rq was previously adjusted by blk_flush_issue() for + * flush sequencing and may already have gone through the + * flush data request completion path. Restore @rq for + * normal completion and end it. + */ + BUG_ON(!list_empty(&rq->queuelist)); + list_del_init(&rq->flush.list); + blk_flush_restore_request(rq); + __blk_end_request_all(rq, error); + break; + + default: + BUG(); + } + + return blk_kick_flush(q) | queued; } -static void flush_data_end_io(struct request *rq, int error) +static void flush_end_io(struct request *flush_rq, int error) { - elv_completed_request(rq->q, rq); - blk_flush_complete_seq_end_io(rq->q, QUEUE_FSEQ_DATA, error); + struct request_queue *q = flush_rq->q; + struct list_head *running = &q->flush_queue[q->flush_running_idx]; + bool was_empty = elv_queue_empty(q); + bool queued = false; + struct request *rq, *n; + + BUG_ON(q->flush_pending_idx == q->flush_running_idx); + + /* account completion of the flush request */ + q->flush_running_idx ^= 1; + elv_completed_request(q, flush_rq); + + /* and push the waiting requests to the next stage */ + list_for_each_entry_safe(rq, n, running, flush.list) { + unsigned int seq = blk_flush_cur_seq(rq); + + BUG_ON(seq != REQ_FSEQ_PREFLUSH && seq != REQ_FSEQ_POSTFLUSH); + queued |= blk_flush_complete_seq(rq, seq, error); + } + + /* after populating an empty queue, kick it to avoid stall */ + if (queued && was_empty) + __blk_run_queue(q); } -static void post_flush_end_io(struct request *rq, int error) +/** + * blk_kick_flush - consider issuing flush request + * @q: request_queue being kicked + * + * Flush related states of @q have changed, consider issuing flush request. + * Please read the comment at the top of this file for more info. + * + * CONTEXT: + * spin_lock_irq(q->queue_lock) + * + * RETURNS: + * %true if flush was issued, %false otherwise. + */ +static bool blk_kick_flush(struct request_queue *q) { - elv_completed_request(rq->q, rq); - blk_flush_complete_seq_end_io(rq->q, QUEUE_FSEQ_POSTFLUSH, error); + struct list_head *pending = &q->flush_queue[q->flush_pending_idx]; + struct request *first_rq = + list_first_entry(pending, struct request, flush.list); + + /* C1 described at the top of this file */ + if (q->flush_pending_idx != q->flush_running_idx || list_empty(pending)) + return false; + + /* C2 and C3 */ + if (!list_empty(&q->flush_data_in_flight) && + time_before(jiffies, + q->flush_pending_since + FLUSH_PENDING_TIMEOUT)) + return false; + + /* + * Issue flush and toggle pending_idx. This makes pending_idx + * different from running_idx, which means flush is in flight. + */ + blk_rq_init(q, &q->flush_rq); + q->flush_rq.cmd_type = REQ_TYPE_FS; + q->flush_rq.cmd_flags = WRITE_FLUSH | REQ_FLUSH_SEQ; + q->flush_rq.rq_disk = first_rq->rq_disk; + q->flush_rq.end_io = flush_end_io; + + q->flush_pending_idx ^= 1; + elv_insert(q, &q->flush_rq, ELEVATOR_INSERT_FRONT); + return true; } -static void init_flush_request(struct request *rq, struct gendisk *disk) +static void flush_data_end_io(struct request *rq, int error) { - rq->cmd_type = REQ_TYPE_FS; - rq->cmd_flags = WRITE_FLUSH; - rq->rq_disk = disk; + struct request_queue *q = rq->q; + bool was_empty = elv_queue_empty(q); + + /* after populating an empty queue, kick it to avoid stall */ + if (blk_flush_complete_seq(rq, REQ_FSEQ_DATA, error) && was_empty) + __blk_run_queue(q); } -static struct request *queue_next_fseq(struct request_queue *q) +/** + * blk_insert_flush - insert a new FLUSH/FUA request + * @rq: request to insert + * + * To be called from elv_insert() for %ELEVATOR_INSERT_FLUSH insertions. + * @rq is being submitted. Analyze what needs to be done and put it on the + * right queue. + * + * CONTEXT: + * spin_lock_irq(q->queue_lock) + */ +void blk_insert_flush(struct request *rq) { - struct request *orig_rq = q->orig_flush_rq; - struct request *rq = &q->flush_rq; + struct request_queue *q = rq->q; + unsigned int fflags = q->flush_flags; /* may change, cache */ + unsigned int policy = blk_flush_policy(fflags, rq); - blk_rq_init(q, rq); + BUG_ON(rq->end_io); + BUG_ON(!rq->bio || rq->bio != rq->biotail); - switch (blk_flush_cur_seq(q)) { - case QUEUE_FSEQ_PREFLUSH: - init_flush_request(rq, orig_rq->rq_disk); - rq->end_io = pre_flush_end_io; - break; - case QUEUE_FSEQ_DATA: - init_request_from_bio(rq, orig_rq->bio); - /* - * orig_rq->rq_disk may be different from - * bio->bi_bdev->bd_disk if orig_rq got here through - * remapping drivers. Make sure rq->rq_disk points - * to the same one as orig_rq. - */ - rq->rq_disk = orig_rq->rq_disk; - rq->cmd_flags &= ~(REQ_FLUSH | REQ_FUA); - rq->cmd_flags |= orig_rq->cmd_flags & (REQ_FLUSH | REQ_FUA); - rq->end_io = flush_data_end_io; - break; - case QUEUE_FSEQ_POSTFLUSH: - init_flush_request(rq, orig_rq->rq_disk); - rq->end_io = post_flush_end_io; - break; - default: - BUG(); + /* + * @policy now records what operations need to be done. Adjust + * REQ_FLUSH and FUA for the driver. + */ + rq->cmd_flags &= ~REQ_FLUSH; + if (!(fflags & REQ_FUA)) + rq->cmd_flags &= ~REQ_FUA; + + /* + * If there's data but flush is not necessary, the request can be + * processed directly without going through flush machinery. Queue + * for normal execution. + */ + if ((policy & REQ_FSEQ_DATA) && + !(policy & (REQ_FSEQ_PREFLUSH | REQ_FSEQ_POSTFLUSH))) { + list_add(&rq->queuelist, &q->queue_head); + return; } + /* + * @rq should go through flush machinery. Mark it part of flush + * sequence and submit for further processing. + */ + memset(&rq->flush, 0, sizeof(rq->flush)); + INIT_LIST_HEAD(&rq->flush.list); rq->cmd_flags |= REQ_FLUSH_SEQ; - elv_insert(q, rq, ELEVATOR_INSERT_FRONT); - return rq; + rq->end_io = flush_data_end_io; + + blk_flush_complete_seq(rq, REQ_FSEQ_ACTIONS & ~policy, 0); } -struct request *blk_do_flush(struct request_queue *q, struct request *rq) +/** + * blk_abort_flushes - @q is being aborted, abort flush requests + * @q: request_queue being aborted + * + * To be called from elv_abort_queue(). @q is being aborted. Prepare all + * FLUSH/FUA requests for abortion. + * + * CONTEXT: + * spin_lock_irq(q->queue_lock) + */ +void blk_abort_flushes(struct request_queue *q) { - unsigned int fflags = q->flush_flags; /* may change, cache it */ - bool has_flush = fflags & REQ_FLUSH, has_fua = fflags & REQ_FUA; - bool do_preflush = has_flush && (rq->cmd_flags & REQ_FLUSH); - bool do_postflush = has_flush && !has_fua && (rq->cmd_flags & REQ_FUA); - unsigned skip = 0; + struct request *rq, *n; + int i; /* - * Special case. If there's data but flush is not necessary, - * the request can be issued directly. - * - * Flush w/o data should be able to be issued directly too but - * currently some drivers assume that rq->bio contains - * non-zero data if it isn't NULL and empty FLUSH requests - * getting here usually have bio's without data. + * Requests in flight for data are already owned by the dispatch + * queue or the device driver. Just restore for normal completion. */ - if (blk_rq_sectors(rq) && !do_preflush && !do_postflush) { - rq->cmd_flags &= ~REQ_FLUSH; - if (!has_fua) - rq->cmd_flags &= ~REQ_FUA; - return rq; + list_for_each_entry_safe(rq, n, &q->flush_data_in_flight, flush.list) { + list_del_init(&rq->flush.list); + blk_flush_restore_request(rq); } /* - * Sequenced flushes can't be processed in parallel. If - * another one is already in progress, queue for later - * processing. + * We need to give away requests on flush queues. Restore for + * normal completion and put them on the dispatch queue. */ - if (q->flush_seq) { - list_move_tail(&rq->queuelist, &q->pending_flushes); - return NULL; + for (i = 0; i < ARRAY_SIZE(q->flush_queue); i++) { + list_for_each_entry_safe(rq, n, &q->flush_queue[i], + flush.list) { + list_del_init(&rq->flush.list); + blk_flush_restore_request(rq); + list_add_tail(&rq->queuelist, &q->queue_head); + } } - - /* - * Start a new flush sequence - */ - q->flush_err = 0; - q->flush_seq |= QUEUE_FSEQ_STARTED; - - /* adjust FLUSH/FUA of the original request and stash it away */ - rq->cmd_flags &= ~REQ_FLUSH; - if (!has_fua) - rq->cmd_flags &= ~REQ_FUA; - blk_dequeue_request(rq); - q->orig_flush_rq = rq; - - /* skip unneded sequences and return the first one */ - if (!do_preflush) - skip |= QUEUE_FSEQ_PREFLUSH; - if (!blk_rq_sectors(rq)) - skip |= QUEUE_FSEQ_DATA; - if (!do_postflush) - skip |= QUEUE_FSEQ_POSTFLUSH; - return blk_flush_complete_seq(q, skip, 0); } static void bio_end_flush(struct bio *bio, int err) diff --git a/block/blk.h b/block/blk.h index 9d2ee8f4d9af..284b500852bd 100644 --- a/block/blk.h +++ b/block/blk.h @@ -51,21 +51,17 @@ static inline void blk_clear_rq_complete(struct request *rq) */ #define ELV_ON_HASH(rq) (!hlist_unhashed(&(rq)->hash)) -struct request *blk_do_flush(struct request_queue *q, struct request *rq); +void blk_insert_flush(struct request *rq); +void blk_abort_flushes(struct request_queue *q); static inline struct request *__elv_next_request(struct request_queue *q) { struct request *rq; while (1) { - while (!list_empty(&q->queue_head)) { + if (!list_empty(&q->queue_head)) { rq = list_entry_rq(q->queue_head.next); - if (!(rq->cmd_flags & (REQ_FLUSH | REQ_FUA)) || - (rq->cmd_flags & REQ_FLUSH_SEQ)) - return rq; - rq = blk_do_flush(q, rq); - if (rq) - return rq; + return rq; } if (!q->elevator->ops->elevator_dispatch_fn(q, 0)) diff --git a/block/elevator.c b/block/elevator.c index 2569512830d3..270e0972eb9f 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -673,6 +673,11 @@ void elv_insert(struct request_queue *q, struct request *rq, int where) q->elevator->ops->elevator_add_req_fn(q, rq); break; + case ELEVATOR_INSERT_FLUSH: + rq->cmd_flags |= REQ_SOFTBARRIER; + blk_insert_flush(rq); + break; + default: printk(KERN_ERR "%s: bad insertion point %d\n", __func__, where); @@ -785,6 +790,8 @@ void elv_abort_queue(struct request_queue *q) { struct request *rq; + blk_abort_flushes(q); + while (!list_empty(&q->queue_head)) { rq = list_entry_rq(q->queue_head.next); rq->cmd_flags |= REQ_QUIET; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 36ab42c9bb99..6d7e9afd08c3 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -99,13 +99,18 @@ struct request { /* * The rb_node is only used inside the io scheduler, requests * are pruned when moved to the dispatch queue. So let the - * completion_data share space with the rb_node. + * flush fields share space with the rb_node. */ union { struct rb_node rb_node; /* sort/lookup */ - void *completion_data; + struct { + unsigned int seq; + struct list_head list; + } flush; }; + void *completion_data; + /* * Three pointers are available for the IO schedulers, if they need * more they have to dynamically allocate it. @@ -362,11 +367,12 @@ struct request_queue * for flush operations */ unsigned int flush_flags; - unsigned int flush_seq; - int flush_err; + unsigned int flush_pending_idx:1; + unsigned int flush_running_idx:1; + unsigned long flush_pending_since; + struct list_head flush_queue[2]; + struct list_head flush_data_in_flight; struct request flush_rq; - struct request *orig_flush_rq; - struct list_head pending_flushes; struct mutex sysfs_lock; diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 4fd978e7eb83..86120c916fcc 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -167,6 +167,7 @@ extern struct request *elv_rb_find(struct rb_root *, sector_t); #define ELEVATOR_INSERT_BACK 2 #define ELEVATOR_INSERT_SORT 3 #define ELEVATOR_INSERT_REQUEUE 4 +#define ELEVATOR_INSERT_FLUSH 5 /* * return values from elevator_may_queue_fn -- cgit v1.2.3 From 19df0c2fef010e94e90df514aaf4e73f6b80145c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 25 Jan 2011 14:26:50 +0100 Subject: percpu: align percpu readmostly subsection to cacheline Currently percpu readmostly subsection may share cachelines with other percpu subsections which may result in unnecessary cacheline bounce and performance degradation. This patch adds @cacheline parameter to PERCPU() and PERCPU_VADDR() linker macros, makes each arch linker scripts specify its cacheline size and use it to align percpu subsections. This is based on Shaohua's x86 only patch. Signed-off-by: Tejun Heo Cc: Shaohua Li --- arch/alpha/kernel/vmlinux.lds.S | 2 +- arch/arm/kernel/vmlinux.lds.S | 2 +- arch/blackfin/kernel/vmlinux.lds.S | 2 +- arch/cris/kernel/vmlinux.lds.S | 2 +- arch/frv/kernel/vmlinux.lds.S | 2 +- arch/ia64/kernel/vmlinux.lds.S | 2 +- arch/m32r/kernel/vmlinux.lds.S | 2 +- arch/mips/kernel/vmlinux.lds.S | 2 +- arch/mn10300/kernel/vmlinux.lds.S | 2 +- arch/parisc/kernel/vmlinux.lds.S | 2 +- arch/powerpc/kernel/vmlinux.lds.S | 2 +- arch/s390/kernel/vmlinux.lds.S | 2 +- arch/sh/kernel/vmlinux.lds.S | 2 +- arch/sparc/kernel/vmlinux.lds.S | 2 +- arch/tile/kernel/vmlinux.lds.S | 2 +- arch/um/include/asm/common.lds.S | 2 +- arch/x86/kernel/vmlinux.lds.S | 4 ++-- arch/xtensa/kernel/vmlinux.lds.S | 2 +- include/asm-generic/vmlinux.lds.h | 35 ++++++++++++++++++++++------------- 19 files changed, 41 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/arch/alpha/kernel/vmlinux.lds.S b/arch/alpha/kernel/vmlinux.lds.S index 003ef4c02585..173518f8c8bb 100644 --- a/arch/alpha/kernel/vmlinux.lds.S +++ b/arch/alpha/kernel/vmlinux.lds.S @@ -38,7 +38,7 @@ SECTIONS __init_begin = ALIGN(PAGE_SIZE); INIT_TEXT_SECTION(PAGE_SIZE) INIT_DATA_SECTION(16) - PERCPU(PAGE_SIZE) + PERCPU(64, PAGE_SIZE) /* Align to THREAD_SIZE rather than PAGE_SIZE here so any padding page needed for the THREAD_SIZE aligned init_task gets freed after init */ . = ALIGN(THREAD_SIZE); diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 86b66f3f2031..cf78a03bf810 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -70,7 +70,7 @@ SECTIONS #endif } - PERCPU(PAGE_SIZE) + PERCPU(32, PAGE_SIZE) #ifndef CONFIG_XIP_KERNEL . = ALIGN(PAGE_SIZE); diff --git a/arch/blackfin/kernel/vmlinux.lds.S b/arch/blackfin/kernel/vmlinux.lds.S index 4122678529c0..c40d07f708e8 100644 --- a/arch/blackfin/kernel/vmlinux.lds.S +++ b/arch/blackfin/kernel/vmlinux.lds.S @@ -136,7 +136,7 @@ SECTIONS . = ALIGN(16); INIT_DATA_SECTION(16) - PERCPU(4) + PERCPU(32, 4) .exit.data : { diff --git a/arch/cris/kernel/vmlinux.lds.S b/arch/cris/kernel/vmlinux.lds.S index 442218980db0..c62e1346f47c 100644 --- a/arch/cris/kernel/vmlinux.lds.S +++ b/arch/cris/kernel/vmlinux.lds.S @@ -107,7 +107,7 @@ SECTIONS #endif __vmlinux_end = .; /* Last address of the physical file. */ #ifdef CONFIG_ETRAX_ARCH_V32 - PERCPU(PAGE_SIZE) + PERCPU(32, PAGE_SIZE) .init.ramfs : { INIT_RAM_FS diff --git a/arch/frv/kernel/vmlinux.lds.S b/arch/frv/kernel/vmlinux.lds.S index 8b973f3cc90e..0daae8af5787 100644 --- a/arch/frv/kernel/vmlinux.lds.S +++ b/arch/frv/kernel/vmlinux.lds.S @@ -37,7 +37,7 @@ SECTIONS _einittext = .; INIT_DATA_SECTION(8) - PERCPU(4096) + PERCPU(L1_CACHE_BYTES, 4096) . = ALIGN(PAGE_SIZE); __init_end = .; diff --git a/arch/ia64/kernel/vmlinux.lds.S b/arch/ia64/kernel/vmlinux.lds.S index 5a4d044dcb1c..787de4a77d82 100644 --- a/arch/ia64/kernel/vmlinux.lds.S +++ b/arch/ia64/kernel/vmlinux.lds.S @@ -198,7 +198,7 @@ SECTIONS { /* Per-cpu data: */ . = ALIGN(PERCPU_PAGE_SIZE); - PERCPU_VADDR(PERCPU_ADDR, :percpu) + PERCPU_VADDR(SMP_CACHE_BYTES, PERCPU_ADDR, :percpu) __phys_per_cpu_start = __per_cpu_load; /* * ensure percpu data fits diff --git a/arch/m32r/kernel/vmlinux.lds.S b/arch/m32r/kernel/vmlinux.lds.S index 7da94eaa082b..c194d64cdbb9 100644 --- a/arch/m32r/kernel/vmlinux.lds.S +++ b/arch/m32r/kernel/vmlinux.lds.S @@ -53,7 +53,7 @@ SECTIONS __init_begin = .; INIT_TEXT_SECTION(PAGE_SIZE) INIT_DATA_SECTION(16) - PERCPU(PAGE_SIZE) + PERCPU(32, PAGE_SIZE) . = ALIGN(PAGE_SIZE); __init_end = .; /* freed after init ends here */ diff --git a/arch/mips/kernel/vmlinux.lds.S b/arch/mips/kernel/vmlinux.lds.S index 570607b376b5..832afbb87588 100644 --- a/arch/mips/kernel/vmlinux.lds.S +++ b/arch/mips/kernel/vmlinux.lds.S @@ -115,7 +115,7 @@ SECTIONS EXIT_DATA } - PERCPU(PAGE_SIZE) + PERCPU(1 << CONFIG_MIPS_L1_CACHE_SHIFT, PAGE_SIZE) . = ALIGN(PAGE_SIZE); __init_end = .; /* freed after init ends here */ diff --git a/arch/mn10300/kernel/vmlinux.lds.S b/arch/mn10300/kernel/vmlinux.lds.S index febbeee7f2f5..968bcd2cb022 100644 --- a/arch/mn10300/kernel/vmlinux.lds.S +++ b/arch/mn10300/kernel/vmlinux.lds.S @@ -70,7 +70,7 @@ SECTIONS .exit.text : { EXIT_TEXT; } .exit.data : { EXIT_DATA; } - PERCPU(PAGE_SIZE) + PERCPU(32, PAGE_SIZE) . = ALIGN(PAGE_SIZE); __init_end = .; /* freed after init ends here */ diff --git a/arch/parisc/kernel/vmlinux.lds.S b/arch/parisc/kernel/vmlinux.lds.S index d64a6bbec2aa..8f1e4efd143e 100644 --- a/arch/parisc/kernel/vmlinux.lds.S +++ b/arch/parisc/kernel/vmlinux.lds.S @@ -145,7 +145,7 @@ SECTIONS EXIT_DATA } - PERCPU(PAGE_SIZE) + PERCPU(L1_CACHE_BYTES, PAGE_SIZE) . = ALIGN(PAGE_SIZE); __init_end = .; /* freed after init ends here */ diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S index 8a0deefac08d..b9150f07d266 100644 --- a/arch/powerpc/kernel/vmlinux.lds.S +++ b/arch/powerpc/kernel/vmlinux.lds.S @@ -160,7 +160,7 @@ SECTIONS INIT_RAM_FS } - PERCPU(PAGE_SIZE) + PERCPU(L1_CACHE_BYTES, PAGE_SIZE) . = ALIGN(8); .machine.desc : AT(ADDR(.machine.desc) - LOAD_OFFSET) { diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index a68ac10213b2..1bc18cdb525b 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -77,7 +77,7 @@ SECTIONS . = ALIGN(PAGE_SIZE); INIT_DATA_SECTION(0x100) - PERCPU(PAGE_SIZE) + PERCPU(0x100, PAGE_SIZE) . = ALIGN(PAGE_SIZE); __init_end = .; /* freed after init ends here */ diff --git a/arch/sh/kernel/vmlinux.lds.S b/arch/sh/kernel/vmlinux.lds.S index 7f8a709c3ada..af4d46187a79 100644 --- a/arch/sh/kernel/vmlinux.lds.S +++ b/arch/sh/kernel/vmlinux.lds.S @@ -66,7 +66,7 @@ SECTIONS __machvec_end = .; } - PERCPU(PAGE_SIZE) + PERCPU(L1_CACHE_BYTES, PAGE_SIZE) /* * .exit.text is discarded at runtime, not link time, to deal with diff --git a/arch/sparc/kernel/vmlinux.lds.S b/arch/sparc/kernel/vmlinux.lds.S index 0c1e6783657f..92b557afe535 100644 --- a/arch/sparc/kernel/vmlinux.lds.S +++ b/arch/sparc/kernel/vmlinux.lds.S @@ -108,7 +108,7 @@ SECTIONS __sun4v_2insn_patch_end = .; } - PERCPU(PAGE_SIZE) + PERCPU(SMP_CACHE_BYTES, PAGE_SIZE) . = ALIGN(PAGE_SIZE); __init_end = .; diff --git a/arch/tile/kernel/vmlinux.lds.S b/arch/tile/kernel/vmlinux.lds.S index 25fdc0c1839a..c6ce378e0678 100644 --- a/arch/tile/kernel/vmlinux.lds.S +++ b/arch/tile/kernel/vmlinux.lds.S @@ -63,7 +63,7 @@ SECTIONS *(.init.page) } :data =0 INIT_DATA_SECTION(16) - PERCPU(PAGE_SIZE) + PERCPU(L2_CACHE_BYTES, PAGE_SIZE) . = ALIGN(PAGE_SIZE); VMLINUX_SYMBOL(_einitdata) = .; diff --git a/arch/um/include/asm/common.lds.S b/arch/um/include/asm/common.lds.S index ac55b9efa1ce..34bede8aad4a 100644 --- a/arch/um/include/asm/common.lds.S +++ b/arch/um/include/asm/common.lds.S @@ -42,7 +42,7 @@ INIT_SETUP(0) } - PERCPU(32) + PERCPU(32, 32) .initcall.init : { INIT_CALLS diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S index bf4700755184..cef446f8ac78 100644 --- a/arch/x86/kernel/vmlinux.lds.S +++ b/arch/x86/kernel/vmlinux.lds.S @@ -230,7 +230,7 @@ SECTIONS * output PHDR, so the next output section - .init.text - should * start another segment - init. */ - PERCPU_VADDR(0, :percpu) + PERCPU_VADDR(INTERNODE_CACHE_BYTES, 0, :percpu) #endif INIT_TEXT_SECTION(PAGE_SIZE) @@ -305,7 +305,7 @@ SECTIONS } #if !defined(CONFIG_X86_64) || !defined(CONFIG_SMP) - PERCPU(THREAD_SIZE) + PERCPU(INTERNODE_CACHE_BYTES, THREAD_SIZE) #endif . = ALIGN(PAGE_SIZE); diff --git a/arch/xtensa/kernel/vmlinux.lds.S b/arch/xtensa/kernel/vmlinux.lds.S index 9b526154c9ba..a2820065927e 100644 --- a/arch/xtensa/kernel/vmlinux.lds.S +++ b/arch/xtensa/kernel/vmlinux.lds.S @@ -155,7 +155,7 @@ SECTIONS INIT_RAM_FS } - PERCPU(PAGE_SIZE) + PERCPU(XCHAL_ICACHE_LINESIZE, PAGE_SIZE) /* We need this dummy segment here */ diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 6ebb81030d2d..439df587c12c 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -15,7 +15,7 @@ * HEAD_TEXT_SECTION * INIT_TEXT_SECTION(PAGE_SIZE) * INIT_DATA_SECTION(...) - * PERCPU(PAGE_SIZE) + * PERCPU(CACHELINE_SIZE, PAGE_SIZE) * __init_end = .; * * _stext = .; @@ -683,13 +683,18 @@ /** * PERCPU_VADDR - define output section for percpu area + * @cacheline: cacheline size * @vaddr: explicit base address (optional) * @phdr: destination PHDR (optional) * - * Macro which expands to output section for percpu area. If @vaddr - * is not blank, it specifies explicit base address and all percpu - * symbols will be offset from the given address. If blank, @vaddr - * always equals @laddr + LOAD_OFFSET. + * Macro which expands to output section for percpu area. + * + * @cacheline is used to align subsections to avoid false cacheline + * sharing between subsections for different purposes. + * + * If @vaddr is not blank, it specifies explicit base address and all + * percpu symbols will be offset from the given address. If blank, + * @vaddr always equals @laddr + LOAD_OFFSET. * * @phdr defines the output PHDR to use if not blank. Be warned that * output PHDR is sticky. If @phdr is specified, the next output @@ -700,7 +705,7 @@ * If there is no need to put the percpu section at a predetermined * address, use PERCPU(). */ -#define PERCPU_VADDR(vaddr, phdr) \ +#define PERCPU_VADDR(cacheline, vaddr, phdr) \ VMLINUX_SYMBOL(__per_cpu_load) = .; \ .data..percpu vaddr : AT(VMLINUX_SYMBOL(__per_cpu_load) \ - LOAD_OFFSET) { \ @@ -708,7 +713,9 @@ *(.data..percpu..first) \ . = ALIGN(PAGE_SIZE); \ *(.data..percpu..page_aligned) \ + . = ALIGN(cacheline); \ *(.data..percpu..readmostly) \ + . = ALIGN(cacheline); \ *(.data..percpu) \ *(.data..percpu..shared_aligned) \ VMLINUX_SYMBOL(__per_cpu_end) = .; \ @@ -717,18 +724,18 @@ /** * PERCPU - define output section for percpu area, simple version + * @cacheline: cacheline size * @align: required alignment * - * Align to @align and outputs output section for percpu area. This - * macro doesn't maniuplate @vaddr or @phdr and __per_cpu_load and + * Align to @align and outputs output section for percpu area. This macro + * doesn't manipulate @vaddr or @phdr and __per_cpu_load and * __per_cpu_start will be identical. * - * This macro is equivalent to ALIGN(align); PERCPU_VADDR( , ) except - * that __per_cpu_load is defined as a relative symbol against - * .data..percpu which is required for relocatable x86_32 - * configuration. + * This macro is equivalent to ALIGN(@align); PERCPU_VADDR(@cacheline,,) + * except that __per_cpu_load is defined as a relative symbol against + * .data..percpu which is required for relocatable x86_32 configuration. */ -#define PERCPU(align) \ +#define PERCPU(cacheline, align) \ . = ALIGN(align); \ .data..percpu : AT(ADDR(.data..percpu) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__per_cpu_load) = .; \ @@ -736,7 +743,9 @@ *(.data..percpu..first) \ . = ALIGN(PAGE_SIZE); \ *(.data..percpu..page_aligned) \ + . = ALIGN(cacheline); \ *(.data..percpu..readmostly) \ + . = ALIGN(cacheline); \ *(.data..percpu) \ *(.data..percpu..shared_aligned) \ VMLINUX_SYMBOL(__per_cpu_end) = .; \ -- cgit v1.2.3 From 181e055e6bed80afbf8ba2bb5e3ce84fbd3f633c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 24 Jan 2011 14:05:25 +0000 Subject: ASoC: Fix type for snd_soc_volatile_register() We generally refer to registers as unsigned ints (including in the underlying CODEC driver operation). Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 3 ++- sound/soc/soc-core.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index c184f84a354c..1355ef029d82 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -267,7 +267,8 @@ int snd_soc_register_codec(struct device *dev, const struct snd_soc_codec_driver *codec_drv, struct snd_soc_dai_driver *dai_drv, int num_dai); void snd_soc_unregister_codec(struct device *dev); -int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg); +int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, + unsigned int reg); int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, int addr_bits, int data_bits, enum snd_soc_control_type control); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b0e7689159c1..14861f95f629 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2029,7 +2029,8 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) * * Boolean function indiciating if a CODEC register is volatile. */ -int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg) +int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, + unsigned int reg) { if (codec->volatile_register) return codec->volatile_register(codec, reg); -- cgit v1.2.3 From 3d23c73fa0a47e8aecd2a4d8f280f45f6f7611a1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 24 Jan 2011 21:51:25 +0000 Subject: ASoC: Remove controls from sequenced PGA arguments We have zero users for PGA controls and the core support for them was removed a while ago so no point in cut'n'pasting them into new macros, even if it's too much hassle to update the existing ones. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc-dapm.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 6c9ae237814b..6a25e6993859 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -158,11 +158,11 @@ .event = wevent, .event_flags = wflags} /* additional sequencing control within an event type */ -#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, wcontrols, \ - wncontrols, wevent, wflags) \ +#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \ + wevent, wflags) \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ - .event = wevent, .event_flags = wflags, .subseq = wsubseq} + .invert = winvert, .event = wevent, .event_flags = wflags, \ + .subseq = wsubseq} #define SND_SOC_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, winvert, wevent, \ wflags) \ { .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \ -- cgit v1.2.3 From eb03355660b44cf6b1ed2f895085b9de8f74efbc Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 24 Jan 2011 15:11:08 +0000 Subject: drm: Add an interface to reset the device Iterate over the attached CRTCs, encoders and connectors and call the supplied reset vfunc in order to reset any cached state back to unknown. Useful after an invalidation event such as a GPU reset or resuming. Tested-by: Takashi Iwai Signed-off-by: Chris Wilson --- drivers/gpu/drm/drm_crtc.c | 20 ++++++++++++++++++++ include/drm/drm_crtc.h | 7 +++++++ 2 files changed, 27 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 2baa6708e44c..654faa803dcb 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2674,3 +2674,23 @@ out: mutex_unlock(&dev->mode_config.mutex); return ret; } + +void drm_mode_config_reset(struct drm_device *dev) +{ + struct drm_crtc *crtc; + struct drm_encoder *encoder; + struct drm_connector *connector; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + if (crtc->funcs->reset) + crtc->funcs->reset(crtc); + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) + if (encoder->funcs->reset) + encoder->funcs->reset(encoder); + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + if (connector->funcs->reset) + connector->funcs->reset(connector); +} +EXPORT_SYMBOL(drm_mode_config_reset); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index acd7fade160d..801be59f4f15 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -275,6 +275,7 @@ struct drm_pending_vblank_event; /** * drm_crtc_funcs - control CRTCs for a given device + * @reset: reset CRTC after state has been invalidate (e.g. resume) * @dpms: control display power levels * @save: save CRTC state * @resore: restore CRTC state @@ -302,6 +303,8 @@ struct drm_crtc_funcs { void (*save)(struct drm_crtc *crtc); /* suspend? */ /* Restore CRTC state */ void (*restore)(struct drm_crtc *crtc); /* resume? */ + /* Reset CRTC state */ + void (*reset)(struct drm_crtc *crtc); /* cursor controls */ int (*cursor_set)(struct drm_crtc *crtc, struct drm_file *file_priv, @@ -379,6 +382,7 @@ struct drm_crtc { * @dpms: set power state (see drm_crtc_funcs above) * @save: save connector state * @restore: restore connector state + * @reset: reset connector after state has been invalidate (e.g. resume) * @mode_valid: is this mode valid on the given connector? * @mode_fixup: try to fixup proposed mode for this connector * @mode_set: set this mode @@ -396,6 +400,7 @@ struct drm_connector_funcs { void (*dpms)(struct drm_connector *connector, int mode); void (*save)(struct drm_connector *connector); void (*restore)(struct drm_connector *connector); + void (*reset)(struct drm_connector *connector); /* Check to see if anything is attached to the connector. * @force is set to false whilst polling, true when checking the @@ -413,6 +418,7 @@ struct drm_connector_funcs { }; struct drm_encoder_funcs { + void (*reset)(struct drm_encoder *encoder); void (*destroy)(struct drm_encoder *encoder); }; @@ -656,6 +662,7 @@ extern struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, struct drm_display_mode *mode); extern void drm_mode_debug_printmodeline(struct drm_display_mode *mode); extern void drm_mode_config_init(struct drm_device *dev); +extern void drm_mode_config_reset(struct drm_device *dev); extern void drm_mode_config_cleanup(struct drm_device *dev); extern void drm_mode_set_name(struct drm_display_mode *mode); extern bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2); -- cgit v1.2.3 From 41b2610c3443e6c4760e61fc10eef73f96f9f6a5 Mon Sep 17 00:00:00 2001 From: Hans Rosenfeld Date: Mon, 24 Jan 2011 16:05:42 +0100 Subject: x86, amd: Extend AMD northbridge caching code to support "Link Control" devices "Link Control" devices (NB function 4) will be used by L3 cache partitioning on family 0x15. Signed-off-by: Hans Rosenfeld Cc: LKML-Reference: <1295881543-572552-4-git-send-email-hans.rosenfeld@amd.com> Signed-off-by: Ingo Molnar --- arch/x86/include/asm/amd_nb.h | 1 + arch/x86/kernel/amd_nb.c | 11 +++++++++-- include/linux/pci_ids.h | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/amd_nb.h b/arch/x86/include/asm/amd_nb.h index 64dc82ee19f0..3e7070071d73 100644 --- a/arch/x86/include/asm/amd_nb.h +++ b/arch/x86/include/asm/amd_nb.h @@ -26,6 +26,7 @@ extern void amd_get_nodes(struct bootnode *nodes); struct amd_northbridge { struct pci_dev *misc; + struct pci_dev *link; }; struct amd_northbridge_info { diff --git a/arch/x86/kernel/amd_nb.c b/arch/x86/kernel/amd_nb.c index a4f394c8e055..4ae9a961c33c 100644 --- a/arch/x86/kernel/amd_nb.c +++ b/arch/x86/kernel/amd_nb.c @@ -20,6 +20,11 @@ struct pci_device_id amd_nb_misc_ids[] = { }; EXPORT_SYMBOL(amd_nb_misc_ids); +static struct pci_device_id amd_nb_link_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_LINK) }, + {} +}; + const struct amd_nb_bus_dev_range amd_nb_bus_dev_ranges[] __initconst = { { 0x00, 0x18, 0x20 }, { 0xff, 0x00, 0x20 }, @@ -45,7 +50,7 @@ int amd_cache_northbridges(void) { int i = 0; struct amd_northbridge *nb; - struct pci_dev *misc; + struct pci_dev *misc, *link; if (amd_nb_num()) return 0; @@ -64,10 +69,12 @@ int amd_cache_northbridges(void) amd_northbridges.nb = nb; amd_northbridges.num = i; - misc = NULL; + link = misc = NULL; for (i = 0; i != amd_nb_num(); i++) { node_to_amd_nb(i)->misc = misc = next_northbridge(misc, amd_nb_misc_ids); + node_to_amd_nb(i)->link = link = + next_northbridge(link, amd_nb_link_ids); } /* some CPU families (e.g. family 0x11) do not support GART */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 3adb06ebf841..580de67f318b 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -518,6 +518,7 @@ #define PCI_DEVICE_ID_AMD_11H_NB_MISC 0x1303 #define PCI_DEVICE_ID_AMD_11H_NB_LINK 0x1304 #define PCI_DEVICE_ID_AMD_15H_NB_MISC 0x1603 +#define PCI_DEVICE_ID_AMD_15H_NB_LINK 0x1604 #define PCI_DEVICE_ID_AMD_CNB17H_F3 0x1703 #define PCI_DEVICE_ID_AMD_LANCE 0x2000 #define PCI_DEVICE_ID_AMD_LANCE_HOME 0x2001 -- cgit v1.2.3 From f17c13ca52d5c5a6a164536244a6debb8cd17983 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 24 Jan 2011 10:43:19 +0900 Subject: ASoC: sh: fsi: modify selection method of I2S/PCM/SPDIF format Current format selection of FSI-codecs depended on platform information for FSI, and chip default settings for codecs. It is not understandable/formal method. This patch modify FSI and FSI-codecs to use snd_soc_dai_set_fmt. But FSI can use I2S/PCM and SPDIF format today. It can be selected to I2S/PCM by snd_soc_dai_set_fmt, but can not select SPDIF. So, this patch change FSI platform information to have DAI/SPDIF mode. If platform selects DAI mode (default), FSI-codecs can select I2S/PCM by snd_soc_dai_set_fmt, and if it is SPDIF mode, FSI become SPDIF format. Signed-off-by: Kuninori Morimoto Acked-by: Paul Mundt Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- arch/arm/mach-shmobile/board-ag5evm.c | 8 --- arch/arm/mach-shmobile/board-ap4evb.c | 6 +- arch/arm/mach-shmobile/board-mackerel.c | 6 +- arch/sh/boards/mach-ecovec24/setup.c | 4 +- arch/sh/boards/mach-se/7724/setup.c | 4 +- include/sound/sh_fsi.h | 70 ++++++--------------- sound/soc/sh/fsi-ak4642.c | 3 +- sound/soc/sh/fsi-da7210.c | 3 +- sound/soc/sh/fsi.c | 106 +++++++++++++++++--------------- 9 files changed, 84 insertions(+), 126 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-shmobile/board-ag5evm.c b/arch/arm/mach-shmobile/board-ag5evm.c index 9ee55e0fbeb1..343362d02075 100644 --- a/arch/arm/mach-shmobile/board-ag5evm.c +++ b/arch/arm/mach-shmobile/board-ag5evm.c @@ -118,11 +118,6 @@ static struct platform_device keysc_device = { }; /* FSI A */ -static struct sh_fsi_platform_info fsi_info = { - .porta_flags = SH_FSI_OFMT(I2S) | - SH_FSI_IFMT(I2S), -}; - static struct resource fsi_resources[] = { [0] = { .name = "FSI", @@ -141,9 +136,6 @@ static struct platform_device fsi_device = { .id = -1, .num_resources = ARRAY_SIZE(fsi_resources), .resource = fsi_resources, - .dev = { - .platform_data = &fsi_info, - }, }; static struct resource sh_mmcif_resources[] = { diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c index 920ed81f1c61..17f528a76a1c 100644 --- a/arch/arm/mach-shmobile/board-ap4evb.c +++ b/arch/arm/mach-shmobile/board-ap4evb.c @@ -673,14 +673,12 @@ static int fsi_set_rate(struct device *dev, int is_porta, int rate, int enable) } static struct sh_fsi_platform_info fsi_info = { - .porta_flags = SH_FSI_BRS_INV | - SH_FSI_OFMT(PCM) | - SH_FSI_IFMT(PCM), + .porta_flags = SH_FSI_BRS_INV, .portb_flags = SH_FSI_BRS_INV | SH_FSI_BRM_INV | SH_FSI_LRS_INV | - SH_FSI_OFMT(SPDIF), + SH_FSI_FMT_SPDIF, .set_rate = fsi_set_rate, }; diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c index aa4bcc347044..73b8c90b5072 100644 --- a/arch/arm/mach-shmobile/board-mackerel.c +++ b/arch/arm/mach-shmobile/board-mackerel.c @@ -614,14 +614,12 @@ fsi_set_rate_end: } static struct sh_fsi_platform_info fsi_info = { - .porta_flags = SH_FSI_BRS_INV | - SH_FSI_OFMT(PCM) | - SH_FSI_IFMT(PCM), + .porta_flags = SH_FSI_BRS_INV, .portb_flags = SH_FSI_BRS_INV | SH_FSI_BRM_INV | SH_FSI_LRS_INV | - SH_FSI_OFMT(SPDIF), + SH_FSI_FMT_SPDIF, .set_rate = fsi_set_rate, }; diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index 037416f346cf..b96b79b970b2 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -723,9 +723,7 @@ static struct platform_device camera_devices[] = { /* FSI */ static struct sh_fsi_platform_info fsi_info = { - .portb_flags = SH_FSI_BRS_INV | - SH_FSI_OFMT(I2S) | - SH_FSI_IFMT(I2S), + .portb_flags = SH_FSI_BRS_INV, }; static struct resource fsi_resources[] = { diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index b4aef05dd8b5..c8bcf6a19b55 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -286,9 +286,7 @@ static struct platform_device ceu1_device = { /* FSI */ /* change J20, J21, J22 pin to 1-2 connection to use slave mode */ static struct sh_fsi_platform_info fsi_info = { - .porta_flags = SH_FSI_BRS_INV | - SH_FSI_OFMT(PCM) | - SH_FSI_IFMT(PCM), + .porta_flags = SH_FSI_BRS_INV, }; static struct resource fsi_resources[] = { diff --git a/include/sound/sh_fsi.h b/include/sound/sh_fsi.h index 18e43279f70f..9a155f9d0a12 100644 --- a/include/sound/sh_fsi.h +++ b/include/sound/sh_fsi.h @@ -15,61 +15,29 @@ #define FSI_PORT_A 0 #define FSI_PORT_B 1 -/* flags format - - * 0xABC0EEFF - * - * A: channel size for TDM (input) - * B: channel size for TDM (ooutput) - * C: inversion - * E: input format - * F: output format - */ - #include #include -/* TDM channel */ -#define SH_FSI_SET_CH_I(x) ((x & 0xF) << 28) -#define SH_FSI_SET_CH_O(x) ((x & 0xF) << 24) - -#define SH_FSI_CH_IMASK 0xF0000000 -#define SH_FSI_CH_OMASK 0x0F000000 -#define SH_FSI_GET_CH_I(x) ((x & SH_FSI_CH_IMASK) >> 28) -#define SH_FSI_GET_CH_O(x) ((x & SH_FSI_CH_OMASK) >> 24) - -/* clock inversion */ -#define SH_FSI_INVERSION_MASK 0x00F00000 -#define SH_FSI_LRM_INV (1 << 20) -#define SH_FSI_BRM_INV (1 << 21) -#define SH_FSI_LRS_INV (1 << 22) -#define SH_FSI_BRS_INV (1 << 23) - -/* DI format */ -#define SH_FSI_FMT_MASK 0x000000FF -#define SH_FSI_IFMT(x) (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 8) -#define SH_FSI_OFMT(x) (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 0) -#define SH_FSI_GET_IFMT(x) ((x >> 8) & SH_FSI_FMT_MASK) -#define SH_FSI_GET_OFMT(x) ((x >> 0) & SH_FSI_FMT_MASK) - -#define SH_FSI_FMT_MONO 0 -#define SH_FSI_FMT_MONO_DELAY 1 -#define SH_FSI_FMT_PCM 2 -#define SH_FSI_FMT_I2S 3 -#define SH_FSI_FMT_TDM 4 -#define SH_FSI_FMT_TDM_DELAY 5 -#define SH_FSI_FMT_SPDIF 6 - - -#define SH_FSI_IFMT_TDM_CH(x) \ - (SH_FSI_IFMT(TDM) | SH_FSI_SET_CH_I(x)) -#define SH_FSI_IFMT_TDM_DELAY_CH(x) \ - (SH_FSI_IFMT(TDM_DELAY) | SH_FSI_SET_CH_I(x)) +/* + * flags format + * + * 0x000000BA + * + * A: inversion + * B: format mode + */ -#define SH_FSI_OFMT_TDM_CH(x) \ - (SH_FSI_OFMT(TDM) | SH_FSI_SET_CH_O(x)) -#define SH_FSI_OFMT_TDM_DELAY_CH(x) \ - (SH_FSI_OFMT(TDM_DELAY) | SH_FSI_SET_CH_O(x)) +/* A: clock inversion */ +#define SH_FSI_INVERSION_MASK 0x0000000F +#define SH_FSI_LRM_INV (1 << 0) +#define SH_FSI_BRM_INV (1 << 1) +#define SH_FSI_LRS_INV (1 << 2) +#define SH_FSI_BRS_INV (1 << 3) + +/* B: format mode */ +#define SH_FSI_FMT_MASK 0x000000F0 +#define SH_FSI_FMT_DAI (0 << 4) +#define SH_FSI_FMT_SPDIF (1 << 4) /* diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c index ce058c749e6a..d6f4703b3c07 100644 --- a/sound/soc/sh/fsi-ak4642.c +++ b/sound/soc/sh/fsi-ak4642.c @@ -36,7 +36,8 @@ static int fsi_ak4642_dai_init(struct snd_soc_pcm_runtime *rtd) if (ret < 0) return ret; - ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBS_CFS); + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_LEFT_J | + SND_SOC_DAIFMT_CBS_CFS); return ret; } diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c index 9b24ed466ab1..dbafd7ac5590 100644 --- a/sound/soc/sh/fsi-da7210.c +++ b/sound/soc/sh/fsi-da7210.c @@ -25,7 +25,8 @@ static int fsi_da7210_init(struct snd_soc_pcm_runtime *rtd) if (ret < 0) return ret; - ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBS_CFS); + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_CBS_CFS); return ret; } diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 3c53693d7266..0c9997e2d8c0 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -757,9 +757,7 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); - struct fsi_master *master = fsi_get_master(fsi); u32 flags = fsi_get_info_flags(fsi); - u32 fmt; u32 data; int is_play = fsi_is_play(substream); @@ -779,54 +777,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, fsi_reg_write(fsi, CKG2, data); - /* do fmt, di fmt */ - data = 0; - fmt = is_play ? SH_FSI_GET_OFMT(flags) : SH_FSI_GET_IFMT(flags); - switch (fmt) { - case SH_FSI_FMT_MONO: - data = CR_MONO; - fsi->chan_num = 1; - break; - case SH_FSI_FMT_MONO_DELAY: - data = CR_MONO_D; - fsi->chan_num = 1; - break; - case SH_FSI_FMT_PCM: - data = CR_PCM; - fsi->chan_num = 2; - break; - case SH_FSI_FMT_I2S: - data = CR_I2S; - fsi->chan_num = 2; - break; - case SH_FSI_FMT_TDM: - fsi->chan_num = is_play ? - SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); - data = CR_TDM | (fsi->chan_num - 1); - break; - case SH_FSI_FMT_TDM_DELAY: - fsi->chan_num = is_play ? - SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); - data = CR_TDM_D | (fsi->chan_num - 1); - break; - case SH_FSI_FMT_SPDIF: - if (master->core->ver < 2) { - dev_err(dai->dev, "This FSI can not use SPDIF\n"); - return -EINVAL; - } - data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM; - fsi->chan_num = 2; - fsi_spdif_clk_ctrl(fsi, 1); - fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); - break; - default: - dev_err(dai->dev, "unknown format.\n"); - return -EINVAL; - } - is_play ? - fsi_reg_write(fsi, DO_FMT, data) : - fsi_reg_write(fsi, DI_FMT, data); - /* irq clear */ fsi_irq_disable(fsi, is_play); fsi_irq_clear_status(fsi); @@ -881,9 +831,52 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } +static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt) +{ + u32 data = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + data = CR_I2S; + fsi->chan_num = 2; + break; + case SND_SOC_DAIFMT_LEFT_J: + data = CR_PCM; + fsi->chan_num = 2; + break; + default: + return -EINVAL; + } + + fsi_reg_write(fsi, DO_FMT, data); + fsi_reg_write(fsi, DI_FMT, data); + + return 0; +} + +static int fsi_set_fmt_spdif(struct fsi_priv *fsi) +{ + struct fsi_master *master = fsi_get_master(fsi); + u32 data = 0; + + if (master->core->ver < 2) + return -EINVAL; + + data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM; + fsi->chan_num = 2; + fsi_spdif_clk_ctrl(fsi, 1); + fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); + + fsi_reg_write(fsi, DO_FMT, data); + fsi_reg_write(fsi, DI_FMT, data); + + return 0; +} + static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai); + u32 flags = fsi_get_info_flags(fsi); u32 data = 0; int ret; @@ -901,7 +894,18 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) goto set_fmt_exit; } fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data); - ret = 0; + + /* set format */ + switch (flags & SH_FSI_FMT_MASK) { + case SH_FSI_FMT_DAI: + ret = fsi_set_fmt_dai(fsi, fmt & SND_SOC_DAIFMT_FORMAT_MASK); + break; + case SH_FSI_FMT_SPDIF: + ret = fsi_set_fmt_spdif(fsi); + break; + default: + ret = -EINVAL; + } set_fmt_exit: pm_runtime_put_sync(dai->dev); -- cgit v1.2.3 From 4dd53d891ca46dcc1fde0376a33540d3fd83cb9a Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Tue, 21 Dec 2010 17:09:00 -0800 Subject: softirqs: Free up pf flag PF_KSOFTIRQD Cleanup patch, freeing up PF_KSOFTIRQD and use per_cpu ksoftirqd pointer instead, as suggested by Eric Dumazet. Tested-by: Shaun Ruffell Signed-off-by: Venkatesh Pallipadi Signed-off-by: Peter Zijlstra LKML-Reference: <1292980144-28796-2-git-send-email-venki@google.com> Signed-off-by: Ingo Molnar --- include/linux/interrupt.h | 7 +++++++ include/linux/sched.h | 1 - kernel/sched.c | 2 +- kernel/softirq.c | 3 +-- 4 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 55e0d4253e49..a1382b9b5813 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -426,6 +426,13 @@ extern void raise_softirq(unsigned int nr); */ DECLARE_PER_CPU(struct list_head [NR_SOFTIRQS], softirq_work_list); +DECLARE_PER_CPU(struct task_struct *, ksoftirqd); + +static inline struct task_struct *this_cpu_ksoftirqd(void) +{ + return this_cpu_read(ksoftirqd); +} + /* Try to send a softirq to a remote cpu. If this cannot be done, the * work will be queued to the local cpu. */ diff --git a/include/linux/sched.h b/include/linux/sched.h index d747f948b34e..af6e15fbfb78 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1715,7 +1715,6 @@ extern void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t * /* * Per process flags */ -#define PF_KSOFTIRQD 0x00000001 /* I am ksoftirqd */ #define PF_STARTING 0x00000002 /* being created */ #define PF_EXITING 0x00000004 /* getting shut down */ #define PF_EXITPIDONE 0x00000008 /* pi exit done on shut down */ diff --git a/kernel/sched.c b/kernel/sched.c index 6820b5b3a969..8b89b3bba565 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1880,7 +1880,7 @@ void account_system_vtime(struct task_struct *curr) */ if (hardirq_count()) __this_cpu_add(cpu_hardirq_time, delta); - else if (in_serving_softirq() && !(curr->flags & PF_KSOFTIRQD)) + else if (in_serving_softirq() && curr != this_cpu_ksoftirqd()) __this_cpu_add(cpu_softirq_time, delta); irq_time_write_end(); diff --git a/kernel/softirq.c b/kernel/softirq.c index 68eb5efec388..0cee50487629 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -54,7 +54,7 @@ EXPORT_SYMBOL(irq_stat); static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp; -static DEFINE_PER_CPU(struct task_struct *, ksoftirqd); +DEFINE_PER_CPU(struct task_struct *, ksoftirqd); char *softirq_to_name[NR_SOFTIRQS] = { "HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "BLOCK_IOPOLL", @@ -721,7 +721,6 @@ static int run_ksoftirqd(void * __bind_cpu) { set_current_state(TASK_INTERRUPTIBLE); - current->flags |= PF_KSOFTIRQD; while (!kthread_should_stop()) { preempt_disable(); if (!local_softirq_pending()) { -- cgit v1.2.3 From a1dabb6bfffccb897eff3e1d102dacf2a4bedf3b Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Tue, 21 Dec 2010 17:09:01 -0800 Subject: time: Add nsecs_to_cputime64 interface for asm-generic Add nsecs_to_cputime64 interface. This is used in following patches that updates cpu irq stat based on ns granularity info in IRQ_TIME_ACCOUNTING. Tested-by: Shaun Ruffell Signed-off-by: Venkatesh Pallipadi Signed-off-by: Peter Zijlstra LKML-Reference: <1292980144-28796-3-git-send-email-venki@google.com> Signed-off-by: Ingo Molnar --- include/asm-generic/cputime.h | 3 +++ include/linux/jiffies.h | 1 + kernel/time.c | 23 +++++++++++++++++++++-- 3 files changed, 25 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-generic/cputime.h b/include/asm-generic/cputime.h index 2bcc5c7c22a6..61e03dd7939e 100644 --- a/include/asm-generic/cputime.h +++ b/include/asm-generic/cputime.h @@ -30,6 +30,9 @@ typedef u64 cputime64_t; #define cputime64_to_jiffies64(__ct) (__ct) #define jiffies64_to_cputime64(__jif) (__jif) #define cputime_to_cputime64(__ct) ((u64) __ct) +#define cputime64_gt(__a, __b) ((__a) > (__b)) + +#define nsecs_to_cputime64(__ct) nsecs_to_jiffies64(__ct) /* diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h index 6811f4bfc6e7..922aa313c9f9 100644 --- a/include/linux/jiffies.h +++ b/include/linux/jiffies.h @@ -307,6 +307,7 @@ extern clock_t jiffies_to_clock_t(long x); extern unsigned long clock_t_to_jiffies(unsigned long x); extern u64 jiffies_64_to_clock_t(u64 x); extern u64 nsec_to_clock_t(u64 x); +extern u64 nsecs_to_jiffies64(u64 n); extern unsigned long nsecs_to_jiffies(u64 n); #define TIMESTAMP_SIZE 30 diff --git a/kernel/time.c b/kernel/time.c index 32174359576f..55337a816b20 100644 --- a/kernel/time.c +++ b/kernel/time.c @@ -645,7 +645,7 @@ u64 nsec_to_clock_t(u64 x) } /** - * nsecs_to_jiffies - Convert nsecs in u64 to jiffies + * nsecs_to_jiffies64 - Convert nsecs in u64 to jiffies64 * * @n: nsecs in u64 * @@ -657,7 +657,7 @@ u64 nsec_to_clock_t(u64 x) * NSEC_PER_SEC = 10^9 = (5^9 * 2^9) = (1953125 * 512) * ULLONG_MAX ns = 18446744073.709551615 secs = about 584 years */ -unsigned long nsecs_to_jiffies(u64 n) +u64 nsecs_to_jiffies64(u64 n) { #if (NSEC_PER_SEC % HZ) == 0 /* Common case, HZ = 100, 128, 200, 250, 256, 500, 512, 1000 etc. */ @@ -674,6 +674,25 @@ unsigned long nsecs_to_jiffies(u64 n) #endif } + +/** + * nsecs_to_jiffies - Convert nsecs in u64 to jiffies + * + * @n: nsecs in u64 + * + * Unlike {m,u}secs_to_jiffies, type of input is not unsigned int but u64. + * And this doesn't return MAX_JIFFY_OFFSET since this function is designed + * for scheduler, not for use in device drivers to calculate timeout value. + * + * note: + * NSEC_PER_SEC = 10^9 = (5^9 * 2^9) = (1953125 * 512) + * ULLONG_MAX ns = 18446744073.709551615 secs = about 584 years + */ +unsigned long nsecs_to_jiffies(u64 n) +{ + return (unsigned long)nsecs_to_jiffies64(n); +} + #if (BITS_PER_LONG < 64) u64 get_jiffies_64(void) { -- cgit v1.2.3 From da7a735e51f9622eb3e1672594d4a41da01d7e4f Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 17 Jan 2011 17:03:27 +0100 Subject: sched: Fix switch_from_fair() When a task is taken out of the fair class we must ensure the vruntime is properly normalized because when we put it back in it will assume to be normalized. The case that goes wrong is when changing away from the fair class while sleeping. Sleeping tasks have non-normalized vruntime in order to make sleeper-fairness work. So treat the switch away from fair as a wakeup and preserve the relative vruntime. Also update sysrq-n to call the ->switch_{to,from} methods. Reported-by: Onkalo Samu Signed-off-by: Peter Zijlstra LKML-Reference: Signed-off-by: Ingo Molnar --- include/linux/sched.h | 8 +++----- kernel/sched.c | 25 ++++++++++++++----------- kernel/sched_fair.c | 42 ++++++++++++++++++++++++++++++++++++------ kernel/sched_idletask.c | 7 +++---- kernel/sched_rt.c | 19 ++++++++++--------- kernel/sched_stoptask.c | 7 +++---- 6 files changed, 69 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index af6e15fbfb78..0542774914d4 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1084,12 +1084,10 @@ struct sched_class { void (*task_tick) (struct rq *rq, struct task_struct *p, int queued); void (*task_fork) (struct task_struct *p); - void (*switched_from) (struct rq *this_rq, struct task_struct *task, - int running); - void (*switched_to) (struct rq *this_rq, struct task_struct *task, - int running); + void (*switched_from) (struct rq *this_rq, struct task_struct *task); + void (*switched_to) (struct rq *this_rq, struct task_struct *task); void (*prio_changed) (struct rq *this_rq, struct task_struct *task, - int oldprio, int running); + int oldprio); unsigned int (*get_rr_interval) (struct rq *rq, struct task_struct *task); diff --git a/kernel/sched.c b/kernel/sched.c index 8b718b59b09f..78fa75394011 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2057,14 +2057,14 @@ inline int task_curr(const struct task_struct *p) static inline void check_class_changed(struct rq *rq, struct task_struct *p, const struct sched_class *prev_class, - int oldprio, int running) + int oldprio) { if (prev_class != p->sched_class) { if (prev_class->switched_from) - prev_class->switched_from(rq, p, running); - p->sched_class->switched_to(rq, p, running); - } else - p->sched_class->prio_changed(rq, p, oldprio, running); + prev_class->switched_from(rq, p); + p->sched_class->switched_to(rq, p); + } else if (oldprio != p->prio) + p->sched_class->prio_changed(rq, p, oldprio); } static void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags) @@ -2598,6 +2598,7 @@ static void __sched_fork(struct task_struct *p) p->se.sum_exec_runtime = 0; p->se.prev_sum_exec_runtime = 0; p->se.nr_migrations = 0; + p->se.vruntime = 0; #ifdef CONFIG_SCHEDSTATS memset(&p->se.statistics, 0, sizeof(p->se.statistics)); @@ -4696,11 +4697,10 @@ void rt_mutex_setprio(struct task_struct *p, int prio) if (running) p->sched_class->set_curr_task(rq); - if (on_rq) { + if (on_rq) enqueue_task(rq, p, oldprio < prio ? ENQUEUE_HEAD : 0); - check_class_changed(rq, p, prev_class, oldprio, running); - } + check_class_changed(rq, p, prev_class, oldprio); task_rq_unlock(rq, &flags); } @@ -5028,11 +5028,10 @@ recheck: if (running) p->sched_class->set_curr_task(rq); - if (on_rq) { + if (on_rq) activate_task(rq, p, 0); - check_class_changed(rq, p, prev_class, oldprio, running); - } + check_class_changed(rq, p, prev_class, oldprio); __task_rq_unlock(rq); raw_spin_unlock_irqrestore(&p->pi_lock, flags); @@ -8237,6 +8236,8 @@ EXPORT_SYMBOL(__might_sleep); #ifdef CONFIG_MAGIC_SYSRQ static void normalize_task(struct rq *rq, struct task_struct *p) { + const struct sched_class *prev_class = p->sched_class; + int old_prio = p->prio; int on_rq; on_rq = p->se.on_rq; @@ -8247,6 +8248,8 @@ static void normalize_task(struct rq *rq, struct task_struct *p) activate_task(rq, p, 0); resched_task(rq->curr); } + + check_class_changed(rq, p, prev_class, old_prio); } void normalize_rt_tasks(void) diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index 4cbc9121094c..55040f3938d8 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -4078,33 +4078,62 @@ static void task_fork_fair(struct task_struct *p) * Priority of the task has changed. Check to see if we preempt * the current task. */ -static void prio_changed_fair(struct rq *rq, struct task_struct *p, - int oldprio, int running) +static void +prio_changed_fair(struct rq *rq, struct task_struct *p, int oldprio) { + if (!p->se.on_rq) + return; + /* * Reschedule if we are currently running on this runqueue and * our priority decreased, or if we are not currently running on * this runqueue and our priority is higher than the current's */ - if (running) { + if (rq->curr == p) { if (p->prio > oldprio) resched_task(rq->curr); } else check_preempt_curr(rq, p, 0); } +static void switched_from_fair(struct rq *rq, struct task_struct *p) +{ + struct sched_entity *se = &p->se; + struct cfs_rq *cfs_rq = cfs_rq_of(se); + + /* + * Ensure the task's vruntime is normalized, so that when its + * switched back to the fair class the enqueue_entity(.flags=0) will + * do the right thing. + * + * If it was on_rq, then the dequeue_entity(.flags=0) will already + * have normalized the vruntime, if it was !on_rq, then only when + * the task is sleeping will it still have non-normalized vruntime. + */ + if (!se->on_rq && p->state != TASK_RUNNING) { + /* + * Fix up our vruntime so that the current sleep doesn't + * cause 'unlimited' sleep bonus. + */ + place_entity(cfs_rq, se, 0); + se->vruntime -= cfs_rq->min_vruntime; + } +} + /* * We switched to the sched_fair class. */ -static void switched_to_fair(struct rq *rq, struct task_struct *p, - int running) +static void switched_to_fair(struct rq *rq, struct task_struct *p) { + if (!p->se.on_rq) + return; + /* * We were most likely switched from sched_rt, so * kick off the schedule if running, otherwise just see * if we can still preempt the current task. */ - if (running) + if (rq->curr == p) resched_task(rq->curr); else check_preempt_curr(rq, p, 0); @@ -4190,6 +4219,7 @@ static const struct sched_class fair_sched_class = { .task_fork = task_fork_fair, .prio_changed = prio_changed_fair, + .switched_from = switched_from_fair, .switched_to = switched_to_fair, .get_rr_interval = get_rr_interval_fair, diff --git a/kernel/sched_idletask.c b/kernel/sched_idletask.c index 41eb62a0808b..c82f26c1b7c3 100644 --- a/kernel/sched_idletask.c +++ b/kernel/sched_idletask.c @@ -52,14 +52,13 @@ static void set_curr_task_idle(struct rq *rq) { } -static void -switched_to_idle(struct rq *rq, struct task_struct *p, int running) +static void switched_to_idle(struct rq *rq, struct task_struct *p) { BUG(); } -static void prio_changed_idle(struct rq *rq, struct task_struct *p, - int oldprio, int running) +static void +prio_changed_idle(struct rq *rq, struct task_struct *p, int oldprio) { BUG(); } diff --git a/kernel/sched_rt.c b/kernel/sched_rt.c index c914ec747ca6..c381fdc18c64 100644 --- a/kernel/sched_rt.c +++ b/kernel/sched_rt.c @@ -1595,8 +1595,7 @@ static void rq_offline_rt(struct rq *rq) * When switch from the rt queue, we bring ourselves to a position * that we might want to pull RT tasks from other runqueues. */ -static void switched_from_rt(struct rq *rq, struct task_struct *p, - int running) +static void switched_from_rt(struct rq *rq, struct task_struct *p) { /* * If there are other RT tasks then we will reschedule @@ -1605,7 +1604,7 @@ static void switched_from_rt(struct rq *rq, struct task_struct *p, * we may need to handle the pulling of RT tasks * now. */ - if (!rq->rt.rt_nr_running) + if (p->se.on_rq && !rq->rt.rt_nr_running) pull_rt_task(rq); } @@ -1624,8 +1623,7 @@ static inline void init_sched_rt_class(void) * with RT tasks. In this case we try to push them off to * other runqueues. */ -static void switched_to_rt(struct rq *rq, struct task_struct *p, - int running) +static void switched_to_rt(struct rq *rq, struct task_struct *p) { int check_resched = 1; @@ -1636,7 +1634,7 @@ static void switched_to_rt(struct rq *rq, struct task_struct *p, * If that current running task is also an RT task * then see if we can move to another run queue. */ - if (!running) { + if (p->se.on_rq && rq->curr != p) { #ifdef CONFIG_SMP if (rq->rt.overloaded && push_rt_task(rq) && /* Don't resched if we changed runqueues */ @@ -1652,10 +1650,13 @@ static void switched_to_rt(struct rq *rq, struct task_struct *p, * Priority of the task has changed. This may cause * us to initiate a push or pull. */ -static void prio_changed_rt(struct rq *rq, struct task_struct *p, - int oldprio, int running) +static void +prio_changed_rt(struct rq *rq, struct task_struct *p, int oldprio) { - if (running) { + if (!p->se.on_rq) + return; + + if (rq->curr == p) { #ifdef CONFIG_SMP /* * If our priority decreases while running, we diff --git a/kernel/sched_stoptask.c b/kernel/sched_stoptask.c index 2bf6b47058c1..84ec9bcf82d9 100644 --- a/kernel/sched_stoptask.c +++ b/kernel/sched_stoptask.c @@ -59,14 +59,13 @@ static void set_curr_task_stop(struct rq *rq) { } -static void switched_to_stop(struct rq *rq, struct task_struct *p, - int running) +static void switched_to_stop(struct rq *rq, struct task_struct *p) { BUG(); /* its impossible to change to this class */ } -static void prio_changed_stop(struct rq *rq, struct task_struct *p, - int oldprio, int running) +static void +prio_changed_stop(struct rq *rq, struct task_struct *p, int oldprio) { BUG(); /* how!?, what priority? */ } -- cgit v1.2.3 From 1ea1bdf7faa4d0b5293e605f2e1ef1c2c59f6b53 Mon Sep 17 00:00:00 2001 From: Ari Kauppi Date: Thu, 20 Jan 2011 13:57:18 -0500 Subject: oprofile: Fix usage of CONFIG_HW_PERF_EVENTS for oprofile_perf_init and friends The implementations are flagged in Makefile with CONFIG_HW_PERF_EVENTS instead of CONFIG_PERF_EVENTS. Cc: stable@kernel.org # 37.x Signed-off-by: Ari Kauppi Signed-off-by: Robert Richter --- include/linux/oprofile.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/oprofile.h b/include/linux/oprofile.h index 32fb81212fd1..54c83827f913 100644 --- a/include/linux/oprofile.h +++ b/include/linux/oprofile.h @@ -186,10 +186,10 @@ int oprofile_add_data(struct op_entry *entry, unsigned long val); int oprofile_add_data64(struct op_entry *entry, u64 val); int oprofile_write_commit(struct op_entry *entry); -#ifdef CONFIG_PERF_EVENTS +#ifdef CONFIG_HW_PERF_EVENTS int __init oprofile_perf_init(struct oprofile_operations *ops); void oprofile_perf_exit(void); char *op_name_from_perf_id(void); -#endif /* CONFIG_PERF_EVENTS */ +#endif /* CONFIG_HW_PERF_EVENTS */ #endif /* OPROFILE_H */ -- cgit v1.2.3 From d14dd7e20d5e526557f5d3cfef4046a642f80924 Mon Sep 17 00:00:00 2001 From: Ari Kauppi Date: Thu, 20 Jan 2011 13:57:19 -0500 Subject: ARM: oprofile: Fix backtraces in timer mode Always allow backtraces when using oprofile on ARM, even if a PMU isn't present. Restores functionality originally introduced in commit 1b7b56982fdcd9d85effd76f3928cf5d6eb26155 ("oprofile: Always allow backtraces on ARM") by Richard Purdie. It is not that obvious, but there is now only one oprofile_arch_init() function. So the .backtrace callback is available also in timer mode. Implemented by removing code and using stubs for oprofile_perf_{init, exit} provided by . This allows cleaning of other architecture specific implementations too. Cc: stable@kernel.org # 37.x Signed-off-by: Ari Kauppi Acked-by: Will Deacon Signed-off-by: Robert Richter --- arch/arm/oprofile/common.c | 12 ++---------- include/linux/oprofile.h | 9 +++++++++ 2 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c index 8aa974491dfc..2b663918c464 100644 --- a/arch/arm/oprofile/common.c +++ b/arch/arm/oprofile/common.c @@ -10,8 +10,6 @@ */ #include -#include -#include #include #include #include @@ -46,6 +44,7 @@ char *op_name_from_perf_id(void) return NULL; } } +#endif static int report_trace(struct stackframe *frame, void *d) { @@ -111,6 +110,7 @@ static void arm_backtrace(struct pt_regs * const regs, unsigned int depth) int __init oprofile_arch_init(struct oprofile_operations *ops) { + /* provide backtrace support also in timer mode: */ ops->backtrace = arm_backtrace; return oprofile_perf_init(ops); @@ -120,11 +120,3 @@ void __exit oprofile_arch_exit(void) { oprofile_perf_exit(); } -#else -int __init oprofile_arch_init(struct oprofile_operations *ops) -{ - pr_info("oprofile: hardware counters not available\n"); - return -ENODEV; -} -void __exit oprofile_arch_exit(void) {} -#endif /* CONFIG_HW_PERF_EVENTS */ diff --git a/include/linux/oprofile.h b/include/linux/oprofile.h index 54c83827f913..1ca64113efe8 100644 --- a/include/linux/oprofile.h +++ b/include/linux/oprofile.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include /* Each escaped entry is prefixed by ESCAPE_CODE @@ -190,6 +192,13 @@ int oprofile_write_commit(struct op_entry *entry); int __init oprofile_perf_init(struct oprofile_operations *ops); void oprofile_perf_exit(void); char *op_name_from_perf_id(void); +#else +static inline int __init oprofile_perf_init(struct oprofile_operations *ops) +{ + pr_info("oprofile: hardware counters not available\n"); + return -ENODEV; +} +static inline void oprofile_perf_exit(void) { } #endif /* CONFIG_HW_PERF_EVENTS */ #endif /* OPROFILE_H */ -- cgit v1.2.3 From 62fa8a846d7de4b299232e330c74b7783539df76 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 26 Jan 2011 20:51:05 -0800 Subject: net: Implement read-only protection and COW'ing of metrics. Routing metrics are now copy-on-write. Initially a route entry points it's metrics at a read-only location. If a routing table entry exists, it will point there. Else it will point at the all zero metric place-holder called 'dst_default_metrics'. The writeability state of the metrics is stored in the low bits of the metrics pointer, we have two bits left to spare if we want to store more states. For the initial implementation, COW is implemented simply via kmalloc. However future enhancements will change this to place the writable metrics somewhere else, in order to increase sharing. Very likely this "somewhere else" will be the inetpeer cache. Note also that this means that metrics updates may transiently fail if we cannot COW the metrics successfully. But even by itself, this patch should decrease memory usage and increase cache locality especially for routing workloads. In those cases the read-only metric copies stay in place and never get written to. TCP workloads where metrics get updated, and those rare cases where PMTU triggers occur, will take a very slight performance hit. But that hit will be alleviated when the long-term writable metrics move to a more sharable location. Since the metrics storage went from a u32 array of RTAX_MAX entries to what is essentially a pointer, some retooling of the dst_entry layout was necessary. Most importantly, we need to preserve the alignment of the reference count so that it doesn't share cache lines with the read-mostly state, as per Eric Dumazet's alignment assertion checks. The only non-trivial bit here is the move of the 'flags' member into the writeable cacheline. This is OK since we are always accessing the flags around the same moment when we made a modification to the reference count. Signed-off-by: David S. Miller --- include/net/dst.h | 114 ++++++++++++++++++++++++++++++++---------------- include/net/dst_ops.h | 1 + include/net/route.h | 2 + net/core/dst.c | 39 +++++++++++++++++ net/decnet/dn_route.c | 18 +++++--- net/ipv4/route.c | 45 ++++++++++++++++++- net/ipv4/xfrm4_policy.c | 4 ++ net/ipv6/route.c | 15 +++++-- net/ipv6/xfrm6_policy.c | 2 + 9 files changed, 194 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index be5a0d4c491d..94a8c234ea2a 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -40,24 +40,10 @@ struct dst_entry { struct rcu_head rcu_head; struct dst_entry *child; struct net_device *dev; - short error; - short obsolete; - int flags; -#define DST_HOST 0x0001 -#define DST_NOXFRM 0x0002 -#define DST_NOPOLICY 0x0004 -#define DST_NOHASH 0x0008 -#define DST_NOCACHE 0x0010 + struct dst_ops *ops; + unsigned long _metrics; unsigned long expires; - - unsigned short header_len; /* more space at head required */ - unsigned short trailer_len; /* space to reserve at tail */ - - unsigned int rate_tokens; - unsigned long rate_last; /* rate limiting for ICMP */ - struct dst_entry *path; - struct neighbour *neighbour; struct hh_cache *hh; #ifdef CONFIG_XFRM @@ -68,17 +54,16 @@ struct dst_entry { int (*input)(struct sk_buff*); int (*output)(struct sk_buff*); - struct dst_ops *ops; - - u32 _metrics[RTAX_MAX]; - + short error; + short obsolete; + unsigned short header_len; /* more space at head required */ + unsigned short trailer_len; /* space to reserve at tail */ #ifdef CONFIG_IP_ROUTE_CLASSID __u32 tclassid; #else __u32 __pad2; #endif - /* * Align __refcnt to a 64 bytes alignment * (L1_CACHE_SIZE would be too much) @@ -93,6 +78,14 @@ struct dst_entry { atomic_t __refcnt; /* client references */ int __use; unsigned long lastuse; + unsigned long rate_last; /* rate limiting for ICMP */ + unsigned int rate_tokens; + int flags; +#define DST_HOST 0x0001 +#define DST_NOXFRM 0x0002 +#define DST_NOPOLICY 0x0004 +#define DST_NOHASH 0x0008 +#define DST_NOCACHE 0x0010 union { struct dst_entry *next; struct rtable __rcu *rt_next; @@ -103,10 +96,69 @@ struct dst_entry { #ifdef __KERNEL__ +extern u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old); + +#define DST_METRICS_READ_ONLY 0x1UL +#define __DST_METRICS_PTR(Y) \ + ((u32 *)((Y) & ~DST_METRICS_READ_ONLY)) +#define DST_METRICS_PTR(X) __DST_METRICS_PTR((X)->_metrics) + +static inline bool dst_metrics_read_only(const struct dst_entry *dst) +{ + return dst->_metrics & DST_METRICS_READ_ONLY; +} + +extern void __dst_destroy_metrics_generic(struct dst_entry *dst, unsigned long old); + +static inline void dst_destroy_metrics_generic(struct dst_entry *dst) +{ + unsigned long val = dst->_metrics; + if (!(val & DST_METRICS_READ_ONLY)) + __dst_destroy_metrics_generic(dst, val); +} + +static inline u32 *dst_metrics_write_ptr(struct dst_entry *dst) +{ + unsigned long p = dst->_metrics; + + if (p & DST_METRICS_READ_ONLY) + return dst->ops->cow_metrics(dst, p); + return __DST_METRICS_PTR(p); +} + +/* This may only be invoked before the entry has reached global + * visibility. + */ +static inline void dst_init_metrics(struct dst_entry *dst, + const u32 *src_metrics, + bool read_only) +{ + dst->_metrics = ((unsigned long) src_metrics) | + (read_only ? DST_METRICS_READ_ONLY : 0); +} + +static inline void dst_copy_metrics(struct dst_entry *dest, const struct dst_entry *src) +{ + u32 *dst_metrics = dst_metrics_write_ptr(dest); + + if (dst_metrics) { + u32 *src_metrics = DST_METRICS_PTR(src); + + memcpy(dst_metrics, src_metrics, RTAX_MAX * sizeof(u32)); + } +} + +static inline u32 *dst_metrics_ptr(struct dst_entry *dst) +{ + return DST_METRICS_PTR(dst); +} + static inline u32 dst_metric_raw(const struct dst_entry *dst, const int metric) { - return dst->_metrics[metric-1]; + u32 *p = DST_METRICS_PTR(dst); + + return p[metric-1]; } static inline u32 @@ -131,22 +183,10 @@ dst_metric_advmss(const struct dst_entry *dst) static inline void dst_metric_set(struct dst_entry *dst, int metric, u32 val) { - dst->_metrics[metric-1] = val; -} - -static inline void dst_import_metrics(struct dst_entry *dst, const u32 *src_metrics) -{ - memcpy(dst->_metrics, src_metrics, RTAX_MAX * sizeof(u32)); -} + u32 *p = dst_metrics_write_ptr(dst); -static inline void dst_copy_metrics(struct dst_entry *dest, const struct dst_entry *src) -{ - dst_import_metrics(dest, src->_metrics); -} - -static inline u32 *dst_metrics_ptr(struct dst_entry *dst) -{ - return dst->_metrics; + if (p) + p[metric-1] = val; } static inline u32 diff --git a/include/net/dst_ops.h b/include/net/dst_ops.h index 21a320b8708e..dc0746328947 100644 --- a/include/net/dst_ops.h +++ b/include/net/dst_ops.h @@ -18,6 +18,7 @@ struct dst_ops { struct dst_entry * (*check)(struct dst_entry *, __u32 cookie); unsigned int (*default_advmss)(const struct dst_entry *); unsigned int (*default_mtu)(const struct dst_entry *); + u32 * (*cow_metrics)(struct dst_entry *, unsigned long); void (*destroy)(struct dst_entry *); void (*ifdown)(struct dst_entry *, struct net_device *dev, int how); diff --git a/include/net/route.h b/include/net/route.h index 93e10c453f6b..5677cbf0c6e6 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -49,6 +49,7 @@ struct fib_nh; struct inet_peer; +struct fib_info; struct rtable { struct dst_entry dst; @@ -69,6 +70,7 @@ struct rtable { /* Miscellaneous cached information */ __be32 rt_spec_dst; /* RFC1122 specific destination */ struct inet_peer *peer; /* long-living peer info */ + struct fib_info *fi; /* for client ref to shared metrics */ }; static inline bool rt_is_input_route(struct rtable *rt) diff --git a/net/core/dst.c b/net/core/dst.c index b99c7c7ffce2..578893505702 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -164,6 +164,8 @@ int dst_discard(struct sk_buff *skb) } EXPORT_SYMBOL(dst_discard); +static const u32 dst_default_metrics[RTAX_MAX]; + void *dst_alloc(struct dst_ops *ops) { struct dst_entry *dst; @@ -180,6 +182,7 @@ void *dst_alloc(struct dst_ops *ops) dst->lastuse = jiffies; dst->path = dst; dst->input = dst->output = dst_discard; + dst_init_metrics(dst, dst_default_metrics, true); #if RT_CACHE_DEBUG >= 2 atomic_inc(&dst_total); #endif @@ -282,6 +285,42 @@ void dst_release(struct dst_entry *dst) } EXPORT_SYMBOL(dst_release); +u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old) +{ + u32 *p = kmalloc(sizeof(u32) * RTAX_MAX, GFP_ATOMIC); + + if (p) { + u32 *old_p = __DST_METRICS_PTR(old); + unsigned long prev, new; + + memcpy(p, old_p, sizeof(u32) * RTAX_MAX); + + new = (unsigned long) p; + prev = cmpxchg(&dst->_metrics, old, new); + + if (prev != old) { + kfree(p); + p = __DST_METRICS_PTR(prev); + if (prev & DST_METRICS_READ_ONLY) + p = NULL; + } + } + return p; +} +EXPORT_SYMBOL(dst_cow_metrics_generic); + +/* Caller asserts that dst_metrics_read_only(dst) is false. */ +void __dst_destroy_metrics_generic(struct dst_entry *dst, unsigned long old) +{ + unsigned long prev, new; + + new = (unsigned long) dst_default_metrics; + prev = cmpxchg(&dst->_metrics, old, new); + if (prev == old) + kfree(__DST_METRICS_PTR(old)); +} +EXPORT_SYMBOL(__dst_destroy_metrics_generic); + /** * skb_dst_set_noref - sets skb dst, without a reference * @skb: buffer diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 5e636365d33c..42c9c62d3417 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -112,6 +112,7 @@ static int dn_dst_gc(struct dst_ops *ops); static struct dst_entry *dn_dst_check(struct dst_entry *, __u32); static unsigned int dn_dst_default_advmss(const struct dst_entry *dst); static unsigned int dn_dst_default_mtu(const struct dst_entry *dst); +static void dn_dst_destroy(struct dst_entry *); static struct dst_entry *dn_dst_negative_advice(struct dst_entry *); static void dn_dst_link_failure(struct sk_buff *); static void dn_dst_update_pmtu(struct dst_entry *dst, u32 mtu); @@ -133,11 +134,18 @@ static struct dst_ops dn_dst_ops = { .check = dn_dst_check, .default_advmss = dn_dst_default_advmss, .default_mtu = dn_dst_default_mtu, + .cow_metrics = dst_cow_metrics_generic, + .destroy = dn_dst_destroy, .negative_advice = dn_dst_negative_advice, .link_failure = dn_dst_link_failure, .update_pmtu = dn_dst_update_pmtu, }; +static void dn_dst_destroy(struct dst_entry *dst) +{ + dst_destroy_metrics_generic(dst); +} + static __inline__ unsigned dn_hash(__le16 src, __le16 dst) { __u16 tmp = (__u16 __force)(src ^ dst); @@ -814,14 +822,14 @@ static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res) { struct dn_fib_info *fi = res->fi; struct net_device *dev = rt->dst.dev; + unsigned int mss_metric; struct neighbour *n; - unsigned int metric; if (fi) { if (DN_FIB_RES_GW(*res) && DN_FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) rt->rt_gateway = DN_FIB_RES_GW(*res); - dst_import_metrics(&rt->dst, fi->fib_metrics); + dst_init_metrics(&rt->dst, fi->fib_metrics, true); } rt->rt_type = res->type; @@ -834,10 +842,10 @@ static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res) if (dst_metric(&rt->dst, RTAX_MTU) > rt->dst.dev->mtu) dst_metric_set(&rt->dst, RTAX_MTU, rt->dst.dev->mtu); - metric = dst_metric_raw(&rt->dst, RTAX_ADVMSS); - if (metric) { + mss_metric = dst_metric_raw(&rt->dst, RTAX_ADVMSS); + if (mss_metric) { unsigned int mss = dn_mss_from_pmtu(dev, dst_mtu(&rt->dst)); - if (metric > mss) + if (mss_metric > mss) dst_metric_set(&rt->dst, RTAX_ADVMSS, mss); } return 0; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 3e5b7cc2db4f..980030d4e4ae 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -152,6 +152,36 @@ static void ipv4_dst_ifdown(struct dst_entry *dst, struct net_device *dev, { } +static u32 *ipv4_cow_metrics(struct dst_entry *dst, unsigned long old) +{ + u32 *p = kmalloc(sizeof(u32) * RTAX_MAX, GFP_ATOMIC); + + if (p) { + u32 *old_p = __DST_METRICS_PTR(old); + unsigned long prev, new; + + memcpy(p, old_p, sizeof(u32) * RTAX_MAX); + + new = (unsigned long) p; + prev = cmpxchg(&dst->_metrics, old, new); + + if (prev != old) { + kfree(p); + p = __DST_METRICS_PTR(prev); + if (prev & DST_METRICS_READ_ONLY) + p = NULL; + } else { + struct rtable *rt = (struct rtable *) dst; + + if (rt->fi) { + fib_info_put(rt->fi); + rt->fi = NULL; + } + } + } + return p; +} + static struct dst_ops ipv4_dst_ops = { .family = AF_INET, .protocol = cpu_to_be16(ETH_P_IP), @@ -159,6 +189,7 @@ static struct dst_ops ipv4_dst_ops = { .check = ipv4_dst_check, .default_advmss = ipv4_default_advmss, .default_mtu = ipv4_default_mtu, + .cow_metrics = ipv4_cow_metrics, .destroy = ipv4_dst_destroy, .ifdown = ipv4_dst_ifdown, .negative_advice = ipv4_negative_advice, @@ -1441,6 +1472,8 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, if (rt->peer) atomic_inc(&rt->peer->refcnt); + if (rt->fi) + atomic_inc(&rt->fi->fib_clntref); if (arp_bind_neighbour(&rt->dst) || !(rt->dst.neighbour->nud_state & @@ -1720,6 +1753,11 @@ static void ipv4_dst_destroy(struct dst_entry *dst) struct rtable *rt = (struct rtable *) dst; struct inet_peer *peer = rt->peer; + dst_destroy_metrics_generic(dst); + if (rt->fi) { + fib_info_put(rt->fi); + rt->fi = NULL; + } if (peer) { rt->peer = NULL; inet_putpeer(peer); @@ -1824,7 +1862,9 @@ static void rt_set_nexthop(struct rtable *rt, struct fib_result *res, u32 itag) if (FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) rt->rt_gateway = FIB_RES_GW(*res); - dst_import_metrics(dst, fi->fib_metrics); + rt->fi = fi; + atomic_inc(&fi->fib_clntref); + dst_init_metrics(dst, fi->fib_metrics, true); #ifdef CONFIG_IP_ROUTE_CLASSID dst->tclassid = FIB_RES_NH(*res).nh_tclassid; #endif @@ -2752,6 +2792,9 @@ static int ipv4_dst_blackhole(struct net *net, struct rtable **rp, struct flowi rt->peer = ort->peer; if (rt->peer) atomic_inc(&rt->peer->refcnt); + rt->fi = ort->fi; + if (rt->fi) + atomic_inc(&rt->fi->fib_clntref); dst_free(new); } diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index b057d40addec..19fbdec6baaa 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -196,8 +196,11 @@ static void xfrm4_dst_destroy(struct dst_entry *dst) { struct xfrm_dst *xdst = (struct xfrm_dst *)dst; + dst_destroy_metrics_generic(dst); + if (likely(xdst->u.rt.peer)) inet_putpeer(xdst->u.rt.peer); + xfrm_dst_destroy(xdst); } @@ -215,6 +218,7 @@ static struct dst_ops xfrm4_dst_ops = { .protocol = cpu_to_be16(ETH_P_IP), .gc = xfrm4_garbage_collect, .update_pmtu = xfrm4_update_pmtu, + .cow_metrics = dst_cow_metrics_generic, .destroy = xfrm4_dst_destroy, .ifdown = xfrm4_dst_ifdown, .local_out = __ip_local_out, diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 1534508f6c68..45fafa018f12 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -105,6 +105,7 @@ static struct dst_ops ip6_dst_ops_template = { .check = ip6_dst_check, .default_advmss = ip6_default_advmss, .default_mtu = ip6_default_mtu, + .cow_metrics = dst_cow_metrics_generic, .destroy = ip6_dst_destroy, .ifdown = ip6_dst_ifdown, .negative_advice = ip6_negative_advice, @@ -125,6 +126,10 @@ static struct dst_ops ip6_dst_blackhole_ops = { .update_pmtu = ip6_rt_blackhole_update_pmtu, }; +static const u32 ip6_template_metrics[RTAX_MAX] = { + [RTAX_HOPLIMIT - 1] = 255, +}; + static struct rt6_info ip6_null_entry_template = { .dst = { .__refcnt = ATOMIC_INIT(1), @@ -193,6 +198,7 @@ static void ip6_dst_destroy(struct dst_entry *dst) rt->rt6i_idev = NULL; in6_dev_put(idev); } + dst_destroy_metrics_generic(dst); if (peer) { BUG_ON(!(rt->rt6i_flags & RTF_CACHE)); rt->rt6i_peer = NULL; @@ -2681,7 +2687,8 @@ static int __net_init ip6_route_net_init(struct net *net) net->ipv6.ip6_null_entry->dst.path = (struct dst_entry *)net->ipv6.ip6_null_entry; net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; - dst_metric_set(&net->ipv6.ip6_null_entry->dst, RTAX_HOPLIMIT, 255); + dst_init_metrics(&net->ipv6.ip6_null_entry->dst, + ip6_template_metrics, true); #ifdef CONFIG_IPV6_MULTIPLE_TABLES net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, @@ -2692,7 +2699,8 @@ static int __net_init ip6_route_net_init(struct net *net) net->ipv6.ip6_prohibit_entry->dst.path = (struct dst_entry *)net->ipv6.ip6_prohibit_entry; net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; - dst_metric_set(&net->ipv6.ip6_prohibit_entry->dst, RTAX_HOPLIMIT, 255); + dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, + ip6_template_metrics, true); net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, sizeof(*net->ipv6.ip6_blk_hole_entry), @@ -2702,7 +2710,8 @@ static int __net_init ip6_route_net_init(struct net *net) net->ipv6.ip6_blk_hole_entry->dst.path = (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; - dst_metric_set(&net->ipv6.ip6_blk_hole_entry->dst, RTAX_HOPLIMIT, 255); + dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, + ip6_template_metrics, true); #endif net->ipv6.sysctl.flush_delay = 0; diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index da87428681cc..834dc02f1d4f 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -220,6 +220,7 @@ static void xfrm6_dst_destroy(struct dst_entry *dst) if (likely(xdst->u.rt6.rt6i_idev)) in6_dev_put(xdst->u.rt6.rt6i_idev); + dst_destroy_metrics_generic(dst); if (likely(xdst->u.rt6.rt6i_peer)) inet_putpeer(xdst->u.rt6.rt6i_peer); xfrm_dst_destroy(xdst); @@ -257,6 +258,7 @@ static struct dst_ops xfrm6_dst_ops = { .protocol = cpu_to_be16(ETH_P_IPV6), .gc = xfrm6_garbage_collect, .update_pmtu = xfrm6_update_pmtu, + .cow_metrics = dst_cow_metrics_generic, .destroy = xfrm6_dst_destroy, .ifdown = xfrm6_dst_ifdown, .local_out = __ip6_local_out, -- cgit v1.2.3 From 0dca1793063c28dde8f6c49c9c72203fe5cb6efc Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Wed, 26 Jan 2011 19:32:14 +0100 Subject: ALSA: hdspm - Add support for RME RayDAT and AIO Incorporate changes by Florian Faber into hdspm.c. Code taken from http://wiki.linuxproaudio.org/index.php/Driver:hdspe Heavily reworked to mostly comply with the coding standard (whitespace fixes, line width, C++ style comments) The code was tested and confirmed to be working on RME RayDAT. Signed-off-by: Adrian Knoth Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- include/sound/hdspm.h | 349 +++- sound/pci/rme9652/hdspm.c | 4231 +++++++++++++++++++++++++++++++++------------ 2 files changed, 3424 insertions(+), 1156 deletions(-) (limited to 'include') diff --git a/include/sound/hdspm.h b/include/sound/hdspm.h index 81990b2bcc98..c3f18194b08e 100644 --- a/include/sound/hdspm.h +++ b/include/sound/hdspm.h @@ -3,8 +3,8 @@ /* * Copyright (C) 2003 Winfried Ritsch (IEM) * based on hdsp.h from Thomas Charbonnel (thomas@undata.org) - * - * + * + * * 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 @@ -23,50 +23,41 @@ /* Maximum channels is 64 even on 56Mode you have 64playbacks to matrix */ #define HDSPM_MAX_CHANNELS 64 -/* -------------------- IOCTL Peak/RMS Meters -------------------- */ - -/* peam rms level structure like we get from hardware - - maybe in future we can memory map it so I just copy it - to user on ioctl call now an dont change anything - rms are made out of low and high values - where (long) ????_rms = (????_rms_l >> 8) + ((????_rms_h & 0xFFFFFF00)<<24) - (i asume so from the code) -*/ - -struct hdspm_peak_rms { - - unsigned int level_offset[1024]; +enum hdspm_io_type { + MADI, + MADIface, + AIO, + AES32, + RayDAT +}; - unsigned int input_peak[64]; - unsigned int playback_peak[64]; - unsigned int output_peak[64]; - unsigned int xxx_peak[64]; /* not used */ +enum hdspm_speed { + ss, + ds, + qs +}; - unsigned int reserved[256]; /* not used */ +/* -------------------- IOCTL Peak/RMS Meters -------------------- */ - unsigned int input_rms_l[64]; - unsigned int playback_rms_l[64]; - unsigned int output_rms_l[64]; - unsigned int xxx_rms_l[64]; /* not used */ +struct hdspm_peak_rms { + uint32_t input_peaks[64]; + uint32_t playback_peaks[64]; + uint32_t output_peaks[64]; - unsigned int input_rms_h[64]; - unsigned int playback_rms_h[64]; - unsigned int output_rms_h[64]; - unsigned int xxx_rms_h[64]; /* not used */ -}; + uint64_t input_rms[64]; + uint64_t playback_rms[64]; + uint64_t output_rms[64]; -struct hdspm_peak_rms_ioctl { - struct hdspm_peak_rms *peak; + uint8_t speed; /* enum {ss, ds, qs} */ + int status2; }; -/* use indirect access due to the limit of ioctl bit size */ #define SNDRV_HDSPM_IOCTL_GET_PEAK_RMS \ - _IOR('H', 0x40, struct hdspm_peak_rms_ioctl) + _IOR('H', 0x42, struct hdspm_peak_rms) /* ------------ CONFIG block IOCTL ---------------------- */ -struct hdspm_config_info { +struct hdspm_config { unsigned char pref_sync_ref; unsigned char wordclock_sync_check; unsigned char madi_sync_check; @@ -80,18 +71,121 @@ struct hdspm_config_info { unsigned int analog_out; }; -#define SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO \ - _IOR('H', 0x41, struct hdspm_config_info) +#define SNDRV_HDSPM_IOCTL_GET_CONFIG \ + _IOR('H', 0x41, struct hdspm_config) + +/** + * If there's a TCO (TimeCode Option) board installed, + * there are further options and status data available. + * The hdspm_ltc structure contains the current SMPTE + * timecode and some status information and can be + * obtained via SNDRV_HDSPM_IOCTL_GET_LTC or in the + * hdspm_status struct. + **/ + +enum hdspm_ltc_format { + format_invalid, + fps_24, + fps_25, + fps_2997, + fps_30 +}; + +enum hdspm_ltc_frame { + frame_invalid, + drop_frame, + full_frame +}; + +enum hdspm_ltc_input_format { + ntsc, + pal, + no_video +}; + +struct hdspm_ltc { + unsigned int ltc; + + enum hdspm_ltc_format format; + enum hdspm_ltc_frame frame; + enum hdspm_ltc_input_format input_format; +}; + +#define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_mixer_ioctl) + +/** + * The status data reflects the device's current state + * as determined by the card's configuration and + * connection status. + **/ + +enum hdspm_sync { + hdspm_sync_no_lock = 0, + hdspm_sync_lock = 1, + hdspm_sync_sync = 2 +}; + +enum hdspm_madi_input { + hdspm_input_optical = 0, + hdspm_input_coax = 1 +}; + +enum hdspm_madi_channel_format { + hdspm_format_ch_64 = 0, + hdspm_format_ch_56 = 1 +}; + +enum hdspm_madi_frame_format { + hdspm_frame_48 = 0, + hdspm_frame_96 = 1 +}; + +enum hdspm_syncsource { + syncsource_wc = 0, + syncsource_madi = 1, + syncsource_tco = 2, + syncsource_sync = 3, + syncsource_none = 4 +}; + +struct hdspm_status { + uint8_t card_type; /* enum hdspm_io_type */ + enum hdspm_syncsource autosync_source; + uint64_t card_clock; + uint32_t master_period; + + union { + struct { + uint8_t sync_wc; /* enum hdspm_sync */ + uint8_t sync_madi; /* enum hdspm_sync */ + uint8_t sync_tco; /* enum hdspm_sync */ + uint8_t sync_in; /* enum hdspm_sync */ + uint8_t madi_input; /* enum hdspm_madi_input */ + uint8_t channel_format; /* enum hdspm_madi_channel_format */ + uint8_t frame_format; /* enum hdspm_madi_frame_format */ + } madi; + } card_specific; +}; -/* get Soundcard Version */ +#define SNDRV_HDSPM_IOCTL_GET_STATUS \ + _IOR('H', 0x47, struct hdspm_status) + +/** + * Get information about the card and its add-ons. + **/ + +#define HDSPM_ADDON_TCO 1 struct hdspm_version { + uint8_t card_type; /* enum hdspm_io_type */ + char cardname[20]; + unsigned int serial; unsigned short firmware_rev; + int addons; }; -#define SNDRV_HDSPM_IOCTL_GET_VERSION _IOR('H', 0x43, struct hdspm_version) - +#define SNDRV_HDSPM_IOCTL_GET_VERSION _IOR('H', 0x48, struct hdspm_version) /* ------------- get Matrix Mixer IOCTL --------------- */ @@ -103,7 +197,7 @@ struct hdspm_version { /* equivalent to hardware definition, maybe for future feature of mmap of * them */ -/* each of 64 outputs has 64 infader and 64 outfader: +/* each of 64 outputs has 64 infader and 64 outfader: Ins to Outs mixer[out].in[in], Outstreams to Outs mixer[out].pb[pb] */ #define HDSPM_MIXER_CHANNELS HDSPM_MAX_CHANNELS @@ -131,4 +225,175 @@ typedef struct hdspm_version hdspm_version_t; typedef struct hdspm_channelfader snd_hdspm_channelfader_t; typedef struct hdspm_mixer hdspm_mixer_t; -#endif /* __SOUND_HDSPM_H */ +/* These tables map the ALSA channels 1..N to the channels that we + need to use in order to find the relevant channel buffer. RME + refers to this kind of mapping as between "the ADAT channel and + the DMA channel." We index it using the logical audio channel, + and the value is the DMA channel (i.e. channel buffer number) + where the data for that channel can be read/written from/to. +*/ + +char channel_map_unity_ss[HDSPM_MAX_CHANNELS] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63 +}; + +char channel_map_unity_ds[HDSPM_MAX_CHANNELS] = { + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, 50, 52, 54, 56, 58, 60, 62, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +char channel_map_unity_qs[HDSPM_MAX_CHANNELS] = { + 0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +char channel_map_raydat_ss[HDSPM_MAX_CHANNELS] = { + 4, 5, 6, 7, 8, 9, 10, 11, /* ADAT 1 */ + 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT 2 */ + 20, 21, 22, 23, 24, 25, 26, 27, /* ADAT 3 */ + 28, 29, 30, 31, 32, 33, 34, 35, /* ADAT 4 */ + 0, 1, /* AES */ + 2, 3, /* SPDIF */ + -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +char channel_map_raydat_ds[HDSPM_MAX_CHANNELS] = { + 4, 5, 6, 7, /* ADAT 1 */ + 8, 9, 10, 11, /* ADAT 2 */ + 12, 13, 14, 15, /* ADAT 3 */ + 16, 17, 18, 19, /* ADAT 4 */ + 0, 1, /* AES */ + 2, 3, /* SPDIF */ + -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +char channel_map_raydat_qs[HDSPM_MAX_CHANNELS] = { + 4, 5, /* ADAT 1 */ + 6, 7, /* ADAT 2 */ + 8, 9, /* ADAT 3 */ + 10, 11, /* ADAT 4 */ + 0, 1, /* AES */ + 2, 3, /* SPDIF */ + -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line in */ + 8, 9, /* aes in, */ + 10, 11, /* spdif in */ + 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT in */ + -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line out */ + 8, 9, /* aes out */ + 10, 11, /* spdif out */ + 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT out */ + 6, 7, /* phone out */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line in */ + 8, 9, /* aes in */ + 10, 11, /* spdif in */ + 12, 14, 16, 18, /* adat in */ + -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line out */ + 8, 9, /* aes out */ + 10, 11, /* spdif out */ + 12, 14, 16, 18, /* adat out */ + 6, 7, /* phone out */ + -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line in */ + 8, 9, /* aes in */ + 10, 11, /* spdif in */ + 12, 16, /* adat in */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line out */ + 8, 9, /* aes out */ + 10, 11, /* spdif out */ + 12, 16, /* adat out */ + 6, 7, /* phone out */ + -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +#endif diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index f5eadfc0672a..2db871d9a007 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -8,6 +8,21 @@ * Modified 2006-06-01 for AES32 support by Remy Bruno * * + * Modified 2009-04-13 for proper metering by Florian Faber + * + * + * Modified 2009-04-14 for native float support by Florian Faber + * + * + * Modified 2009-04-26 fixed bug in rms metering by Florian Faber + * + * + * Modified 2009-04-30 added hw serial number support by Florian Faber + * + * Modified 2011-01-14 added S/PDIF input on RayDATs by Adrian Knoth + * + * Modified 2011-01-25 variable period sizes on RayDAT/AIO by Adrian Knoth + * * 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 @@ -35,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -47,15 +63,6 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */ -/* Disable precise pointer at start */ -static int precise_ptr[SNDRV_CARDS]; - -/* Send all playback to line outs */ -static int line_outs_monitor[SNDRV_CARDS]; - -/* Enable Analog Outs on Channel 63/64 by default */ -static int enable_monitor[SNDRV_CARDS]; - module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for RME HDSPM interface."); @@ -65,42 +72,39 @@ MODULE_PARM_DESC(id, "ID string for RME HDSPM interface."); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable/disable specific HDSPM soundcards."); -module_param_array(precise_ptr, bool, NULL, 0444); -MODULE_PARM_DESC(precise_ptr, "Enable or disable precise pointer."); - -module_param_array(line_outs_monitor, bool, NULL, 0444); -MODULE_PARM_DESC(line_outs_monitor, - "Send playback streams to analog outs by default."); - -module_param_array(enable_monitor, bool, NULL, 0444); -MODULE_PARM_DESC(enable_monitor, - "Enable Analog Out on Channel 63/64 by default."); MODULE_AUTHOR - ("Winfried Ritsch , " - "Paul Davis , " - "Marcus Andersson, Thomas Charbonnel , " - "Remy Bruno "); +( + "Winfried Ritsch , " + "Paul Davis , " + "Marcus Andersson, Thomas Charbonnel , " + "Remy Bruno , " + "Florian Faber , " + "Adrian Knoth " +); MODULE_DESCRIPTION("RME HDSPM"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); -/* --- Write registers. --- +/* --- Write registers. --- These are defined as byte-offsets from the iobase value. */ +#define HDSPM_WR_SETTINGS 0 +#define HDSPM_outputBufferAddress 32 +#define HDSPM_inputBufferAddress 36 #define HDSPM_controlRegister 64 #define HDSPM_interruptConfirmation 96 #define HDSPM_control2Reg 256 /* not in specs ???????? */ #define HDSPM_freqReg 256 /* for AES32 */ -#define HDSPM_midiDataOut0 352 /* just believe in old code */ -#define HDSPM_midiDataOut1 356 +#define HDSPM_midiDataOut0 352 /* just believe in old code */ +#define HDSPM_midiDataOut1 356 #define HDSPM_eeprom_wr 384 /* for AES32 */ /* DMA enable for 64 channels, only Bit 0 is relevant */ -#define HDSPM_outputEnableBase 512 /* 512-767 input DMA */ +#define HDSPM_outputEnableBase 512 /* 512-767 input DMA */ #define HDSPM_inputEnableBase 768 /* 768-1023 output DMA */ -/* 16 page addresses for each of the 64 channels DMA buffer in and out +/* 16 page addresses for each of the 64 channels DMA buffer in and out (each 64k=16*4k) Buffer must be 4k aligned (which is default i386 ????) */ #define HDSPM_pageAddressBufferOut 8192 #define HDSPM_pageAddressBufferIn (HDSPM_pageAddressBufferOut+64*16*4) @@ -119,22 +123,84 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_statusRegister2 192 #define HDSPM_timecodeRegister 128 +/* AIO, RayDAT */ +#define HDSPM_RD_STATUS_0 0 +#define HDSPM_RD_STATUS_1 64 +#define HDSPM_RD_STATUS_2 128 +#define HDSPM_RD_STATUS_3 192 + +#define HDSPM_RD_TCO 256 +#define HDSPM_RD_PLL_FREQ 512 +#define HDSPM_WR_TCO 128 + +#define HDSPM_TCO1_TCO_lock 0x00000001 +#define HDSPM_TCO1_WCK_Input_Range_LSB 0x00000002 +#define HDSPM_TCO1_WCK_Input_Range_MSB 0x00000004 +#define HDSPM_TCO1_LTC_Input_valid 0x00000008 +#define HDSPM_TCO1_WCK_Input_valid 0x00000010 +#define HDSPM_TCO1_Video_Input_Format_NTSC 0x00000020 +#define HDSPM_TCO1_Video_Input_Format_PAL 0x00000040 + +#define HDSPM_TCO1_set_TC 0x00000100 +#define HDSPM_TCO1_set_drop_frame_flag 0x00000200 +#define HDSPM_TCO1_LTC_Format_LSB 0x00000400 +#define HDSPM_TCO1_LTC_Format_MSB 0x00000800 + +#define HDSPM_TCO2_TC_run 0x00010000 +#define HDSPM_TCO2_WCK_IO_ratio_LSB 0x00020000 +#define HDSPM_TCO2_WCK_IO_ratio_MSB 0x00040000 +#define HDSPM_TCO2_set_num_drop_frames_LSB 0x00080000 +#define HDSPM_TCO2_set_num_drop_frames_MSB 0x00100000 +#define HDSPM_TCO2_set_jam_sync 0x00200000 +#define HDSPM_TCO2_set_flywheel 0x00400000 + +#define HDSPM_TCO2_set_01_4 0x01000000 +#define HDSPM_TCO2_set_pull_down 0x02000000 +#define HDSPM_TCO2_set_pull_up 0x04000000 +#define HDSPM_TCO2_set_freq 0x08000000 +#define HDSPM_TCO2_set_term_75R 0x10000000 +#define HDSPM_TCO2_set_input_LSB 0x20000000 +#define HDSPM_TCO2_set_input_MSB 0x40000000 +#define HDSPM_TCO2_set_freq_from_app 0x80000000 + + +#define HDSPM_midiDataOut0 352 +#define HDSPM_midiDataOut1 356 +#define HDSPM_midiDataOut2 368 + #define HDSPM_midiDataIn0 360 #define HDSPM_midiDataIn1 364 +#define HDSPM_midiDataIn2 372 +#define HDSPM_midiDataIn3 376 /* status is data bytes in MIDI-FIFO (0-128) */ -#define HDSPM_midiStatusOut0 384 -#define HDSPM_midiStatusOut1 388 -#define HDSPM_midiStatusIn0 392 -#define HDSPM_midiStatusIn1 396 +#define HDSPM_midiStatusOut0 384 +#define HDSPM_midiStatusOut1 388 +#define HDSPM_midiStatusOut2 400 + +#define HDSPM_midiStatusIn0 392 +#define HDSPM_midiStatusIn1 396 +#define HDSPM_midiStatusIn2 404 +#define HDSPM_midiStatusIn3 408 /* the meters are regular i/o-mapped registers, but offset considerably from the rest. the peak registers are reset - when read; the least-significant 4 bits are full-scale counters; + when read; the least-significant 4 bits are full-scale counters; the actual peak value is in the most-significant 24 bits. */ -#define HDSPM_MADI_peakrmsbase 4096 /* 4096-8191 2x64x32Bit Meters */ + +#define HDSPM_MADI_INPUT_PEAK 4096 +#define HDSPM_MADI_PLAYBACK_PEAK 4352 +#define HDSPM_MADI_OUTPUT_PEAK 4608 + +#define HDSPM_MADI_INPUT_RMS_L 6144 +#define HDSPM_MADI_PLAYBACK_RMS_L 6400 +#define HDSPM_MADI_OUTPUT_RMS_L 6656 + +#define HDSPM_MADI_INPUT_RMS_H 7168 +#define HDSPM_MADI_PLAYBACK_RMS_H 7424 +#define HDSPM_MADI_OUTPUT_RMS_H 7680 /* --- Control Register bits --------- */ #define HDSPM_Start (1<<0) /* start engine */ @@ -143,7 +209,9 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_Latency1 (1<<2) /* where n is defined */ #define HDSPM_Latency2 (1<<3) /* by Latency{2,1,0} */ -#define HDSPM_ClockModeMaster (1<<4) /* 1=Master, 0=Slave/Autosync */ +#define HDSPM_ClockModeMaster (1<<4) /* 1=Master, 0=Autosync */ +#define HDSPM_c0Master 0x1 /* Master clock bit in settings + register [RayDAT, AIO] */ #define HDSPM_AudioInterruptEnable (1<<5) /* what do you think ? */ @@ -157,7 +225,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); 56channelMODE=0 */ /* MADI ONLY*/ #define HDSPM_Emphasis (1<<10) /* Emphasis */ /* AES32 ONLY */ -#define HDSPM_AutoInp (1<<11) /* Auto Input (takeover) == Safe Mode, +#define HDSPM_AutoInp (1<<11) /* Auto Input (takeover) == Safe Mode, 0=off, 1=on */ /* MADI ONLY */ #define HDSPM_Dolby (1<<11) /* Dolby = "NonAudio" ?? */ /* AES32 ONLY */ @@ -166,22 +234,23 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); */ #define HDSPM_InputSelect1 (1<<15) /* should be 0 */ -#define HDSPM_SyncRef0 (1<<16) /* 0=WOrd, 1=MADI */ -#define HDSPM_SyncRef1 (1<<17) /* for AES32: SyncRefN codes the AES # */ #define HDSPM_SyncRef2 (1<<13) #define HDSPM_SyncRef3 (1<<25) #define HDSPM_SMUX (1<<18) /* Frame ??? */ /* MADI ONY */ -#define HDSPM_clr_tms (1<<19) /* clear track marker, do not use +#define HDSPM_clr_tms (1<<19) /* clear track marker, do not use AES additional bits in lower 5 Audiodatabits ??? */ #define HDSPM_taxi_reset (1<<20) /* ??? */ /* MADI ONLY ? */ #define HDSPM_WCK48 (1<<20) /* Frame ??? = HDSPM_SMUX */ /* AES32 ONLY */ -#define HDSPM_Midi0InterruptEnable (1<<22) -#define HDSPM_Midi1InterruptEnable (1<<23) +#define HDSPM_Midi0InterruptEnable 0x0400000 +#define HDSPM_Midi1InterruptEnable 0x0800000 +#define HDSPM_Midi2InterruptEnable 0x0200000 +#define HDSPM_Midi3InterruptEnable 0x4000000 #define HDSPM_LineOut (1<<24) /* Analog Out on channel 63/64 on=1, mute=0 */ +#define HDSPe_FLOAT_FORMAT 0x2000000 #define HDSPM_DS_DoubleWire (1<<26) /* AES32 ONLY */ #define HDSPM_QS_DoubleWire (1<<27) /* AES32 ONLY */ @@ -198,11 +267,18 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_InputCoaxial (HDSPM_InputSelect0) #define HDSPM_SyncRefMask (HDSPM_SyncRef0|HDSPM_SyncRef1|\ HDSPM_SyncRef2|HDSPM_SyncRef3) -#define HDSPM_SyncRef_Word 0 -#define HDSPM_SyncRef_MADI (HDSPM_SyncRef0) -#define HDSPM_SYNC_FROM_WORD 0 /* Preferred sync reference */ -#define HDSPM_SYNC_FROM_MADI 1 /* choices - used by "pref_sync_ref" */ +#define HDSPM_c0_SyncRef0 0x2 +#define HDSPM_c0_SyncRef1 0x4 +#define HDSPM_c0_SyncRef2 0x8 +#define HDSPM_c0_SyncRef3 0x10 +#define HDSPM_c0_SyncRefMask (HDSPM_c0_SyncRef0 | HDSPM_c0_SyncRef1 |\ + HDSPM_c0_SyncRef2 | HDSPM_c0_SyncRef3) + +#define HDSPM_SYNC_FROM_WORD 0 /* Preferred sync reference */ +#define HDSPM_SYNC_FROM_MADI 1 /* choices - used by "pref_sync_ref" */ +#define HDSPM_SYNC_FROM_TCO 2 +#define HDSPM_SYNC_FROM_SYNC_IN 3 #define HDSPM_Frequency32KHz HDSPM_Frequency0 #define HDSPM_Frequency44_1KHz HDSPM_Frequency1 @@ -216,17 +292,6 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_Frequency192KHz (HDSPM_QuadSpeed|HDSPM_Frequency1|\ HDSPM_Frequency0) -/* --- for internal discrimination */ -#define HDSPM_CLOCK_SOURCE_AUTOSYNC 0 /* Sample Clock Sources */ -#define HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ 1 -#define HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ 2 -#define HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ 3 -#define HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ 4 -#define HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ 5 -#define HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ 6 -#define HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ 7 -#define HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ 8 -#define HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ 9 /* Synccheck Status */ #define HDSPM_SYNC_CHECK_NO_LOCK 0 @@ -236,14 +301,16 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); /* AutoSync References - used by "autosync_ref" control switch */ #define HDSPM_AUTOSYNC_FROM_WORD 0 #define HDSPM_AUTOSYNC_FROM_MADI 1 -#define HDSPM_AUTOSYNC_FROM_NONE 2 +#define HDSPM_AUTOSYNC_FROM_TCO 2 +#define HDSPM_AUTOSYNC_FROM_SYNC_IN 3 +#define HDSPM_AUTOSYNC_FROM_NONE 4 /* Possible sources of MADI input */ #define HDSPM_OPTICAL 0 /* optical */ #define HDSPM_COAXIAL 1 /* BNC */ #define hdspm_encode_latency(x) (((x)<<1) & HDSPM_LatencyMask) -#define hdspm_decode_latency(x) (((x) & HDSPM_LatencyMask)>>1) +#define hdspm_decode_latency(x) ((((x) & HDSPM_LatencyMask)>>1)) #define hdspm_encode_in(x) (((x)&0x3)<<14) #define hdspm_decode_in(x) (((x)>>14)&0x3) @@ -270,13 +337,21 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_AB_int (1<<2) /* InputChannel Opt=0, Coax=1 * (like inp0) */ + #define HDSPM_madiLock (1<<3) /* MADI Locked =1, no=0 */ +#define HDSPM_madiSync (1<<18) /* MADI is in sync */ + +#define HDSPM_tcoLock 0x00000020 /* Optional TCO locked status FOR HDSPe MADI! */ +#define HDSPM_tcoSync 0x10000000 /* Optional TCO sync status */ + +#define HDSPM_syncInLock 0x00010000 /* Sync In lock status FOR HDSPe MADI! */ +#define HDSPM_syncInSync 0x00020000 /* Sync In sync status FOR HDSPe MADI! */ #define HDSPM_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */ - /* since 64byte accurate last 6 bits - are not used */ + /* since 64byte accurate, last 6 bits are not used */ + + -#define HDSPM_madiSync (1<<18) /* MADI is in sync */ #define HDSPM_DoubleSpeedStatus (1<<19) /* (input) card in double speed */ #define HDSPM_madiFreq0 (1<<22) /* system freq 0=error */ @@ -287,8 +362,19 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_BufferID (1<<26) /* (Double)Buffer ID toggles with * Interrupt */ -#define HDSPM_midi0IRQPending (1<<30) /* MIDI IRQ is pending */ -#define HDSPM_midi1IRQPending (1<<31) /* and aktiv */ +#define HDSPM_tco_detect 0x08000000 +#define HDSPM_tco_lock 0x20000000 + +#define HDSPM_s2_tco_detect 0x00000040 +#define HDSPM_s2_AEBO_D 0x00000080 +#define HDSPM_s2_AEBI_D 0x00000100 + + +#define HDSPM_midi0IRQPending 0x40000000 +#define HDSPM_midi1IRQPending 0x80000000 +#define HDSPM_midi2IRQPending 0x20000000 +#define HDSPM_midi2IRQPendingAES 0x00000020 +#define HDSPM_midi3IRQPending 0x00200000 /* --- status bit helpers */ #define HDSPM_madiFreqMask (HDSPM_madiFreq0|HDSPM_madiFreq1|\ @@ -317,7 +403,10 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_wc_freq2 (1<<7) /* 100=64, 101=88.2, 110=96, */ /* missing Bit for 111=128, 1000=176.4, 1001=192 */ -#define HDSPM_SelSyncRef0 (1<<8) /* Sync Source in slave mode */ +#define HDSPM_SyncRef0 0x10000 /* Sync Reference */ +#define HDSPM_SyncRef1 0x20000 + +#define HDSPM_SelSyncRef0 (1<<8) /* AutoSync Source */ #define HDSPM_SelSyncRef1 (1<<9) /* 000=word, 001=MADI, */ #define HDSPM_SelSyncRef2 (1<<10) /* 111=no valid signal */ @@ -331,11 +420,19 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_wcFreq88_2 (HDSPM_wc_freq0|HDSPM_wc_freq2) #define HDSPM_wcFreq96 (HDSPM_wc_freq1|HDSPM_wc_freq2) +#define HDSPM_status1_F_0 0x0400000 +#define HDSPM_status1_F_1 0x0800000 +#define HDSPM_status1_F_2 0x1000000 +#define HDSPM_status1_F_3 0x2000000 +#define HDSPM_status1_freqMask (HDSPM_status1_F_0|HDSPM_status1_F_1|HDSPM_status1_F_2|HDSPM_status1_F_3) + #define HDSPM_SelSyncRefMask (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\ HDSPM_SelSyncRef2) #define HDSPM_SelSyncRef_WORD 0 #define HDSPM_SelSyncRef_MADI (HDSPM_SelSyncRef0) +#define HDSPM_SelSyncRef_TCO (HDSPM_SelSyncRef1) +#define HDSPM_SelSyncRef_SyncIn (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1) #define HDSPM_SelSyncRef_NVALID (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\ HDSPM_SelSyncRef2) @@ -345,7 +442,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); /* status */ #define HDSPM_AES32_wcLock 0x0200000 #define HDSPM_AES32_wcFreq_bit 22 -/* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function +/* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function HDSPM_bit2freq */ #define HDSPM_AES32_syncref_bit 16 /* (status >> HDSPM_AES32_syncref_bit) & 0xF gives sync source */ @@ -398,28 +495,184 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define MADI_DS_CHANNELS 32 #define MADI_QS_CHANNELS 16 +#define RAYDAT_SS_CHANNELS 36 +#define RAYDAT_DS_CHANNELS 20 +#define RAYDAT_QS_CHANNELS 12 + +#define AIO_IN_SS_CHANNELS 14 +#define AIO_IN_DS_CHANNELS 10 +#define AIO_IN_QS_CHANNELS 8 +#define AIO_OUT_SS_CHANNELS 16 +#define AIO_OUT_DS_CHANNELS 12 +#define AIO_OUT_QS_CHANNELS 10 + /* the size of a substream (1 mono data stream) */ #define HDSPM_CHANNEL_BUFFER_SAMPLES (16*1024) #define HDSPM_CHANNEL_BUFFER_BYTES (4*HDSPM_CHANNEL_BUFFER_SAMPLES) /* the size of the area we need to allocate for DMA transfers. the size is the same regardless of the number of channels, and - also the latency to use. + also the latency to use. for one direction !!! */ #define HDSPM_DMA_AREA_BYTES (HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES) #define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024) /* revisions >= 230 indicate AES32 card */ -#define HDSPM_AESREVISION 230 +#define HDSPM_MADI_REV 210 +#define HDSPM_RAYDAT_REV 211 +#define HDSPM_AIO_REV 212 +#define HDSPM_MADIFACE_REV 213 +#define HDSPM_AES_REV 240 /* speed factor modes */ #define HDSPM_SPEED_SINGLE 0 #define HDSPM_SPEED_DOUBLE 1 #define HDSPM_SPEED_QUAD 2 + /* names for speed modes */ static char *hdspm_speed_names[] = { "single", "double", "quad" }; +static char *texts_autosync_aes_tco[] = { "Word Clock", + "AES1", "AES2", "AES3", "AES4", + "AES5", "AES6", "AES7", "AES8", + "TCO" }; +static char *texts_autosync_aes[] = { "Word Clock", + "AES1", "AES2", "AES3", "AES4", + "AES5", "AES6", "AES7", "AES8" }; +static char *texts_autosync_madi_tco[] = { "Word Clock", + "MADI", "TCO", "Sync In" }; +static char *texts_autosync_madi[] = { "Word Clock", + "MADI", "Sync In" }; + +static char *texts_autosync_raydat_tco[] = { + "Word Clock", + "ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4", + "AES", "SPDIF", "TCO", "Sync In" +}; +static char *texts_autosync_raydat[] = { + "Word Clock", + "ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4", + "AES", "SPDIF", "Sync In" +}; +static char *texts_autosync_aio_tco[] = { + "Word Clock", + "ADAT", "AES", "SPDIF", "TCO", "Sync In" +}; +static char *texts_autosync_aio[] = { "Word Clock", + "ADAT", "AES", "SPDIF", "Sync In" }; + +static char *texts_freq[] = { + "No Lock", + "32 kHz", + "44.1 kHz", + "48 kHz", + "64 kHz", + "88.2 kHz", + "96 kHz", + "128 kHz", + "176.4 kHz", + "192 kHz" +}; + +static char *texts_sync_status[] = { + "no lock", + "lock", + "sync" +}; + +static char *texts_ports_madi[] = { + "MADI.1", "MADI.2", "MADI.3", "MADI.4", "MADI.5", "MADI.6", + "MADI.7", "MADI.8", "MADI.9", "MADI.10", "MADI.11", "MADI.12", + "MADI.13", "MADI.14", "MADI.15", "MADI.16", "MADI.17", "MADI.18", + "MADI.19", "MADI.20", "MADI.21", "MADI.22", "MADI.23", "MADI.24", + "MADI.25", "MADI.26", "MADI.27", "MADI.28", "MADI.29", "MADI.30", + "MADI.31", "MADI.32", "MADI.33", "MADI.34", "MADI.35", "MADI.36", + "MADI.37", "MADI.38", "MADI.39", "MADI.40", "MADI.41", "MADI.42", + "MADI.43", "MADI.44", "MADI.45", "MADI.46", "MADI.47", "MADI.48", + "MADI.49", "MADI.50", "MADI.51", "MADI.52", "MADI.53", "MADI.54", + "MADI.55", "MADI.56", "MADI.57", "MADI.58", "MADI.59", "MADI.60", + "MADI.61", "MADI.62", "MADI.63", "MADI.64", +}; + + +static char *texts_ports_raydat_ss[] = { + "ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4", "ADAT1.5", "ADAT1.6", + "ADAT1.7", "ADAT1.8", "ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4", + "ADAT2.5", "ADAT2.6", "ADAT2.7", "ADAT2.8", "ADAT3.1", "ADAT3.2", + "ADAT3.3", "ADAT3.4", "ADAT3.5", "ADAT3.6", "ADAT3.7", "ADAT3.8", + "ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4", "ADAT4.5", "ADAT4.6", + "ADAT4.7", "ADAT4.8", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R" +}; + +static char *texts_ports_raydat_ds[] = { + "ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4", + "ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4", + "ADAT3.1", "ADAT3.2", "ADAT3.3", "ADAT3.4", + "ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R" +}; + +static char *texts_ports_raydat_qs[] = { + "ADAT1.1", "ADAT1.2", + "ADAT2.1", "ADAT2.2", + "ADAT3.1", "ADAT3.2", + "ADAT4.1", "ADAT4.2", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R" +}; + + +static char *texts_ports_aio_in_ss[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6", + "ADAT.7", "ADAT.8" +}; + +static char *texts_ports_aio_out_ss[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6", + "ADAT.7", "ADAT.8", + "Phone.L", "Phone.R" +}; + +static char *texts_ports_aio_in_ds[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4" +}; + +static char *texts_ports_aio_out_ds[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", + "Phone.L", "Phone.R" +}; + +static char *texts_ports_aio_in_qs[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4" +}; + +static char *texts_ports_aio_out_qs[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", + "Phone.L", "Phone.R" +}; + struct hdspm_midi { struct hdspm *hdspm; int id; @@ -430,6 +683,21 @@ struct hdspm_midi { struct timer_list timer; spinlock_t lock; int pending; + int dataIn; + int statusIn; + int dataOut; + int statusOut; + int ie; + int irq; +}; + +struct hdspm_tco { + int input; + int framerate; + int wordclock; + int samplerate; + int pull; + int term; /* 0 = off, 1 = on */ }; struct hdspm { @@ -441,21 +709,39 @@ struct hdspm { char *card_name; /* for procinfo */ unsigned short firmware_rev; /* dont know if relevant (yes if AES32)*/ - unsigned char is_aes32; /* indicates if card is AES32 */ + uint8_t io_type; - int precise_ptr; /* use precise pointers, to be tested */ int monitor_outs; /* set up monitoring outs init flag */ u32 control_register; /* cached value */ u32 control2_register; /* cached value */ + u32 settings_register; - struct hdspm_midi midi[2]; + struct hdspm_midi midi[4]; struct tasklet_struct midi_tasklet; size_t period_bytes; - unsigned char ss_channels; /* channels of card in single speed */ - unsigned char ds_channels; /* Double Speed */ - unsigned char qs_channels; /* Quad Speed */ + unsigned char ss_in_channels; + unsigned char ds_in_channels; + unsigned char qs_in_channels; + unsigned char ss_out_channels; + unsigned char ds_out_channels; + unsigned char qs_out_channels; + + unsigned char max_channels_in; + unsigned char max_channels_out; + + char *channel_map_in; + char *channel_map_out; + + char *channel_map_in_ss, *channel_map_in_ds, *channel_map_in_qs; + char *channel_map_out_ss, *channel_map_out_ds, *channel_map_out_qs; + + char **port_names_in; + char **port_names_out; + + char **port_names_in_ss, **port_names_in_ds, **port_names_in_qs; + char **port_names_out_ss, **port_names_out_ds, **port_names_out_qs; unsigned char *playback_buffer; /* suitably aligned address */ unsigned char *capture_buffer; /* suitably aligned address */ @@ -468,14 +754,13 @@ struct hdspm { int last_internal_sample_rate; int system_sample_rate; - char *channel_map; /* channel map for DS and Quadspeed */ - int dev; /* Hardware vars... */ int irq; unsigned long port; void __iomem *iobase; int irq_count; /* for debug */ + int midiPorts; struct snd_card *card; /* one card */ struct snd_pcm *pcm; /* has one pcm */ @@ -487,28 +772,15 @@ struct hdspm { struct snd_kcontrol *playback_mixer_ctls[HDSPM_MAX_CHANNELS]; /* but input to much, so not used */ struct snd_kcontrol *input_mixer_ctls[HDSPM_MAX_CHANNELS]; - /* full mixer accessible over mixer ioctl or hwdep-device */ + /* full mixer accessable over mixer ioctl or hwdep-device */ struct hdspm_mixer *mixer; -}; + struct hdspm_tco *tco; /* NULL if no TCO detected */ -/* These tables map the ALSA channels 1..N to the channels that we - need to use in order to find the relevant channel buffer. RME - refer to this kind of mapping as between "the ADAT channel and - the DMA channel." We index it using the logical audio channel, - and the value is the DMA channel (i.e. channel buffer number) - where the data for that channel can be read/written from/to. -*/ + char **texts_autosync; + int texts_autosync_items; -static char channel_map_madi_ss[HDSPM_MAX_CHANNELS] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63 + cycles_t last_interrupt; }; @@ -532,11 +804,11 @@ static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card, static int __devinit snd_hdspm_create_pcm(struct snd_card *card, struct hdspm * hdspm); -static inline void snd_hdspm_initialize_midi_flush(struct hdspm * hdspm); -static int hdspm_update_simple_mixer_controls(struct hdspm * hdspm); -static int hdspm_autosync_ref(struct hdspm * hdspm); -static int snd_hdspm_set_defaults(struct hdspm * hdspm); -static void hdspm_set_sgbuf(struct hdspm * hdspm, +static inline void snd_hdspm_initialize_midi_flush(struct hdspm *hdspm); +static int hdspm_update_simple_mixer_controls(struct hdspm *hdspm); +static int hdspm_autosync_ref(struct hdspm *hdspm); +static int snd_hdspm_set_defaults(struct hdspm *hdspm); +static void hdspm_set_sgbuf(struct hdspm *hdspm, struct snd_pcm_substream *substream, unsigned int reg, int channels); @@ -550,7 +822,7 @@ static inline int HDSPM_bit2freq(int n) return bit2freq_tab[n]; } -/* Write/read to/from HDSPM with Addresses in Bytes +/* Write/read to/from HDSPM with Adresses in Bytes not words but only 32Bit writes are allowed */ static inline void hdspm_write(struct hdspm * hdspm, unsigned int reg, @@ -564,8 +836,8 @@ static inline unsigned int hdspm_read(struct hdspm * hdspm, unsigned int reg) return readl(hdspm->iobase + reg); } -/* for each output channel (chan) I have an Input (in) and Playback (pb) Fader - mixer is write only on hardware so we have to cache him for read +/* for each output channel (chan) I have an Input (in) and Playback (pb) Fader + mixer is write only on hardware so we have to cache him for read each fader is a u32, but uses only the first 16 bit */ static inline int hdspm_read_in_gain(struct hdspm * hdspm, unsigned int chan, @@ -641,30 +913,67 @@ static int snd_hdspm_use_is_exclusive(struct hdspm *hdspm) /* check for external sample rate */ static int hdspm_external_sample_rate(struct hdspm *hdspm) { - if (hdspm->is_aes32) { - unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); - unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); - unsigned int timecode = - hdspm_read(hdspm, HDSPM_timecodeRegister); + unsigned int status, status2, timecode; + int syncref, rate = 0, rate_bits; - int syncref = hdspm_autosync_ref(hdspm); + switch (hdspm->io_type) { + case AES32: + status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + status = hdspm_read(hdspm, HDSPM_statusRegister); + timecode = hdspm_read(hdspm, HDSPM_timecodeRegister); + + syncref = hdspm_autosync_ref(hdspm); if (syncref == HDSPM_AES32_AUTOSYNC_FROM_WORD && status & HDSPM_AES32_wcLock) - return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) - & 0xF); + return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF); + if (syncref >= HDSPM_AES32_AUTOSYNC_FROM_AES1 && - syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 && - status2 & (HDSPM_LockAES >> - (syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1))) - return HDSPM_bit2freq((timecode >> - (4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF); + syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 && + status2 & (HDSPM_LockAES >> + (syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1))) + return HDSPM_bit2freq((timecode >> (4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF); return 0; - } else { - unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); - unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); - unsigned int rate_bits; - int rate = 0; + break; + + case MADIface: + status = hdspm_read(hdspm, HDSPM_statusRegister); + + if (!(status & HDSPM_madiLock)) { + rate = 0; /* no lock */ + } else { + switch (status & (HDSPM_status1_freqMask)) { + case HDSPM_status1_F_0*1: + rate = 32000; break; + case HDSPM_status1_F_0*2: + rate = 44100; break; + case HDSPM_status1_F_0*3: + rate = 48000; break; + case HDSPM_status1_F_0*4: + rate = 64000; break; + case HDSPM_status1_F_0*5: + rate = 88200; break; + case HDSPM_status1_F_0*6: + rate = 96000; break; + case HDSPM_status1_F_0*7: + rate = 128000; break; + case HDSPM_status1_F_0*8: + rate = 176400; break; + case HDSPM_status1_F_0*9: + rate = 192000; break; + default: + rate = 0; break; + } + } + + break; + + case MADI: + case AIO: + case RayDAT: + status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + status = hdspm_read(hdspm, HDSPM_statusRegister); + rate = 0; /* if wordclock has synced freq and wordclock is valid */ if ((status2 & HDSPM_wcLock) != 0 && @@ -672,6 +981,7 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm) rate_bits = status2 & HDSPM_wcFreqMask; + switch (rate_bits) { case HDSPM_wcFreq32: rate = 32000; @@ -691,7 +1001,6 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm) case HDSPM_wcFreq96: rate = 96000; break; - /* Quadspeed Bit missing ???? */ default: rate = 0; break; @@ -702,10 +1011,10 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm) * word has priority to MADI */ if (rate != 0 && - (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD) + (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD) return rate; - /* maby a madi input (which is taken if sel sync is madi) */ + /* maybe a madi input (which is taken if sel sync is madi) */ if (status & HDSPM_madiLock) { rate_bits = status & HDSPM_madiFreqMask; @@ -742,36 +1051,26 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm) break; } } - return rate; + break; } + + return rate; } /* Latency function */ -static inline void hdspm_compute_period_size(struct hdspm * hdspm) +static inline void hdspm_compute_period_size(struct hdspm *hdspm) { - hdspm->period_bytes = - 1 << ((hdspm_decode_latency(hdspm->control_register) + 8)); + hdspm->period_bytes = 1 << ((hdspm_decode_latency(hdspm->control_register) + 8)); } -static snd_pcm_uframes_t hdspm_hw_pointer(struct hdspm * hdspm) + +static snd_pcm_uframes_t hdspm_hw_pointer(struct hdspm *hdspm) { int position; position = hdspm_read(hdspm, HDSPM_statusRegister); - - if (!hdspm->precise_ptr) - return (position & HDSPM_BufferID) ? - (hdspm->period_bytes / 4) : 0; - - /* hwpointer comes in bytes and is 64Bytes accurate (by docu since - PCI Burst) - i have experimented that it is at most 64 Byte to much for playing - so substraction of 64 byte should be ok for ALSA, but use it only - for application where you know what you do since if you come to - near with record pointer it can be a disaster */ - position &= HDSPM_BufferPositionMask; - position = ((position - 64) % (2 * hdspm->period_bytes)) / 4; + position /= 4; /* Bytes per sample */ return position; } @@ -805,7 +1104,7 @@ static void hdspm_silence_playback(struct hdspm *hdspm) } } -static int hdspm_set_interrupt_interval(struct hdspm * s, unsigned int frames) +static int hdspm_set_interrupt_interval(struct hdspm *s, unsigned int frames) { int n; @@ -829,21 +1128,53 @@ static int hdspm_set_interrupt_interval(struct hdspm * s, unsigned int frames) return 0; } +static u64 hdspm_calc_dds_value(struct hdspm *hdspm, u64 period) +{ + u64 freq_const; + + if (period == 0) + return 0; + + switch (hdspm->io_type) { + case MADI: + case AES32: + freq_const = 110069313433624ULL; + break; + case RayDAT: + case AIO: + freq_const = 104857600000000ULL; + break; + case MADIface: + freq_const = 131072000000000ULL; + } + + return div_u64(freq_const, period); +} + + static void hdspm_set_dds_value(struct hdspm *hdspm, int rate) { u64 n; - + if (rate >= 112000) rate /= 4; else if (rate >= 56000) rate /= 2; - /* RME says n = 104857600000000, but in the windows MADI driver, I see: -// return 104857600000000 / rate; // 100 MHz - return 110100480000000 / rate; // 105 MHz - */ - /* n = 104857600000000ULL; */ /* = 2^20 * 10^8 */ - n = 110100480000000ULL; /* Value checked for AES32 and MADI */ + switch (hdspm->io_type) { + case MADIface: + n = 131072000000000ULL; /* 125 MHz */ + break; + case MADI: + case AES32: + n = 110069313433624ULL; /* 105 MHz */ + break; + case RayDAT: + case AIO: + n = 104857600000000ULL; /* 100 MHz */ + break; + } + n = div_u64(n, rate); /* n should be less than 2^32 for being written to FREQ register */ snd_BUG_ON(n >> 32); @@ -864,13 +1195,13 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally) if (!(hdspm->control_register & HDSPM_ClockModeMaster)) { - /* SLAVE --- */ + /* SLAVE --- */ if (called_internally) { - /* request from ctl or card initialization - just make a warning an remember setting - for future master mode switching */ - + /* request from ctl or card initialization + just make a warning an remember setting + for future master mode switching */ + snd_printk(KERN_WARNING "HDSPM: " "Warning: device is not running " "as a clock master.\n"); @@ -907,7 +1238,7 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally) Note that a similar but essentially insoluble problem exists for externally-driven rate changes. All we can do is to flag rate - changes in the read/write routines. + changes in the read/write routines. */ if (current_rate <= 48000) @@ -975,16 +1306,35 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally) /* For AES32, need to set DDS value in FREQ register For MADI, also apparently */ hdspm_set_dds_value(hdspm, rate); - - if (hdspm->is_aes32 && rate != current_rate) + + if (AES32 == hdspm->io_type && rate != current_rate) hdspm_write(hdspm, HDSPM_eeprom_wr, 0); - - /* For AES32 and for MADI (at least rev 204), channel_map needs to - * always be channel_map_madi_ss, whatever the sample rate */ - hdspm->channel_map = channel_map_madi_ss; hdspm->system_sample_rate = rate; + if (rate <= 48000) { + hdspm->channel_map_in = hdspm->channel_map_in_ss; + hdspm->channel_map_out = hdspm->channel_map_out_ss; + hdspm->max_channels_in = hdspm->ss_in_channels; + hdspm->max_channels_out = hdspm->ss_out_channels; + hdspm->port_names_in = hdspm->port_names_in_ss; + hdspm->port_names_out = hdspm->port_names_out_ss; + } else if (rate <= 96000) { + hdspm->channel_map_in = hdspm->channel_map_in_ds; + hdspm->channel_map_out = hdspm->channel_map_out_ds; + hdspm->max_channels_in = hdspm->ds_in_channels; + hdspm->max_channels_out = hdspm->ds_out_channels; + hdspm->port_names_in = hdspm->port_names_in_ds; + hdspm->port_names_out = hdspm->port_names_out_ds; + } else { + hdspm->channel_map_in = hdspm->channel_map_in_qs; + hdspm->channel_map_out = hdspm->channel_map_out_qs; + hdspm->max_channels_in = hdspm->qs_in_channels; + hdspm->max_channels_out = hdspm->qs_out_channels; + hdspm->port_names_in = hdspm->port_names_in_qs; + hdspm->port_names_out = hdspm->port_names_out_qs; + } + if (not_set != 0) return -1; @@ -1019,39 +1369,26 @@ static inline unsigned char snd_hdspm_midi_read_byte (struct hdspm *hdspm, int id) { /* the hardware already does the relevant bit-mask with 0xff */ - if (id) - return hdspm_read(hdspm, HDSPM_midiDataIn1); - else - return hdspm_read(hdspm, HDSPM_midiDataIn0); + return hdspm_read(hdspm, hdspm->midi[id].dataIn); } static inline void snd_hdspm_midi_write_byte (struct hdspm *hdspm, int id, int val) { /* the hardware already does the relevant bit-mask with 0xff */ - if (id) - hdspm_write(hdspm, HDSPM_midiDataOut1, val); - else - hdspm_write(hdspm, HDSPM_midiDataOut0, val); + return hdspm_write(hdspm, hdspm->midi[id].dataOut, val); } static inline int snd_hdspm_midi_input_available (struct hdspm *hdspm, int id) { - if (id) - return (hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xff); - else - return (hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xff); + return hdspm_read(hdspm, hdspm->midi[id].statusIn) & 0xFF; } static inline int snd_hdspm_midi_output_possible (struct hdspm *hdspm, int id) { int fifo_bytes_used; - if (id) - fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut1); - else - fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut0); - fifo_bytes_used &= 0xff; + fifo_bytes_used = hdspm_read(hdspm, hdspm->midi[id].statusOut) & 0xFF; if (fifo_bytes_used < 128) return 128 - fifo_bytes_used; @@ -1074,7 +1411,7 @@ static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi) unsigned char buf[128]; /* Output is not interrupt driven */ - + spin_lock_irqsave (&hmidi->lock, flags); if (hmidi->output && !snd_rawmidi_transmit_empty (hmidi->output)) { @@ -1083,11 +1420,11 @@ static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi) if (n_pending > 0) { if (n_pending > (int)sizeof (buf)) n_pending = sizeof (buf); - + to_write = snd_rawmidi_transmit (hmidi->output, buf, n_pending); if (to_write > 0) { - for (i = 0; i < to_write; ++i) + for (i = 0; i < to_write; ++i) snd_hdspm_midi_write_byte (hmidi->hdspm, hmidi->id, buf[i]); @@ -1127,12 +1464,11 @@ static int snd_hdspm_midi_input_read (struct hdspm_midi *hmidi) } } hmidi->pending = 0; - if (hmidi->id) - hmidi->hdspm->control_register |= HDSPM_Midi1InterruptEnable; - else - hmidi->hdspm->control_register |= HDSPM_Midi0InterruptEnable; + + hmidi->hdspm->control_register |= hmidi->ie; hdspm_write(hmidi->hdspm, HDSPM_controlRegister, hmidi->hdspm->control_register); + spin_unlock_irqrestore (&hmidi->lock, flags); return snd_hdspm_midi_output_write (hmidi); } @@ -1143,20 +1479,18 @@ snd_hdspm_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) struct hdspm *hdspm; struct hdspm_midi *hmidi; unsigned long flags; - u32 ie; hmidi = substream->rmidi->private_data; hdspm = hmidi->hdspm; - ie = hmidi->id ? - HDSPM_Midi1InterruptEnable : HDSPM_Midi0InterruptEnable; + spin_lock_irqsave (&hdspm->lock, flags); if (up) { - if (!(hdspm->control_register & ie)) { + if (!(hdspm->control_register & hmidi->ie)) { snd_hdspm_flush_midi_input (hdspm, hmidi->id); - hdspm->control_register |= ie; + hdspm->control_register |= hmidi->ie; } } else { - hdspm->control_register &= ~ie; + hdspm->control_register &= ~hmidi->ie; } hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); @@ -1167,14 +1501,14 @@ static void snd_hdspm_midi_output_timer(unsigned long data) { struct hdspm_midi *hmidi = (struct hdspm_midi *) data; unsigned long flags; - + snd_hdspm_midi_output_write(hmidi); spin_lock_irqsave (&hmidi->lock, flags); /* this does not bump hmidi->istimer, because the kernel automatically removed the timer when it expired, and we are now adding it back, thus - leaving istimer wherever it was set before. + leaving istimer wherever it was set before. */ if (hmidi->istimer) { @@ -1288,22 +1622,103 @@ static int __devinit snd_hdspm_create_midi (struct snd_card *card, hdspm->midi[id].hdspm = hdspm; spin_lock_init (&hdspm->midi[id].lock); - sprintf (buf, "%s MIDI %d", card->shortname, id+1); - err = snd_rawmidi_new (card, buf, id, 1, 1, &hdspm->midi[id].rmidi); - if (err < 0) - return err; + if (0 == id) { + if (MADIface == hdspm->io_type) { + /* MIDI-over-MADI on HDSPe MADIface */ + hdspm->midi[0].dataIn = HDSPM_midiDataIn2; + hdspm->midi[0].statusIn = HDSPM_midiStatusIn2; + hdspm->midi[0].dataOut = HDSPM_midiDataOut2; + hdspm->midi[0].statusOut = HDSPM_midiStatusOut2; + hdspm->midi[0].ie = HDSPM_Midi2InterruptEnable; + hdspm->midi[0].irq = HDSPM_midi2IRQPending; + } else { + hdspm->midi[0].dataIn = HDSPM_midiDataIn0; + hdspm->midi[0].statusIn = HDSPM_midiStatusIn0; + hdspm->midi[0].dataOut = HDSPM_midiDataOut0; + hdspm->midi[0].statusOut = HDSPM_midiStatusOut0; + hdspm->midi[0].ie = HDSPM_Midi0InterruptEnable; + hdspm->midi[0].irq = HDSPM_midi0IRQPending; + } + } else if (1 == id) { + hdspm->midi[1].dataIn = HDSPM_midiDataIn1; + hdspm->midi[1].statusIn = HDSPM_midiStatusIn1; + hdspm->midi[1].dataOut = HDSPM_midiDataOut1; + hdspm->midi[1].statusOut = HDSPM_midiStatusOut1; + hdspm->midi[1].ie = HDSPM_Midi1InterruptEnable; + hdspm->midi[1].irq = HDSPM_midi1IRQPending; + } else if ((2 == id) && (MADI == hdspm->io_type)) { + /* MIDI-over-MADI on HDSPe MADI */ + hdspm->midi[2].dataIn = HDSPM_midiDataIn2; + hdspm->midi[2].statusIn = HDSPM_midiStatusIn2; + hdspm->midi[2].dataOut = HDSPM_midiDataOut2; + hdspm->midi[2].statusOut = HDSPM_midiStatusOut2; + hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable; + hdspm->midi[2].irq = HDSPM_midi2IRQPending; + } else if (2 == id) { + /* TCO MTC, read only */ + hdspm->midi[2].dataIn = HDSPM_midiDataIn2; + hdspm->midi[2].statusIn = HDSPM_midiStatusIn2; + hdspm->midi[2].dataOut = -1; + hdspm->midi[2].statusOut = -1; + hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable; + hdspm->midi[2].irq = HDSPM_midi2IRQPendingAES; + } else if (3 == id) { + /* TCO MTC on HDSPe MADI */ + hdspm->midi[3].dataIn = HDSPM_midiDataIn3; + hdspm->midi[3].statusIn = HDSPM_midiStatusIn3; + hdspm->midi[3].dataOut = -1; + hdspm->midi[3].statusOut = -1; + hdspm->midi[3].ie = HDSPM_Midi3InterruptEnable; + hdspm->midi[3].irq = HDSPM_midi3IRQPending; + } + + if ((id < 2) || ((2 == id) && ((MADI == hdspm->io_type) || + (MADIface == hdspm->io_type)))) { + if ((id == 0) && (MADIface == hdspm->io_type)) { + sprintf(buf, "%s MIDIoverMADI", card->shortname); + } else if ((id == 2) && (MADI == hdspm->io_type)) { + sprintf(buf, "%s MIDIoverMADI", card->shortname); + } else { + sprintf(buf, "%s MIDI %d", card->shortname, id+1); + } + err = snd_rawmidi_new(card, buf, id, 1, 1, + &hdspm->midi[id].rmidi); + if (err < 0) + return err; - sprintf(hdspm->midi[id].rmidi->name, "HDSPM MIDI %d", id+1); - hdspm->midi[id].rmidi->private_data = &hdspm->midi[id]; + sprintf(hdspm->midi[id].rmidi->name, "%s MIDI %d", + card->id, id+1); + hdspm->midi[id].rmidi->private_data = &hdspm->midi[id]; + + snd_rawmidi_set_ops(hdspm->midi[id].rmidi, + SNDRV_RAWMIDI_STREAM_OUTPUT, + &snd_hdspm_midi_output); + snd_rawmidi_set_ops(hdspm->midi[id].rmidi, + SNDRV_RAWMIDI_STREAM_INPUT, + &snd_hdspm_midi_input); + + hdspm->midi[id].rmidi->info_flags |= + SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + } else { + /* TCO MTC, read only */ + sprintf(buf, "%s MTC %d", card->shortname, id+1); + err = snd_rawmidi_new(card, buf, id, 1, 1, + &hdspm->midi[id].rmidi); + if (err < 0) + return err; + + sprintf(hdspm->midi[id].rmidi->name, + "%s MTC %d", card->id, id+1); + hdspm->midi[id].rmidi->private_data = &hdspm->midi[id]; - snd_rawmidi_set_ops(hdspm->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, - &snd_hdspm_midi_output); - snd_rawmidi_set_ops(hdspm->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_INPUT, - &snd_hdspm_midi_input); + snd_rawmidi_set_ops(hdspm->midi[id].rmidi, + SNDRV_RAWMIDI_STREAM_INPUT, + &snd_hdspm_midi_input); - hdspm->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | - SNDRV_RAWMIDI_INFO_INPUT | - SNDRV_RAWMIDI_INFO_DUPLEX; + hdspm->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; + } return 0; } @@ -1312,12 +1727,15 @@ static int __devinit snd_hdspm_create_midi (struct snd_card *card, static void hdspm_midi_tasklet(unsigned long arg) { struct hdspm *hdspm = (struct hdspm *)arg; - - if (hdspm->midi[0].pending) - snd_hdspm_midi_input_read (&hdspm->midi[0]); - if (hdspm->midi[1].pending) - snd_hdspm_midi_input_read (&hdspm->midi[1]); -} + int i = 0; + + while (i < hdspm->midiPorts) { + if (hdspm->midi[i].pending) + snd_hdspm_midi_input_read(&hdspm->midi[i]); + + i++; + } +} /*----------------------------------------------------------------------------- @@ -1326,6 +1744,22 @@ static void hdspm_midi_tasklet(unsigned long arg) /* get the system sample rate which is set */ + +/** + * Calculate the real sample rate from the + * current DDS value. + **/ +static int hdspm_get_system_sample_rate(struct hdspm *hdspm) +{ + unsigned int period, rate; + + period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ); + rate = hdspm_calc_dds_value(hdspm, period); + + return rate; +} + + #define HDSPM_SYSTEM_SAMPLE_RATE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -1340,112 +1774,251 @@ static int snd_hdspm_info_system_sample_rate(struct snd_kcontrol *kcontrol, { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; + uinfo->value.integer.min = 27000; + uinfo->value.integer.max = 207000; + uinfo->value.integer.step = 1; return 0; } + static int snd_hdspm_get_system_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value * ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - ucontrol->value.enumerated.item[0] = hdspm->system_sample_rate; + ucontrol->value.integer.value[0] = hdspm_get_system_sample_rate(hdspm); + return 0; +} + + +/** + * Returns the WordClock sample rate class for the given card. + **/ +static int hdspm_get_wc_sample_rate(struct hdspm *hdspm) +{ + int status; + + switch (hdspm->io_type) { + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); + return (status >> 16) & 0xF; + break; + default: + break; + } + + + return 0; +} + + +/** + * Returns the TCO sample rate class for the given card. + **/ +static int hdspm_get_tco_sample_rate(struct hdspm *hdspm) +{ + int status; + + if (hdspm->tco) { + switch (hdspm->io_type) { + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); + return (status >> 20) & 0xF; + break; + default: + break; + } + } + + return 0; +} + + +/** + * Returns the SYNC_IN sample rate class for the given card. + **/ +static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm) +{ + int status; + + if (hdspm->tco) { + switch (hdspm->io_type) { + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_RD_STATUS_2); + return (status >> 12) & 0xF; + break; + default: + break; + } + } + return 0; } + +/** + * Returns the sample rate class for input source for + * 'new style' cards like the AIO and RayDAT. + **/ +static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx) +{ + int status = hdspm_read(hdspm, HDSPM_RD_STATUS_2); + + return (status >> (idx*4)) & 0xF; +} + + + #define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .access = SNDRV_CTL_ELEM_ACCESS_READ, \ - .info = snd_hdspm_info_autosync_sample_rate, \ - .get = snd_hdspm_get_autosync_sample_rate \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .private_value = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ, \ + .info = snd_hdspm_info_autosync_sample_rate, \ + .get = snd_hdspm_get_autosync_sample_rate \ } + static int snd_hdspm_info_autosync_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "32000", "44100", "48000", - "64000", "88200", "96000", - "128000", "176400", "192000", - "None" - }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 10; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); + texts_freq[uinfo->value.enumerated.item]); return 0; } + static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value * ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - switch (hdspm_external_sample_rate(hdspm)) { - case 32000: - ucontrol->value.enumerated.item[0] = 0; - break; - case 44100: - ucontrol->value.enumerated.item[0] = 1; - break; - case 48000: - ucontrol->value.enumerated.item[0] = 2; - break; - case 64000: - ucontrol->value.enumerated.item[0] = 3; - break; - case 88200: - ucontrol->value.enumerated.item[0] = 4; - break; - case 96000: - ucontrol->value.enumerated.item[0] = 5; - break; - case 128000: - ucontrol->value.enumerated.item[0] = 6; - break; - case 176400: - ucontrol->value.enumerated.item[0] = 7; - break; - case 192000: - ucontrol->value.enumerated.item[0] = 8; - break; + switch (hdspm->io_type) { + case RayDAT: + switch (kcontrol->private_value) { + case 0: + ucontrol->value.enumerated.item[0] = + hdspm_get_wc_sample_rate(hdspm); + break; + case 7: + ucontrol->value.enumerated.item[0] = + hdspm_get_tco_sample_rate(hdspm); + break; + case 8: + ucontrol->value.enumerated.item[0] = + hdspm_get_sync_in_sample_rate(hdspm); + break; + default: + ucontrol->value.enumerated.item[0] = + hdspm_get_s1_sample_rate(hdspm, + kcontrol->private_value-1); + } + case AIO: + switch (kcontrol->private_value) { + case 0: /* WC */ + ucontrol->value.enumerated.item[0] = + hdspm_get_wc_sample_rate(hdspm); + break; + case 4: /* TCO */ + ucontrol->value.enumerated.item[0] = + hdspm_get_tco_sample_rate(hdspm); + break; + case 5: /* SYNC_IN */ + ucontrol->value.enumerated.item[0] = + hdspm_get_sync_in_sample_rate(hdspm); + break; + default: + ucontrol->value.enumerated.item[0] = + hdspm_get_s1_sample_rate(hdspm, + ucontrol->id.index-1); + } default: - ucontrol->value.enumerated.item[0] = 9; + break; } - return 0; -} -#define HDSPM_SYSTEM_CLOCK_MODE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .access = SNDRV_CTL_ELEM_ACCESS_READ, \ - .info = snd_hdspm_info_system_clock_mode, \ - .get = snd_hdspm_get_system_clock_mode, \ + return 0; } +#define HDSPM_SYSTEM_CLOCK_MODE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_system_clock_mode, \ + .get = snd_hdspm_get_system_clock_mode, \ + .put = snd_hdspm_put_system_clock_mode, \ +} + + +/** + * Returns the system clock mode for the given card. + * @returns 0 - master, 1 - slave + **/ +static int hdspm_system_clock_mode(struct hdspm *hdspm) +{ + switch (hdspm->io_type) { + case AIO: + case RayDAT: + if (hdspm->settings_register & HDSPM_c0Master) + return 0; + break; -static int hdspm_system_clock_mode(struct hdspm * hdspm) -{ - /* Always reflect the hardware info, rme is never wrong !!!! */ + default: + if (hdspm->control_register & HDSPM_ClockModeMaster) + return 0; + } - if (hdspm->control_register & HDSPM_ClockModeMaster) - return 0; return 1; } -static int snd_hdspm_info_system_clock_mode(struct snd_kcontrol *kcontrol, + +/** + * Sets the system clock mode. + * @param mode 0 - master, 1 - slave + **/ +static void hdspm_set_system_clock_mode(struct hdspm *hdspm, int mode) +{ + switch (hdspm->io_type) { + case AIO: + case RayDAT: + if (0 == mode) + hdspm->settings_register |= HDSPM_c0Master; + else + hdspm->settings_register &= ~HDSPM_c0Master; + + hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register); + break; + + default: + if (0 == mode) + hdspm->control_register |= HDSPM_ClockModeMaster; + else + hdspm->control_register &= ~HDSPM_ClockModeMaster; + + hdspm_write(hdspm, HDSPM_controlRegister, + hdspm->control_register); + } +} + + +static int snd_hdspm_info_system_clock_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Master", "Slave" }; + static char *texts[] = { "Master", "AutoSync" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; @@ -1463,96 +2036,83 @@ static int snd_hdspm_get_system_clock_mode(struct snd_kcontrol *kcontrol, { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - ucontrol->value.enumerated.item[0] = - hdspm_system_clock_mode(hdspm); + ucontrol->value.enumerated.item[0] = hdspm_system_clock_mode(hdspm); return 0; } -#define HDSPM_CLOCK_SOURCE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .info = snd_hdspm_info_clock_source, \ - .get = snd_hdspm_get_clock_source, \ - .put = snd_hdspm_put_clock_source \ +static int snd_hdspm_put_system_clock_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + int val; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + + val = ucontrol->value.enumerated.item[0]; + if (val < 0) + val = 0; + else if (val > 1) + val = 1; + + hdspm_set_system_clock_mode(hdspm, val); + + return 0; +} + + +#define HDSPM_INTERNAL_CLOCK(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_clock_source, \ + .get = snd_hdspm_get_clock_source, \ + .put = snd_hdspm_put_clock_source \ } + static int hdspm_clock_source(struct hdspm * hdspm) { - if (hdspm->control_register & HDSPM_ClockModeMaster) { - switch (hdspm->system_sample_rate) { - case 32000: - return 1; - case 44100: - return 2; - case 48000: - return 3; - case 64000: - return 4; - case 88200: - return 5; - case 96000: - return 6; - case 128000: - return 7; - case 176400: - return 8; - case 192000: - return 9; - default: - return 3; - } - } else { - return 0; + switch (hdspm->system_sample_rate) { + case 32000: return 0; + case 44100: return 1; + case 48000: return 2; + case 64000: return 3; + case 88200: return 4; + case 96000: return 5; + case 128000: return 6; + case 176400: return 7; + case 192000: return 8; } + + return -1; } static int hdspm_set_clock_source(struct hdspm * hdspm, int mode) { int rate; switch (mode) { - - case HDSPM_CLOCK_SOURCE_AUTOSYNC: - if (hdspm_external_sample_rate(hdspm) != 0) { - hdspm->control_register &= ~HDSPM_ClockModeMaster; - hdspm_write(hdspm, HDSPM_controlRegister, - hdspm->control_register); - return 0; - } - return -1; - case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ: - rate = 32000; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ: - rate = 44100; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ: - rate = 48000; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ: - rate = 64000; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ: - rate = 88200; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ: - rate = 96000; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ: - rate = 128000; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ: - rate = 176400; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ: - rate = 192000; - break; - + case 0: + rate = 32000; break; + case 1: + rate = 44100; break; + case 2: + rate = 48000; break; + case 3: + rate = 64000; break; + case 4: + rate = 88200; break; + case 5: + rate = 96000; break; + case 6: + rate = 128000; break; + case 7: + rate = 176400; break; + case 8: + rate = 192000; break; default: - rate = 44100; + rate = 48000; } - hdspm->control_register |= HDSPM_ClockModeMaster; - hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); hdspm_set_rate(hdspm, rate, 1); return 0; } @@ -1560,25 +2120,16 @@ static int hdspm_set_clock_source(struct hdspm * hdspm, int mode) static int snd_hdspm_info_clock_source(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "AutoSync", - "Internal 32.0 kHz", "Internal 44.1 kHz", - "Internal 48.0 kHz", - "Internal 64.0 kHz", "Internal 88.2 kHz", - "Internal 96.0 kHz", - "Internal 128.0 kHz", "Internal 176.4 kHz", - "Internal 192.0 kHz" - }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = 10; + uinfo->value.enumerated.items = 9; if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); + texts_freq[uinfo->value.enumerated.item+1]); return 0; } @@ -1615,134 +2166,301 @@ static int snd_hdspm_put_clock_source(struct snd_kcontrol *kcontrol, return change; } -#define HDSPM_PREF_SYNC_REF(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .info = snd_hdspm_info_pref_sync_ref, \ - .get = snd_hdspm_get_pref_sync_ref, \ - .put = snd_hdspm_put_pref_sync_ref \ -} +#define HDSPM_PREF_SYNC_REF(xname, xindex) \ +{.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_pref_sync_ref, \ + .get = snd_hdspm_get_pref_sync_ref, \ + .put = snd_hdspm_put_pref_sync_ref \ +} + + +/** + * Returns the current preferred sync reference setting. + * The semantics of the return value are depending on the + * card, please see the comments for clarification. + **/ static int hdspm_pref_sync_ref(struct hdspm * hdspm) { - /* Notice that this looks at the requested sync source, - not the one actually in use. - */ - if (hdspm->is_aes32) { + switch (hdspm->io_type) { + case AES32: switch (hdspm->control_register & HDSPM_SyncRefMask) { - /* number gives AES index, except for 0 which - corresponds to WordClock */ - case 0: return 0; - case HDSPM_SyncRef0: return 1; - case HDSPM_SyncRef1: return 2; - case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3; - case HDSPM_SyncRef2: return 4; - case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5; - case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6; - case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0: return 7; - case HDSPM_SyncRef3: return 8; + case 0: return 0; /* WC */ + case HDSPM_SyncRef0: return 1; /* AES 1 */ + case HDSPM_SyncRef1: return 2; /* AES 2 */ + case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3; /* AES 3 */ + case HDSPM_SyncRef2: return 4; /* AES 4 */ + case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5; /* AES 5 */ + case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6; /* AES 6 */ + case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0: + return 7; /* AES 7 */ + case HDSPM_SyncRef3: return 8; /* AES 8 */ + case HDSPM_SyncRef3+HDSPM_SyncRef0: return 9; /* TCO */ } - } else { - switch (hdspm->control_register & HDSPM_SyncRefMask) { - case HDSPM_SyncRef_Word: - return HDSPM_SYNC_FROM_WORD; - case HDSPM_SyncRef_MADI: - return HDSPM_SYNC_FROM_MADI; + break; + + case MADI: + case MADIface: + if (hdspm->tco) { + switch (hdspm->control_register & HDSPM_SyncRefMask) { + case 0: return 0; /* WC */ + case HDSPM_SyncRef0: return 1; /* MADI */ + case HDSPM_SyncRef1: return 2; /* TCO */ + case HDSPM_SyncRef1+HDSPM_SyncRef0: + return 3; /* SYNC_IN */ + } + } else { + switch (hdspm->control_register & HDSPM_SyncRefMask) { + case 0: return 0; /* WC */ + case HDSPM_SyncRef0: return 1; /* MADI */ + case HDSPM_SyncRef1+HDSPM_SyncRef0: + return 2; /* SYNC_IN */ + } + } + break; + + case RayDAT: + if (hdspm->tco) { + switch ((hdspm->settings_register & + HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) { + case 0: return 0; /* WC */ + case 3: return 1; /* ADAT 1 */ + case 4: return 2; /* ADAT 2 */ + case 5: return 3; /* ADAT 3 */ + case 6: return 4; /* ADAT 4 */ + case 1: return 5; /* AES */ + case 2: return 6; /* SPDIF */ + case 9: return 7; /* TCO */ + case 10: return 8; /* SYNC_IN */ + } + } else { + switch ((hdspm->settings_register & + HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) { + case 0: return 0; /* WC */ + case 3: return 1; /* ADAT 1 */ + case 4: return 2; /* ADAT 2 */ + case 5: return 3; /* ADAT 3 */ + case 6: return 4; /* ADAT 4 */ + case 1: return 5; /* AES */ + case 2: return 6; /* SPDIF */ + case 10: return 7; /* SYNC_IN */ + } } + + break; + + case AIO: + if (hdspm->tco) { + switch ((hdspm->settings_register & + HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) { + case 0: return 0; /* WC */ + case 3: return 1; /* ADAT */ + case 1: return 2; /* AES */ + case 2: return 3; /* SPDIF */ + case 9: return 4; /* TCO */ + case 10: return 5; /* SYNC_IN */ + } + } else { + switch ((hdspm->settings_register & + HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) { + case 0: return 0; /* WC */ + case 3: return 1; /* ADAT */ + case 1: return 2; /* AES */ + case 2: return 3; /* SPDIF */ + case 10: return 4; /* SYNC_IN */ + } + } + + break; } - return HDSPM_SYNC_FROM_WORD; + return -1; } + +/** + * Set the preferred sync reference to . The semantics + * of are depending on the card type, see the comments + * for clarification. + **/ static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref) { - hdspm->control_register &= ~HDSPM_SyncRefMask; + int p = 0; - if (hdspm->is_aes32) { - switch (pref) { - case 0: - hdspm->control_register |= 0; - break; - case 1: - hdspm->control_register |= HDSPM_SyncRef0; - break; - case 2: - hdspm->control_register |= HDSPM_SyncRef1; - break; - case 3: - hdspm->control_register |= HDSPM_SyncRef1+HDSPM_SyncRef0; - break; - case 4: - hdspm->control_register |= HDSPM_SyncRef2; - break; - case 5: - hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef0; - break; - case 6: - hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef1; - break; - case 7: - hdspm->control_register |= - HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0; - break; - case 8: - hdspm->control_register |= HDSPM_SyncRef3; - break; - default: - return -1; - } - } else { + switch (hdspm->io_type) { + case AES32: + hdspm->control_register &= ~HDSPM_SyncRefMask; switch (pref) { - case HDSPM_SYNC_FROM_MADI: - hdspm->control_register |= HDSPM_SyncRef_MADI; + case 0: /* WC */ + break; + case 1: /* AES 1 */ + hdspm->control_register |= HDSPM_SyncRef0; + break; + case 2: /* AES 2 */ + hdspm->control_register |= HDSPM_SyncRef1; + break; + case 3: /* AES 3 */ + hdspm->control_register |= + HDSPM_SyncRef1+HDSPM_SyncRef0; + break; + case 4: /* AES 4 */ + hdspm->control_register |= HDSPM_SyncRef2; + break; + case 5: /* AES 5 */ + hdspm->control_register |= + HDSPM_SyncRef2+HDSPM_SyncRef0; + break; + case 6: /* AES 6 */ + hdspm->control_register |= + HDSPM_SyncRef2+HDSPM_SyncRef1; + break; + case 7: /* AES 7 */ + hdspm->control_register |= + HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0; break; - case HDSPM_SYNC_FROM_WORD: - hdspm->control_register |= HDSPM_SyncRef_Word; + case 8: /* AES 8 */ + hdspm->control_register |= HDSPM_SyncRef3; + break; + case 9: /* TCO */ + hdspm->control_register |= + HDSPM_SyncRef3+HDSPM_SyncRef0; break; default: return -1; } + + break; + + case MADI: + case MADIface: + hdspm->control_register &= ~HDSPM_SyncRefMask; + if (hdspm->tco) { + switch (pref) { + case 0: /* WC */ + break; + case 1: /* MADI */ + hdspm->control_register |= HDSPM_SyncRef0; + break; + case 2: /* TCO */ + hdspm->control_register |= HDSPM_SyncRef1; + break; + case 3: /* SYNC_IN */ + hdspm->control_register |= + HDSPM_SyncRef0+HDSPM_SyncRef1; + break; + default: + return -1; + } + } else { + switch (pref) { + case 0: /* WC */ + break; + case 1: /* MADI */ + hdspm->control_register |= HDSPM_SyncRef0; + break; + case 2: /* SYNC_IN */ + hdspm->control_register |= + HDSPM_SyncRef0+HDSPM_SyncRef1; + break; + default: + return -1; + } + } + + break; + + case RayDAT: + if (hdspm->tco) { + switch (pref) { + case 0: p = 0; break; /* WC */ + case 1: p = 3; break; /* ADAT 1 */ + case 2: p = 4; break; /* ADAT 2 */ + case 3: p = 5; break; /* ADAT 3 */ + case 4: p = 6; break; /* ADAT 4 */ + case 5: p = 1; break; /* AES */ + case 6: p = 2; break; /* SPDIF */ + case 7: p = 9; break; /* TCO */ + case 8: p = 10; break; /* SYNC_IN */ + default: return -1; + } + } else { + switch (pref) { + case 0: p = 0; break; /* WC */ + case 1: p = 3; break; /* ADAT 1 */ + case 2: p = 4; break; /* ADAT 2 */ + case 3: p = 5; break; /* ADAT 3 */ + case 4: p = 6; break; /* ADAT 4 */ + case 5: p = 1; break; /* AES */ + case 6: p = 2; break; /* SPDIF */ + case 7: p = 10; break; /* SYNC_IN */ + default: return -1; + } + } + break; + + case AIO: + if (hdspm->tco) { + switch (pref) { + case 0: p = 0; break; /* WC */ + case 1: p = 3; break; /* ADAT */ + case 2: p = 1; break; /* AES */ + case 3: p = 2; break; /* SPDIF */ + case 4: p = 9; break; /* TCO */ + case 5: p = 10; break; /* SYNC_IN */ + default: return -1; + } + } else { + switch (pref) { + case 0: p = 0; break; /* WC */ + case 1: p = 3; break; /* ADAT */ + case 2: p = 1; break; /* AES */ + case 3: p = 2; break; /* SPDIF */ + case 4: p = 10; break; /* SYNC_IN */ + default: return -1; + } + } + break; } - hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + + switch (hdspm->io_type) { + case RayDAT: + case AIO: + hdspm->settings_register &= ~HDSPM_c0_SyncRefMask; + hdspm->settings_register |= HDSPM_c0_SyncRef0 * p; + hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register); + break; + + case MADI: + case MADIface: + case AES32: + hdspm_write(hdspm, HDSPM_controlRegister, + hdspm->control_register); + } + return 0; } + static int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - if (hdspm->is_aes32) { - static char *texts[] = { "Word", "AES1", "AES2", "AES3", - "AES4", "AES5", "AES6", "AES7", "AES8" }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - - uinfo->value.enumerated.items = 9; - - if (uinfo->value.enumerated.item >= - uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - } else { - static char *texts[] = { "Word", "MADI" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = hdspm->texts_autosync_items; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; - uinfo->value.enumerated.items = 2; + strcpy(uinfo->value.enumerated.name, + hdspm->texts_autosync[uinfo->value.enumerated.item]); - if (uinfo->value.enumerated.item >= - uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - } return 0; } @@ -1750,32 +2468,41 @@ static int snd_hdspm_get_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + int psf = hdspm_pref_sync_ref(hdspm); - ucontrol->value.enumerated.item[0] = hdspm_pref_sync_ref(hdspm); - return 0; + if (psf >= 0) { + ucontrol->value.enumerated.item[0] = psf; + return 0; + } + + return -1; } static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - int change, max; - unsigned int val; - - max = hdspm->is_aes32 ? 9 : 2; + int val, change = 0; if (!snd_hdspm_use_is_exclusive(hdspm)) return -EBUSY; - val = ucontrol->value.enumerated.item[0] % max; + val = ucontrol->value.enumerated.item[0]; + + if (val < 0) + val = 0; + else if (val >= hdspm->texts_autosync_items) + val = hdspm->texts_autosync_items-1; spin_lock_irq(&hdspm->lock); - change = (int) val != hdspm_pref_sync_ref(hdspm); - hdspm_set_pref_sync_ref(hdspm, val); + if (val != hdspm_pref_sync_ref(hdspm)) + change = (0 == hdspm_set_pref_sync_ref(hdspm, val)) ? 1 : 0; + spin_unlock_irq(&hdspm->lock); return change; } + #define HDSPM_AUTOSYNC_REF(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -1785,18 +2512,18 @@ static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol, .get = snd_hdspm_get_autosync_ref, \ } -static int hdspm_autosync_ref(struct hdspm * hdspm) +static int hdspm_autosync_ref(struct hdspm *hdspm) { - if (hdspm->is_aes32) { + if (AES32 == hdspm->io_type) { unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); - unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) & - 0xF; + unsigned int syncref = + (status >> HDSPM_AES32_syncref_bit) & 0xF; if (syncref == 0) return HDSPM_AES32_AUTOSYNC_FROM_WORD; if (syncref <= 8) return syncref; return HDSPM_AES32_AUTOSYNC_FROM_NONE; - } else { + } else if (MADI == hdspm->io_type) { /* This looks at the autosync selected sync reference */ unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); @@ -1805,22 +2532,27 @@ static int hdspm_autosync_ref(struct hdspm * hdspm) return HDSPM_AUTOSYNC_FROM_WORD; case HDSPM_SelSyncRef_MADI: return HDSPM_AUTOSYNC_FROM_MADI; + case HDSPM_SelSyncRef_TCO: + return HDSPM_AUTOSYNC_FROM_TCO; + case HDSPM_SelSyncRef_SyncIn: + return HDSPM_AUTOSYNC_FROM_SYNC_IN; case HDSPM_SelSyncRef_NVALID: return HDSPM_AUTOSYNC_FROM_NONE; default: return 0; } - return 0; } + return 0; } + static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - if (hdspm->is_aes32) { + if (AES32 == hdspm->io_type) { static char *texts[] = { "WordClock", "AES1", "AES2", "AES3", "AES4", "AES5", "AES6", "AES7", "AES8", "None"}; @@ -1833,14 +2565,15 @@ static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - } else { - static char *texts[] = { "WordClock", "MADI", "None" }; + } else if (MADI == hdspm->io_type) { + static char *texts[] = {"Word Clock", "MADI", "TCO", + "Sync In", "None" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = 3; + uinfo->value.enumerated.items = 5; if (uinfo->value.enumerated.item >= - uinfo->value.enumerated.items) + uinfo->value.enumerated.items) uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, @@ -1858,6 +2591,7 @@ static int snd_hdspm_get_autosync_ref(struct snd_kcontrol *kcontrol, return 0; } + #define HDSPM_LINE_OUT(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -1914,6 +2648,7 @@ static int snd_hdspm_put_line_out(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_TX_64(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -1969,6 +2704,7 @@ static int snd_hdspm_put_tx_64(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_C_TMS(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2024,6 +2760,7 @@ static int snd_hdspm_put_c_tms(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_SAFE_MODE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2079,6 +2816,7 @@ static int snd_hdspm_put_safe_mode(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_EMPHASIS(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2134,6 +2872,7 @@ static int snd_hdspm_put_emphasis(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_DOLBY(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2189,6 +2928,7 @@ static int snd_hdspm_put_dolby(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_PROFESSIONAL(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2315,6 +3055,7 @@ static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_DS_WIRE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2386,6 +3127,7 @@ static int snd_hdspm_put_ds_wire(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_QS_WIRE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2472,15 +3214,6 @@ static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol, return change; } -/* Simple Mixer - deprecated since to much faders ??? - MIXER interface says output (source, destination, value) - where source > MAX_channels are playback channels - on MADICARD - - playback mixer matrix: [channelout+64] [output] [value] - - input(thru) mixer matrix: [channelin] [output] [value] - (better do 2 kontrols for separation ?) -*/ #define HDSPM_MIXER(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ @@ -2586,7 +3319,7 @@ static int snd_hdspm_put_mixer(struct snd_kcontrol *kcontrol, /* The simple mixer control(s) provide gain control for the basic 1:1 mappings of playback streams to output - streams. + streams. */ #define HDSPM_PLAYBACK_MIXER \ @@ -2604,7 +3337,7 @@ static int snd_hdspm_info_playback_mixer(struct snd_kcontrol *kcontrol, uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = 65536; + uinfo->value.integer.max = 64; uinfo->value.integer.step = 1; return 0; } @@ -2614,28 +3347,17 @@ static int snd_hdspm_get_playback_mixer(struct snd_kcontrol *kcontrol, { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); int channel; - int mapped_channel; channel = ucontrol->id.index - 1; if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS)) return -EINVAL; - mapped_channel = hdspm->channel_map[channel]; - if (mapped_channel < 0) - return -EINVAL; - spin_lock_irq(&hdspm->lock); ucontrol->value.integer.value[0] = - hdspm_read_pb_gain(hdspm, mapped_channel, mapped_channel); + (hdspm_read_pb_gain(hdspm, channel, channel)*64)/UNITY_GAIN; spin_unlock_irq(&hdspm->lock); - /* - snd_printdd("get pb mixer index %d, channel %d, mapped_channel %d, " - "value %d\n", - ucontrol->id.index, channel, mapped_channel, - ucontrol->value.integer.value[0]); - */ return 0; } @@ -2645,7 +3367,6 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol, struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); int change; int channel; - int mapped_channel; int gain; if (!snd_hdspm_use_is_exclusive(hdspm)) @@ -2656,59 +3377,60 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol, if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS)) return -EINVAL; - mapped_channel = hdspm->channel_map[channel]; - if (mapped_channel < 0) - return -EINVAL; - - gain = ucontrol->value.integer.value[0]; + gain = ucontrol->value.integer.value[0]*UNITY_GAIN/64; spin_lock_irq(&hdspm->lock); change = - gain != hdspm_read_pb_gain(hdspm, mapped_channel, - mapped_channel); + gain != hdspm_read_pb_gain(hdspm, channel, + channel); if (change) - hdspm_write_pb_gain(hdspm, mapped_channel, mapped_channel, + hdspm_write_pb_gain(hdspm, channel, channel, gain); spin_unlock_irq(&hdspm->lock); return change; } -#define HDSPM_WC_SYNC_CHECK(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ - .info = snd_hdspm_info_sync_check, \ - .get = snd_hdspm_get_wc_sync_check \ +#define HDSPM_SYNC_CHECK(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .private_value = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_sync_check, \ + .get = snd_hdspm_get_sync_check \ } + static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "No Lock", "Lock", "Sync" }; + static char *texts[] = { "No Lock", "Lock", "Sync", "N/A" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = 3; + uinfo->value.enumerated.items = 4; if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; + uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); + texts[uinfo->value.enumerated.item]); return 0; } -static int hdspm_wc_sync_check(struct hdspm * hdspm) +static int hdspm_wc_sync_check(struct hdspm *hdspm) { - if (hdspm->is_aes32) { - int status = hdspm_read(hdspm, HDSPM_statusRegister); - if (status & HDSPM_AES32_wcLock) { - /* I don't know how to differenciate sync from lock. - Doing as if sync for now */ + int status, status2; + + switch (hdspm->io_type) { + case AES32: + status = hdspm_read(hdspm, HDSPM_statusRegister); + if (status & HDSPM_wcSync) return 2; - } + else if (status & HDSPM_wcLock) + return 1; return 0; - } else { - int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + break; + + case MADI: + status2 = hdspm_read(hdspm, HDSPM_statusRegister2); if (status2 & HDSPM_wcLock) { if (status2 & HDSPM_wcSync) return 2; @@ -2716,29 +3438,30 @@ static int hdspm_wc_sync_check(struct hdspm * hdspm) return 1; } return 0; - } -} + break; -static int snd_hdspm_get_wc_sync_check(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_statusRegister); - ucontrol->value.enumerated.item[0] = hdspm_wc_sync_check(hdspm); - return 0; -} + if (status & 0x2000000) + return 2; + else if (status & 0x1000000) + return 1; + return 0; + break; -#define HDSPM_MADI_SYNC_CHECK(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ - .info = snd_hdspm_info_sync_check, \ - .get = snd_hdspm_get_madisync_sync_check \ + case MADIface: + break; + } + + + return 3; } -static int hdspm_madisync_sync_check(struct hdspm * hdspm) + +static int hdspm_madi_sync_check(struct hdspm *hdspm) { int status = hdspm_read(hdspm, HDSPM_statusRegister); if (status & HDSPM_madiLock) { @@ -2750,89 +3473,727 @@ static int hdspm_madisync_sync_check(struct hdspm * hdspm) return 0; } -static int snd_hdspm_get_madisync_sync_check(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value * - ucontrol) -{ - struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - ucontrol->value.enumerated.item[0] = - hdspm_madisync_sync_check(hdspm); - return 0; -} +static int hdspm_s1_sync_check(struct hdspm *hdspm, int idx) +{ + int status, lock, sync; + status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); -#define HDSPM_AES_SYNC_CHECK(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ - .info = snd_hdspm_info_sync_check, \ - .get = snd_hdspm_get_aes_sync_check \ -} + lock = (status & (0x1<> idx)) { - /* I don't know how to differenciate sync from lock. - Doing as if sync for now */ + if (lock && sync) return 2; - } + else if (lock) + return 1; return 0; } -static int snd_hdspm_get_aes_sync_check(struct snd_kcontrol *kcontrol, + +static int hdspm_sync_in_sync_check(struct hdspm *hdspm) +{ + int status, lock = 0, sync = 0; + + switch (hdspm->io_type) { + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_RD_STATUS_3); + lock = (status & 0x400) ? 1 : 0; + sync = (status & 0x800) ? 1 : 0; + break; + + case MADI: + case AES32: + status = hdspm_read(hdspm, HDSPM_statusRegister2); + lock = (status & 0x400000) ? 1 : 0; + sync = (status & 0x800000) ? 1 : 0; + break; + + case MADIface: + break; + } + + if (lock && sync) + return 2; + else if (lock) + return 1; + + return 0; +} + +static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx) +{ + int status2, lock, sync; + status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + + lock = (status2 & (0x0080 >> idx)) ? 1 : 0; + sync = (status2 & (0x8000 >> idx)) ? 1 : 0; + + if (sync) + return 2; + else if (lock) + return 1; + return 0; +} + + +static int hdspm_tco_sync_check(struct hdspm *hdspm) +{ + int status; + + if (hdspm->tco) { + switch (hdspm->io_type) { + case MADI: + case AES32: + status = hdspm_read(hdspm, HDSPM_statusRegister); + if (status & HDSPM_tcoLock) { + if (status & HDSPM_tcoSync) + return 2; + else + return 1; + } + return 0; + + break; + + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); + + if (status & 0x8000000) + return 2; /* Sync */ + if (status & 0x4000000) + return 1; /* Lock */ + return 0; /* No signal */ + break; + + default: + break; + } + } + + return 3; /* N/A */ +} + + +static int snd_hdspm_get_sync_check(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + int val = -1; + + switch (hdspm->io_type) { + case RayDAT: + switch (kcontrol->private_value) { + case 0: /* WC */ + val = hdspm_wc_sync_check(hdspm); break; + case 7: /* TCO */ + val = hdspm_tco_sync_check(hdspm); break; + case 8: /* SYNC IN */ + val = hdspm_sync_in_sync_check(hdspm); break; + default: + val = hdspm_s1_sync_check(hdspm, ucontrol->id.index-1); + } + + case AIO: + switch (kcontrol->private_value) { + case 0: /* WC */ + val = hdspm_wc_sync_check(hdspm); break; + case 4: /* TCO */ + val = hdspm_tco_sync_check(hdspm); break; + case 5: /* SYNC IN */ + val = hdspm_sync_in_sync_check(hdspm); break; + default: + val = hdspm_s1_sync_check(hdspm, ucontrol->id.index-1); + } + + case MADI: + switch (kcontrol->private_value) { + case 0: /* WC */ + val = hdspm_wc_sync_check(hdspm); break; + case 1: /* MADI */ + val = hdspm_madi_sync_check(hdspm); break; + case 2: /* TCO */ + val = hdspm_tco_sync_check(hdspm); break; + case 3: /* SYNC_IN */ + val = hdspm_sync_in_sync_check(hdspm); break; + } + + case MADIface: + val = hdspm_madi_sync_check(hdspm); /* MADI */ + break; + + case AES32: + switch (kcontrol->private_value) { + case 0: /* WC */ + val = hdspm_wc_sync_check(hdspm); break; + case 9: /* TCO */ + val = hdspm_tco_sync_check(hdspm); break; + case 10 /* SYNC IN */: + val = hdspm_sync_in_sync_check(hdspm); break; + default: + val = hdspm_aes_sync_check(hdspm, + ucontrol->id.index-1); + } + + } + + if (-1 == val) + val = 3; + + ucontrol->value.enumerated.item[0] = val; + return 0; +} + + + +/** + * TCO controls + **/ +static void hdspm_tco_write(struct hdspm *hdspm) +{ + unsigned int tc[4] = { 0, 0, 0, 0}; + + switch (hdspm->tco->input) { + case 0: + tc[2] |= HDSPM_TCO2_set_input_MSB; + break; + case 1: + tc[2] |= HDSPM_TCO2_set_input_LSB; + break; + default: + break; + } + + switch (hdspm->tco->framerate) { + case 1: + tc[1] |= HDSPM_TCO1_LTC_Format_LSB; + break; + case 2: + tc[1] |= HDSPM_TCO1_LTC_Format_MSB; + break; + case 3: + tc[1] |= HDSPM_TCO1_LTC_Format_MSB + + HDSPM_TCO1_set_drop_frame_flag; + break; + case 4: + tc[1] |= HDSPM_TCO1_LTC_Format_LSB + + HDSPM_TCO1_LTC_Format_MSB; + break; + case 5: + tc[1] |= HDSPM_TCO1_LTC_Format_LSB + + HDSPM_TCO1_LTC_Format_MSB + + HDSPM_TCO1_set_drop_frame_flag; + break; + default: + break; + } + + switch (hdspm->tco->wordclock) { + case 1: + tc[2] |= HDSPM_TCO2_WCK_IO_ratio_LSB; + break; + case 2: + tc[2] |= HDSPM_TCO2_WCK_IO_ratio_MSB; + break; + default: + break; + } + + switch (hdspm->tco->samplerate) { + case 1: + tc[2] |= HDSPM_TCO2_set_freq; + break; + case 2: + tc[2] |= HDSPM_TCO2_set_freq_from_app; + break; + default: + break; + } + + switch (hdspm->tco->pull) { + case 1: + tc[2] |= HDSPM_TCO2_set_pull_up; + break; + case 2: + tc[2] |= HDSPM_TCO2_set_pull_down; + break; + case 3: + tc[2] |= HDSPM_TCO2_set_pull_up + HDSPM_TCO2_set_01_4; + break; + case 4: + tc[2] |= HDSPM_TCO2_set_pull_down + HDSPM_TCO2_set_01_4; + break; + default: + break; + } + + if (1 == hdspm->tco->term) { + tc[2] |= HDSPM_TCO2_set_term_75R; + } + + hdspm_write(hdspm, HDSPM_WR_TCO, tc[0]); + hdspm_write(hdspm, HDSPM_WR_TCO+4, tc[1]); + hdspm_write(hdspm, HDSPM_WR_TCO+8, tc[2]); + hdspm_write(hdspm, HDSPM_WR_TCO+12, tc[3]); +} + + +#define HDSPM_TCO_SAMPLE_RATE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_sample_rate, \ + .get = snd_hdspm_get_tco_sample_rate, \ + .put = snd_hdspm_put_tco_sample_rate \ +} + +static int snd_hdspm_info_tco_sample_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "44.1 kHz", "48 kHz" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_hdspm_get_tco_sample_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm->tco->samplerate; + + return 0; +} + +static int snd_hdspm_put_tco_sample_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + if (hdspm->tco->samplerate != ucontrol->value.enumerated.item[0]) { + hdspm->tco->samplerate = ucontrol->value.enumerated.item[0]; + + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + + +#define HDSPM_TCO_PULL(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_pull, \ + .get = snd_hdspm_get_tco_pull, \ + .put = snd_hdspm_put_tco_pull \ +} + +static int snd_hdspm_info_tco_pull(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "0", "+ 0.1 %", "- 0.1 %", "+ 4 %", "- 4 %" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 5; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_hdspm_get_tco_pull(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm->tco->pull; + + return 0; +} + +static int snd_hdspm_put_tco_pull(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + if (hdspm->tco->pull != ucontrol->value.enumerated.item[0]) { + hdspm->tco->pull = ucontrol->value.enumerated.item[0]; + + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + +#define HDSPM_TCO_WCK_CONVERSION(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_wck_conversion, \ + .get = snd_hdspm_get_tco_wck_conversion, \ + .put = snd_hdspm_put_tco_wck_conversion \ +} + +static int snd_hdspm_info_tco_wck_conversion(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_hdspm_get_tco_wck_conversion(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm->tco->wordclock; + + return 0; +} + +static int snd_hdspm_put_tco_wck_conversion(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + if (hdspm->tco->wordclock != ucontrol->value.enumerated.item[0]) { + hdspm->tco->wordclock = ucontrol->value.enumerated.item[0]; + + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + + +#define HDSPM_TCO_FRAME_RATE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_frame_rate, \ + .get = snd_hdspm_get_tco_frame_rate, \ + .put = snd_hdspm_put_tco_frame_rate \ +} + +static int snd_hdspm_info_tco_frame_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "24 fps", "25 fps", "29.97fps", + "29.97 dfps", "30 fps", "30 dfps" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 6; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_hdspm_get_tco_frame_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - int offset; struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - offset = ucontrol->id.index - 1; - if (offset < 0 || offset >= 8) - return -EINVAL; + ucontrol->value.enumerated.item[0] = hdspm->tco->framerate; - ucontrol->value.enumerated.item[0] = - hdspm_aes_sync_check(hdspm, offset); return 0; } +static int snd_hdspm_put_tco_frame_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); -static struct snd_kcontrol_new snd_hdspm_controls_madi[] = { + if (hdspm->tco->framerate != ucontrol->value.enumerated.item[0]) { + hdspm->tco->framerate = ucontrol->value.enumerated.item[0]; - HDSPM_MIXER("Mixer", 0), -/* 'Sample Clock Source' complies with the alsa control naming scheme */ - HDSPM_CLOCK_SOURCE("Sample Clock Source", 0), + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + +#define HDSPM_TCO_SYNC_SOURCE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_sync_source, \ + .get = snd_hdspm_get_tco_sync_source, \ + .put = snd_hdspm_put_tco_sync_source \ +} + +static int snd_hdspm_info_tco_sync_source(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "LTC", "Video", "WCK" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_hdspm_get_tco_sync_source(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm->tco->input; + + return 0; +} + +static int snd_hdspm_put_tco_sync_source(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + if (hdspm->tco->input != ucontrol->value.enumerated.item[0]) { + hdspm->tco->input = ucontrol->value.enumerated.item[0]; + + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + + +#define HDSPM_TCO_WORD_TERM(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_word_term, \ + .get = snd_hdspm_get_tco_word_term, \ + .put = snd_hdspm_put_tco_word_term \ +} + +static int snd_hdspm_info_tco_word_term(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + + +static int snd_hdspm_get_tco_word_term(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm->tco->term; + + return 0; +} + + +static int snd_hdspm_put_tco_word_term(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + if (hdspm->tco->term != ucontrol->value.enumerated.item[0]) { + hdspm->tco->term = ucontrol->value.enumerated.item[0]; + + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + + + + +static struct snd_kcontrol_new snd_hdspm_controls_madi[] = { + HDSPM_MIXER("Mixer", 0), + HDSPM_INTERNAL_CLOCK("Internal Clock", 0), HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0), HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0), HDSPM_AUTOSYNC_REF("AutoSync Reference", 0), HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), -/* 'External Rate' complies with the alsa control naming scheme */ - HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), - HDSPM_WC_SYNC_CHECK("Word Clock Lock Status", 0), - HDSPM_MADI_SYNC_CHECK("MADI Sync Lock Status", 0), + HDSPM_SYNC_CHECK("WC SyncCheck", 0), + HDSPM_SYNC_CHECK("MADI SyncCheck", 1), + HDSPM_SYNC_CHECK("TCO SyncCHeck", 2), + HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 3), HDSPM_LINE_OUT("Line Out", 0), HDSPM_TX_64("TX 64 channels mode", 0), HDSPM_C_TMS("Clear Track Marker", 0), HDSPM_SAFE_MODE("Safe Mode", 0), + HDSPM_INPUT_SELECT("Input Select", 0) +}; + + +static struct snd_kcontrol_new snd_hdspm_controls_madiface[] = { + HDSPM_MIXER("Mixer", 0), + HDSPM_INTERNAL_CLOCK("Internal Clock", 0), + HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0), + HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), + HDSPM_SYNC_CHECK("MADI SyncCheck", 0), + HDSPM_TX_64("TX 64 channels mode", 0), + HDSPM_C_TMS("Clear Track Marker", 0), + HDSPM_SAFE_MODE("Safe Mode", 0), HDSPM_INPUT_SELECT("Input Select", 0), }; -static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = { +static struct snd_kcontrol_new snd_hdspm_controls_aio[] = { + HDSPM_MIXER("Mixer", 0), + HDSPM_INTERNAL_CLOCK("Internal Clock", 0), + HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0), + HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0), + HDSPM_AUTOSYNC_REF("AutoSync Reference", 0), + HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), + HDSPM_SYNC_CHECK("WC SyncCheck", 0), + HDSPM_SYNC_CHECK("AES SyncCheck", 1), + HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2), + HDSPM_SYNC_CHECK("ADAT SyncCheck", 3), + HDSPM_SYNC_CHECK("TCO SyncCheck", 4), + HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 5), + HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1), + HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2), + HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT Frequency", 3), + HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 4), + HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5) + + /* + HDSPM_INPUT_SELECT("Input Select", 0), + HDSPM_SPDIF_OPTICAL("SPDIF Out Optical", 0), + HDSPM_PROFESSIONAL("SPDIF Out Professional", 0); + HDSPM_SPDIF_IN("SPDIF In", 0); + HDSPM_BREAKOUT_CABLE("Breakout Cable", 0); + HDSPM_INPUT_LEVEL("Input Level", 0); + HDSPM_OUTPUT_LEVEL("Output Level", 0); + HDSPM_PHONES("Phones", 0); + */ +}; +static struct snd_kcontrol_new snd_hdspm_controls_raydat[] = { + HDSPM_MIXER("Mixer", 0), + HDSPM_INTERNAL_CLOCK("Internal Clock", 0), + HDSPM_SYSTEM_CLOCK_MODE("Clock Mode", 0), + HDSPM_PREF_SYNC_REF("Pref Sync Ref", 0), + HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), + HDSPM_SYNC_CHECK("WC SyncCheck", 0), + HDSPM_SYNC_CHECK("AES SyncCheck", 1), + HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2), + HDSPM_SYNC_CHECK("ADAT1 SyncCheck", 3), + HDSPM_SYNC_CHECK("ADAT2 SyncCheck", 4), + HDSPM_SYNC_CHECK("ADAT3 SyncCheck", 5), + HDSPM_SYNC_CHECK("ADAT4 SyncCheck", 6), + HDSPM_SYNC_CHECK("TCO SyncCheck", 7), + HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 8), + HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1), + HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2), + HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT1 Frequency", 3), + HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT2 Frequency", 4), + HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT3 Frequency", 5), + HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT4 Frequency", 6), + HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 7), + HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8) +}; + +static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = { HDSPM_MIXER("Mixer", 0), -/* 'Sample Clock Source' complies with the alsa control naming scheme */ - HDSPM_CLOCK_SOURCE("Sample Clock Source", 0), - + HDSPM_INTERNAL_CLOCK("Internal Clock", 0), HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0), HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0), HDSPM_AUTOSYNC_REF("AutoSync Reference", 0), HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), -/* 'External Rate' complies with the alsa control naming scheme */ HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), - HDSPM_WC_SYNC_CHECK("Word Clock Lock Status", 0), -/* HDSPM_AES_SYNC_CHECK("AES Lock Status", 0),*/ /* created in snd_hdspm_create_controls() */ + HDSPM_SYNC_CHECK("WC Sync Check", 0), + HDSPM_SYNC_CHECK("AES1 Sync Check", 1), + HDSPM_SYNC_CHECK("AES2 Sync Check", 2), + HDSPM_SYNC_CHECK("AES3 Sync Check", 3), + HDSPM_SYNC_CHECK("AES4 Sync Check", 4), + HDSPM_SYNC_CHECK("AES5 Sync Check", 5), + HDSPM_SYNC_CHECK("AES6 Sync Check", 6), + HDSPM_SYNC_CHECK("AES7 Sync Check", 7), + HDSPM_SYNC_CHECK("AES8 Sync Check", 8), + HDSPM_SYNC_CHECK("TCO Sync Check", 9), + HDSPM_SYNC_CHECK("SYNC IN Sync Check", 10), + HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES1 Frequency", 1), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES2 Frequency", 2), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES3 Frequency", 3), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES4 Frequency", 4), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES5 Frequency", 5), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES6 Frequency", 6), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES7 Frequency", 7), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES8 Frequency", 8), + HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 9), + HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 10), HDSPM_LINE_OUT("Line Out", 0), HDSPM_EMPHASIS("Emphasis", 0), HDSPM_DOLBY("Non Audio", 0), @@ -2842,6 +4203,19 @@ static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = { HDSPM_QS_WIRE("Quad Speed Wire Mode", 0), }; + + +/* Control elements for the optional TCO module */ +static struct snd_kcontrol_new snd_hdspm_controls_tco[] = { + HDSPM_TCO_SAMPLE_RATE("TCO Sample Rate", 0), + HDSPM_TCO_PULL("TCO Pull", 0), + HDSPM_TCO_WCK_CONVERSION("TCO WCK Conversion", 0), + HDSPM_TCO_FRAME_RATE("TCO Frame Rate", 0), + HDSPM_TCO_SYNC_SOURCE("TCO Sync Source", 0), + HDSPM_TCO_WORD_TERM("TCO Word Term", 0) +}; + + static struct snd_kcontrol_new snd_hdspm_playback_mixer = HDSPM_PLAYBACK_MIXER; @@ -2849,78 +4223,76 @@ static int hdspm_update_simple_mixer_controls(struct hdspm * hdspm) { int i; - for (i = hdspm->ds_channels; i < hdspm->ss_channels; ++i) { + for (i = hdspm->ds_out_channels; i < hdspm->ss_out_channels; ++i) { if (hdspm->system_sample_rate > 48000) { hdspm->playback_mixer_ctls[i]->vd[0].access = - SNDRV_CTL_ELEM_ACCESS_INACTIVE | - SNDRV_CTL_ELEM_ACCESS_READ | - SNDRV_CTL_ELEM_ACCESS_VOLATILE; + SNDRV_CTL_ELEM_ACCESS_INACTIVE | + SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE; } else { hdspm->playback_mixer_ctls[i]->vd[0].access = - SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_VOLATILE; + SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE; } snd_ctl_notify(hdspm->card, SNDRV_CTL_EVENT_MASK_VALUE | - SNDRV_CTL_EVENT_MASK_INFO, - &hdspm->playback_mixer_ctls[i]->id); + SNDRV_CTL_EVENT_MASK_INFO, + &hdspm->playback_mixer_ctls[i]->id); } return 0; } -static int snd_hdspm_create_controls(struct snd_card *card, struct hdspm * hdspm) +static int snd_hdspm_create_controls(struct snd_card *card, + struct hdspm *hdspm) { unsigned int idx, limit; int err; struct snd_kcontrol *kctl; + struct snd_kcontrol_new *list = NULL; - /* add control list first */ - if (hdspm->is_aes32) { - struct snd_kcontrol_new aes_sync_ctl = - HDSPM_AES_SYNC_CHECK("AES Lock Status", 0); + switch (hdspm->io_type) { + case MADI: + list = snd_hdspm_controls_madi; + limit = ARRAY_SIZE(snd_hdspm_controls_madi); + break; + case MADIface: + list = snd_hdspm_controls_madiface; + limit = ARRAY_SIZE(snd_hdspm_controls_madiface); + break; + case AIO: + list = snd_hdspm_controls_aio; + limit = ARRAY_SIZE(snd_hdspm_controls_aio); + break; + case RayDAT: + list = snd_hdspm_controls_raydat; + limit = ARRAY_SIZE(snd_hdspm_controls_raydat); + break; + case AES32: + list = snd_hdspm_controls_aes32; + limit = ARRAY_SIZE(snd_hdspm_controls_aes32); + break; + } - for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls_aes32); - idx++) { - err = snd_ctl_add(card, - snd_ctl_new1(&snd_hdspm_controls_aes32[idx], - hdspm)); - if (err < 0) - return err; - } - for (idx = 1; idx <= 8; idx++) { - aes_sync_ctl.index = idx; + if (NULL != list) { + for (idx = 0; idx < limit; idx++) { err = snd_ctl_add(card, - snd_ctl_new1(&aes_sync_ctl, hdspm)); - if (err < 0) - return err; - } - } else { - for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls_madi); - idx++) { - err = snd_ctl_add(card, - snd_ctl_new1(&snd_hdspm_controls_madi[idx], - hdspm)); + snd_ctl_new1(&list[idx], hdspm)); if (err < 0) return err; } } - /* Channel playback mixer as default control - Note: the whole matrix would be 128*HDSPM_MIXER_CHANNELS Faders, - thats too * big for any alsamixer they are accessible via special - IOCTL on hwdep and the mixer 2dimensional mixer control - */ + /* create simple 1:1 playback mixer controls */ snd_hdspm_playback_mixer.name = "Chn"; - limit = HDSPM_MAX_CHANNELS; - - /* The index values are one greater than the channel ID so that - * alsamixer will display them correctly. We want to use the index - * for fast lookup of the relevant channel, but if we use it at all, - * most ALSA software does the wrong thing with it ... - */ - + if (hdspm->system_sample_rate >= 128000) { + limit = hdspm->qs_out_channels; + } else if (hdspm->system_sample_rate >= 64000) { + limit = hdspm->ds_out_channels; + } else { + limit = hdspm->ss_out_channels; + } for (idx = 0; idx < limit; ++idx) { snd_hdspm_playback_mixer.index = idx + 1; kctl = snd_ctl_new1(&snd_hdspm_playback_mixer, hdspm); @@ -2930,11 +4302,24 @@ static int snd_hdspm_create_controls(struct snd_card *card, struct hdspm * hdspm hdspm->playback_mixer_ctls[idx] = kctl; } + + if (hdspm->tco) { + /* add tco control elements */ + list = snd_hdspm_controls_tco; + limit = ARRAY_SIZE(snd_hdspm_controls_tco); + for (idx = 0; idx < limit; idx++) { + err = snd_ctl_add(card, + snd_ctl_new1(&list[idx], hdspm)); + if (err < 0) + return err; + } + } + return 0; } /*------------------------------------------------------------ - /proc interface + /proc interface ------------------------------------------------------------*/ static void @@ -2942,72 +4327,178 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, struct snd_info_buffer *buffer) { struct hdspm *hdspm = entry->private_data; - unsigned int status; - unsigned int status2; + unsigned int status, status2, control, freq; + char *pref_sync_ref; char *autosync_ref; char *system_clock_mode; - char *clock_source; char *insel; - char *syncref; int x, x2; + /* TCO stuff */ + int a, ltc, frames, seconds, minutes, hours; + unsigned int period; + u64 freq_const = 0; + u32 rate; + status = hdspm_read(hdspm, HDSPM_statusRegister); status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + control = hdspm->control_register; + freq = hdspm_read(hdspm, HDSPM_timecodeRegister); snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n", - hdspm->card_name, hdspm->card->number + 1, - hdspm->firmware_rev, - (status2 & HDSPM_version0) | - (status2 & HDSPM_version1) | (status2 & - HDSPM_version2)); + hdspm->card_name, hdspm->card->number + 1, + hdspm->firmware_rev, + (status2 & HDSPM_version0) | + (status2 & HDSPM_version1) | (status2 & + HDSPM_version2)); + + snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n", + (hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF, + (hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF); snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", - hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase); + hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase); snd_iprintf(buffer, "--- System ---\n"); snd_iprintf(buffer, - "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n", - status & HDSPM_audioIRQPending, - (status & HDSPM_midi0IRQPending) ? 1 : 0, - (status & HDSPM_midi1IRQPending) ? 1 : 0, - hdspm->irq_count); + "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n", + status & HDSPM_audioIRQPending, + (status & HDSPM_midi0IRQPending) ? 1 : 0, + (status & HDSPM_midi1IRQPending) ? 1 : 0, + hdspm->irq_count); snd_iprintf(buffer, - "HW pointer: id = %d, rawptr = %d (%d->%d) " - "estimated= %ld (bytes)\n", - ((status & HDSPM_BufferID) ? 1 : 0), - (status & HDSPM_BufferPositionMask), - (status & HDSPM_BufferPositionMask) % - (2 * (int)hdspm->period_bytes), - ((status & HDSPM_BufferPositionMask) - 64) % - (2 * (int)hdspm->period_bytes), - (long) hdspm_hw_pointer(hdspm) * 4); + "HW pointer: id = %d, rawptr = %d (%d->%d) " + "estimated= %ld (bytes)\n", + ((status & HDSPM_BufferID) ? 1 : 0), + (status & HDSPM_BufferPositionMask), + (status & HDSPM_BufferPositionMask) % + (2 * (int)hdspm->period_bytes), + ((status & HDSPM_BufferPositionMask) - 64) % + (2 * (int)hdspm->period_bytes), + (long) hdspm_hw_pointer(hdspm) * 4); snd_iprintf(buffer, - "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n", - hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF); + "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n", + hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF); snd_iprintf(buffer, - "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, " - "status2=0x%x\n", - hdspm->control_register, hdspm->control2_register, - status, status2); + "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n", + hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF); + snd_iprintf(buffer, + "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, " + "status2=0x%x\n", + hdspm->control_register, hdspm->control2_register, + status, status2); + if (status & HDSPM_tco_detect) { + snd_iprintf(buffer, "TCO module detected.\n"); + a = hdspm_read(hdspm, HDSPM_RD_TCO+4); + if (a & HDSPM_TCO1_LTC_Input_valid) { + snd_iprintf(buffer, " LTC valid, "); + switch (a & (HDSPM_TCO1_LTC_Format_LSB | + HDSPM_TCO1_LTC_Format_MSB)) { + case 0: + snd_iprintf(buffer, "24 fps, "); + break; + case HDSPM_TCO1_LTC_Format_LSB: + snd_iprintf(buffer, "25 fps, "); + break; + case HDSPM_TCO1_LTC_Format_MSB: + snd_iprintf(buffer, "29.97 fps, "); + break; + default: + snd_iprintf(buffer, "30 fps, "); + break; + } + if (a & HDSPM_TCO1_set_drop_frame_flag) { + snd_iprintf(buffer, "drop frame\n"); + } else { + snd_iprintf(buffer, "full frame\n"); + } + } else { + snd_iprintf(buffer, " no LTC\n"); + } + if (a & HDSPM_TCO1_Video_Input_Format_NTSC) { + snd_iprintf(buffer, " Video: NTSC\n"); + } else if (a & HDSPM_TCO1_Video_Input_Format_PAL) { + snd_iprintf(buffer, " Video: PAL\n"); + } else { + snd_iprintf(buffer, " No video\n"); + } + if (a & HDSPM_TCO1_TCO_lock) { + snd_iprintf(buffer, " Sync: lock\n"); + } else { + snd_iprintf(buffer, " Sync: no lock\n"); + } + + switch (hdspm->io_type) { + case MADI: + case AES32: + freq_const = 110069313433624ULL; + break; + case RayDAT: + case AIO: + freq_const = 104857600000000ULL; + break; + case MADIface: + break; /* no TCO possible */ + } + + period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ); + snd_iprintf(buffer, " period: %u\n", period); + + + /* rate = freq_const/period; */ + rate = div_u64(freq_const, period); + + if (control & HDSPM_QuadSpeed) { + rate *= 4; + } else if (control & HDSPM_DoubleSpeed) { + rate *= 2; + } + + snd_iprintf(buffer, " Frequency: %u Hz\n", + (unsigned int) rate); + + ltc = hdspm_read(hdspm, HDSPM_RD_TCO); + frames = ltc & 0xF; + ltc >>= 4; + frames += (ltc & 0x3) * 10; + ltc >>= 4; + seconds = ltc & 0xF; + ltc >>= 4; + seconds += (ltc & 0x7) * 10; + ltc >>= 4; + minutes = ltc & 0xF; + ltc >>= 4; + minutes += (ltc & 0x7) * 10; + ltc >>= 4; + hours = ltc & 0xF; + ltc >>= 4; + hours += (ltc & 0x3) * 10; + snd_iprintf(buffer, + " LTC In: %02d:%02d:%02d:%02d\n", + hours, minutes, seconds, frames); + + } else { + snd_iprintf(buffer, "No TCO module detected.\n"); + } snd_iprintf(buffer, "--- Settings ---\n"); x = 1 << (6 + hdspm_decode_latency(hdspm->control_register & - HDSPM_LatencyMask)); + HDSPM_LatencyMask)); snd_iprintf(buffer, - "Size (Latency): %d samples (2 periods of %lu bytes)\n", - x, (unsigned long) hdspm->period_bytes); + "Size (Latency): %d samples (2 periods of %lu bytes)\n", + x, (unsigned long) hdspm->period_bytes); - snd_iprintf(buffer, "Line out: %s, Precise Pointer: %s\n", - (hdspm->control_register & HDSPM_LineOut) ? "on " : "off", - (hdspm->precise_ptr) ? "on" : "off"); + snd_iprintf(buffer, "Line out: %s\n", + (hdspm->control_register & HDSPM_LineOut) ? "on " : "off"); switch (hdspm->control_register & HDSPM_InputMask) { case HDSPM_InputOptical: @@ -3017,63 +4508,22 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, insel = "Coaxial"; break; default: - insel = "Unknown"; - } - - switch (hdspm->control_register & HDSPM_SyncRefMask) { - case HDSPM_SyncRef_Word: - syncref = "WordClock"; - break; - case HDSPM_SyncRef_MADI: - syncref = "MADI"; - break; - default: - syncref = "Unknown"; + insel = "Unkown"; } - snd_iprintf(buffer, "Inputsel = %s, SyncRef = %s\n", insel, - syncref); snd_iprintf(buffer, - "ClearTrackMarker = %s, Transmit in %s Channel Mode, " - "Auto Input %s\n", - (hdspm-> - control_register & HDSPM_clr_tms) ? "on" : "off", - (hdspm-> - control_register & HDSPM_TX_64ch) ? "64" : "56", - (hdspm-> - control_register & HDSPM_AutoInp) ? "on" : "off"); + "ClearTrackMarker = %s, Transmit in %s Channel Mode, " + "Auto Input %s\n", + (hdspm->control_register & HDSPM_clr_tms) ? "on" : "off", + (hdspm->control_register & HDSPM_TX_64ch) ? "64" : "56", + (hdspm->control_register & HDSPM_AutoInp) ? "on" : "off"); + - switch (hdspm_clock_source(hdspm)) { - case HDSPM_CLOCK_SOURCE_AUTOSYNC: - clock_source = "AutoSync"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ: - clock_source = "Internal 32 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ: - clock_source = "Internal 44.1 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ: - clock_source = "Internal 48 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ: - clock_source = "Internal 64 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ: - clock_source = "Internal 88.2 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ: - clock_source = "Internal 96 kHz"; - break; - default: - clock_source = "Error"; - } - snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source); if (!(hdspm->control_register & HDSPM_ClockModeMaster)) - system_clock_mode = "Slave"; + system_clock_mode = "AutoSync"; else system_clock_mode = "Master"; - snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode); + snd_iprintf(buffer, "AutoSync Reference: %s\n", system_clock_mode); switch (hdspm_pref_sync_ref(hdspm)) { case HDSPM_SYNC_FROM_WORD: @@ -3082,15 +4532,21 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, case HDSPM_SYNC_FROM_MADI: pref_sync_ref = "MADI Sync"; break; + case HDSPM_SYNC_FROM_TCO: + pref_sync_ref = "TCO"; + break; + case HDSPM_SYNC_FROM_SYNC_IN: + pref_sync_ref = "Sync In"; + break; default: pref_sync_ref = "XXXX Clock"; break; } snd_iprintf(buffer, "Preferred Sync Reference: %s\n", - pref_sync_ref); + pref_sync_ref); snd_iprintf(buffer, "System Clock Frequency: %d\n", - hdspm->system_sample_rate); + hdspm->system_sample_rate); snd_iprintf(buffer, "--- Status:\n"); @@ -3099,12 +4555,18 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, x2 = status2 & HDSPM_wcSync; snd_iprintf(buffer, "Inputs MADI=%s, WordClock=%s\n", - (status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") : - "NoLock", - (status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") : - "NoLock"); + (status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") : + "NoLock", + (status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") : + "NoLock"); switch (hdspm_autosync_ref(hdspm)) { + case HDSPM_AUTOSYNC_FROM_SYNC_IN: + autosync_ref = "Sync In"; + break; + case HDSPM_AUTOSYNC_FROM_TCO: + autosync_ref = "TCO"; + break; case HDSPM_AUTOSYNC_FROM_WORD: autosync_ref = "Word Clock"; break; @@ -3119,15 +4581,15 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, break; } snd_iprintf(buffer, - "AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n", - autosync_ref, hdspm_external_sample_rate(hdspm), - (status & HDSPM_madiFreqMask) >> 22, - (status2 & HDSPM_wcFreqMask) >> 5); + "AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n", + autosync_ref, hdspm_external_sample_rate(hdspm), + (status & HDSPM_madiFreqMask) >> 22, + (status2 & HDSPM_wcFreqMask) >> 5); snd_iprintf(buffer, "Input: %s, Mode=%s\n", - (status & HDSPM_AB_int) ? "Coax" : "Optical", - (status & HDSPM_RX_64ch) ? "64 channels" : - "56 channels"); + (status & HDSPM_AB_int) ? "Coax" : "Optical", + (status & HDSPM_RX_64ch) ? "64 channels" : + "56 channels"); snd_iprintf(buffer, "\n"); } @@ -3142,8 +4604,6 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, unsigned int timecode; int pref_syncref; char *autosync_ref; - char *system_clock_mode; - char *clock_source; int x; status = hdspm_read(hdspm, HDSPM_statusRegister); @@ -3183,24 +4643,27 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF, hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF); snd_iprintf(buffer, - "Register: ctrl1=0x%x, status1=0x%x, status2=0x%x, " - "timecode=0x%x\n", - hdspm->control_register, - status, status2, timecode); + "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n", + hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF); + snd_iprintf(buffer, + "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, " + "status2=0x%x\n", + hdspm->control_register, hdspm->control2_register, + status, status2); snd_iprintf(buffer, "--- Settings ---\n"); x = 1 << (6 + hdspm_decode_latency(hdspm->control_register & - HDSPM_LatencyMask)); + HDSPM_LatencyMask)); snd_iprintf(buffer, "Size (Latency): %d samples (2 periods of %lu bytes)\n", x, (unsigned long) hdspm->period_bytes); - snd_iprintf(buffer, "Line out: %s, Precise Pointer: %s\n", + snd_iprintf(buffer, "Line out: %s\n", (hdspm-> - control_register & HDSPM_LineOut) ? "on " : "off", - (hdspm->precise_ptr) ? "on" : "off"); + control_register & HDSPM_LineOut) ? "on " : "off"); snd_iprintf(buffer, "ClearTrackMarker %s, Emphasis %s, Dolby %s\n", @@ -3211,46 +4674,6 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, (hdspm-> control_register & HDSPM_Dolby) ? "on" : "off"); - switch (hdspm_clock_source(hdspm)) { - case HDSPM_CLOCK_SOURCE_AUTOSYNC: - clock_source = "AutoSync"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ: - clock_source = "Internal 32 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ: - clock_source = "Internal 44.1 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ: - clock_source = "Internal 48 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ: - clock_source = "Internal 64 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ: - clock_source = "Internal 88.2 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ: - clock_source = "Internal 96 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ: - clock_source = "Internal 128 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ: - clock_source = "Internal 176.4 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ: - clock_source = "Internal 192 kHz"; - break; - default: - clock_source = "Error"; - } - snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source); - if (!(hdspm->control_register & HDSPM_ClockModeMaster)) - system_clock_mode = "Slave"; - else - system_clock_mode = "Master"; - snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode); pref_syncref = hdspm_pref_sync_ref(hdspm); if (pref_syncref == 0) @@ -3274,38 +4697,108 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, snd_iprintf(buffer, "--- Status:\n"); snd_iprintf(buffer, "Word: %s Frequency: %d\n", - (status & HDSPM_AES32_wcLock)? "Sync " : "No Lock", + (status & HDSPM_AES32_wcLock) ? "Sync " : "No Lock", HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF)); for (x = 0; x < 8; x++) { snd_iprintf(buffer, "AES%d: %s Frequency: %d\n", x+1, (status2 & (HDSPM_LockAES >> x)) ? - "Sync ": "No Lock", + "Sync " : "No Lock", HDSPM_bit2freq((timecode >> (4*x)) & 0xF)); } switch (hdspm_autosync_ref(hdspm)) { - case HDSPM_AES32_AUTOSYNC_FROM_NONE: autosync_ref="None"; break; - case HDSPM_AES32_AUTOSYNC_FROM_WORD: autosync_ref="Word Clock"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES1: autosync_ref="AES1"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES2: autosync_ref="AES2"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES3: autosync_ref="AES3"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES4: autosync_ref="AES4"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES5: autosync_ref="AES5"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES6: autosync_ref="AES6"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES7: autosync_ref="AES7"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES8: autosync_ref="AES8"; break; - default: autosync_ref = "---"; break; + case HDSPM_AES32_AUTOSYNC_FROM_NONE: + autosync_ref = "None"; break; + case HDSPM_AES32_AUTOSYNC_FROM_WORD: + autosync_ref = "Word Clock"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES1: + autosync_ref = "AES1"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES2: + autosync_ref = "AES2"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES3: + autosync_ref = "AES3"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES4: + autosync_ref = "AES4"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES5: + autosync_ref = "AES5"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES6: + autosync_ref = "AES6"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES7: + autosync_ref = "AES7"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES8: + autosync_ref = "AES8"; break; + default: + autosync_ref = "---"; break; } snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref); snd_iprintf(buffer, "\n"); } +static void +snd_hdspm_proc_read_raydat(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdspm *hdspm = entry->private_data; + unsigned int status1, status2, status3, control, i; + unsigned int lock, sync; + + status1 = hdspm_read(hdspm, HDSPM_RD_STATUS_1); /* s1 */ + status2 = hdspm_read(hdspm, HDSPM_RD_STATUS_2); /* freq */ + status3 = hdspm_read(hdspm, HDSPM_RD_STATUS_3); /* s2 */ + + control = hdspm->control_register; + + snd_iprintf(buffer, "STATUS1: 0x%08x\n", status1); + snd_iprintf(buffer, "STATUS2: 0x%08x\n", status2); + snd_iprintf(buffer, "STATUS3: 0x%08x\n", status3); + + + snd_iprintf(buffer, "\n*** CLOCK MODE\n\n"); + + snd_iprintf(buffer, "Clock mode : %s\n", + (hdspm_system_clock_mode(hdspm) == 0) ? "master" : "slave"); + snd_iprintf(buffer, "System frequency: %d Hz\n", + hdspm_get_system_sample_rate(hdspm)); + + snd_iprintf(buffer, "\n*** INPUT STATUS\n\n"); + + lock = 0x1; + sync = 0x100; + + for (i = 0; i < 8; i++) { + snd_iprintf(buffer, "s1_input %d: Lock %d, Sync %d, Freq %s\n", + i, + (status1 & lock) ? 1 : 0, + (status1 & sync) ? 1 : 0, + texts_freq[(status2 >> (i * 4)) & 0xF]); + + lock = lock<<1; + sync = sync<<1; + } + + snd_iprintf(buffer, "WC input: Lock %d, Sync %d, Freq %s\n", + (status1 & 0x1000000) ? 1 : 0, + (status1 & 0x2000000) ? 1 : 0, + texts_freq[(status1 >> 16) & 0xF]); + + snd_iprintf(buffer, "TCO input: Lock %d, Sync %d, Freq %s\n", + (status1 & 0x4000000) ? 1 : 0, + (status1 & 0x8000000) ? 1 : 0, + texts_freq[(status1 >> 20) & 0xF]); + + snd_iprintf(buffer, "SYNC IN: Lock %d, Sync %d, Freq %s\n", + (status3 & 0x400) ? 1 : 0, + (status3 & 0x800) ? 1 : 0, + texts_freq[(status2 >> 12) & 0xF]); + +} + #ifdef CONFIG_SND_DEBUG static void -snd_hdspm_proc_read_debug(struct snd_info_entry * entry, +snd_hdspm_proc_read_debug(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct hdspm *hdspm = entry->private_data; @@ -3322,16 +4815,68 @@ snd_hdspm_proc_read_debug(struct snd_info_entry * entry, #endif +static void snd_hdspm_proc_ports_in(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdspm *hdspm = entry->private_data; + int i; + + snd_iprintf(buffer, "# generated by hdspm\n"); + + for (i = 0; i < hdspm->max_channels_in; i++) { + snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_in[i]); + } +} + +static void snd_hdspm_proc_ports_out(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdspm *hdspm = entry->private_data; + int i; + + snd_iprintf(buffer, "# generated by hdspm\n"); + + for (i = 0; i < hdspm->max_channels_out; i++) { + snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_out[i]); + } +} + -static void __devinit snd_hdspm_proc_init(struct hdspm * hdspm) +static void __devinit snd_hdspm_proc_init(struct hdspm *hdspm) { struct snd_info_entry *entry; - if (!snd_card_proc_new(hdspm->card, "hdspm", &entry)) - snd_info_set_text_ops(entry, hdspm, - hdspm->is_aes32 ? - snd_hdspm_proc_read_aes32 : - snd_hdspm_proc_read_madi); + if (!snd_card_proc_new(hdspm->card, "hdspm", &entry)) { + switch (hdspm->io_type) { + case AES32: + snd_info_set_text_ops(entry, hdspm, + snd_hdspm_proc_read_aes32); + break; + case MADI: + snd_info_set_text_ops(entry, hdspm, + snd_hdspm_proc_read_madi); + break; + case MADIface: + /* snd_info_set_text_ops(entry, hdspm, + snd_hdspm_proc_read_madiface); */ + break; + case RayDAT: + snd_info_set_text_ops(entry, hdspm, + snd_hdspm_proc_read_raydat); + break; + case AIO: + break; + } + } + + if (!snd_card_proc_new(hdspm->card, "ports.in", &entry)) { + snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_ports_in); + } + + if (!snd_card_proc_new(hdspm->card, "ports.out", &entry)) { + snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_ports_out); + } + #ifdef CONFIG_SND_DEBUG /* debug file to read all hdspm registers */ if (!snd_card_proc_new(hdspm->card, "debug", &entry)) @@ -3341,47 +4886,48 @@ static void __devinit snd_hdspm_proc_init(struct hdspm * hdspm) } /*------------------------------------------------------------ - hdspm intitialize + hdspm intitialize ------------------------------------------------------------*/ static int snd_hdspm_set_defaults(struct hdspm * hdspm) { - unsigned int i; - /* ASSUMPTION: hdspm->lock is either held, or there is no need to hold it (e.g. during module initialization). - */ + */ /* set defaults: */ - if (hdspm->is_aes32) + hdspm->settings_register = 0; + + switch (hdspm->io_type) { + case MADI: + case MADIface: + hdspm->control_register = + 0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000; + break; + + case RayDAT: + case AIO: + hdspm->settings_register = 0x1 + 0x1000; + /* Magic values are: LAT_0, LAT_2, Master, freq1, tx64ch, inp_0, + * line_out */ + hdspm->control_register = + 0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000; + break; + + case AES32: hdspm->control_register = HDSPM_ClockModeMaster | /* Master Cloack Mode on */ - hdspm_encode_latency(7) | /* latency maximum = - * 8192 samples - */ + hdspm_encode_latency(7) | /* latency max=8192samples */ HDSPM_SyncRef0 | /* AES1 is syncclock */ HDSPM_LineOut | /* Analog output in */ HDSPM_Professional; /* Professional mode */ - else - hdspm->control_register = - HDSPM_ClockModeMaster | /* Master Cloack Mode on */ - hdspm_encode_latency(7) | /* latency maximum = - * 8192 samples - */ - HDSPM_InputCoaxial | /* Input Coax not Optical */ - HDSPM_SyncRef_MADI | /* Madi is syncclock */ - HDSPM_LineOut | /* Analog output in */ - HDSPM_TX_64ch | /* transmit in 64ch mode */ - HDSPM_AutoInp; /* AutoInput chossing (takeover) */ - - /* ! HDSPM_Frequency0|HDSPM_Frequency1 = 44.1khz */ - /* ! HDSPM_DoubleSpeed HDSPM_QuadSpeed = normal speed */ - /* ! HDSPM_clr_tms = do not clear bits in track marks */ + break; + } hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); - if (!hdspm->is_aes32) { + if (AES32 == hdspm->io_type) { /* No control2 register for AES32 */ #ifdef SNDRV_BIG_ENDIAN hdspm->control2_register = HDSPM_BIGENDIAN_MODE; @@ -3397,57 +4943,59 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm) all_in_all_mixer(hdspm, 0 * UNITY_GAIN); - if (line_outs_monitor[hdspm->dev]) { - - snd_printk(KERN_INFO "HDSPM: " - "sending all playback streams to line outs.\n"); - - for (i = 0; i < HDSPM_MIXER_CHANNELS; i++) { - if (hdspm_write_pb_gain(hdspm, i, i, UNITY_GAIN)) - return -EIO; - } + if (hdspm->io_type == AIO || hdspm->io_type == RayDAT) { + hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register); } /* set a default rate so that the channel map is set up. */ - hdspm->channel_map = channel_map_madi_ss; - hdspm_set_rate(hdspm, 44100, 1); + hdspm_set_rate(hdspm, 48000, 1); return 0; } /*------------------------------------------------------------ - interrupt + interrupt ------------------------------------------------------------*/ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id) { struct hdspm *hdspm = (struct hdspm *) dev_id; unsigned int status; - int audio; - int midi0; - int midi1; - unsigned int midi0status; - unsigned int midi1status; - int schedule = 0; + int i, audio, midi, schedule = 0; + /* cycles_t now; */ status = hdspm_read(hdspm, HDSPM_statusRegister); audio = status & HDSPM_audioIRQPending; - midi0 = status & HDSPM_midi0IRQPending; - midi1 = status & HDSPM_midi1IRQPending; + midi = status & (HDSPM_midi0IRQPending | HDSPM_midi1IRQPending | + HDSPM_midi2IRQPending | HDSPM_midi3IRQPending); + + /* now = get_cycles(); */ + /** + * LAT_2..LAT_0 period counter (win) counter (mac) + * 6 4096 ~256053425 ~514672358 + * 5 2048 ~128024983 ~257373821 + * 4 1024 ~64023706 ~128718089 + * 3 512 ~32005945 ~64385999 + * 2 256 ~16003039 ~32260176 + * 1 128 ~7998738 ~16194507 + * 0 64 ~3998231 ~8191558 + **/ + /* + snd_printk(KERN_INFO "snd_hdspm_interrupt %llu @ %llx\n", + now-hdspm->last_interrupt, status & 0xFFC0); + hdspm->last_interrupt = now; + */ - if (!audio && !midi0 && !midi1) + if (!audio && !midi) return IRQ_NONE; hdspm_write(hdspm, HDSPM_interruptConfirmation, 0); hdspm->irq_count++; - midi0status = hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xff; - midi1status = hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xff; if (audio) { - if (hdspm->capture_substream) snd_pcm_period_elapsed(hdspm->capture_substream); @@ -3455,118 +5003,44 @@ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id) snd_pcm_period_elapsed(hdspm->playback_substream); } - if (midi0 && midi0status) { - /* we disable interrupts for this input until processing - * is done - */ - hdspm->control_register &= ~HDSPM_Midi0InterruptEnable; - hdspm_write(hdspm, HDSPM_controlRegister, - hdspm->control_register); - hdspm->midi[0].pending = 1; - schedule = 1; - } - if (midi1 && midi1status) { - /* we disable interrupts for this input until processing - * is done - */ - hdspm->control_register &= ~HDSPM_Midi1InterruptEnable; - hdspm_write(hdspm, HDSPM_controlRegister, - hdspm->control_register); - hdspm->midi[1].pending = 1; - schedule = 1; + if (midi) { + i = 0; + while (i < hdspm->midiPorts) { + if ((hdspm_read(hdspm, + hdspm->midi[i].statusIn) & 0xff) && + (status & hdspm->midi[i].irq)) { + /* we disable interrupts for this input until + * processing is done + */ + hdspm->control_register &= ~hdspm->midi[i].ie; + hdspm_write(hdspm, HDSPM_controlRegister, + hdspm->control_register); + hdspm->midi[i].pending = 1; + schedule = 1; + } + + i++; + } + + if (schedule) + tasklet_hi_schedule(&hdspm->midi_tasklet); } - if (schedule) - tasklet_schedule(&hdspm->midi_tasklet); + return IRQ_HANDLED; } /*------------------------------------------------------------ - pcm interface + pcm interface ------------------------------------------------------------*/ -static snd_pcm_uframes_t snd_hdspm_hw_pointer(struct snd_pcm_substream * - substream) +static snd_pcm_uframes_t snd_hdspm_hw_pointer(struct snd_pcm_substream + *substream) { struct hdspm *hdspm = snd_pcm_substream_chip(substream); return hdspm_hw_pointer(hdspm); } -static char *hdspm_channel_buffer_location(struct hdspm * hdspm, - int stream, int channel) -{ - int mapped_channel; - - if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS)) - return NULL; - - mapped_channel = hdspm->channel_map[channel]; - if (mapped_channel < 0) - return NULL; - - if (stream == SNDRV_PCM_STREAM_CAPTURE) - return hdspm->capture_buffer + - mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES; - else - return hdspm->playback_buffer + - mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES; -} - - -/* dont know why need it ??? */ -static int snd_hdspm_playback_copy(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - void __user *src, snd_pcm_uframes_t count) -{ - struct hdspm *hdspm = snd_pcm_substream_chip(substream); - char *channel_buf; - - if (snd_BUG_ON(pos + count > HDSPM_CHANNEL_BUFFER_BYTES / 4)) - return -EINVAL; - - channel_buf = - hdspm_channel_buffer_location(hdspm, substream->pstr->stream, - channel); - - if (snd_BUG_ON(!channel_buf)) - return -EIO; - - return copy_from_user(channel_buf + pos * 4, src, count * 4); -} - -static int snd_hdspm_capture_copy(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - void __user *dst, snd_pcm_uframes_t count) -{ - struct hdspm *hdspm = snd_pcm_substream_chip(substream); - char *channel_buf; - - if (snd_BUG_ON(pos + count > HDSPM_CHANNEL_BUFFER_BYTES / 4)) - return -EINVAL; - - channel_buf = - hdspm_channel_buffer_location(hdspm, substream->pstr->stream, - channel); - if (snd_BUG_ON(!channel_buf)) - return -EIO; - return copy_to_user(dst, channel_buf + pos * 4, count * 4); -} - -static int snd_hdspm_hw_silence(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) -{ - struct hdspm *hdspm = snd_pcm_substream_chip(substream); - char *channel_buf; - - channel_buf = - hdspm_channel_buffer_location(hdspm, substream->pstr->stream, - channel); - if (snd_BUG_ON(!channel_buf)) - return -EIO; - memset(channel_buf + pos * 4, 0, count * 4); - return 0; -} static int snd_hdspm_reset(struct snd_pcm_substream *substream) { @@ -3589,7 +5063,7 @@ static int snd_hdspm_reset(struct snd_pcm_substream *substream) snd_pcm_group_for_each_entry(s, substream) { if (s == other) { oruntime->status->hw_ptr = - runtime->status->hw_ptr; + runtime->status->hw_ptr; break; } } @@ -3621,19 +5095,19 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, /* The other stream is open, and not by the same task as this one. Make sure that the parameters that matter are the same. - */ + */ if (params_rate(params) != hdspm->system_sample_rate) { spin_unlock_irq(&hdspm->lock); _snd_pcm_hw_param_setempty(params, - SNDRV_PCM_HW_PARAM_RATE); + SNDRV_PCM_HW_PARAM_RATE); return -EBUSY; } if (params_period_size(params) != hdspm->period_bytes / 4) { spin_unlock_irq(&hdspm->lock); _snd_pcm_hw_param_setempty(params, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + SNDRV_PCM_HW_PARAM_PERIOD_SIZE); return -EBUSY; } @@ -3646,18 +5120,20 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, spin_lock_irq(&hdspm->lock); err = hdspm_set_rate(hdspm, params_rate(params), 0); if (err < 0) { + snd_printk(KERN_INFO "err on hdspm_set_rate: %d\n", err); spin_unlock_irq(&hdspm->lock); _snd_pcm_hw_param_setempty(params, - SNDRV_PCM_HW_PARAM_RATE); + SNDRV_PCM_HW_PARAM_RATE); return err; } spin_unlock_irq(&hdspm->lock); err = hdspm_set_interrupt_interval(hdspm, - params_period_size(params)); + params_period_size(params)); if (err < 0) { + snd_printk(KERN_INFO "err on hdspm_set_interrupt_interval: %d\n", err); _snd_pcm_hw_param_setempty(params, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + SNDRV_PCM_HW_PARAM_PERIOD_SIZE); return err; } @@ -3667,10 +5143,13 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, /* malloc all buffer even if not enabled to get sure */ /* Update for MADI rev 204: we need to allocate for all channels, * otherwise it doesn't work at 96kHz */ + err = - snd_pcm_lib_malloc_pages(substream, HDSPM_DMA_AREA_BYTES); - if (err < 0) + snd_pcm_lib_malloc_pages(substream, HDSPM_DMA_AREA_BYTES); + if (err < 0) { + snd_printk(KERN_INFO "err on snd_pcm_lib_malloc_pages: %d\n", err); return err; + } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -3681,7 +5160,7 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, snd_hdspm_enable_out(hdspm, i, 1); hdspm->playback_buffer = - (unsigned char *) substream->runtime->dma_area; + (unsigned char *) substream->runtime->dma_area; snd_printdd("Allocated sample buffer for playback at %p\n", hdspm->playback_buffer); } else { @@ -3692,23 +5171,40 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, snd_hdspm_enable_in(hdspm, i, 1); hdspm->capture_buffer = - (unsigned char *) substream->runtime->dma_area; + (unsigned char *) substream->runtime->dma_area; snd_printdd("Allocated sample buffer for capture at %p\n", hdspm->capture_buffer); } + /* snd_printdd("Allocated sample buffer for %s at 0x%08X\n", substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture", snd_pcm_sgbuf_get_addr(substream, 0)); - */ + */ /* - snd_printdd("set_hwparams: %s %d Hz, %d channels, bs = %d\n", - substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? - "playback" : "capture", - params_rate(params), params_channels(params), - params_buffer_size(params)); - */ + snd_printdd("set_hwparams: %s %d Hz, %d channels, bs = %d\n", + substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + "playback" : "capture", + params_rate(params), params_channels(params), + params_buffer_size(params)); + */ + + + /* Switch to native float format if requested */ + if (SNDRV_PCM_FORMAT_FLOAT_LE == params_format(params)) { + if (!(hdspm->control_register & HDSPe_FLOAT_FORMAT)) + snd_printk(KERN_INFO "hdspm: Switching to native 32bit LE float format.\n"); + + hdspm->control_register |= HDSPe_FLOAT_FORMAT; + } else if (SNDRV_PCM_FORMAT_S32_LE == params_format(params)) { + if (hdspm->control_register & HDSPe_FLOAT_FORMAT) + snd_printk(KERN_INFO "hdspm: Switching to native 32bit LE integer format.\n"); + + hdspm->control_register &= ~HDSPe_FLOAT_FORMAT; + } + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + return 0; } @@ -3719,14 +5215,14 @@ static int snd_hdspm_hw_free(struct snd_pcm_substream *substream) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - /* params_channels(params) should be enough, + /* params_channels(params) should be enough, but to get sure in case of error */ - for (i = 0; i < HDSPM_MAX_CHANNELS; ++i) + for (i = 0; i < hdspm->max_channels_out; ++i) snd_hdspm_enable_out(hdspm, i, 0); hdspm->playback_buffer = NULL; } else { - for (i = 0; i < HDSPM_MAX_CHANNELS; ++i) + for (i = 0; i < hdspm->max_channels_in; ++i) snd_hdspm_enable_in(hdspm, i, 0); hdspm->capture_buffer = NULL; @@ -3738,37 +5234,58 @@ static int snd_hdspm_hw_free(struct snd_pcm_substream *substream) return 0; } + static int snd_hdspm_channel_info(struct snd_pcm_substream *substream, - struct snd_pcm_channel_info * info) + struct snd_pcm_channel_info *info) { struct hdspm *hdspm = snd_pcm_substream_chip(substream); - int mapped_channel; - if (snd_BUG_ON(info->channel >= HDSPM_MAX_CHANNELS)) - return -EINVAL; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (snd_BUG_ON(info->channel >= hdspm->max_channels_out)) { + snd_printk(KERN_INFO "snd_hdspm_channel_info: output channel out of range (%d)\n", info->channel); + return -EINVAL; + } - mapped_channel = hdspm->channel_map[info->channel]; - if (mapped_channel < 0) - return -EINVAL; + if (hdspm->channel_map_out[info->channel] < 0) { + snd_printk(KERN_INFO "snd_hdspm_channel_info: output channel %d mapped out\n", info->channel); + return -EINVAL; + } + + info->offset = hdspm->channel_map_out[info->channel] * + HDSPM_CHANNEL_BUFFER_BYTES; + } else { + if (snd_BUG_ON(info->channel >= hdspm->max_channels_in)) { + snd_printk(KERN_INFO "snd_hdspm_channel_info: input channel out of range (%d)\n", info->channel); + return -EINVAL; + } + + if (hdspm->channel_map_in[info->channel] < 0) { + snd_printk(KERN_INFO "snd_hdspm_channel_info: input channel %d mapped out\n", info->channel); + return -EINVAL; + } + + info->offset = hdspm->channel_map_in[info->channel] * + HDSPM_CHANNEL_BUFFER_BYTES; + } - info->offset = mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES; info->first = 0; info->step = 32; return 0; } + static int snd_hdspm_ioctl(struct snd_pcm_substream *substream, - unsigned int cmd, void *arg) + unsigned int cmd, void *arg) { switch (cmd) { case SNDRV_PCM_IOCTL1_RESET: return snd_hdspm_reset(substream); case SNDRV_PCM_IOCTL1_CHANNEL_INFO: - { - struct snd_pcm_channel_info *info = arg; - return snd_hdspm_channel_info(substream, info); - } + { + struct snd_pcm_channel_info *info = arg; + return snd_hdspm_channel_info(substream, info); + } default: break; } @@ -3815,19 +5332,19 @@ static int snd_hdspm_trigger(struct snd_pcm_substream *substream, int cmd) } if (cmd == SNDRV_PCM_TRIGGER_START) { if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) - && substream->stream == - SNDRV_PCM_STREAM_CAPTURE) + && substream->stream == + SNDRV_PCM_STREAM_CAPTURE) hdspm_silence_playback(hdspm); } else { if (running && - substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + substream->stream == SNDRV_PCM_STREAM_PLAYBACK) hdspm_silence_playback(hdspm); } } else { if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) hdspm_silence_playback(hdspm); } - _ok: +_ok: snd_pcm_trigger_done(substream, substream); if (!hdspm->running && running) hdspm_start_audio(hdspm); @@ -3844,8 +5361,18 @@ static int snd_hdspm_prepare(struct snd_pcm_substream *substream) return 0; } -static unsigned int period_sizes[] = - { 64, 128, 256, 512, 1024, 2048, 4096, 8192 }; +static unsigned int period_sizes_old[] = { + 64, 128, 256, 512, 1024, 2048, 4096 +}; + +static unsigned int period_sizes_new[] = { + 32, 64, 128, 256, 512, 1024, 2048, 4096 +}; + +/* RayDAT and AIO always have a buffer of 16384 samples per channel */ +static unsigned int raydat_aio_buffer_sizes[] = { + 16384 +}; static struct snd_pcm_hardware snd_hdspm_playback_subinfo = { .info = (SNDRV_PCM_INFO_MMAP | @@ -3866,9 +5393,9 @@ static struct snd_pcm_hardware snd_hdspm_playback_subinfo = { .buffer_bytes_max = HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS, .period_bytes_min = (64 * 4), - .period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS, + .period_bytes_max = (4096 * 4) * HDSPM_MAX_CHANNELS, .periods_min = 2, - .periods_max = 2, + .periods_max = 512, .fifo_size = 0 }; @@ -3891,20 +5418,66 @@ static struct snd_pcm_hardware snd_hdspm_capture_subinfo = { .buffer_bytes_max = HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS, .period_bytes_min = (64 * 4), - .period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS, + .period_bytes_max = (4096 * 4) * HDSPM_MAX_CHANNELS, .periods_min = 2, - .periods_max = 2, + .periods_max = 512, .fifo_size = 0 }; -static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = { - .count = ARRAY_SIZE(period_sizes), - .list = period_sizes, +static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes_old = { + .count = ARRAY_SIZE(period_sizes_old), + .list = period_sizes_old, + .mask = 0 +}; + +static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes_new = { + .count = ARRAY_SIZE(period_sizes_new), + .list = period_sizes_new, + .mask = 0 +}; + +static struct snd_pcm_hw_constraint_list hw_constraints_raydat_io_buffer = { + .count = ARRAY_SIZE(raydat_aio_buffer_sizes), + .list = raydat_aio_buffer_sizes, .mask = 0 }; +static int snd_hdspm_hw_rule_in_channels_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct hdspm *hdspm = rule->private; + struct snd_interval *c = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *r = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + + if (r->min > 96000 && r->max <= 192000) { + struct snd_interval t = { + .min = hdspm->qs_in_channels, + .max = hdspm->qs_in_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } else if (r->min > 48000 && r->max <= 96000) { + struct snd_interval t = { + .min = hdspm->ds_in_channels, + .max = hdspm->ds_in_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } else if (r->max < 64000) { + struct snd_interval t = { + .min = hdspm->ss_in_channels, + .max = hdspm->ss_in_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } + + return 0; +} -static int snd_hdspm_hw_rule_channels_rate(struct snd_pcm_hw_params *params, +static int snd_hdspm_hw_rule_out_channels_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule * rule) { struct hdspm *hdspm = rule->private; @@ -3913,25 +5486,33 @@ static int snd_hdspm_hw_rule_channels_rate(struct snd_pcm_hw_params *params, struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - if (r->min > 48000 && r->max <= 96000) { + if (r->min > 96000 && r->max <= 192000) { + struct snd_interval t = { + .min = hdspm->qs_out_channels, + .max = hdspm->qs_out_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } else if (r->min > 48000 && r->max <= 96000) { struct snd_interval t = { - .min = hdspm->ds_channels, - .max = hdspm->ds_channels, + .min = hdspm->ds_out_channels, + .max = hdspm->ds_out_channels, .integer = 1, }; return snd_interval_refine(c, &t); } else if (r->max < 64000) { struct snd_interval t = { - .min = hdspm->ss_channels, - .max = hdspm->ss_channels, + .min = hdspm->ss_out_channels, + .max = hdspm->ss_out_channels, .integer = 1, }; return snd_interval_refine(c, &t); + } else { } return 0; } -static int snd_hdspm_hw_rule_rate_channels(struct snd_pcm_hw_params *params, +static int snd_hdspm_hw_rule_rate_in_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule * rule) { struct hdspm *hdspm = rule->private; @@ -3940,42 +5521,92 @@ static int snd_hdspm_hw_rule_rate_channels(struct snd_pcm_hw_params *params, struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - if (c->min >= hdspm->ss_channels) { + if (c->min >= hdspm->ss_in_channels) { struct snd_interval t = { .min = 32000, .max = 48000, .integer = 1, }; return snd_interval_refine(r, &t); - } else if (c->max <= hdspm->ds_channels) { + } else if (c->max <= hdspm->qs_in_channels) { + struct snd_interval t = { + .min = 128000, + .max = 192000, + .integer = 1, + }; + return snd_interval_refine(r, &t); + } else if (c->max <= hdspm->ds_in_channels) { struct snd_interval t = { .min = 64000, .max = 96000, .integer = 1, }; + return snd_interval_refine(r, &t); + } + + return 0; +} +static int snd_hdspm_hw_rule_rate_out_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct hdspm *hdspm = rule->private; + struct snd_interval *c = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *r = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (c->min >= hdspm->ss_out_channels) { + struct snd_interval t = { + .min = 32000, + .max = 48000, + .integer = 1, + }; + return snd_interval_refine(r, &t); + } else if (c->max <= hdspm->qs_out_channels) { + struct snd_interval t = { + .min = 128000, + .max = 192000, + .integer = 1, + }; + return snd_interval_refine(r, &t); + } else if (c->max <= hdspm->ds_out_channels) { + struct snd_interval t = { + .min = 64000, + .max = 96000, + .integer = 1, + }; return snd_interval_refine(r, &t); } + return 0; } -static int snd_hdspm_hw_rule_channels(struct snd_pcm_hw_params *params, +static int snd_hdspm_hw_rule_in_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { unsigned int list[3]; struct hdspm *hdspm = rule->private; struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - if (hdspm->is_aes32) { - list[0] = hdspm->qs_channels; - list[1] = hdspm->ds_channels; - list[2] = hdspm->ss_channels; - return snd_interval_list(c, 3, list, 0); - } else { - list[0] = hdspm->ds_channels; - list[1] = hdspm->ss_channels; - return snd_interval_list(c, 2, list, 0); - } + + list[0] = hdspm->qs_in_channels; + list[1] = hdspm->ds_in_channels; + list[2] = hdspm->ss_in_channels; + return snd_interval_list(c, 3, list, 0); +} + +static int snd_hdspm_hw_rule_out_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + unsigned int list[3]; + struct hdspm *hdspm = rule->private; + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + list[0] = hdspm->qs_out_channels; + list[1] = hdspm->ds_out_channels; + list[2] = hdspm->ss_out_channels; + return snd_interval_list(c, 3, list, 0); } @@ -3999,6 +5630,7 @@ static int snd_hdspm_playback_open(struct snd_pcm_substream *substream) snd_pcm_set_sync(substream); + runtime->hw = snd_hdspm_playback_subinfo; if (hdspm->capture_substream == NULL) @@ -4011,24 +5643,38 @@ static int snd_hdspm_playback_open(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); - snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - &hw_constraints_period_sizes); + switch (hdspm->io_type) { + case AIO: + case RayDAT: + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + &hw_constraints_period_sizes_new); + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + &hw_constraints_raydat_io_buffer); + + break; + + default: + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + &hw_constraints_period_sizes_old); + } - if (hdspm->is_aes32) { + if (AES32 == hdspm->io_type) { snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hdspm_hw_constraints_aes32_sample_rates); } else { snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_channels, hdspm, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_hdspm_hw_rule_out_channels, hdspm, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_channels_rate, hdspm, - SNDRV_PCM_HW_PARAM_RATE, -1); + snd_hdspm_hw_rule_out_channels_rate, hdspm, + SNDRV_PCM_HW_PARAM_RATE, -1); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - snd_hdspm_hw_rule_rate_channels, hdspm, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_hdspm_hw_rule_rate_out_channels, hdspm, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); } return 0; } @@ -4066,22 +5712,36 @@ static int snd_hdspm_capture_open(struct snd_pcm_substream *substream) spin_unlock_irq(&hdspm->lock); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); - snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - &hw_constraints_period_sizes); - if (hdspm->is_aes32) { + switch (hdspm->io_type) { + case AIO: + case RayDAT: + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + &hw_constraints_period_sizes_new); + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + &hw_constraints_raydat_io_buffer); + break; + + default: + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + &hw_constraints_period_sizes_old); + } + + if (AES32 == hdspm->io_type) { snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hdspm_hw_constraints_aes32_sample_rates); } else { snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_channels, hdspm, + snd_hdspm_hw_rule_in_channels, hdspm, SNDRV_PCM_HW_PARAM_CHANNELS, -1); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_channels_rate, hdspm, + snd_hdspm_hw_rule_in_channels_rate, hdspm, SNDRV_PCM_HW_PARAM_RATE, -1); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - snd_hdspm_hw_rule_rate_channels, hdspm, + snd_hdspm_hw_rule_rate_in_channels, hdspm, SNDRV_PCM_HW_PARAM_CHANNELS, -1); } return 0; @@ -4100,41 +5760,136 @@ static int snd_hdspm_capture_release(struct snd_pcm_substream *substream) return 0; } -static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file, - unsigned int cmd, unsigned long arg) +static int snd_hdspm_hwdep_dummy_op(struct snd_hwdep *hw, struct file *file) +{ + /* we have nothing to initialize but the call is required */ + return 0; +} + +static inline int copy_u32_le(void __user *dest, void __iomem *src) +{ + u32 val = readl(src); + return copy_to_user(dest, &val, 4); +} + +static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long __user arg) { + void __user *argp = (void __user *)arg; struct hdspm *hdspm = hw->private_data; struct hdspm_mixer_ioctl mixer; - struct hdspm_config_info info; + struct hdspm_config info; + struct hdspm_status status; struct hdspm_version hdspm_version; - struct hdspm_peak_rms_ioctl rms; + struct hdspm_peak_rms levels; + struct hdspm_ltc ltc; + unsigned int statusregister; + long unsigned int s; + int i = 0; switch (cmd) { case SNDRV_HDSPM_IOCTL_GET_PEAK_RMS: - if (copy_from_user(&rms, (void __user *)arg, sizeof(rms))) + for (i = 0; i < HDSPM_MAX_CHANNELS; i++) { + levels.input_peaks[i] = + readl(hdspm->iobase + + HDSPM_MADI_INPUT_PEAK + i*4); + levels.playback_peaks[i] = + readl(hdspm->iobase + + HDSPM_MADI_PLAYBACK_PEAK + i*4); + levels.output_peaks[i] = + readl(hdspm->iobase + + HDSPM_MADI_OUTPUT_PEAK + i*4); + + levels.input_rms[i] = + ((uint64_t) readl(hdspm->iobase + + HDSPM_MADI_INPUT_RMS_H + i*4) << 32) | + (uint64_t) readl(hdspm->iobase + + HDSPM_MADI_INPUT_RMS_L + i*4); + levels.playback_rms[i] = + ((uint64_t)readl(hdspm->iobase + + HDSPM_MADI_PLAYBACK_RMS_H+i*4) << 32) | + (uint64_t)readl(hdspm->iobase + + HDSPM_MADI_PLAYBACK_RMS_L + i*4); + levels.output_rms[i] = + ((uint64_t)readl(hdspm->iobase + + HDSPM_MADI_OUTPUT_RMS_H + i*4) << 32) | + (uint64_t)readl(hdspm->iobase + + HDSPM_MADI_OUTPUT_RMS_L + i*4); + } + + if (hdspm->system_sample_rate > 96000) { + levels.speed = qs; + } else if (hdspm->system_sample_rate > 48000) { + levels.speed = ds; + } else { + levels.speed = ss; + } + levels.status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + + s = copy_to_user(argp, &levels, sizeof(struct hdspm_peak_rms)); + if (0 != s) { + /* snd_printk(KERN_ERR "copy_to_user(.., .., %lu): %lu + [Levels]\n", sizeof(struct hdspm_peak_rms), s); + */ return -EFAULT; - /* maybe there is a chance to memorymap in future - * so dont touch just copy - */ - if(copy_to_user_fromio((void __user *)rms.peak, - hdspm->iobase+HDSPM_MADI_peakrmsbase, - sizeof(struct hdspm_peak_rms)) != 0 ) + } + break; + + case SNDRV_HDSPM_IOCTL_GET_LTC: + ltc.ltc = hdspm_read(hdspm, HDSPM_RD_TCO); + i = hdspm_read(hdspm, HDSPM_RD_TCO + 4); + if (i & HDSPM_TCO1_LTC_Input_valid) { + switch (i & (HDSPM_TCO1_LTC_Format_LSB | + HDSPM_TCO1_LTC_Format_MSB)) { + case 0: + ltc.format = fps_24; + break; + case HDSPM_TCO1_LTC_Format_LSB: + ltc.format = fps_25; + break; + case HDSPM_TCO1_LTC_Format_MSB: + ltc.format = fps_2997; + break; + default: + ltc.format = 30; + break; + } + if (i & HDSPM_TCO1_set_drop_frame_flag) { + ltc.frame = drop_frame; + } else { + ltc.frame = full_frame; + } + } else { + ltc.format = format_invalid; + ltc.frame = frame_invalid; + } + if (i & HDSPM_TCO1_Video_Input_Format_NTSC) { + ltc.input_format = ntsc; + } else if (i & HDSPM_TCO1_Video_Input_Format_PAL) { + ltc.input_format = pal; + } else { + ltc.input_format = no_video; + } + + s = copy_to_user(argp, <c, sizeof(struct hdspm_ltc)); + if (0 != s) { + /* + snd_printk(KERN_ERR "copy_to_user(.., .., %lu): %lu [LTC]\n", sizeof(struct hdspm_ltc), s); */ return -EFAULT; + } break; - - case SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO: + case SNDRV_HDSPM_IOCTL_GET_CONFIG: - memset(&info, 0, sizeof(info)); spin_lock_irq(&hdspm->lock); info.pref_sync_ref = hdspm_pref_sync_ref(hdspm); info.wordclock_sync_check = hdspm_wc_sync_check(hdspm); info.system_sample_rate = hdspm->system_sample_rate; info.autosync_sample_rate = - hdspm_external_sample_rate(hdspm); + hdspm_external_sample_rate(hdspm); info.system_clock_mode = hdspm_system_clock_mode(hdspm); info.clock_source = hdspm_clock_source(hdspm); info.autosync_ref = hdspm_autosync_ref(hdspm); @@ -4145,10 +5900,58 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file, return -EFAULT; break; + case SNDRV_HDSPM_IOCTL_GET_STATUS: + status.card_type = hdspm->io_type; + + status.autosync_source = hdspm_autosync_ref(hdspm); + + status.card_clock = 110069313433624ULL; + status.master_period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ); + + switch (hdspm->io_type) { + case MADI: + case MADIface: + status.card_specific.madi.sync_wc = + hdspm_wc_sync_check(hdspm); + status.card_specific.madi.sync_madi = + hdspm_madi_sync_check(hdspm); + status.card_specific.madi.sync_tco = + hdspm_tco_sync_check(hdspm); + status.card_specific.madi.sync_in = + hdspm_sync_in_sync_check(hdspm); + + statusregister = + hdspm_read(hdspm, HDSPM_statusRegister); + status.card_specific.madi.madi_input = + (statusregister & HDSPM_AB_int) ? 1 : 0; + status.card_specific.madi.channel_format = + (statusregister & HDSPM_TX_64ch) ? 1 : 0; + /* TODO: Mac driver sets it when f_s>48kHz */ + status.card_specific.madi.frame_format = 0; + + default: + break; + } + + if (copy_to_user((void __user *) arg, &status, sizeof(status))) + return -EFAULT; + + + break; + case SNDRV_HDSPM_IOCTL_GET_VERSION: + hdspm_version.card_type = hdspm->io_type; + strncpy(hdspm_version.cardname, hdspm->card_name, + sizeof(hdspm_version.cardname)); + hdspm_version.serial = (hdspm_read(hdspm, + HDSPM_midiStatusIn0)>>8) & 0xFFFFFF; hdspm_version.firmware_rev = hdspm->firmware_rev; + hdspm_version.addons = 0; + if (hdspm->tco) + hdspm_version.addons |= HDSPM_ADDON_TCO; + if (copy_to_user((void __user *) arg, &hdspm_version, - sizeof(hdspm_version))) + sizeof(hdspm_version))) return -EFAULT; break; @@ -4156,7 +5959,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file, if (copy_from_user(&mixer, (void __user *)arg, sizeof(mixer))) return -EFAULT; if (copy_to_user((void __user *)mixer.mixer, hdspm->mixer, - sizeof(struct hdspm_mixer))) + sizeof(struct hdspm_mixer))) return -EFAULT; break; @@ -4175,8 +5978,6 @@ static struct snd_pcm_ops snd_hdspm_playback_ops = { .prepare = snd_hdspm_prepare, .trigger = snd_hdspm_trigger, .pointer = snd_hdspm_hw_pointer, - .copy = snd_hdspm_playback_copy, - .silence = snd_hdspm_hw_silence, .page = snd_pcm_sgbuf_ops_page, }; @@ -4189,7 +5990,6 @@ static struct snd_pcm_ops snd_hdspm_capture_ops = { .prepare = snd_hdspm_prepare, .trigger = snd_hdspm_trigger, .pointer = snd_hdspm_hw_pointer, - .copy = snd_hdspm_capture_copy, .page = snd_pcm_sgbuf_ops_page, }; @@ -4207,16 +6007,18 @@ static int __devinit snd_hdspm_create_hwdep(struct snd_card *card, hw->private_data = hdspm; strcpy(hw->name, "HDSPM hwdep interface"); + hw->ops.open = snd_hdspm_hwdep_dummy_op; hw->ops.ioctl = snd_hdspm_hwdep_ioctl; + hw->ops.release = snd_hdspm_hwdep_dummy_op; return 0; } /*------------------------------------------------------------ - memory interface + memory interface ------------------------------------------------------------*/ -static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm) +static int __devinit snd_hdspm_preallocate_memory(struct hdspm *hdspm) { int err; struct snd_pcm *pcm; @@ -4228,7 +6030,7 @@ static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm) err = snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_DEV_SG, + SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(hdspm->pci), wanted, wanted); @@ -4242,19 +6044,23 @@ static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm) return 0; } -static void hdspm_set_sgbuf(struct hdspm * hdspm, + +static void hdspm_set_sgbuf(struct hdspm *hdspm, struct snd_pcm_substream *substream, unsigned int reg, int channels) { int i; + + /* continuous memory segment */ for (i = 0; i < (channels * 16); i++) hdspm_write(hdspm, reg + 4 * i, - snd_pcm_sgbuf_get_addr(substream, 4096 * i)); + snd_pcm_sgbuf_get_addr(substream, 4096 * i)); } + /* ------------- ALSA Devices ---------------------------- */ static int __devinit snd_hdspm_create_pcm(struct snd_card *card, - struct hdspm * hdspm) + struct hdspm *hdspm) { struct snd_pcm *pcm; int err; @@ -4290,20 +6096,21 @@ static inline void snd_hdspm_initialize_midi_flush(struct hdspm * hdspm) static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card, struct hdspm * hdspm) { - int err; + int err, i; snd_printdd("Create card...\n"); err = snd_hdspm_create_pcm(card, hdspm); if (err < 0) return err; - err = snd_hdspm_create_midi(card, hdspm, 0); - if (err < 0) - return err; - - err = snd_hdspm_create_midi(card, hdspm, 1); - if (err < 0) - return err; + i = 0; + while (i < hdspm->midiPorts) { + err = snd_hdspm_create_midi(card, hdspm, i); + if (err < 0) { + return err; + } + i++; + } err = snd_hdspm_create_controls(card, hdspm); if (err < 0) @@ -4346,37 +6153,49 @@ static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card, } static int __devinit snd_hdspm_create(struct snd_card *card, - struct hdspm *hdspm, - int precise_ptr, int enable_monitor) -{ + struct hdspm *hdspm) { + struct pci_dev *pci = hdspm->pci; int err; unsigned long io_extent; hdspm->irq = -1; - - spin_lock_init(&hdspm->midi[0].lock); - spin_lock_init(&hdspm->midi[1].lock); - hdspm->card = card; spin_lock_init(&hdspm->lock); - tasklet_init(&hdspm->midi_tasklet, - hdspm_midi_tasklet, (unsigned long) hdspm); - pci_read_config_word(hdspm->pci, - PCI_CLASS_REVISION, &hdspm->firmware_rev); - - hdspm->is_aes32 = (hdspm->firmware_rev >= HDSPM_AESREVISION); + PCI_CLASS_REVISION, &hdspm->firmware_rev); strcpy(card->mixername, "Xilinx FPGA"); - if (hdspm->is_aes32) { - strcpy(card->driver, "HDSPAES32"); - hdspm->card_name = "RME HDSPM AES32"; - } else { - strcpy(card->driver, "HDSPM"); - hdspm->card_name = "RME HDSPM MADI"; + strcpy(card->driver, "HDSPM"); + + switch (hdspm->firmware_rev) { + case HDSPM_MADI_REV: + hdspm->io_type = MADI; + hdspm->card_name = "RME MADI"; + hdspm->midiPorts = 3; + break; + case HDSPM_RAYDAT_REV: + hdspm->io_type = RayDAT; + hdspm->card_name = "RME RayDAT"; + hdspm->midiPorts = 2; + break; + case HDSPM_AIO_REV: + hdspm->io_type = AIO; + hdspm->card_name = "RME AIO"; + hdspm->midiPorts = 1; + break; + case HDSPM_MADIFACE_REV: + hdspm->io_type = MADIface; + hdspm->card_name = "RME MADIface"; + hdspm->midiPorts = 1; + break; + case HDSPM_AES_REV: + hdspm->io_type = AES32; + hdspm->card_name = "RME AES32"; + hdspm->midiPorts = 2; + break; } err = pci_enable_device(pci); @@ -4393,22 +6212,21 @@ static int __devinit snd_hdspm_create(struct snd_card *card, io_extent = pci_resource_len(pci, 0); snd_printdd("grabbed memory region 0x%lx-0x%lx\n", - hdspm->port, hdspm->port + io_extent - 1); - + hdspm->port, hdspm->port + io_extent - 1); hdspm->iobase = ioremap_nocache(hdspm->port, io_extent); if (!hdspm->iobase) { snd_printk(KERN_ERR "HDSPM: " - "unable to remap region 0x%lx-0x%lx\n", - hdspm->port, hdspm->port + io_extent - 1); + "unable to remap region 0x%lx-0x%lx\n", + hdspm->port, hdspm->port + io_extent - 1); return -EBUSY; } snd_printdd("remapped region (0x%lx) 0x%lx-0x%lx\n", - (unsigned long)hdspm->iobase, hdspm->port, - hdspm->port + io_extent - 1); + (unsigned long)hdspm->iobase, hdspm->port, + hdspm->port + io_extent - 1); if (request_irq(pci->irq, snd_hdspm_interrupt, - IRQF_SHARED, "hdspm", hdspm)) { + IRQF_SHARED, "hdspm", hdspm)) { snd_printk(KERN_ERR "HDSPM: unable to use IRQ %d\n", pci->irq); return -EBUSY; } @@ -4416,23 +6234,195 @@ static int __devinit snd_hdspm_create(struct snd_card *card, snd_printdd("use IRQ %d\n", pci->irq); hdspm->irq = pci->irq; - hdspm->precise_ptr = precise_ptr; - - hdspm->monitor_outs = enable_monitor; snd_printdd("kmalloc Mixer memory of %zd Bytes\n", - sizeof(struct hdspm_mixer)); + sizeof(struct hdspm_mixer)); hdspm->mixer = kzalloc(sizeof(struct hdspm_mixer), GFP_KERNEL); if (!hdspm->mixer) { snd_printk(KERN_ERR "HDSPM: " - "unable to kmalloc Mixer memory of %d Bytes\n", - (int)sizeof(struct hdspm_mixer)); + "unable to kmalloc Mixer memory of %d Bytes\n", + (int)sizeof(struct hdspm_mixer)); return err; } - hdspm->ss_channels = MADI_SS_CHANNELS; - hdspm->ds_channels = MADI_DS_CHANNELS; - hdspm->qs_channels = MADI_QS_CHANNELS; + hdspm->port_names_in = NULL; + hdspm->port_names_out = NULL; + + switch (hdspm->io_type) { + case AES32: + break; + + case MADI: + case MADIface: + hdspm->ss_in_channels = hdspm->ss_out_channels = + MADI_SS_CHANNELS; + hdspm->ds_in_channels = hdspm->ds_out_channels = + MADI_DS_CHANNELS; + hdspm->qs_in_channels = hdspm->qs_out_channels = + MADI_QS_CHANNELS; + + hdspm->channel_map_in_ss = hdspm->channel_map_out_ss = + channel_map_unity_ss; + hdspm->channel_map_in_ds = hdspm->channel_map_out_ss = + channel_map_unity_ss; + hdspm->channel_map_in_qs = hdspm->channel_map_out_ss = + channel_map_unity_ss; + + hdspm->port_names_in_ss = hdspm->port_names_out_ss = + texts_ports_madi; + hdspm->port_names_in_ds = hdspm->port_names_out_ds = + texts_ports_madi; + hdspm->port_names_in_qs = hdspm->port_names_out_qs = + texts_ports_madi; + break; + + case AIO: + if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) { + snd_printk(KERN_INFO "HDSPM: AEB input board found, but not supported\n"); + } + + hdspm->ss_in_channels = AIO_IN_SS_CHANNELS; + hdspm->ds_in_channels = AIO_IN_DS_CHANNELS; + hdspm->qs_in_channels = AIO_IN_QS_CHANNELS; + hdspm->ss_out_channels = AIO_OUT_SS_CHANNELS; + hdspm->ds_out_channels = AIO_OUT_DS_CHANNELS; + hdspm->qs_out_channels = AIO_OUT_QS_CHANNELS; + + hdspm->channel_map_out_ss = channel_map_aio_out_ss; + hdspm->channel_map_out_ds = channel_map_aio_out_ds; + hdspm->channel_map_out_qs = channel_map_aio_out_qs; + + hdspm->channel_map_in_ss = channel_map_aio_in_ss; + hdspm->channel_map_in_ds = channel_map_aio_in_ds; + hdspm->channel_map_in_qs = channel_map_aio_in_qs; + + hdspm->port_names_in_ss = texts_ports_aio_in_ss; + hdspm->port_names_out_ss = texts_ports_aio_out_ss; + hdspm->port_names_in_ds = texts_ports_aio_in_ds; + hdspm->port_names_out_ds = texts_ports_aio_out_ds; + hdspm->port_names_in_qs = texts_ports_aio_in_qs; + hdspm->port_names_out_qs = texts_ports_aio_out_qs; + + break; + + case RayDAT: + hdspm->ss_in_channels = hdspm->ss_out_channels = + RAYDAT_SS_CHANNELS; + hdspm->ds_in_channels = hdspm->ds_out_channels = + RAYDAT_DS_CHANNELS; + hdspm->qs_in_channels = hdspm->qs_out_channels = + RAYDAT_QS_CHANNELS; + + hdspm->max_channels_in = RAYDAT_SS_CHANNELS; + hdspm->max_channels_out = RAYDAT_SS_CHANNELS; + + hdspm->channel_map_in_ss = hdspm->channel_map_out_ss = + channel_map_raydat_ss; + hdspm->channel_map_in_ds = hdspm->channel_map_out_ds = + channel_map_raydat_ds; + hdspm->channel_map_in_qs = hdspm->channel_map_out_qs = + channel_map_raydat_qs; + hdspm->channel_map_in = hdspm->channel_map_out = + channel_map_raydat_ss; + + hdspm->port_names_in_ss = hdspm->port_names_out_ss = + texts_ports_raydat_ss; + hdspm->port_names_in_ds = hdspm->port_names_out_ds = + texts_ports_raydat_ds; + hdspm->port_names_in_qs = hdspm->port_names_out_qs = + texts_ports_raydat_qs; + + + break; + + } + + /* TCO detection */ + switch (hdspm->io_type) { + case AIO: + case RayDAT: + if (hdspm_read(hdspm, HDSPM_statusRegister2) & + HDSPM_s2_tco_detect) { + hdspm->midiPorts++; + hdspm->tco = kzalloc(sizeof(struct hdspm_tco), + GFP_KERNEL); + if (NULL != hdspm->tco) { + hdspm_tco_write(hdspm); + } + snd_printk(KERN_INFO "HDSPM: AIO/RayDAT TCO module found\n"); + } else { + hdspm->tco = NULL; + } + break; + + case MADI: + if (hdspm_read(hdspm, HDSPM_statusRegister) & HDSPM_tco_detect) { + hdspm->midiPorts++; + hdspm->tco = kzalloc(sizeof(struct hdspm_tco), + GFP_KERNEL); + if (NULL != hdspm->tco) { + hdspm_tco_write(hdspm); + } + snd_printk(KERN_INFO "HDSPM: MADI TCO module found\n"); + } else { + hdspm->tco = NULL; + } + break; + + default: + hdspm->tco = NULL; + } + + /* texts */ + switch (hdspm->io_type) { + case AES32: + if (hdspm->tco) { + hdspm->texts_autosync = texts_autosync_aes_tco; + hdspm->texts_autosync_items = 10; + } else { + hdspm->texts_autosync = texts_autosync_aes; + hdspm->texts_autosync_items = 9; + } + break; + + case MADI: + if (hdspm->tco) { + hdspm->texts_autosync = texts_autosync_madi_tco; + hdspm->texts_autosync_items = 4; + } else { + hdspm->texts_autosync = texts_autosync_madi; + hdspm->texts_autosync_items = 3; + } + break; + + case MADIface: + + break; + + case RayDAT: + if (hdspm->tco) { + hdspm->texts_autosync = texts_autosync_raydat_tco; + hdspm->texts_autosync_items = 9; + } else { + hdspm->texts_autosync = texts_autosync_raydat; + hdspm->texts_autosync_items = 8; + } + break; + + case AIO: + if (hdspm->tco) { + hdspm->texts_autosync = texts_autosync_aio_tco; + hdspm->texts_autosync_items = 6; + } else { + hdspm->texts_autosync = texts_autosync_aio; + hdspm->texts_autosync_items = 5; + } + break; + + } + + tasklet_init(&hdspm->midi_tasklet, + hdspm_midi_tasklet, (unsigned long) hdspm); snd_printdd("create alsa devices.\n"); err = snd_hdspm_create_alsa_devices(card, hdspm); @@ -4444,6 +6434,7 @@ static int __devinit snd_hdspm_create(struct snd_card *card, return 0; } + static int snd_hdspm_free(struct hdspm * hdspm) { @@ -4452,7 +6443,8 @@ static int snd_hdspm_free(struct hdspm * hdspm) /* stop th audio, and cancel all interrupts */ hdspm->control_register &= ~(HDSPM_Start | HDSPM_AudioInterruptEnable | - HDSPM_Midi0InterruptEnable | HDSPM_Midi1InterruptEnable); + HDSPM_Midi0InterruptEnable | HDSPM_Midi1InterruptEnable | + HDSPM_Midi2InterruptEnable | HDSPM_Midi3InterruptEnable); hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); } @@ -4472,6 +6464,7 @@ static int snd_hdspm_free(struct hdspm * hdspm) return 0; } + static void snd_hdspm_card_free(struct snd_card *card) { struct hdspm *hdspm = card->private_data; @@ -4480,6 +6473,7 @@ static void snd_hdspm_card_free(struct snd_card *card) snd_hdspm_free(hdspm); } + static int __devinit snd_hdspm_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { @@ -4496,7 +6490,7 @@ static int __devinit snd_hdspm_probe(struct pci_dev *pci, } err = snd_card_create(index[dev], id[dev], - THIS_MODULE, sizeof(struct hdspm), &card); + THIS_MODULE, sizeof(struct hdspm), &card); if (err < 0) return err; @@ -4507,16 +6501,25 @@ static int __devinit snd_hdspm_probe(struct pci_dev *pci, snd_card_set_dev(card, &pci->dev); - err = snd_hdspm_create(card, hdspm, precise_ptr[dev], - enable_monitor[dev]); + err = snd_hdspm_create(card, hdspm); if (err < 0) { snd_card_free(card); return err; } - strcpy(card->shortname, "HDSPM MADI"); - sprintf(card->longname, "%s at 0x%lx, irq %d", hdspm->card_name, - hdspm->port, hdspm->irq); + if (hdspm->io_type != MADIface) { + sprintf(card->shortname, "%s_%x", + hdspm->card_name, + (hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF); + sprintf(card->longname, "%s S/N 0x%x at 0x%lx, irq %d", + hdspm->card_name, + (hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF, + hdspm->port, hdspm->irq); + } else { + sprintf(card->shortname, "%s", hdspm->card_name); + sprintf(card->longname, "%s at 0x%lx, irq %d", + hdspm->card_name, hdspm->port, hdspm->irq); + } err = snd_card_register(card); if (err < 0) { -- cgit v1.2.3 From 55a57606b26665870f2993dc53a43daad157dbcd Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Thu, 27 Jan 2011 11:23:15 +0100 Subject: ALSA: [hdspm] Move static mapping arrays to .c As requested by Takashi and Jaroslav, these arrays should not be in the header file. Signed-off-by: Adrian Knoth Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- include/sound/hdspm.h | 170 --------------------------------------------- sound/pci/rme9652/hdspm.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+), 170 deletions(-) (limited to 'include') diff --git a/include/sound/hdspm.h b/include/sound/hdspm.h index c3f18194b08e..1774ff5ff632 100644 --- a/include/sound/hdspm.h +++ b/include/sound/hdspm.h @@ -225,175 +225,5 @@ typedef struct hdspm_version hdspm_version_t; typedef struct hdspm_channelfader snd_hdspm_channelfader_t; typedef struct hdspm_mixer hdspm_mixer_t; -/* These tables map the ALSA channels 1..N to the channels that we - need to use in order to find the relevant channel buffer. RME - refers to this kind of mapping as between "the ADAT channel and - the DMA channel." We index it using the logical audio channel, - and the value is the DMA channel (i.e. channel buffer number) - where the data for that channel can be read/written from/to. -*/ - -char channel_map_unity_ss[HDSPM_MAX_CHANNELS] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63 -}; - -char channel_map_unity_ds[HDSPM_MAX_CHANNELS] = { - 0, 2, 4, 6, 8, 10, 12, 14, - 16, 18, 20, 22, 24, 26, 28, 30, - 32, 34, 36, 38, 40, 42, 44, 46, - 48, 50, 52, 54, 56, 58, 60, 62, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -}; - -char channel_map_unity_qs[HDSPM_MAX_CHANNELS] = { - 0, 4, 8, 12, 16, 20, 24, 28, - 32, 36, 40, 44, 48, 52, 56, 60, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -}; - -char channel_map_raydat_ss[HDSPM_MAX_CHANNELS] = { - 4, 5, 6, 7, 8, 9, 10, 11, /* ADAT 1 */ - 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT 2 */ - 20, 21, 22, 23, 24, 25, 26, 27, /* ADAT 3 */ - 28, 29, 30, 31, 32, 33, 34, 35, /* ADAT 4 */ - 0, 1, /* AES */ - 2, 3, /* SPDIF */ - -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -}; - -char channel_map_raydat_ds[HDSPM_MAX_CHANNELS] = { - 4, 5, 6, 7, /* ADAT 1 */ - 8, 9, 10, 11, /* ADAT 2 */ - 12, 13, 14, 15, /* ADAT 3 */ - 16, 17, 18, 19, /* ADAT 4 */ - 0, 1, /* AES */ - 2, 3, /* SPDIF */ - -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -}; - -char channel_map_raydat_qs[HDSPM_MAX_CHANNELS] = { - 4, 5, /* ADAT 1 */ - 6, 7, /* ADAT 2 */ - 8, 9, /* ADAT 3 */ - 10, 11, /* ADAT 4 */ - 0, 1, /* AES */ - 2, 3, /* SPDIF */ - -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -}; - -char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = { - 0, 1, /* line in */ - 8, 9, /* aes in, */ - 10, 11, /* spdif in */ - 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT in */ - -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -}; - -char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = { - 0, 1, /* line out */ - 8, 9, /* aes out */ - 10, 11, /* spdif out */ - 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT out */ - 6, 7, /* phone out */ - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -}; - -char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = { - 0, 1, /* line in */ - 8, 9, /* aes in */ - 10, 11, /* spdif in */ - 12, 14, 16, 18, /* adat in */ - -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 -}; - -char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = { - 0, 1, /* line out */ - 8, 9, /* aes out */ - 10, 11, /* spdif out */ - 12, 14, 16, 18, /* adat out */ - 6, 7, /* phone out */ - -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 -}; - -char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = { - 0, 1, /* line in */ - 8, 9, /* aes in */ - 10, 11, /* spdif in */ - 12, 16, /* adat in */ - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 -}; - -char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = { - 0, 1, /* line out */ - 8, 9, /* aes out */ - 10, 11, /* spdif out */ - 12, 16, /* adat out */ - 6, 7, /* phone out */ - -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 -}; #endif diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 2db871d9a007..28a1eb3f4d02 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -673,6 +673,177 @@ static char *texts_ports_aio_out_qs[] = { "Phone.L", "Phone.R" }; +/* These tables map the ALSA channels 1..N to the channels that we + need to use in order to find the relevant channel buffer. RME + refers to this kind of mapping as between "the ADAT channel and + the DMA channel." We index it using the logical audio channel, + and the value is the DMA channel (i.e. channel buffer number) + where the data for that channel can be read/written from/to. +*/ + +static char channel_map_unity_ss[HDSPM_MAX_CHANNELS] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63 +}; + +static char channel_map_unity_ds[HDSPM_MAX_CHANNELS] = { + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, 50, 52, 54, 56, 58, 60, 62, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_unity_qs[HDSPM_MAX_CHANNELS] = { + 0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_raydat_ss[HDSPM_MAX_CHANNELS] = { + 4, 5, 6, 7, 8, 9, 10, 11, /* ADAT 1 */ + 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT 2 */ + 20, 21, 22, 23, 24, 25, 26, 27, /* ADAT 3 */ + 28, 29, 30, 31, 32, 33, 34, 35, /* ADAT 4 */ + 0, 1, /* AES */ + 2, 3, /* SPDIF */ + -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_raydat_ds[HDSPM_MAX_CHANNELS] = { + 4, 5, 6, 7, /* ADAT 1 */ + 8, 9, 10, 11, /* ADAT 2 */ + 12, 13, 14, 15, /* ADAT 3 */ + 16, 17, 18, 19, /* ADAT 4 */ + 0, 1, /* AES */ + 2, 3, /* SPDIF */ + -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_raydat_qs[HDSPM_MAX_CHANNELS] = { + 4, 5, /* ADAT 1 */ + 6, 7, /* ADAT 2 */ + 8, 9, /* ADAT 3 */ + 10, 11, /* ADAT 4 */ + 0, 1, /* AES */ + 2, 3, /* SPDIF */ + -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line in */ + 8, 9, /* aes in, */ + 10, 11, /* spdif in */ + 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT in */ + -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line out */ + 8, 9, /* aes out */ + 10, 11, /* spdif out */ + 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT out */ + 6, 7, /* phone out */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line in */ + 8, 9, /* aes in */ + 10, 11, /* spdif in */ + 12, 14, 16, 18, /* adat in */ + -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line out */ + 8, 9, /* aes out */ + 10, 11, /* spdif out */ + 12, 14, 16, 18, /* adat out */ + 6, 7, /* phone out */ + -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line in */ + 8, 9, /* aes in */ + 10, 11, /* spdif in */ + 12, 16, /* adat in */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line out */ + 8, 9, /* aes out */ + 10, 11, /* spdif out */ + 12, 16, /* adat out */ + 6, 7, /* phone out */ + -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + struct hdspm_midi { struct hdspm *hdspm; int id; -- cgit v1.2.3 From 92578c0b8078f6919f9b47e7e16a1cf770bd127b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jan 2011 15:24:55 +0100 Subject: kthread: Replace deprecated spinlock initialization SPIN_LOCK_UNLOCK is deprecated. Use the lockdep capable variant instead. Signed-off-by: Thomas Gleixner --- include/linux/kthread.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/kthread.h b/include/linux/kthread.h index ce0775aa64c3..7ff16f7d3ed4 100644 --- a/include/linux/kthread.h +++ b/include/linux/kthread.h @@ -64,7 +64,7 @@ struct kthread_work { }; #define KTHREAD_WORKER_INIT(worker) { \ - .lock = SPIN_LOCK_UNLOCKED, \ + .lock = __SPIN_LOCK_UNLOCKED((worker).lock), \ .work_list = LIST_HEAD_INIT((worker).work_list), \ } -- cgit v1.2.3 From d04fa5a3ba06c3b7a1c4a6860d0fa4825507a755 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jan 2011 15:30:09 +0100 Subject: locking: Remove deprecated lock initializers Last users are gone. Remove the left overs. Signed-off-by: Thomas Gleixner --- Documentation/spinlocks.txt | 24 +----------------------- include/linux/rwlock_types.h | 8 -------- include/linux/spinlock_types.h | 8 -------- scripts/checkpatch.pl | 5 ----- 4 files changed, 1 insertion(+), 44 deletions(-) (limited to 'include') diff --git a/Documentation/spinlocks.txt b/Documentation/spinlocks.txt index 178c831b907d..2e3c64b1a6a5 100644 --- a/Documentation/spinlocks.txt +++ b/Documentation/spinlocks.txt @@ -86,7 +86,7 @@ to change the variables it has to get an exclusive write lock. The routines look the same as above: - rwlock_t xxx_lock = RW_LOCK_UNLOCKED; + rwlock_t xxx_lock = __RW_LOCK_UNLOCKED(xxx_lock); unsigned long flags; @@ -196,25 +196,3 @@ appropriate: For static initialization, use DEFINE_SPINLOCK() / DEFINE_RWLOCK() or __SPIN_LOCK_UNLOCKED() / __RW_LOCK_UNLOCKED() as appropriate. - -SPIN_LOCK_UNLOCKED and RW_LOCK_UNLOCKED are deprecated. These interfere -with lockdep state tracking. - -Most of the time, you can simply turn: - static spinlock_t xxx_lock = SPIN_LOCK_UNLOCKED; -into: - static DEFINE_SPINLOCK(xxx_lock); - -Static structure member variables go from: - - struct foo bar { - .lock = SPIN_LOCK_UNLOCKED; - }; - -to: - - struct foo bar { - .lock = __SPIN_LOCK_UNLOCKED(bar.lock); - }; - -Declaration of static rw_locks undergo a similar transformation. diff --git a/include/linux/rwlock_types.h b/include/linux/rwlock_types.h index bd31808c7d8e..cc0072e93e36 100644 --- a/include/linux/rwlock_types.h +++ b/include/linux/rwlock_types.h @@ -43,14 +43,6 @@ typedef struct { RW_DEP_MAP_INIT(lockname) } #endif -/* - * RW_LOCK_UNLOCKED defeat lockdep state tracking and is hence - * deprecated. - * - * Please use DEFINE_RWLOCK() or __RW_LOCK_UNLOCKED() as appropriate. - */ -#define RW_LOCK_UNLOCKED __RW_LOCK_UNLOCKED(old_style_rw_init) - #define DEFINE_RWLOCK(x) rwlock_t x = __RW_LOCK_UNLOCKED(x) #endif /* __LINUX_RWLOCK_TYPES_H */ diff --git a/include/linux/spinlock_types.h b/include/linux/spinlock_types.h index 851b7783720d..73548eb13a5d 100644 --- a/include/linux/spinlock_types.h +++ b/include/linux/spinlock_types.h @@ -81,14 +81,6 @@ typedef struct spinlock { #define __SPIN_LOCK_UNLOCKED(lockname) \ (spinlock_t ) __SPIN_LOCK_INITIALIZER(lockname) -/* - * SPIN_LOCK_UNLOCKED defeats lockdep state tracking and is hence - * deprecated. - * Please use DEFINE_SPINLOCK() or __SPIN_LOCK_UNLOCKED() as - * appropriate. - */ -#define SPIN_LOCK_UNLOCKED __SPIN_LOCK_UNLOCKED(old_style_spin_init) - #define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x) #include diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 4c0383da1c9a..58848e3e392c 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2654,11 +2654,6 @@ sub process { WARN("Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt\n" . $herecurr); } -# SPIN_LOCK_UNLOCKED & RW_LOCK_UNLOCKED are deprecated - if ($line =~ /\b(SPIN_LOCK_UNLOCKED|RW_LOCK_UNLOCKED)/) { - ERROR("Use of $1 is deprecated: see Documentation/spinlocks.txt\n" . $herecurr); - } - # warn about #if 0 if ($line =~ /^.\s*\#\s*if\s+0\b/) { CHK("if this code is redundant consider removing it\n" . -- cgit v1.2.3 From c16a87ce063f79e0ec7d25ce2950e1bc6db03c72 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 26 Jan 2011 20:05:50 +0000 Subject: rwsem: Cleanup includes All rwsem implementations include the same headers. Include them from include/linux/rwsem.h Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra Cc: David Howells Cc: Benjamin Herrenschmidt Cc: Matt Turner Acked-by: Tony Luck Acked-by: Heiko Carstens Cc: Paul Mundt Acked-by: David Miller Cc: Chris Zankel LKML-Reference: <20110126195833.483520950@linutronix.de> --- arch/alpha/include/asm/rwsem.h | 4 ---- arch/ia64/include/asm/rwsem.h | 3 --- arch/powerpc/include/asm/rwsem.h | 5 ----- arch/s390/include/asm/rwsem.h | 5 ----- arch/sh/include/asm/rwsem.h | 5 ----- arch/sparc/include/asm/rwsem.h | 6 ------ arch/x86/include/asm/rwsem.h | 6 ------ arch/xtensa/include/asm/rwsem.h | 6 ------ include/linux/rwsem-spinlock.h | 8 -------- include/linux/rwsem.h | 3 +++ 10 files changed, 3 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/arch/alpha/include/asm/rwsem.h b/arch/alpha/include/asm/rwsem.h index 19c9cc7ed94b..839a3fa20944 100644 --- a/arch/alpha/include/asm/rwsem.h +++ b/arch/alpha/include/asm/rwsem.h @@ -13,10 +13,6 @@ #ifdef __KERNEL__ #include -#include -#include - -struct rwsem_waiter; extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); diff --git a/arch/ia64/include/asm/rwsem.h b/arch/ia64/include/asm/rwsem.h index 215d5454c7d3..9bcf0792b01c 100644 --- a/arch/ia64/include/asm/rwsem.h +++ b/arch/ia64/include/asm/rwsem.h @@ -25,9 +25,6 @@ #error "Please don't include directly, use instead." #endif -#include -#include - #include /* diff --git a/arch/powerpc/include/asm/rwsem.h b/arch/powerpc/include/asm/rwsem.h index 8447d89fbe72..c12abbe1d06f 100644 --- a/arch/powerpc/include/asm/rwsem.h +++ b/arch/powerpc/include/asm/rwsem.h @@ -13,11 +13,6 @@ * by Paul Mackerras . */ -#include -#include -#include -#include - /* * the semaphore definition */ diff --git a/arch/s390/include/asm/rwsem.h b/arch/s390/include/asm/rwsem.h index 423fdda2322d..9cc8592b33bb 100644 --- a/arch/s390/include/asm/rwsem.h +++ b/arch/s390/include/asm/rwsem.h @@ -43,11 +43,6 @@ #ifdef __KERNEL__ -#include -#include - -struct rwsem_waiter; - extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *); extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *); extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *); diff --git a/arch/sh/include/asm/rwsem.h b/arch/sh/include/asm/rwsem.h index 06e2251a5e48..df6f34623c54 100644 --- a/arch/sh/include/asm/rwsem.h +++ b/arch/sh/include/asm/rwsem.h @@ -11,11 +11,6 @@ #endif #ifdef __KERNEL__ -#include -#include -#include -#include - /* * the semaphore definition */ diff --git a/arch/sparc/include/asm/rwsem.h b/arch/sparc/include/asm/rwsem.h index a2b4302869bc..902d36bf150d 100644 --- a/arch/sparc/include/asm/rwsem.h +++ b/arch/sparc/include/asm/rwsem.h @@ -12,12 +12,6 @@ #endif #ifdef __KERNEL__ - -#include -#include - -struct rwsem_waiter; - struct rw_semaphore { signed long count; #define RWSEM_UNLOCKED_VALUE 0x00000000L diff --git a/arch/x86/include/asm/rwsem.h b/arch/x86/include/asm/rwsem.h index d1e41b0f9b60..a626cff86041 100644 --- a/arch/x86/include/asm/rwsem.h +++ b/arch/x86/include/asm/rwsem.h @@ -37,14 +37,8 @@ #endif #ifdef __KERNEL__ - -#include -#include -#include #include -struct rwsem_waiter; - extern asmregparm struct rw_semaphore * rwsem_down_read_failed(struct rw_semaphore *sem); extern asmregparm struct rw_semaphore * diff --git a/arch/xtensa/include/asm/rwsem.h b/arch/xtensa/include/asm/rwsem.h index 9d32f6874393..1be2102c648e 100644 --- a/arch/xtensa/include/asm/rwsem.h +++ b/arch/xtensa/include/asm/rwsem.h @@ -16,12 +16,6 @@ #ifndef _LINUX_RWSEM_H #error "Please don't include directly, use instead." #endif - -#include -#include -#include -#include - /* * the semaphore definition */ diff --git a/include/linux/rwsem-spinlock.h b/include/linux/rwsem-spinlock.h index bdfcc2527970..8c0dc7fc07a4 100644 --- a/include/linux/rwsem-spinlock.h +++ b/include/linux/rwsem-spinlock.h @@ -12,15 +12,7 @@ #error "please don't include linux/rwsem-spinlock.h directly, use linux/rwsem.h instead" #endif -#include -#include - #ifdef __KERNEL__ - -#include - -struct rwsem_waiter; - /* * the rw-semaphore definition * - if activity is 0 then there are no active readers or writers diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index efd348fe8ca7..496296d12d62 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -11,6 +11,9 @@ #include #include +#include +#include + #include #include -- cgit v1.2.3 From 1c8ed640d918290ddc1de5ada02ef6686a733c9f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 26 Jan 2011 20:05:56 +0000 Subject: rwsem: Move duplicate struct rwsem declaration to linux/rwsem.h The difference between these declarations is the data type of the count member and the lack of lockdep in some architectures/ long is equivivalent to signed long and the #ifdef guarded dep_map member does not hurt anyone. Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra Cc: David Howells Cc: Benjamin Herrenschmidt Cc: Matt Turner Acked-by: Tony Luck Acked-by: Heiko Carstens Cc: Paul Mundt Acked-by: David Miller Cc: Chris Zankel LKML-Reference: <20110126195833.679641914@linutronix.de> Signed-off-by: Thomas Gleixner --- arch/alpha/include/asm/rwsem.h | 8 -------- arch/ia64/include/asm/rwsem.h | 9 --------- arch/powerpc/include/asm/rwsem.h | 9 --------- arch/s390/include/asm/rwsem.h | 12 ------------ arch/sh/include/asm/rwsem.h | 12 +----------- arch/sparc/include/asm/rwsem.h | 9 +-------- arch/x86/include/asm/rwsem.h | 12 ------------ arch/xtensa/include/asm/rwsem.h | 9 +-------- include/linux/rwsem.h | 13 ++++++++++++- 9 files changed, 15 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/arch/alpha/include/asm/rwsem.h b/arch/alpha/include/asm/rwsem.h index 839a3fa20944..3e5619ead29d 100644 --- a/arch/alpha/include/asm/rwsem.h +++ b/arch/alpha/include/asm/rwsem.h @@ -19,20 +19,12 @@ extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *); extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); -/* - * the semaphore definition - */ -struct rw_semaphore { - long count; #define RWSEM_UNLOCKED_VALUE 0x0000000000000000L #define RWSEM_ACTIVE_BIAS 0x0000000000000001L #define RWSEM_ACTIVE_MASK 0x00000000ffffffffL #define RWSEM_WAITING_BIAS (-0x0000000100000000L) #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) - spinlock_t wait_lock; - struct list_head wait_list; -}; #define __RWSEM_INITIALIZER(name) \ { RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait_lock), \ diff --git a/arch/ia64/include/asm/rwsem.h b/arch/ia64/include/asm/rwsem.h index 9bcf0792b01c..1fe465804dc7 100644 --- a/arch/ia64/include/asm/rwsem.h +++ b/arch/ia64/include/asm/rwsem.h @@ -27,15 +27,6 @@ #include -/* - * the semaphore definition - */ -struct rw_semaphore { - signed long count; - spinlock_t wait_lock; - struct list_head wait_list; -}; - #define RWSEM_UNLOCKED_VALUE __IA64_UL_CONST(0x0000000000000000) #define RWSEM_ACTIVE_BIAS (1L) #define RWSEM_ACTIVE_MASK (0xffffffffL) diff --git a/arch/powerpc/include/asm/rwsem.h b/arch/powerpc/include/asm/rwsem.h index c12abbe1d06f..bc1acc229223 100644 --- a/arch/powerpc/include/asm/rwsem.h +++ b/arch/powerpc/include/asm/rwsem.h @@ -28,15 +28,6 @@ #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) -struct rw_semaphore { - long count; - spinlock_t wait_lock; - struct list_head wait_list; -#ifdef CONFIG_DEBUG_LOCK_ALLOC - struct lockdep_map dep_map; -#endif -}; - #ifdef CONFIG_DEBUG_LOCK_ALLOC # define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname } #else diff --git a/arch/s390/include/asm/rwsem.h b/arch/s390/include/asm/rwsem.h index 9cc8592b33bb..6e075f1d97b4 100644 --- a/arch/s390/include/asm/rwsem.h +++ b/arch/s390/include/asm/rwsem.h @@ -49,18 +49,6 @@ extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *); extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *); extern struct rw_semaphore *rwsem_downgrade_write(struct rw_semaphore *); -/* - * the semaphore definition - */ -struct rw_semaphore { - signed long count; - spinlock_t wait_lock; - struct list_head wait_list; -#ifdef CONFIG_DEBUG_LOCK_ALLOC - struct lockdep_map dep_map; -#endif -}; - #ifndef __s390x__ #define RWSEM_UNLOCKED_VALUE 0x00000000 #define RWSEM_ACTIVE_BIAS 0x00000001 diff --git a/arch/sh/include/asm/rwsem.h b/arch/sh/include/asm/rwsem.h index df6f34623c54..dffc62589f79 100644 --- a/arch/sh/include/asm/rwsem.h +++ b/arch/sh/include/asm/rwsem.h @@ -11,23 +11,13 @@ #endif #ifdef __KERNEL__ -/* - * the semaphore definition - */ -struct rw_semaphore { - long count; + #define RWSEM_UNLOCKED_VALUE 0x00000000 #define RWSEM_ACTIVE_BIAS 0x00000001 #define RWSEM_ACTIVE_MASK 0x0000ffff #define RWSEM_WAITING_BIAS (-0x00010000) #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) - spinlock_t wait_lock; - struct list_head wait_list; -#ifdef CONFIG_DEBUG_LOCK_ALLOC - struct lockdep_map dep_map; -#endif -}; #ifdef CONFIG_DEBUG_LOCK_ALLOC # define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname } diff --git a/arch/sparc/include/asm/rwsem.h b/arch/sparc/include/asm/rwsem.h index 902d36bf150d..4c16d1de2ab5 100644 --- a/arch/sparc/include/asm/rwsem.h +++ b/arch/sparc/include/asm/rwsem.h @@ -12,20 +12,13 @@ #endif #ifdef __KERNEL__ -struct rw_semaphore { - signed long count; + #define RWSEM_UNLOCKED_VALUE 0x00000000L #define RWSEM_ACTIVE_BIAS 0x00000001L #define RWSEM_ACTIVE_MASK 0xffffffffL #define RWSEM_WAITING_BIAS (-RWSEM_ACTIVE_MASK-1) #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) - spinlock_t wait_lock; - struct list_head wait_list; -#ifdef CONFIG_DEBUG_LOCK_ALLOC - struct lockdep_map dep_map; -#endif -}; #ifdef CONFIG_DEBUG_LOCK_ALLOC # define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname } diff --git a/arch/x86/include/asm/rwsem.h b/arch/x86/include/asm/rwsem.h index c30206c2bbf9..995cfe4c985d 100644 --- a/arch/x86/include/asm/rwsem.h +++ b/arch/x86/include/asm/rwsem.h @@ -49,8 +49,6 @@ extern asmregparm struct rw_semaphore * rwsem_downgrade_wake(struct rw_semaphore *sem); /* - * the semaphore definition - * * The bias values and the counter type limits the number of * potential readers/writers to 32767 for 32 bits and 2147483647 * for 64 bits. @@ -68,22 +66,12 @@ extern asmregparm struct rw_semaphore * #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) -struct rw_semaphore { - long count; - spinlock_t wait_lock; - struct list_head wait_list; -#ifdef CONFIG_DEBUG_LOCK_ALLOC - struct lockdep_map dep_map; -#endif -}; - #ifdef CONFIG_DEBUG_LOCK_ALLOC # define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname } #else # define __RWSEM_DEP_MAP_INIT(lockname) #endif - #define __RWSEM_INITIALIZER(name) \ { \ RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait_lock), \ diff --git a/arch/xtensa/include/asm/rwsem.h b/arch/xtensa/include/asm/rwsem.h index 1be2102c648e..585cab9b0bdf 100644 --- a/arch/xtensa/include/asm/rwsem.h +++ b/arch/xtensa/include/asm/rwsem.h @@ -16,20 +16,13 @@ #ifndef _LINUX_RWSEM_H #error "Please don't include directly, use instead." #endif -/* - * the semaphore definition - */ -struct rw_semaphore { - signed long count; + #define RWSEM_UNLOCKED_VALUE 0x00000000 #define RWSEM_ACTIVE_BIAS 0x00000001 #define RWSEM_ACTIVE_MASK 0x0000ffff #define RWSEM_WAITING_BIAS (-0x00010000) #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) - spinlock_t wait_lock; - struct list_head wait_list; -}; #define __RWSEM_INITIALIZER(name) \ { RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait_lock), \ diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index 496296d12d62..e8be18edb664 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -22,7 +22,18 @@ struct rw_semaphore; #ifdef CONFIG_RWSEM_GENERIC_SPINLOCK #include /* use a generic implementation */ #else -#include /* use an arch-specific implementation */ +/* All arch specific implementations share the same struct */ +struct rw_semaphore { + long count; + spinlock_t wait_lock; + struct list_head wait_list; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif +}; + +/* Include the arch specific part */ +#include #endif /* -- cgit v1.2.3 From 12249b34414dba7f386aadcf6be7ca36c6878300 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 26 Jan 2011 20:06:00 +0000 Subject: rwsem: Move duplicate init macros and functions to linux/rwsem.h The rwsem initializers and related macros and functions are mostly the same. Some of them lack the lockdep initializer, but having it in place does not matter for architectures which do not support lockdep. powerpc, sparc, x86: No functional change sh, s390: Removes the duplicate init_rwsem (inline and #define) alpha, ia64, xtensa: Use the lockdep capable init function in lib/rwsem.c which is just uninlining the init function for the LOCKDEP=n case Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra Cc: David Howells Cc: Benjamin Herrenschmidt Cc: Matt Turner Acked-by: Tony Luck Acked-by: Heiko Carstens Cc: Paul Mundt Acked-by: David Miller Cc: Chris Zankel LKML-Reference: <20110126195833.771812729@linutronix.de> --- arch/alpha/include/asm/rwsem.h | 14 -------------- arch/ia64/include/asm/rwsem.h | 15 --------------- arch/powerpc/include/asm/rwsem.h | 27 --------------------------- arch/s390/include/asm/rwsem.h | 35 ----------------------------------- arch/sh/include/asm/rwsem.h | 31 ------------------------------- arch/sparc/include/asm/rwsem.h | 23 ----------------------- arch/x86/include/asm/rwsem.h | 25 ------------------------- arch/xtensa/include/asm/rwsem.h | 14 -------------- include/linux/rwsem-spinlock.h | 23 +---------------------- include/linux/rwsem.h | 25 +++++++++++++++++++++++++ 10 files changed, 26 insertions(+), 206 deletions(-) (limited to 'include') diff --git a/arch/alpha/include/asm/rwsem.h b/arch/alpha/include/asm/rwsem.h index 3e5619ead29d..c9f5b90e926e 100644 --- a/arch/alpha/include/asm/rwsem.h +++ b/arch/alpha/include/asm/rwsem.h @@ -26,20 +26,6 @@ extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) -#define __RWSEM_INITIALIZER(name) \ - { RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait_lock), \ - LIST_HEAD_INIT((name).wait_list) } - -#define DECLARE_RWSEM(name) \ - struct rw_semaphore name = __RWSEM_INITIALIZER(name) - -static inline void init_rwsem(struct rw_semaphore *sem) -{ - sem->count = RWSEM_UNLOCKED_VALUE; - spin_lock_init(&sem->wait_lock); - INIT_LIST_HEAD(&sem->wait_list); -} - static inline void __down_read(struct rw_semaphore *sem) { long oldcount; diff --git a/arch/ia64/include/asm/rwsem.h b/arch/ia64/include/asm/rwsem.h index 1fe465804dc7..379854630e9d 100644 --- a/arch/ia64/include/asm/rwsem.h +++ b/arch/ia64/include/asm/rwsem.h @@ -34,26 +34,11 @@ #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) -#define __RWSEM_INITIALIZER(name) \ - { RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait_lock), \ - LIST_HEAD_INIT((name).wait_list) } - -#define DECLARE_RWSEM(name) \ - struct rw_semaphore name = __RWSEM_INITIALIZER(name) - extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); -static inline void -init_rwsem (struct rw_semaphore *sem) -{ - sem->count = RWSEM_UNLOCKED_VALUE; - spin_lock_init(&sem->wait_lock); - INIT_LIST_HEAD(&sem->wait_list); -} - /* * lock for reading */ diff --git a/arch/powerpc/include/asm/rwsem.h b/arch/powerpc/include/asm/rwsem.h index bc1acc229223..f86fdf743afb 100644 --- a/arch/powerpc/include/asm/rwsem.h +++ b/arch/powerpc/include/asm/rwsem.h @@ -28,38 +28,11 @@ #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) -#ifdef CONFIG_DEBUG_LOCK_ALLOC -# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname } -#else -# define __RWSEM_DEP_MAP_INIT(lockname) -#endif - -#define __RWSEM_INITIALIZER(name) \ -{ \ - RWSEM_UNLOCKED_VALUE, \ - __SPIN_LOCK_UNLOCKED((name).wait_lock), \ - LIST_HEAD_INIT((name).wait_list) \ - __RWSEM_DEP_MAP_INIT(name) \ -} - -#define DECLARE_RWSEM(name) \ - struct rw_semaphore name = __RWSEM_INITIALIZER(name) - extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); -extern void __init_rwsem(struct rw_semaphore *sem, const char *name, - struct lock_class_key *key); - -#define init_rwsem(sem) \ - do { \ - static struct lock_class_key __key; \ - \ - __init_rwsem((sem), #sem, &__key); \ - } while (0) - /* * lock for reading */ diff --git a/arch/s390/include/asm/rwsem.h b/arch/s390/include/asm/rwsem.h index 6e075f1d97b4..f0f527756ee1 100644 --- a/arch/s390/include/asm/rwsem.h +++ b/arch/s390/include/asm/rwsem.h @@ -63,41 +63,6 @@ extern struct rw_semaphore *rwsem_downgrade_write(struct rw_semaphore *); #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) -/* - * initialisation - */ - -#ifdef CONFIG_DEBUG_LOCK_ALLOC -# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname } -#else -# define __RWSEM_DEP_MAP_INIT(lockname) -#endif - -#define __RWSEM_INITIALIZER(name) \ - { RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait.lock), \ - LIST_HEAD_INIT((name).wait_list) __RWSEM_DEP_MAP_INIT(name) } - -#define DECLARE_RWSEM(name) \ - struct rw_semaphore name = __RWSEM_INITIALIZER(name) - -static inline void init_rwsem(struct rw_semaphore *sem) -{ - sem->count = RWSEM_UNLOCKED_VALUE; - spin_lock_init(&sem->wait_lock); - INIT_LIST_HEAD(&sem->wait_list); -} - -extern void __init_rwsem(struct rw_semaphore *sem, const char *name, - struct lock_class_key *key); - -#define init_rwsem(sem) \ -do { \ - static struct lock_class_key __key; \ - \ - __init_rwsem((sem), #sem, &__key); \ -} while (0) - - /* * lock for reading */ diff --git a/arch/sh/include/asm/rwsem.h b/arch/sh/include/asm/rwsem.h index dffc62589f79..798699d0687b 100644 --- a/arch/sh/include/asm/rwsem.h +++ b/arch/sh/include/asm/rwsem.h @@ -19,42 +19,11 @@ #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) -#ifdef CONFIG_DEBUG_LOCK_ALLOC -# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname } -#else -# define __RWSEM_DEP_MAP_INIT(lockname) -#endif - -#define __RWSEM_INITIALIZER(name) \ - { RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait_lock), \ - LIST_HEAD_INIT((name).wait_list) \ - __RWSEM_DEP_MAP_INIT(name) } - -#define DECLARE_RWSEM(name) \ - struct rw_semaphore name = __RWSEM_INITIALIZER(name) - extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); -extern void __init_rwsem(struct rw_semaphore *sem, const char *name, - struct lock_class_key *key); - -#define init_rwsem(sem) \ -do { \ - static struct lock_class_key __key; \ - \ - __init_rwsem((sem), #sem, &__key); \ -} while (0) - -static inline void init_rwsem(struct rw_semaphore *sem) -{ - sem->count = RWSEM_UNLOCKED_VALUE; - spin_lock_init(&sem->wait_lock); - INIT_LIST_HEAD(&sem->wait_list); -} - /* * lock for reading */ diff --git a/arch/sparc/include/asm/rwsem.h b/arch/sparc/include/asm/rwsem.h index 4c16d1de2ab5..79f7c1ca6f91 100644 --- a/arch/sparc/include/asm/rwsem.h +++ b/arch/sparc/include/asm/rwsem.h @@ -20,34 +20,11 @@ #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) -#ifdef CONFIG_DEBUG_LOCK_ALLOC -# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname } -#else -# define __RWSEM_DEP_MAP_INIT(lockname) -#endif - -#define __RWSEM_INITIALIZER(name) \ -{ RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait_lock), \ - LIST_HEAD_INIT((name).wait_list) __RWSEM_DEP_MAP_INIT(name) } - -#define DECLARE_RWSEM(name) \ - struct rw_semaphore name = __RWSEM_INITIALIZER(name) - extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); -extern void __init_rwsem(struct rw_semaphore *sem, const char *name, - struct lock_class_key *key); - -#define init_rwsem(sem) \ -do { \ - static struct lock_class_key __key; \ - \ - __init_rwsem((sem), #sem, &__key); \ -} while (0) - /* * lock for reading */ diff --git a/arch/x86/include/asm/rwsem.h b/arch/x86/include/asm/rwsem.h index 995cfe4c985d..c0c91b4ecc81 100644 --- a/arch/x86/include/asm/rwsem.h +++ b/arch/x86/include/asm/rwsem.h @@ -66,31 +66,6 @@ extern asmregparm struct rw_semaphore * #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) -#ifdef CONFIG_DEBUG_LOCK_ALLOC -# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname } -#else -# define __RWSEM_DEP_MAP_INIT(lockname) -#endif - -#define __RWSEM_INITIALIZER(name) \ -{ \ - RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait_lock), \ - LIST_HEAD_INIT((name).wait_list) __RWSEM_DEP_MAP_INIT(name) \ -} - -#define DECLARE_RWSEM(name) \ - struct rw_semaphore name = __RWSEM_INITIALIZER(name) - -extern void __init_rwsem(struct rw_semaphore *sem, const char *name, - struct lock_class_key *key); - -#define init_rwsem(sem) \ -do { \ - static struct lock_class_key __key; \ - \ - __init_rwsem((sem), #sem, &__key); \ -} while (0) - /* * lock for reading */ diff --git a/arch/xtensa/include/asm/rwsem.h b/arch/xtensa/include/asm/rwsem.h index 585cab9b0bdf..2fa420212353 100644 --- a/arch/xtensa/include/asm/rwsem.h +++ b/arch/xtensa/include/asm/rwsem.h @@ -24,25 +24,11 @@ #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) -#define __RWSEM_INITIALIZER(name) \ - { RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait_lock), \ - LIST_HEAD_INIT((name).wait_list) } - -#define DECLARE_RWSEM(name) \ - struct rw_semaphore name = __RWSEM_INITIALIZER(name) - extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem); extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); -static inline void init_rwsem(struct rw_semaphore *sem) -{ - sem->count = RWSEM_UNLOCKED_VALUE; - spin_lock_init(&sem->wait_lock); - INIT_LIST_HEAD(&sem->wait_list); -} - /* * lock for reading */ diff --git a/include/linux/rwsem-spinlock.h b/include/linux/rwsem-spinlock.h index 8c0dc7fc07a4..34701241b673 100644 --- a/include/linux/rwsem-spinlock.h +++ b/include/linux/rwsem-spinlock.h @@ -29,28 +29,7 @@ struct rw_semaphore { #endif }; -#ifdef CONFIG_DEBUG_LOCK_ALLOC -# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname } -#else -# define __RWSEM_DEP_MAP_INIT(lockname) -#endif - -#define __RWSEM_INITIALIZER(name) \ -{ 0, __SPIN_LOCK_UNLOCKED(name.wait_lock), LIST_HEAD_INIT((name).wait_list) \ - __RWSEM_DEP_MAP_INIT(name) } - -#define DECLARE_RWSEM(name) \ - struct rw_semaphore name = __RWSEM_INITIALIZER(name) - -extern void __init_rwsem(struct rw_semaphore *sem, const char *name, - struct lock_class_key *key); - -#define init_rwsem(sem) \ -do { \ - static struct lock_class_key __key; \ - \ - __init_rwsem((sem), #sem, &__key); \ -} while (0) +#define RWSEM_UNLOCKED_VALUE 0x00000000 extern void __down_read(struct rw_semaphore *sem); extern int __down_read_trylock(struct rw_semaphore *sem); diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index e8be18edb664..8970c3e802e2 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -36,6 +36,31 @@ struct rw_semaphore { #include #endif +/* Common initializer macros and functions */ + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname } +#else +# define __RWSEM_DEP_MAP_INIT(lockname) +#endif + +#define __RWSEM_INITIALIZER(name) \ + { RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED(name.wait_lock), \ + LIST_HEAD_INIT((name).wait_list) __RWSEM_DEP_MAP_INIT(name) } + +#define DECLARE_RWSEM(name) \ + struct rw_semaphore name = __RWSEM_INITIALIZER(name) + +extern void __init_rwsem(struct rw_semaphore *sem, const char *name, + struct lock_class_key *key); + +#define init_rwsem(sem) \ +do { \ + static struct lock_class_key __key; \ + \ + __init_rwsem((sem), #sem, &__key); \ +} while (0) + /* * lock for reading */ -- cgit v1.2.3 From 41e5887fa39ab272d9266a09cbefdef270e28b93 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 26 Jan 2011 20:06:03 +0000 Subject: rwsem: Unify the duplicate rwsem_is_locked() inlines Instead of having the same implementation in each architecture, move it to linux/rwsem.h and remove the duplicates. It's unlikely that an arch will ever implement something different, but we can deal with that when it happens. Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra Cc: David Howells Cc: Benjamin Herrenschmidt Cc: Matt Turner Acked-by: Tony Luck Acked-by: Heiko Carstens Cc: Paul Mundt Acked-by: David Miller Cc: Chris Zankel LKML-Reference: <20110126195833.876773757@linutronix.de> Signed-off-by: Thomas Gleixner --- arch/alpha/include/asm/rwsem.h | 5 ----- arch/ia64/include/asm/rwsem.h | 5 ----- arch/powerpc/include/asm/rwsem.h | 5 ----- arch/s390/include/asm/rwsem.h | 5 ----- arch/sh/include/asm/rwsem.h | 5 ----- arch/sparc/include/asm/rwsem.h | 5 ----- arch/x86/include/asm/rwsem.h | 5 ----- arch/xtensa/include/asm/rwsem.h | 5 ----- include/linux/rwsem.h | 7 +++++++ 9 files changed, 7 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/arch/alpha/include/asm/rwsem.h b/arch/alpha/include/asm/rwsem.h index c9f5b90e926e..fc7f54337c3a 100644 --- a/arch/alpha/include/asm/rwsem.h +++ b/arch/alpha/include/asm/rwsem.h @@ -224,10 +224,5 @@ static inline long rwsem_atomic_update(long val, struct rw_semaphore *sem) #endif } -static inline int rwsem_is_locked(struct rw_semaphore *sem) -{ - return (sem->count != 0); -} - #endif /* __KERNEL__ */ #endif /* _ALPHA_RWSEM_H */ diff --git a/arch/ia64/include/asm/rwsem.h b/arch/ia64/include/asm/rwsem.h index 379854630e9d..148b61a96b8c 100644 --- a/arch/ia64/include/asm/rwsem.h +++ b/arch/ia64/include/asm/rwsem.h @@ -147,9 +147,4 @@ __downgrade_write (struct rw_semaphore *sem) #define rwsem_atomic_add(delta, sem) atomic64_add(delta, (atomic64_t *)(&(sem)->count)) #define rwsem_atomic_update(delta, sem) atomic64_add_return(delta, (atomic64_t *)(&(sem)->count)) -static inline int rwsem_is_locked(struct rw_semaphore *sem) -{ - return (sem->count != 0); -} - #endif /* _ASM_IA64_RWSEM_H */ diff --git a/arch/powerpc/include/asm/rwsem.h b/arch/powerpc/include/asm/rwsem.h index f86fdf743afb..38e8e5c508d5 100644 --- a/arch/powerpc/include/asm/rwsem.h +++ b/arch/powerpc/include/asm/rwsem.h @@ -133,10 +133,5 @@ static inline long rwsem_atomic_update(long delta, struct rw_semaphore *sem) return atomic_long_add_return(delta, (atomic_long_t *)&sem->count); } -static inline int rwsem_is_locked(struct rw_semaphore *sem) -{ - return sem->count != 0; -} - #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_RWSEM_H */ diff --git a/arch/s390/include/asm/rwsem.h b/arch/s390/include/asm/rwsem.h index f0f527756ee1..80522dcb6cc2 100644 --- a/arch/s390/include/asm/rwsem.h +++ b/arch/s390/include/asm/rwsem.h @@ -325,10 +325,5 @@ static inline long rwsem_atomic_update(long delta, struct rw_semaphore *sem) return new; } -static inline int rwsem_is_locked(struct rw_semaphore *sem) -{ - return (sem->count != 0); -} - #endif /* __KERNEL__ */ #endif /* _S390_RWSEM_H */ diff --git a/arch/sh/include/asm/rwsem.h b/arch/sh/include/asm/rwsem.h index 798699d0687b..2f8cf9761eb5 100644 --- a/arch/sh/include/asm/rwsem.h +++ b/arch/sh/include/asm/rwsem.h @@ -133,10 +133,5 @@ static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem) return atomic_add_return(delta, (atomic_t *)(&sem->count)); } -static inline int rwsem_is_locked(struct rw_semaphore *sem) -{ - return (sem->count != 0); -} - #endif /* __KERNEL__ */ #endif /* _ASM_SH_RWSEM_H */ diff --git a/arch/sparc/include/asm/rwsem.h b/arch/sparc/include/asm/rwsem.h index 79f7c1ca6f91..4f4391fd5658 100644 --- a/arch/sparc/include/asm/rwsem.h +++ b/arch/sparc/include/asm/rwsem.h @@ -124,11 +124,6 @@ static inline long rwsem_atomic_update(long delta, struct rw_semaphore *sem) return atomic64_add_return(delta, (atomic64_t *)(&sem->count)); } -static inline int rwsem_is_locked(struct rw_semaphore *sem) -{ - return (sem->count != 0); -} - #endif /* __KERNEL__ */ #endif /* _SPARC64_RWSEM_H */ diff --git a/arch/x86/include/asm/rwsem.h b/arch/x86/include/asm/rwsem.h index c0c91b4ecc81..776d76fe2f90 100644 --- a/arch/x86/include/asm/rwsem.h +++ b/arch/x86/include/asm/rwsem.h @@ -222,10 +222,5 @@ static inline long rwsem_atomic_update(long delta, struct rw_semaphore *sem) return tmp + delta; } -static inline int rwsem_is_locked(struct rw_semaphore *sem) -{ - return (sem->count != 0); -} - #endif /* __KERNEL__ */ #endif /* _ASM_X86_RWSEM_H */ diff --git a/arch/xtensa/include/asm/rwsem.h b/arch/xtensa/include/asm/rwsem.h index 2fa420212353..da61633ccba8 100644 --- a/arch/xtensa/include/asm/rwsem.h +++ b/arch/xtensa/include/asm/rwsem.h @@ -133,9 +133,4 @@ static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem) return atomic_add_return(delta, (atomic_t *)(&sem->count)); } -static inline int rwsem_is_locked(struct rw_semaphore *sem) -{ - return (sem->count != 0); -} - #endif /* _XTENSA_RWSEM_H */ diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index 8970c3e802e2..8b9c7e9f6910 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -34,6 +34,13 @@ struct rw_semaphore { /* Include the arch specific part */ #include + +/* In all implementations count != 0 means locked */ +static inline int rwsem_is_locked(struct rw_semaphore *sem) +{ + return sem->count != 0; +} + #endif /* Common initializer macros and functions */ -- cgit v1.2.3 From aac72277fda6ef788bb8d5deaa502ce9b9b6e472 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 26 Jan 2011 20:06:06 +0000 Subject: rwsem: Move duplicate function prototypes to linux/rwsem.h All architecture specific rwsem headers carry the same function prototypes. Just x86 adds asmregparm, which is an empty define on all other architectures. S390 has a stale rwsem_downgrade_write() prototype. Remove the duplicates and add the prototypes to linux/rwsem.h Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra Cc: David Howells Cc: Benjamin Herrenschmidt Cc: Richard Henderson Acked-by: Tony Luck Acked-by: Heiko Carstens Cc: Paul Mundt Acked-by: David Miller Cc: Chris Zankel LKML-Reference: <20110126195833.970840140@linutronix.de> Signed-off-by: Thomas Gleixner --- arch/alpha/include/asm/rwsem.h | 5 ----- arch/ia64/include/asm/rwsem.h | 5 ----- arch/powerpc/include/asm/rwsem.h | 5 ----- arch/s390/include/asm/rwsem.h | 6 ------ arch/sh/include/asm/rwsem.h | 5 ----- arch/sparc/include/asm/rwsem.h | 5 ----- arch/x86/include/asm/rwsem.h | 9 --------- arch/xtensa/include/asm/rwsem.h | 5 ----- include/linux/rwsem.h | 5 +++++ 9 files changed, 5 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/arch/alpha/include/asm/rwsem.h b/arch/alpha/include/asm/rwsem.h index fc7f54337c3a..a83bbea62c67 100644 --- a/arch/alpha/include/asm/rwsem.h +++ b/arch/alpha/include/asm/rwsem.h @@ -14,11 +14,6 @@ #include -extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *); -extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); - #define RWSEM_UNLOCKED_VALUE 0x0000000000000000L #define RWSEM_ACTIVE_BIAS 0x0000000000000001L #define RWSEM_ACTIVE_MASK 0x00000000ffffffffL diff --git a/arch/ia64/include/asm/rwsem.h b/arch/ia64/include/asm/rwsem.h index 148b61a96b8c..3027e7516d85 100644 --- a/arch/ia64/include/asm/rwsem.h +++ b/arch/ia64/include/asm/rwsem.h @@ -34,11 +34,6 @@ #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) -extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); - /* * lock for reading */ diff --git a/arch/powerpc/include/asm/rwsem.h b/arch/powerpc/include/asm/rwsem.h index 38e8e5c508d5..bb1e2cdeb9bf 100644 --- a/arch/powerpc/include/asm/rwsem.h +++ b/arch/powerpc/include/asm/rwsem.h @@ -28,11 +28,6 @@ #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) -extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); - /* * lock for reading */ diff --git a/arch/s390/include/asm/rwsem.h b/arch/s390/include/asm/rwsem.h index 80522dcb6cc2..d0eb4653cebd 100644 --- a/arch/s390/include/asm/rwsem.h +++ b/arch/s390/include/asm/rwsem.h @@ -43,12 +43,6 @@ #ifdef __KERNEL__ -extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *); -extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *); -extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *); -extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *); -extern struct rw_semaphore *rwsem_downgrade_write(struct rw_semaphore *); - #ifndef __s390x__ #define RWSEM_UNLOCKED_VALUE 0x00000000 #define RWSEM_ACTIVE_BIAS 0x00000001 diff --git a/arch/sh/include/asm/rwsem.h b/arch/sh/include/asm/rwsem.h index 2f8cf9761eb5..edab57265293 100644 --- a/arch/sh/include/asm/rwsem.h +++ b/arch/sh/include/asm/rwsem.h @@ -19,11 +19,6 @@ #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) -extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); - /* * lock for reading */ diff --git a/arch/sparc/include/asm/rwsem.h b/arch/sparc/include/asm/rwsem.h index 4f4391fd5658..069bf4d663a1 100644 --- a/arch/sparc/include/asm/rwsem.h +++ b/arch/sparc/include/asm/rwsem.h @@ -20,11 +20,6 @@ #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) -extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); - /* * lock for reading */ diff --git a/arch/x86/include/asm/rwsem.h b/arch/x86/include/asm/rwsem.h index 776d76fe2f90..df4cd32b4cc6 100644 --- a/arch/x86/include/asm/rwsem.h +++ b/arch/x86/include/asm/rwsem.h @@ -39,15 +39,6 @@ #ifdef __KERNEL__ #include -extern asmregparm struct rw_semaphore * - rwsem_down_read_failed(struct rw_semaphore *sem); -extern asmregparm struct rw_semaphore * - rwsem_down_write_failed(struct rw_semaphore *sem); -extern asmregparm struct rw_semaphore * - rwsem_wake(struct rw_semaphore *); -extern asmregparm struct rw_semaphore * - rwsem_downgrade_wake(struct rw_semaphore *sem); - /* * The bias values and the counter type limits the number of * potential readers/writers to 32767 for 32 bits and 2147483647 diff --git a/arch/xtensa/include/asm/rwsem.h b/arch/xtensa/include/asm/rwsem.h index da61633ccba8..249619e7e7f2 100644 --- a/arch/xtensa/include/asm/rwsem.h +++ b/arch/xtensa/include/asm/rwsem.h @@ -24,11 +24,6 @@ #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) -extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); - /* * lock for reading */ diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index 8b9c7e9f6910..f7c957f3165f 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -32,6 +32,11 @@ struct rw_semaphore { #endif }; +extern asmregparm struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); +extern asmregparm struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); +extern asmregparm struct rw_semaphore *rwsem_wake(struct rw_semaphore *); +extern asmregparm struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); + /* Include the arch specific part */ #include -- cgit v1.2.3 From d123375425d7df4b6081a631fc1203fceafa59b2 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 26 Jan 2011 21:32:01 +0100 Subject: rwsem: Remove redundant asmregparm annotation Peter Zijlstra pointed out, that the only user of asmregparm (x86) is compiling the kernel already with -mregparm=3. So the annotation of the rwsem functions is redundant. Remove it. Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra Cc: David Howells Cc: Benjamin Herrenschmidt Cc: Matt Turner Cc: Tony Luck Cc: Heiko Carstens Cc: Paul Mundt Cc: David Miller Cc: Chris Zankel LKML-Reference: Signed-off-by: Thomas Gleixner --- include/linux/rwsem.h | 8 ++++---- lib/rwsem.c | 10 ++++------ 2 files changed, 8 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index f7c957f3165f..a8afe9cd000c 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -32,10 +32,10 @@ struct rw_semaphore { #endif }; -extern asmregparm struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); -extern asmregparm struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); -extern asmregparm struct rw_semaphore *rwsem_wake(struct rw_semaphore *); -extern asmregparm struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); +extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); +extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); +extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *); +extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); /* Include the arch specific part */ #include diff --git a/lib/rwsem.c b/lib/rwsem.c index f236d7cd5cf3..aa7c3052261f 100644 --- a/lib/rwsem.c +++ b/lib/rwsem.c @@ -222,8 +222,7 @@ rwsem_down_failed_common(struct rw_semaphore *sem, /* * wait for the read lock to be granted */ -asmregparm struct rw_semaphore __sched * -rwsem_down_read_failed(struct rw_semaphore *sem) +struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem) { return rwsem_down_failed_common(sem, RWSEM_WAITING_FOR_READ, -RWSEM_ACTIVE_READ_BIAS); @@ -232,8 +231,7 @@ rwsem_down_read_failed(struct rw_semaphore *sem) /* * wait for the write lock to be granted */ -asmregparm struct rw_semaphore __sched * -rwsem_down_write_failed(struct rw_semaphore *sem) +struct rw_semaphore __sched *rwsem_down_write_failed(struct rw_semaphore *sem) { return rwsem_down_failed_common(sem, RWSEM_WAITING_FOR_WRITE, -RWSEM_ACTIVE_WRITE_BIAS); @@ -243,7 +241,7 @@ rwsem_down_write_failed(struct rw_semaphore *sem) * handle waking up a waiter on the semaphore * - up_read/up_write has decremented the active part of count if we come here */ -asmregparm struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem) +struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem) { unsigned long flags; @@ -263,7 +261,7 @@ asmregparm struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem) * - caller incremented waiting part of count and discovered it still negative * - just wake up any readers at the front of the queue */ -asmregparm struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem) +struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem) { unsigned long flags; -- cgit v1.2.3 From 70b2ac126a60c87145ae8a8eb1b4dccaa0bf5468 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 26 Jan 2011 14:05:25 +0000 Subject: ASoC: Use card rather than soc-audio device to card PM functions The platform device for the card is tied closely to the soc-audio implementation which we're currently trying to remove in favour of allowing cards to have their own devices. Begin removing it by replacing it with the card in the suspend and resume callbacks we give to cards, also taking the opportunity to remove the legacy suspend types which are currently hard coded anyway. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 8 ++++---- sound/soc/pxa/raumfeld.c | 4 ++-- sound/soc/pxa/zylonite.c | 5 ++--- sound/soc/soc-core.c | 9 ++++----- 4 files changed, 12 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 1355ef029d82..4a489ae44a6e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -654,10 +654,10 @@ struct snd_soc_card { /* the pre and post PM functions are used to do any PM work before and * after the codec and DAI's do any PM work. */ - int (*suspend_pre)(struct platform_device *pdev, pm_message_t state); - int (*suspend_post)(struct platform_device *pdev, pm_message_t state); - int (*resume_pre)(struct platform_device *pdev); - int (*resume_post)(struct platform_device *pdev); + int (*suspend_pre)(struct snd_soc_card *card); + int (*suspend_post)(struct snd_soc_card *card); + int (*resume_pre)(struct snd_soc_card *card); + int (*resume_post)(struct snd_soc_card *card); /* callbacks */ int (*set_bias_level)(struct snd_soc_card *, diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c index 0fd60f423036..db1dd560a585 100644 --- a/sound/soc/pxa/raumfeld.c +++ b/sound/soc/pxa/raumfeld.c @@ -151,13 +151,13 @@ static struct snd_soc_ops raumfeld_cs4270_ops = { .hw_params = raumfeld_cs4270_hw_params, }; -static int raumfeld_line_suspend(struct platform_device *pdev, pm_message_t state) +static int raumfeld_line_suspend(struct snd_soc_card *card) { raumfeld_enable_audio(false); return 0; } -static int raumfeld_line_resume(struct platform_device *pdev) +static int raumfeld_line_resume(struct snd_soc_card *card) { raumfeld_enable_audio(true); return 0; diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index b222a7d72027..7b729013a728 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -226,8 +226,7 @@ static int zylonite_remove(struct platform_device *pdev) return 0; } -static int zylonite_suspend_post(struct platform_device *pdev, - pm_message_t state) +static int zylonite_suspend_post(struct snd_soc_card *card) { if (clk_pout) clk_disable(pout); @@ -235,7 +234,7 @@ static int zylonite_suspend_post(struct platform_device *pdev, return 0; } -static int zylonite_resume_pre(struct platform_device *pdev) +static int zylonite_resume_pre(struct snd_soc_card *card) { int ret = 0; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 14861f95f629..446838e7d3ec 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1011,7 +1011,7 @@ static int soc_suspend(struct device *dev) } if (card->suspend_pre) - card->suspend_pre(pdev, PMSG_SUSPEND); + card->suspend_pre(card); for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; @@ -1078,7 +1078,7 @@ static int soc_suspend(struct device *dev) } if (card->suspend_post) - card->suspend_post(pdev, PMSG_SUSPEND); + card->suspend_post(card); return 0; } @@ -1090,7 +1090,6 @@ static void soc_resume_deferred(struct work_struct *work) { struct snd_soc_card *card = container_of(work, struct snd_soc_card, deferred_resume_work); - struct platform_device *pdev = to_platform_device(card->dev); struct snd_soc_codec *codec; int i; @@ -1104,7 +1103,7 @@ static void soc_resume_deferred(struct work_struct *work) snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D2); if (card->resume_pre) - card->resume_pre(pdev); + card->resume_pre(card); /* resume AC97 DAIs */ for (i = 0; i < card->num_rtd; i++) { @@ -1179,7 +1178,7 @@ static void soc_resume_deferred(struct work_struct *work) } if (card->resume_post) - card->resume_post(pdev); + card->resume_post(card); dev_dbg(card->dev, "resume work completed\n"); -- cgit v1.2.3 From e7361ec4996c170c63c4ac379085896db85ff34d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 26 Jan 2011 14:17:20 +0000 Subject: ASoC: Replace pdev with card in machine driver probe and remove In order to support cards instantiated without using soc-audio remove the use of the platform device in the card probe() and remove() ops. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 4 ++-- sound/soc/fsl/mpc8610_hpcd.c | 6 ++---- sound/soc/fsl/p1022_ds.c | 6 ++---- sound/soc/pxa/tosa.c | 4 ++-- sound/soc/pxa/zylonite.c | 4 ++-- sound/soc/soc-core.c | 8 +++----- 6 files changed, 13 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 4a489ae44a6e..2d10090a08c0 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -649,8 +649,8 @@ struct snd_soc_card { bool instantiated; - int (*probe)(struct platform_device *pdev); - int (*remove)(struct platform_device *pdev); + int (*probe)(struct snd_soc_card *card); + int (*remove)(struct snd_soc_card *card); /* the pre and post PM functions are used to do any PM work before and * after the codec and DAI's do any PM work. */ diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 7d7847a1e66b..c16c6b2eff95 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -53,9 +53,8 @@ struct mpc8610_hpcd_data { * * Here we program the DMACR and PMUXCR registers. */ -static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) +static int mpc8610_hpcd_machine_probe(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct mpc8610_hpcd_data *machine_data = container_of(card, struct mpc8610_hpcd_data, card); struct ccsr_guts_86xx __iomem *guts; @@ -138,9 +137,8 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) * This function is called to remove the sound device for one SSI. We * de-program the DMACR and PMUXCR register. */ -static int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) +static int mpc8610_hpcd_machine_remove(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct mpc8610_hpcd_data *machine_data = container_of(card, struct mpc8610_hpcd_data, card); struct ccsr_guts_86xx __iomem *guts; diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c index 026b756961e0..66e0b68af147 100644 --- a/sound/soc/fsl/p1022_ds.c +++ b/sound/soc/fsl/p1022_ds.c @@ -85,9 +85,8 @@ struct machine_data { * * Here we program the DMACR and PMUXCR registers. */ -static int p1022_ds_machine_probe(struct platform_device *sound_device) +static int p1022_ds_machine_probe(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct machine_data *mdata = container_of(card, struct machine_data, card); struct ccsr_guts_85xx __iomem *guts; @@ -160,9 +159,8 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream) * This function is called to remove the sound device for one SSI. We * de-program the DMACR and PMUXCR register. */ -static int p1022_ds_machine_remove(struct platform_device *sound_device) +static int p1022_ds_machine_remove(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct machine_data *mdata = container_of(card, struct machine_data, card); struct ccsr_guts_85xx __iomem *guts; diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index f75804ef0897..489139a31cf9 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -237,7 +237,7 @@ static struct snd_soc_dai_link tosa_dai[] = { }, }; -static int tosa_probe(struct platform_device *dev) +static int tosa_probe(struct snd_soc_card *card) { int ret; @@ -251,7 +251,7 @@ static int tosa_probe(struct platform_device *dev) return ret; } -static int tosa_remove(struct platform_device *dev) +static int tosa_remove(struct snd_soc_card *card) { gpio_free(TOSA_GPIO_L_MUTE); return 0; diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index 7b729013a728..c5858296b48a 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -189,7 +189,7 @@ static struct snd_soc_dai_link zylonite_dai[] = { }, }; -static int zylonite_probe(struct platform_device *pdev) +static int zylonite_probe(struct snd_soc_card *card) { int ret; @@ -216,7 +216,7 @@ static int zylonite_probe(struct platform_device *pdev) return 0; } -static int zylonite_remove(struct platform_device *pdev) +static int zylonite_remove(struct snd_soc_card *card) { if (clk_pout) { clk_disable(pout); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 446838e7d3ec..4bc2365bf1dd 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1717,7 +1717,6 @@ static int snd_soc_init_codec_cache(struct snd_soc_codec *codec, static void snd_soc_instantiate_card(struct snd_soc_card *card) { - struct platform_device *pdev = to_platform_device(card->dev); struct snd_soc_codec *codec; struct snd_soc_codec_conf *codec_conf; enum snd_soc_compress_type compress_type; @@ -1781,7 +1780,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) /* initialise the sound card only once */ if (card->probe) { - ret = card->probe(pdev); + ret = card->probe(card); if (ret < 0) goto card_probe_error; } @@ -1842,7 +1841,7 @@ probe_dai_err: card_probe_error: if (card->remove) - card->remove(pdev); + card->remove(card); snd_card_free(card->snd_card); @@ -1888,7 +1887,6 @@ static int soc_probe(struct platform_device *pdev) static int soc_cleanup_card_resources(struct snd_soc_card *card) { - struct platform_device *pdev = to_platform_device(card->dev); int i; /* make sure any delayed work runs */ @@ -1909,7 +1907,7 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card) /* remove the card */ if (card->remove) - card->remove(pdev); + card->remove(card); kfree(card->rtd); snd_card_free(card->snd_card); -- cgit v1.2.3 From 6f8ab4ac292f81b9246ddf363bf1c6a2fc7a0629 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 26 Jan 2011 14:59:27 +0000 Subject: ASoC: Export card PM callbacks for use in direct registered cards Allow hookup of cards registered directly with the core to the PM operations by exporting the device power management operations to modules, also exporting the default PM operations since it is expected that most cards will end up using exactly the same setup. Note that the callbacks require that the driver data for the card be the snd_soc_card. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 5 +++++ sound/soc/soc-core.c | 34 +++++++++++++++++----------------- 2 files changed, 22 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 2d10090a08c0..7e8cf4f318a9 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -260,6 +260,9 @@ enum snd_soc_compress_type { int snd_soc_register_card(struct snd_soc_card *card); int snd_soc_unregister_card(struct snd_soc_card *card); +int snd_soc_suspend(struct device *dev); +int snd_soc_resume(struct device *dev); +int snd_soc_poweroff(struct device *dev); int snd_soc_register_platform(struct device *dev, struct snd_soc_platform_driver *platform_drv); void snd_soc_unregister_platform(struct device *dev); @@ -802,4 +805,6 @@ static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card) extern struct dentry *snd_soc_debugfs_root; #endif +extern const struct dev_pm_ops snd_soc_pm_ops; + #endif diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4bc2365bf1dd..5dffc7a469c0 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -965,12 +965,11 @@ static struct snd_pcm_ops soc_pcm_ops = { .pointer = soc_pcm_pointer, }; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP /* powers down audio subsystem for suspend */ -static int soc_suspend(struct device *dev) +int snd_soc_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_card *card = dev_get_drvdata(dev); struct snd_soc_codec *codec; int i; @@ -1082,6 +1081,7 @@ static int soc_suspend(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_suspend); /* deferred resume work, so resume can complete before we finished * setting our codec back up, which can be very slow on I2C @@ -1187,10 +1187,9 @@ static void soc_resume_deferred(struct work_struct *work) } /* powers up audio subsystem after a suspend */ -static int soc_resume(struct device *dev) +int snd_soc_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_card *card = dev_get_drvdata(dev); int i; /* AC97 devices might have other drivers hanging off them so @@ -1212,9 +1211,10 @@ static int soc_resume(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_resume); #else -#define soc_suspend NULL -#define soc_resume NULL +#define snd_soc_suspend NULL +#define snd_soc_resume NULL #endif static struct snd_soc_dai_ops null_dai_ops = { @@ -1924,10 +1924,9 @@ static int soc_remove(struct platform_device *pdev) return 0; } -static int soc_poweroff(struct device *dev) +int snd_soc_poweroff(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_card *card = dev_get_drvdata(dev); int i; if (!card->instantiated) @@ -1944,11 +1943,12 @@ static int soc_poweroff(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_poweroff); -static const struct dev_pm_ops soc_pm_ops = { - .suspend = soc_suspend, - .resume = soc_resume, - .poweroff = soc_poweroff, +const struct dev_pm_ops snd_soc_pm_ops = { + .suspend = snd_soc_suspend, + .resume = snd_soc_resume, + .poweroff = snd_soc_poweroff, }; /* ASoC platform driver */ @@ -1956,7 +1956,7 @@ static struct platform_driver soc_driver = { .driver = { .name = "soc-audio", .owner = THIS_MODULE, - .pm = &soc_pm_ops, + .pm = &snd_soc_pm_ops, }, .probe = soc_probe, .remove = soc_remove, -- cgit v1.2.3 From aaee8ef146111566e1c607bdf368d73fb966be2e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 26 Jan 2011 20:53:50 +0000 Subject: ASoC: Make cache status available via debugfs Could just as well live in sysfs but sysfs doesn't have the simple value export helpers debugfs does. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 4 ++-- sound/soc/soc-core.c | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 7e8cf4f318a9..64856d656f15 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -493,14 +493,14 @@ struct snd_soc_codec { struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ unsigned int active; unsigned int cache_bypass:1; /* Suppress access to the cache */ - unsigned int cache_only:1; /* Suppress writes to hardware */ - unsigned int cache_sync:1; /* Cache needs to be synced to hardware */ unsigned int suspended:1; /* Codec is in suspend PM state */ unsigned int probed:1; /* Codec has been probed */ unsigned int ac97_registered:1; /* Codec has been AC97 registered */ unsigned int ac97_created:1; /* Codec has been created by SoC */ unsigned int sysfs_registered:1; /* codec has been sysfs registered */ unsigned int cache_init:1; /* codec cache has been initialized */ + u32 cache_only; /* Suppress writes to hardware */ + u32 cache_sync; /* Cache needs to be synced to hardware */ /* codec IO */ void *control_data; /* codec control (i2c/3wire) data */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 5dffc7a469c0..8af47f7a8fcb 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -235,6 +235,11 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec) return; } + debugfs_create_bool("cache_sync", 0444, codec->debugfs_codec_root, + &codec->cache_sync); + debugfs_create_bool("cache_only", 0444, codec->debugfs_codec_root, + &codec->cache_only); + codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, codec->debugfs_codec_root, codec, &codec_reg_fops); -- cgit v1.2.3 From f85a9e0d260905f98d4ca6b66f0e64f63a729dba Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 26 Jan 2011 21:41:28 +0000 Subject: ASoC: Add subsequence information to seq_notify callbacks Allows drivers to distinguish which subsequence is being notified when they get called back. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc-dapm.h | 2 +- include/sound/soc.h | 2 +- sound/soc/soc-dapm.c | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 6a25e6993859..979ed84e07d6 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -501,7 +501,7 @@ struct snd_soc_dapm_context { struct snd_soc_dapm_update *update; void (*seq_notifier)(struct snd_soc_dapm_context *, - enum snd_soc_dapm_type); + enum snd_soc_dapm_type, int); struct device *dev; /* from parent - for debug */ struct snd_soc_codec *codec; /* parent codec */ diff --git a/include/sound/soc.h b/include/sound/soc.h index 64856d656f15..7ecdaefd1b63 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -553,7 +553,7 @@ struct snd_soc_codec_driver { enum snd_soc_bias_level level); void (*seq_notifier)(struct snd_soc_dapm_context *, - enum snd_soc_dapm_type); + enum snd_soc_dapm_type, int); }; /* SoC platform interface */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 37b376f4c75d..0f94fd057f29 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -899,7 +899,8 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) if (sort[i] == cur_sort) cur_dapm->seq_notifier(cur_dapm, - i); + i, + cur_subseq); } INIT_LIST_HEAD(&pending); @@ -968,7 +969,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) if (sort[i] == cur_sort) cur_dapm->seq_notifier(cur_dapm, - i); + i, cur_subseq); } } -- cgit v1.2.3 From ea18e137baf3e3e9212bfd7b071555fc712159b5 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 26 Jan 2011 11:04:08 +0100 Subject: ALSA: Release v1.0.24 Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- include/sound/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/version.h b/include/sound/version.h index bf69a5b7e65f..8fc5321e1ecc 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h */ -#define CONFIG_SND_VERSION "1.0.23" +#define CONFIG_SND_VERSION "1.0.24" #define CONFIG_SND_DATE "" -- cgit v1.2.3 From f9820a46dd7888b05a36e81166fb1abcc47dcc3f Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Mon, 29 Nov 2010 13:52:18 -0500 Subject: ttm: Introduce a placeholder for DMA (bus) addresses. This is right now limited to only non-pool constructs. [v2: Fixed indentation issues, add review-by tag] Reviewed-by: Thomas Hellstrom Signed-off-by: Konrad Rzeszutek Wilk Tested-by: Ian Campbell --- drivers/gpu/drm/ttm/ttm_page_alloc.c | 8 +++++--- drivers/gpu/drm/ttm/ttm_tt.c | 10 ++++++++-- include/drm/ttm/ttm_bo_driver.h | 2 ++ include/drm/ttm/ttm_page_alloc.h | 8 ++++++-- 4 files changed, 21 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c index b1e02fffd3cc..9d9d92945f8c 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c @@ -38,6 +38,7 @@ #include #include /* for seq_printf */ #include +#include #include @@ -662,7 +663,8 @@ out: * cached pages. */ int ttm_get_pages(struct list_head *pages, int flags, - enum ttm_caching_state cstate, unsigned count) + enum ttm_caching_state cstate, unsigned count, + dma_addr_t *dma_address) { struct ttm_page_pool *pool = ttm_get_pool(flags, cstate); struct page *p = NULL; @@ -720,7 +722,7 @@ int ttm_get_pages(struct list_head *pages, int flags, printk(KERN_ERR TTM_PFX "Failed to allocate extra pages " "for large request."); - ttm_put_pages(pages, 0, flags, cstate); + ttm_put_pages(pages, 0, flags, cstate, NULL); return r; } } @@ -731,7 +733,7 @@ int ttm_get_pages(struct list_head *pages, int flags, /* Put all pages in pages list to correct pool to wait for reuse */ void ttm_put_pages(struct list_head *pages, unsigned page_count, int flags, - enum ttm_caching_state cstate) + enum ttm_caching_state cstate, dma_addr_t *dma_address) { unsigned long irq_flags; struct ttm_page_pool *pool = ttm_get_pool(flags, cstate); diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index af789dc869b9..0d39001259fb 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -49,12 +49,16 @@ static int ttm_tt_swapin(struct ttm_tt *ttm); static void ttm_tt_alloc_page_directory(struct ttm_tt *ttm) { ttm->pages = drm_calloc_large(ttm->num_pages, sizeof(*ttm->pages)); + ttm->dma_address = drm_calloc_large(ttm->num_pages, + sizeof(*ttm->dma_address)); } static void ttm_tt_free_page_directory(struct ttm_tt *ttm) { drm_free_large(ttm->pages); ttm->pages = NULL; + drm_free_large(ttm->dma_address); + ttm->dma_address = NULL; } static void ttm_tt_free_user_pages(struct ttm_tt *ttm) @@ -105,7 +109,8 @@ static struct page *__ttm_tt_get_page(struct ttm_tt *ttm, int index) INIT_LIST_HEAD(&h); - ret = ttm_get_pages(&h, ttm->page_flags, ttm->caching_state, 1); + ret = ttm_get_pages(&h, ttm->page_flags, ttm->caching_state, 1, + &ttm->dma_address[index]); if (ret != 0) return NULL; @@ -298,7 +303,8 @@ static void ttm_tt_free_alloced_pages(struct ttm_tt *ttm) count++; } } - ttm_put_pages(&h, count, ttm->page_flags, ttm->caching_state); + ttm_put_pages(&h, count, ttm->page_flags, ttm->caching_state, + ttm->dma_address); ttm->state = tt_unpopulated; ttm->first_himem_page = ttm->num_pages; ttm->last_lomem_page = -1; diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 8e0c848326b6..6dc4fccda73c 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -149,6 +149,7 @@ enum ttm_caching_state { * @swap_storage: Pointer to shmem struct file for swap storage. * @caching_state: The current caching state of the pages. * @state: The current binding state of the pages. + * @dma_address: The DMA (bus) addresses of the pages (if TTM_PAGE_FLAG_DMA32) * * This is a structure holding the pages, caching- and aperture binding * status for a buffer object that isn't backed by fixed (VRAM / AGP) @@ -173,6 +174,7 @@ struct ttm_tt { tt_unbound, tt_unpopulated, } state; + dma_addr_t *dma_address; }; #define TTM_MEMTYPE_FLAG_FIXED (1 << 0) /* Fixed (on-card) PCI memory */ diff --git a/include/drm/ttm/ttm_page_alloc.h b/include/drm/ttm/ttm_page_alloc.h index 116821448c38..8062890f725e 100644 --- a/include/drm/ttm/ttm_page_alloc.h +++ b/include/drm/ttm/ttm_page_alloc.h @@ -36,11 +36,13 @@ * @flags: ttm flags for page allocation. * @cstate: ttm caching state for the page. * @count: number of pages to allocate. + * @dma_address: The DMA (bus) address of pages (if TTM_PAGE_FLAG_DMA32 set). */ int ttm_get_pages(struct list_head *pages, int flags, enum ttm_caching_state cstate, - unsigned count); + unsigned count, + dma_addr_t *dma_address); /** * Put linked list of pages to pool. * @@ -49,11 +51,13 @@ int ttm_get_pages(struct list_head *pages, * count. * @flags: ttm flags for page allocation. * @cstate: ttm caching state. + * @dma_address: The DMA (bus) address of pages (if TTM_PAGE_FLAG_DMA32 set). */ void ttm_put_pages(struct list_head *pages, unsigned page_count, int flags, - enum ttm_caching_state cstate); + enum ttm_caching_state cstate, + dma_addr_t *dma_address); /** * Initialize pool allocator. */ -- cgit v1.2.3 From 27e8b237944af967e0a808580278d432cb028455 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Thu, 2 Dec 2010 10:24:13 -0500 Subject: ttm: Expand (*populate) to support an array of DMA addresses. We pass in the array of ttm pages to be populated in the GART/MM of the card (or AGP). Patch titled: "ttm: Utilize the DMA API for pages that have TTM_PAGE_FLAG_DMA32 set." uses the DMA API to make those pages have a proper DMA addresses (in the situation where page_to_phys or virt_to_phys do not give use the DMA (bus) address). Since we are using the DMA API on those pages, we should pass in the DMA address to this function so it can save it in its proper fields (later patches use it). [v2: Added reviewed-by tag] Reviewed-by: Thomas Hellstrom Signed-off-by: Konrad Rzeszutek Wilk Tested-by: Ian Campbell --- drivers/gpu/drm/nouveau/nouveau_sgdma.c | 3 ++- drivers/gpu/drm/radeon/radeon_ttm.c | 3 ++- drivers/gpu/drm/ttm/ttm_agp_backend.c | 3 ++- drivers/gpu/drm/ttm/ttm_tt.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c | 3 ++- include/drm/ttm/ttm_bo_driver.h | 4 +++- 6 files changed, 12 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c index 288bacac7e5a..edc140ab4df1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c +++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c @@ -20,7 +20,8 @@ struct nouveau_sgdma_be { static int nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages, - struct page **pages, struct page *dummy_read_page) + struct page **pages, struct page *dummy_read_page, + dma_addr_t *dma_addrs) { struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; struct drm_device *dev = nvbe->dev; diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 01c2c736a1da..6f156e9d3f31 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -655,7 +655,8 @@ struct radeon_ttm_backend { static int radeon_ttm_backend_populate(struct ttm_backend *backend, unsigned long num_pages, struct page **pages, - struct page *dummy_read_page) + struct page *dummy_read_page, + dma_addr_t *dma_addrs) { struct radeon_ttm_backend *gtt; diff --git a/drivers/gpu/drm/ttm/ttm_agp_backend.c b/drivers/gpu/drm/ttm/ttm_agp_backend.c index f999e36f30b4..1c4a72f681c1 100644 --- a/drivers/gpu/drm/ttm/ttm_agp_backend.c +++ b/drivers/gpu/drm/ttm/ttm_agp_backend.c @@ -47,7 +47,8 @@ struct ttm_agp_backend { static int ttm_agp_populate(struct ttm_backend *backend, unsigned long num_pages, struct page **pages, - struct page *dummy_read_page) + struct page *dummy_read_page, + dma_addr_t *dma_addrs) { struct ttm_agp_backend *agp_be = container_of(backend, struct ttm_agp_backend, backend); diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index 0d39001259fb..86d5b1745a45 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -169,7 +169,7 @@ int ttm_tt_populate(struct ttm_tt *ttm) } be->func->populate(be, ttm->num_pages, ttm->pages, - ttm->dummy_read_page); + ttm->dummy_read_page, ttm->dma_address); ttm->state = tt_unbound; return 0; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c index 80bc37b274e7..87e43e0733bf 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c @@ -102,7 +102,8 @@ struct vmw_ttm_backend { static int vmw_ttm_populate(struct ttm_backend *backend, unsigned long num_pages, struct page **pages, - struct page *dummy_read_page) + struct page *dummy_read_page, + dma_addr_t *dma_addrs) { struct vmw_ttm_backend *vmw_be = container_of(backend, struct vmw_ttm_backend, backend); diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 6dc4fccda73c..ebcd3dd7203b 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -50,13 +50,15 @@ struct ttm_backend_func { * @pages: Array of pointers to ttm pages. * @dummy_read_page: Page to be used instead of NULL pages in the * array @pages. + * @dma_addrs: Array of DMA (bus) address of the ttm pages. * * Populate the backend with ttm pages. Depending on the backend, * it may or may not copy the @pages array. */ int (*populate) (struct ttm_backend *backend, unsigned long num_pages, struct page **pages, - struct page *dummy_read_page); + struct page *dummy_read_page, + dma_addr_t *dma_addrs); /** * struct ttm_backend_func member clear * -- cgit v1.2.3 From 606598237c856b0c6584c2263288657658140da9 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 26 Jan 2011 20:55:53 -0800 Subject: inetpeer: Add metrics storage to inetpeer entries. Signed-off-by: David S. Miller --- include/net/inetpeer.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index 599d96e74114..2af0c63d3975 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -33,8 +34,8 @@ struct inet_peer { atomic_t refcnt; /* * Once inet_peer is queued for deletion (refcnt == -1), following fields - * are not available: rid, ip_id_count, tcp_ts, tcp_ts_stamp - * We can share memory with rcu_head to keep inet_peer small + * are not available: rid, ip_id_count, tcp_ts, tcp_ts_stamp, metrics + * We can share memory with rcu_head to help keep inet_peer small. */ union { struct { @@ -42,6 +43,7 @@ struct inet_peer { atomic_t ip_id_count; /* IP ID for the next packet */ __u32 tcp_ts; __u32 tcp_ts_stamp; + u32 metrics[RTAX_MAX]; }; struct rcu_head rcu; }; -- cgit v1.2.3 From 144001bddcb4db62c2261f1d703d835851031577 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 27 Jan 2011 13:52:16 -0800 Subject: inetpeer: Mark metrics as "new" in fresh inetpeer entries. Set the RTAX_LOCKED metric to INETPEER_METRICS_NEW (basically, all ones) on fresh inetpeer entries. This way code can determine if default metrics have been loaded in from a routing table entry already. Signed-off-by: David S. Miller --- include/net/inetpeer.h | 7 +++++++ net/ipv4/inetpeer.c | 1 + 2 files changed, 8 insertions(+) (limited to 'include') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index 2af0c63d3975..61f2c66edb2a 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -51,6 +51,13 @@ struct inet_peer { void inet_initpeers(void) __init; +#define INETPEER_METRICS_NEW (~(u32) 0) + +static inline bool inet_metrics_new(const struct inet_peer *p) +{ + return p->metrics[RTAX_LOCK-1] == INETPEER_METRICS_NEW; +} + /* can be called with or without local BH being disabled */ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create); diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index a96e65674ac3..b6513b13d729 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -512,6 +512,7 @@ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) atomic_set(&p->rid, 0); atomic_set(&p->ip_id_count, secure_ip_id(daddr->a4)); p->tcp_ts_stamp = 0; + p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW; INIT_LIST_HEAD(&p->unused); -- cgit v1.2.3 From ccf434380d1a67df2dcb9113206b77d0cb0a1cef Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 26 Jan 2011 18:08:02 +0000 Subject: net: fix dev_seq_next() Commit c6d14c84566d (net: Introduce for_each_netdev_rcu() iterator) added a race in dev_seq_next(). The rcu_dereference() call should be done _before_ testing the end of list, or we might return a wrong net_device if a concurrent thread changes net_device list under us. Note : discovered thanks to a sparse warning : net/core/dev.c:3919:9: error: incompatible types in comparison expression (different address spaces) Signed-off-by: Eric Dumazet CC: Paul E. McKenney Signed-off-by: David S. Miller --- include/linux/netdevice.h | 9 ++++++++- net/core/dev.c | 11 +++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 8858422c5c5d..c7d707452228 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1447,7 +1447,7 @@ static inline struct net_device *next_net_device_rcu(struct net_device *dev) struct net *net; net = dev_net(dev); - lh = rcu_dereference(dev->dev_list.next); + lh = rcu_dereference(list_next_rcu(&dev->dev_list)); return lh == &net->dev_base_head ? NULL : net_device_entry(lh); } @@ -1457,6 +1457,13 @@ static inline struct net_device *first_net_device(struct net *net) net_device_entry(net->dev_base_head.next); } +static inline struct net_device *first_net_device_rcu(struct net *net) +{ + struct list_head *lh = rcu_dereference(list_next_rcu(&net->dev_base_head)); + + return lh == &net->dev_base_head ? NULL : net_device_entry(lh); +} + extern int netdev_boot_setup_check(struct net_device *dev); extern unsigned long netdev_boot_base(const char *prefix, int unit); extern struct net_device *dev_getbyhwaddr_rcu(struct net *net, unsigned short type, diff --git a/net/core/dev.c b/net/core/dev.c index 1b4c07fe295f..ddd5df2b61d4 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4051,12 +4051,15 @@ void *dev_seq_start(struct seq_file *seq, loff_t *pos) void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) { - struct net_device *dev = (v == SEQ_START_TOKEN) ? - first_net_device(seq_file_net(seq)) : - next_net_device((struct net_device *)v); + struct net_device *dev = v; + + if (v == SEQ_START_TOKEN) + dev = first_net_device_rcu(seq_file_net(seq)); + else + dev = next_net_device_rcu(dev); ++*pos; - return rcu_dereference(dev); + return dev; } void dev_seq_stop(struct seq_file *seq, void *v) -- cgit v1.2.3 From a4daad6b0923030fbd3b00a01f570e4c3eef446b Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 27 Jan 2011 22:01:53 -0800 Subject: net: Pre-COW metrics for TCP. TCP is going to record metrics for the connection, so pre-COW the route metrics at route cache entry creation time. This avoids several atomic operations that have to occur if we COW the metrics after the entry reaches global visibility. Signed-off-by: David S. Miller --- include/net/flow.h | 3 ++- include/net/inet_sock.h | 8 +++++++- include/net/route.h | 4 ++++ net/ipv4/route.c | 26 +++++++++++++++++++++++--- 4 files changed, 36 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/flow.h b/include/net/flow.h index 240b7f356c71..1ae901f24436 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -48,7 +48,8 @@ struct flowi { __u8 proto; __u8 flags; -#define FLOWI_FLAG_ANYSRC 0x01 +#define FLOWI_FLAG_ANYSRC 0x01 +#define FLOWI_FLAG_PRECOW_METRICS 0x02 union { struct { __be16 sport; diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 8181498fa96c..6e6dfd757682 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -219,7 +219,13 @@ static inline struct request_sock *inet_reqsk_alloc(struct request_sock_ops *ops static inline __u8 inet_sk_flowi_flags(const struct sock *sk) { - return inet_sk(sk)->transparent ? FLOWI_FLAG_ANYSRC : 0; + __u8 flags = 0; + + if (inet_sk(sk)->transparent) + flags |= FLOWI_FLAG_ANYSRC; + if (sk->sk_protocol == IPPROTO_TCP) + flags |= FLOWI_FLAG_PRECOW_METRICS; + return flags; } #endif /* _INET_SOCK_H */ diff --git a/include/net/route.h b/include/net/route.h index 5677cbf0c6e6..e5864658dc76 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -182,6 +182,8 @@ static inline int ip_route_connect(struct rtable **rp, __be32 dst, if (inet_sk(sk)->transparent) fl.flags |= FLOWI_FLAG_ANYSRC; + if (protocol == IPPROTO_TCP) + fl.flags |= FLOWI_FLAG_PRECOW_METRICS; if (!dst || !src) { err = __ip_route_output_key(net, rp, &fl); @@ -209,6 +211,8 @@ static inline int ip_route_newports(struct rtable **rp, u8 protocol, fl.proto = protocol; if (inet_sk(sk)->transparent) fl.flags |= FLOWI_FLAG_ANYSRC; + if (protocol == IPPROTO_TCP) + fl.flags |= FLOWI_FLAG_PRECOW_METRICS; ip_rt_put(*rp); *rp = NULL; security_sk_classify_flow(sk, &fl); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 68cee358d9a3..dd57f4896736 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1857,6 +1857,28 @@ static unsigned int ipv4_default_mtu(const struct dst_entry *dst) return mtu; } +static void rt_init_metrics(struct rtable *rt, struct fib_info *fi) +{ + if (!(rt->fl.flags & FLOWI_FLAG_PRECOW_METRICS)) { + no_cow: + rt->fi = fi; + atomic_inc(&fi->fib_clntref); + dst_init_metrics(&rt->dst, fi->fib_metrics, true); + } else { + struct inet_peer *peer; + + if (!rt->peer) + rt_bind_peer(rt, 1); + peer = rt->peer; + if (!peer) + goto no_cow; + if (inet_metrics_new(peer)) + memcpy(peer->metrics, fi->fib_metrics, + sizeof(u32) * RTAX_MAX); + dst_init_metrics(&rt->dst, peer->metrics, false); + } +} + static void rt_set_nexthop(struct rtable *rt, struct fib_result *res, u32 itag) { struct dst_entry *dst = &rt->dst; @@ -1866,9 +1888,7 @@ static void rt_set_nexthop(struct rtable *rt, struct fib_result *res, u32 itag) if (FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) rt->rt_gateway = FIB_RES_GW(*res); - rt->fi = fi; - atomic_inc(&fi->fib_clntref); - dst_init_metrics(dst, fi->fib_metrics, true); + rt_init_metrics(rt, fi); #ifdef CONFIG_IP_ROUTE_CLASSID dst->tclassid = FIB_RES_NH(*res).nh_tclassid; #endif -- cgit v1.2.3 From dddf3e4c257879bc35cda3f542507c43f2648a2a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 28 Jan 2011 13:11:47 +0000 Subject: ASoC: Add card driver data Provide driver data for cards within the card structure. To simplify the implementation of the PM operations we don't use the struct device driver data as this is used by the core to retrieve the card in callbacks from the device model and PM core. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 7ecdaefd1b63..4b6c0a8c332f 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -705,6 +705,8 @@ struct snd_soc_card { struct dentry *debugfs_pop_time; #endif u32 pop_time; + + void *drvdata; }; /* SoC machine DAI configuration, glues a codec and cpu DAI together */ @@ -756,6 +758,17 @@ unsigned int snd_soc_write(struct snd_soc_codec *codec, /* device driver data */ +static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card, + void *data) +{ + card->drvdata = data; +} + +static inline void *snd_soc_card_get_drvdata(struct snd_soc_card *card) +{ + return card->drvdata; +} + static inline void snd_soc_codec_set_drvdata(struct snd_soc_codec *codec, void *data) { -- cgit v1.2.3 From 6d744bacee8195c915c514409a81d470ce7b1177 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 27 Jan 2011 14:13:17 +0100 Subject: mac80211: add MCS information to radiotap This adds the MCS information we currently get from the drivers into radiotap. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/ieee80211_radiotap.h | 25 +++++++++++++++++++++++++ net/mac80211/rx.c | 17 +++++++++++++++++ 2 files changed, 42 insertions(+) (limited to 'include') diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index af49f8ab7f81..b0be5fb9de19 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -178,6 +178,11 @@ struct ieee80211_radiotap_header { * * Number of unicast retries a transmitted frame used. * + * IEEE80211_RADIOTAP_MCS u8, u8, u8 unitless + * + * Contains a bitmap of known fields/flags, the flags, and + * the MCS index. + * */ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_TSFT = 0, @@ -199,6 +204,8 @@ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_RTS_RETRIES = 16, IEEE80211_RADIOTAP_DATA_RETRIES = 17, + IEEE80211_RADIOTAP_MCS = 19, + /* valid in every it_present bitmap, even vendor namespaces */ IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29, IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30, @@ -245,6 +252,24 @@ enum ieee80211_radiotap_type { #define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ #define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ + +/* For IEEE80211_RADIOTAP_MCS */ +#define IEEE80211_RADIOTAP_MCS_HAVE_BW 0x01 +#define IEEE80211_RADIOTAP_MCS_HAVE_MCS 0x02 +#define IEEE80211_RADIOTAP_MCS_HAVE_GI 0x04 +#define IEEE80211_RADIOTAP_MCS_HAVE_FMT 0x08 +#define IEEE80211_RADIOTAP_MCS_HAVE_FEC 0x10 + +#define IEEE80211_RADIOTAP_MCS_BW_MASK 0x03 +#define IEEE80211_RADIOTAP_MCS_BW_20 0 +#define IEEE80211_RADIOTAP_MCS_BW_40 1 +#define IEEE80211_RADIOTAP_MCS_BW_20L 2 +#define IEEE80211_RADIOTAP_MCS_BW_20U 3 +#define IEEE80211_RADIOTAP_MCS_SGI 0x04 +#define IEEE80211_RADIOTAP_MCS_FMT_GF 0x08 +#define IEEE80211_RADIOTAP_MCS_FEC_LDPC 0x10 + + /* Ugly macro to convert literal channel numbers into their mhz equivalents * There are certianly some conditions that will break this (like feeding it '30') * but they shouldn't arise since nothing talks on channel 30. */ diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index f36d70f5b062..7185c9316be2 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -85,6 +85,9 @@ ieee80211_rx_radiotap_len(struct ieee80211_local *local, if (len & 1) /* padding for RX_FLAGS if necessary */ len++; + if (status->flag & RX_FLAG_HT) /* HT info */ + len += 3; + return len; } @@ -193,6 +196,20 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, rx_flags |= IEEE80211_RADIOTAP_F_RX_BADPLCP; put_unaligned_le16(rx_flags, pos); pos += 2; + + if (status->flag & RX_FLAG_HT) { + rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS); + *pos++ = IEEE80211_RADIOTAP_MCS_HAVE_MCS | + IEEE80211_RADIOTAP_MCS_HAVE_GI | + IEEE80211_RADIOTAP_MCS_HAVE_BW; + *pos = 0; + if (status->flag & RX_FLAG_SHORT_GI) + *pos |= IEEE80211_RADIOTAP_MCS_SGI; + if (status->flag & RX_FLAG_40MHZ) + *pos |= IEEE80211_RADIOTAP_MCS_BW_40; + pos++; + *pos++ = status->rate_idx; + } } /* -- cgit v1.2.3 From 9c150e82ac50a611237bbebd508d17f6347d577c Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 28 Jan 2011 14:01:25 -0800 Subject: ipv4: Allocate fib metrics dynamically. This is the initial gateway towards super-sharing metrics if they are all set to zero for a route. Signed-off-by: David S. Miller --- include/net/ip_fib.h | 2 +- net/ipv4/fib_semantics.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 65d1fcdbc63b..2c0508a6e07c 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -77,7 +77,7 @@ struct fib_info { int fib_protocol; __be32 fib_prefsrc; u32 fib_priority; - u32 fib_metrics[RTAX_MAX]; + u32 *fib_metrics; #define fib_mtu fib_metrics[RTAX_MTU-1] #define fib_window fib_metrics[RTAX_WINDOW-1] #define fib_rtt fib_metrics[RTAX_RTT-1] diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 9aff11d7278f..363ec39228d3 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -152,6 +152,7 @@ static void free_fib_info_rcu(struct rcu_head *head) { struct fib_info *fi = container_of(head, struct fib_info, rcu); + kfree(fi->fib_metrics); kfree(fi); } @@ -742,6 +743,9 @@ struct fib_info *fib_create_info(struct fib_config *cfg) fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); if (fi == NULL) goto failure; + fi->fib_metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); + if (!fi->fib_metrics) + goto failure; fib_info_cnt++; fi->fib_net = hold_net(net); -- cgit v1.2.3 From 725d1e1b457dc2bbebb337677e73efe7c6d14da5 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 28 Jan 2011 14:05:05 -0800 Subject: ipv4: Attach FIB info to dst_default_metrics when possible If there are no explicit metrics attached to a route, hook fi->fib_info up to dst_default_metrics. Signed-off-by: David S. Miller --- include/net/dst.h | 1 + net/core/dst.c | 2 +- net/ipv4/fib_semantics.c | 12 ++++++++---- 3 files changed, 10 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index 94a8c234ea2a..484f80b69ada 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -97,6 +97,7 @@ struct dst_entry { #ifdef __KERNEL__ extern u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old); +extern const u32 dst_default_metrics[RTAX_MAX]; #define DST_METRICS_READ_ONLY 0x1UL #define __DST_METRICS_PTR(Y) \ diff --git a/net/core/dst.c b/net/core/dst.c index 578893505702..c1674fde827d 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -164,7 +164,7 @@ int dst_discard(struct sk_buff *skb) } EXPORT_SYMBOL(dst_discard); -static const u32 dst_default_metrics[RTAX_MAX]; +const u32 dst_default_metrics[RTAX_MAX]; void *dst_alloc(struct dst_ops *ops) { diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 363ec39228d3..48e93a560077 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -152,7 +152,8 @@ static void free_fib_info_rcu(struct rcu_head *head) { struct fib_info *fi = container_of(head, struct fib_info, rcu); - kfree(fi->fib_metrics); + if (fi->fib_metrics != (u32 *) dst_default_metrics) + kfree(fi->fib_metrics); kfree(fi); } @@ -743,9 +744,12 @@ struct fib_info *fib_create_info(struct fib_config *cfg) fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); if (fi == NULL) goto failure; - fi->fib_metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); - if (!fi->fib_metrics) - goto failure; + if (cfg->fc_mx) { + fi->fib_metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); + if (!fi->fib_metrics) + goto failure; + } else + fi->fib_metrics = (u32 *) dst_default_metrics; fib_info_cnt++; fi->fib_net = hold_net(net); -- cgit v1.2.3 From f301c062dcdd113bc977ae1ebc8c12232f8531a9 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Fri, 21 Jan 2011 14:11:53 +0000 Subject: dmaengine/dw_dmac: allow src/dst masters to be configured at runtime Some platforms have flexible mastering capabilities and this needs to be selected at runtime. If the platform has specified private data in the form of the dw_dma_slave then fetch the source and destination masters from here. If this isn't present, default to the previous of 0 and 1. v2: cleanup whitespace Acked-by: Hans-Christian Egtvedt Signed-off-by: Jamie Iles Signed-off-by: Dan Williams --- drivers/dma/dw_dmac.c | 31 +++++++++++++++++-------------- include/linux/dw_dmac.h | 2 ++ 2 files changed, 19 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index db22754be35d..a4cf2614085a 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -32,15 +32,18 @@ * which does not support descriptor writeback. */ -/* NOTE: DMS+SMS is system-specific. We should get this information - * from the platform code somehow. - */ -#define DWC_DEFAULT_CTLLO (DWC_CTLL_DST_MSIZE(0) \ - | DWC_CTLL_SRC_MSIZE(0) \ - | DWC_CTLL_DMS(0) \ - | DWC_CTLL_SMS(1) \ - | DWC_CTLL_LLP_D_EN \ - | DWC_CTLL_LLP_S_EN) +#define DWC_DEFAULT_CTLLO(private) ({ \ + struct dw_dma_slave *__slave = (private); \ + int dms = __slave ? __slave->dst_master : 0; \ + int sms = __slave ? __slave->src_master : 1; \ + \ + (DWC_CTLL_DST_MSIZE(0) \ + | DWC_CTLL_SRC_MSIZE(0) \ + | DWC_CTLL_LLP_D_EN \ + | DWC_CTLL_LLP_S_EN \ + | DWC_CTLL_DMS(dms) \ + | DWC_CTLL_SMS(sms)); \ + }) /* * This is configuration-dependent and usually a funny size like 4095. @@ -591,7 +594,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, else src_width = dst_width = 0; - ctllo = DWC_DEFAULT_CTLLO + ctllo = DWC_DEFAULT_CTLLO(chan->private) | DWC_CTLL_DST_WIDTH(dst_width) | DWC_CTLL_SRC_WIDTH(src_width) | DWC_CTLL_DST_INC @@ -672,7 +675,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, switch (direction) { case DMA_TO_DEVICE: - ctllo = (DWC_DEFAULT_CTLLO + ctllo = (DWC_DEFAULT_CTLLO(chan->private) | DWC_CTLL_DST_WIDTH(reg_width) | DWC_CTLL_DST_FIX | DWC_CTLL_SRC_INC @@ -717,7 +720,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, } break; case DMA_FROM_DEVICE: - ctllo = (DWC_DEFAULT_CTLLO + ctllo = (DWC_DEFAULT_CTLLO(chan->private) | DWC_CTLL_SRC_WIDTH(reg_width) | DWC_CTLL_DST_INC | DWC_CTLL_SRC_FIX @@ -1129,7 +1132,7 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, case DMA_TO_DEVICE: desc->lli.dar = dws->tx_reg; desc->lli.sar = buf_addr + (period_len * i); - desc->lli.ctllo = (DWC_DEFAULT_CTLLO + desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan->private) | DWC_CTLL_DST_WIDTH(reg_width) | DWC_CTLL_SRC_WIDTH(reg_width) | DWC_CTLL_DST_FIX @@ -1140,7 +1143,7 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, case DMA_FROM_DEVICE: desc->lli.dar = buf_addr + (period_len * i); desc->lli.sar = dws->rx_reg; - desc->lli.ctllo = (DWC_DEFAULT_CTLLO + desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan->private) | DWC_CTLL_SRC_WIDTH(reg_width) | DWC_CTLL_DST_WIDTH(reg_width) | DWC_CTLL_DST_INC diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h index c8aad713a046..8014eb81054d 100644 --- a/include/linux/dw_dmac.h +++ b/include/linux/dw_dmac.h @@ -52,6 +52,8 @@ struct dw_dma_slave { enum dw_dma_slave_width reg_width; u32 cfg_hi; u32 cfg_lo; + int src_master; + int dst_master; }; /* Platform-configurable bits in CFG_HI */ -- cgit v1.2.3 From 95ea759e9e116dade3e7386be2a3db76c90f4675 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Fri, 21 Jan 2011 14:11:54 +0000 Subject: dmaengine/dw_dmac: provide a mechanism to indicate private devices Some platforms (e.g. Picochip PC3XX) have multiple DMA controllers where some may be used for slave transfers and others for general purpose memcpy type transfers. Add a .is_private boolean to the platform data structure so that controllers can be marked as private so that the DMA_PRIVATE capability will be set for that controller. Signed-off-by: Jamie Iles Signed-off-by: Dan Williams --- drivers/dma/dw_dmac.c | 2 ++ include/linux/dw_dmac.h | 3 +++ 2 files changed, 5 insertions(+) (limited to 'include') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index a4cf2614085a..08dab3badad2 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1341,6 +1341,8 @@ static int __init dw_probe(struct platform_device *pdev) dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask); dma_cap_set(DMA_SLAVE, dw->dma.cap_mask); + if (pdata->is_private) + dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask); dw->dma.dev = &pdev->dev; dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources; dw->dma.device_free_chan_resources = dwc_free_chan_resources; diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h index 8014eb81054d..deec66b37180 100644 --- a/include/linux/dw_dmac.h +++ b/include/linux/dw_dmac.h @@ -16,9 +16,12 @@ /** * struct dw_dma_platform_data - Controller configuration parameters * @nr_channels: Number of channels supported by hardware (max 8) + * @is_private: The device channels should be marked as private and not for + * by the general purpose DMA channel allocator. */ struct dw_dma_platform_data { unsigned int nr_channels; + bool is_private; }; /** -- cgit v1.2.3 From 52fe7c9cc1637110ba4e0e6fe5d07cc0786d62de Mon Sep 17 00:00:00 2001 From: "sjur.brandeland@stericsson.com" Date: Sat, 29 Jan 2011 13:10:37 +0000 Subject: caif: bugfix - add caif headers for userspace usage. Add caif_socket.h and if_caif.h to the kernel header files exported for use by userspace. Signed-off-by: Sjur Braendeland Signed-off-by: David S. Miller --- include/linux/Kbuild | 1 + include/linux/caif/Kbuild | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 include/linux/caif/Kbuild (limited to 'include') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 2296d8b1931f..b0ada6f37dd6 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -1,5 +1,6 @@ header-y += byteorder/ header-y += can/ +header-y += caif/ header-y += dvb/ header-y += hdlc/ header-y += isdn/ diff --git a/include/linux/caif/Kbuild b/include/linux/caif/Kbuild new file mode 100644 index 000000000000..a9cf250689dc --- /dev/null +++ b/include/linux/caif/Kbuild @@ -0,0 +1,2 @@ +header-y += caif_socket.h +header-y += if_caif.h -- cgit v1.2.3 From 709b46e8d90badda1898caea50483c12af178e96 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 29 Jan 2011 16:15:56 +0000 Subject: net: Add compat ioctl support for the ipv4 multicast ioctl SIOCGETSGCNT SIOCGETSGCNT is not a unique ioctl value as it it maps tio SIOCPROTOPRIVATE +1, which unfortunately means the existing infrastructure for compat networking ioctls is insufficient. A trivial compact ioctl implementation would conflict with: SIOCAX25ADDUID SIOCAIPXPRISLT SIOCGETSGCNT_IN6 SIOCGETSGCNT SIOCRSSCAUSE SIOCX25SSUBSCRIP SIOCX25SDTEFACILITIES To make this work I have updated the compat_ioctl decode path to mirror the the normal ioctl decode path. I have added an ipv4 inet_compat_ioctl function so that I can have ipv4 specific compat ioctls. I have added a compat_ioctl function into struct proto so I can break out ioctls by which kind of ip socket I am using. I have added a compat_raw_ioctl function because SIOCGETSGCNT only works on raw sockets. I have added a ipmr_compat_ioctl that mirrors the normal ipmr_ioctl. This was necessary because unfortunately the struct layout for the SIOCGETSGCNT has unsigned longs in it so changes between 32bit and 64bit kernels. This change was sufficient to run a 32bit ip multicast routing daemon on a 64bit kernel. Reported-by: Bill Fenner Signed-off-by: Eric W. Biederman Signed-off-by: David S. Miller --- include/linux/mroute.h | 1 + include/net/sock.h | 2 ++ net/ipv4/af_inet.c | 16 ++++++++++++++++ net/ipv4/ipmr.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ net/ipv4/raw.c | 19 +++++++++++++++++++ 5 files changed, 84 insertions(+) (limited to 'include') diff --git a/include/linux/mroute.h b/include/linux/mroute.h index 0fa7a3a874c8..b21d567692b2 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -150,6 +150,7 @@ static inline int ip_mroute_opt(int opt) extern int ip_mroute_setsockopt(struct sock *, int, char __user *, unsigned int); extern int ip_mroute_getsockopt(struct sock *, int, char __user *, int __user *); extern int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg); +extern int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg); extern int ip_mr_init(void); #else static inline diff --git a/include/net/sock.h b/include/net/sock.h index d884d268c704..bc1cf7d88ccb 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -753,6 +753,8 @@ struct proto { int level, int optname, char __user *optval, int __user *option); + int (*compat_ioctl)(struct sock *sk, + unsigned int cmd, unsigned long arg); #endif int (*sendmsg)(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len); diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index f2b61107df6c..45b89d7bda5a 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -880,6 +880,19 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) } EXPORT_SYMBOL(inet_ioctl); +#ifdef CONFIG_COMPAT +int inet_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + struct sock *sk = sock->sk; + int err = -ENOIOCTLCMD; + + if (sk->sk_prot->compat_ioctl) + err = sk->sk_prot->compat_ioctl(sk, cmd, arg); + + return err; +} +#endif + const struct proto_ops inet_stream_ops = { .family = PF_INET, .owner = THIS_MODULE, @@ -903,6 +916,7 @@ const struct proto_ops inet_stream_ops = { #ifdef CONFIG_COMPAT .compat_setsockopt = compat_sock_common_setsockopt, .compat_getsockopt = compat_sock_common_getsockopt, + .compat_ioctl = inet_compat_ioctl, #endif }; EXPORT_SYMBOL(inet_stream_ops); @@ -929,6 +943,7 @@ const struct proto_ops inet_dgram_ops = { #ifdef CONFIG_COMPAT .compat_setsockopt = compat_sock_common_setsockopt, .compat_getsockopt = compat_sock_common_getsockopt, + .compat_ioctl = inet_compat_ioctl, #endif }; EXPORT_SYMBOL(inet_dgram_ops); @@ -959,6 +974,7 @@ static const struct proto_ops inet_sockraw_ops = { #ifdef CONFIG_COMPAT .compat_setsockopt = compat_sock_common_setsockopt, .compat_getsockopt = compat_sock_common_getsockopt, + .compat_ioctl = inet_compat_ioctl, #endif }; diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 3f3a9afd73e0..7e41ac0b9260 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include @@ -1434,6 +1435,51 @@ int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg) } } +#ifdef CONFIG_COMPAT +struct compat_sioc_sg_req { + struct in_addr src; + struct in_addr grp; + compat_ulong_t pktcnt; + compat_ulong_t bytecnt; + compat_ulong_t wrong_if; +}; + +int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) +{ + struct sioc_sg_req sr; + struct mfc_cache *c; + struct net *net = sock_net(sk); + struct mr_table *mrt; + + mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); + if (mrt == NULL) + return -ENOENT; + + switch (cmd) { + case SIOCGETSGCNT: + if (copy_from_user(&sr, arg, sizeof(sr))) + return -EFAULT; + + rcu_read_lock(); + c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr); + if (c) { + sr.pktcnt = c->mfc_un.res.pkt; + sr.bytecnt = c->mfc_un.res.bytes; + sr.wrong_if = c->mfc_un.res.wrong_if; + rcu_read_unlock(); + + if (copy_to_user(arg, &sr, sizeof(sr))) + return -EFAULT; + return 0; + } + rcu_read_unlock(); + return -EADDRNOTAVAIL; + default: + return -ENOIOCTLCMD; + } +} +#endif + static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr) { diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index a3d5ab786e81..6390ba299b3d 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -76,6 +76,7 @@ #include #include #include +#include static struct raw_hashinfo raw_v4_hashinfo = { .lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock), @@ -838,6 +839,23 @@ static int raw_ioctl(struct sock *sk, int cmd, unsigned long arg) } } +#ifdef CONFIG_COMPAT +static int compat_raw_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case SIOCOUTQ: + case SIOCINQ: + return -ENOIOCTLCMD; + default: +#ifdef CONFIG_IP_MROUTE + return ipmr_compat_ioctl(sk, cmd, compat_ptr(arg)); +#else + return -ENOIOCTLCMD; +#endif + } +} +#endif + struct proto raw_prot = { .name = "RAW", .owner = THIS_MODULE, @@ -860,6 +878,7 @@ struct proto raw_prot = { #ifdef CONFIG_COMPAT .compat_setsockopt = compat_raw_setsockopt, .compat_getsockopt = compat_raw_getsockopt, + .compat_ioctl = compat_raw_ioctl, #endif }; -- cgit v1.2.3 From 78c6e170badd22c86a5b50a7eb038a02024b8f03 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 31 Jan 2011 10:48:04 +0000 Subject: drm/i915: Suppress spurious vblank interrupts Hugh Dickins found that characters in xterm were going missing and oft delayed. Being the curious type, he managed to associate this with the new high-precision vblank patches; disabling these he found, restored the orderliness of his characters. The oddness begins when one realised that Hugh was not using vblanks at all on his system (fvwm and some xterms). Instead, all he had to go on were warning of a pipe underrun, curiously enough at around 60Hz. He poked and found that in addition to the underrun warning, the hardware was flagging the start of a new frame, a vblank, which in turn was kicking off the pending vblank processing code. There is little we can do for the underruns on Hugh's machine, a Crestline [965GM], which must have its FIFO watermarks set to 8. However, we do not need to process the vblank if we know that they are disabled... Reported-by: Hugh Dickins Signed-off-by: Chris Wilson --- drivers/gpu/drm/drm_irq.c | 7 ++++--- drivers/gpu/drm/i915/i915_irq.c | 8 ++++---- include/drm/drmP.h | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 0054e957203f..3dadfa2a8528 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -1250,7 +1250,7 @@ void drm_handle_vblank_events(struct drm_device *dev, int crtc) * Drivers should call this routine in their vblank interrupt handlers to * update the vblank counter and send any signals that may be pending. */ -void drm_handle_vblank(struct drm_device *dev, int crtc) +bool drm_handle_vblank(struct drm_device *dev, int crtc) { u32 vblcount; s64 diff_ns; @@ -1258,7 +1258,7 @@ void drm_handle_vblank(struct drm_device *dev, int crtc) unsigned long irqflags; if (!dev->num_crtcs) - return; + return false; /* Need timestamp lock to prevent concurrent execution with * vblank enable/disable, as this would cause inconsistent @@ -1269,7 +1269,7 @@ void drm_handle_vblank(struct drm_device *dev, int crtc) /* Vblank irq handling disabled. Nothing to do. */ if (!dev->vblank_enabled[crtc]) { spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); - return; + return false; } /* Fetch corresponding timestamp for this vblank interval from @@ -1311,5 +1311,6 @@ void drm_handle_vblank(struct drm_device *dev, int crtc) drm_handle_vblank_events(dev, crtc); spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); + return true; } EXPORT_SYMBOL(drm_handle_vblank); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 062f353497e6..97f946dcc1aa 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1196,18 +1196,18 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) intel_finish_page_flip_plane(dev, 1); } - if (pipea_stats & vblank_status) { + if (pipea_stats & vblank_status && + drm_handle_vblank(dev, 0)) { vblank++; - drm_handle_vblank(dev, 0); if (!dev_priv->flip_pending_is_done) { i915_pageflip_stall_check(dev, 0); intel_finish_page_flip(dev, 0); } } - if (pipeb_stats & vblank_status) { + if (pipeb_stats & vblank_status && + drm_handle_vblank(dev, 1)) { vblank++; - drm_handle_vblank(dev, 1); if (!dev_priv->flip_pending_is_done) { i915_pageflip_stall_check(dev, 1); intel_finish_page_flip(dev, 1); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index a4694c610330..fe29aadb129d 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1367,7 +1367,7 @@ extern int drm_vblank_wait(struct drm_device *dev, unsigned int *vbl_seq); extern u32 drm_vblank_count(struct drm_device *dev, int crtc); extern u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc, struct timeval *vblanktime); -extern void drm_handle_vblank(struct drm_device *dev, int crtc); +extern bool drm_handle_vblank(struct drm_device *dev, int crtc); extern int drm_vblank_get(struct drm_device *dev, int crtc); extern void drm_vblank_put(struct drm_device *dev, int crtc); extern void drm_vblank_off(struct drm_device *dev, int crtc); -- cgit v1.2.3 From 51d07566045787b99219d809639c8724506fc78a Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Tue, 25 Jan 2011 11:10:06 -0800 Subject: bq20z75: Add support for charge properties Adding support for charge properties for gas gauge. Also ensuring that battery mode is correct now for energy as well as charge properties by setting it on the fly. I also added 2 functions to power_supply.h to help identify the units for specific properties more easily by power supplies. Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/bq20z75.c | 98 +++++++++++++++++++++++++++++++++++++++----- include/linux/power_supply.h | 44 ++++++++++++++++++++ 2 files changed, 131 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index 492da27e1a47..4141775e5ff6 100644 --- a/drivers/power/bq20z75.c +++ b/drivers/power/bq20z75.c @@ -38,11 +38,22 @@ enum { REG_CYCLE_COUNT, REG_SERIAL_NUMBER, REG_REMAINING_CAPACITY, + REG_REMAINING_CAPACITY_CHARGE, REG_FULL_CHARGE_CAPACITY, + REG_FULL_CHARGE_CAPACITY_CHARGE, REG_DESIGN_CAPACITY, + REG_DESIGN_CAPACITY_CHARGE, REG_DESIGN_VOLTAGE, }; +/* Battery Mode defines */ +#define BATTERY_MODE_OFFSET 0x03 +#define BATTERY_MODE_MASK 0x8000 +enum bq20z75_battery_mode { + BATTERY_MODE_AMPS, + BATTERY_MODE_WATTS +}; + /* manufacturer access defines */ #define MANUFACTURER_ACCESS_STATUS 0x0006 #define MANUFACTURER_ACCESS_SLEEP 0x0011 @@ -78,8 +89,12 @@ static const struct bq20z75_device_data { BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100), [REG_REMAINING_CAPACITY] = BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535), + [REG_REMAINING_CAPACITY_CHARGE] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_NOW, 0x0F, 0, 65535), [REG_FULL_CHARGE_CAPACITY] = BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535), + [REG_FULL_CHARGE_CAPACITY_CHARGE] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_FULL, 0x10, 0, 65535), [REG_TIME_TO_EMPTY] = BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, 65535), @@ -93,6 +108,9 @@ static const struct bq20z75_device_data { [REG_DESIGN_CAPACITY] = BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, 65535), + [REG_DESIGN_CAPACITY_CHARGE] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 0x18, 0, + 65535), [REG_DESIGN_VOLTAGE] = BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, 65535), @@ -117,6 +135,9 @@ static enum power_supply_property bq20z75_properties[] = { POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_ENERGY_FULL, POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, }; struct bq20z75_info { @@ -260,6 +281,9 @@ static void bq20z75_unit_adjustment(struct i2c_client *client, case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: val->intval *= BASE_UNIT_CONVERSION; break; @@ -281,11 +305,44 @@ static void bq20z75_unit_adjustment(struct i2c_client *client, } } +static enum bq20z75_battery_mode +bq20z75_set_battery_mode(struct i2c_client *client, + enum bq20z75_battery_mode mode) +{ + int ret, original_val; + + original_val = bq20z75_read_word_data(client, BATTERY_MODE_OFFSET); + if (original_val < 0) + return original_val; + + if ((original_val & BATTERY_MODE_MASK) == mode) + return mode; + + if (mode == BATTERY_MODE_AMPS) + ret = original_val & ~BATTERY_MODE_MASK; + else + ret = original_val | BATTERY_MODE_MASK; + + ret = bq20z75_write_word_data(client, BATTERY_MODE_OFFSET, ret); + if (ret < 0) + return ret; + + return original_val & BATTERY_MODE_MASK; +} + static int bq20z75_get_battery_capacity(struct i2c_client *client, int reg_offset, enum power_supply_property psp, union power_supply_propval *val) { s32 ret; + enum bq20z75_battery_mode mode = BATTERY_MODE_WATTS; + + if (power_supply_is_amp_property(psp)) + mode = BATTERY_MODE_AMPS; + + mode = bq20z75_set_battery_mode(client, mode); + if (mode < 0) + return mode; ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr); if (ret < 0) @@ -298,6 +355,10 @@ static int bq20z75_get_battery_capacity(struct i2c_client *client, } else val->intval = ret; + ret = bq20z75_set_battery_mode(client, mode); + if (ret < 0) + return ret; + return 0; } @@ -318,11 +379,25 @@ static int bq20z75_get_battery_serial_number(struct i2c_client *client, return 0; } +static int bq20z75_get_property_index(struct i2c_client *client, + enum power_supply_property psp) +{ + int count; + for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) + if (psp == bq20z75_data[count].psp) + return count; + + dev_warn(&client->dev, + "%s: Invalid Property - %d\n", __func__, psp); + + return -EINVAL; +} + static int bq20z75_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - int count; + int ps_index; int ret; struct bq20z75_info *bq20z75_device = container_of(psy, struct bq20z75_info, power_supply); @@ -343,13 +418,15 @@ static int bq20z75_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_ENERGY_NOW: case POWER_SUPPLY_PROP_ENERGY_FULL: case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: case POWER_SUPPLY_PROP_CAPACITY: - for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) { - if (psp == bq20z75_data[count].psp) - break; - } + ps_index = bq20z75_get_property_index(client, psp); + if (ps_index < 0) + return ps_index; - ret = bq20z75_get_battery_capacity(client, count, psp, val); + ret = bq20z75_get_battery_capacity(client, ps_index, psp, val); if (ret) return ret; @@ -369,12 +446,11 @@ static int bq20z75_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) { - if (psp == bq20z75_data[count].psp) - break; - } + ps_index = bq20z75_get_property_index(client, psp); + if (ps_index < 0) + return ps_index; - ret = bq20z75_get_battery_property(client, count, psp, val); + ret = bq20z75_get_battery_property(client, ps_index, psp, val); if (ret) return ret; diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 7d7325685c42..e3419fc5541e 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -213,4 +213,48 @@ extern void power_supply_unregister(struct power_supply *psy); /* For APM emulation, think legacy userspace. */ extern struct class *power_supply_class; +static inline bool power_supply_is_amp_property(enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN: + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_CHARGE_EMPTY: + case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_CHARGE_AVG: + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + case POWER_SUPPLY_PROP_CURRENT_MAX: + case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_CURRENT_AVG: + return 1; + default: + break; + } + + return 0; +} + +static inline bool power_supply_is_watt_property(enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + case POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN: + case POWER_SUPPLY_PROP_ENERGY_FULL: + case POWER_SUPPLY_PROP_ENERGY_EMPTY: + case POWER_SUPPLY_PROP_ENERGY_NOW: + case POWER_SUPPLY_PROP_ENERGY_AVG: + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + case POWER_SUPPLY_PROP_VOLTAGE_MIN: + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + return 1; + default: + break; + } + + return 0; +} + #endif /* __LINUX_POWER_SUPPLY_H__ */ -- cgit v1.2.3 From 0b9536c957095eb1497828aa51b34ac695f67eae Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Fri, 7 Jan 2011 16:28:16 +0000 Subject: leds: Add ability to blink via simple trigger As blink API is now available, it's possible to add ability to blink via simple trigger. Signed-off-by: Vasily Khoruzhick Signed-off-by: Anton Vorontsov --- drivers/leds/led-triggers.c | 20 ++++++++++++++++++++ include/linux/leds.h | 3 +++ 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index c41eb6180c9c..4bebae733349 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -231,6 +231,26 @@ void led_trigger_event(struct led_trigger *trigger, } EXPORT_SYMBOL_GPL(led_trigger_event); +void led_trigger_blink(struct led_trigger *trigger, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct list_head *entry; + + if (!trigger) + return; + + read_lock(&trigger->leddev_list_lock); + list_for_each(entry, &trigger->led_cdevs) { + struct led_classdev *led_cdev; + + led_cdev = list_entry(entry, struct led_classdev, trig_list); + led_blink_set(led_cdev, delay_on, delay_off); + } + read_unlock(&trigger->leddev_list_lock); +} +EXPORT_SYMBOL_GPL(led_trigger_blink); + void led_trigger_register_simple(const char *name, struct led_trigger **tp) { struct led_trigger *trigger; diff --git a/include/linux/leds.h b/include/linux/leds.h index 0f19df9e37b0..ffd5c3d91193 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -145,6 +145,9 @@ extern void led_trigger_register_simple(const char *name, extern void led_trigger_unregister_simple(struct led_trigger *trigger); extern void led_trigger_event(struct led_trigger *trigger, enum led_brightness event); +extern void led_trigger_blink(struct led_trigger *trigger, + unsigned long *delay_on, + unsigned long *delay_off); #else -- cgit v1.2.3 From 6501f728c56f831626d52b236023e556bca37f51 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Fri, 7 Jan 2011 18:28:17 +0200 Subject: power_supply: Add new LED trigger charging-blink-solid-full Add new trigger to power_supply LEDs. It will blink when battery is charging, and stay solid when battery is charged. It's usefull to indicate battery state when there's only one LED available. Signed-off-by: Vasily Khoruzhick Signed-off-by: Anton Vorontsov --- drivers/power/power_supply_leds.c | 19 +++++++++++++++++++ include/linux/power_supply.h | 2 ++ 2 files changed, 21 insertions(+) (limited to 'include') diff --git a/drivers/power/power_supply_leds.c b/drivers/power/power_supply_leds.c index 031a554837f7..da25eb94e5c6 100644 --- a/drivers/power/power_supply_leds.c +++ b/drivers/power/power_supply_leds.c @@ -21,6 +21,8 @@ static void power_supply_update_bat_leds(struct power_supply *psy) { union power_supply_propval status; + unsigned long delay_on = 0; + unsigned long delay_off = 0; if (psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &status)) return; @@ -32,16 +34,22 @@ static void power_supply_update_bat_leds(struct power_supply *psy) led_trigger_event(psy->charging_full_trig, LED_FULL); led_trigger_event(psy->charging_trig, LED_OFF); led_trigger_event(psy->full_trig, LED_FULL); + led_trigger_event(psy->charging_blink_full_solid_trig, + LED_FULL); break; case POWER_SUPPLY_STATUS_CHARGING: led_trigger_event(psy->charging_full_trig, LED_FULL); led_trigger_event(psy->charging_trig, LED_FULL); led_trigger_event(psy->full_trig, LED_OFF); + led_trigger_blink(psy->charging_blink_full_solid_trig, + &delay_on, &delay_off); break; default: led_trigger_event(psy->charging_full_trig, LED_OFF); led_trigger_event(psy->charging_trig, LED_OFF); led_trigger_event(psy->full_trig, LED_OFF); + led_trigger_event(psy->charging_blink_full_solid_trig, + LED_OFF); break; } } @@ -64,15 +72,24 @@ static int power_supply_create_bat_triggers(struct power_supply *psy) if (!psy->full_trig_name) goto full_failed; + psy->charging_blink_full_solid_trig_name = kasprintf(GFP_KERNEL, + "%s-charging-blink-full-solid", psy->name); + if (!psy->charging_blink_full_solid_trig_name) + goto charging_blink_full_solid_failed; + led_trigger_register_simple(psy->charging_full_trig_name, &psy->charging_full_trig); led_trigger_register_simple(psy->charging_trig_name, &psy->charging_trig); led_trigger_register_simple(psy->full_trig_name, &psy->full_trig); + led_trigger_register_simple(psy->charging_blink_full_solid_trig_name, + &psy->charging_blink_full_solid_trig); goto success; +charging_blink_full_solid_failed: + kfree(psy->full_trig_name); full_failed: kfree(psy->charging_trig_name); charging_failed: @@ -88,6 +105,8 @@ static void power_supply_remove_bat_triggers(struct power_supply *psy) led_trigger_unregister_simple(psy->charging_full_trig); led_trigger_unregister_simple(psy->charging_trig); led_trigger_unregister_simple(psy->full_trig); + led_trigger_unregister_simple(psy->charging_blink_full_solid_trig); + kfree(psy->charging_blink_full_solid_trig_name); kfree(psy->full_trig_name); kfree(psy->charging_trig_name); kfree(psy->charging_full_trig_name); diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index e3419fc5541e..20f23fef63cc 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -173,6 +173,8 @@ struct power_supply { char *full_trig_name; struct led_trigger *online_trig; char *online_trig_name; + struct led_trigger *charging_blink_full_solid_trig; + char *charging_blink_full_solid_trig_name; #endif }; -- cgit v1.2.3 From 871cf1e5f2a17702f58539a3af8b18fc8666ad4c Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Thu, 27 Jan 2011 15:58:55 +0100 Subject: time: Move do_timer() to kernel/time/timekeeping.c do_timer() is primary timekeeping related. calc_global_load() is called from do_timer() as well, but that's more for historical reasons. [ tglx: Fixed up the calc_global_load() reject andmassaged changelog ] Signed-off-by: Torben Hohn Cc: Peter Zijlstra Cc: johnstul@us.ibm.com Cc: yong.zhang0@gmail.com Cc: hch@infradead.org LKML-Reference: <20110127145855.23248.56933.stgit@localhost> Signed-off-by: Thomas Gleixner --- include/linux/time.h | 1 - kernel/time/timekeeping.c | 14 +++++++++++++- kernel/timer.c | 13 ------------- 3 files changed, 13 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/time.h b/include/linux/time.h index 1e6d3b59238d..86a9c487fdd8 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -166,7 +166,6 @@ extern void monotonic_to_bootbased(struct timespec *ts); extern struct timespec timespec_trunc(struct timespec t, unsigned gran); extern int timekeeping_valid_for_hres(void); extern u64 timekeeping_max_deferment(void); -extern void update_wall_time(void); extern void timekeeping_leap_insert(int leapsecond); struct tms; diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index d27c7562902c..c1a178ca0f50 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -779,7 +779,7 @@ static cycle_t logarithmic_accumulation(cycle_t offset, int shift) * * Called from the timer interrupt, must hold a write on xtime_lock. */ -void update_wall_time(void) +static void update_wall_time(void) { struct clocksource *clock; cycle_t offset; @@ -946,3 +946,15 @@ struct timespec get_monotonic_coarse(void) now.tv_nsec + mono.tv_nsec); return now; } + +/* + * The 64-bit jiffies value is not atomic - you MUST NOT read it + * without sampling the sequence number in xtime_lock. + * jiffies is defined in the linker script... + */ +void do_timer(unsigned long ticks) +{ + jiffies_64 += ticks; + update_wall_time(); + calc_global_load(ticks); +} diff --git a/kernel/timer.c b/kernel/timer.c index 43ca9936f2d0..87f656cc2a55 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -1293,19 +1293,6 @@ void run_local_timers(void) raise_softirq(TIMER_SOFTIRQ); } -/* - * The 64-bit jiffies value is not atomic - you MUST NOT read it - * without sampling the sequence number in xtime_lock. - * jiffies is defined in the linker script... - */ - -void do_timer(unsigned long ticks) -{ - jiffies_64 += ticks; - update_wall_time(); - calc_global_load(ticks); -} - #ifdef __ARCH_WANT_SYS_ALARM /* -- cgit v1.2.3 From 48cf76f7104f655bbd48a75c7759dce82c3e1ab6 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Thu, 27 Jan 2011 15:59:05 +0100 Subject: time: Provide get_xtime_and_monotonic_offset() The hrtimer code accesses timekeeping variables under xtime_lock. Provide a sensible accessor function and use it. [ tglx: Removed the conditionals, unused variable, fixed codingstyle and massaged changelog ] Signed-off-by: Torben Hohn Cc: Peter Zijlstra Cc: johnstul@us.ibm.com Cc: yong.zhang0@gmail.com Cc: hch@infradead.org LKML-Reference: <20110127145905.23248.30458.stgit@localhost> Signed-off-by: Thomas Gleixner --- include/linux/time.h | 1 + kernel/hrtimer.c | 13 ++----------- kernel/time/timekeeping.c | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/time.h b/include/linux/time.h index 86a9c487fdd8..4007a12a1b50 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -127,6 +127,7 @@ struct timespec current_kernel_time(void); struct timespec __current_kernel_time(void); /* does not take xtime_lock */ struct timespec __get_wall_to_monotonic(void); /* does not take xtime_lock */ struct timespec get_monotonic_coarse(void); +void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom); #define CURRENT_TIME (current_kernel_time()) #define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 }) diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 0c8d7c048615..57c4d33c9a9d 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -85,13 +85,8 @@ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base) { ktime_t xtim, tomono; struct timespec xts, tom; - unsigned long seq; - do { - seq = read_seqbegin(&xtime_lock); - xts = __current_kernel_time(); - tom = __get_wall_to_monotonic(); - } while (read_seqretry(&xtime_lock, seq)); + get_xtime_and_monotonic_offset(&xts, &tom); xtim = timespec_to_ktime(xts); tomono = timespec_to_ktime(tom); @@ -612,15 +607,11 @@ static void retrigger_next_event(void *arg) { struct hrtimer_cpu_base *base; struct timespec realtime_offset, wtm; - unsigned long seq; if (!hrtimer_hres_active()) return; - do { - seq = read_seqbegin(&xtime_lock); - wtm = __get_wall_to_monotonic(); - } while (read_seqretry(&xtime_lock, seq)); + get_xtime_and_monotonic_offset(&realtime_offset, &wtm); set_normalized_timespec(&realtime_offset, -wtm.tv_sec, -wtm.tv_nsec); base = &__get_cpu_var(hrtimer_bases); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index c1a178ca0f50..c50aaf6cd01d 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -958,3 +958,19 @@ void do_timer(unsigned long ticks) update_wall_time(); calc_global_load(ticks); } + +/** + * get_xtime_and_monotonic_offset() - get xtime and wall_to_monotonic + * @xtim: pointer to timespec to be set with xtime + * @wtom: pointer to timespec to be set with wall_to_monotonic + */ +void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom) +{ + unsigned long seq; + + do { + seq = read_seqbegin(&xtime_lock); + *xtim = xtime; + *wtom = wall_to_monotonic; + } while (read_seqretry(&xtime_lock, seq)); +} -- cgit v1.2.3 From 79ecaf0d15344d78904becf0f25de3fc9b49d430 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 31 Jan 2011 11:07:54 +0100 Subject: time: Remove unused __get_wall_to_monotonic() No users left. Remove it. Signed-off-by: Thomas Gleixner --- include/linux/time.h | 1 - kernel/time/timekeeping.c | 5 ----- 2 files changed, 6 deletions(-) (limited to 'include') diff --git a/include/linux/time.h b/include/linux/time.h index 4007a12a1b50..ce29c86882b1 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -125,7 +125,6 @@ extern int timekeeping_suspended; unsigned long get_seconds(void); struct timespec current_kernel_time(void); struct timespec __current_kernel_time(void); /* does not take xtime_lock */ -struct timespec __get_wall_to_monotonic(void); /* does not take xtime_lock */ struct timespec get_monotonic_coarse(void); void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index c50aaf6cd01d..8da35d1b9e16 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -910,11 +910,6 @@ struct timespec __current_kernel_time(void) return xtime; } -struct timespec __get_wall_to_monotonic(void) -{ - return wall_to_monotonic; -} - struct timespec current_kernel_time(void) { struct timespec now; -- cgit v1.2.3 From f0af911a9dec9de702645182c8d269449e24d24b Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Thu, 27 Jan 2011 15:59:10 +0100 Subject: time: Provide xtime_update() xtime_update() takes xtime_lock write locked and calls do_timer(). Provided to replace the do_timer() calls in the architecture code. Signed-off-by: Torben Hohn Cc: Peter Zijlstra Cc: johnstul@us.ibm.com Cc: yong.zhang0@gmail.com Cc: hch@infradead.org LKML-Reference: <20110127145910.23248.21379.stgit@localhost> Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 1 + kernel/time/timekeeping.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index d747f948b34e..9d9a0787eed3 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2050,6 +2050,7 @@ extern void release_uids(struct user_namespace *ns); #include extern void do_timer(unsigned long ticks); +extern void xtime_update(unsigned long ticks); extern int wake_up_state(struct task_struct *tsk, unsigned int state); extern int wake_up_process(struct task_struct *tsk); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 8da35d1b9e16..02c13a313d15 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -969,3 +969,16 @@ void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom *wtom = wall_to_monotonic; } while (read_seqretry(&xtime_lock, seq)); } + +/** + * xtime_update() - advances the timekeeping infrastructure + * @ticks: number of ticks, that have elapsed since the last call. + * + * Must be called with interrupts disabled. + */ +void xtime_update(unsigned long ticks) +{ + write_seqlock(&xtime_lock); + do_timer(ticks); + write_sequnlock(&xtime_lock); +} -- cgit v1.2.3 From e2830b5c1b2b2217894370a3b95af87d4a958401 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Thu, 27 Jan 2011 16:00:32 +0100 Subject: time: Make do_timer() and xtime_lock local to kernel/time/ All callers of do_timer() are converted to xtime_update(). The only users of xtime_lock are in kernel/time/. Make both local to kernel/time/ and remove them from the global header files. [ tglx: Reuse tick-internal.h instead of creating another local header file. Massaged changelog ] Signed-off-by: Torben Hohn Cc: Peter Zijlstra Cc: johnstul@us.ibm.com Cc: yong.zhang0@gmail.com Cc: hch@infradead.org Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 1 - include/linux/time.h | 2 -- kernel/time/clockevents.c | 1 - kernel/time/jiffies.c | 2 ++ kernel/time/ntp.c | 2 ++ kernel/time/tick-broadcast.c | 1 - kernel/time/tick-common.c | 1 - kernel/time/tick-internal.h | 5 +++++ kernel/time/tick-oneshot.c | 1 - kernel/time/tick-sched.c | 1 - 10 files changed, 9 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 9d9a0787eed3..cdef640aa446 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2049,7 +2049,6 @@ extern void release_uids(struct user_namespace *ns); #include -extern void do_timer(unsigned long ticks); extern void xtime_update(unsigned long ticks); extern int wake_up_state(struct task_struct *tsk, unsigned int state); diff --git a/include/linux/time.h b/include/linux/time.h index ce29c86882b1..38c5206c2673 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -113,8 +113,6 @@ static inline struct timespec timespec_sub(struct timespec lhs, #define timespec_valid(ts) \ (((ts)->tv_sec >= 0) && (((unsigned long) (ts)->tv_nsec) < NSEC_PER_SEC)) -extern seqlock_t xtime_lock; - extern void read_persistent_clock(struct timespec *ts); extern void read_boot_clock(struct timespec *ts); extern int update_persistent_clock(struct timespec now); diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index d7395fdfb9f3..0d74b9ba90c8 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -18,7 +18,6 @@ #include #include #include -#include #include "tick-internal.h" diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c index 2fbc20744797..b2fa506667c0 100644 --- a/kernel/time/jiffies.c +++ b/kernel/time/jiffies.c @@ -25,6 +25,8 @@ #include #include +#include "tick-internal.h" + /* The Jiffies based clocksource is the lowest common * denominator clock source which should function on * all systems. It has the same coarse resolution as diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index 5c00242fa921..ed8cfdf16983 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -16,6 +16,8 @@ #include #include +#include "tick-internal.h" + /* * NTP timekeeping variables: */ diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 48b2761b5668..92ef9a54f0a4 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -18,7 +18,6 @@ #include #include #include -#include #include "tick-internal.h" diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 051bc80a0c43..0e98fac3d479 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -18,7 +18,6 @@ #include #include #include -#include #include diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h index 290eefbc1f60..28c578568c9d 100644 --- a/kernel/time/tick-internal.h +++ b/kernel/time/tick-internal.h @@ -1,6 +1,8 @@ /* * tick internal variable and functions used by low/high res code */ +#include +#include #define TICK_DO_TIMER_NONE -1 #define TICK_DO_TIMER_BOOT -2 @@ -132,3 +134,6 @@ static inline int tick_device_is_functional(struct clock_event_device *dev) { return !(dev->features & CLOCK_EVT_FEAT_DUMMY); } + +extern void do_timer(unsigned long ticks); +extern seqlock_t xtime_lock; diff --git a/kernel/time/tick-oneshot.c b/kernel/time/tick-oneshot.c index 5cbc101f908b..2d04411a5f05 100644 --- a/kernel/time/tick-oneshot.c +++ b/kernel/time/tick-oneshot.c @@ -18,7 +18,6 @@ #include #include #include -#include #include "tick-internal.h" diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index c55ea2433471..d5097c44b407 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3 From a6238f21736af3f47bdebf3895f477f5f23f1af9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 25 Jan 2011 23:17:27 +0100 Subject: appletalk: move to staging For all I know, Appletalk is dead, the only reasonable use right now would be nostalgia, and that can be served well enough by old kernels. The code is largely not in a bad shape, but it still uses the big kernel lock, and nobody seems motivated to change that. FWIW, the last release of MacOS that supported Appletalk was MacOS X 10.5, made in 2007, and it has been abandoned by Apple with 10.6. Using TCP/IP instead of Appletalk has been supported since MacOS 7.6, which was released in 1997 and is able to run on most of the legacy hardware. Signed-off-by: Arnd Bergmann Cc: Arnaldo Carvalho de Melo Cc: netdev@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 3 +- drivers/net/Makefile | 1 - drivers/net/appletalk/Kconfig | 126 -- drivers/net/appletalk/Makefile | 7 - drivers/net/appletalk/cops.c | 1013 ------------- drivers/net/appletalk/cops.h | 60 - drivers/net/appletalk/cops_ffdrv.h | 532 ------- drivers/net/appletalk/cops_ltdrv.h | 241 ---- drivers/net/appletalk/ipddp.c | 335 ----- drivers/net/appletalk/ipddp.h | 27 - drivers/net/appletalk/ltpc.c | 1288 ----------------- drivers/net/appletalk/ltpc.h | 73 - drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/appletalk/Kconfig | 126 ++ drivers/staging/appletalk/Makefile | 12 + drivers/staging/appletalk/aarp.c | 1063 ++++++++++++++ drivers/staging/appletalk/atalk.h | 208 +++ drivers/staging/appletalk/atalk_proc.c | 301 ++++ drivers/staging/appletalk/cops.c | 1013 +++++++++++++ drivers/staging/appletalk/cops.h | 60 + drivers/staging/appletalk/cops_ffdrv.h | 532 +++++++ drivers/staging/appletalk/cops_ltdrv.h | 241 ++++ drivers/staging/appletalk/ddp.c | 1981 ++++++++++++++++++++++++++ drivers/staging/appletalk/dev.c | 44 + drivers/staging/appletalk/ipddp.c | 335 +++++ drivers/staging/appletalk/ipddp.h | 27 + drivers/staging/appletalk/ltpc.c | 1288 +++++++++++++++++ drivers/staging/appletalk/ltpc.h | 73 + drivers/staging/appletalk/sysctl_net_atalk.c | 61 + fs/compat_ioctl.c | 1 - include/linux/Kbuild | 1 - include/linux/atalk.h | 208 --- net/Kconfig | 1 - net/Makefile | 1 - net/appletalk/Makefile | 9 - net/appletalk/aarp.c | 1063 -------------- net/appletalk/atalk_proc.c | 301 ---- net/appletalk/ddp.c | 1981 -------------------------- net/appletalk/dev.c | 44 - net/appletalk/sysctl_net_atalk.c | 61 - net/socket.c | 1 - 42 files changed, 7369 insertions(+), 7377 deletions(-) delete mode 100644 drivers/net/appletalk/Kconfig delete mode 100644 drivers/net/appletalk/Makefile delete mode 100644 drivers/net/appletalk/cops.c delete mode 100644 drivers/net/appletalk/cops.h delete mode 100644 drivers/net/appletalk/cops_ffdrv.h delete mode 100644 drivers/net/appletalk/cops_ltdrv.h delete mode 100644 drivers/net/appletalk/ipddp.c delete mode 100644 drivers/net/appletalk/ipddp.h delete mode 100644 drivers/net/appletalk/ltpc.c delete mode 100644 drivers/net/appletalk/ltpc.h create mode 100644 drivers/staging/appletalk/Kconfig create mode 100644 drivers/staging/appletalk/Makefile create mode 100644 drivers/staging/appletalk/aarp.c create mode 100644 drivers/staging/appletalk/atalk.h create mode 100644 drivers/staging/appletalk/atalk_proc.c create mode 100644 drivers/staging/appletalk/cops.c create mode 100644 drivers/staging/appletalk/cops.h create mode 100644 drivers/staging/appletalk/cops_ffdrv.h create mode 100644 drivers/staging/appletalk/cops_ltdrv.h create mode 100644 drivers/staging/appletalk/ddp.c create mode 100644 drivers/staging/appletalk/dev.c create mode 100644 drivers/staging/appletalk/ipddp.c create mode 100644 drivers/staging/appletalk/ipddp.h create mode 100644 drivers/staging/appletalk/ltpc.c create mode 100644 drivers/staging/appletalk/ltpc.h create mode 100644 drivers/staging/appletalk/sysctl_net_atalk.c delete mode 100644 include/linux/atalk.h delete mode 100644 net/appletalk/Makefile delete mode 100644 net/appletalk/aarp.c delete mode 100644 net/appletalk/atalk_proc.c delete mode 100644 net/appletalk/ddp.c delete mode 100644 net/appletalk/dev.c delete mode 100644 net/appletalk/sysctl_net_atalk.c (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index dd6ca456cde3..3118d67d68fb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -554,8 +554,7 @@ F: drivers/hwmon/applesmc.c APPLETALK NETWORK LAYER M: Arnaldo Carvalho de Melo S: Maintained -F: drivers/net/appletalk/ -F: net/appletalk/ +F: drivers/staging/appletalk/ ARC FRAMEBUFFER DRIVER M: Jaya Kumar diff --git a/drivers/net/Makefile b/drivers/net/Makefile index b90738d13994..11a9c053f0c8 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -265,7 +265,6 @@ obj-$(CONFIG_MACB) += macb.o obj-$(CONFIG_S6GMAC) += s6gmac.o obj-$(CONFIG_ARM) += arm/ -obj-$(CONFIG_DEV_APPLETALK) += appletalk/ obj-$(CONFIG_TR) += tokenring/ obj-$(CONFIG_WAN) += wan/ obj-$(CONFIG_ARCNET) += arcnet/ diff --git a/drivers/net/appletalk/Kconfig b/drivers/net/appletalk/Kconfig deleted file mode 100644 index 0b376a990972..000000000000 --- a/drivers/net/appletalk/Kconfig +++ /dev/null @@ -1,126 +0,0 @@ -# -# Appletalk driver configuration -# -config ATALK - tristate "Appletalk protocol support" - depends on BKL # waiting to be removed from net/appletalk/ddp.c - select LLC - ---help--- - AppleTalk is the protocol that Apple computers can use to communicate - on a network. If your Linux box is connected to such a network and you - wish to connect to it, say Y. You will need to use the netatalk package - so that your Linux box can act as a print and file server for Macs as - well as access AppleTalk printers. Check out - on the WWW for details. - EtherTalk is the name used for AppleTalk over Ethernet and the - cheaper and slower LocalTalk is AppleTalk over a proprietary Apple - network using serial links. EtherTalk and LocalTalk are fully - supported by Linux. - - General information about how to connect Linux, Windows machines and - Macs is on the WWW at . The - NET3-4-HOWTO, available from - , contains valuable - information as well. - - To compile this driver as a module, choose M here: the module will be - called appletalk. You almost certainly want to compile it as a - module so you can restart your AppleTalk stack without rebooting - your machine. I hear that the GNU boycott of Apple is over, so - even politically correct people are allowed to say Y here. - -config DEV_APPLETALK - tristate "Appletalk interfaces support" - depends on ATALK - help - AppleTalk is the protocol that Apple computers can use to communicate - on a network. If your Linux box is connected to such a network, and wish - to do IP over it, or you have a LocalTalk card and wish to use it to - connect to the AppleTalk network, say Y. - - -config LTPC - tristate "Apple/Farallon LocalTalk PC support" - depends on DEV_APPLETALK && (ISA || EISA) && ISA_DMA_API - help - This allows you to use the AppleTalk PC card to connect to LocalTalk - networks. The card is also known as the Farallon PhoneNet PC card. - If you are in doubt, this card is the one with the 65C02 chip on it. - You also need version 1.3.3 or later of the netatalk package. - This driver is experimental, which means that it may not work. - See the file . - -config COPS - tristate "COPS LocalTalk PC support" - depends on DEV_APPLETALK && (ISA || EISA) - help - This allows you to use COPS AppleTalk cards to connect to LocalTalk - networks. You also need version 1.3.3 or later of the netatalk - package. This driver is experimental, which means that it may not - work. This driver will only work if you choose "AppleTalk DDP" - networking support, above. - Please read the file . - -config COPS_DAYNA - bool "Dayna firmware support" - depends on COPS - help - Support COPS compatible cards with Dayna style firmware (Dayna - DL2000/ Daynatalk/PC (half length), COPS LT-95, Farallon PhoneNET PC - III, Farallon PhoneNET PC II). - -config COPS_TANGENT - bool "Tangent firmware support" - depends on COPS - help - Support COPS compatible cards with Tangent style firmware (Tangent - ATB_II, Novell NL-1000, Daystar Digital LT-200. - -config IPDDP - tristate "Appletalk-IP driver support" - depends on DEV_APPLETALK && ATALK - ---help--- - This allows IP networking for users who only have AppleTalk - networking available. This feature is experimental. With this - driver, you can encapsulate IP inside AppleTalk (e.g. if your Linux - box is stuck on an AppleTalk only network) or decapsulate (e.g. if - you want your Linux box to act as an Internet gateway for a zoo of - AppleTalk connected Macs). Please see the file - for more information. - - If you say Y here, the AppleTalk-IP support will be compiled into - the kernel. In this case, you can either use encapsulation or - decapsulation, but not both. With the following two questions, you - decide which one you want. - - To compile the AppleTalk-IP support as a module, choose M here: the - module will be called ipddp. - In this case, you will be able to use both encapsulation and - decapsulation simultaneously, by loading two copies of the module - and specifying different values for the module option ipddp_mode. - -config IPDDP_ENCAP - bool "IP to Appletalk-IP Encapsulation support" - depends on IPDDP - help - If you say Y here, the AppleTalk-IP code will be able to encapsulate - IP packets inside AppleTalk frames; this is useful if your Linux box - is stuck on an AppleTalk network (which hopefully contains a - decapsulator somewhere). Please see - for more information. If - you said Y to "AppleTalk-IP driver support" above and you say Y - here, then you cannot say Y to "AppleTalk-IP to IP Decapsulation - support", below. - -config IPDDP_DECAP - bool "Appletalk-IP to IP Decapsulation support" - depends on IPDDP - help - If you say Y here, the AppleTalk-IP code will be able to decapsulate - AppleTalk-IP frames to IP packets; this is useful if you want your - Linux box to act as an Internet gateway for an AppleTalk network. - Please see for more - information. If you said Y to "AppleTalk-IP driver support" above - and you say Y here, then you cannot say Y to "IP to AppleTalk-IP - Encapsulation support", above. - diff --git a/drivers/net/appletalk/Makefile b/drivers/net/appletalk/Makefile deleted file mode 100644 index 6cfc705f7c5c..000000000000 --- a/drivers/net/appletalk/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for drivers/net/appletalk -# - -obj-$(CONFIG_IPDDP) += ipddp.o -obj-$(CONFIG_COPS) += cops.o -obj-$(CONFIG_LTPC) += ltpc.o diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c deleted file mode 100644 index 748c9f526e71..000000000000 --- a/drivers/net/appletalk/cops.c +++ /dev/null @@ -1,1013 +0,0 @@ -/* cops.c: LocalTalk driver for Linux. - * - * Authors: - * - Jay Schulist - * - * With more than a little help from; - * - Alan Cox - * - * Derived from: - * - skeleton.c: A network driver outline for linux. - * Written 1993-94 by Donald Becker. - * - ltpc.c: A driver for the LocalTalk PC card. - * Written by Bradford W. Johnson. - * - * Copyright 1993 United States Government as represented by the - * Director, National Security Agency. - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - * Changes: - * 19970608 Alan Cox Allowed dual card type support - * Can set board type in insmod - * Hooks for cops_setup routine - * (not yet implemented). - * 19971101 Jay Schulist Fixes for multiple lt* devices. - * 19980607 Steven Hirsch Fixed the badly broken support - * for Tangent type cards. Only - * tested on Daystar LT200. Some - * cleanup of formatting and program - * logic. Added emacs 'local-vars' - * setup for Jay's brace style. - * 20000211 Alan Cox Cleaned up for softnet - */ - -static const char *version = -"cops.c:v0.04 6/7/98 Jay Schulist \n"; -/* - * Sources: - * COPS Localtalk SDK. This provides almost all of the information - * needed. - */ - -/* - * insmod/modprobe configurable stuff. - * - IO Port, choose one your card supports or 0 if you dare. - * - IRQ, also choose one your card supports or nothing and let - * the driver figure it out. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For udelay() */ -#include -#include -#include -#include - -#include -#include -#include - -#include "cops.h" /* Our Stuff */ -#include "cops_ltdrv.h" /* Firmware code for Tangent type cards. */ -#include "cops_ffdrv.h" /* Firmware code for Dayna type cards. */ - -/* - * The name of the card. Is used for messages and in the requests for - * io regions, irqs and dma channels - */ - -static const char *cardname = "cops"; - -#ifdef CONFIG_COPS_DAYNA -static int board_type = DAYNA; /* Module exported */ -#else -static int board_type = TANGENT; -#endif - -static int io = 0x240; /* Default IO for Dayna */ -static int irq = 5; /* Default IRQ */ - -/* - * COPS Autoprobe information. - * Right now if port address is right but IRQ is not 5 this will - * return a 5 no matter what since we will still get a status response. - * Need one more additional check to narrow down after we have gotten - * the ioaddr. But since only other possible IRQs is 3 and 4 so no real - * hurry on this. I *STRONGLY* recommend using IRQ 5 for your card with - * this driver. - * - * This driver has 2 modes and they are: Dayna mode and Tangent mode. - * Each mode corresponds with the type of card. It has been found - * that there are 2 main types of cards and all other cards are - * the same and just have different names or only have minor differences - * such as more IO ports. As this driver is tested it will - * become more clear on exactly what cards are supported. The driver - * defaults to using Dayna mode. To change the drivers mode, simply - * select Dayna or Tangent mode when configuring the kernel. - * - * This driver should support: - * TANGENT driver mode: - * Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200, - * COPS LT-1 - * DAYNA driver mode: - * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, - * Farallon PhoneNET PC III, Farallon PhoneNET PC II - * Other cards possibly supported mode unknown though: - * Dayna DL2000 (Full length), COPS LT/M (Micro-Channel) - * - * Cards NOT supported by this driver but supported by the ltpc.c - * driver written by Bradford W. Johnson - * Farallon PhoneNET PC - * Original Apple LocalTalk PC card - * - * N.B. - * - * The Daystar Digital LT200 boards do not support interrupt-driven - * IO. You must specify 'irq=0xff' as a module parameter to invoke - * polled mode. I also believe that the port probing logic is quite - * dangerous at best and certainly hopeless for a polled card. Best to - * specify both. - Steve H. - * - */ - -/* - * Zero terminated list of IO ports to probe. - */ - -static unsigned int ports[] = { - 0x240, 0x340, 0x200, 0x210, 0x220, 0x230, 0x260, - 0x2A0, 0x300, 0x310, 0x320, 0x330, 0x350, 0x360, - 0 -}; - -/* - * Zero terminated list of IRQ ports to probe. - */ - -static int cops_irqlist[] = { - 5, 4, 3, 0 -}; - -static struct timer_list cops_timer; - -/* use 0 for production, 1 for verification, 2 for debug, 3 for verbose debug */ -#ifndef COPS_DEBUG -#define COPS_DEBUG 1 -#endif -static unsigned int cops_debug = COPS_DEBUG; - -/* The number of low I/O ports used by the card. */ -#define COPS_IO_EXTENT 8 - -/* Information that needs to be kept for each board. */ - -struct cops_local -{ - int board; /* Holds what board type is. */ - int nodeid; /* Set to 1 once have nodeid. */ - unsigned char node_acquire; /* Node ID when acquired. */ - struct atalk_addr node_addr; /* Full node address */ - spinlock_t lock; /* RX/TX lock */ -}; - -/* Index to functions, as function prototypes. */ -static int cops_probe1 (struct net_device *dev, int ioaddr); -static int cops_irq (int ioaddr, int board); - -static int cops_open (struct net_device *dev); -static int cops_jumpstart (struct net_device *dev); -static void cops_reset (struct net_device *dev, int sleep); -static void cops_load (struct net_device *dev); -static int cops_nodeid (struct net_device *dev, int nodeid); - -static irqreturn_t cops_interrupt (int irq, void *dev_id); -static void cops_poll (unsigned long ltdev); -static void cops_timeout(struct net_device *dev); -static void cops_rx (struct net_device *dev); -static netdev_tx_t cops_send_packet (struct sk_buff *skb, - struct net_device *dev); -static void set_multicast_list (struct net_device *dev); -static int cops_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); -static int cops_close (struct net_device *dev); - -static void cleanup_card(struct net_device *dev) -{ - if (dev->irq) - free_irq(dev->irq, dev); - release_region(dev->base_addr, COPS_IO_EXTENT); -} - -/* - * Check for a network adaptor of this type, and return '0' iff one exists. - * If dev->base_addr == 0, probe all likely locations. - * If dev->base_addr in [1..0x1ff], always return failure. - * otherwise go with what we pass in. - */ -struct net_device * __init cops_probe(int unit) -{ - struct net_device *dev; - unsigned *port; - int base_addr; - int err = 0; - - dev = alloc_ltalkdev(sizeof(struct cops_local)); - if (!dev) - return ERR_PTR(-ENOMEM); - - if (unit >= 0) { - sprintf(dev->name, "lt%d", unit); - netdev_boot_setup_check(dev); - irq = dev->irq; - base_addr = dev->base_addr; - } else { - base_addr = dev->base_addr = io; - } - - if (base_addr > 0x1ff) { /* Check a single specified location. */ - err = cops_probe1(dev, base_addr); - } else if (base_addr != 0) { /* Don't probe at all. */ - err = -ENXIO; - } else { - /* FIXME Does this really work for cards which generate irq? - * It's definitely N.G. for polled Tangent. sh - * Dayna cards don't autoprobe well at all, but if your card is - * at IRQ 5 & IO 0x240 we find it every time. ;) JS - */ - for (port = ports; *port && cops_probe1(dev, *port) < 0; port++) - ; - if (!*port) - err = -ENODEV; - } - if (err) - goto out; - err = register_netdev(dev); - if (err) - goto out1; - return dev; -out1: - cleanup_card(dev); -out: - free_netdev(dev); - return ERR_PTR(err); -} - -static const struct net_device_ops cops_netdev_ops = { - .ndo_open = cops_open, - .ndo_stop = cops_close, - .ndo_start_xmit = cops_send_packet, - .ndo_tx_timeout = cops_timeout, - .ndo_do_ioctl = cops_ioctl, - .ndo_set_multicast_list = set_multicast_list, -}; - -/* - * This is the real probe routine. Linux has a history of friendly device - * probes on the ISA bus. A good device probes avoids doing writes, and - * verifies that the correct device exists and functions. - */ -static int __init cops_probe1(struct net_device *dev, int ioaddr) -{ - struct cops_local *lp; - static unsigned version_printed; - int board = board_type; - int retval; - - if(cops_debug && version_printed++ == 0) - printk("%s", version); - - /* Grab the region so no one else tries to probe our ioports. */ - if (!request_region(ioaddr, COPS_IO_EXTENT, dev->name)) - return -EBUSY; - - /* - * Since this board has jumpered interrupts, allocate the interrupt - * vector now. There is no point in waiting since no other device - * can use the interrupt, and this marks the irq as busy. Jumpered - * interrupts are typically not reported by the boards, and we must - * used AutoIRQ to find them. - */ - dev->irq = irq; - switch (dev->irq) - { - case 0: - /* COPS AutoIRQ routine */ - dev->irq = cops_irq(ioaddr, board); - if (dev->irq) - break; - /* No IRQ found on this port, fallthrough */ - case 1: - retval = -EINVAL; - goto err_out; - - /* Fixup for users that don't know that IRQ 2 is really - * IRQ 9, or don't know which one to set. - */ - case 2: - dev->irq = 9; - break; - - /* Polled operation requested. Although irq of zero passed as - * a parameter tells the init routines to probe, we'll - * overload it to denote polled operation at runtime. - */ - case 0xff: - dev->irq = 0; - break; - - default: - break; - } - - /* Reserve any actual interrupt. */ - if (dev->irq) { - retval = request_irq(dev->irq, cops_interrupt, 0, dev->name, dev); - if (retval) - goto err_out; - } - - dev->base_addr = ioaddr; - - lp = netdev_priv(dev); - spin_lock_init(&lp->lock); - - /* Copy local board variable to lp struct. */ - lp->board = board; - - dev->netdev_ops = &cops_netdev_ops; - dev->watchdog_timeo = HZ * 2; - - - /* Tell the user where the card is and what mode we're in. */ - if(board==DAYNA) - printk("%s: %s at %#3x, using IRQ %d, in Dayna mode.\n", - dev->name, cardname, ioaddr, dev->irq); - if(board==TANGENT) { - if(dev->irq) - printk("%s: %s at %#3x, IRQ %d, in Tangent mode\n", - dev->name, cardname, ioaddr, dev->irq); - else - printk("%s: %s at %#3x, using polled IO, in Tangent mode.\n", - dev->name, cardname, ioaddr); - - } - return 0; - -err_out: - release_region(ioaddr, COPS_IO_EXTENT); - return retval; -} - -static int __init cops_irq (int ioaddr, int board) -{ /* - * This does not use the IRQ to determine where the IRQ is. We just - * assume that when we get a correct status response that it's the IRQ. - * This really just verifies the IO port but since we only have access - * to such a small number of IRQs (5, 4, 3) this is not bad. - * This will probably not work for more than one card. - */ - int irqaddr=0; - int i, x, status; - - if(board==DAYNA) - { - outb(0, ioaddr+DAYNA_RESET); - inb(ioaddr+DAYNA_RESET); - mdelay(333); - } - if(board==TANGENT) - { - inb(ioaddr); - outb(0, ioaddr); - outb(0, ioaddr+TANG_RESET); - } - - for(i=0; cops_irqlist[i] !=0; i++) - { - irqaddr = cops_irqlist[i]; - for(x = 0xFFFF; x>0; x --) /* wait for response */ - { - if(board==DAYNA) - { - status = (inb(ioaddr+DAYNA_CARD_STATUS)&3); - if(status == 1) - return irqaddr; - } - if(board==TANGENT) - { - if((inb(ioaddr+TANG_CARD_STATUS)& TANG_TX_READY) !=0) - return irqaddr; - } - } - } - return 0; /* no IRQ found */ -} - -/* - * Open/initialize the board. This is called (in the current kernel) - * sometime after booting when the 'ifconfig' program is run. - */ -static int cops_open(struct net_device *dev) -{ - struct cops_local *lp = netdev_priv(dev); - - if(dev->irq==0) - { - /* - * I don't know if the Dayna-style boards support polled - * operation. For now, only allow it for Tangent. - */ - if(lp->board==TANGENT) /* Poll 20 times per second */ - { - init_timer(&cops_timer); - cops_timer.function = cops_poll; - cops_timer.data = (unsigned long)dev; - cops_timer.expires = jiffies + HZ/20; - add_timer(&cops_timer); - } - else - { - printk(KERN_WARNING "%s: No irq line set\n", dev->name); - return -EAGAIN; - } - } - - cops_jumpstart(dev); /* Start the card up. */ - - netif_start_queue(dev); - return 0; -} - -/* - * This allows for a dynamic start/restart of the entire card. - */ -static int cops_jumpstart(struct net_device *dev) -{ - struct cops_local *lp = netdev_priv(dev); - - /* - * Once the card has the firmware loaded and has acquired - * the nodeid, if it is reset it will lose it all. - */ - cops_reset(dev,1); /* Need to reset card before load firmware. */ - cops_load(dev); /* Load the firmware. */ - - /* - * If atalkd already gave us a nodeid we will use that - * one again, else we wait for atalkd to give us a nodeid - * in cops_ioctl. This may cause a problem if someone steals - * our nodeid while we are resetting. - */ - if(lp->nodeid == 1) - cops_nodeid(dev,lp->node_acquire); - - return 0; -} - -static void tangent_wait_reset(int ioaddr) -{ - int timeout=0; - - while(timeout++ < 5 && (inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) - mdelay(1); /* Wait 1 second */ -} - -/* - * Reset the LocalTalk board. - */ -static void cops_reset(struct net_device *dev, int sleep) -{ - struct cops_local *lp = netdev_priv(dev); - int ioaddr=dev->base_addr; - - if(lp->board==TANGENT) - { - inb(ioaddr); /* Clear request latch. */ - outb(0,ioaddr); /* Clear the TANG_TX_READY flop. */ - outb(0, ioaddr+TANG_RESET); /* Reset the adapter. */ - - tangent_wait_reset(ioaddr); - outb(0, ioaddr+TANG_CLEAR_INT); - } - if(lp->board==DAYNA) - { - outb(0, ioaddr+DAYNA_RESET); /* Assert the reset port */ - inb(ioaddr+DAYNA_RESET); /* Clear the reset */ - if (sleep) - msleep(333); - else - mdelay(333); - } - - netif_wake_queue(dev); -} - -static void cops_load (struct net_device *dev) -{ - struct ifreq ifr; - struct ltfirmware *ltf= (struct ltfirmware *)&ifr.ifr_ifru; - struct cops_local *lp = netdev_priv(dev); - int ioaddr=dev->base_addr; - int length, i = 0; - - strcpy(ifr.ifr_name,"lt0"); - - /* Get card's firmware code and do some checks on it. */ -#ifdef CONFIG_COPS_DAYNA - if(lp->board==DAYNA) - { - ltf->length=sizeof(ffdrv_code); - ltf->data=ffdrv_code; - } - else -#endif -#ifdef CONFIG_COPS_TANGENT - if(lp->board==TANGENT) - { - ltf->length=sizeof(ltdrv_code); - ltf->data=ltdrv_code; - } - else -#endif - { - printk(KERN_INFO "%s; unsupported board type.\n", dev->name); - return; - } - - /* Check to make sure firmware is correct length. */ - if(lp->board==DAYNA && ltf->length!=5983) - { - printk(KERN_WARNING "%s: Firmware is not length of FFDRV.BIN.\n", dev->name); - return; - } - if(lp->board==TANGENT && ltf->length!=2501) - { - printk(KERN_WARNING "%s: Firmware is not length of DRVCODE.BIN.\n", dev->name); - return; - } - - if(lp->board==DAYNA) - { - /* - * We must wait for a status response - * with the DAYNA board. - */ - while(++i<65536) - { - if((inb(ioaddr+DAYNA_CARD_STATUS)&3)==1) - break; - } - - if(i==65536) - return; - } - - /* - * Upload the firmware and kick. Byte-by-byte works nicely here. - */ - i=0; - length = ltf->length; - while(length--) - { - outb(ltf->data[i], ioaddr); - i++; - } - - if(cops_debug > 1) - printk("%s: Uploaded firmware - %d bytes of %d bytes.\n", - dev->name, i, ltf->length); - - if(lp->board==DAYNA) /* Tell Dayna to run the firmware code. */ - outb(1, ioaddr+DAYNA_INT_CARD); - else /* Tell Tang to run the firmware code. */ - inb(ioaddr); - - if(lp->board==TANGENT) - { - tangent_wait_reset(ioaddr); - inb(ioaddr); /* Clear initial ready signal. */ - } -} - -/* - * Get the LocalTalk Nodeid from the card. We can suggest - * any nodeid 1-254. The card will try and get that exact - * address else we can specify 0 as the nodeid and the card - * will autoprobe for a nodeid. - */ -static int cops_nodeid (struct net_device *dev, int nodeid) -{ - struct cops_local *lp = netdev_priv(dev); - int ioaddr = dev->base_addr; - - if(lp->board == DAYNA) - { - /* Empty any pending adapter responses. */ - while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0) - { - outb(0, ioaddr+COPS_CLEAR_INT); /* Clear interrupts. */ - if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST) - cops_rx(dev); /* Kick any packets waiting. */ - schedule(); - } - - outb(2, ioaddr); /* Output command packet length as 2. */ - outb(0, ioaddr); - outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */ - outb(nodeid, ioaddr); /* Suggest node address. */ - } - - if(lp->board == TANGENT) - { - /* Empty any pending adapter responses. */ - while(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY) - { - outb(0, ioaddr+COPS_CLEAR_INT); /* Clear interrupt. */ - cops_rx(dev); /* Kick out packets waiting. */ - schedule(); - } - - /* Not sure what Tangent does if nodeid picked is used. */ - if(nodeid == 0) /* Seed. */ - nodeid = jiffies&0xFF; /* Get a random try */ - outb(2, ioaddr); /* Command length LSB */ - outb(0, ioaddr); /* Command length MSB */ - outb(LAP_INIT, ioaddr); /* Send LAP_INIT byte */ - outb(nodeid, ioaddr); /* LAP address hint. */ - outb(0xFF, ioaddr); /* Int. level to use */ - } - - lp->node_acquire=0; /* Set nodeid holder to 0. */ - while(lp->node_acquire==0) /* Get *True* nodeid finally. */ - { - outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */ - - if(lp->board == DAYNA) - { - if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST) - cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */ - } - if(lp->board == TANGENT) - { - if(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY) - cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */ - } - schedule(); - } - - if(cops_debug > 1) - printk(KERN_DEBUG "%s: Node ID %d has been acquired.\n", - dev->name, lp->node_acquire); - - lp->nodeid=1; /* Set got nodeid to 1. */ - - return 0; -} - -/* - * Poll the Tangent type cards to see if we have work. - */ - -static void cops_poll(unsigned long ltdev) -{ - int ioaddr, status; - int boguscount = 0; - - struct net_device *dev = (struct net_device *)ltdev; - - del_timer(&cops_timer); - - if(dev == NULL) - return; /* We've been downed */ - - ioaddr = dev->base_addr; - do { - status=inb(ioaddr+TANG_CARD_STATUS); - if(status & TANG_RX_READY) - cops_rx(dev); - if(status & TANG_TX_READY) - netif_wake_queue(dev); - status = inb(ioaddr+TANG_CARD_STATUS); - } while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY))); - - /* poll 20 times per second */ - cops_timer.expires = jiffies + HZ/20; - add_timer(&cops_timer); -} - -/* - * The typical workload of the driver: - * Handle the network interface interrupts. - */ -static irqreturn_t cops_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct cops_local *lp; - int ioaddr, status; - int boguscount = 0; - - ioaddr = dev->base_addr; - lp = netdev_priv(dev); - - if(lp->board==DAYNA) - { - do { - outb(0, ioaddr + COPS_CLEAR_INT); - status=inb(ioaddr+DAYNA_CARD_STATUS); - if((status&0x03)==DAYNA_RX_REQUEST) - cops_rx(dev); - netif_wake_queue(dev); - } while(++boguscount < 20); - } - else - { - do { - status=inb(ioaddr+TANG_CARD_STATUS); - if(status & TANG_RX_READY) - cops_rx(dev); - if(status & TANG_TX_READY) - netif_wake_queue(dev); - status=inb(ioaddr+TANG_CARD_STATUS); - } while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY))); - } - - return IRQ_HANDLED; -} - -/* - * We have a good packet(s), get it/them out of the buffers. - */ -static void cops_rx(struct net_device *dev) -{ - int pkt_len = 0; - int rsp_type = 0; - struct sk_buff *skb = NULL; - struct cops_local *lp = netdev_priv(dev); - int ioaddr = dev->base_addr; - int boguscount = 0; - unsigned long flags; - - - spin_lock_irqsave(&lp->lock, flags); - - if(lp->board==DAYNA) - { - outb(0, ioaddr); /* Send out Zero length. */ - outb(0, ioaddr); - outb(DATA_READ, ioaddr); /* Send read command out. */ - - /* Wait for DMA to turn around. */ - while(++boguscount<1000000) - { - barrier(); - if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_READY) - break; - } - - if(boguscount==1000000) - { - printk(KERN_WARNING "%s: DMA timed out.\n",dev->name); - spin_unlock_irqrestore(&lp->lock, flags); - return; - } - } - - /* Get response length. */ - if(lp->board==DAYNA) - pkt_len = inb(ioaddr) & 0xFF; - else - pkt_len = inb(ioaddr) & 0x00FF; - pkt_len |= (inb(ioaddr) << 8); - /* Input IO code. */ - rsp_type=inb(ioaddr); - - /* Malloc up new buffer. */ - skb = dev_alloc_skb(pkt_len); - if(skb == NULL) - { - printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", - dev->name); - dev->stats.rx_dropped++; - while(pkt_len--) /* Discard packet */ - inb(ioaddr); - spin_unlock_irqrestore(&lp->lock, flags); - return; - } - skb->dev = dev; - skb_put(skb, pkt_len); - skb->protocol = htons(ETH_P_LOCALTALK); - - insb(ioaddr, skb->data, pkt_len); /* Eat the Data */ - - if(lp->board==DAYNA) - outb(1, ioaddr+DAYNA_INT_CARD); /* Interrupt the card */ - - spin_unlock_irqrestore(&lp->lock, flags); /* Restore interrupts. */ - - /* Check for bad response length */ - if(pkt_len < 0 || pkt_len > MAX_LLAP_SIZE) - { - printk(KERN_WARNING "%s: Bad packet length of %d bytes.\n", - dev->name, pkt_len); - dev->stats.tx_errors++; - dev_kfree_skb_any(skb); - return; - } - - /* Set nodeid and then get out. */ - if(rsp_type == LAP_INIT_RSP) - { /* Nodeid taken from received packet. */ - lp->node_acquire = skb->data[0]; - dev_kfree_skb_any(skb); - return; - } - - /* One last check to make sure we have a good packet. */ - if(rsp_type != LAP_RESPONSE) - { - printk(KERN_WARNING "%s: Bad packet type %d.\n", dev->name, rsp_type); - dev->stats.tx_errors++; - dev_kfree_skb_any(skb); - return; - } - - skb_reset_mac_header(skb); /* Point to entire packet. */ - skb_pull(skb,3); - skb_reset_transport_header(skb); /* Point to data (Skip header). */ - - /* Update the counters. */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - - /* Send packet to a higher place. */ - netif_rx(skb); -} - -static void cops_timeout(struct net_device *dev) -{ - struct cops_local *lp = netdev_priv(dev); - int ioaddr = dev->base_addr; - - dev->stats.tx_errors++; - if(lp->board==TANGENT) - { - if((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) - printk(KERN_WARNING "%s: No TX complete interrupt.\n", dev->name); - } - printk(KERN_WARNING "%s: Transmit timed out.\n", dev->name); - cops_jumpstart(dev); /* Restart the card. */ - dev->trans_start = jiffies; /* prevent tx timeout */ - netif_wake_queue(dev); -} - - -/* - * Make the card transmit a LocalTalk packet. - */ - -static netdev_tx_t cops_send_packet(struct sk_buff *skb, - struct net_device *dev) -{ - struct cops_local *lp = netdev_priv(dev); - int ioaddr = dev->base_addr; - unsigned long flags; - - /* - * Block a timer-based transmit from overlapping. - */ - - netif_stop_queue(dev); - - spin_lock_irqsave(&lp->lock, flags); - if(lp->board == DAYNA) /* Wait for adapter transmit buffer. */ - while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0) - cpu_relax(); - if(lp->board == TANGENT) /* Wait for adapter transmit buffer. */ - while((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) - cpu_relax(); - - /* Output IO length. */ - outb(skb->len, ioaddr); - if(lp->board == DAYNA) - outb(skb->len >> 8, ioaddr); - else - outb((skb->len >> 8)&0x0FF, ioaddr); - - /* Output IO code. */ - outb(LAP_WRITE, ioaddr); - - if(lp->board == DAYNA) /* Check the transmit buffer again. */ - while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0); - - outsb(ioaddr, skb->data, skb->len); /* Send out the data. */ - - if(lp->board==DAYNA) /* Dayna requires you kick the card */ - outb(1, ioaddr+DAYNA_INT_CARD); - - spin_unlock_irqrestore(&lp->lock, flags); /* Restore interrupts. */ - - /* Done sending packet, update counters and cleanup. */ - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - dev_kfree_skb (skb); - return NETDEV_TX_OK; -} - -/* - * Dummy function to keep the Appletalk layer happy. - */ - -static void set_multicast_list(struct net_device *dev) -{ - if(cops_debug >= 3) - printk("%s: set_multicast_list executed\n", dev->name); -} - -/* - * System ioctls for the COPS LocalTalk card. - */ - -static int cops_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - struct cops_local *lp = netdev_priv(dev); - struct sockaddr_at *sa = (struct sockaddr_at *)&ifr->ifr_addr; - struct atalk_addr *aa = (struct atalk_addr *)&lp->node_addr; - - switch(cmd) - { - case SIOCSIFADDR: - /* Get and set the nodeid and network # atalkd wants. */ - cops_nodeid(dev, sa->sat_addr.s_node); - aa->s_net = sa->sat_addr.s_net; - aa->s_node = lp->node_acquire; - - /* Set broardcast address. */ - dev->broadcast[0] = 0xFF; - - /* Set hardware address. */ - dev->dev_addr[0] = aa->s_node; - dev->addr_len = 1; - return 0; - - case SIOCGIFADDR: - sa->sat_addr.s_net = aa->s_net; - sa->sat_addr.s_node = aa->s_node; - return 0; - - default: - return -EOPNOTSUPP; - } -} - -/* - * The inverse routine to cops_open(). - */ - -static int cops_close(struct net_device *dev) -{ - struct cops_local *lp = netdev_priv(dev); - - /* If we were running polled, yank the timer. - */ - if(lp->board==TANGENT && dev->irq==0) - del_timer(&cops_timer); - - netif_stop_queue(dev); - return 0; -} - - -#ifdef MODULE -static struct net_device *cops_dev; - -MODULE_LICENSE("GPL"); -module_param(io, int, 0); -module_param(irq, int, 0); -module_param(board_type, int, 0); - -static int __init cops_module_init(void) -{ - if (io == 0) - printk(KERN_WARNING "%s: You shouldn't autoprobe with insmod\n", - cardname); - cops_dev = cops_probe(-1); - if (IS_ERR(cops_dev)) - return PTR_ERR(cops_dev); - return 0; -} - -static void __exit cops_module_exit(void) -{ - unregister_netdev(cops_dev); - cleanup_card(cops_dev); - free_netdev(cops_dev); -} -module_init(cops_module_init); -module_exit(cops_module_exit); -#endif /* MODULE */ diff --git a/drivers/net/appletalk/cops.h b/drivers/net/appletalk/cops.h deleted file mode 100644 index fd2750b269c8..000000000000 --- a/drivers/net/appletalk/cops.h +++ /dev/null @@ -1,60 +0,0 @@ -/* cops.h: LocalTalk driver for Linux. - * - * Authors: - * - Jay Schulist - */ - -#ifndef __LINUX_COPSLTALK_H -#define __LINUX_COPSLTALK_H - -#ifdef __KERNEL__ - -/* Max LLAP size we will accept. */ -#define MAX_LLAP_SIZE 603 - -/* Tangent */ -#define TANG_CARD_STATUS 1 -#define TANG_CLEAR_INT 1 -#define TANG_RESET 3 - -#define TANG_TX_READY 1 -#define TANG_RX_READY 2 - -/* Dayna */ -#define DAYNA_CMD_DATA 0 -#define DAYNA_CLEAR_INT 1 -#define DAYNA_CARD_STATUS 2 -#define DAYNA_INT_CARD 3 -#define DAYNA_RESET 4 - -#define DAYNA_RX_READY 0 -#define DAYNA_TX_READY 1 -#define DAYNA_RX_REQUEST 3 - -/* Same on both card types */ -#define COPS_CLEAR_INT 1 - -/* LAP response codes received from the cards. */ -#define LAP_INIT 1 /* Init cmd */ -#define LAP_INIT_RSP 2 /* Init response */ -#define LAP_WRITE 3 /* Write cmd */ -#define DATA_READ 4 /* Data read */ -#define LAP_RESPONSE 4 /* Received ALAP frame response */ -#define LAP_GETSTAT 5 /* Get LAP and HW status */ -#define LAP_RSPSTAT 6 /* Status response */ - -#endif - -/* - * Structure to hold the firmware information. - */ -struct ltfirmware -{ - unsigned int length; - const unsigned char *data; -}; - -#define DAYNA 1 -#define TANGENT 2 - -#endif diff --git a/drivers/net/appletalk/cops_ffdrv.h b/drivers/net/appletalk/cops_ffdrv.h deleted file mode 100644 index b02005087c1b..000000000000 --- a/drivers/net/appletalk/cops_ffdrv.h +++ /dev/null @@ -1,532 +0,0 @@ - -/* - * The firmware this driver downloads into the Localtalk card is a - * separate program and is not GPL'd source code, even though the Linux - * side driver and the routine that loads this data into the card are. - * - * It is taken from the COPS SDK and is under the following license - * - * This material is licensed to you strictly for use in conjunction with - * the use of COPS LocalTalk adapters. - * There is no charge for this SDK. And no waranty express or implied - * about its fitness for any purpose. However, we will cheerefully - * refund every penny you paid for this SDK... - * Regards, - * - * Thomas F. Divine - * Chief Scientist - */ - - -/* cops_ffdrv.h: LocalTalk driver firmware dump for Linux. - * - * Authors: - * - Jay Schulist - */ - - -#ifdef CONFIG_COPS_DAYNA - -static const unsigned char ffdrv_code[] = { - 58,3,0,50,228,149,33,255,255,34,226,149, - 249,17,40,152,33,202,154,183,237,82,77,68, - 11,107,98,19,54,0,237,176,175,50,80,0, - 62,128,237,71,62,32,237,57,51,62,12,237, - 57,50,237,57,54,62,6,237,57,52,62,12, - 237,57,49,33,107,137,34,32,128,33,83,130, - 34,40,128,33,86,130,34,42,128,33,112,130, - 34,36,128,33,211,130,34,38,128,62,0,237, - 57,16,33,63,148,34,34,128,237,94,205,15, - 130,251,205,168,145,24,141,67,111,112,121,114, - 105,103,104,116,32,40,67,41,32,49,57,56, - 56,32,45,32,68,97,121,110,97,32,67,111, - 109,109,117,110,105,99,97,116,105,111,110,115, - 32,32,32,65,108,108,32,114,105,103,104,116, - 115,32,114,101,115,101,114,118,101,100,46,32, - 32,40,68,40,68,7,16,8,34,7,22,6, - 16,5,12,4,8,3,6,140,0,16,39,128, - 0,4,96,10,224,6,0,7,126,2,64,11, - 118,12,6,13,0,14,193,15,0,5,96,3, - 192,1,64,9,8,62,9,211,66,62,192,211, - 66,62,100,61,32,253,6,28,33,205,129,14, - 66,237,163,194,253,129,6,28,33,205,129,14, - 64,237,163,194,9,130,201,62,47,50,71,152, - 62,47,211,68,58,203,129,237,57,20,58,204, - 129,237,57,21,33,77,152,54,132,205,233,129, - 58,228,149,254,209,40,6,56,4,62,0,24, - 2,219,96,33,233,149,119,230,62,33,232,149, - 119,213,33,8,152,17,7,0,25,119,19,25, - 119,209,201,251,237,77,245,197,213,229,221,229, - 205,233,129,62,1,50,106,137,205,158,139,221, - 225,225,209,193,241,251,237,77,245,197,213,219, - 72,237,56,16,230,46,237,57,16,237,56,12, - 58,72,152,183,32,26,6,20,17,128,2,237, - 56,46,187,32,35,237,56,47,186,32,29,219, - 72,230,1,32,3,5,32,232,175,50,72,152, - 229,221,229,62,1,50,106,137,205,158,139,221, - 225,225,24,25,62,1,50,72,152,58,201,129, - 237,57,12,58,202,129,237,57,13,237,56,16, - 246,17,237,57,16,209,193,241,251,237,77,245, - 197,229,213,221,229,237,56,16,230,17,237,57, - 16,237,56,20,58,34,152,246,16,246,8,211, - 68,62,6,61,32,253,58,34,152,246,8,211, - 68,58,203,129,237,57,20,58,204,129,237,57, - 21,237,56,16,246,34,237,57,16,221,225,209, - 225,193,241,251,237,77,33,2,0,57,126,230, - 3,237,100,1,40,2,246,128,230,130,245,62, - 5,211,64,241,211,64,201,229,213,243,237,56, - 16,230,46,237,57,16,237,56,12,251,70,35, - 35,126,254,175,202,77,133,254,129,202,15,133, - 230,128,194,191,132,43,58,44,152,119,33,76, - 152,119,35,62,132,119,120,254,255,40,4,58, - 49,152,119,219,72,43,43,112,17,3,0,237, - 56,52,230,248,237,57,52,219,72,230,1,194, - 141,131,209,225,237,56,52,246,6,237,57,52, - 62,1,55,251,201,62,3,211,66,62,192,211, - 66,62,48,211,66,0,0,219,66,230,1,40, - 4,219,67,24,240,205,203,135,58,75,152,254, - 255,202,128,132,58,49,152,254,161,250,207,131, - 58,34,152,211,68,62,10,211,66,62,128,211, - 66,62,11,211,66,62,6,211,66,24,0,62, - 14,211,66,62,33,211,66,62,1,211,66,62, - 64,211,66,62,3,211,66,62,209,211,66,62, - 100,71,219,66,230,1,32,6,5,32,247,195, - 248,132,219,67,71,58,44,152,184,194,248,132, - 62,100,71,219,66,230,1,32,6,5,32,247, - 195,248,132,219,67,62,100,71,219,66,230,1, - 32,6,5,32,247,195,248,132,219,67,254,133, - 32,7,62,0,50,74,152,24,17,254,173,32, - 7,62,1,50,74,152,24,6,254,141,194,248, - 132,71,209,225,58,49,152,254,132,32,10,62, - 50,205,2,134,205,144,135,24,27,254,140,32, - 15,62,110,205,2,134,62,141,184,32,5,205, - 144,135,24,8,62,10,205,2,134,205,8,134, - 62,1,50,106,137,205,158,139,237,56,52,246, - 6,237,57,52,175,183,251,201,62,20,135,237, - 57,20,175,237,57,21,237,56,16,246,2,237, - 57,16,237,56,20,95,237,56,21,123,254,10, - 48,244,237,56,16,230,17,237,57,16,209,225, - 205,144,135,62,1,50,106,137,205,158,139,237, - 56,52,246,6,237,57,52,175,183,251,201,209, - 225,243,219,72,230,1,40,13,62,10,211,66, - 0,0,219,66,230,192,202,226,132,237,56,52, - 246,6,237,57,52,62,1,55,251,201,205,203, - 135,62,1,50,106,137,205,158,139,237,56,52, - 246,6,237,57,52,183,251,201,209,225,62,1, - 50,106,137,205,158,139,237,56,52,246,6,237, - 57,52,62,2,55,251,201,209,225,243,219,72, - 230,1,202,213,132,62,10,211,66,0,0,219, - 66,230,192,194,213,132,229,62,1,50,106,137, - 42,40,152,205,65,143,225,17,3,0,205,111, - 136,62,6,211,66,58,44,152,211,66,237,56, - 52,246,6,237,57,52,183,251,201,209,197,237, - 56,52,230,248,237,57,52,219,72,230,1,32, - 15,193,225,237,56,52,246,6,237,57,52,62, - 1,55,251,201,14,23,58,37,152,254,0,40, - 14,14,2,254,1,32,5,62,140,119,24,3, - 62,132,119,43,43,197,205,203,135,193,62,1, - 211,66,62,64,211,66,62,3,211,66,62,193, - 211,66,62,100,203,39,71,219,66,230,1,32, - 6,5,32,247,195,229,133,33,238,151,219,67, - 71,58,44,152,184,194,229,133,119,62,100,71, - 219,66,230,1,32,6,5,32,247,195,229,133, - 219,67,35,119,13,32,234,193,225,62,1,50, - 106,137,205,158,139,237,56,52,246,6,237,57, - 52,175,183,251,201,33,234,151,35,35,62,255, - 119,193,225,62,1,50,106,137,205,158,139,237, - 56,52,246,6,237,57,52,175,251,201,243,61, - 32,253,251,201,62,3,211,66,62,192,211,66, - 58,49,152,254,140,32,19,197,229,213,17,181, - 129,33,185,129,1,2,0,237,176,209,225,193, - 24,27,229,213,33,187,129,58,49,152,230,15, - 87,30,2,237,92,25,17,181,129,126,18,19, - 35,126,18,209,225,58,34,152,246,8,211,68, - 58,49,152,254,165,40,14,254,164,40,10,62, - 10,211,66,62,224,211,66,24,25,58,74,152, - 254,0,40,10,62,10,211,66,62,160,211,66, - 24,8,62,10,211,66,62,128,211,66,62,11, - 211,66,62,6,211,66,205,147,143,62,5,211, - 66,62,224,211,66,62,5,211,66,62,96,211, - 66,62,5,61,32,253,62,5,211,66,62,224, - 211,66,62,14,61,32,253,62,5,211,66,62, - 233,211,66,62,128,211,66,58,181,129,61,32, - 253,62,1,211,66,62,192,211,66,1,254,19, - 237,56,46,187,32,6,13,32,247,195,226,134, - 62,192,211,66,0,0,219,66,203,119,40,250, - 219,66,203,87,40,250,243,237,56,16,230,17, - 237,57,16,237,56,20,251,62,5,211,66,62, - 224,211,66,58,182,129,61,32,253,229,33,181, - 129,58,183,129,203,63,119,35,58,184,129,119, - 225,62,10,211,66,62,224,211,66,62,11,211, - 66,62,118,211,66,62,47,211,68,62,5,211, - 66,62,233,211,66,58,181,129,61,32,253,62, - 5,211,66,62,224,211,66,58,182,129,61,32, - 253,62,5,211,66,62,96,211,66,201,229,213, - 58,50,152,230,15,87,30,2,237,92,33,187, - 129,25,17,181,129,126,18,35,19,126,18,209, - 225,58,71,152,246,8,211,68,58,50,152,254, - 165,40,14,254,164,40,10,62,10,211,66,62, - 224,211,66,24,8,62,10,211,66,62,128,211, - 66,62,11,211,66,62,6,211,66,195,248,135, - 62,3,211,66,62,192,211,66,197,229,213,17, - 181,129,33,183,129,1,2,0,237,176,209,225, - 193,62,47,211,68,62,10,211,66,62,224,211, - 66,62,11,211,66,62,118,211,66,62,1,211, - 66,62,0,211,66,205,147,143,195,16,136,62, - 3,211,66,62,192,211,66,197,229,213,17,181, - 129,33,183,129,1,2,0,237,176,209,225,193, - 62,47,211,68,62,10,211,66,62,224,211,66, - 62,11,211,66,62,118,211,66,205,147,143,62, - 5,211,66,62,224,211,66,62,5,211,66,62, - 96,211,66,62,5,61,32,253,62,5,211,66, - 62,224,211,66,62,14,61,32,253,62,5,211, - 66,62,233,211,66,62,128,211,66,58,181,129, - 61,32,253,62,1,211,66,62,192,211,66,1, - 254,19,237,56,46,187,32,6,13,32,247,195, - 88,136,62,192,211,66,0,0,219,66,203,119, - 40,250,219,66,203,87,40,250,62,5,211,66, - 62,224,211,66,58,182,129,61,32,253,62,5, - 211,66,62,96,211,66,201,197,14,67,6,0, - 62,3,211,66,62,192,211,66,62,48,211,66, - 0,0,219,66,230,1,40,4,219,67,24,240, - 62,5,211,66,62,233,211,66,62,128,211,66, - 58,181,129,61,32,253,237,163,29,62,192,211, - 66,219,66,230,4,40,250,237,163,29,32,245, - 219,66,230,4,40,250,62,255,71,219,66,230, - 4,40,3,5,32,247,219,66,230,4,40,250, - 62,5,211,66,62,224,211,66,58,182,129,61, - 32,253,62,5,211,66,62,96,211,66,58,71, - 152,254,1,202,18,137,62,16,211,66,62,56, - 211,66,62,14,211,66,62,33,211,66,62,1, - 211,66,62,248,211,66,237,56,48,246,153,230, - 207,237,57,48,62,3,211,66,62,221,211,66, - 193,201,58,71,152,211,68,62,10,211,66,62, - 128,211,66,62,11,211,66,62,6,211,66,62, - 6,211,66,58,44,152,211,66,62,16,211,66, - 62,56,211,66,62,48,211,66,0,0,62,14, - 211,66,62,33,211,66,62,1,211,66,62,248, - 211,66,237,56,48,246,145,246,8,230,207,237, - 57,48,62,3,211,66,62,221,211,66,193,201, - 44,3,1,0,70,69,1,245,197,213,229,175, - 50,72,152,237,56,16,230,46,237,57,16,237, - 56,12,62,1,211,66,0,0,219,66,95,230, - 160,32,3,195,20,139,123,230,96,194,72,139, - 62,48,211,66,62,1,211,66,62,64,211,66, - 237,91,40,152,205,207,143,25,43,55,237,82, - 218,70,139,34,42,152,98,107,58,44,152,190, - 194,210,138,35,35,62,130,190,194,200,137,62, - 1,50,48,152,62,175,190,202,82,139,62,132, - 190,32,44,50,50,152,62,47,50,71,152,229, - 175,50,106,137,42,40,152,205,65,143,225,54, - 133,43,70,58,44,152,119,43,112,17,3,0, - 62,10,205,2,134,205,111,136,195,158,138,62, - 140,190,32,19,50,50,152,58,233,149,230,4, - 202,222,138,62,1,50,71,152,195,219,137,126, - 254,160,250,185,138,254,166,242,185,138,50,50, - 152,43,126,35,229,213,33,234,149,95,22,0, - 25,126,254,132,40,18,254,140,40,14,58,50, - 152,230,15,87,126,31,21,242,65,138,56,2, - 175,119,58,50,152,230,15,87,58,233,149,230, - 62,31,21,242,85,138,218,98,138,209,225,195, - 20,139,58,50,152,33,100,137,230,15,95,22, - 0,25,126,50,71,152,209,225,58,50,152,254, - 164,250,135,138,58,73,152,254,0,40,4,54, - 173,24,2,54,133,43,70,58,44,152,119,43, - 112,17,3,0,205,70,135,175,50,106,137,205, - 208,139,58,199,129,237,57,12,58,200,129,237, - 57,13,237,56,16,246,17,237,57,16,225,209, - 193,241,251,237,77,62,129,190,194,227,138,54, - 130,43,70,58,44,152,119,43,112,17,3,0, - 205,144,135,195,20,139,35,35,126,254,132,194, - 227,138,175,50,106,137,205,158,139,24,42,58, - 201,154,254,1,40,7,62,1,50,106,137,24, - 237,58,106,137,254,1,202,222,138,62,128,166, - 194,222,138,221,229,221,33,67,152,205,127,142, - 205,109,144,221,225,225,209,193,241,251,237,77, - 58,106,137,254,1,202,44,139,58,50,152,254, - 164,250,44,139,58,73,152,238,1,50,73,152, - 221,229,221,33,51,152,205,127,142,221,225,62, - 1,50,106,137,205,158,139,195,13,139,24,208, - 24,206,24,204,230,64,40,3,195,20,139,195, - 20,139,43,126,33,8,152,119,35,58,44,152, - 119,43,237,91,35,152,205,203,135,205,158,139, - 195,13,139,175,50,78,152,62,3,211,66,62, - 192,211,66,201,197,33,4,0,57,126,35,102, - 111,62,1,50,106,137,219,72,205,141,139,193, - 201,62,1,50,78,152,34,40,152,54,0,35, - 35,54,0,195,163,139,58,78,152,183,200,229, - 33,181,129,58,183,129,119,35,58,184,129,119, - 225,62,47,211,68,62,14,211,66,62,193,211, - 66,62,10,211,66,62,224,211,66,62,11,211, - 66,62,118,211,66,195,3,140,58,78,152,183, - 200,58,71,152,211,68,254,69,40,4,254,70, - 32,17,58,73,152,254,0,40,10,62,10,211, - 66,62,160,211,66,24,8,62,10,211,66,62, - 128,211,66,62,11,211,66,62,6,211,66,62, - 6,211,66,58,44,152,211,66,62,16,211,66, - 62,56,211,66,62,48,211,66,0,0,219,66, - 230,1,40,4,219,67,24,240,62,14,211,66, - 62,33,211,66,42,40,152,205,65,143,62,1, - 211,66,62,248,211,66,237,56,48,246,145,246, - 8,230,207,237,57,48,62,3,211,66,62,221, - 211,66,201,62,16,211,66,62,56,211,66,62, - 48,211,66,0,0,219,66,230,1,40,4,219, - 67,24,240,62,14,211,66,62,33,211,66,62, - 1,211,66,62,248,211,66,237,56,48,246,153, - 230,207,237,57,48,62,3,211,66,62,221,211, - 66,201,229,213,33,234,149,95,22,0,25,126, - 254,132,40,4,254,140,32,2,175,119,123,209, - 225,201,6,8,14,0,31,48,1,12,16,250, - 121,201,33,4,0,57,94,35,86,33,2,0, - 57,126,35,102,111,221,229,34,89,152,237,83, - 91,152,221,33,63,152,205,127,142,58,81,152, - 50,82,152,58,80,152,135,50,80,152,205,162, - 140,254,3,56,16,58,81,152,135,60,230,15, - 50,81,152,175,50,80,152,24,23,58,79,152, - 205,162,140,254,3,48,13,58,81,152,203,63, - 50,81,152,62,255,50,79,152,58,81,152,50, - 82,152,58,79,152,135,50,79,152,62,32,50, - 83,152,50,84,152,237,56,16,230,17,237,57, - 16,219,72,62,192,50,93,152,62,93,50,94, - 152,58,93,152,61,50,93,152,32,9,58,94, - 152,61,50,94,152,40,44,62,170,237,57,20, - 175,237,57,21,237,56,16,246,2,237,57,16, - 219,72,230,1,202,29,141,237,56,20,71,237, - 56,21,120,254,10,48,237,237,56,16,230,17, - 237,57,16,243,62,14,211,66,62,65,211,66, - 251,58,39,152,23,23,60,50,39,152,71,58, - 82,152,160,230,15,40,22,71,14,10,219,66, - 230,16,202,186,141,219,72,230,1,202,186,141, - 13,32,239,16,235,42,89,152,237,91,91,152, - 205,47,131,48,7,61,202,186,141,195,227,141, - 221,225,33,0,0,201,221,33,55,152,205,127, - 142,58,84,152,61,50,84,152,40,19,58,82, - 152,246,1,50,82,152,58,79,152,246,1,50, - 79,152,195,29,141,221,225,33,1,0,201,221, - 33,59,152,205,127,142,58,80,152,246,1,50, - 80,152,58,82,152,135,246,1,50,82,152,58, - 83,152,61,50,83,152,194,29,141,221,225,33, - 2,0,201,221,229,33,0,0,57,17,4,0, - 25,126,50,44,152,230,128,50,85,152,58,85, - 152,183,40,6,221,33,88,2,24,4,221,33, - 150,0,58,44,152,183,40,53,60,40,50,60, - 40,47,61,61,33,86,152,119,35,119,35,54, - 129,175,50,48,152,221,43,221,229,225,124,181, - 40,42,33,86,152,17,3,0,205,189,140,17, - 232,3,27,123,178,32,251,58,48,152,183,40, - 224,58,44,152,71,62,7,128,230,127,71,58, - 85,152,176,50,44,152,24,162,221,225,201,183, - 221,52,0,192,221,52,1,192,221,52,2,192, - 221,52,3,192,55,201,245,62,1,211,100,241, - 201,245,62,1,211,96,241,201,33,2,0,57, - 126,35,102,111,237,56,48,230,175,237,57,48, - 62,48,237,57,49,125,237,57,32,124,237,57, - 33,62,0,237,57,34,62,88,237,57,35,62, - 0,237,57,36,237,57,37,33,128,2,125,237, - 57,38,124,237,57,39,237,56,48,246,97,230, - 207,237,57,48,62,0,237,57,0,62,0,211, - 96,211,100,201,33,2,0,57,126,35,102,111, - 237,56,48,230,175,237,57,48,62,12,237,57, - 49,62,76,237,57,32,62,0,237,57,33,237, - 57,34,125,237,57,35,124,237,57,36,62,0, - 237,57,37,33,128,2,125,237,57,38,124,237, - 57,39,237,56,48,246,97,230,207,237,57,48, - 62,1,211,96,201,33,2,0,57,126,35,102, - 111,229,237,56,48,230,87,237,57,48,125,237, - 57,40,124,237,57,41,62,0,237,57,42,62, - 67,237,57,43,62,0,237,57,44,58,106,137, - 254,1,32,5,33,6,0,24,3,33,128,2, - 125,237,57,46,124,237,57,47,237,56,50,230, - 252,246,2,237,57,50,225,201,33,4,0,57, - 94,35,86,33,2,0,57,126,35,102,111,237, - 56,48,230,87,237,57,48,125,237,57,40,124, - 237,57,41,62,0,237,57,42,62,67,237,57, - 43,62,0,237,57,44,123,237,57,46,122,237, - 57,47,237,56,50,230,244,246,0,237,57,50, - 237,56,48,246,145,230,207,237,57,48,201,213, - 237,56,46,95,237,56,47,87,237,56,46,111, - 237,56,47,103,183,237,82,32,235,33,128,2, - 183,237,82,209,201,213,237,56,38,95,237,56, - 39,87,237,56,38,111,237,56,39,103,183,237, - 82,32,235,33,128,2,183,237,82,209,201,245, - 197,1,52,0,237,120,230,253,237,121,193,241, - 201,245,197,1,52,0,237,120,246,2,237,121, - 193,241,201,33,2,0,57,126,35,102,111,126, - 35,110,103,201,33,0,0,34,102,152,34,96, - 152,34,98,152,33,202,154,34,104,152,237,91, - 104,152,42,226,149,183,237,82,17,0,255,25, - 34,100,152,203,124,40,6,33,0,125,34,100, - 152,42,104,152,35,35,35,229,205,120,139,193, - 201,205,186,149,229,42,40,152,35,35,35,229, - 205,39,144,193,124,230,3,103,221,117,254,221, - 116,255,237,91,42,152,35,35,35,183,237,82, - 32,12,17,5,0,42,42,152,205,171,149,242, - 169,144,42,40,152,229,205,120,139,193,195,198, - 149,237,91,42,152,42,98,152,25,34,98,152, - 19,19,19,42,102,152,25,34,102,152,237,91, - 100,152,33,158,253,25,237,91,102,152,205,171, - 149,242,214,144,33,0,0,34,102,152,62,1, - 50,95,152,205,225,144,195,198,149,58,95,152, - 183,200,237,91,96,152,42,102,152,205,171,149, - 242,5,145,237,91,102,152,33,98,2,25,237, - 91,96,152,205,171,149,250,37,145,237,91,96, - 152,42,102,152,183,237,82,32,7,42,98,152, - 125,180,40,13,237,91,102,152,42,96,152,205, - 171,149,242,58,145,237,91,104,152,42,102,152, - 25,35,35,35,229,205,120,139,193,175,50,95, - 152,201,195,107,139,205,206,149,250,255,243,205, - 225,144,251,58,230,149,183,194,198,149,17,1, - 0,42,98,152,205,171,149,250,198,149,62,1, - 50,230,149,237,91,96,152,42,104,152,25,221, - 117,252,221,116,253,237,91,104,152,42,96,152, - 25,35,35,35,221,117,254,221,116,255,35,35, - 35,229,205,39,144,124,230,3,103,35,35,35, - 221,117,250,221,116,251,235,221,110,252,221,102, - 253,115,35,114,35,54,4,62,1,211,100,211, - 84,195,198,149,33,0,0,34,102,152,34,96, - 152,34,98,152,33,202,154,34,104,152,237,91, - 104,152,42,226,149,183,237,82,17,0,255,25, - 34,100,152,33,109,152,54,0,33,107,152,229, - 205,240,142,193,62,47,50,34,152,62,132,50, - 49,152,205,241,145,205,61,145,58,39,152,60, - 50,39,152,24,241,205,206,149,251,255,33,109, - 152,126,183,202,198,149,110,221,117,251,33,109, - 152,54,0,221,126,251,254,1,40,28,254,3, - 40,101,254,4,202,190,147,254,5,202,147,147, - 254,8,40,87,33,107,152,229,205,240,142,195, - 198,149,58,201,154,183,32,21,33,111,152,126, - 50,229,149,205,52,144,33,110,152,110,38,0, - 229,205,11,142,193,237,91,96,152,42,104,152, - 25,221,117,254,221,116,255,35,35,54,2,17, - 2,0,43,43,115,35,114,58,44,152,35,35, - 119,58,228,149,35,119,62,1,211,100,211,84, - 62,1,50,201,154,24,169,205,153,142,58,231, - 149,183,40,250,175,50,231,149,33,110,152,126, - 254,255,40,91,58,233,149,230,63,183,40,83, - 94,22,0,33,234,149,25,126,183,40,13,33, - 110,152,94,33,234,150,25,126,254,3,32,36, - 205,81,148,125,180,33,110,152,94,22,0,40, - 17,33,234,149,25,54,0,33,107,152,229,205, - 240,142,193,195,198,149,33,234,150,25,54,0, - 33,110,152,94,22,0,33,234,149,25,126,50, - 49,152,254,132,32,37,62,47,50,34,152,42, - 107,152,229,33,110,152,229,205,174,140,193,193, - 125,180,33,110,152,94,22,0,33,234,150,202, - 117,147,25,52,195,120,147,58,49,152,254,140, - 32,7,62,1,50,34,152,24,210,62,32,50, - 106,152,24,19,58,49,152,95,58,106,152,163, - 183,58,106,152,32,11,203,63,50,106,152,58, - 106,152,183,32,231,254,2,40,51,254,4,40, - 38,254,8,40,26,254,16,40,13,254,32,32, - 158,62,165,50,49,152,62,69,24,190,62,164, - 50,49,152,62,70,24,181,62,163,50,49,152, - 175,24,173,62,162,50,49,152,62,1,24,164, - 62,161,50,49,152,62,3,24,155,25,54,0, - 221,126,251,254,8,40,7,58,230,149,183,202, - 32,146,33,107,152,229,205,240,142,193,211,84, - 195,198,149,237,91,96,152,42,104,152,25,221, - 117,254,221,116,255,35,35,54,6,17,2,0, - 43,43,115,35,114,58,228,149,35,35,119,58, - 233,149,35,119,205,146,142,195,32,146,237,91, - 96,152,42,104,152,25,229,205,160,142,193,58, - 231,149,183,40,250,175,50,231,149,243,237,91, - 96,152,42,104,152,25,221,117,254,221,116,255, - 78,35,70,221,113,252,221,112,253,89,80,42, - 98,152,183,237,82,34,98,152,203,124,40,19, - 33,0,0,34,98,152,34,102,152,34,96,152, - 62,1,50,95,152,24,40,221,94,252,221,86, - 253,19,19,19,42,96,152,25,34,96,152,237, - 91,100,152,33,158,253,25,237,91,96,152,205, - 171,149,242,55,148,33,0,0,34,96,152,175, - 50,230,149,251,195,32,146,245,62,1,50,231, - 149,62,16,237,57,0,211,80,241,251,237,77, - 201,205,186,149,229,229,33,0,0,34,37,152, - 33,110,152,126,50,234,151,58,44,152,33,235, - 151,119,221,54,253,0,221,54,254,0,195,230, - 148,33,236,151,54,175,33,3,0,229,33,234, - 151,229,205,174,140,193,193,33,236,151,126,254, - 255,40,74,33,245,151,110,221,117,255,33,249, - 151,126,221,166,255,221,119,255,33,253,151,126, - 221,166,255,221,119,255,58,232,149,95,221,126, - 255,163,221,119,255,183,40,15,230,191,33,110, - 152,94,22,0,33,234,149,25,119,24,12,33, - 110,152,94,22,0,33,234,149,25,54,132,33, - 0,0,195,198,149,221,110,253,221,102,254,35, - 221,117,253,221,116,254,17,32,0,221,110,253, - 221,102,254,205,171,149,250,117,148,58,233,149, - 203,87,40,84,33,1,0,34,37,152,221,54, - 253,0,221,54,254,0,24,53,33,236,151,54, - 175,33,3,0,229,33,234,151,229,205,174,140, - 193,193,33,236,151,126,254,255,40,14,33,110, - 152,94,22,0,33,234,149,25,54,140,24,159, - 221,110,253,221,102,254,35,221,117,253,221,116, - 254,17,32,0,221,110,253,221,102,254,205,171, - 149,250,12,149,33,2,0,34,37,152,221,54, - 253,0,221,54,254,0,24,54,33,236,151,54, - 175,33,3,0,229,33,234,151,229,205,174,140, - 193,193,33,236,151,126,254,255,40,15,33,110, - 152,94,22,0,33,234,149,25,54,132,195,211, - 148,221,110,253,221,102,254,35,221,117,253,221, - 116,254,17,32,0,221,110,253,221,102,254,205, - 171,149,250,96,149,33,1,0,195,198,149,124, - 170,250,179,149,237,82,201,124,230,128,237,82, - 60,201,225,253,229,221,229,221,33,0,0,221, - 57,233,221,249,221,225,253,225,201,233,225,253, - 229,221,229,221,33,0,0,221,57,94,35,86, - 35,235,57,249,235,233,0,0,0,0,0,0, - 62,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 175,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,133,1,0,0,0,63, - 255,255,255,255,0,0,0,63,0,0,0,0, - 0,0,0,0,0,0,0,24,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0 - } ; - -#endif diff --git a/drivers/net/appletalk/cops_ltdrv.h b/drivers/net/appletalk/cops_ltdrv.h deleted file mode 100644 index c699b1ad31da..000000000000 --- a/drivers/net/appletalk/cops_ltdrv.h +++ /dev/null @@ -1,241 +0,0 @@ -/* - * The firmware this driver downloads into the Localtalk card is a - * separate program and is not GPL'd source code, even though the Linux - * side driver and the routine that loads this data into the card are. - * - * It is taken from the COPS SDK and is under the following license - * - * This material is licensed to you strictly for use in conjunction with - * the use of COPS LocalTalk adapters. - * There is no charge for this SDK. And no waranty express or implied - * about its fitness for any purpose. However, we will cheerefully - * refund every penny you paid for this SDK... - * Regards, - * - * Thomas F. Divine - * Chief Scientist - */ - - -/* cops_ltdrv.h: LocalTalk driver firmware dump for Linux. - * - * Authors: - * - Jay Schulist - */ - - -#ifdef CONFIG_COPS_TANGENT - -static const unsigned char ltdrv_code[] = { - 58,3,0,50,148,10,33,143,15,62,85,119, - 190,32,9,62,170,119,190,32,3,35,24,241, - 34,146,10,249,17,150,10,33,143,15,183,237, - 82,77,68,11,107,98,19,54,0,237,176,62, - 16,237,57,51,62,0,237,57,50,237,57,54, - 62,12,237,57,49,62,195,33,39,2,50,56, - 0,34,57,0,237,86,205,30,2,251,205,60, - 10,24,169,67,111,112,121,114,105,103,104,116, - 32,40,99,41,32,49,57,56,56,45,49,57, - 57,50,44,32,80,114,105,110,116,105,110,103, - 32,67,111,109,109,117,110,105,99,97,116,105, - 111,110,115,32,65,115,115,111,99,105,97,116, - 101,115,44,32,73,110,99,46,65,108,108,32, - 114,105,103,104,116,115,32,114,101,115,101,114, - 118,101,100,46,32,32,4,4,22,40,255,60, - 4,96,10,224,6,0,7,126,2,64,11,246, - 12,6,13,0,14,193,15,0,5,96,3,192, - 1,0,9,8,62,3,211,82,62,192,211,82, - 201,62,3,211,82,62,213,211,82,201,62,5, - 211,82,62,224,211,82,201,62,5,211,82,62, - 224,211,82,201,62,5,211,82,62,96,211,82, - 201,6,28,33,180,1,14,82,237,163,194,4, - 2,33,39,2,34,64,0,58,3,0,230,1, - 192,62,11,237,121,62,118,237,121,201,33,182, - 10,54,132,205,253,1,201,245,197,213,229,42, - 150,10,14,83,17,98,2,67,20,237,162,58, - 179,1,95,219,82,230,1,32,6,29,32,247, - 195,17,3,62,1,211,82,219,82,95,230,160, - 32,10,237,162,32,225,21,32,222,195,15,3, - 237,162,123,230,96,194,21,3,62,48,211,82, - 62,1,211,82,175,211,82,237,91,150,10,43, - 55,237,82,218,19,3,34,152,10,98,107,58, - 154,10,190,32,81,62,1,50,158,10,35,35, - 62,132,190,32,44,54,133,43,70,58,154,10, - 119,43,112,17,3,0,205,137,3,62,16,211, - 82,62,56,211,82,205,217,1,42,150,10,14, - 83,17,98,2,67,20,58,178,1,95,195,59, - 2,62,129,190,194,227,2,54,130,43,70,58, - 154,10,119,43,112,17,3,0,205,137,3,195, - 254,2,35,35,126,254,132,194,227,2,205,61, - 3,24,20,62,128,166,194,222,2,221,229,221, - 33,175,10,205,93,6,205,144,7,221,225,225, - 209,193,241,251,237,77,221,229,221,33,159,10, - 205,93,6,221,225,205,61,3,195,247,2,24, - 237,24,235,24,233,230,64,40,2,24,227,24, - 225,175,50,179,10,205,208,1,201,197,33,4, - 0,57,126,35,102,111,205,51,3,193,201,62, - 1,50,179,10,34,150,10,54,0,58,179,10, - 183,200,62,14,211,82,62,193,211,82,62,10, - 211,82,62,224,211,82,62,6,211,82,58,154, - 10,211,82,62,16,211,82,62,56,211,82,62, - 48,211,82,219,82,230,1,40,4,219,83,24, - 242,62,14,211,82,62,33,211,82,62,1,211, - 82,62,9,211,82,62,32,211,82,205,217,1, - 201,14,83,205,208,1,24,23,14,83,205,208, - 1,205,226,1,58,174,1,61,32,253,205,244, - 1,58,174,1,61,32,253,205,226,1,58,175, - 1,61,32,253,62,5,211,82,62,233,211,82, - 62,128,211,82,58,176,1,61,32,253,237,163, - 27,62,192,211,82,219,82,230,4,40,250,237, - 163,27,122,179,32,243,219,82,230,4,40,250, - 58,178,1,71,219,82,230,4,40,3,5,32, - 247,219,82,230,4,40,250,205,235,1,58,177, - 1,61,32,253,205,244,1,201,229,213,35,35, - 126,230,128,194,145,4,43,58,154,10,119,43, - 70,33,181,10,119,43,112,17,3,0,243,62, - 10,211,82,219,82,230,128,202,41,4,209,225, - 62,1,55,251,201,205,144,3,58,180,10,254, - 255,202,127,4,205,217,1,58,178,1,71,219, - 82,230,1,32,6,5,32,247,195,173,4,219, - 83,71,58,154,10,184,194,173,4,58,178,1, - 71,219,82,230,1,32,6,5,32,247,195,173, - 4,219,83,58,178,1,71,219,82,230,1,32, - 6,5,32,247,195,173,4,219,83,254,133,194, - 173,4,58,179,1,24,4,58,179,1,135,61, - 32,253,209,225,205,137,3,205,61,3,183,251, - 201,209,225,243,62,10,211,82,219,82,230,128, - 202,164,4,62,1,55,251,201,205,144,3,205, - 61,3,183,251,201,209,225,62,2,55,251,201, - 243,62,14,211,82,62,33,211,82,251,201,33, - 4,0,57,94,35,86,33,2,0,57,126,35, - 102,111,221,229,34,193,10,237,83,195,10,221, - 33,171,10,205,93,6,58,185,10,50,186,10, - 58,184,10,135,50,184,10,205,112,6,254,3, - 56,16,58,185,10,135,60,230,15,50,185,10, - 175,50,184,10,24,23,58,183,10,205,112,6, - 254,3,48,13,58,185,10,203,63,50,185,10, - 62,255,50,183,10,58,185,10,50,186,10,58, - 183,10,135,50,183,10,62,32,50,187,10,50, - 188,10,6,255,219,82,230,16,32,3,5,32, - 247,205,180,4,6,40,219,82,230,16,40,3, - 5,32,247,62,10,211,82,219,82,230,128,194, - 46,5,219,82,230,16,40,214,237,95,71,58, - 186,10,160,230,15,40,32,71,14,10,62,10, - 211,82,219,82,230,128,202,119,5,205,180,4, - 195,156,5,219,82,230,16,202,156,5,13,32, - 229,16,225,42,193,10,237,91,195,10,205,252, - 3,48,7,61,202,156,5,195,197,5,221,225, - 33,0,0,201,221,33,163,10,205,93,6,58, - 188,10,61,50,188,10,40,19,58,186,10,246, - 1,50,186,10,58,183,10,246,1,50,183,10, - 195,46,5,221,225,33,1,0,201,221,33,167, - 10,205,93,6,58,184,10,246,1,50,184,10, - 58,186,10,135,246,1,50,186,10,58,187,10, - 61,50,187,10,194,46,5,221,225,33,2,0, - 201,221,229,33,0,0,57,17,4,0,25,126, - 50,154,10,230,128,50,189,10,58,189,10,183, - 40,6,221,33,88,2,24,4,221,33,150,0, - 58,154,10,183,40,49,60,40,46,61,33,190, - 10,119,35,119,35,54,129,175,50,158,10,221, - 43,221,229,225,124,181,40,42,33,190,10,17, - 3,0,205,206,4,17,232,3,27,123,178,32, - 251,58,158,10,183,40,224,58,154,10,71,62, - 7,128,230,127,71,58,189,10,176,50,154,10, - 24,166,221,225,201,183,221,52,0,192,221,52, - 1,192,221,52,2,192,221,52,3,192,55,201, - 6,8,14,0,31,48,1,12,16,250,121,201, - 33,2,0,57,94,35,86,35,78,35,70,35, - 126,35,102,105,79,120,68,103,237,176,201,33, - 2,0,57,126,35,102,111,62,17,237,57,48, - 125,237,57,40,124,237,57,41,62,0,237,57, - 42,62,64,237,57,43,62,0,237,57,44,33, - 128,2,125,237,57,46,124,237,57,47,62,145, - 237,57,48,211,68,58,149,10,211,66,201,33, - 2,0,57,126,35,102,111,62,33,237,57,48, - 62,64,237,57,32,62,0,237,57,33,237,57, - 34,125,237,57,35,124,237,57,36,62,0,237, - 57,37,33,128,2,125,237,57,38,124,237,57, - 39,62,97,237,57,48,211,67,58,149,10,211, - 66,201,237,56,46,95,237,56,47,87,237,56, - 46,111,237,56,47,103,183,237,82,32,235,33, - 128,2,183,237,82,201,237,56,38,95,237,56, - 39,87,237,56,38,111,237,56,39,103,183,237, - 82,32,235,33,128,2,183,237,82,201,205,106, - 10,221,110,6,221,102,7,126,35,110,103,195, - 118,10,205,106,10,33,0,0,34,205,10,34, - 198,10,34,200,10,33,143,15,34,207,10,237, - 91,207,10,42,146,10,183,237,82,17,0,255, - 25,34,203,10,203,124,40,6,33,0,125,34, - 203,10,42,207,10,229,205,37,3,195,118,10, - 205,106,10,229,42,150,10,35,35,35,229,205, - 70,7,193,124,230,3,103,221,117,254,221,116, - 255,237,91,152,10,35,35,35,183,237,82,32, - 12,17,5,0,42,152,10,205,91,10,242,203, - 7,42,150,10,229,205,37,3,195,118,10,237, - 91,152,10,42,200,10,25,34,200,10,42,205, - 10,25,34,205,10,237,91,203,10,33,158,253, - 25,237,91,205,10,205,91,10,242,245,7,33, - 0,0,34,205,10,62,1,50,197,10,205,5, - 8,33,0,0,57,249,195,118,10,205,106,10, - 58,197,10,183,202,118,10,237,91,198,10,42, - 205,10,205,91,10,242,46,8,237,91,205,10, - 33,98,2,25,237,91,198,10,205,91,10,250, - 78,8,237,91,198,10,42,205,10,183,237,82, - 32,7,42,200,10,125,180,40,13,237,91,205, - 10,42,198,10,205,91,10,242,97,8,237,91, - 207,10,42,205,10,25,229,205,37,3,175,50, - 197,10,195,118,10,205,29,3,33,0,0,57, - 249,195,118,10,205,106,10,58,202,10,183,40, - 22,205,14,7,237,91,209,10,19,19,19,205, - 91,10,242,139,8,33,1,0,195,118,10,33, - 0,0,195,118,10,205,126,10,252,255,205,108, - 8,125,180,194,118,10,237,91,200,10,33,0, - 0,205,91,10,242,118,10,237,91,207,10,42, - 198,10,25,221,117,254,221,116,255,35,35,35, - 229,205,70,7,193,124,230,3,103,35,35,35, - 221,117,252,221,116,253,229,221,110,254,221,102, - 255,229,33,212,10,229,205,124,6,193,193,221, - 110,252,221,102,253,34,209,10,33,211,10,54, - 4,33,209,10,227,205,147,6,193,62,1,50, - 202,10,243,221,94,252,221,86,253,42,200,10, - 183,237,82,34,200,10,203,124,40,17,33,0, - 0,34,200,10,34,205,10,34,198,10,50,197, - 10,24,37,221,94,252,221,86,253,42,198,10, - 25,34,198,10,237,91,203,10,33,158,253,25, - 237,91,198,10,205,91,10,242,68,9,33,0, - 0,34,198,10,205,5,8,33,0,0,57,249, - 251,195,118,10,205,106,10,33,49,13,126,183, - 40,16,205,42,7,237,91,47,13,19,19,19, - 205,91,10,242,117,9,58,142,15,198,1,50, - 142,15,195,118,10,33,49,13,126,254,1,40, - 25,254,3,202,7,10,254,5,202,21,10,33, - 49,13,54,0,33,47,13,229,205,207,6,195, - 118,10,58,141,15,183,32,72,33,51,13,126, - 50,149,10,205,86,7,33,50,13,126,230,127, - 183,32,40,58,142,15,230,127,50,142,15,183, - 32,5,198,1,50,142,15,33,50,13,126,111, - 23,159,103,203,125,58,142,15,40,5,198,128, - 50,142,15,33,50,13,119,33,50,13,126,111, - 23,159,103,229,205,237,5,193,33,211,10,54, - 2,33,2,0,34,209,10,58,154,10,33,212, - 10,119,58,148,10,33,213,10,119,33,209,10, - 229,205,147,6,193,24,128,42,47,13,229,33, - 50,13,229,205,191,4,193,24,239,33,211,10, - 54,6,33,3,0,34,209,10,58,154,10,33, - 212,10,119,58,148,10,33,213,10,119,33,214, - 10,54,5,33,209,10,229,205,147,6,24,200, - 205,106,10,33,49,13,54,0,33,47,13,229, - 205,207,6,33,209,10,227,205,147,6,193,205, - 80,9,205,145,8,24,248,124,170,250,99,10, - 237,82,201,124,230,128,237,82,60,201,225,253, - 229,221,229,221,33,0,0,221,57,233,221,249, - 221,225,253,225,201,233,225,253,229,221,229,221, - 33,0,0,221,57,94,35,86,35,235,57,249, - 235,233,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0 - } ; - -#endif diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c deleted file mode 100644 index 10d0dba572c2..000000000000 --- a/drivers/net/appletalk/ipddp.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * ipddp.c: IP to Appletalk-IP Encapsulation driver for Linux - * Appletalk-IP to IP Decapsulation driver for Linux - * - * Authors: - * - DDP-IP Encap by: Bradford W. Johnson - * - DDP-IP Decap by: Jay Schulist - * - * Derived from: - * - Almost all code already existed in net/appletalk/ddp.c I just - * moved/reorginized it into a driver file. Original IP-over-DDP code - * was done by Bradford W. Johnson - * - skeleton.c: A network driver outline for linux. - * Written 1993-94 by Donald Becker. - * - dummy.c: A dummy net driver. By Nick Holloway. - * - MacGate: A user space Daemon for Appletalk-IP Decap for - * Linux by Jay Schulist - * - * Copyright 1993 United States Government as represented by the - * Director, National Security Agency. - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ipddp.h" /* Our stuff */ - -static const char version[] = KERN_INFO "ipddp.c:v0.01 8/28/97 Bradford W. Johnson \n"; - -static struct ipddp_route *ipddp_route_list; -static DEFINE_SPINLOCK(ipddp_route_lock); - -#ifdef CONFIG_IPDDP_ENCAP -static int ipddp_mode = IPDDP_ENCAP; -#else -static int ipddp_mode = IPDDP_DECAP; -#endif - -/* Index to functions, as function prototypes. */ -static netdev_tx_t ipddp_xmit(struct sk_buff *skb, - struct net_device *dev); -static int ipddp_create(struct ipddp_route *new_rt); -static int ipddp_delete(struct ipddp_route *rt); -static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt); -static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); - -static const struct net_device_ops ipddp_netdev_ops = { - .ndo_start_xmit = ipddp_xmit, - .ndo_do_ioctl = ipddp_ioctl, - .ndo_change_mtu = eth_change_mtu, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static struct net_device * __init ipddp_init(void) -{ - static unsigned version_printed; - struct net_device *dev; - int err; - - dev = alloc_etherdev(0); - if (!dev) - return ERR_PTR(-ENOMEM); - - dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; - strcpy(dev->name, "ipddp%d"); - - if (version_printed++ == 0) - printk(version); - - /* Initialize the device structure. */ - dev->netdev_ops = &ipddp_netdev_ops; - - dev->type = ARPHRD_IPDDP; /* IP over DDP tunnel */ - dev->mtu = 585; - dev->flags |= IFF_NOARP; - - /* - * The worst case header we will need is currently a - * ethernet header (14 bytes) and a ddp header (sizeof ddpehdr+1) - * We send over SNAP so that takes another 8 bytes. - */ - dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1; - - err = register_netdev(dev); - if (err) { - free_netdev(dev); - return ERR_PTR(err); - } - - /* Let the user now what mode we are in */ - if(ipddp_mode == IPDDP_ENCAP) - printk("%s: Appletalk-IP Encap. mode by Bradford W. Johnson \n", - dev->name); - if(ipddp_mode == IPDDP_DECAP) - printk("%s: Appletalk-IP Decap. mode by Jay Schulist \n", - dev->name); - - return dev; -} - - -/* - * Transmit LLAP/ELAP frame using aarp_send_ddp. - */ -static netdev_tx_t ipddp_xmit(struct sk_buff *skb, struct net_device *dev) -{ - __be32 paddr = skb_rtable(skb)->rt_gateway; - struct ddpehdr *ddp; - struct ipddp_route *rt; - struct atalk_addr *our_addr; - - spin_lock(&ipddp_route_lock); - - /* - * Find appropriate route to use, based only on IP number. - */ - for(rt = ipddp_route_list; rt != NULL; rt = rt->next) - { - if(rt->ip == paddr) - break; - } - if(rt == NULL) { - spin_unlock(&ipddp_route_lock); - return NETDEV_TX_OK; - } - - our_addr = atalk_find_dev_addr(rt->dev); - - if(ipddp_mode == IPDDP_DECAP) - /* - * Pull off the excess room that should not be there. - * This is due to a hard-header problem. This is the - * quick fix for now though, till it breaks. - */ - skb_pull(skb, 35-(sizeof(struct ddpehdr)+1)); - - /* Create the Extended DDP header */ - ddp = (struct ddpehdr *)skb->data; - ddp->deh_len_hops = htons(skb->len + (1<<10)); - ddp->deh_sum = 0; - - /* - * For Localtalk we need aarp_send_ddp to strip the - * long DDP header and place a shot DDP header on it. - */ - if(rt->dev->type == ARPHRD_LOCALTLK) - { - ddp->deh_dnet = 0; /* FIXME more hops?? */ - ddp->deh_snet = 0; - } - else - { - ddp->deh_dnet = rt->at.s_net; /* FIXME more hops?? */ - ddp->deh_snet = our_addr->s_net; - } - ddp->deh_dnode = rt->at.s_node; - ddp->deh_snode = our_addr->s_node; - ddp->deh_dport = 72; - ddp->deh_sport = 72; - - *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */ - - skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */ - - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - aarp_send_ddp(rt->dev, skb, &rt->at, NULL); - - spin_unlock(&ipddp_route_lock); - - return NETDEV_TX_OK; -} - -/* - * Create a routing entry. We first verify that the - * record does not already exist. If it does we return -EEXIST - */ -static int ipddp_create(struct ipddp_route *new_rt) -{ - struct ipddp_route *rt = kmalloc(sizeof(*rt), GFP_KERNEL); - - if (rt == NULL) - return -ENOMEM; - - rt->ip = new_rt->ip; - rt->at = new_rt->at; - rt->next = NULL; - if ((rt->dev = atrtr_get_dev(&rt->at)) == NULL) { - kfree(rt); - return -ENETUNREACH; - } - - spin_lock_bh(&ipddp_route_lock); - if (__ipddp_find_route(rt)) { - spin_unlock_bh(&ipddp_route_lock); - kfree(rt); - return -EEXIST; - } - - rt->next = ipddp_route_list; - ipddp_route_list = rt; - - spin_unlock_bh(&ipddp_route_lock); - - return 0; -} - -/* - * Delete a route, we only delete a FULL match. - * If route does not exist we return -ENOENT. - */ -static int ipddp_delete(struct ipddp_route *rt) -{ - struct ipddp_route **r = &ipddp_route_list; - struct ipddp_route *tmp; - - spin_lock_bh(&ipddp_route_lock); - while((tmp = *r) != NULL) - { - if(tmp->ip == rt->ip && - tmp->at.s_net == rt->at.s_net && - tmp->at.s_node == rt->at.s_node) - { - *r = tmp->next; - spin_unlock_bh(&ipddp_route_lock); - kfree(tmp); - return 0; - } - r = &tmp->next; - } - - spin_unlock_bh(&ipddp_route_lock); - return -ENOENT; -} - -/* - * Find a routing entry, we only return a FULL match - */ -static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt) -{ - struct ipddp_route *f; - - for(f = ipddp_route_list; f != NULL; f = f->next) - { - if(f->ip == rt->ip && - f->at.s_net == rt->at.s_net && - f->at.s_node == rt->at.s_node) - return f; - } - - return NULL; -} - -static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - struct ipddp_route __user *rt = ifr->ifr_data; - struct ipddp_route rcp, rcp2, *rp; - - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - - if(copy_from_user(&rcp, rt, sizeof(rcp))) - return -EFAULT; - - switch(cmd) - { - case SIOCADDIPDDPRT: - return ipddp_create(&rcp); - - case SIOCFINDIPDDPRT: - spin_lock_bh(&ipddp_route_lock); - rp = __ipddp_find_route(&rcp); - if (rp) - memcpy(&rcp2, rp, sizeof(rcp2)); - spin_unlock_bh(&ipddp_route_lock); - - if (rp) { - if (copy_to_user(rt, &rcp2, - sizeof(struct ipddp_route))) - return -EFAULT; - return 0; - } else - return -ENOENT; - - case SIOCDELIPDDPRT: - return ipddp_delete(&rcp); - - default: - return -EINVAL; - } -} - -static struct net_device *dev_ipddp; - -MODULE_LICENSE("GPL"); -module_param(ipddp_mode, int, 0); - -static int __init ipddp_init_module(void) -{ - dev_ipddp = ipddp_init(); - if (IS_ERR(dev_ipddp)) - return PTR_ERR(dev_ipddp); - return 0; -} - -static void __exit ipddp_cleanup_module(void) -{ - struct ipddp_route *p; - - unregister_netdev(dev_ipddp); - free_netdev(dev_ipddp); - - while (ipddp_route_list) { - p = ipddp_route_list->next; - kfree(ipddp_route_list); - ipddp_route_list = p; - } -} - -module_init(ipddp_init_module); -module_exit(ipddp_cleanup_module); diff --git a/drivers/net/appletalk/ipddp.h b/drivers/net/appletalk/ipddp.h deleted file mode 100644 index 531519da99a3..000000000000 --- a/drivers/net/appletalk/ipddp.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * ipddp.h: Header for IP-over-DDP driver for Linux. - */ - -#ifndef __LINUX_IPDDP_H -#define __LINUX_IPDDP_H - -#ifdef __KERNEL__ - -#define SIOCADDIPDDPRT (SIOCDEVPRIVATE) -#define SIOCDELIPDDPRT (SIOCDEVPRIVATE+1) -#define SIOCFINDIPDDPRT (SIOCDEVPRIVATE+2) - -struct ipddp_route -{ - struct net_device *dev; /* Carrier device */ - __be32 ip; /* IP address */ - struct atalk_addr at; /* Gateway appletalk address */ - int flags; - struct ipddp_route *next; -}; - -#define IPDDP_ENCAP 1 -#define IPDDP_DECAP 2 - -#endif /* __KERNEL__ */ -#endif /* __LINUX_IPDDP_H */ diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c deleted file mode 100644 index e69eead12ec7..000000000000 --- a/drivers/net/appletalk/ltpc.c +++ /dev/null @@ -1,1288 +0,0 @@ -/*** ltpc.c -- a driver for the LocalTalk PC card. - * - * Copyright (c) 1995,1996 Bradford W. Johnson - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - * This is ALPHA code at best. It may not work for you. It may - * damage your equipment. It may damage your relations with other - * users of your network. Use it at your own risk! - * - * Based in part on: - * skeleton.c by Donald Becker - * dummy.c by Nick Holloway and Alan Cox - * loopback.c by Ross Biro, Fred van Kampen, Donald Becker - * the netatalk source code (UMICH) - * lots of work on the card... - * - * I do not have access to the (proprietary) SDK that goes with the card. - * If you do, I don't want to know about it, and you can probably write - * a better driver yourself anyway. This does mean that the pieces that - * talk to the card are guesswork on my part, so use at your own risk! - * - * This is my first try at writing Linux networking code, and is also - * guesswork. Again, use at your own risk! (Although on this part, I'd - * welcome suggestions) - * - * This is a loadable kernel module which seems to work at my site - * consisting of a 1.2.13 linux box running netatalk 1.3.3, and with - * the kernel support from 1.3.3b2 including patches routing.patch - * and ddp.disappears.from.chooser. In order to run it, you will need - * to patch ddp.c and aarp.c in the kernel, but only a little... - * - * I'm fairly confident that while this is arguably badly written, the - * problems that people experience will be "higher level", that is, with - * complications in the netatalk code. The driver itself doesn't do - * anything terribly complicated -- it pretends to be an ether device - * as far as netatalk is concerned, strips the DDP data out of the ether - * frame and builds a LLAP packet to send out the card. In the other - * direction, it receives LLAP frames from the card and builds a fake - * ether packet that it then tosses up to the networking code. You can - * argue (correctly) that this is an ugly way to do things, but it - * requires a minimal amount of fooling with the code in ddp.c and aarp.c. - * - * The card will do a lot more than is used here -- I *think* it has the - * layers up through ATP. Even if you knew how that part works (which I - * don't) it would be a big job to carve up the kernel ddp code to insert - * things at a higher level, and probably a bad idea... - * - * There are a number of other cards that do LocalTalk on the PC. If - * nobody finds any insurmountable (at the netatalk level) problems - * here, this driver should encourage people to put some work into the - * other cards (some of which I gather are still commercially available) - * and also to put hooks for LocalTalk into the official ddp code. - * - * I welcome comments and suggestions. This is my first try at Linux - * networking stuff, and there are probably lots of things that I did - * suboptimally. - * - ***/ - -/*** - * - * $Log: ltpc.c,v $ - * Revision 1.1.2.1 2000/03/01 05:35:07 jgarzik - * at and tr cleanup - * - * Revision 1.8 1997/01/28 05:44:54 bradford - * Clean up for non-module a little. - * Hacked about a bit to clean things up - Alan Cox - * Probably broken it from the origina 1.8 - * - - * 1998/11/09: David Huggins-Daines - * Cleaned up the initialization code to use the standard autoirq methods, - and to probe for things in the standard order of i/o, irq, dma. This - removes the "reset the reset" hack, because I couldn't figure out an - easy way to get the card to trigger an interrupt after it. - * Added support for passing configuration parameters on the kernel command - line and through insmod - * Changed the device name from "ltalk0" to "lt0", both to conform with the - other localtalk driver, and to clear up the inconsistency between the - module and the non-module versions of the driver :-) - * Added a bunch of comments (I was going to make some enums for the state - codes and the register offsets, but I'm still not sure exactly what their - semantics are) - * Don't poll anymore in interrupt-driven mode - * It seems to work as a module now (as of 2.1.127), but I don't think - I'm responsible for that... - - * - * Revision 1.7 1996/12/12 03:42:33 bradford - * DMA alloc cribbed from 3c505.c. - * - * Revision 1.6 1996/12/12 03:18:58 bradford - * Added virt_to_bus; works in 2.1.13. - * - * Revision 1.5 1996/12/12 03:13:22 root - * xmitQel initialization -- think through better though. - * - * Revision 1.4 1996/06/18 14:55:55 root - * Change names to ltpc. Tabs. Took a shot at dma alloc, - * although more needs to be done eventually. - * - * Revision 1.3 1996/05/22 14:59:39 root - * Change dev->open, dev->close to track dummy.c in 1.99.(around 7) - * - * Revision 1.2 1996/05/22 14:58:24 root - * Change tabs mostly. - * - * Revision 1.1 1996/04/23 04:45:09 root - * Initial revision - * - * Revision 0.16 1996/03/05 15:59:56 root - * Change ARPHRD_LOCALTLK definition to the "real" one. - * - * Revision 0.15 1996/03/05 06:28:30 root - * Changes for kernel 1.3.70. Still need a few patches to kernel, but - * it's getting closer. - * - * Revision 0.14 1996/02/25 17:38:32 root - * More cleanups. Removed query to card on get_stats. - * - * Revision 0.13 1996/02/21 16:27:40 root - * Refix debug_print_skb. Fix mac.raw gotcha that appeared in 1.3.65. - * Clean up receive code a little. - * - * Revision 0.12 1996/02/19 16:34:53 root - * Fix debug_print_skb. Kludge outgoing snet to 0 when using startup - * range. Change debug to mask: 1 for verbose, 2 for higher level stuff - * including packet printing, 4 for lower level (card i/o) stuff. - * - * Revision 0.11 1996/02/12 15:53:38 root - * Added router sends (requires new aarp.c patch) - * - * Revision 0.10 1996/02/11 00:19:35 root - * Change source LTALK_LOGGING debug switch to insmod ... debug=2. - * - * Revision 0.9 1996/02/10 23:59:35 root - * Fixed those fixes for 1.2 -- DANGER! The at.h that comes with netatalk - * has a *different* definition of struct sockaddr_at than the Linux kernel - * does. This is an "insidious and invidious" bug... - * (Actually the preceding comment is false -- it's the atalk.h in the - * ancient atalk-0.06 that's the problem) - * - * Revision 0.8 1996/02/10 19:09:00 root - * Merge 1.3 changes. Tested OK under 1.3.60. - * - * Revision 0.7 1996/02/10 17:56:56 root - * Added debug=1 parameter on insmod for debugging prints. Tried - * to fix timer unload on rmmod, but I don't think that's the problem. - * - * Revision 0.6 1995/12/31 19:01:09 root - * Clean up rmmod, irq comments per feedback from Corin Anderson (Thanks Corey!) - * Clean up initial probing -- sometimes the card wakes up latched in reset. - * - * Revision 0.5 1995/12/22 06:03:44 root - * Added comments in front and cleaned up a bit. - * This version sent out to people. - * - * Revision 0.4 1995/12/18 03:46:44 root - * Return shortDDP to longDDP fake to 0/0. Added command structs. - * - ***/ - -/* ltpc jumpers are: -* -* Interrupts -- set at most one. If none are set, the driver uses -* polled mode. Because the card was developed in the XT era, the -* original documentation refers to IRQ2. Since you'll be running -* this on an AT (or later) class machine, that really means IRQ9. -* -* SW1 IRQ 4 -* SW2 IRQ 3 -* SW3 IRQ 9 (2 in original card documentation only applies to XT) -* -* -* DMA -- choose DMA 1 or 3, and set both corresponding switches. -* -* SW4 DMA 3 -* SW5 DMA 1 -* SW6 DMA 3 -* SW7 DMA 1 -* -* -* I/O address -- choose one. -* -* SW8 220 / 240 -*/ - -/* To have some stuff logged, do -* insmod ltpc.o debug=1 -* -* For a whole bunch of stuff, use higher numbers. -* -* The default is 0, i.e. no messages except for the probe results. -*/ - -/* insmod-tweakable variables */ -static int debug; -#define DEBUG_VERBOSE 1 -#define DEBUG_UPPER 2 -#define DEBUG_LOWER 4 - -static int io; -static int irq; -static int dma; - -#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 - -/* our stuff */ -#include "ltpc.h" - -static DEFINE_SPINLOCK(txqueue_lock); -static DEFINE_SPINLOCK(mbox_lock); - -/* function prototypes */ -static int do_read(struct net_device *dev, void *cbuf, int cbuflen, - void *dbuf, int dbuflen); -static int sendup_buffer (struct net_device *dev); - -/* Dma Memory related stuff, cribbed directly from 3c505.c */ - -static unsigned long dma_mem_alloc(int size) -{ - int order = get_order(size); - - return __get_dma_pages(GFP_KERNEL, order); -} - -/* DMA data buffer, DMA command buffer */ -static unsigned char *ltdmabuf; -static unsigned char *ltdmacbuf; - -/* private struct, holds our appletalk address */ - -struct ltpc_private -{ - struct atalk_addr my_addr; -}; - -/* transmit queue element struct */ - -struct xmitQel { - struct xmitQel *next; - /* command buffer */ - unsigned char *cbuf; - short cbuflen; - /* data buffer */ - unsigned char *dbuf; - short dbuflen; - unsigned char QWrite; /* read or write data */ - unsigned char mailbox; -}; - -/* the transmit queue itself */ - -static struct xmitQel *xmQhd, *xmQtl; - -static void enQ(struct xmitQel *qel) -{ - unsigned long flags; - qel->next = NULL; - - spin_lock_irqsave(&txqueue_lock, flags); - if (xmQtl) { - xmQtl->next = qel; - } else { - xmQhd = qel; - } - xmQtl = qel; - spin_unlock_irqrestore(&txqueue_lock, flags); - - if (debug & DEBUG_LOWER) - printk("enqueued a 0x%02x command\n",qel->cbuf[0]); -} - -static struct xmitQel *deQ(void) -{ - unsigned long flags; - int i; - struct xmitQel *qel=NULL; - - spin_lock_irqsave(&txqueue_lock, flags); - if (xmQhd) { - qel = xmQhd; - xmQhd = qel->next; - if(!xmQhd) xmQtl = NULL; - } - spin_unlock_irqrestore(&txqueue_lock, flags); - - if ((debug & DEBUG_LOWER) && qel) { - int n; - printk(KERN_DEBUG "ltpc: dequeued command "); - n = qel->cbuflen; - if (n>100) n=100; - for(i=0;icbuf[i]); - printk("\n"); - } - - return qel; -} - -/* and... the queue elements we'll be using */ -static struct xmitQel qels[16]; - -/* and their corresponding mailboxes */ -static unsigned char mailbox[16]; -static unsigned char mboxinuse[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - -static int wait_timeout(struct net_device *dev, int c) -{ - /* returns true if it stayed c */ - /* this uses base+6, but it's ok */ - int i; - - /* twenty second or so total */ - - for(i=0;i<200000;i++) { - if ( c != inb_p(dev->base_addr+6) ) return 0; - udelay(100); - } - return 1; /* timed out */ -} - -/* get the first free mailbox */ - -static int getmbox(void) -{ - unsigned long flags; - int i; - - spin_lock_irqsave(&mbox_lock, flags); - for(i=1;i<16;i++) if(!mboxinuse[i]) { - mboxinuse[i]=1; - spin_unlock_irqrestore(&mbox_lock, flags); - return i; - } - spin_unlock_irqrestore(&mbox_lock, flags); - return 0; -} - -/* read a command from the card */ -static void handlefc(struct net_device *dev) -{ - /* called *only* from idle, non-reentrant */ - int dma = dev->dma; - int base = dev->base_addr; - unsigned long flags; - - - flags=claim_dma_lock(); - disable_dma(dma); - clear_dma_ff(dma); - set_dma_mode(dma,DMA_MODE_READ); - set_dma_addr(dma,virt_to_bus(ltdmacbuf)); - set_dma_count(dma,50); - enable_dma(dma); - release_dma_lock(flags); - - inb_p(base+3); - inb_p(base+2); - - if ( wait_timeout(dev,0xfc) ) printk("timed out in handlefc\n"); -} - -/* read data from the card */ -static void handlefd(struct net_device *dev) -{ - int dma = dev->dma; - int base = dev->base_addr; - unsigned long flags; - - flags=claim_dma_lock(); - disable_dma(dma); - clear_dma_ff(dma); - set_dma_mode(dma,DMA_MODE_READ); - set_dma_addr(dma,virt_to_bus(ltdmabuf)); - set_dma_count(dma,800); - enable_dma(dma); - release_dma_lock(flags); - - inb_p(base+3); - inb_p(base+2); - - if ( wait_timeout(dev,0xfd) ) printk("timed out in handlefd\n"); - sendup_buffer(dev); -} - -static void handlewrite(struct net_device *dev) -{ - /* called *only* from idle, non-reentrant */ - /* on entry, 0xfb and ltdmabuf holds data */ - int dma = dev->dma; - int base = dev->base_addr; - unsigned long flags; - - flags=claim_dma_lock(); - disable_dma(dma); - clear_dma_ff(dma); - set_dma_mode(dma,DMA_MODE_WRITE); - set_dma_addr(dma,virt_to_bus(ltdmabuf)); - set_dma_count(dma,800); - enable_dma(dma); - release_dma_lock(flags); - - inb_p(base+3); - inb_p(base+2); - - if ( wait_timeout(dev,0xfb) ) { - flags=claim_dma_lock(); - printk("timed out in handlewrite, dma res %d\n", - get_dma_residue(dev->dma) ); - release_dma_lock(flags); - } -} - -static void handleread(struct net_device *dev) -{ - /* on entry, 0xfb */ - /* on exit, ltdmabuf holds data */ - int dma = dev->dma; - int base = dev->base_addr; - unsigned long flags; - - - flags=claim_dma_lock(); - disable_dma(dma); - clear_dma_ff(dma); - set_dma_mode(dma,DMA_MODE_READ); - set_dma_addr(dma,virt_to_bus(ltdmabuf)); - set_dma_count(dma,800); - enable_dma(dma); - release_dma_lock(flags); - - inb_p(base+3); - inb_p(base+2); - if ( wait_timeout(dev,0xfb) ) printk("timed out in handleread\n"); -} - -static void handlecommand(struct net_device *dev) -{ - /* on entry, 0xfa and ltdmacbuf holds command */ - int dma = dev->dma; - int base = dev->base_addr; - unsigned long flags; - - flags=claim_dma_lock(); - disable_dma(dma); - clear_dma_ff(dma); - set_dma_mode(dma,DMA_MODE_WRITE); - set_dma_addr(dma,virt_to_bus(ltdmacbuf)); - set_dma_count(dma,50); - enable_dma(dma); - release_dma_lock(flags); - inb_p(base+3); - inb_p(base+2); - if ( wait_timeout(dev,0xfa) ) printk("timed out in handlecommand\n"); -} - -/* ready made command for getting the result from the card */ -static unsigned char rescbuf[2] = {LT_GETRESULT,0}; -static unsigned char resdbuf[2]; - -static int QInIdle; - -/* idle expects to be called with the IRQ line high -- either because of - * an interrupt, or because the line is tri-stated - */ - -static void idle(struct net_device *dev) -{ - unsigned long flags; - int state; - /* FIXME This is initialized to shut the warning up, but I need to - * think this through again. - */ - struct xmitQel *q = NULL; - int oops; - int i; - int base = dev->base_addr; - - spin_lock_irqsave(&txqueue_lock, flags); - if(QInIdle) { - spin_unlock_irqrestore(&txqueue_lock, flags); - return; - } - QInIdle = 1; - spin_unlock_irqrestore(&txqueue_lock, flags); - - /* this tri-states the IRQ line */ - (void) inb_p(base+6); - - oops = 100; - -loop: - if (0>oops--) { - printk("idle: looped too many times\n"); - goto done; - } - - state = inb_p(base+6); - if (state != inb_p(base+6)) goto loop; - - switch(state) { - case 0xfc: - /* incoming command */ - if (debug & DEBUG_LOWER) printk("idle: fc\n"); - handlefc(dev); - break; - case 0xfd: - /* incoming data */ - if(debug & DEBUG_LOWER) printk("idle: fd\n"); - handlefd(dev); - break; - case 0xf9: - /* result ready */ - if (debug & DEBUG_LOWER) printk("idle: f9\n"); - if(!mboxinuse[0]) { - mboxinuse[0] = 1; - qels[0].cbuf = rescbuf; - qels[0].cbuflen = 2; - qels[0].dbuf = resdbuf; - qels[0].dbuflen = 2; - qels[0].QWrite = 0; - qels[0].mailbox = 0; - enQ(&qels[0]); - } - inb_p(dev->base_addr+1); - inb_p(dev->base_addr+0); - if( wait_timeout(dev,0xf9) ) - printk("timed out idle f9\n"); - break; - case 0xf8: - /* ?? */ - if (xmQhd) { - inb_p(dev->base_addr+1); - inb_p(dev->base_addr+0); - if(wait_timeout(dev,0xf8) ) - printk("timed out idle f8\n"); - } else { - goto done; - } - break; - case 0xfa: - /* waiting for command */ - if(debug & DEBUG_LOWER) printk("idle: fa\n"); - if (xmQhd) { - q=deQ(); - memcpy(ltdmacbuf,q->cbuf,q->cbuflen); - ltdmacbuf[1] = q->mailbox; - if (debug>1) { - int n; - printk("ltpc: sent command "); - n = q->cbuflen; - if (n>100) n=100; - for(i=0;iQWrite) { - memcpy(ltdmabuf,q->dbuf,q->dbuflen); - handlewrite(dev); - } else { - handleread(dev); - /* non-zero mailbox numbers are for - commmands, 0 is for GETRESULT - requests */ - if(q->mailbox) { - memcpy(q->dbuf,ltdmabuf,q->dbuflen); - } else { - /* this was a result */ - mailbox[ 0x0f & ltdmabuf[0] ] = ltdmabuf[1]; - mboxinuse[0]=0; - } - } - break; - } - goto loop; - -done: - QInIdle=0; - - /* now set the interrupts back as appropriate */ - /* the first read takes it out of tri-state (but still high) */ - /* the second resets it */ - /* note that after this point, any read of base+6 will - trigger an interrupt */ - - if (dev->irq) { - inb_p(base+7); - inb_p(base+7); - } -} - - -static int do_write(struct net_device *dev, void *cbuf, int cbuflen, - void *dbuf, int dbuflen) -{ - - int i = getmbox(); - int ret; - - if(i) { - qels[i].cbuf = (unsigned char *) cbuf; - qels[i].cbuflen = cbuflen; - qels[i].dbuf = (unsigned char *) dbuf; - qels[i].dbuflen = dbuflen; - qels[i].QWrite = 1; - qels[i].mailbox = i; /* this should be initted rather */ - enQ(&qels[i]); - idle(dev); - ret = mailbox[i]; - mboxinuse[i]=0; - return ret; - } - printk("ltpc: could not allocate mbox\n"); - return -1; -} - -static int do_read(struct net_device *dev, void *cbuf, int cbuflen, - void *dbuf, int dbuflen) -{ - - int i = getmbox(); - int ret; - - if(i) { - qels[i].cbuf = (unsigned char *) cbuf; - qels[i].cbuflen = cbuflen; - qels[i].dbuf = (unsigned char *) dbuf; - qels[i].dbuflen = dbuflen; - qels[i].QWrite = 0; - qels[i].mailbox = i; /* this should be initted rather */ - enQ(&qels[i]); - idle(dev); - ret = mailbox[i]; - mboxinuse[i]=0; - return ret; - } - printk("ltpc: could not allocate mbox\n"); - return -1; -} - -/* end of idle handlers -- what should be seen is do_read, do_write */ - -static struct timer_list ltpc_timer; - -static netdev_tx_t ltpc_xmit(struct sk_buff *skb, struct net_device *dev); - -static int read_30 ( struct net_device *dev) -{ - lt_command c; - c.getflags.command = LT_GETFLAGS; - return do_read(dev, &c, sizeof(c.getflags),&c,0); -} - -static int set_30 (struct net_device *dev,int x) -{ - lt_command c; - c.setflags.command = LT_SETFLAGS; - c.setflags.flags = x; - return do_write(dev, &c, sizeof(c.setflags),&c,0); -} - -/* LLAP to DDP translation */ - -static int sendup_buffer (struct net_device *dev) -{ - /* on entry, command is in ltdmacbuf, data in ltdmabuf */ - /* called from idle, non-reentrant */ - - int dnode, snode, llaptype, len; - int sklen; - struct sk_buff *skb; - struct lt_rcvlap *ltc = (struct lt_rcvlap *) ltdmacbuf; - - if (ltc->command != LT_RCVLAP) { - printk("unknown command 0x%02x from ltpc card\n",ltc->command); - return -1; - } - dnode = ltc->dnode; - snode = ltc->snode; - llaptype = ltc->laptype; - len = ltc->length; - - sklen = len; - if (llaptype == 1) - sklen += 8; /* correct for short ddp */ - if(sklen > 800) { - printk(KERN_INFO "%s: nonsense length in ltpc command 0x14: 0x%08x\n", - dev->name,sklen); - return -1; - } - - if ( (llaptype==0) || (llaptype>2) ) { - printk(KERN_INFO "%s: unknown LLAP type: %d\n",dev->name,llaptype); - return -1; - } - - - skb = dev_alloc_skb(3+sklen); - if (skb == NULL) - { - printk("%s: dropping packet due to memory squeeze.\n", - dev->name); - return -1; - } - skb->dev = dev; - - if (sklen > len) - skb_reserve(skb,8); - skb_put(skb,len+3); - skb->protocol = htons(ETH_P_LOCALTALK); - /* add LLAP header */ - skb->data[0] = dnode; - skb->data[1] = snode; - skb->data[2] = llaptype; - skb_reset_mac_header(skb); /* save pointer to llap header */ - skb_pull(skb,3); - - /* copy ddp(s,e)hdr + contents */ - skb_copy_to_linear_data(skb, ltdmabuf, len); - - skb_reset_transport_header(skb); - - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - - /* toss it onwards */ - netif_rx(skb); - return 0; -} - -/* the handler for the board interrupt */ - -static irqreturn_t -ltpc_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - - if (dev==NULL) { - printk("ltpc_interrupt: unknown device.\n"); - return IRQ_NONE; - } - - inb_p(dev->base_addr+6); /* disable further interrupts from board */ - - idle(dev); /* handle whatever is coming in */ - - /* idle re-enables interrupts from board */ - - return IRQ_HANDLED; -} - -/*** - * - * The ioctls that the driver responds to are: - * - * SIOCSIFADDR -- do probe using the passed node hint. - * SIOCGIFADDR -- return net, node. - * - * some of this stuff should be done elsewhere. - * - ***/ - -static int ltpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - struct sockaddr_at *sa = (struct sockaddr_at *) &ifr->ifr_addr; - /* we'll keep the localtalk node address in dev->pa_addr */ - struct ltpc_private *ltpc_priv = netdev_priv(dev); - struct atalk_addr *aa = <pc_priv->my_addr; - struct lt_init c; - int ltflags; - - if(debug & DEBUG_VERBOSE) printk("ltpc_ioctl called\n"); - - switch(cmd) { - case SIOCSIFADDR: - - aa->s_net = sa->sat_addr.s_net; - - /* this does the probe and returns the node addr */ - c.command = LT_INIT; - c.hint = sa->sat_addr.s_node; - - aa->s_node = do_read(dev,&c,sizeof(c),&c,0); - - /* get all llap frames raw */ - ltflags = read_30(dev); - ltflags |= LT_FLAG_ALLLAP; - set_30 (dev,ltflags); - - dev->broadcast[0] = 0xFF; - dev->dev_addr[0] = aa->s_node; - - dev->addr_len=1; - - return 0; - - case SIOCGIFADDR: - - sa->sat_addr.s_net = aa->s_net; - sa->sat_addr.s_node = aa->s_node; - - return 0; - - default: - return -EINVAL; - } -} - -static void set_multicast_list(struct net_device *dev) -{ - /* This needs to be present to keep netatalk happy. */ - /* Actually netatalk needs fixing! */ -} - -static int ltpc_poll_counter; - -static void ltpc_poll(unsigned long l) -{ - struct net_device *dev = (struct net_device *) l; - - del_timer(<pc_timer); - - if(debug & DEBUG_VERBOSE) { - if (!ltpc_poll_counter) { - ltpc_poll_counter = 50; - printk("ltpc poll is alive\n"); - } - ltpc_poll_counter--; - } - - if (!dev) - return; /* we've been downed */ - - /* poll 20 times per second */ - idle(dev); - ltpc_timer.expires = jiffies + HZ/20; - - add_timer(<pc_timer); -} - -/* DDP to LLAP translation */ - -static netdev_tx_t ltpc_xmit(struct sk_buff *skb, struct net_device *dev) -{ - /* in kernel 1.3.xx, on entry skb->data points to ddp header, - * and skb->len is the length of the ddp data + ddp header - */ - int i; - struct lt_sendlap cbuf; - unsigned char *hdr; - - cbuf.command = LT_SENDLAP; - cbuf.dnode = skb->data[0]; - cbuf.laptype = skb->data[2]; - skb_pull(skb,3); /* skip past LLAP header */ - cbuf.length = skb->len; /* this is host order */ - skb_reset_transport_header(skb); - - if(debug & DEBUG_UPPER) { - printk("command "); - for(i=0;i<6;i++) - printk("%02x ",((unsigned char *)&cbuf)[i]); - printk("\n"); - } - - hdr = skb_transport_header(skb); - do_write(dev, &cbuf, sizeof(cbuf), hdr, skb->len); - - if(debug & DEBUG_UPPER) { - printk("sent %d ddp bytes\n",skb->len); - for (i = 0; i < skb->len; i++) - printk("%02x ", hdr[i]); - printk("\n"); - } - - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -/* initialization stuff */ - -static int __init ltpc_probe_dma(int base, int dma) -{ - int want = (dma == 3) ? 2 : (dma == 1) ? 1 : 3; - unsigned long timeout; - unsigned long f; - - if (want & 1) { - if (request_dma(1,"ltpc")) { - want &= ~1; - } else { - f=claim_dma_lock(); - disable_dma(1); - clear_dma_ff(1); - set_dma_mode(1,DMA_MODE_WRITE); - set_dma_addr(1,virt_to_bus(ltdmabuf)); - set_dma_count(1,sizeof(struct lt_mem)); - enable_dma(1); - release_dma_lock(f); - } - } - if (want & 2) { - if (request_dma(3,"ltpc")) { - want &= ~2; - } else { - f=claim_dma_lock(); - disable_dma(3); - clear_dma_ff(3); - set_dma_mode(3,DMA_MODE_WRITE); - set_dma_addr(3,virt_to_bus(ltdmabuf)); - set_dma_count(3,sizeof(struct lt_mem)); - enable_dma(3); - release_dma_lock(f); - } - } - /* set up request */ - - /* FIXME -- do timings better! */ - - ltdmabuf[0] = LT_READMEM; - ltdmabuf[1] = 1; /* mailbox */ - ltdmabuf[2] = 0; ltdmabuf[3] = 0; /* address */ - ltdmabuf[4] = 0; ltdmabuf[5] = 1; /* read 0x0100 bytes */ - ltdmabuf[6] = 0; /* dunno if this is necessary */ - - inb_p(io+1); - inb_p(io+0); - timeout = jiffies+100*HZ/100; - while(time_before(jiffies, timeout)) { - if ( 0xfa == inb_p(io+6) ) break; - } - - inb_p(io+3); - inb_p(io+2); - while(time_before(jiffies, timeout)) { - if ( 0xfb == inb_p(io+6) ) break; - } - - /* release the other dma channel (if we opened both of them) */ - - if ((want & 2) && (get_dma_residue(3)==sizeof(struct lt_mem))) { - want &= ~2; - free_dma(3); - } - - if ((want & 1) && (get_dma_residue(1)==sizeof(struct lt_mem))) { - want &= ~1; - free_dma(1); - } - - if (!want) - return 0; - - return (want & 2) ? 3 : 1; -} - -static const struct net_device_ops ltpc_netdev = { - .ndo_start_xmit = ltpc_xmit, - .ndo_do_ioctl = ltpc_ioctl, - .ndo_set_multicast_list = set_multicast_list, -}; - -struct net_device * __init ltpc_probe(void) -{ - struct net_device *dev; - int err = -ENOMEM; - int x=0,y=0; - int autoirq; - unsigned long f; - unsigned long timeout; - - dev = alloc_ltalkdev(sizeof(struct ltpc_private)); - if (!dev) - goto out; - - /* probe for the I/O port address */ - - if (io != 0x240 && request_region(0x220,8,"ltpc")) { - x = inb_p(0x220+6); - if ( (x!=0xff) && (x>=0xf0) ) { - io = 0x220; - goto got_port; - } - release_region(0x220,8); - } - if (io != 0x220 && request_region(0x240,8,"ltpc")) { - y = inb_p(0x240+6); - if ( (y!=0xff) && (y>=0xf0) ){ - io = 0x240; - goto got_port; - } - release_region(0x240,8); - } - - /* give up in despair */ - printk(KERN_ERR "LocalTalk card not found; 220 = %02x, 240 = %02x.\n", x,y); - err = -ENODEV; - goto out1; - - got_port: - /* probe for the IRQ line */ - if (irq < 2) { - unsigned long irq_mask; - - irq_mask = probe_irq_on(); - /* reset the interrupt line */ - inb_p(io+7); - inb_p(io+7); - /* trigger an interrupt (I hope) */ - inb_p(io+6); - mdelay(2); - autoirq = probe_irq_off(irq_mask); - - if (autoirq == 0) { - printk(KERN_ERR "ltpc: probe at %#x failed to detect IRQ line.\n", io); - } else { - irq = autoirq; - } - } - - /* allocate a DMA buffer */ - ltdmabuf = (unsigned char *) dma_mem_alloc(1000); - if (!ltdmabuf) { - printk(KERN_ERR "ltpc: mem alloc failed\n"); - err = -ENOMEM; - goto out2; - } - - ltdmacbuf = <dmabuf[800]; - - if(debug & DEBUG_VERBOSE) { - printk("ltdmabuf pointer %08lx\n",(unsigned long) ltdmabuf); - } - - /* reset the card */ - - inb_p(io+1); - inb_p(io+3); - - msleep(20); - - inb_p(io+0); - inb_p(io+2); - inb_p(io+7); /* clear reset */ - inb_p(io+4); - inb_p(io+5); - inb_p(io+5); /* enable dma */ - inb_p(io+6); /* tri-state interrupt line */ - - ssleep(1); - - /* now, figure out which dma channel we're using, unless it's - already been specified */ - /* well, 0 is a legal DMA channel, but the LTPC card doesn't - use it... */ - dma = ltpc_probe_dma(io, dma); - if (!dma) { /* no dma channel */ - printk(KERN_ERR "No DMA channel found on ltpc card.\n"); - err = -ENODEV; - goto out3; - } - - /* print out friendly message */ - if(irq) - printk(KERN_INFO "Apple/Farallon LocalTalk-PC card at %03x, IR%d, DMA%d.\n",io,irq,dma); - else - printk(KERN_INFO "Apple/Farallon LocalTalk-PC card at %03x, DMA%d. Using polled mode.\n",io,dma); - - dev->netdev_ops = <pc_netdev; - dev->base_addr = io; - dev->irq = irq; - dev->dma = dma; - - /* the card will want to send a result at this point */ - /* (I think... leaving out this part makes the kernel crash, - so I put it back in...) */ - - f=claim_dma_lock(); - disable_dma(dma); - clear_dma_ff(dma); - set_dma_mode(dma,DMA_MODE_READ); - set_dma_addr(dma,virt_to_bus(ltdmabuf)); - set_dma_count(dma,0x100); - enable_dma(dma); - release_dma_lock(f); - - (void) inb_p(io+3); - (void) inb_p(io+2); - timeout = jiffies+100*HZ/100; - - while(time_before(jiffies, timeout)) { - if( 0xf9 == inb_p(io+6)) - break; - schedule(); - } - - if(debug & DEBUG_VERBOSE) { - printk("setting up timer and irq\n"); - } - - /* grab it and don't let go :-) */ - if (irq && request_irq( irq, ltpc_interrupt, 0, "ltpc", dev) >= 0) - { - (void) inb_p(io+7); /* enable interrupts from board */ - (void) inb_p(io+7); /* and reset irq line */ - } else { - if( irq ) - printk(KERN_ERR "ltpc: IRQ already in use, using polled mode.\n"); - dev->irq = 0; - /* polled mode -- 20 times per second */ - /* this is really, really slow... should it poll more often? */ - init_timer(<pc_timer); - ltpc_timer.function=ltpc_poll; - ltpc_timer.data = (unsigned long) dev; - - ltpc_timer.expires = jiffies + HZ/20; - add_timer(<pc_timer); - } - err = register_netdev(dev); - if (err) - goto out4; - - return NULL; -out4: - del_timer_sync(<pc_timer); - if (dev->irq) - free_irq(dev->irq, dev); -out3: - free_pages((unsigned long)ltdmabuf, get_order(1000)); -out2: - release_region(io, 8); -out1: - free_netdev(dev); -out: - return ERR_PTR(err); -} - -#ifndef MODULE -/* handles "ltpc=io,irq,dma" kernel command lines */ -static int __init ltpc_setup(char *str) -{ - int ints[5]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - if (ints[0] == 0) { - if (str && !strncmp(str, "auto", 4)) { - /* do nothing :-) */ - } - else { - /* usage message */ - printk (KERN_ERR - "ltpc: usage: ltpc=auto|iobase[,irq[,dma]]\n"); - return 0; - } - } else { - io = ints[1]; - if (ints[0] > 1) { - irq = ints[2]; - } - if (ints[0] > 2) { - dma = ints[3]; - } - /* ignore any other parameters */ - } - return 1; -} - -__setup("ltpc=", ltpc_setup); -#endif /* MODULE */ - -static struct net_device *dev_ltpc; - -#ifdef MODULE - -MODULE_LICENSE("GPL"); -module_param(debug, int, 0); -module_param(io, int, 0); -module_param(irq, int, 0); -module_param(dma, int, 0); - - -static int __init ltpc_module_init(void) -{ - if(io == 0) - printk(KERN_NOTICE - "ltpc: Autoprobing is not recommended for modules\n"); - - dev_ltpc = ltpc_probe(); - if (IS_ERR(dev_ltpc)) - return PTR_ERR(dev_ltpc); - return 0; -} -module_init(ltpc_module_init); -#endif - -static void __exit ltpc_cleanup(void) -{ - - if(debug & DEBUG_VERBOSE) printk("unregister_netdev\n"); - unregister_netdev(dev_ltpc); - - ltpc_timer.data = 0; /* signal the poll routine that we're done */ - - del_timer_sync(<pc_timer); - - if(debug & DEBUG_VERBOSE) printk("freeing irq\n"); - - if (dev_ltpc->irq) - free_irq(dev_ltpc->irq, dev_ltpc); - - if(debug & DEBUG_VERBOSE) printk("freeing dma\n"); - - if (dev_ltpc->dma) - free_dma(dev_ltpc->dma); - - if(debug & DEBUG_VERBOSE) printk("freeing ioaddr\n"); - - if (dev_ltpc->base_addr) - release_region(dev_ltpc->base_addr,8); - - free_netdev(dev_ltpc); - - if(debug & DEBUG_VERBOSE) printk("free_pages\n"); - - free_pages( (unsigned long) ltdmabuf, get_order(1000)); - - if(debug & DEBUG_VERBOSE) printk("returning from cleanup_module\n"); -} - -module_exit(ltpc_cleanup); diff --git a/drivers/net/appletalk/ltpc.h b/drivers/net/appletalk/ltpc.h deleted file mode 100644 index cd30544a3729..000000000000 --- a/drivers/net/appletalk/ltpc.h +++ /dev/null @@ -1,73 +0,0 @@ -/*** ltpc.h - * - * - ***/ - -#define LT_GETRESULT 0x00 -#define LT_WRITEMEM 0x01 -#define LT_READMEM 0x02 -#define LT_GETFLAGS 0x04 -#define LT_SETFLAGS 0x05 -#define LT_INIT 0x10 -#define LT_SENDLAP 0x13 -#define LT_RCVLAP 0x14 - -/* the flag that we care about */ -#define LT_FLAG_ALLLAP 0x04 - -struct lt_getresult { - unsigned char command; - unsigned char mailbox; -}; - -struct lt_mem { - unsigned char command; - unsigned char mailbox; - unsigned short addr; /* host order */ - unsigned short length; /* host order */ -}; - -struct lt_setflags { - unsigned char command; - unsigned char mailbox; - unsigned char flags; -}; - -struct lt_getflags { - unsigned char command; - unsigned char mailbox; -}; - -struct lt_init { - unsigned char command; - unsigned char mailbox; - unsigned char hint; -}; - -struct lt_sendlap { - unsigned char command; - unsigned char mailbox; - unsigned char dnode; - unsigned char laptype; - unsigned short length; /* host order */ -}; - -struct lt_rcvlap { - unsigned char command; - unsigned char dnode; - unsigned char snode; - unsigned char laptype; - unsigned short length; /* host order */ -}; - -union lt_command { - struct lt_getresult getresult; - struct lt_mem mem; - struct lt_setflags setflags; - struct lt_getflags getflags; - struct lt_init init; - struct lt_sendlap sendlap; - struct lt_rcvlap rcvlap; -}; -typedef union lt_command lt_command; - diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index b80755da5394..584d4e264807 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -169,6 +169,8 @@ source "drivers/staging/bcm/Kconfig" source "drivers/staging/ft1000/Kconfig" +source "drivers/staging/appletalk/Kconfig" + source "drivers/staging/intel_sst/Kconfig" source "drivers/staging/speakup/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index fd26509e5efa..88f0c64e7e58 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_ATH6K_LEGACY) += ath6kl/ obj-$(CONFIG_USB_ENESTORAGE) += keucr/ obj-$(CONFIG_BCM_WIMAX) += bcm/ obj-$(CONFIG_FT1000) += ft1000/ +obj-$(CONFIG_DEV_APPLETALK) += appletalk/ obj-$(CONFIG_SND_INTEL_SST) += intel_sst/ obj-$(CONFIG_SPEAKUP) += speakup/ obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/ diff --git a/drivers/staging/appletalk/Kconfig b/drivers/staging/appletalk/Kconfig new file mode 100644 index 000000000000..0b376a990972 --- /dev/null +++ b/drivers/staging/appletalk/Kconfig @@ -0,0 +1,126 @@ +# +# Appletalk driver configuration +# +config ATALK + tristate "Appletalk protocol support" + depends on BKL # waiting to be removed from net/appletalk/ddp.c + select LLC + ---help--- + AppleTalk is the protocol that Apple computers can use to communicate + on a network. If your Linux box is connected to such a network and you + wish to connect to it, say Y. You will need to use the netatalk package + so that your Linux box can act as a print and file server for Macs as + well as access AppleTalk printers. Check out + on the WWW for details. + EtherTalk is the name used for AppleTalk over Ethernet and the + cheaper and slower LocalTalk is AppleTalk over a proprietary Apple + network using serial links. EtherTalk and LocalTalk are fully + supported by Linux. + + General information about how to connect Linux, Windows machines and + Macs is on the WWW at . The + NET3-4-HOWTO, available from + , contains valuable + information as well. + + To compile this driver as a module, choose M here: the module will be + called appletalk. You almost certainly want to compile it as a + module so you can restart your AppleTalk stack without rebooting + your machine. I hear that the GNU boycott of Apple is over, so + even politically correct people are allowed to say Y here. + +config DEV_APPLETALK + tristate "Appletalk interfaces support" + depends on ATALK + help + AppleTalk is the protocol that Apple computers can use to communicate + on a network. If your Linux box is connected to such a network, and wish + to do IP over it, or you have a LocalTalk card and wish to use it to + connect to the AppleTalk network, say Y. + + +config LTPC + tristate "Apple/Farallon LocalTalk PC support" + depends on DEV_APPLETALK && (ISA || EISA) && ISA_DMA_API + help + This allows you to use the AppleTalk PC card to connect to LocalTalk + networks. The card is also known as the Farallon PhoneNet PC card. + If you are in doubt, this card is the one with the 65C02 chip on it. + You also need version 1.3.3 or later of the netatalk package. + This driver is experimental, which means that it may not work. + See the file . + +config COPS + tristate "COPS LocalTalk PC support" + depends on DEV_APPLETALK && (ISA || EISA) + help + This allows you to use COPS AppleTalk cards to connect to LocalTalk + networks. You also need version 1.3.3 or later of the netatalk + package. This driver is experimental, which means that it may not + work. This driver will only work if you choose "AppleTalk DDP" + networking support, above. + Please read the file . + +config COPS_DAYNA + bool "Dayna firmware support" + depends on COPS + help + Support COPS compatible cards with Dayna style firmware (Dayna + DL2000/ Daynatalk/PC (half length), COPS LT-95, Farallon PhoneNET PC + III, Farallon PhoneNET PC II). + +config COPS_TANGENT + bool "Tangent firmware support" + depends on COPS + help + Support COPS compatible cards with Tangent style firmware (Tangent + ATB_II, Novell NL-1000, Daystar Digital LT-200. + +config IPDDP + tristate "Appletalk-IP driver support" + depends on DEV_APPLETALK && ATALK + ---help--- + This allows IP networking for users who only have AppleTalk + networking available. This feature is experimental. With this + driver, you can encapsulate IP inside AppleTalk (e.g. if your Linux + box is stuck on an AppleTalk only network) or decapsulate (e.g. if + you want your Linux box to act as an Internet gateway for a zoo of + AppleTalk connected Macs). Please see the file + for more information. + + If you say Y here, the AppleTalk-IP support will be compiled into + the kernel. In this case, you can either use encapsulation or + decapsulation, but not both. With the following two questions, you + decide which one you want. + + To compile the AppleTalk-IP support as a module, choose M here: the + module will be called ipddp. + In this case, you will be able to use both encapsulation and + decapsulation simultaneously, by loading two copies of the module + and specifying different values for the module option ipddp_mode. + +config IPDDP_ENCAP + bool "IP to Appletalk-IP Encapsulation support" + depends on IPDDP + help + If you say Y here, the AppleTalk-IP code will be able to encapsulate + IP packets inside AppleTalk frames; this is useful if your Linux box + is stuck on an AppleTalk network (which hopefully contains a + decapsulator somewhere). Please see + for more information. If + you said Y to "AppleTalk-IP driver support" above and you say Y + here, then you cannot say Y to "AppleTalk-IP to IP Decapsulation + support", below. + +config IPDDP_DECAP + bool "Appletalk-IP to IP Decapsulation support" + depends on IPDDP + help + If you say Y here, the AppleTalk-IP code will be able to decapsulate + AppleTalk-IP frames to IP packets; this is useful if you want your + Linux box to act as an Internet gateway for an AppleTalk network. + Please see for more + information. If you said Y to "AppleTalk-IP driver support" above + and you say Y here, then you cannot say Y to "IP to AppleTalk-IP + Encapsulation support", above. + diff --git a/drivers/staging/appletalk/Makefile b/drivers/staging/appletalk/Makefile new file mode 100644 index 000000000000..2a5129a5c6bc --- /dev/null +++ b/drivers/staging/appletalk/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for drivers/staging/appletalk +# +obj-$(CONFIG_ATALK) += appletalk.o + +appletalk-y := aarp.o ddp.o dev.o +appletalk-$(CONFIG_PROC_FS) += atalk_proc.o +appletalk-$(CONFIG_SYSCTL) += sysctl_net_atalk.o + +obj-$(CONFIG_IPDDP) += ipddp.o +obj-$(CONFIG_COPS) += cops.o +obj-$(CONFIG_LTPC) += ltpc.o diff --git a/drivers/staging/appletalk/aarp.c b/drivers/staging/appletalk/aarp.c new file mode 100644 index 000000000000..7163a1dd501f --- /dev/null +++ b/drivers/staging/appletalk/aarp.c @@ -0,0 +1,1063 @@ +/* + * AARP: An implementation of the AppleTalk AARP protocol for + * Ethernet 'ELAP'. + * + * Alan Cox + * + * This doesn't fit cleanly with the IP arp. Potentially we can use + * the generic neighbour discovery code to clean this up. + * + * FIXME: + * We ought to handle the retransmits with a single list and a + * separate fast timer for when it is needed. + * Use neighbour discovery code. + * Token Ring Support. + * + * 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. + * + * + * References: + * Inside AppleTalk (2nd Ed). + * Fixes: + * Jaume Grau - flush caches on AARP_PROBE + * Rob Newberry - Added proxy AARP and AARP proc fs, + * moved probing from DDP module. + * Arnaldo C. Melo - don't mangle rx packets + * + */ + +#include +#include +#include +#include +#include +#include "atalk.h" +#include +#include +#include +#include + +int sysctl_aarp_expiry_time = AARP_EXPIRY_TIME; +int sysctl_aarp_tick_time = AARP_TICK_TIME; +int sysctl_aarp_retransmit_limit = AARP_RETRANSMIT_LIMIT; +int sysctl_aarp_resolve_time = AARP_RESOLVE_TIME; + +/* Lists of aarp entries */ +/** + * struct aarp_entry - AARP entry + * @last_sent - Last time we xmitted the aarp request + * @packet_queue - Queue of frames wait for resolution + * @status - Used for proxy AARP + * expires_at - Entry expiry time + * target_addr - DDP Address + * dev - Device to use + * hwaddr - Physical i/f address of target/router + * xmit_count - When this hits 10 we give up + * next - Next entry in chain + */ +struct aarp_entry { + /* These first two are only used for unresolved entries */ + unsigned long last_sent; + struct sk_buff_head packet_queue; + int status; + unsigned long expires_at; + struct atalk_addr target_addr; + struct net_device *dev; + char hwaddr[6]; + unsigned short xmit_count; + struct aarp_entry *next; +}; + +/* Hashed list of resolved, unresolved and proxy entries */ +static struct aarp_entry *resolved[AARP_HASH_SIZE]; +static struct aarp_entry *unresolved[AARP_HASH_SIZE]; +static struct aarp_entry *proxies[AARP_HASH_SIZE]; +static int unresolved_count; + +/* One lock protects it all. */ +static DEFINE_RWLOCK(aarp_lock); + +/* Used to walk the list and purge/kick entries. */ +static struct timer_list aarp_timer; + +/* + * Delete an aarp queue + * + * Must run under aarp_lock. + */ +static void __aarp_expire(struct aarp_entry *a) +{ + skb_queue_purge(&a->packet_queue); + kfree(a); +} + +/* + * Send an aarp queue entry request + * + * Must run under aarp_lock. + */ +static void __aarp_send_query(struct aarp_entry *a) +{ + static unsigned char aarp_eth_multicast[ETH_ALEN] = + { 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; + struct net_device *dev = a->dev; + struct elapaarp *eah; + int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length; + struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); + struct atalk_addr *sat = atalk_find_dev_addr(dev); + + if (!skb) + return; + + if (!sat) { + kfree_skb(skb); + return; + } + + /* Set up the buffer */ + skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb_put(skb, sizeof(*eah)); + skb->protocol = htons(ETH_P_ATALK); + skb->dev = dev; + eah = aarp_hdr(skb); + + /* Set up the ARP */ + eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); + eah->pa_type = htons(ETH_P_ATALK); + eah->hw_len = ETH_ALEN; + eah->pa_len = AARP_PA_ALEN; + eah->function = htons(AARP_REQUEST); + + memcpy(eah->hw_src, dev->dev_addr, ETH_ALEN); + + eah->pa_src_zero = 0; + eah->pa_src_net = sat->s_net; + eah->pa_src_node = sat->s_node; + + memset(eah->hw_dst, '\0', ETH_ALEN); + + eah->pa_dst_zero = 0; + eah->pa_dst_net = a->target_addr.s_net; + eah->pa_dst_node = a->target_addr.s_node; + + /* Send it */ + aarp_dl->request(aarp_dl, skb, aarp_eth_multicast); + /* Update the sending count */ + a->xmit_count++; + a->last_sent = jiffies; +} + +/* This runs under aarp_lock and in softint context, so only atomic memory + * allocations can be used. */ +static void aarp_send_reply(struct net_device *dev, struct atalk_addr *us, + struct atalk_addr *them, unsigned char *sha) +{ + struct elapaarp *eah; + int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length; + struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); + + if (!skb) + return; + + /* Set up the buffer */ + skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb_put(skb, sizeof(*eah)); + skb->protocol = htons(ETH_P_ATALK); + skb->dev = dev; + eah = aarp_hdr(skb); + + /* Set up the ARP */ + eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); + eah->pa_type = htons(ETH_P_ATALK); + eah->hw_len = ETH_ALEN; + eah->pa_len = AARP_PA_ALEN; + eah->function = htons(AARP_REPLY); + + memcpy(eah->hw_src, dev->dev_addr, ETH_ALEN); + + eah->pa_src_zero = 0; + eah->pa_src_net = us->s_net; + eah->pa_src_node = us->s_node; + + if (!sha) + memset(eah->hw_dst, '\0', ETH_ALEN); + else + memcpy(eah->hw_dst, sha, ETH_ALEN); + + eah->pa_dst_zero = 0; + eah->pa_dst_net = them->s_net; + eah->pa_dst_node = them->s_node; + + /* Send it */ + aarp_dl->request(aarp_dl, skb, sha); +} + +/* + * Send probe frames. Called from aarp_probe_network and + * aarp_proxy_probe_network. + */ + +static void aarp_send_probe(struct net_device *dev, struct atalk_addr *us) +{ + struct elapaarp *eah; + int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length; + struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); + static unsigned char aarp_eth_multicast[ETH_ALEN] = + { 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; + + if (!skb) + return; + + /* Set up the buffer */ + skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb_put(skb, sizeof(*eah)); + skb->protocol = htons(ETH_P_ATALK); + skb->dev = dev; + eah = aarp_hdr(skb); + + /* Set up the ARP */ + eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); + eah->pa_type = htons(ETH_P_ATALK); + eah->hw_len = ETH_ALEN; + eah->pa_len = AARP_PA_ALEN; + eah->function = htons(AARP_PROBE); + + memcpy(eah->hw_src, dev->dev_addr, ETH_ALEN); + + eah->pa_src_zero = 0; + eah->pa_src_net = us->s_net; + eah->pa_src_node = us->s_node; + + memset(eah->hw_dst, '\0', ETH_ALEN); + + eah->pa_dst_zero = 0; + eah->pa_dst_net = us->s_net; + eah->pa_dst_node = us->s_node; + + /* Send it */ + aarp_dl->request(aarp_dl, skb, aarp_eth_multicast); +} + +/* + * Handle an aarp timer expire + * + * Must run under the aarp_lock. + */ + +static void __aarp_expire_timer(struct aarp_entry **n) +{ + struct aarp_entry *t; + + while (*n) + /* Expired ? */ + if (time_after(jiffies, (*n)->expires_at)) { + t = *n; + *n = (*n)->next; + __aarp_expire(t); + } else + n = &((*n)->next); +} + +/* + * Kick all pending requests 5 times a second. + * + * Must run under the aarp_lock. + */ +static void __aarp_kick(struct aarp_entry **n) +{ + struct aarp_entry *t; + + while (*n) + /* Expired: if this will be the 11th tx, we delete instead. */ + if ((*n)->xmit_count >= sysctl_aarp_retransmit_limit) { + t = *n; + *n = (*n)->next; + __aarp_expire(t); + } else { + __aarp_send_query(*n); + n = &((*n)->next); + } +} + +/* + * A device has gone down. Take all entries referring to the device + * and remove them. + * + * Must run under the aarp_lock. + */ +static void __aarp_expire_device(struct aarp_entry **n, struct net_device *dev) +{ + struct aarp_entry *t; + + while (*n) + if ((*n)->dev == dev) { + t = *n; + *n = (*n)->next; + __aarp_expire(t); + } else + n = &((*n)->next); +} + +/* Handle the timer event */ +static void aarp_expire_timeout(unsigned long unused) +{ + int ct; + + write_lock_bh(&aarp_lock); + + for (ct = 0; ct < AARP_HASH_SIZE; ct++) { + __aarp_expire_timer(&resolved[ct]); + __aarp_kick(&unresolved[ct]); + __aarp_expire_timer(&unresolved[ct]); + __aarp_expire_timer(&proxies[ct]); + } + + write_unlock_bh(&aarp_lock); + mod_timer(&aarp_timer, jiffies + + (unresolved_count ? sysctl_aarp_tick_time : + sysctl_aarp_expiry_time)); +} + +/* Network device notifier chain handler. */ +static int aarp_device_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct net_device *dev = ptr; + int ct; + + if (!net_eq(dev_net(dev), &init_net)) + return NOTIFY_DONE; + + if (event == NETDEV_DOWN) { + write_lock_bh(&aarp_lock); + + for (ct = 0; ct < AARP_HASH_SIZE; ct++) { + __aarp_expire_device(&resolved[ct], dev); + __aarp_expire_device(&unresolved[ct], dev); + __aarp_expire_device(&proxies[ct], dev); + } + + write_unlock_bh(&aarp_lock); + } + return NOTIFY_DONE; +} + +/* Expire all entries in a hash chain */ +static void __aarp_expire_all(struct aarp_entry **n) +{ + struct aarp_entry *t; + + while (*n) { + t = *n; + *n = (*n)->next; + __aarp_expire(t); + } +} + +/* Cleanup all hash chains -- module unloading */ +static void aarp_purge(void) +{ + int ct; + + write_lock_bh(&aarp_lock); + for (ct = 0; ct < AARP_HASH_SIZE; ct++) { + __aarp_expire_all(&resolved[ct]); + __aarp_expire_all(&unresolved[ct]); + __aarp_expire_all(&proxies[ct]); + } + write_unlock_bh(&aarp_lock); +} + +/* + * Create a new aarp entry. This must use GFP_ATOMIC because it + * runs while holding spinlocks. + */ +static struct aarp_entry *aarp_alloc(void) +{ + struct aarp_entry *a = kmalloc(sizeof(*a), GFP_ATOMIC); + + if (a) + skb_queue_head_init(&a->packet_queue); + return a; +} + +/* + * Find an entry. We might return an expired but not yet purged entry. We + * don't care as it will do no harm. + * + * This must run under the aarp_lock. + */ +static struct aarp_entry *__aarp_find_entry(struct aarp_entry *list, + struct net_device *dev, + struct atalk_addr *sat) +{ + while (list) { + if (list->target_addr.s_net == sat->s_net && + list->target_addr.s_node == sat->s_node && + list->dev == dev) + break; + list = list->next; + } + + return list; +} + +/* Called from the DDP code, and thus must be exported. */ +void aarp_proxy_remove(struct net_device *dev, struct atalk_addr *sa) +{ + int hash = sa->s_node % (AARP_HASH_SIZE - 1); + struct aarp_entry *a; + + write_lock_bh(&aarp_lock); + + a = __aarp_find_entry(proxies[hash], dev, sa); + if (a) + a->expires_at = jiffies - 1; + + write_unlock_bh(&aarp_lock); +} + +/* This must run under aarp_lock. */ +static struct atalk_addr *__aarp_proxy_find(struct net_device *dev, + struct atalk_addr *sa) +{ + int hash = sa->s_node % (AARP_HASH_SIZE - 1); + struct aarp_entry *a = __aarp_find_entry(proxies[hash], dev, sa); + + return a ? sa : NULL; +} + +/* + * Probe a Phase 1 device or a device that requires its Net:Node to + * be set via an ioctl. + */ +static void aarp_send_probe_phase1(struct atalk_iface *iface) +{ + struct ifreq atreq; + struct sockaddr_at *sa = (struct sockaddr_at *)&atreq.ifr_addr; + const struct net_device_ops *ops = iface->dev->netdev_ops; + + sa->sat_addr.s_node = iface->address.s_node; + sa->sat_addr.s_net = ntohs(iface->address.s_net); + + /* We pass the Net:Node to the drivers/cards by a Device ioctl. */ + if (!(ops->ndo_do_ioctl(iface->dev, &atreq, SIOCSIFADDR))) { + ops->ndo_do_ioctl(iface->dev, &atreq, SIOCGIFADDR); + if (iface->address.s_net != htons(sa->sat_addr.s_net) || + iface->address.s_node != sa->sat_addr.s_node) + iface->status |= ATIF_PROBE_FAIL; + + iface->address.s_net = htons(sa->sat_addr.s_net); + iface->address.s_node = sa->sat_addr.s_node; + } +} + + +void aarp_probe_network(struct atalk_iface *atif) +{ + if (atif->dev->type == ARPHRD_LOCALTLK || + atif->dev->type == ARPHRD_PPP) + aarp_send_probe_phase1(atif); + else { + unsigned int count; + + for (count = 0; count < AARP_RETRANSMIT_LIMIT; count++) { + aarp_send_probe(atif->dev, &atif->address); + + /* Defer 1/10th */ + msleep(100); + + if (atif->status & ATIF_PROBE_FAIL) + break; + } + } +} + +int aarp_proxy_probe_network(struct atalk_iface *atif, struct atalk_addr *sa) +{ + int hash, retval = -EPROTONOSUPPORT; + struct aarp_entry *entry; + unsigned int count; + + /* + * we don't currently support LocalTalk or PPP for proxy AARP; + * if someone wants to try and add it, have fun + */ + if (atif->dev->type == ARPHRD_LOCALTLK || + atif->dev->type == ARPHRD_PPP) + goto out; + + /* + * create a new AARP entry with the flags set to be published -- + * we need this one to hang around even if it's in use + */ + entry = aarp_alloc(); + retval = -ENOMEM; + if (!entry) + goto out; + + entry->expires_at = -1; + entry->status = ATIF_PROBE; + entry->target_addr.s_node = sa->s_node; + entry->target_addr.s_net = sa->s_net; + entry->dev = atif->dev; + + write_lock_bh(&aarp_lock); + + hash = sa->s_node % (AARP_HASH_SIZE - 1); + entry->next = proxies[hash]; + proxies[hash] = entry; + + for (count = 0; count < AARP_RETRANSMIT_LIMIT; count++) { + aarp_send_probe(atif->dev, sa); + + /* Defer 1/10th */ + write_unlock_bh(&aarp_lock); + msleep(100); + write_lock_bh(&aarp_lock); + + if (entry->status & ATIF_PROBE_FAIL) + break; + } + + if (entry->status & ATIF_PROBE_FAIL) { + entry->expires_at = jiffies - 1; /* free the entry */ + retval = -EADDRINUSE; /* return network full */ + } else { /* clear the probing flag */ + entry->status &= ~ATIF_PROBE; + retval = 1; + } + + write_unlock_bh(&aarp_lock); +out: + return retval; +} + +/* Send a DDP frame */ +int aarp_send_ddp(struct net_device *dev, struct sk_buff *skb, + struct atalk_addr *sa, void *hwaddr) +{ + static char ddp_eth_multicast[ETH_ALEN] = + { 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; + int hash; + struct aarp_entry *a; + + skb_reset_network_header(skb); + + /* Check for LocalTalk first */ + if (dev->type == ARPHRD_LOCALTLK) { + struct atalk_addr *at = atalk_find_dev_addr(dev); + struct ddpehdr *ddp = (struct ddpehdr *)skb->data; + int ft = 2; + + /* + * Compressible ? + * + * IFF: src_net == dest_net == device_net + * (zero matches anything) + */ + + if ((!ddp->deh_snet || at->s_net == ddp->deh_snet) && + (!ddp->deh_dnet || at->s_net == ddp->deh_dnet)) { + skb_pull(skb, sizeof(*ddp) - 4); + + /* + * The upper two remaining bytes are the port + * numbers we just happen to need. Now put the + * length in the lower two. + */ + *((__be16 *)skb->data) = htons(skb->len); + ft = 1; + } + /* + * Nice and easy. No AARP type protocols occur here so we can + * just shovel it out with a 3 byte LLAP header + */ + + skb_push(skb, 3); + skb->data[0] = sa->s_node; + skb->data[1] = at->s_node; + skb->data[2] = ft; + skb->dev = dev; + goto sendit; + } + + /* On a PPP link we neither compress nor aarp. */ + if (dev->type == ARPHRD_PPP) { + skb->protocol = htons(ETH_P_PPPTALK); + skb->dev = dev; + goto sendit; + } + + /* Non ELAP we cannot do. */ + if (dev->type != ARPHRD_ETHER) + goto free_it; + + skb->dev = dev; + skb->protocol = htons(ETH_P_ATALK); + hash = sa->s_node % (AARP_HASH_SIZE - 1); + + /* Do we have a resolved entry? */ + if (sa->s_node == ATADDR_BCAST) { + /* Send it */ + ddp_dl->request(ddp_dl, skb, ddp_eth_multicast); + goto sent; + } + + write_lock_bh(&aarp_lock); + a = __aarp_find_entry(resolved[hash], dev, sa); + + if (a) { /* Return 1 and fill in the address */ + a->expires_at = jiffies + (sysctl_aarp_expiry_time * 10); + ddp_dl->request(ddp_dl, skb, a->hwaddr); + write_unlock_bh(&aarp_lock); + goto sent; + } + + /* Do we have an unresolved entry: This is the less common path */ + a = __aarp_find_entry(unresolved[hash], dev, sa); + if (a) { /* Queue onto the unresolved queue */ + skb_queue_tail(&a->packet_queue, skb); + goto out_unlock; + } + + /* Allocate a new entry */ + a = aarp_alloc(); + if (!a) { + /* Whoops slipped... good job it's an unreliable protocol 8) */ + write_unlock_bh(&aarp_lock); + goto free_it; + } + + /* Set up the queue */ + skb_queue_tail(&a->packet_queue, skb); + a->expires_at = jiffies + sysctl_aarp_resolve_time; + a->dev = dev; + a->next = unresolved[hash]; + a->target_addr = *sa; + a->xmit_count = 0; + unresolved[hash] = a; + unresolved_count++; + + /* Send an initial request for the address */ + __aarp_send_query(a); + + /* + * Switch to fast timer if needed (That is if this is the first + * unresolved entry to get added) + */ + + if (unresolved_count == 1) + mod_timer(&aarp_timer, jiffies + sysctl_aarp_tick_time); + + /* Now finally, it is safe to drop the lock. */ +out_unlock: + write_unlock_bh(&aarp_lock); + + /* Tell the ddp layer we have taken over for this frame. */ + goto sent; + +sendit: + if (skb->sk) + skb->priority = skb->sk->sk_priority; + if (dev_queue_xmit(skb)) + goto drop; +sent: + return NET_XMIT_SUCCESS; +free_it: + kfree_skb(skb); +drop: + return NET_XMIT_DROP; +} +EXPORT_SYMBOL(aarp_send_ddp); + +/* + * An entry in the aarp unresolved queue has become resolved. Send + * all the frames queued under it. + * + * Must run under aarp_lock. + */ +static void __aarp_resolved(struct aarp_entry **list, struct aarp_entry *a, + int hash) +{ + struct sk_buff *skb; + + while (*list) + if (*list == a) { + unresolved_count--; + *list = a->next; + + /* Move into the resolved list */ + a->next = resolved[hash]; + resolved[hash] = a; + + /* Kick frames off */ + while ((skb = skb_dequeue(&a->packet_queue)) != NULL) { + a->expires_at = jiffies + + sysctl_aarp_expiry_time * 10; + ddp_dl->request(ddp_dl, skb, a->hwaddr); + } + } else + list = &((*list)->next); +} + +/* + * This is called by the SNAP driver whenever we see an AARP SNAP + * frame. We currently only support Ethernet. + */ +static int aarp_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct elapaarp *ea = aarp_hdr(skb); + int hash, ret = 0; + __u16 function; + struct aarp_entry *a; + struct atalk_addr sa, *ma, da; + struct atalk_iface *ifa; + + if (!net_eq(dev_net(dev), &init_net)) + goto out0; + + /* We only do Ethernet SNAP AARP. */ + if (dev->type != ARPHRD_ETHER) + goto out0; + + /* Frame size ok? */ + if (!skb_pull(skb, sizeof(*ea))) + goto out0; + + function = ntohs(ea->function); + + /* Sanity check fields. */ + if (function < AARP_REQUEST || function > AARP_PROBE || + ea->hw_len != ETH_ALEN || ea->pa_len != AARP_PA_ALEN || + ea->pa_src_zero || ea->pa_dst_zero) + goto out0; + + /* Looks good. */ + hash = ea->pa_src_node % (AARP_HASH_SIZE - 1); + + /* Build an address. */ + sa.s_node = ea->pa_src_node; + sa.s_net = ea->pa_src_net; + + /* Process the packet. Check for replies of me. */ + ifa = atalk_find_dev(dev); + if (!ifa) + goto out1; + + if (ifa->status & ATIF_PROBE && + ifa->address.s_node == ea->pa_dst_node && + ifa->address.s_net == ea->pa_dst_net) { + ifa->status |= ATIF_PROBE_FAIL; /* Fail the probe (in use) */ + goto out1; + } + + /* Check for replies of proxy AARP entries */ + da.s_node = ea->pa_dst_node; + da.s_net = ea->pa_dst_net; + + write_lock_bh(&aarp_lock); + a = __aarp_find_entry(proxies[hash], dev, &da); + + if (a && a->status & ATIF_PROBE) { + a->status |= ATIF_PROBE_FAIL; + /* + * we do not respond to probe or request packets for + * this address while we are probing this address + */ + goto unlock; + } + + switch (function) { + case AARP_REPLY: + if (!unresolved_count) /* Speed up */ + break; + + /* Find the entry. */ + a = __aarp_find_entry(unresolved[hash], dev, &sa); + if (!a || dev != a->dev) + break; + + /* We can fill one in - this is good. */ + memcpy(a->hwaddr, ea->hw_src, ETH_ALEN); + __aarp_resolved(&unresolved[hash], a, hash); + if (!unresolved_count) + mod_timer(&aarp_timer, + jiffies + sysctl_aarp_expiry_time); + break; + + case AARP_REQUEST: + case AARP_PROBE: + + /* + * If it is my address set ma to my address and reply. + * We can treat probe and request the same. Probe + * simply means we shouldn't cache the querying host, + * as in a probe they are proposing an address not + * using one. + * + * Support for proxy-AARP added. We check if the + * address is one of our proxies before we toss the + * packet out. + */ + + sa.s_node = ea->pa_dst_node; + sa.s_net = ea->pa_dst_net; + + /* See if we have a matching proxy. */ + ma = __aarp_proxy_find(dev, &sa); + if (!ma) + ma = &ifa->address; + else { /* We need to make a copy of the entry. */ + da.s_node = sa.s_node; + da.s_net = sa.s_net; + ma = &da; + } + + if (function == AARP_PROBE) { + /* + * A probe implies someone trying to get an + * address. So as a precaution flush any + * entries we have for this address. + */ + a = __aarp_find_entry(resolved[sa.s_node % + (AARP_HASH_SIZE - 1)], + skb->dev, &sa); + + /* + * Make it expire next tick - that avoids us + * getting into a probe/flush/learn/probe/ + * flush/learn cycle during probing of a slow + * to respond host addr. + */ + if (a) { + a->expires_at = jiffies - 1; + mod_timer(&aarp_timer, jiffies + + sysctl_aarp_tick_time); + } + } + + if (sa.s_node != ma->s_node) + break; + + if (sa.s_net && ma->s_net && sa.s_net != ma->s_net) + break; + + sa.s_node = ea->pa_src_node; + sa.s_net = ea->pa_src_net; + + /* aarp_my_address has found the address to use for us. + */ + aarp_send_reply(dev, ma, &sa, ea->hw_src); + break; + } + +unlock: + write_unlock_bh(&aarp_lock); +out1: + ret = 1; +out0: + kfree_skb(skb); + return ret; +} + +static struct notifier_block aarp_notifier = { + .notifier_call = aarp_device_event, +}; + +static unsigned char aarp_snap_id[] = { 0x00, 0x00, 0x00, 0x80, 0xF3 }; + +void __init aarp_proto_init(void) +{ + aarp_dl = register_snap_client(aarp_snap_id, aarp_rcv); + if (!aarp_dl) + printk(KERN_CRIT "Unable to register AARP with SNAP.\n"); + setup_timer(&aarp_timer, aarp_expire_timeout, 0); + aarp_timer.expires = jiffies + sysctl_aarp_expiry_time; + add_timer(&aarp_timer); + register_netdevice_notifier(&aarp_notifier); +} + +/* Remove the AARP entries associated with a device. */ +void aarp_device_down(struct net_device *dev) +{ + int ct; + + write_lock_bh(&aarp_lock); + + for (ct = 0; ct < AARP_HASH_SIZE; ct++) { + __aarp_expire_device(&resolved[ct], dev); + __aarp_expire_device(&unresolved[ct], dev); + __aarp_expire_device(&proxies[ct], dev); + } + + write_unlock_bh(&aarp_lock); +} + +#ifdef CONFIG_PROC_FS +struct aarp_iter_state { + int bucket; + struct aarp_entry **table; +}; + +/* + * Get the aarp entry that is in the chain described + * by the iterator. + * If pos is set then skip till that index. + * pos = 1 is the first entry + */ +static struct aarp_entry *iter_next(struct aarp_iter_state *iter, loff_t *pos) +{ + int ct = iter->bucket; + struct aarp_entry **table = iter->table; + loff_t off = 0; + struct aarp_entry *entry; + + rescan: + while(ct < AARP_HASH_SIZE) { + for (entry = table[ct]; entry; entry = entry->next) { + if (!pos || ++off == *pos) { + iter->table = table; + iter->bucket = ct; + return entry; + } + } + ++ct; + } + + if (table == resolved) { + ct = 0; + table = unresolved; + goto rescan; + } + if (table == unresolved) { + ct = 0; + table = proxies; + goto rescan; + } + return NULL; +} + +static void *aarp_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(aarp_lock) +{ + struct aarp_iter_state *iter = seq->private; + + read_lock_bh(&aarp_lock); + iter->table = resolved; + iter->bucket = 0; + + return *pos ? iter_next(iter, pos) : SEQ_START_TOKEN; +} + +static void *aarp_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct aarp_entry *entry = v; + struct aarp_iter_state *iter = seq->private; + + ++*pos; + + /* first line after header */ + if (v == SEQ_START_TOKEN) + entry = iter_next(iter, NULL); + + /* next entry in current bucket */ + else if (entry->next) + entry = entry->next; + + /* next bucket or table */ + else { + ++iter->bucket; + entry = iter_next(iter, NULL); + } + return entry; +} + +static void aarp_seq_stop(struct seq_file *seq, void *v) + __releases(aarp_lock) +{ + read_unlock_bh(&aarp_lock); +} + +static const char *dt2str(unsigned long ticks) +{ + static char buf[32]; + + sprintf(buf, "%ld.%02ld", ticks / HZ, ((ticks % HZ) * 100 ) / HZ); + + return buf; +} + +static int aarp_seq_show(struct seq_file *seq, void *v) +{ + struct aarp_iter_state *iter = seq->private; + struct aarp_entry *entry = v; + unsigned long now = jiffies; + + if (v == SEQ_START_TOKEN) + seq_puts(seq, + "Address Interface Hardware Address" + " Expires LastSend Retry Status\n"); + else { + seq_printf(seq, "%04X:%02X %-12s", + ntohs(entry->target_addr.s_net), + (unsigned int) entry->target_addr.s_node, + entry->dev ? entry->dev->name : "????"); + seq_printf(seq, "%pM", entry->hwaddr); + seq_printf(seq, " %8s", + dt2str((long)entry->expires_at - (long)now)); + if (iter->table == unresolved) + seq_printf(seq, " %8s %6hu", + dt2str(now - entry->last_sent), + entry->xmit_count); + else + seq_puts(seq, " "); + seq_printf(seq, " %s\n", + (iter->table == resolved) ? "resolved" + : (iter->table == unresolved) ? "unresolved" + : (iter->table == proxies) ? "proxies" + : "unknown"); + } + return 0; +} + +static const struct seq_operations aarp_seq_ops = { + .start = aarp_seq_start, + .next = aarp_seq_next, + .stop = aarp_seq_stop, + .show = aarp_seq_show, +}; + +static int aarp_seq_open(struct inode *inode, struct file *file) +{ + return seq_open_private(file, &aarp_seq_ops, + sizeof(struct aarp_iter_state)); +} + +const struct file_operations atalk_seq_arp_fops = { + .owner = THIS_MODULE, + .open = aarp_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; +#endif + +/* General module cleanup. Called from cleanup_module() in ddp.c. */ +void aarp_cleanup_module(void) +{ + del_timer_sync(&aarp_timer); + unregister_netdevice_notifier(&aarp_notifier); + unregister_snap_client(aarp_dl); + aarp_purge(); +} diff --git a/drivers/staging/appletalk/atalk.h b/drivers/staging/appletalk/atalk.h new file mode 100644 index 000000000000..d34c187432ed --- /dev/null +++ b/drivers/staging/appletalk/atalk.h @@ -0,0 +1,208 @@ +#ifndef __LINUX_ATALK_H__ +#define __LINUX_ATALK_H__ + +#include +#include + +/* + * AppleTalk networking structures + * + * The following are directly referenced from the University Of Michigan + * netatalk for compatibility reasons. + */ +#define ATPORT_FIRST 1 +#define ATPORT_RESERVED 128 +#define ATPORT_LAST 254 /* 254 is only legal on localtalk */ +#define ATADDR_ANYNET (__u16)0 +#define ATADDR_ANYNODE (__u8)0 +#define ATADDR_ANYPORT (__u8)0 +#define ATADDR_BCAST (__u8)255 +#define DDP_MAXSZ 587 +#define DDP_MAXHOPS 15 /* 4 bits of hop counter */ + +#define SIOCATALKDIFADDR (SIOCPROTOPRIVATE + 0) + +struct atalk_addr { + __be16 s_net; + __u8 s_node; +}; + +struct sockaddr_at { + sa_family_t sat_family; + __u8 sat_port; + struct atalk_addr sat_addr; + char sat_zero[8]; +}; + +struct atalk_netrange { + __u8 nr_phase; + __be16 nr_firstnet; + __be16 nr_lastnet; +}; + +#ifdef __KERNEL__ + +#include + +struct atalk_route { + struct net_device *dev; + struct atalk_addr target; + struct atalk_addr gateway; + int flags; + struct atalk_route *next; +}; + +/** + * struct atalk_iface - AppleTalk Interface + * @dev - Network device associated with this interface + * @address - Our address + * @status - What are we doing? + * @nets - Associated direct netrange + * @next - next element in the list of interfaces + */ +struct atalk_iface { + struct net_device *dev; + struct atalk_addr address; + int status; +#define ATIF_PROBE 1 /* Probing for an address */ +#define ATIF_PROBE_FAIL 2 /* Probe collided */ + struct atalk_netrange nets; + struct atalk_iface *next; +}; + +struct atalk_sock { + /* struct sock has to be the first member of atalk_sock */ + struct sock sk; + __be16 dest_net; + __be16 src_net; + unsigned char dest_node; + unsigned char src_node; + unsigned char dest_port; + unsigned char src_port; +}; + +static inline struct atalk_sock *at_sk(struct sock *sk) +{ + return (struct atalk_sock *)sk; +} + +struct ddpehdr { + __be16 deh_len_hops; /* lower 10 bits are length, next 4 - hops */ + __be16 deh_sum; + __be16 deh_dnet; + __be16 deh_snet; + __u8 deh_dnode; + __u8 deh_snode; + __u8 deh_dport; + __u8 deh_sport; + /* And netatalk apps expect to stick the type in themselves */ +}; + +static __inline__ struct ddpehdr *ddp_hdr(struct sk_buff *skb) +{ + return (struct ddpehdr *)skb_transport_header(skb); +} + +/* AppleTalk AARP headers */ +struct elapaarp { + __be16 hw_type; +#define AARP_HW_TYPE_ETHERNET 1 +#define AARP_HW_TYPE_TOKENRING 2 + __be16 pa_type; + __u8 hw_len; + __u8 pa_len; +#define AARP_PA_ALEN 4 + __be16 function; +#define AARP_REQUEST 1 +#define AARP_REPLY 2 +#define AARP_PROBE 3 + __u8 hw_src[ETH_ALEN]; + __u8 pa_src_zero; + __be16 pa_src_net; + __u8 pa_src_node; + __u8 hw_dst[ETH_ALEN]; + __u8 pa_dst_zero; + __be16 pa_dst_net; + __u8 pa_dst_node; +} __attribute__ ((packed)); + +static __inline__ struct elapaarp *aarp_hdr(struct sk_buff *skb) +{ + return (struct elapaarp *)skb_transport_header(skb); +} + +/* Not specified - how long till we drop a resolved entry */ +#define AARP_EXPIRY_TIME (5 * 60 * HZ) +/* Size of hash table */ +#define AARP_HASH_SIZE 16 +/* Fast retransmission timer when resolving */ +#define AARP_TICK_TIME (HZ / 5) +/* Send 10 requests then give up (2 seconds) */ +#define AARP_RETRANSMIT_LIMIT 10 +/* + * Some value bigger than total retransmit time + a bit for last reply to + * appear and to stop continual requests + */ +#define AARP_RESOLVE_TIME (10 * HZ) + +extern struct datalink_proto *ddp_dl, *aarp_dl; +extern void aarp_proto_init(void); + +/* Inter module exports */ + +/* Give a device find its atif control structure */ +static inline struct atalk_iface *atalk_find_dev(struct net_device *dev) +{ + return dev->atalk_ptr; +} + +extern struct atalk_addr *atalk_find_dev_addr(struct net_device *dev); +extern struct net_device *atrtr_get_dev(struct atalk_addr *sa); +extern int aarp_send_ddp(struct net_device *dev, + struct sk_buff *skb, + struct atalk_addr *sa, void *hwaddr); +extern void aarp_device_down(struct net_device *dev); +extern void aarp_probe_network(struct atalk_iface *atif); +extern int aarp_proxy_probe_network(struct atalk_iface *atif, + struct atalk_addr *sa); +extern void aarp_proxy_remove(struct net_device *dev, + struct atalk_addr *sa); + +extern void aarp_cleanup_module(void); + +extern struct hlist_head atalk_sockets; +extern rwlock_t atalk_sockets_lock; + +extern struct atalk_route *atalk_routes; +extern rwlock_t atalk_routes_lock; + +extern struct atalk_iface *atalk_interfaces; +extern rwlock_t atalk_interfaces_lock; + +extern struct atalk_route atrtr_default; + +extern const struct file_operations atalk_seq_arp_fops; + +extern int sysctl_aarp_expiry_time; +extern int sysctl_aarp_tick_time; +extern int sysctl_aarp_retransmit_limit; +extern int sysctl_aarp_resolve_time; + +#ifdef CONFIG_SYSCTL +extern void atalk_register_sysctl(void); +extern void atalk_unregister_sysctl(void); +#else +#define atalk_register_sysctl() do { } while(0) +#define atalk_unregister_sysctl() do { } while(0) +#endif + +#ifdef CONFIG_PROC_FS +extern int atalk_proc_init(void); +extern void atalk_proc_exit(void); +#else +#define atalk_proc_init() ({ 0; }) +#define atalk_proc_exit() do { } while(0) +#endif /* CONFIG_PROC_FS */ + +#endif /* __KERNEL__ */ +#endif /* __LINUX_ATALK_H__ */ diff --git a/drivers/staging/appletalk/atalk_proc.c b/drivers/staging/appletalk/atalk_proc.c new file mode 100644 index 000000000000..d012ba2e67d1 --- /dev/null +++ b/drivers/staging/appletalk/atalk_proc.c @@ -0,0 +1,301 @@ +/* + * atalk_proc.c - proc support for Appletalk + * + * Copyright(c) Arnaldo Carvalho de Melo + * + * 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, version 2. + */ + +#include +#include +#include +#include +#include +#include "atalk.h" + + +static __inline__ struct atalk_iface *atalk_get_interface_idx(loff_t pos) +{ + struct atalk_iface *i; + + for (i = atalk_interfaces; pos && i; i = i->next) + --pos; + + return i; +} + +static void *atalk_seq_interface_start(struct seq_file *seq, loff_t *pos) + __acquires(atalk_interfaces_lock) +{ + loff_t l = *pos; + + read_lock_bh(&atalk_interfaces_lock); + return l ? atalk_get_interface_idx(--l) : SEQ_START_TOKEN; +} + +static void *atalk_seq_interface_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct atalk_iface *i; + + ++*pos; + if (v == SEQ_START_TOKEN) { + i = NULL; + if (atalk_interfaces) + i = atalk_interfaces; + goto out; + } + i = v; + i = i->next; +out: + return i; +} + +static void atalk_seq_interface_stop(struct seq_file *seq, void *v) + __releases(atalk_interfaces_lock) +{ + read_unlock_bh(&atalk_interfaces_lock); +} + +static int atalk_seq_interface_show(struct seq_file *seq, void *v) +{ + struct atalk_iface *iface; + + if (v == SEQ_START_TOKEN) { + seq_puts(seq, "Interface Address Networks " + "Status\n"); + goto out; + } + + iface = v; + seq_printf(seq, "%-16s %04X:%02X %04X-%04X %d\n", + iface->dev->name, ntohs(iface->address.s_net), + iface->address.s_node, ntohs(iface->nets.nr_firstnet), + ntohs(iface->nets.nr_lastnet), iface->status); +out: + return 0; +} + +static __inline__ struct atalk_route *atalk_get_route_idx(loff_t pos) +{ + struct atalk_route *r; + + for (r = atalk_routes; pos && r; r = r->next) + --pos; + + return r; +} + +static void *atalk_seq_route_start(struct seq_file *seq, loff_t *pos) + __acquires(atalk_routes_lock) +{ + loff_t l = *pos; + + read_lock_bh(&atalk_routes_lock); + return l ? atalk_get_route_idx(--l) : SEQ_START_TOKEN; +} + +static void *atalk_seq_route_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct atalk_route *r; + + ++*pos; + if (v == SEQ_START_TOKEN) { + r = NULL; + if (atalk_routes) + r = atalk_routes; + goto out; + } + r = v; + r = r->next; +out: + return r; +} + +static void atalk_seq_route_stop(struct seq_file *seq, void *v) + __releases(atalk_routes_lock) +{ + read_unlock_bh(&atalk_routes_lock); +} + +static int atalk_seq_route_show(struct seq_file *seq, void *v) +{ + struct atalk_route *rt; + + if (v == SEQ_START_TOKEN) { + seq_puts(seq, "Target Router Flags Dev\n"); + goto out; + } + + if (atrtr_default.dev) { + rt = &atrtr_default; + seq_printf(seq, "Default %04X:%02X %-4d %s\n", + ntohs(rt->gateway.s_net), rt->gateway.s_node, + rt->flags, rt->dev->name); + } + + rt = v; + seq_printf(seq, "%04X:%02X %04X:%02X %-4d %s\n", + ntohs(rt->target.s_net), rt->target.s_node, + ntohs(rt->gateway.s_net), rt->gateway.s_node, + rt->flags, rt->dev->name); +out: + return 0; +} + +static void *atalk_seq_socket_start(struct seq_file *seq, loff_t *pos) + __acquires(atalk_sockets_lock) +{ + read_lock_bh(&atalk_sockets_lock); + return seq_hlist_start_head(&atalk_sockets, *pos); +} + +static void *atalk_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos) +{ + return seq_hlist_next(v, &atalk_sockets, pos); +} + +static void atalk_seq_socket_stop(struct seq_file *seq, void *v) + __releases(atalk_sockets_lock) +{ + read_unlock_bh(&atalk_sockets_lock); +} + +static int atalk_seq_socket_show(struct seq_file *seq, void *v) +{ + struct sock *s; + struct atalk_sock *at; + + if (v == SEQ_START_TOKEN) { + seq_printf(seq, "Type Local_addr Remote_addr Tx_queue " + "Rx_queue St UID\n"); + goto out; + } + + s = sk_entry(v); + at = at_sk(s); + + seq_printf(seq, "%02X %04X:%02X:%02X %04X:%02X:%02X %08X:%08X " + "%02X %d\n", + s->sk_type, ntohs(at->src_net), at->src_node, at->src_port, + ntohs(at->dest_net), at->dest_node, at->dest_port, + sk_wmem_alloc_get(s), + sk_rmem_alloc_get(s), + s->sk_state, SOCK_INODE(s->sk_socket)->i_uid); +out: + return 0; +} + +static const struct seq_operations atalk_seq_interface_ops = { + .start = atalk_seq_interface_start, + .next = atalk_seq_interface_next, + .stop = atalk_seq_interface_stop, + .show = atalk_seq_interface_show, +}; + +static const struct seq_operations atalk_seq_route_ops = { + .start = atalk_seq_route_start, + .next = atalk_seq_route_next, + .stop = atalk_seq_route_stop, + .show = atalk_seq_route_show, +}; + +static const struct seq_operations atalk_seq_socket_ops = { + .start = atalk_seq_socket_start, + .next = atalk_seq_socket_next, + .stop = atalk_seq_socket_stop, + .show = atalk_seq_socket_show, +}; + +static int atalk_seq_interface_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &atalk_seq_interface_ops); +} + +static int atalk_seq_route_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &atalk_seq_route_ops); +} + +static int atalk_seq_socket_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &atalk_seq_socket_ops); +} + +static const struct file_operations atalk_seq_interface_fops = { + .owner = THIS_MODULE, + .open = atalk_seq_interface_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static const struct file_operations atalk_seq_route_fops = { + .owner = THIS_MODULE, + .open = atalk_seq_route_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static const struct file_operations atalk_seq_socket_fops = { + .owner = THIS_MODULE, + .open = atalk_seq_socket_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static struct proc_dir_entry *atalk_proc_dir; + +int __init atalk_proc_init(void) +{ + struct proc_dir_entry *p; + int rc = -ENOMEM; + + atalk_proc_dir = proc_mkdir("atalk", init_net.proc_net); + if (!atalk_proc_dir) + goto out; + + p = proc_create("interface", S_IRUGO, atalk_proc_dir, + &atalk_seq_interface_fops); + if (!p) + goto out_interface; + + p = proc_create("route", S_IRUGO, atalk_proc_dir, + &atalk_seq_route_fops); + if (!p) + goto out_route; + + p = proc_create("socket", S_IRUGO, atalk_proc_dir, + &atalk_seq_socket_fops); + if (!p) + goto out_socket; + + p = proc_create("arp", S_IRUGO, atalk_proc_dir, &atalk_seq_arp_fops); + if (!p) + goto out_arp; + + rc = 0; +out: + return rc; +out_arp: + remove_proc_entry("socket", atalk_proc_dir); +out_socket: + remove_proc_entry("route", atalk_proc_dir); +out_route: + remove_proc_entry("interface", atalk_proc_dir); +out_interface: + remove_proc_entry("atalk", init_net.proc_net); + goto out; +} + +void __exit atalk_proc_exit(void) +{ + remove_proc_entry("interface", atalk_proc_dir); + remove_proc_entry("route", atalk_proc_dir); + remove_proc_entry("socket", atalk_proc_dir); + remove_proc_entry("arp", atalk_proc_dir); + remove_proc_entry("atalk", init_net.proc_net); +} diff --git a/drivers/staging/appletalk/cops.c b/drivers/staging/appletalk/cops.c new file mode 100644 index 000000000000..661d42eff7d8 --- /dev/null +++ b/drivers/staging/appletalk/cops.c @@ -0,0 +1,1013 @@ +/* cops.c: LocalTalk driver for Linux. + * + * Authors: + * - Jay Schulist + * + * With more than a little help from; + * - Alan Cox + * + * Derived from: + * - skeleton.c: A network driver outline for linux. + * Written 1993-94 by Donald Becker. + * - ltpc.c: A driver for the LocalTalk PC card. + * Written by Bradford W. Johnson. + * + * Copyright 1993 United States Government as represented by the + * Director, National Security Agency. + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * Changes: + * 19970608 Alan Cox Allowed dual card type support + * Can set board type in insmod + * Hooks for cops_setup routine + * (not yet implemented). + * 19971101 Jay Schulist Fixes for multiple lt* devices. + * 19980607 Steven Hirsch Fixed the badly broken support + * for Tangent type cards. Only + * tested on Daystar LT200. Some + * cleanup of formatting and program + * logic. Added emacs 'local-vars' + * setup for Jay's brace style. + * 20000211 Alan Cox Cleaned up for softnet + */ + +static const char *version = +"cops.c:v0.04 6/7/98 Jay Schulist \n"; +/* + * Sources: + * COPS Localtalk SDK. This provides almost all of the information + * needed. + */ + +/* + * insmod/modprobe configurable stuff. + * - IO Port, choose one your card supports or 0 if you dare. + * - IRQ, also choose one your card supports or nothing and let + * the driver figure it out. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* For udelay() */ +#include +#include +#include + +#include +#include +#include + +#include "atalk.h" +#include "cops.h" /* Our Stuff */ +#include "cops_ltdrv.h" /* Firmware code for Tangent type cards. */ +#include "cops_ffdrv.h" /* Firmware code for Dayna type cards. */ + +/* + * The name of the card. Is used for messages and in the requests for + * io regions, irqs and dma channels + */ + +static const char *cardname = "cops"; + +#ifdef CONFIG_COPS_DAYNA +static int board_type = DAYNA; /* Module exported */ +#else +static int board_type = TANGENT; +#endif + +static int io = 0x240; /* Default IO for Dayna */ +static int irq = 5; /* Default IRQ */ + +/* + * COPS Autoprobe information. + * Right now if port address is right but IRQ is not 5 this will + * return a 5 no matter what since we will still get a status response. + * Need one more additional check to narrow down after we have gotten + * the ioaddr. But since only other possible IRQs is 3 and 4 so no real + * hurry on this. I *STRONGLY* recommend using IRQ 5 for your card with + * this driver. + * + * This driver has 2 modes and they are: Dayna mode and Tangent mode. + * Each mode corresponds with the type of card. It has been found + * that there are 2 main types of cards and all other cards are + * the same and just have different names or only have minor differences + * such as more IO ports. As this driver is tested it will + * become more clear on exactly what cards are supported. The driver + * defaults to using Dayna mode. To change the drivers mode, simply + * select Dayna or Tangent mode when configuring the kernel. + * + * This driver should support: + * TANGENT driver mode: + * Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200, + * COPS LT-1 + * DAYNA driver mode: + * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, + * Farallon PhoneNET PC III, Farallon PhoneNET PC II + * Other cards possibly supported mode unknown though: + * Dayna DL2000 (Full length), COPS LT/M (Micro-Channel) + * + * Cards NOT supported by this driver but supported by the ltpc.c + * driver written by Bradford W. Johnson + * Farallon PhoneNET PC + * Original Apple LocalTalk PC card + * + * N.B. + * + * The Daystar Digital LT200 boards do not support interrupt-driven + * IO. You must specify 'irq=0xff' as a module parameter to invoke + * polled mode. I also believe that the port probing logic is quite + * dangerous at best and certainly hopeless for a polled card. Best to + * specify both. - Steve H. + * + */ + +/* + * Zero terminated list of IO ports to probe. + */ + +static unsigned int ports[] = { + 0x240, 0x340, 0x200, 0x210, 0x220, 0x230, 0x260, + 0x2A0, 0x300, 0x310, 0x320, 0x330, 0x350, 0x360, + 0 +}; + +/* + * Zero terminated list of IRQ ports to probe. + */ + +static int cops_irqlist[] = { + 5, 4, 3, 0 +}; + +static struct timer_list cops_timer; + +/* use 0 for production, 1 for verification, 2 for debug, 3 for verbose debug */ +#ifndef COPS_DEBUG +#define COPS_DEBUG 1 +#endif +static unsigned int cops_debug = COPS_DEBUG; + +/* The number of low I/O ports used by the card. */ +#define COPS_IO_EXTENT 8 + +/* Information that needs to be kept for each board. */ + +struct cops_local +{ + int board; /* Holds what board type is. */ + int nodeid; /* Set to 1 once have nodeid. */ + unsigned char node_acquire; /* Node ID when acquired. */ + struct atalk_addr node_addr; /* Full node address */ + spinlock_t lock; /* RX/TX lock */ +}; + +/* Index to functions, as function prototypes. */ +static int cops_probe1 (struct net_device *dev, int ioaddr); +static int cops_irq (int ioaddr, int board); + +static int cops_open (struct net_device *dev); +static int cops_jumpstart (struct net_device *dev); +static void cops_reset (struct net_device *dev, int sleep); +static void cops_load (struct net_device *dev); +static int cops_nodeid (struct net_device *dev, int nodeid); + +static irqreturn_t cops_interrupt (int irq, void *dev_id); +static void cops_poll (unsigned long ltdev); +static void cops_timeout(struct net_device *dev); +static void cops_rx (struct net_device *dev); +static netdev_tx_t cops_send_packet (struct sk_buff *skb, + struct net_device *dev); +static void set_multicast_list (struct net_device *dev); +static int cops_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); +static int cops_close (struct net_device *dev); + +static void cleanup_card(struct net_device *dev) +{ + if (dev->irq) + free_irq(dev->irq, dev); + release_region(dev->base_addr, COPS_IO_EXTENT); +} + +/* + * Check for a network adaptor of this type, and return '0' iff one exists. + * If dev->base_addr == 0, probe all likely locations. + * If dev->base_addr in [1..0x1ff], always return failure. + * otherwise go with what we pass in. + */ +struct net_device * __init cops_probe(int unit) +{ + struct net_device *dev; + unsigned *port; + int base_addr; + int err = 0; + + dev = alloc_ltalkdev(sizeof(struct cops_local)); + if (!dev) + return ERR_PTR(-ENOMEM); + + if (unit >= 0) { + sprintf(dev->name, "lt%d", unit); + netdev_boot_setup_check(dev); + irq = dev->irq; + base_addr = dev->base_addr; + } else { + base_addr = dev->base_addr = io; + } + + if (base_addr > 0x1ff) { /* Check a single specified location. */ + err = cops_probe1(dev, base_addr); + } else if (base_addr != 0) { /* Don't probe at all. */ + err = -ENXIO; + } else { + /* FIXME Does this really work for cards which generate irq? + * It's definitely N.G. for polled Tangent. sh + * Dayna cards don't autoprobe well at all, but if your card is + * at IRQ 5 & IO 0x240 we find it every time. ;) JS + */ + for (port = ports; *port && cops_probe1(dev, *port) < 0; port++) + ; + if (!*port) + err = -ENODEV; + } + if (err) + goto out; + err = register_netdev(dev); + if (err) + goto out1; + return dev; +out1: + cleanup_card(dev); +out: + free_netdev(dev); + return ERR_PTR(err); +} + +static const struct net_device_ops cops_netdev_ops = { + .ndo_open = cops_open, + .ndo_stop = cops_close, + .ndo_start_xmit = cops_send_packet, + .ndo_tx_timeout = cops_timeout, + .ndo_do_ioctl = cops_ioctl, + .ndo_set_multicast_list = set_multicast_list, +}; + +/* + * This is the real probe routine. Linux has a history of friendly device + * probes on the ISA bus. A good device probes avoids doing writes, and + * verifies that the correct device exists and functions. + */ +static int __init cops_probe1(struct net_device *dev, int ioaddr) +{ + struct cops_local *lp; + static unsigned version_printed; + int board = board_type; + int retval; + + if(cops_debug && version_printed++ == 0) + printk("%s", version); + + /* Grab the region so no one else tries to probe our ioports. */ + if (!request_region(ioaddr, COPS_IO_EXTENT, dev->name)) + return -EBUSY; + + /* + * Since this board has jumpered interrupts, allocate the interrupt + * vector now. There is no point in waiting since no other device + * can use the interrupt, and this marks the irq as busy. Jumpered + * interrupts are typically not reported by the boards, and we must + * used AutoIRQ to find them. + */ + dev->irq = irq; + switch (dev->irq) + { + case 0: + /* COPS AutoIRQ routine */ + dev->irq = cops_irq(ioaddr, board); + if (dev->irq) + break; + /* No IRQ found on this port, fallthrough */ + case 1: + retval = -EINVAL; + goto err_out; + + /* Fixup for users that don't know that IRQ 2 is really + * IRQ 9, or don't know which one to set. + */ + case 2: + dev->irq = 9; + break; + + /* Polled operation requested. Although irq of zero passed as + * a parameter tells the init routines to probe, we'll + * overload it to denote polled operation at runtime. + */ + case 0xff: + dev->irq = 0; + break; + + default: + break; + } + + /* Reserve any actual interrupt. */ + if (dev->irq) { + retval = request_irq(dev->irq, cops_interrupt, 0, dev->name, dev); + if (retval) + goto err_out; + } + + dev->base_addr = ioaddr; + + lp = netdev_priv(dev); + spin_lock_init(&lp->lock); + + /* Copy local board variable to lp struct. */ + lp->board = board; + + dev->netdev_ops = &cops_netdev_ops; + dev->watchdog_timeo = HZ * 2; + + + /* Tell the user where the card is and what mode we're in. */ + if(board==DAYNA) + printk("%s: %s at %#3x, using IRQ %d, in Dayna mode.\n", + dev->name, cardname, ioaddr, dev->irq); + if(board==TANGENT) { + if(dev->irq) + printk("%s: %s at %#3x, IRQ %d, in Tangent mode\n", + dev->name, cardname, ioaddr, dev->irq); + else + printk("%s: %s at %#3x, using polled IO, in Tangent mode.\n", + dev->name, cardname, ioaddr); + + } + return 0; + +err_out: + release_region(ioaddr, COPS_IO_EXTENT); + return retval; +} + +static int __init cops_irq (int ioaddr, int board) +{ /* + * This does not use the IRQ to determine where the IRQ is. We just + * assume that when we get a correct status response that it's the IRQ. + * This really just verifies the IO port but since we only have access + * to such a small number of IRQs (5, 4, 3) this is not bad. + * This will probably not work for more than one card. + */ + int irqaddr=0; + int i, x, status; + + if(board==DAYNA) + { + outb(0, ioaddr+DAYNA_RESET); + inb(ioaddr+DAYNA_RESET); + mdelay(333); + } + if(board==TANGENT) + { + inb(ioaddr); + outb(0, ioaddr); + outb(0, ioaddr+TANG_RESET); + } + + for(i=0; cops_irqlist[i] !=0; i++) + { + irqaddr = cops_irqlist[i]; + for(x = 0xFFFF; x>0; x --) /* wait for response */ + { + if(board==DAYNA) + { + status = (inb(ioaddr+DAYNA_CARD_STATUS)&3); + if(status == 1) + return irqaddr; + } + if(board==TANGENT) + { + if((inb(ioaddr+TANG_CARD_STATUS)& TANG_TX_READY) !=0) + return irqaddr; + } + } + } + return 0; /* no IRQ found */ +} + +/* + * Open/initialize the board. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + */ +static int cops_open(struct net_device *dev) +{ + struct cops_local *lp = netdev_priv(dev); + + if(dev->irq==0) + { + /* + * I don't know if the Dayna-style boards support polled + * operation. For now, only allow it for Tangent. + */ + if(lp->board==TANGENT) /* Poll 20 times per second */ + { + init_timer(&cops_timer); + cops_timer.function = cops_poll; + cops_timer.data = (unsigned long)dev; + cops_timer.expires = jiffies + HZ/20; + add_timer(&cops_timer); + } + else + { + printk(KERN_WARNING "%s: No irq line set\n", dev->name); + return -EAGAIN; + } + } + + cops_jumpstart(dev); /* Start the card up. */ + + netif_start_queue(dev); + return 0; +} + +/* + * This allows for a dynamic start/restart of the entire card. + */ +static int cops_jumpstart(struct net_device *dev) +{ + struct cops_local *lp = netdev_priv(dev); + + /* + * Once the card has the firmware loaded and has acquired + * the nodeid, if it is reset it will lose it all. + */ + cops_reset(dev,1); /* Need to reset card before load firmware. */ + cops_load(dev); /* Load the firmware. */ + + /* + * If atalkd already gave us a nodeid we will use that + * one again, else we wait for atalkd to give us a nodeid + * in cops_ioctl. This may cause a problem if someone steals + * our nodeid while we are resetting. + */ + if(lp->nodeid == 1) + cops_nodeid(dev,lp->node_acquire); + + return 0; +} + +static void tangent_wait_reset(int ioaddr) +{ + int timeout=0; + + while(timeout++ < 5 && (inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) + mdelay(1); /* Wait 1 second */ +} + +/* + * Reset the LocalTalk board. + */ +static void cops_reset(struct net_device *dev, int sleep) +{ + struct cops_local *lp = netdev_priv(dev); + int ioaddr=dev->base_addr; + + if(lp->board==TANGENT) + { + inb(ioaddr); /* Clear request latch. */ + outb(0,ioaddr); /* Clear the TANG_TX_READY flop. */ + outb(0, ioaddr+TANG_RESET); /* Reset the adapter. */ + + tangent_wait_reset(ioaddr); + outb(0, ioaddr+TANG_CLEAR_INT); + } + if(lp->board==DAYNA) + { + outb(0, ioaddr+DAYNA_RESET); /* Assert the reset port */ + inb(ioaddr+DAYNA_RESET); /* Clear the reset */ + if (sleep) + msleep(333); + else + mdelay(333); + } + + netif_wake_queue(dev); +} + +static void cops_load (struct net_device *dev) +{ + struct ifreq ifr; + struct ltfirmware *ltf= (struct ltfirmware *)&ifr.ifr_ifru; + struct cops_local *lp = netdev_priv(dev); + int ioaddr=dev->base_addr; + int length, i = 0; + + strcpy(ifr.ifr_name,"lt0"); + + /* Get card's firmware code and do some checks on it. */ +#ifdef CONFIG_COPS_DAYNA + if(lp->board==DAYNA) + { + ltf->length=sizeof(ffdrv_code); + ltf->data=ffdrv_code; + } + else +#endif +#ifdef CONFIG_COPS_TANGENT + if(lp->board==TANGENT) + { + ltf->length=sizeof(ltdrv_code); + ltf->data=ltdrv_code; + } + else +#endif + { + printk(KERN_INFO "%s; unsupported board type.\n", dev->name); + return; + } + + /* Check to make sure firmware is correct length. */ + if(lp->board==DAYNA && ltf->length!=5983) + { + printk(KERN_WARNING "%s: Firmware is not length of FFDRV.BIN.\n", dev->name); + return; + } + if(lp->board==TANGENT && ltf->length!=2501) + { + printk(KERN_WARNING "%s: Firmware is not length of DRVCODE.BIN.\n", dev->name); + return; + } + + if(lp->board==DAYNA) + { + /* + * We must wait for a status response + * with the DAYNA board. + */ + while(++i<65536) + { + if((inb(ioaddr+DAYNA_CARD_STATUS)&3)==1) + break; + } + + if(i==65536) + return; + } + + /* + * Upload the firmware and kick. Byte-by-byte works nicely here. + */ + i=0; + length = ltf->length; + while(length--) + { + outb(ltf->data[i], ioaddr); + i++; + } + + if(cops_debug > 1) + printk("%s: Uploaded firmware - %d bytes of %d bytes.\n", + dev->name, i, ltf->length); + + if(lp->board==DAYNA) /* Tell Dayna to run the firmware code. */ + outb(1, ioaddr+DAYNA_INT_CARD); + else /* Tell Tang to run the firmware code. */ + inb(ioaddr); + + if(lp->board==TANGENT) + { + tangent_wait_reset(ioaddr); + inb(ioaddr); /* Clear initial ready signal. */ + } +} + +/* + * Get the LocalTalk Nodeid from the card. We can suggest + * any nodeid 1-254. The card will try and get that exact + * address else we can specify 0 as the nodeid and the card + * will autoprobe for a nodeid. + */ +static int cops_nodeid (struct net_device *dev, int nodeid) +{ + struct cops_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + + if(lp->board == DAYNA) + { + /* Empty any pending adapter responses. */ + while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0) + { + outb(0, ioaddr+COPS_CLEAR_INT); /* Clear interrupts. */ + if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST) + cops_rx(dev); /* Kick any packets waiting. */ + schedule(); + } + + outb(2, ioaddr); /* Output command packet length as 2. */ + outb(0, ioaddr); + outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */ + outb(nodeid, ioaddr); /* Suggest node address. */ + } + + if(lp->board == TANGENT) + { + /* Empty any pending adapter responses. */ + while(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY) + { + outb(0, ioaddr+COPS_CLEAR_INT); /* Clear interrupt. */ + cops_rx(dev); /* Kick out packets waiting. */ + schedule(); + } + + /* Not sure what Tangent does if nodeid picked is used. */ + if(nodeid == 0) /* Seed. */ + nodeid = jiffies&0xFF; /* Get a random try */ + outb(2, ioaddr); /* Command length LSB */ + outb(0, ioaddr); /* Command length MSB */ + outb(LAP_INIT, ioaddr); /* Send LAP_INIT byte */ + outb(nodeid, ioaddr); /* LAP address hint. */ + outb(0xFF, ioaddr); /* Int. level to use */ + } + + lp->node_acquire=0; /* Set nodeid holder to 0. */ + while(lp->node_acquire==0) /* Get *True* nodeid finally. */ + { + outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */ + + if(lp->board == DAYNA) + { + if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST) + cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */ + } + if(lp->board == TANGENT) + { + if(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY) + cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */ + } + schedule(); + } + + if(cops_debug > 1) + printk(KERN_DEBUG "%s: Node ID %d has been acquired.\n", + dev->name, lp->node_acquire); + + lp->nodeid=1; /* Set got nodeid to 1. */ + + return 0; +} + +/* + * Poll the Tangent type cards to see if we have work. + */ + +static void cops_poll(unsigned long ltdev) +{ + int ioaddr, status; + int boguscount = 0; + + struct net_device *dev = (struct net_device *)ltdev; + + del_timer(&cops_timer); + + if(dev == NULL) + return; /* We've been downed */ + + ioaddr = dev->base_addr; + do { + status=inb(ioaddr+TANG_CARD_STATUS); + if(status & TANG_RX_READY) + cops_rx(dev); + if(status & TANG_TX_READY) + netif_wake_queue(dev); + status = inb(ioaddr+TANG_CARD_STATUS); + } while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY))); + + /* poll 20 times per second */ + cops_timer.expires = jiffies + HZ/20; + add_timer(&cops_timer); +} + +/* + * The typical workload of the driver: + * Handle the network interface interrupts. + */ +static irqreturn_t cops_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct cops_local *lp; + int ioaddr, status; + int boguscount = 0; + + ioaddr = dev->base_addr; + lp = netdev_priv(dev); + + if(lp->board==DAYNA) + { + do { + outb(0, ioaddr + COPS_CLEAR_INT); + status=inb(ioaddr+DAYNA_CARD_STATUS); + if((status&0x03)==DAYNA_RX_REQUEST) + cops_rx(dev); + netif_wake_queue(dev); + } while(++boguscount < 20); + } + else + { + do { + status=inb(ioaddr+TANG_CARD_STATUS); + if(status & TANG_RX_READY) + cops_rx(dev); + if(status & TANG_TX_READY) + netif_wake_queue(dev); + status=inb(ioaddr+TANG_CARD_STATUS); + } while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY))); + } + + return IRQ_HANDLED; +} + +/* + * We have a good packet(s), get it/them out of the buffers. + */ +static void cops_rx(struct net_device *dev) +{ + int pkt_len = 0; + int rsp_type = 0; + struct sk_buff *skb = NULL; + struct cops_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + int boguscount = 0; + unsigned long flags; + + + spin_lock_irqsave(&lp->lock, flags); + + if(lp->board==DAYNA) + { + outb(0, ioaddr); /* Send out Zero length. */ + outb(0, ioaddr); + outb(DATA_READ, ioaddr); /* Send read command out. */ + + /* Wait for DMA to turn around. */ + while(++boguscount<1000000) + { + barrier(); + if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_READY) + break; + } + + if(boguscount==1000000) + { + printk(KERN_WARNING "%s: DMA timed out.\n",dev->name); + spin_unlock_irqrestore(&lp->lock, flags); + return; + } + } + + /* Get response length. */ + if(lp->board==DAYNA) + pkt_len = inb(ioaddr) & 0xFF; + else + pkt_len = inb(ioaddr) & 0x00FF; + pkt_len |= (inb(ioaddr) << 8); + /* Input IO code. */ + rsp_type=inb(ioaddr); + + /* Malloc up new buffer. */ + skb = dev_alloc_skb(pkt_len); + if(skb == NULL) + { + printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", + dev->name); + dev->stats.rx_dropped++; + while(pkt_len--) /* Discard packet */ + inb(ioaddr); + spin_unlock_irqrestore(&lp->lock, flags); + return; + } + skb->dev = dev; + skb_put(skb, pkt_len); + skb->protocol = htons(ETH_P_LOCALTALK); + + insb(ioaddr, skb->data, pkt_len); /* Eat the Data */ + + if(lp->board==DAYNA) + outb(1, ioaddr+DAYNA_INT_CARD); /* Interrupt the card */ + + spin_unlock_irqrestore(&lp->lock, flags); /* Restore interrupts. */ + + /* Check for bad response length */ + if(pkt_len < 0 || pkt_len > MAX_LLAP_SIZE) + { + printk(KERN_WARNING "%s: Bad packet length of %d bytes.\n", + dev->name, pkt_len); + dev->stats.tx_errors++; + dev_kfree_skb_any(skb); + return; + } + + /* Set nodeid and then get out. */ + if(rsp_type == LAP_INIT_RSP) + { /* Nodeid taken from received packet. */ + lp->node_acquire = skb->data[0]; + dev_kfree_skb_any(skb); + return; + } + + /* One last check to make sure we have a good packet. */ + if(rsp_type != LAP_RESPONSE) + { + printk(KERN_WARNING "%s: Bad packet type %d.\n", dev->name, rsp_type); + dev->stats.tx_errors++; + dev_kfree_skb_any(skb); + return; + } + + skb_reset_mac_header(skb); /* Point to entire packet. */ + skb_pull(skb,3); + skb_reset_transport_header(skb); /* Point to data (Skip header). */ + + /* Update the counters. */ + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + + /* Send packet to a higher place. */ + netif_rx(skb); +} + +static void cops_timeout(struct net_device *dev) +{ + struct cops_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + + dev->stats.tx_errors++; + if(lp->board==TANGENT) + { + if((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) + printk(KERN_WARNING "%s: No TX complete interrupt.\n", dev->name); + } + printk(KERN_WARNING "%s: Transmit timed out.\n", dev->name); + cops_jumpstart(dev); /* Restart the card. */ + dev->trans_start = jiffies; /* prevent tx timeout */ + netif_wake_queue(dev); +} + + +/* + * Make the card transmit a LocalTalk packet. + */ + +static netdev_tx_t cops_send_packet(struct sk_buff *skb, + struct net_device *dev) +{ + struct cops_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + unsigned long flags; + + /* + * Block a timer-based transmit from overlapping. + */ + + netif_stop_queue(dev); + + spin_lock_irqsave(&lp->lock, flags); + if(lp->board == DAYNA) /* Wait for adapter transmit buffer. */ + while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0) + cpu_relax(); + if(lp->board == TANGENT) /* Wait for adapter transmit buffer. */ + while((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) + cpu_relax(); + + /* Output IO length. */ + outb(skb->len, ioaddr); + if(lp->board == DAYNA) + outb(skb->len >> 8, ioaddr); + else + outb((skb->len >> 8)&0x0FF, ioaddr); + + /* Output IO code. */ + outb(LAP_WRITE, ioaddr); + + if(lp->board == DAYNA) /* Check the transmit buffer again. */ + while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0); + + outsb(ioaddr, skb->data, skb->len); /* Send out the data. */ + + if(lp->board==DAYNA) /* Dayna requires you kick the card */ + outb(1, ioaddr+DAYNA_INT_CARD); + + spin_unlock_irqrestore(&lp->lock, flags); /* Restore interrupts. */ + + /* Done sending packet, update counters and cleanup. */ + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + dev_kfree_skb (skb); + return NETDEV_TX_OK; +} + +/* + * Dummy function to keep the Appletalk layer happy. + */ + +static void set_multicast_list(struct net_device *dev) +{ + if(cops_debug >= 3) + printk("%s: set_multicast_list executed\n", dev->name); +} + +/* + * System ioctls for the COPS LocalTalk card. + */ + +static int cops_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct cops_local *lp = netdev_priv(dev); + struct sockaddr_at *sa = (struct sockaddr_at *)&ifr->ifr_addr; + struct atalk_addr *aa = (struct atalk_addr *)&lp->node_addr; + + switch(cmd) + { + case SIOCSIFADDR: + /* Get and set the nodeid and network # atalkd wants. */ + cops_nodeid(dev, sa->sat_addr.s_node); + aa->s_net = sa->sat_addr.s_net; + aa->s_node = lp->node_acquire; + + /* Set broardcast address. */ + dev->broadcast[0] = 0xFF; + + /* Set hardware address. */ + dev->dev_addr[0] = aa->s_node; + dev->addr_len = 1; + return 0; + + case SIOCGIFADDR: + sa->sat_addr.s_net = aa->s_net; + sa->sat_addr.s_node = aa->s_node; + return 0; + + default: + return -EOPNOTSUPP; + } +} + +/* + * The inverse routine to cops_open(). + */ + +static int cops_close(struct net_device *dev) +{ + struct cops_local *lp = netdev_priv(dev); + + /* If we were running polled, yank the timer. + */ + if(lp->board==TANGENT && dev->irq==0) + del_timer(&cops_timer); + + netif_stop_queue(dev); + return 0; +} + + +#ifdef MODULE +static struct net_device *cops_dev; + +MODULE_LICENSE("GPL"); +module_param(io, int, 0); +module_param(irq, int, 0); +module_param(board_type, int, 0); + +static int __init cops_module_init(void) +{ + if (io == 0) + printk(KERN_WARNING "%s: You shouldn't autoprobe with insmod\n", + cardname); + cops_dev = cops_probe(-1); + if (IS_ERR(cops_dev)) + return PTR_ERR(cops_dev); + return 0; +} + +static void __exit cops_module_exit(void) +{ + unregister_netdev(cops_dev); + cleanup_card(cops_dev); + free_netdev(cops_dev); +} +module_init(cops_module_init); +module_exit(cops_module_exit); +#endif /* MODULE */ diff --git a/drivers/staging/appletalk/cops.h b/drivers/staging/appletalk/cops.h new file mode 100644 index 000000000000..fd2750b269c8 --- /dev/null +++ b/drivers/staging/appletalk/cops.h @@ -0,0 +1,60 @@ +/* cops.h: LocalTalk driver for Linux. + * + * Authors: + * - Jay Schulist + */ + +#ifndef __LINUX_COPSLTALK_H +#define __LINUX_COPSLTALK_H + +#ifdef __KERNEL__ + +/* Max LLAP size we will accept. */ +#define MAX_LLAP_SIZE 603 + +/* Tangent */ +#define TANG_CARD_STATUS 1 +#define TANG_CLEAR_INT 1 +#define TANG_RESET 3 + +#define TANG_TX_READY 1 +#define TANG_RX_READY 2 + +/* Dayna */ +#define DAYNA_CMD_DATA 0 +#define DAYNA_CLEAR_INT 1 +#define DAYNA_CARD_STATUS 2 +#define DAYNA_INT_CARD 3 +#define DAYNA_RESET 4 + +#define DAYNA_RX_READY 0 +#define DAYNA_TX_READY 1 +#define DAYNA_RX_REQUEST 3 + +/* Same on both card types */ +#define COPS_CLEAR_INT 1 + +/* LAP response codes received from the cards. */ +#define LAP_INIT 1 /* Init cmd */ +#define LAP_INIT_RSP 2 /* Init response */ +#define LAP_WRITE 3 /* Write cmd */ +#define DATA_READ 4 /* Data read */ +#define LAP_RESPONSE 4 /* Received ALAP frame response */ +#define LAP_GETSTAT 5 /* Get LAP and HW status */ +#define LAP_RSPSTAT 6 /* Status response */ + +#endif + +/* + * Structure to hold the firmware information. + */ +struct ltfirmware +{ + unsigned int length; + const unsigned char *data; +}; + +#define DAYNA 1 +#define TANGENT 2 + +#endif diff --git a/drivers/staging/appletalk/cops_ffdrv.h b/drivers/staging/appletalk/cops_ffdrv.h new file mode 100644 index 000000000000..b02005087c1b --- /dev/null +++ b/drivers/staging/appletalk/cops_ffdrv.h @@ -0,0 +1,532 @@ + +/* + * The firmware this driver downloads into the Localtalk card is a + * separate program and is not GPL'd source code, even though the Linux + * side driver and the routine that loads this data into the card are. + * + * It is taken from the COPS SDK and is under the following license + * + * This material is licensed to you strictly for use in conjunction with + * the use of COPS LocalTalk adapters. + * There is no charge for this SDK. And no waranty express or implied + * about its fitness for any purpose. However, we will cheerefully + * refund every penny you paid for this SDK... + * Regards, + * + * Thomas F. Divine + * Chief Scientist + */ + + +/* cops_ffdrv.h: LocalTalk driver firmware dump for Linux. + * + * Authors: + * - Jay Schulist + */ + + +#ifdef CONFIG_COPS_DAYNA + +static const unsigned char ffdrv_code[] = { + 58,3,0,50,228,149,33,255,255,34,226,149, + 249,17,40,152,33,202,154,183,237,82,77,68, + 11,107,98,19,54,0,237,176,175,50,80,0, + 62,128,237,71,62,32,237,57,51,62,12,237, + 57,50,237,57,54,62,6,237,57,52,62,12, + 237,57,49,33,107,137,34,32,128,33,83,130, + 34,40,128,33,86,130,34,42,128,33,112,130, + 34,36,128,33,211,130,34,38,128,62,0,237, + 57,16,33,63,148,34,34,128,237,94,205,15, + 130,251,205,168,145,24,141,67,111,112,121,114, + 105,103,104,116,32,40,67,41,32,49,57,56, + 56,32,45,32,68,97,121,110,97,32,67,111, + 109,109,117,110,105,99,97,116,105,111,110,115, + 32,32,32,65,108,108,32,114,105,103,104,116, + 115,32,114,101,115,101,114,118,101,100,46,32, + 32,40,68,40,68,7,16,8,34,7,22,6, + 16,5,12,4,8,3,6,140,0,16,39,128, + 0,4,96,10,224,6,0,7,126,2,64,11, + 118,12,6,13,0,14,193,15,0,5,96,3, + 192,1,64,9,8,62,9,211,66,62,192,211, + 66,62,100,61,32,253,6,28,33,205,129,14, + 66,237,163,194,253,129,6,28,33,205,129,14, + 64,237,163,194,9,130,201,62,47,50,71,152, + 62,47,211,68,58,203,129,237,57,20,58,204, + 129,237,57,21,33,77,152,54,132,205,233,129, + 58,228,149,254,209,40,6,56,4,62,0,24, + 2,219,96,33,233,149,119,230,62,33,232,149, + 119,213,33,8,152,17,7,0,25,119,19,25, + 119,209,201,251,237,77,245,197,213,229,221,229, + 205,233,129,62,1,50,106,137,205,158,139,221, + 225,225,209,193,241,251,237,77,245,197,213,219, + 72,237,56,16,230,46,237,57,16,237,56,12, + 58,72,152,183,32,26,6,20,17,128,2,237, + 56,46,187,32,35,237,56,47,186,32,29,219, + 72,230,1,32,3,5,32,232,175,50,72,152, + 229,221,229,62,1,50,106,137,205,158,139,221, + 225,225,24,25,62,1,50,72,152,58,201,129, + 237,57,12,58,202,129,237,57,13,237,56,16, + 246,17,237,57,16,209,193,241,251,237,77,245, + 197,229,213,221,229,237,56,16,230,17,237,57, + 16,237,56,20,58,34,152,246,16,246,8,211, + 68,62,6,61,32,253,58,34,152,246,8,211, + 68,58,203,129,237,57,20,58,204,129,237,57, + 21,237,56,16,246,34,237,57,16,221,225,209, + 225,193,241,251,237,77,33,2,0,57,126,230, + 3,237,100,1,40,2,246,128,230,130,245,62, + 5,211,64,241,211,64,201,229,213,243,237,56, + 16,230,46,237,57,16,237,56,12,251,70,35, + 35,126,254,175,202,77,133,254,129,202,15,133, + 230,128,194,191,132,43,58,44,152,119,33,76, + 152,119,35,62,132,119,120,254,255,40,4,58, + 49,152,119,219,72,43,43,112,17,3,0,237, + 56,52,230,248,237,57,52,219,72,230,1,194, + 141,131,209,225,237,56,52,246,6,237,57,52, + 62,1,55,251,201,62,3,211,66,62,192,211, + 66,62,48,211,66,0,0,219,66,230,1,40, + 4,219,67,24,240,205,203,135,58,75,152,254, + 255,202,128,132,58,49,152,254,161,250,207,131, + 58,34,152,211,68,62,10,211,66,62,128,211, + 66,62,11,211,66,62,6,211,66,24,0,62, + 14,211,66,62,33,211,66,62,1,211,66,62, + 64,211,66,62,3,211,66,62,209,211,66,62, + 100,71,219,66,230,1,32,6,5,32,247,195, + 248,132,219,67,71,58,44,152,184,194,248,132, + 62,100,71,219,66,230,1,32,6,5,32,247, + 195,248,132,219,67,62,100,71,219,66,230,1, + 32,6,5,32,247,195,248,132,219,67,254,133, + 32,7,62,0,50,74,152,24,17,254,173,32, + 7,62,1,50,74,152,24,6,254,141,194,248, + 132,71,209,225,58,49,152,254,132,32,10,62, + 50,205,2,134,205,144,135,24,27,254,140,32, + 15,62,110,205,2,134,62,141,184,32,5,205, + 144,135,24,8,62,10,205,2,134,205,8,134, + 62,1,50,106,137,205,158,139,237,56,52,246, + 6,237,57,52,175,183,251,201,62,20,135,237, + 57,20,175,237,57,21,237,56,16,246,2,237, + 57,16,237,56,20,95,237,56,21,123,254,10, + 48,244,237,56,16,230,17,237,57,16,209,225, + 205,144,135,62,1,50,106,137,205,158,139,237, + 56,52,246,6,237,57,52,175,183,251,201,209, + 225,243,219,72,230,1,40,13,62,10,211,66, + 0,0,219,66,230,192,202,226,132,237,56,52, + 246,6,237,57,52,62,1,55,251,201,205,203, + 135,62,1,50,106,137,205,158,139,237,56,52, + 246,6,237,57,52,183,251,201,209,225,62,1, + 50,106,137,205,158,139,237,56,52,246,6,237, + 57,52,62,2,55,251,201,209,225,243,219,72, + 230,1,202,213,132,62,10,211,66,0,0,219, + 66,230,192,194,213,132,229,62,1,50,106,137, + 42,40,152,205,65,143,225,17,3,0,205,111, + 136,62,6,211,66,58,44,152,211,66,237,56, + 52,246,6,237,57,52,183,251,201,209,197,237, + 56,52,230,248,237,57,52,219,72,230,1,32, + 15,193,225,237,56,52,246,6,237,57,52,62, + 1,55,251,201,14,23,58,37,152,254,0,40, + 14,14,2,254,1,32,5,62,140,119,24,3, + 62,132,119,43,43,197,205,203,135,193,62,1, + 211,66,62,64,211,66,62,3,211,66,62,193, + 211,66,62,100,203,39,71,219,66,230,1,32, + 6,5,32,247,195,229,133,33,238,151,219,67, + 71,58,44,152,184,194,229,133,119,62,100,71, + 219,66,230,1,32,6,5,32,247,195,229,133, + 219,67,35,119,13,32,234,193,225,62,1,50, + 106,137,205,158,139,237,56,52,246,6,237,57, + 52,175,183,251,201,33,234,151,35,35,62,255, + 119,193,225,62,1,50,106,137,205,158,139,237, + 56,52,246,6,237,57,52,175,251,201,243,61, + 32,253,251,201,62,3,211,66,62,192,211,66, + 58,49,152,254,140,32,19,197,229,213,17,181, + 129,33,185,129,1,2,0,237,176,209,225,193, + 24,27,229,213,33,187,129,58,49,152,230,15, + 87,30,2,237,92,25,17,181,129,126,18,19, + 35,126,18,209,225,58,34,152,246,8,211,68, + 58,49,152,254,165,40,14,254,164,40,10,62, + 10,211,66,62,224,211,66,24,25,58,74,152, + 254,0,40,10,62,10,211,66,62,160,211,66, + 24,8,62,10,211,66,62,128,211,66,62,11, + 211,66,62,6,211,66,205,147,143,62,5,211, + 66,62,224,211,66,62,5,211,66,62,96,211, + 66,62,5,61,32,253,62,5,211,66,62,224, + 211,66,62,14,61,32,253,62,5,211,66,62, + 233,211,66,62,128,211,66,58,181,129,61,32, + 253,62,1,211,66,62,192,211,66,1,254,19, + 237,56,46,187,32,6,13,32,247,195,226,134, + 62,192,211,66,0,0,219,66,203,119,40,250, + 219,66,203,87,40,250,243,237,56,16,230,17, + 237,57,16,237,56,20,251,62,5,211,66,62, + 224,211,66,58,182,129,61,32,253,229,33,181, + 129,58,183,129,203,63,119,35,58,184,129,119, + 225,62,10,211,66,62,224,211,66,62,11,211, + 66,62,118,211,66,62,47,211,68,62,5,211, + 66,62,233,211,66,58,181,129,61,32,253,62, + 5,211,66,62,224,211,66,58,182,129,61,32, + 253,62,5,211,66,62,96,211,66,201,229,213, + 58,50,152,230,15,87,30,2,237,92,33,187, + 129,25,17,181,129,126,18,35,19,126,18,209, + 225,58,71,152,246,8,211,68,58,50,152,254, + 165,40,14,254,164,40,10,62,10,211,66,62, + 224,211,66,24,8,62,10,211,66,62,128,211, + 66,62,11,211,66,62,6,211,66,195,248,135, + 62,3,211,66,62,192,211,66,197,229,213,17, + 181,129,33,183,129,1,2,0,237,176,209,225, + 193,62,47,211,68,62,10,211,66,62,224,211, + 66,62,11,211,66,62,118,211,66,62,1,211, + 66,62,0,211,66,205,147,143,195,16,136,62, + 3,211,66,62,192,211,66,197,229,213,17,181, + 129,33,183,129,1,2,0,237,176,209,225,193, + 62,47,211,68,62,10,211,66,62,224,211,66, + 62,11,211,66,62,118,211,66,205,147,143,62, + 5,211,66,62,224,211,66,62,5,211,66,62, + 96,211,66,62,5,61,32,253,62,5,211,66, + 62,224,211,66,62,14,61,32,253,62,5,211, + 66,62,233,211,66,62,128,211,66,58,181,129, + 61,32,253,62,1,211,66,62,192,211,66,1, + 254,19,237,56,46,187,32,6,13,32,247,195, + 88,136,62,192,211,66,0,0,219,66,203,119, + 40,250,219,66,203,87,40,250,62,5,211,66, + 62,224,211,66,58,182,129,61,32,253,62,5, + 211,66,62,96,211,66,201,197,14,67,6,0, + 62,3,211,66,62,192,211,66,62,48,211,66, + 0,0,219,66,230,1,40,4,219,67,24,240, + 62,5,211,66,62,233,211,66,62,128,211,66, + 58,181,129,61,32,253,237,163,29,62,192,211, + 66,219,66,230,4,40,250,237,163,29,32,245, + 219,66,230,4,40,250,62,255,71,219,66,230, + 4,40,3,5,32,247,219,66,230,4,40,250, + 62,5,211,66,62,224,211,66,58,182,129,61, + 32,253,62,5,211,66,62,96,211,66,58,71, + 152,254,1,202,18,137,62,16,211,66,62,56, + 211,66,62,14,211,66,62,33,211,66,62,1, + 211,66,62,248,211,66,237,56,48,246,153,230, + 207,237,57,48,62,3,211,66,62,221,211,66, + 193,201,58,71,152,211,68,62,10,211,66,62, + 128,211,66,62,11,211,66,62,6,211,66,62, + 6,211,66,58,44,152,211,66,62,16,211,66, + 62,56,211,66,62,48,211,66,0,0,62,14, + 211,66,62,33,211,66,62,1,211,66,62,248, + 211,66,237,56,48,246,145,246,8,230,207,237, + 57,48,62,3,211,66,62,221,211,66,193,201, + 44,3,1,0,70,69,1,245,197,213,229,175, + 50,72,152,237,56,16,230,46,237,57,16,237, + 56,12,62,1,211,66,0,0,219,66,95,230, + 160,32,3,195,20,139,123,230,96,194,72,139, + 62,48,211,66,62,1,211,66,62,64,211,66, + 237,91,40,152,205,207,143,25,43,55,237,82, + 218,70,139,34,42,152,98,107,58,44,152,190, + 194,210,138,35,35,62,130,190,194,200,137,62, + 1,50,48,152,62,175,190,202,82,139,62,132, + 190,32,44,50,50,152,62,47,50,71,152,229, + 175,50,106,137,42,40,152,205,65,143,225,54, + 133,43,70,58,44,152,119,43,112,17,3,0, + 62,10,205,2,134,205,111,136,195,158,138,62, + 140,190,32,19,50,50,152,58,233,149,230,4, + 202,222,138,62,1,50,71,152,195,219,137,126, + 254,160,250,185,138,254,166,242,185,138,50,50, + 152,43,126,35,229,213,33,234,149,95,22,0, + 25,126,254,132,40,18,254,140,40,14,58,50, + 152,230,15,87,126,31,21,242,65,138,56,2, + 175,119,58,50,152,230,15,87,58,233,149,230, + 62,31,21,242,85,138,218,98,138,209,225,195, + 20,139,58,50,152,33,100,137,230,15,95,22, + 0,25,126,50,71,152,209,225,58,50,152,254, + 164,250,135,138,58,73,152,254,0,40,4,54, + 173,24,2,54,133,43,70,58,44,152,119,43, + 112,17,3,0,205,70,135,175,50,106,137,205, + 208,139,58,199,129,237,57,12,58,200,129,237, + 57,13,237,56,16,246,17,237,57,16,225,209, + 193,241,251,237,77,62,129,190,194,227,138,54, + 130,43,70,58,44,152,119,43,112,17,3,0, + 205,144,135,195,20,139,35,35,126,254,132,194, + 227,138,175,50,106,137,205,158,139,24,42,58, + 201,154,254,1,40,7,62,1,50,106,137,24, + 237,58,106,137,254,1,202,222,138,62,128,166, + 194,222,138,221,229,221,33,67,152,205,127,142, + 205,109,144,221,225,225,209,193,241,251,237,77, + 58,106,137,254,1,202,44,139,58,50,152,254, + 164,250,44,139,58,73,152,238,1,50,73,152, + 221,229,221,33,51,152,205,127,142,221,225,62, + 1,50,106,137,205,158,139,195,13,139,24,208, + 24,206,24,204,230,64,40,3,195,20,139,195, + 20,139,43,126,33,8,152,119,35,58,44,152, + 119,43,237,91,35,152,205,203,135,205,158,139, + 195,13,139,175,50,78,152,62,3,211,66,62, + 192,211,66,201,197,33,4,0,57,126,35,102, + 111,62,1,50,106,137,219,72,205,141,139,193, + 201,62,1,50,78,152,34,40,152,54,0,35, + 35,54,0,195,163,139,58,78,152,183,200,229, + 33,181,129,58,183,129,119,35,58,184,129,119, + 225,62,47,211,68,62,14,211,66,62,193,211, + 66,62,10,211,66,62,224,211,66,62,11,211, + 66,62,118,211,66,195,3,140,58,78,152,183, + 200,58,71,152,211,68,254,69,40,4,254,70, + 32,17,58,73,152,254,0,40,10,62,10,211, + 66,62,160,211,66,24,8,62,10,211,66,62, + 128,211,66,62,11,211,66,62,6,211,66,62, + 6,211,66,58,44,152,211,66,62,16,211,66, + 62,56,211,66,62,48,211,66,0,0,219,66, + 230,1,40,4,219,67,24,240,62,14,211,66, + 62,33,211,66,42,40,152,205,65,143,62,1, + 211,66,62,248,211,66,237,56,48,246,145,246, + 8,230,207,237,57,48,62,3,211,66,62,221, + 211,66,201,62,16,211,66,62,56,211,66,62, + 48,211,66,0,0,219,66,230,1,40,4,219, + 67,24,240,62,14,211,66,62,33,211,66,62, + 1,211,66,62,248,211,66,237,56,48,246,153, + 230,207,237,57,48,62,3,211,66,62,221,211, + 66,201,229,213,33,234,149,95,22,0,25,126, + 254,132,40,4,254,140,32,2,175,119,123,209, + 225,201,6,8,14,0,31,48,1,12,16,250, + 121,201,33,4,0,57,94,35,86,33,2,0, + 57,126,35,102,111,221,229,34,89,152,237,83, + 91,152,221,33,63,152,205,127,142,58,81,152, + 50,82,152,58,80,152,135,50,80,152,205,162, + 140,254,3,56,16,58,81,152,135,60,230,15, + 50,81,152,175,50,80,152,24,23,58,79,152, + 205,162,140,254,3,48,13,58,81,152,203,63, + 50,81,152,62,255,50,79,152,58,81,152,50, + 82,152,58,79,152,135,50,79,152,62,32,50, + 83,152,50,84,152,237,56,16,230,17,237,57, + 16,219,72,62,192,50,93,152,62,93,50,94, + 152,58,93,152,61,50,93,152,32,9,58,94, + 152,61,50,94,152,40,44,62,170,237,57,20, + 175,237,57,21,237,56,16,246,2,237,57,16, + 219,72,230,1,202,29,141,237,56,20,71,237, + 56,21,120,254,10,48,237,237,56,16,230,17, + 237,57,16,243,62,14,211,66,62,65,211,66, + 251,58,39,152,23,23,60,50,39,152,71,58, + 82,152,160,230,15,40,22,71,14,10,219,66, + 230,16,202,186,141,219,72,230,1,202,186,141, + 13,32,239,16,235,42,89,152,237,91,91,152, + 205,47,131,48,7,61,202,186,141,195,227,141, + 221,225,33,0,0,201,221,33,55,152,205,127, + 142,58,84,152,61,50,84,152,40,19,58,82, + 152,246,1,50,82,152,58,79,152,246,1,50, + 79,152,195,29,141,221,225,33,1,0,201,221, + 33,59,152,205,127,142,58,80,152,246,1,50, + 80,152,58,82,152,135,246,1,50,82,152,58, + 83,152,61,50,83,152,194,29,141,221,225,33, + 2,0,201,221,229,33,0,0,57,17,4,0, + 25,126,50,44,152,230,128,50,85,152,58,85, + 152,183,40,6,221,33,88,2,24,4,221,33, + 150,0,58,44,152,183,40,53,60,40,50,60, + 40,47,61,61,33,86,152,119,35,119,35,54, + 129,175,50,48,152,221,43,221,229,225,124,181, + 40,42,33,86,152,17,3,0,205,189,140,17, + 232,3,27,123,178,32,251,58,48,152,183,40, + 224,58,44,152,71,62,7,128,230,127,71,58, + 85,152,176,50,44,152,24,162,221,225,201,183, + 221,52,0,192,221,52,1,192,221,52,2,192, + 221,52,3,192,55,201,245,62,1,211,100,241, + 201,245,62,1,211,96,241,201,33,2,0,57, + 126,35,102,111,237,56,48,230,175,237,57,48, + 62,48,237,57,49,125,237,57,32,124,237,57, + 33,62,0,237,57,34,62,88,237,57,35,62, + 0,237,57,36,237,57,37,33,128,2,125,237, + 57,38,124,237,57,39,237,56,48,246,97,230, + 207,237,57,48,62,0,237,57,0,62,0,211, + 96,211,100,201,33,2,0,57,126,35,102,111, + 237,56,48,230,175,237,57,48,62,12,237,57, + 49,62,76,237,57,32,62,0,237,57,33,237, + 57,34,125,237,57,35,124,237,57,36,62,0, + 237,57,37,33,128,2,125,237,57,38,124,237, + 57,39,237,56,48,246,97,230,207,237,57,48, + 62,1,211,96,201,33,2,0,57,126,35,102, + 111,229,237,56,48,230,87,237,57,48,125,237, + 57,40,124,237,57,41,62,0,237,57,42,62, + 67,237,57,43,62,0,237,57,44,58,106,137, + 254,1,32,5,33,6,0,24,3,33,128,2, + 125,237,57,46,124,237,57,47,237,56,50,230, + 252,246,2,237,57,50,225,201,33,4,0,57, + 94,35,86,33,2,0,57,126,35,102,111,237, + 56,48,230,87,237,57,48,125,237,57,40,124, + 237,57,41,62,0,237,57,42,62,67,237,57, + 43,62,0,237,57,44,123,237,57,46,122,237, + 57,47,237,56,50,230,244,246,0,237,57,50, + 237,56,48,246,145,230,207,237,57,48,201,213, + 237,56,46,95,237,56,47,87,237,56,46,111, + 237,56,47,103,183,237,82,32,235,33,128,2, + 183,237,82,209,201,213,237,56,38,95,237,56, + 39,87,237,56,38,111,237,56,39,103,183,237, + 82,32,235,33,128,2,183,237,82,209,201,245, + 197,1,52,0,237,120,230,253,237,121,193,241, + 201,245,197,1,52,0,237,120,246,2,237,121, + 193,241,201,33,2,0,57,126,35,102,111,126, + 35,110,103,201,33,0,0,34,102,152,34,96, + 152,34,98,152,33,202,154,34,104,152,237,91, + 104,152,42,226,149,183,237,82,17,0,255,25, + 34,100,152,203,124,40,6,33,0,125,34,100, + 152,42,104,152,35,35,35,229,205,120,139,193, + 201,205,186,149,229,42,40,152,35,35,35,229, + 205,39,144,193,124,230,3,103,221,117,254,221, + 116,255,237,91,42,152,35,35,35,183,237,82, + 32,12,17,5,0,42,42,152,205,171,149,242, + 169,144,42,40,152,229,205,120,139,193,195,198, + 149,237,91,42,152,42,98,152,25,34,98,152, + 19,19,19,42,102,152,25,34,102,152,237,91, + 100,152,33,158,253,25,237,91,102,152,205,171, + 149,242,214,144,33,0,0,34,102,152,62,1, + 50,95,152,205,225,144,195,198,149,58,95,152, + 183,200,237,91,96,152,42,102,152,205,171,149, + 242,5,145,237,91,102,152,33,98,2,25,237, + 91,96,152,205,171,149,250,37,145,237,91,96, + 152,42,102,152,183,237,82,32,7,42,98,152, + 125,180,40,13,237,91,102,152,42,96,152,205, + 171,149,242,58,145,237,91,104,152,42,102,152, + 25,35,35,35,229,205,120,139,193,175,50,95, + 152,201,195,107,139,205,206,149,250,255,243,205, + 225,144,251,58,230,149,183,194,198,149,17,1, + 0,42,98,152,205,171,149,250,198,149,62,1, + 50,230,149,237,91,96,152,42,104,152,25,221, + 117,252,221,116,253,237,91,104,152,42,96,152, + 25,35,35,35,221,117,254,221,116,255,35,35, + 35,229,205,39,144,124,230,3,103,35,35,35, + 221,117,250,221,116,251,235,221,110,252,221,102, + 253,115,35,114,35,54,4,62,1,211,100,211, + 84,195,198,149,33,0,0,34,102,152,34,96, + 152,34,98,152,33,202,154,34,104,152,237,91, + 104,152,42,226,149,183,237,82,17,0,255,25, + 34,100,152,33,109,152,54,0,33,107,152,229, + 205,240,142,193,62,47,50,34,152,62,132,50, + 49,152,205,241,145,205,61,145,58,39,152,60, + 50,39,152,24,241,205,206,149,251,255,33,109, + 152,126,183,202,198,149,110,221,117,251,33,109, + 152,54,0,221,126,251,254,1,40,28,254,3, + 40,101,254,4,202,190,147,254,5,202,147,147, + 254,8,40,87,33,107,152,229,205,240,142,195, + 198,149,58,201,154,183,32,21,33,111,152,126, + 50,229,149,205,52,144,33,110,152,110,38,0, + 229,205,11,142,193,237,91,96,152,42,104,152, + 25,221,117,254,221,116,255,35,35,54,2,17, + 2,0,43,43,115,35,114,58,44,152,35,35, + 119,58,228,149,35,119,62,1,211,100,211,84, + 62,1,50,201,154,24,169,205,153,142,58,231, + 149,183,40,250,175,50,231,149,33,110,152,126, + 254,255,40,91,58,233,149,230,63,183,40,83, + 94,22,0,33,234,149,25,126,183,40,13,33, + 110,152,94,33,234,150,25,126,254,3,32,36, + 205,81,148,125,180,33,110,152,94,22,0,40, + 17,33,234,149,25,54,0,33,107,152,229,205, + 240,142,193,195,198,149,33,234,150,25,54,0, + 33,110,152,94,22,0,33,234,149,25,126,50, + 49,152,254,132,32,37,62,47,50,34,152,42, + 107,152,229,33,110,152,229,205,174,140,193,193, + 125,180,33,110,152,94,22,0,33,234,150,202, + 117,147,25,52,195,120,147,58,49,152,254,140, + 32,7,62,1,50,34,152,24,210,62,32,50, + 106,152,24,19,58,49,152,95,58,106,152,163, + 183,58,106,152,32,11,203,63,50,106,152,58, + 106,152,183,32,231,254,2,40,51,254,4,40, + 38,254,8,40,26,254,16,40,13,254,32,32, + 158,62,165,50,49,152,62,69,24,190,62,164, + 50,49,152,62,70,24,181,62,163,50,49,152, + 175,24,173,62,162,50,49,152,62,1,24,164, + 62,161,50,49,152,62,3,24,155,25,54,0, + 221,126,251,254,8,40,7,58,230,149,183,202, + 32,146,33,107,152,229,205,240,142,193,211,84, + 195,198,149,237,91,96,152,42,104,152,25,221, + 117,254,221,116,255,35,35,54,6,17,2,0, + 43,43,115,35,114,58,228,149,35,35,119,58, + 233,149,35,119,205,146,142,195,32,146,237,91, + 96,152,42,104,152,25,229,205,160,142,193,58, + 231,149,183,40,250,175,50,231,149,243,237,91, + 96,152,42,104,152,25,221,117,254,221,116,255, + 78,35,70,221,113,252,221,112,253,89,80,42, + 98,152,183,237,82,34,98,152,203,124,40,19, + 33,0,0,34,98,152,34,102,152,34,96,152, + 62,1,50,95,152,24,40,221,94,252,221,86, + 253,19,19,19,42,96,152,25,34,96,152,237, + 91,100,152,33,158,253,25,237,91,96,152,205, + 171,149,242,55,148,33,0,0,34,96,152,175, + 50,230,149,251,195,32,146,245,62,1,50,231, + 149,62,16,237,57,0,211,80,241,251,237,77, + 201,205,186,149,229,229,33,0,0,34,37,152, + 33,110,152,126,50,234,151,58,44,152,33,235, + 151,119,221,54,253,0,221,54,254,0,195,230, + 148,33,236,151,54,175,33,3,0,229,33,234, + 151,229,205,174,140,193,193,33,236,151,126,254, + 255,40,74,33,245,151,110,221,117,255,33,249, + 151,126,221,166,255,221,119,255,33,253,151,126, + 221,166,255,221,119,255,58,232,149,95,221,126, + 255,163,221,119,255,183,40,15,230,191,33,110, + 152,94,22,0,33,234,149,25,119,24,12,33, + 110,152,94,22,0,33,234,149,25,54,132,33, + 0,0,195,198,149,221,110,253,221,102,254,35, + 221,117,253,221,116,254,17,32,0,221,110,253, + 221,102,254,205,171,149,250,117,148,58,233,149, + 203,87,40,84,33,1,0,34,37,152,221,54, + 253,0,221,54,254,0,24,53,33,236,151,54, + 175,33,3,0,229,33,234,151,229,205,174,140, + 193,193,33,236,151,126,254,255,40,14,33,110, + 152,94,22,0,33,234,149,25,54,140,24,159, + 221,110,253,221,102,254,35,221,117,253,221,116, + 254,17,32,0,221,110,253,221,102,254,205,171, + 149,250,12,149,33,2,0,34,37,152,221,54, + 253,0,221,54,254,0,24,54,33,236,151,54, + 175,33,3,0,229,33,234,151,229,205,174,140, + 193,193,33,236,151,126,254,255,40,15,33,110, + 152,94,22,0,33,234,149,25,54,132,195,211, + 148,221,110,253,221,102,254,35,221,117,253,221, + 116,254,17,32,0,221,110,253,221,102,254,205, + 171,149,250,96,149,33,1,0,195,198,149,124, + 170,250,179,149,237,82,201,124,230,128,237,82, + 60,201,225,253,229,221,229,221,33,0,0,221, + 57,233,221,249,221,225,253,225,201,233,225,253, + 229,221,229,221,33,0,0,221,57,94,35,86, + 35,235,57,249,235,233,0,0,0,0,0,0, + 62,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 175,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,133,1,0,0,0,63, + 255,255,255,255,0,0,0,63,0,0,0,0, + 0,0,0,0,0,0,0,24,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0 + } ; + +#endif diff --git a/drivers/staging/appletalk/cops_ltdrv.h b/drivers/staging/appletalk/cops_ltdrv.h new file mode 100644 index 000000000000..c699b1ad31da --- /dev/null +++ b/drivers/staging/appletalk/cops_ltdrv.h @@ -0,0 +1,241 @@ +/* + * The firmware this driver downloads into the Localtalk card is a + * separate program and is not GPL'd source code, even though the Linux + * side driver and the routine that loads this data into the card are. + * + * It is taken from the COPS SDK and is under the following license + * + * This material is licensed to you strictly for use in conjunction with + * the use of COPS LocalTalk adapters. + * There is no charge for this SDK. And no waranty express or implied + * about its fitness for any purpose. However, we will cheerefully + * refund every penny you paid for this SDK... + * Regards, + * + * Thomas F. Divine + * Chief Scientist + */ + + +/* cops_ltdrv.h: LocalTalk driver firmware dump for Linux. + * + * Authors: + * - Jay Schulist + */ + + +#ifdef CONFIG_COPS_TANGENT + +static const unsigned char ltdrv_code[] = { + 58,3,0,50,148,10,33,143,15,62,85,119, + 190,32,9,62,170,119,190,32,3,35,24,241, + 34,146,10,249,17,150,10,33,143,15,183,237, + 82,77,68,11,107,98,19,54,0,237,176,62, + 16,237,57,51,62,0,237,57,50,237,57,54, + 62,12,237,57,49,62,195,33,39,2,50,56, + 0,34,57,0,237,86,205,30,2,251,205,60, + 10,24,169,67,111,112,121,114,105,103,104,116, + 32,40,99,41,32,49,57,56,56,45,49,57, + 57,50,44,32,80,114,105,110,116,105,110,103, + 32,67,111,109,109,117,110,105,99,97,116,105, + 111,110,115,32,65,115,115,111,99,105,97,116, + 101,115,44,32,73,110,99,46,65,108,108,32, + 114,105,103,104,116,115,32,114,101,115,101,114, + 118,101,100,46,32,32,4,4,22,40,255,60, + 4,96,10,224,6,0,7,126,2,64,11,246, + 12,6,13,0,14,193,15,0,5,96,3,192, + 1,0,9,8,62,3,211,82,62,192,211,82, + 201,62,3,211,82,62,213,211,82,201,62,5, + 211,82,62,224,211,82,201,62,5,211,82,62, + 224,211,82,201,62,5,211,82,62,96,211,82, + 201,6,28,33,180,1,14,82,237,163,194,4, + 2,33,39,2,34,64,0,58,3,0,230,1, + 192,62,11,237,121,62,118,237,121,201,33,182, + 10,54,132,205,253,1,201,245,197,213,229,42, + 150,10,14,83,17,98,2,67,20,237,162,58, + 179,1,95,219,82,230,1,32,6,29,32,247, + 195,17,3,62,1,211,82,219,82,95,230,160, + 32,10,237,162,32,225,21,32,222,195,15,3, + 237,162,123,230,96,194,21,3,62,48,211,82, + 62,1,211,82,175,211,82,237,91,150,10,43, + 55,237,82,218,19,3,34,152,10,98,107,58, + 154,10,190,32,81,62,1,50,158,10,35,35, + 62,132,190,32,44,54,133,43,70,58,154,10, + 119,43,112,17,3,0,205,137,3,62,16,211, + 82,62,56,211,82,205,217,1,42,150,10,14, + 83,17,98,2,67,20,58,178,1,95,195,59, + 2,62,129,190,194,227,2,54,130,43,70,58, + 154,10,119,43,112,17,3,0,205,137,3,195, + 254,2,35,35,126,254,132,194,227,2,205,61, + 3,24,20,62,128,166,194,222,2,221,229,221, + 33,175,10,205,93,6,205,144,7,221,225,225, + 209,193,241,251,237,77,221,229,221,33,159,10, + 205,93,6,221,225,205,61,3,195,247,2,24, + 237,24,235,24,233,230,64,40,2,24,227,24, + 225,175,50,179,10,205,208,1,201,197,33,4, + 0,57,126,35,102,111,205,51,3,193,201,62, + 1,50,179,10,34,150,10,54,0,58,179,10, + 183,200,62,14,211,82,62,193,211,82,62,10, + 211,82,62,224,211,82,62,6,211,82,58,154, + 10,211,82,62,16,211,82,62,56,211,82,62, + 48,211,82,219,82,230,1,40,4,219,83,24, + 242,62,14,211,82,62,33,211,82,62,1,211, + 82,62,9,211,82,62,32,211,82,205,217,1, + 201,14,83,205,208,1,24,23,14,83,205,208, + 1,205,226,1,58,174,1,61,32,253,205,244, + 1,58,174,1,61,32,253,205,226,1,58,175, + 1,61,32,253,62,5,211,82,62,233,211,82, + 62,128,211,82,58,176,1,61,32,253,237,163, + 27,62,192,211,82,219,82,230,4,40,250,237, + 163,27,122,179,32,243,219,82,230,4,40,250, + 58,178,1,71,219,82,230,4,40,3,5,32, + 247,219,82,230,4,40,250,205,235,1,58,177, + 1,61,32,253,205,244,1,201,229,213,35,35, + 126,230,128,194,145,4,43,58,154,10,119,43, + 70,33,181,10,119,43,112,17,3,0,243,62, + 10,211,82,219,82,230,128,202,41,4,209,225, + 62,1,55,251,201,205,144,3,58,180,10,254, + 255,202,127,4,205,217,1,58,178,1,71,219, + 82,230,1,32,6,5,32,247,195,173,4,219, + 83,71,58,154,10,184,194,173,4,58,178,1, + 71,219,82,230,1,32,6,5,32,247,195,173, + 4,219,83,58,178,1,71,219,82,230,1,32, + 6,5,32,247,195,173,4,219,83,254,133,194, + 173,4,58,179,1,24,4,58,179,1,135,61, + 32,253,209,225,205,137,3,205,61,3,183,251, + 201,209,225,243,62,10,211,82,219,82,230,128, + 202,164,4,62,1,55,251,201,205,144,3,205, + 61,3,183,251,201,209,225,62,2,55,251,201, + 243,62,14,211,82,62,33,211,82,251,201,33, + 4,0,57,94,35,86,33,2,0,57,126,35, + 102,111,221,229,34,193,10,237,83,195,10,221, + 33,171,10,205,93,6,58,185,10,50,186,10, + 58,184,10,135,50,184,10,205,112,6,254,3, + 56,16,58,185,10,135,60,230,15,50,185,10, + 175,50,184,10,24,23,58,183,10,205,112,6, + 254,3,48,13,58,185,10,203,63,50,185,10, + 62,255,50,183,10,58,185,10,50,186,10,58, + 183,10,135,50,183,10,62,32,50,187,10,50, + 188,10,6,255,219,82,230,16,32,3,5,32, + 247,205,180,4,6,40,219,82,230,16,40,3, + 5,32,247,62,10,211,82,219,82,230,128,194, + 46,5,219,82,230,16,40,214,237,95,71,58, + 186,10,160,230,15,40,32,71,14,10,62,10, + 211,82,219,82,230,128,202,119,5,205,180,4, + 195,156,5,219,82,230,16,202,156,5,13,32, + 229,16,225,42,193,10,237,91,195,10,205,252, + 3,48,7,61,202,156,5,195,197,5,221,225, + 33,0,0,201,221,33,163,10,205,93,6,58, + 188,10,61,50,188,10,40,19,58,186,10,246, + 1,50,186,10,58,183,10,246,1,50,183,10, + 195,46,5,221,225,33,1,0,201,221,33,167, + 10,205,93,6,58,184,10,246,1,50,184,10, + 58,186,10,135,246,1,50,186,10,58,187,10, + 61,50,187,10,194,46,5,221,225,33,2,0, + 201,221,229,33,0,0,57,17,4,0,25,126, + 50,154,10,230,128,50,189,10,58,189,10,183, + 40,6,221,33,88,2,24,4,221,33,150,0, + 58,154,10,183,40,49,60,40,46,61,33,190, + 10,119,35,119,35,54,129,175,50,158,10,221, + 43,221,229,225,124,181,40,42,33,190,10,17, + 3,0,205,206,4,17,232,3,27,123,178,32, + 251,58,158,10,183,40,224,58,154,10,71,62, + 7,128,230,127,71,58,189,10,176,50,154,10, + 24,166,221,225,201,183,221,52,0,192,221,52, + 1,192,221,52,2,192,221,52,3,192,55,201, + 6,8,14,0,31,48,1,12,16,250,121,201, + 33,2,0,57,94,35,86,35,78,35,70,35, + 126,35,102,105,79,120,68,103,237,176,201,33, + 2,0,57,126,35,102,111,62,17,237,57,48, + 125,237,57,40,124,237,57,41,62,0,237,57, + 42,62,64,237,57,43,62,0,237,57,44,33, + 128,2,125,237,57,46,124,237,57,47,62,145, + 237,57,48,211,68,58,149,10,211,66,201,33, + 2,0,57,126,35,102,111,62,33,237,57,48, + 62,64,237,57,32,62,0,237,57,33,237,57, + 34,125,237,57,35,124,237,57,36,62,0,237, + 57,37,33,128,2,125,237,57,38,124,237,57, + 39,62,97,237,57,48,211,67,58,149,10,211, + 66,201,237,56,46,95,237,56,47,87,237,56, + 46,111,237,56,47,103,183,237,82,32,235,33, + 128,2,183,237,82,201,237,56,38,95,237,56, + 39,87,237,56,38,111,237,56,39,103,183,237, + 82,32,235,33,128,2,183,237,82,201,205,106, + 10,221,110,6,221,102,7,126,35,110,103,195, + 118,10,205,106,10,33,0,0,34,205,10,34, + 198,10,34,200,10,33,143,15,34,207,10,237, + 91,207,10,42,146,10,183,237,82,17,0,255, + 25,34,203,10,203,124,40,6,33,0,125,34, + 203,10,42,207,10,229,205,37,3,195,118,10, + 205,106,10,229,42,150,10,35,35,35,229,205, + 70,7,193,124,230,3,103,221,117,254,221,116, + 255,237,91,152,10,35,35,35,183,237,82,32, + 12,17,5,0,42,152,10,205,91,10,242,203, + 7,42,150,10,229,205,37,3,195,118,10,237, + 91,152,10,42,200,10,25,34,200,10,42,205, + 10,25,34,205,10,237,91,203,10,33,158,253, + 25,237,91,205,10,205,91,10,242,245,7,33, + 0,0,34,205,10,62,1,50,197,10,205,5, + 8,33,0,0,57,249,195,118,10,205,106,10, + 58,197,10,183,202,118,10,237,91,198,10,42, + 205,10,205,91,10,242,46,8,237,91,205,10, + 33,98,2,25,237,91,198,10,205,91,10,250, + 78,8,237,91,198,10,42,205,10,183,237,82, + 32,7,42,200,10,125,180,40,13,237,91,205, + 10,42,198,10,205,91,10,242,97,8,237,91, + 207,10,42,205,10,25,229,205,37,3,175,50, + 197,10,195,118,10,205,29,3,33,0,0,57, + 249,195,118,10,205,106,10,58,202,10,183,40, + 22,205,14,7,237,91,209,10,19,19,19,205, + 91,10,242,139,8,33,1,0,195,118,10,33, + 0,0,195,118,10,205,126,10,252,255,205,108, + 8,125,180,194,118,10,237,91,200,10,33,0, + 0,205,91,10,242,118,10,237,91,207,10,42, + 198,10,25,221,117,254,221,116,255,35,35,35, + 229,205,70,7,193,124,230,3,103,35,35,35, + 221,117,252,221,116,253,229,221,110,254,221,102, + 255,229,33,212,10,229,205,124,6,193,193,221, + 110,252,221,102,253,34,209,10,33,211,10,54, + 4,33,209,10,227,205,147,6,193,62,1,50, + 202,10,243,221,94,252,221,86,253,42,200,10, + 183,237,82,34,200,10,203,124,40,17,33,0, + 0,34,200,10,34,205,10,34,198,10,50,197, + 10,24,37,221,94,252,221,86,253,42,198,10, + 25,34,198,10,237,91,203,10,33,158,253,25, + 237,91,198,10,205,91,10,242,68,9,33,0, + 0,34,198,10,205,5,8,33,0,0,57,249, + 251,195,118,10,205,106,10,33,49,13,126,183, + 40,16,205,42,7,237,91,47,13,19,19,19, + 205,91,10,242,117,9,58,142,15,198,1,50, + 142,15,195,118,10,33,49,13,126,254,1,40, + 25,254,3,202,7,10,254,5,202,21,10,33, + 49,13,54,0,33,47,13,229,205,207,6,195, + 118,10,58,141,15,183,32,72,33,51,13,126, + 50,149,10,205,86,7,33,50,13,126,230,127, + 183,32,40,58,142,15,230,127,50,142,15,183, + 32,5,198,1,50,142,15,33,50,13,126,111, + 23,159,103,203,125,58,142,15,40,5,198,128, + 50,142,15,33,50,13,119,33,50,13,126,111, + 23,159,103,229,205,237,5,193,33,211,10,54, + 2,33,2,0,34,209,10,58,154,10,33,212, + 10,119,58,148,10,33,213,10,119,33,209,10, + 229,205,147,6,193,24,128,42,47,13,229,33, + 50,13,229,205,191,4,193,24,239,33,211,10, + 54,6,33,3,0,34,209,10,58,154,10,33, + 212,10,119,58,148,10,33,213,10,119,33,214, + 10,54,5,33,209,10,229,205,147,6,24,200, + 205,106,10,33,49,13,54,0,33,47,13,229, + 205,207,6,33,209,10,227,205,147,6,193,205, + 80,9,205,145,8,24,248,124,170,250,99,10, + 237,82,201,124,230,128,237,82,60,201,225,253, + 229,221,229,221,33,0,0,221,57,233,221,249, + 221,225,253,225,201,233,225,253,229,221,229,221, + 33,0,0,221,57,94,35,86,35,235,57,249, + 235,233,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0 + } ; + +#endif diff --git a/drivers/staging/appletalk/ddp.c b/drivers/staging/appletalk/ddp.c new file mode 100644 index 000000000000..940dd1908339 --- /dev/null +++ b/drivers/staging/appletalk/ddp.c @@ -0,0 +1,1981 @@ +/* + * DDP: An implementation of the AppleTalk DDP protocol for + * Ethernet 'ELAP'. + * + * Alan Cox + * + * With more than a little assistance from + * + * Wesley Craig + * + * Fixes: + * Neil Horman : Added missing device ioctls + * Michael Callahan : Made routing work + * Wesley Craig : Fix probing to listen to a + * passed node id. + * Alan Cox : Added send/recvmsg support + * Alan Cox : Moved at. to protinfo in + * socket. + * Alan Cox : Added firewall hooks. + * Alan Cox : Supports new ARPHRD_LOOPBACK + * Christer Weinigel : Routing and /proc fixes. + * Bradford Johnson : LocalTalk. + * Tom Dyas : Module support. + * Alan Cox : Hooks for PPP (based on the + * LocalTalk hook). + * Alan Cox : Posix bits + * Alan Cox/Mike Freeman : Possible fix to NBP problems + * Bradford Johnson : IP-over-DDP (experimental) + * Jay Schulist : Moved IP-over-DDP to its own + * driver file. (ipddp.c & ipddp.h) + * Jay Schulist : Made work as module with + * AppleTalk drivers, cleaned it. + * Rob Newberry : Added proxy AARP and AARP + * procfs, moved probing to AARP + * module. + * Adrian Sun/ + * Michael Zuelsdorff : fix for net.0 packets. don't + * allow illegal ether/tokentalk + * port assignment. we lose a + * valid localtalk port as a + * result. + * Arnaldo C. de Melo : Cleanup, in preparation for + * shared skb support 8) + * Arnaldo C. de Melo : Move proc stuff to atalk_proc.c, + * use seq_file + * + * 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 /* For TIOCOUTQ/INQ */ +#include +#include +#include +#include +#include +#include +#include +#include "atalk.h" +#include "../../net/core/kmap_skb.h" + +struct datalink_proto *ddp_dl, *aarp_dl; +static const struct proto_ops atalk_dgram_ops; + +/**************************************************************************\ +* * +* Handlers for the socket list. * +* * +\**************************************************************************/ + +HLIST_HEAD(atalk_sockets); +DEFINE_RWLOCK(atalk_sockets_lock); + +static inline void __atalk_insert_socket(struct sock *sk) +{ + sk_add_node(sk, &atalk_sockets); +} + +static inline void atalk_remove_socket(struct sock *sk) +{ + write_lock_bh(&atalk_sockets_lock); + sk_del_node_init(sk); + write_unlock_bh(&atalk_sockets_lock); +} + +static struct sock *atalk_search_socket(struct sockaddr_at *to, + struct atalk_iface *atif) +{ + struct sock *s; + struct hlist_node *node; + + read_lock_bh(&atalk_sockets_lock); + sk_for_each(s, node, &atalk_sockets) { + struct atalk_sock *at = at_sk(s); + + if (to->sat_port != at->src_port) + continue; + + if (to->sat_addr.s_net == ATADDR_ANYNET && + to->sat_addr.s_node == ATADDR_BCAST) + goto found; + + if (to->sat_addr.s_net == at->src_net && + (to->sat_addr.s_node == at->src_node || + to->sat_addr.s_node == ATADDR_BCAST || + to->sat_addr.s_node == ATADDR_ANYNODE)) + goto found; + + /* XXXX.0 -- we got a request for this router. make sure + * that the node is appropriately set. */ + if (to->sat_addr.s_node == ATADDR_ANYNODE && + to->sat_addr.s_net != ATADDR_ANYNET && + atif->address.s_node == at->src_node) { + to->sat_addr.s_node = atif->address.s_node; + goto found; + } + } + s = NULL; +found: + read_unlock_bh(&atalk_sockets_lock); + return s; +} + +/** + * atalk_find_or_insert_socket - Try to find a socket matching ADDR + * @sk - socket to insert in the list if it is not there already + * @sat - address to search for + * + * Try to find a socket matching ADDR in the socket list, if found then return + * it. If not, insert SK into the socket list. + * + * This entire operation must execute atomically. + */ +static struct sock *atalk_find_or_insert_socket(struct sock *sk, + struct sockaddr_at *sat) +{ + struct sock *s; + struct hlist_node *node; + struct atalk_sock *at; + + write_lock_bh(&atalk_sockets_lock); + sk_for_each(s, node, &atalk_sockets) { + at = at_sk(s); + + if (at->src_net == sat->sat_addr.s_net && + at->src_node == sat->sat_addr.s_node && + at->src_port == sat->sat_port) + goto found; + } + s = NULL; + __atalk_insert_socket(sk); /* Wheee, it's free, assign and insert. */ +found: + write_unlock_bh(&atalk_sockets_lock); + return s; +} + +static void atalk_destroy_timer(unsigned long data) +{ + struct sock *sk = (struct sock *)data; + + if (sk_has_allocations(sk)) { + sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME; + add_timer(&sk->sk_timer); + } else + sock_put(sk); +} + +static inline void atalk_destroy_socket(struct sock *sk) +{ + atalk_remove_socket(sk); + skb_queue_purge(&sk->sk_receive_queue); + + if (sk_has_allocations(sk)) { + setup_timer(&sk->sk_timer, atalk_destroy_timer, + (unsigned long)sk); + sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME; + add_timer(&sk->sk_timer); + } else + sock_put(sk); +} + +/**************************************************************************\ +* * +* Routing tables for the AppleTalk socket layer. * +* * +\**************************************************************************/ + +/* Anti-deadlock ordering is atalk_routes_lock --> iface_lock -DaveM */ +struct atalk_route *atalk_routes; +DEFINE_RWLOCK(atalk_routes_lock); + +struct atalk_iface *atalk_interfaces; +DEFINE_RWLOCK(atalk_interfaces_lock); + +/* For probing devices or in a routerless network */ +struct atalk_route atrtr_default; + +/* AppleTalk interface control */ +/* + * Drop a device. Doesn't drop any of its routes - that is the caller's + * problem. Called when we down the interface or delete the address. + */ +static void atif_drop_device(struct net_device *dev) +{ + struct atalk_iface **iface = &atalk_interfaces; + struct atalk_iface *tmp; + + write_lock_bh(&atalk_interfaces_lock); + while ((tmp = *iface) != NULL) { + if (tmp->dev == dev) { + *iface = tmp->next; + dev_put(dev); + kfree(tmp); + dev->atalk_ptr = NULL; + } else + iface = &tmp->next; + } + write_unlock_bh(&atalk_interfaces_lock); +} + +static struct atalk_iface *atif_add_device(struct net_device *dev, + struct atalk_addr *sa) +{ + struct atalk_iface *iface = kzalloc(sizeof(*iface), GFP_KERNEL); + + if (!iface) + goto out; + + dev_hold(dev); + iface->dev = dev; + dev->atalk_ptr = iface; + iface->address = *sa; + iface->status = 0; + + write_lock_bh(&atalk_interfaces_lock); + iface->next = atalk_interfaces; + atalk_interfaces = iface; + write_unlock_bh(&atalk_interfaces_lock); +out: + return iface; +} + +/* Perform phase 2 AARP probing on our tentative address */ +static int atif_probe_device(struct atalk_iface *atif) +{ + int netrange = ntohs(atif->nets.nr_lastnet) - + ntohs(atif->nets.nr_firstnet) + 1; + int probe_net = ntohs(atif->address.s_net); + int probe_node = atif->address.s_node; + int netct, nodect; + + /* Offset the network we start probing with */ + if (probe_net == ATADDR_ANYNET) { + probe_net = ntohs(atif->nets.nr_firstnet); + if (netrange) + probe_net += jiffies % netrange; + } + if (probe_node == ATADDR_ANYNODE) + probe_node = jiffies & 0xFF; + + /* Scan the networks */ + atif->status |= ATIF_PROBE; + for (netct = 0; netct <= netrange; netct++) { + /* Sweep the available nodes from a given start */ + atif->address.s_net = htons(probe_net); + for (nodect = 0; nodect < 256; nodect++) { + atif->address.s_node = (nodect + probe_node) & 0xFF; + if (atif->address.s_node > 0 && + atif->address.s_node < 254) { + /* Probe a proposed address */ + aarp_probe_network(atif); + + if (!(atif->status & ATIF_PROBE_FAIL)) { + atif->status &= ~ATIF_PROBE; + return 0; + } + } + atif->status &= ~ATIF_PROBE_FAIL; + } + probe_net++; + if (probe_net > ntohs(atif->nets.nr_lastnet)) + probe_net = ntohs(atif->nets.nr_firstnet); + } + atif->status &= ~ATIF_PROBE; + + return -EADDRINUSE; /* Network is full... */ +} + + +/* Perform AARP probing for a proxy address */ +static int atif_proxy_probe_device(struct atalk_iface *atif, + struct atalk_addr* proxy_addr) +{ + int netrange = ntohs(atif->nets.nr_lastnet) - + ntohs(atif->nets.nr_firstnet) + 1; + /* we probe the interface's network */ + int probe_net = ntohs(atif->address.s_net); + int probe_node = ATADDR_ANYNODE; /* we'll take anything */ + int netct, nodect; + + /* Offset the network we start probing with */ + if (probe_net == ATADDR_ANYNET) { + probe_net = ntohs(atif->nets.nr_firstnet); + if (netrange) + probe_net += jiffies % netrange; + } + + if (probe_node == ATADDR_ANYNODE) + probe_node = jiffies & 0xFF; + + /* Scan the networks */ + for (netct = 0; netct <= netrange; netct++) { + /* Sweep the available nodes from a given start */ + proxy_addr->s_net = htons(probe_net); + for (nodect = 0; nodect < 256; nodect++) { + proxy_addr->s_node = (nodect + probe_node) & 0xFF; + if (proxy_addr->s_node > 0 && + proxy_addr->s_node < 254) { + /* Tell AARP to probe a proposed address */ + int ret = aarp_proxy_probe_network(atif, + proxy_addr); + + if (ret != -EADDRINUSE) + return ret; + } + } + probe_net++; + if (probe_net > ntohs(atif->nets.nr_lastnet)) + probe_net = ntohs(atif->nets.nr_firstnet); + } + + return -EADDRINUSE; /* Network is full... */ +} + + +struct atalk_addr *atalk_find_dev_addr(struct net_device *dev) +{ + struct atalk_iface *iface = dev->atalk_ptr; + return iface ? &iface->address : NULL; +} + +static struct atalk_addr *atalk_find_primary(void) +{ + struct atalk_iface *fiface = NULL; + struct atalk_addr *retval; + struct atalk_iface *iface; + + /* + * Return a point-to-point interface only if + * there is no non-ptp interface available. + */ + read_lock_bh(&atalk_interfaces_lock); + for (iface = atalk_interfaces; iface; iface = iface->next) { + if (!fiface && !(iface->dev->flags & IFF_LOOPBACK)) + fiface = iface; + if (!(iface->dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) { + retval = &iface->address; + goto out; + } + } + + if (fiface) + retval = &fiface->address; + else if (atalk_interfaces) + retval = &atalk_interfaces->address; + else + retval = NULL; +out: + read_unlock_bh(&atalk_interfaces_lock); + return retval; +} + +/* + * Find a match for 'any network' - ie any of our interfaces with that + * node number will do just nicely. + */ +static struct atalk_iface *atalk_find_anynet(int node, struct net_device *dev) +{ + struct atalk_iface *iface = dev->atalk_ptr; + + if (!iface || iface->status & ATIF_PROBE) + goto out_err; + + if (node != ATADDR_BCAST && + iface->address.s_node != node && + node != ATADDR_ANYNODE) + goto out_err; +out: + return iface; +out_err: + iface = NULL; + goto out; +} + +/* Find a match for a specific network:node pair */ +static struct atalk_iface *atalk_find_interface(__be16 net, int node) +{ + struct atalk_iface *iface; + + read_lock_bh(&atalk_interfaces_lock); + for (iface = atalk_interfaces; iface; iface = iface->next) { + if ((node == ATADDR_BCAST || + node == ATADDR_ANYNODE || + iface->address.s_node == node) && + iface->address.s_net == net && + !(iface->status & ATIF_PROBE)) + break; + + /* XXXX.0 -- net.0 returns the iface associated with net */ + if (node == ATADDR_ANYNODE && net != ATADDR_ANYNET && + ntohs(iface->nets.nr_firstnet) <= ntohs(net) && + ntohs(net) <= ntohs(iface->nets.nr_lastnet)) + break; + } + read_unlock_bh(&atalk_interfaces_lock); + return iface; +} + + +/* + * Find a route for an AppleTalk packet. This ought to get cached in + * the socket (later on...). We know about host routes and the fact + * that a route must be direct to broadcast. + */ +static struct atalk_route *atrtr_find(struct atalk_addr *target) +{ + /* + * we must search through all routes unless we find a + * host route, because some host routes might overlap + * network routes + */ + struct atalk_route *net_route = NULL; + struct atalk_route *r; + + read_lock_bh(&atalk_routes_lock); + for (r = atalk_routes; r; r = r->next) { + if (!(r->flags & RTF_UP)) + continue; + + if (r->target.s_net == target->s_net) { + if (r->flags & RTF_HOST) { + /* + * if this host route is for the target, + * the we're done + */ + if (r->target.s_node == target->s_node) + goto out; + } else + /* + * this route will work if there isn't a + * direct host route, so cache it + */ + net_route = r; + } + } + + /* + * if we found a network route but not a direct host + * route, then return it + */ + if (net_route) + r = net_route; + else if (atrtr_default.dev) + r = &atrtr_default; + else /* No route can be found */ + r = NULL; +out: + read_unlock_bh(&atalk_routes_lock); + return r; +} + + +/* + * Given an AppleTalk network, find the device to use. This can be + * a simple lookup. + */ +struct net_device *atrtr_get_dev(struct atalk_addr *sa) +{ + struct atalk_route *atr = atrtr_find(sa); + return atr ? atr->dev : NULL; +} + +/* Set up a default router */ +static void atrtr_set_default(struct net_device *dev) +{ + atrtr_default.dev = dev; + atrtr_default.flags = RTF_UP; + atrtr_default.gateway.s_net = htons(0); + atrtr_default.gateway.s_node = 0; +} + +/* + * Add a router. Basically make sure it looks valid and stuff the + * entry in the list. While it uses netranges we always set them to one + * entry to work like netatalk. + */ +static int atrtr_create(struct rtentry *r, struct net_device *devhint) +{ + struct sockaddr_at *ta = (struct sockaddr_at *)&r->rt_dst; + struct sockaddr_at *ga = (struct sockaddr_at *)&r->rt_gateway; + struct atalk_route *rt; + struct atalk_iface *iface, *riface; + int retval = -EINVAL; + + /* + * Fixme: Raise/Lower a routing change semaphore for these + * operations. + */ + + /* Validate the request */ + if (ta->sat_family != AF_APPLETALK || + (!devhint && ga->sat_family != AF_APPLETALK)) + goto out; + + /* Now walk the routing table and make our decisions */ + write_lock_bh(&atalk_routes_lock); + for (rt = atalk_routes; rt; rt = rt->next) { + if (r->rt_flags != rt->flags) + continue; + + if (ta->sat_addr.s_net == rt->target.s_net) { + if (!(rt->flags & RTF_HOST)) + break; + if (ta->sat_addr.s_node == rt->target.s_node) + break; + } + } + + if (!devhint) { + riface = NULL; + + read_lock_bh(&atalk_interfaces_lock); + for (iface = atalk_interfaces; iface; iface = iface->next) { + if (!riface && + ntohs(ga->sat_addr.s_net) >= + ntohs(iface->nets.nr_firstnet) && + ntohs(ga->sat_addr.s_net) <= + ntohs(iface->nets.nr_lastnet)) + riface = iface; + + if (ga->sat_addr.s_net == iface->address.s_net && + ga->sat_addr.s_node == iface->address.s_node) + riface = iface; + } + read_unlock_bh(&atalk_interfaces_lock); + + retval = -ENETUNREACH; + if (!riface) + goto out_unlock; + + devhint = riface->dev; + } + + if (!rt) { + rt = kzalloc(sizeof(*rt), GFP_ATOMIC); + + retval = -ENOBUFS; + if (!rt) + goto out_unlock; + + rt->next = atalk_routes; + atalk_routes = rt; + } + + /* Fill in the routing entry */ + rt->target = ta->sat_addr; + dev_hold(devhint); + rt->dev = devhint; + rt->flags = r->rt_flags; + rt->gateway = ga->sat_addr; + + retval = 0; +out_unlock: + write_unlock_bh(&atalk_routes_lock); +out: + return retval; +} + +/* Delete a route. Find it and discard it */ +static int atrtr_delete(struct atalk_addr * addr) +{ + struct atalk_route **r = &atalk_routes; + int retval = 0; + struct atalk_route *tmp; + + write_lock_bh(&atalk_routes_lock); + while ((tmp = *r) != NULL) { + if (tmp->target.s_net == addr->s_net && + (!(tmp->flags&RTF_GATEWAY) || + tmp->target.s_node == addr->s_node)) { + *r = tmp->next; + dev_put(tmp->dev); + kfree(tmp); + goto out; + } + r = &tmp->next; + } + retval = -ENOENT; +out: + write_unlock_bh(&atalk_routes_lock); + return retval; +} + +/* + * Called when a device is downed. Just throw away any routes + * via it. + */ +static void atrtr_device_down(struct net_device *dev) +{ + struct atalk_route **r = &atalk_routes; + struct atalk_route *tmp; + + write_lock_bh(&atalk_routes_lock); + while ((tmp = *r) != NULL) { + if (tmp->dev == dev) { + *r = tmp->next; + dev_put(dev); + kfree(tmp); + } else + r = &tmp->next; + } + write_unlock_bh(&atalk_routes_lock); + + if (atrtr_default.dev == dev) + atrtr_set_default(NULL); +} + +/* Actually down the interface */ +static inline void atalk_dev_down(struct net_device *dev) +{ + atrtr_device_down(dev); /* Remove all routes for the device */ + aarp_device_down(dev); /* Remove AARP entries for the device */ + atif_drop_device(dev); /* Remove the device */ +} + +/* + * A device event has occurred. Watch for devices going down and + * delete our use of them (iface and route). + */ +static int ddp_device_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct net_device *dev = ptr; + + if (!net_eq(dev_net(dev), &init_net)) + return NOTIFY_DONE; + + if (event == NETDEV_DOWN) + /* Discard any use of this */ + atalk_dev_down(dev); + + return NOTIFY_DONE; +} + +/* ioctl calls. Shouldn't even need touching */ +/* Device configuration ioctl calls */ +static int atif_ioctl(int cmd, void __user *arg) +{ + static char aarp_mcast[6] = { 0x09, 0x00, 0x00, 0xFF, 0xFF, 0xFF }; + struct ifreq atreq; + struct atalk_netrange *nr; + struct sockaddr_at *sa; + struct net_device *dev; + struct atalk_iface *atif; + int ct; + int limit; + struct rtentry rtdef; + int add_route; + + if (copy_from_user(&atreq, arg, sizeof(atreq))) + return -EFAULT; + + dev = __dev_get_by_name(&init_net, atreq.ifr_name); + if (!dev) + return -ENODEV; + + sa = (struct sockaddr_at *)&atreq.ifr_addr; + atif = atalk_find_dev(dev); + + switch (cmd) { + case SIOCSIFADDR: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (sa->sat_family != AF_APPLETALK) + return -EINVAL; + if (dev->type != ARPHRD_ETHER && + dev->type != ARPHRD_LOOPBACK && + dev->type != ARPHRD_LOCALTLK && + dev->type != ARPHRD_PPP) + return -EPROTONOSUPPORT; + + nr = (struct atalk_netrange *)&sa->sat_zero[0]; + add_route = 1; + + /* + * if this is a point-to-point iface, and we already + * have an iface for this AppleTalk address, then we + * should not add a route + */ + if ((dev->flags & IFF_POINTOPOINT) && + atalk_find_interface(sa->sat_addr.s_net, + sa->sat_addr.s_node)) { + printk(KERN_DEBUG "AppleTalk: point-to-point " + "interface added with " + "existing address\n"); + add_route = 0; + } + + /* + * Phase 1 is fine on LocalTalk but we don't do + * EtherTalk phase 1. Anyone wanting to add it go ahead. + */ + if (dev->type == ARPHRD_ETHER && nr->nr_phase != 2) + return -EPROTONOSUPPORT; + if (sa->sat_addr.s_node == ATADDR_BCAST || + sa->sat_addr.s_node == 254) + return -EINVAL; + if (atif) { + /* Already setting address */ + if (atif->status & ATIF_PROBE) + return -EBUSY; + + atif->address.s_net = sa->sat_addr.s_net; + atif->address.s_node = sa->sat_addr.s_node; + atrtr_device_down(dev); /* Flush old routes */ + } else { + atif = atif_add_device(dev, &sa->sat_addr); + if (!atif) + return -ENOMEM; + } + atif->nets = *nr; + + /* + * Check if the chosen address is used. If so we + * error and atalkd will try another. + */ + + if (!(dev->flags & IFF_LOOPBACK) && + !(dev->flags & IFF_POINTOPOINT) && + atif_probe_device(atif) < 0) { + atif_drop_device(dev); + return -EADDRINUSE; + } + + /* Hey it worked - add the direct routes */ + sa = (struct sockaddr_at *)&rtdef.rt_gateway; + sa->sat_family = AF_APPLETALK; + sa->sat_addr.s_net = atif->address.s_net; + sa->sat_addr.s_node = atif->address.s_node; + sa = (struct sockaddr_at *)&rtdef.rt_dst; + rtdef.rt_flags = RTF_UP; + sa->sat_family = AF_APPLETALK; + sa->sat_addr.s_node = ATADDR_ANYNODE; + if (dev->flags & IFF_LOOPBACK || + dev->flags & IFF_POINTOPOINT) + rtdef.rt_flags |= RTF_HOST; + + /* Routerless initial state */ + if (nr->nr_firstnet == htons(0) && + nr->nr_lastnet == htons(0xFFFE)) { + sa->sat_addr.s_net = atif->address.s_net; + atrtr_create(&rtdef, dev); + atrtr_set_default(dev); + } else { + limit = ntohs(nr->nr_lastnet); + if (limit - ntohs(nr->nr_firstnet) > 4096) { + printk(KERN_WARNING "Too many routes/" + "iface.\n"); + return -EINVAL; + } + if (add_route) + for (ct = ntohs(nr->nr_firstnet); + ct <= limit; ct++) { + sa->sat_addr.s_net = htons(ct); + atrtr_create(&rtdef, dev); + } + } + dev_mc_add_global(dev, aarp_mcast); + return 0; + + case SIOCGIFADDR: + if (!atif) + return -EADDRNOTAVAIL; + + sa->sat_family = AF_APPLETALK; + sa->sat_addr = atif->address; + break; + + case SIOCGIFBRDADDR: + if (!atif) + return -EADDRNOTAVAIL; + + sa->sat_family = AF_APPLETALK; + sa->sat_addr.s_net = atif->address.s_net; + sa->sat_addr.s_node = ATADDR_BCAST; + break; + + case SIOCATALKDIFADDR: + case SIOCDIFADDR: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (sa->sat_family != AF_APPLETALK) + return -EINVAL; + atalk_dev_down(dev); + break; + + case SIOCSARP: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (sa->sat_family != AF_APPLETALK) + return -EINVAL; + /* + * for now, we only support proxy AARP on ELAP; + * we should be able to do it for LocalTalk, too. + */ + if (dev->type != ARPHRD_ETHER) + return -EPROTONOSUPPORT; + + /* + * atif points to the current interface on this network; + * we aren't concerned about its current status (at + * least for now), but it has all the settings about + * the network we're going to probe. Consequently, it + * must exist. + */ + if (!atif) + return -EADDRNOTAVAIL; + + nr = (struct atalk_netrange *)&(atif->nets); + /* + * Phase 1 is fine on Localtalk but we don't do + * Ethertalk phase 1. Anyone wanting to add it go ahead. + */ + if (dev->type == ARPHRD_ETHER && nr->nr_phase != 2) + return -EPROTONOSUPPORT; + + if (sa->sat_addr.s_node == ATADDR_BCAST || + sa->sat_addr.s_node == 254) + return -EINVAL; + + /* + * Check if the chosen address is used. If so we + * error and ATCP will try another. + */ + if (atif_proxy_probe_device(atif, &(sa->sat_addr)) < 0) + return -EADDRINUSE; + + /* + * We now have an address on the local network, and + * the AARP code will defend it for us until we take it + * down. We don't set up any routes right now, because + * ATCP will install them manually via SIOCADDRT. + */ + break; + + case SIOCDARP: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (sa->sat_family != AF_APPLETALK) + return -EINVAL; + if (!atif) + return -EADDRNOTAVAIL; + + /* give to aarp module to remove proxy entry */ + aarp_proxy_remove(atif->dev, &(sa->sat_addr)); + return 0; + } + + return copy_to_user(arg, &atreq, sizeof(atreq)) ? -EFAULT : 0; +} + +/* Routing ioctl() calls */ +static int atrtr_ioctl(unsigned int cmd, void __user *arg) +{ + struct rtentry rt; + + if (copy_from_user(&rt, arg, sizeof(rt))) + return -EFAULT; + + switch (cmd) { + case SIOCDELRT: + if (rt.rt_dst.sa_family != AF_APPLETALK) + return -EINVAL; + return atrtr_delete(&((struct sockaddr_at *) + &rt.rt_dst)->sat_addr); + + case SIOCADDRT: { + struct net_device *dev = NULL; + if (rt.rt_dev) { + char name[IFNAMSIZ]; + if (copy_from_user(name, rt.rt_dev, IFNAMSIZ-1)) + return -EFAULT; + name[IFNAMSIZ-1] = '\0'; + dev = __dev_get_by_name(&init_net, name); + if (!dev) + return -ENODEV; + } + return atrtr_create(&rt, dev); + } + } + return -EINVAL; +} + +/**************************************************************************\ +* * +* Handling for system calls applied via the various interfaces to an * +* AppleTalk socket object. * +* * +\**************************************************************************/ + +/* + * Checksum: This is 'optional'. It's quite likely also a good + * candidate for assembler hackery 8) + */ +static unsigned long atalk_sum_partial(const unsigned char *data, + int len, unsigned long sum) +{ + /* This ought to be unwrapped neatly. I'll trust gcc for now */ + while (len--) { + sum += *data++; + sum = rol16(sum, 1); + } + return sum; +} + +/* Checksum skb data -- similar to skb_checksum */ +static unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset, + int len, unsigned long sum) +{ + int start = skb_headlen(skb); + struct sk_buff *frag_iter; + int i, copy; + + /* checksum stuff in header space */ + if ( (copy = start - offset) > 0) { + if (copy > len) + copy = len; + sum = atalk_sum_partial(skb->data + offset, copy, sum); + if ( (len -= copy) == 0) + return sum; + + offset += copy; + } + + /* checksum stuff in frags */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + int end; + + WARN_ON(start > offset + len); + + end = start + skb_shinfo(skb)->frags[i].size; + if ((copy = end - offset) > 0) { + u8 *vaddr; + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + if (copy > len) + copy = len; + vaddr = kmap_skb_frag(frag); + sum = atalk_sum_partial(vaddr + frag->page_offset + + offset - start, copy, sum); + kunmap_skb_frag(vaddr); + + if (!(len -= copy)) + return sum; + offset += copy; + } + start = end; + } + + skb_walk_frags(skb, frag_iter) { + int end; + + WARN_ON(start > offset + len); + + end = start + frag_iter->len; + if ((copy = end - offset) > 0) { + if (copy > len) + copy = len; + sum = atalk_sum_skb(frag_iter, offset - start, + copy, sum); + if ((len -= copy) == 0) + return sum; + offset += copy; + } + start = end; + } + + BUG_ON(len > 0); + + return sum; +} + +static __be16 atalk_checksum(const struct sk_buff *skb, int len) +{ + unsigned long sum; + + /* skip header 4 bytes */ + sum = atalk_sum_skb(skb, 4, len-4, 0); + + /* Use 0xFFFF for 0. 0 itself means none */ + return sum ? htons((unsigned short)sum) : htons(0xFFFF); +} + +static struct proto ddp_proto = { + .name = "DDP", + .owner = THIS_MODULE, + .obj_size = sizeof(struct atalk_sock), +}; + +/* + * Create a socket. Initialise the socket, blank the addresses + * set the state. + */ +static int atalk_create(struct net *net, struct socket *sock, int protocol, + int kern) +{ + struct sock *sk; + int rc = -ESOCKTNOSUPPORT; + + if (!net_eq(net, &init_net)) + return -EAFNOSUPPORT; + + /* + * We permit SOCK_DGRAM and RAW is an extension. It is trivial to do + * and gives you the full ELAP frame. Should be handy for CAP 8) + */ + if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM) + goto out; + rc = -ENOMEM; + sk = sk_alloc(net, PF_APPLETALK, GFP_KERNEL, &ddp_proto); + if (!sk) + goto out; + rc = 0; + sock->ops = &atalk_dgram_ops; + sock_init_data(sock, sk); + + /* Checksums on by default */ + sock_set_flag(sk, SOCK_ZAPPED); +out: + return rc; +} + +/* Free a socket. No work needed */ +static int atalk_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + lock_kernel(); + if (sk) { + sock_orphan(sk); + sock->sk = NULL; + atalk_destroy_socket(sk); + } + unlock_kernel(); + return 0; +} + +/** + * atalk_pick_and_bind_port - Pick a source port when one is not given + * @sk - socket to insert into the tables + * @sat - address to search for + * + * Pick a source port when one is not given. If we can find a suitable free + * one, we insert the socket into the tables using it. + * + * This whole operation must be atomic. + */ +static int atalk_pick_and_bind_port(struct sock *sk, struct sockaddr_at *sat) +{ + int retval; + + write_lock_bh(&atalk_sockets_lock); + + for (sat->sat_port = ATPORT_RESERVED; + sat->sat_port < ATPORT_LAST; + sat->sat_port++) { + struct sock *s; + struct hlist_node *node; + + sk_for_each(s, node, &atalk_sockets) { + struct atalk_sock *at = at_sk(s); + + if (at->src_net == sat->sat_addr.s_net && + at->src_node == sat->sat_addr.s_node && + at->src_port == sat->sat_port) + goto try_next_port; + } + + /* Wheee, it's free, assign and insert. */ + __atalk_insert_socket(sk); + at_sk(sk)->src_port = sat->sat_port; + retval = 0; + goto out; + +try_next_port:; + } + + retval = -EBUSY; +out: + write_unlock_bh(&atalk_sockets_lock); + return retval; +} + +static int atalk_autobind(struct sock *sk) +{ + struct atalk_sock *at = at_sk(sk); + struct sockaddr_at sat; + struct atalk_addr *ap = atalk_find_primary(); + int n = -EADDRNOTAVAIL; + + if (!ap || ap->s_net == htons(ATADDR_ANYNET)) + goto out; + + at->src_net = sat.sat_addr.s_net = ap->s_net; + at->src_node = sat.sat_addr.s_node = ap->s_node; + + n = atalk_pick_and_bind_port(sk, &sat); + if (!n) + sock_reset_flag(sk, SOCK_ZAPPED); +out: + return n; +} + +/* Set the address 'our end' of the connection */ +static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) +{ + struct sockaddr_at *addr = (struct sockaddr_at *)uaddr; + struct sock *sk = sock->sk; + struct atalk_sock *at = at_sk(sk); + int err; + + if (!sock_flag(sk, SOCK_ZAPPED) || + addr_len != sizeof(struct sockaddr_at)) + return -EINVAL; + + if (addr->sat_family != AF_APPLETALK) + return -EAFNOSUPPORT; + + lock_kernel(); + if (addr->sat_addr.s_net == htons(ATADDR_ANYNET)) { + struct atalk_addr *ap = atalk_find_primary(); + + err = -EADDRNOTAVAIL; + if (!ap) + goto out; + + at->src_net = addr->sat_addr.s_net = ap->s_net; + at->src_node = addr->sat_addr.s_node= ap->s_node; + } else { + err = -EADDRNOTAVAIL; + if (!atalk_find_interface(addr->sat_addr.s_net, + addr->sat_addr.s_node)) + goto out; + + at->src_net = addr->sat_addr.s_net; + at->src_node = addr->sat_addr.s_node; + } + + if (addr->sat_port == ATADDR_ANYPORT) { + err = atalk_pick_and_bind_port(sk, addr); + + if (err < 0) + goto out; + } else { + at->src_port = addr->sat_port; + + err = -EADDRINUSE; + if (atalk_find_or_insert_socket(sk, addr)) + goto out; + } + + sock_reset_flag(sk, SOCK_ZAPPED); + err = 0; +out: + unlock_kernel(); + return err; +} + +/* Set the address we talk to */ +static int atalk_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags) +{ + struct sock *sk = sock->sk; + struct atalk_sock *at = at_sk(sk); + struct sockaddr_at *addr; + int err; + + sk->sk_state = TCP_CLOSE; + sock->state = SS_UNCONNECTED; + + if (addr_len != sizeof(*addr)) + return -EINVAL; + + addr = (struct sockaddr_at *)uaddr; + + if (addr->sat_family != AF_APPLETALK) + return -EAFNOSUPPORT; + + if (addr->sat_addr.s_node == ATADDR_BCAST && + !sock_flag(sk, SOCK_BROADCAST)) { +#if 1 + printk(KERN_WARNING "%s is broken and did not set " + "SO_BROADCAST. It will break when 2.2 is " + "released.\n", + current->comm); +#else + return -EACCES; +#endif + } + + lock_kernel(); + err = -EBUSY; + if (sock_flag(sk, SOCK_ZAPPED)) + if (atalk_autobind(sk) < 0) + goto out; + + err = -ENETUNREACH; + if (!atrtr_get_dev(&addr->sat_addr)) + goto out; + + at->dest_port = addr->sat_port; + at->dest_net = addr->sat_addr.s_net; + at->dest_node = addr->sat_addr.s_node; + + sock->state = SS_CONNECTED; + sk->sk_state = TCP_ESTABLISHED; + err = 0; +out: + unlock_kernel(); + return err; +} + +/* + * Find the name of an AppleTalk socket. Just copy the right + * fields into the sockaddr. + */ +static int atalk_getname(struct socket *sock, struct sockaddr *uaddr, + int *uaddr_len, int peer) +{ + struct sockaddr_at sat; + struct sock *sk = sock->sk; + struct atalk_sock *at = at_sk(sk); + int err; + + lock_kernel(); + err = -ENOBUFS; + if (sock_flag(sk, SOCK_ZAPPED)) + if (atalk_autobind(sk) < 0) + goto out; + + *uaddr_len = sizeof(struct sockaddr_at); + memset(&sat.sat_zero, 0, sizeof(sat.sat_zero)); + + if (peer) { + err = -ENOTCONN; + if (sk->sk_state != TCP_ESTABLISHED) + goto out; + + sat.sat_addr.s_net = at->dest_net; + sat.sat_addr.s_node = at->dest_node; + sat.sat_port = at->dest_port; + } else { + sat.sat_addr.s_net = at->src_net; + sat.sat_addr.s_node = at->src_node; + sat.sat_port = at->src_port; + } + + err = 0; + sat.sat_family = AF_APPLETALK; + memcpy(uaddr, &sat, sizeof(sat)); + +out: + unlock_kernel(); + return err; +} + +static unsigned int atalk_poll(struct file *file, struct socket *sock, + poll_table *wait) +{ + int err; + lock_kernel(); + err = datagram_poll(file, sock, wait); + unlock_kernel(); + return err; +} + +#if defined(CONFIG_IPDDP) || defined(CONFIG_IPDDP_MODULE) +static __inline__ int is_ip_over_ddp(struct sk_buff *skb) +{ + return skb->data[12] == 22; +} + +static int handle_ip_over_ddp(struct sk_buff *skb) +{ + struct net_device *dev = __dev_get_by_name(&init_net, "ipddp0"); + struct net_device_stats *stats; + + /* This needs to be able to handle ipddp"N" devices */ + if (!dev) { + kfree_skb(skb); + return NET_RX_DROP; + } + + skb->protocol = htons(ETH_P_IP); + skb_pull(skb, 13); + skb->dev = dev; + skb_reset_transport_header(skb); + + stats = netdev_priv(dev); + stats->rx_packets++; + stats->rx_bytes += skb->len + 13; + return netif_rx(skb); /* Send the SKB up to a higher place. */ +} +#else +/* make it easy for gcc to optimize this test out, i.e. kill the code */ +#define is_ip_over_ddp(skb) 0 +#define handle_ip_over_ddp(skb) 0 +#endif + +static int atalk_route_packet(struct sk_buff *skb, struct net_device *dev, + struct ddpehdr *ddp, __u16 len_hops, int origlen) +{ + struct atalk_route *rt; + struct atalk_addr ta; + + /* + * Don't route multicast, etc., packets, or packets sent to "this + * network" + */ + if (skb->pkt_type != PACKET_HOST || !ddp->deh_dnet) { + /* + * FIXME: + * + * Can it ever happen that a packet is from a PPP iface and + * needs to be broadcast onto the default network? + */ + if (dev->type == ARPHRD_PPP) + printk(KERN_DEBUG "AppleTalk: didn't forward broadcast " + "packet received from PPP iface\n"); + goto free_it; + } + + ta.s_net = ddp->deh_dnet; + ta.s_node = ddp->deh_dnode; + + /* Route the packet */ + rt = atrtr_find(&ta); + /* increment hops count */ + len_hops += 1 << 10; + if (!rt || !(len_hops & (15 << 10))) + goto free_it; + + /* FIXME: use skb->cb to be able to use shared skbs */ + + /* + * Route goes through another gateway, so set the target to the + * gateway instead. + */ + + if (rt->flags & RTF_GATEWAY) { + ta.s_net = rt->gateway.s_net; + ta.s_node = rt->gateway.s_node; + } + + /* Fix up skb->len field */ + skb_trim(skb, min_t(unsigned int, origlen, + (rt->dev->hard_header_len + + ddp_dl->header_length + (len_hops & 1023)))); + + /* FIXME: use skb->cb to be able to use shared skbs */ + ddp->deh_len_hops = htons(len_hops); + + /* + * Send the buffer onwards + * + * Now we must always be careful. If it's come from LocalTalk to + * EtherTalk it might not fit + * + * Order matters here: If a packet has to be copied to make a new + * headroom (rare hopefully) then it won't need unsharing. + * + * Note. ddp-> becomes invalid at the realloc. + */ + if (skb_headroom(skb) < 22) { + /* 22 bytes - 12 ether, 2 len, 3 802.2 5 snap */ + struct sk_buff *nskb = skb_realloc_headroom(skb, 32); + kfree_skb(skb); + skb = nskb; + } else + skb = skb_unshare(skb, GFP_ATOMIC); + + /* + * If the buffer didn't vanish into the lack of space bitbucket we can + * send it. + */ + if (skb == NULL) + goto drop; + + if (aarp_send_ddp(rt->dev, skb, &ta, NULL) == NET_XMIT_DROP) + return NET_RX_DROP; + return NET_RX_SUCCESS; +free_it: + kfree_skb(skb); +drop: + return NET_RX_DROP; +} + +/** + * atalk_rcv - Receive a packet (in skb) from device dev + * @skb - packet received + * @dev - network device where the packet comes from + * @pt - packet type + * + * Receive a packet (in skb) from device dev. This has come from the SNAP + * decoder, and on entry skb->transport_header is the DDP header, skb->len + * is the DDP header, skb->len is the DDP length. The physical headers + * have been extracted. PPP should probably pass frames marked as for this + * layer. [ie ARPHRD_ETHERTALK] + */ +static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct ddpehdr *ddp; + struct sock *sock; + struct atalk_iface *atif; + struct sockaddr_at tosat; + int origlen; + __u16 len_hops; + + if (!net_eq(dev_net(dev), &init_net)) + goto drop; + + /* Don't mangle buffer if shared */ + if (!(skb = skb_share_check(skb, GFP_ATOMIC))) + goto out; + + /* Size check and make sure header is contiguous */ + if (!pskb_may_pull(skb, sizeof(*ddp))) + goto drop; + + ddp = ddp_hdr(skb); + + len_hops = ntohs(ddp->deh_len_hops); + + /* Trim buffer in case of stray trailing data */ + origlen = skb->len; + skb_trim(skb, min_t(unsigned int, skb->len, len_hops & 1023)); + + /* + * Size check to see if ddp->deh_len was crap + * (Otherwise we'll detonate most spectacularly + * in the middle of atalk_checksum() or recvmsg()). + */ + if (skb->len < sizeof(*ddp) || skb->len < (len_hops & 1023)) { + pr_debug("AppleTalk: dropping corrupted frame (deh_len=%u, " + "skb->len=%u)\n", len_hops & 1023, skb->len); + goto drop; + } + + /* + * Any checksums. Note we don't do htons() on this == is assumed to be + * valid for net byte orders all over the networking code... + */ + if (ddp->deh_sum && + atalk_checksum(skb, len_hops & 1023) != ddp->deh_sum) + /* Not a valid AppleTalk frame - dustbin time */ + goto drop; + + /* Check the packet is aimed at us */ + if (!ddp->deh_dnet) /* Net 0 is 'this network' */ + atif = atalk_find_anynet(ddp->deh_dnode, dev); + else + atif = atalk_find_interface(ddp->deh_dnet, ddp->deh_dnode); + + if (!atif) { + /* Not ours, so we route the packet via the correct + * AppleTalk iface + */ + return atalk_route_packet(skb, dev, ddp, len_hops, origlen); + } + + /* if IP over DDP is not selected this code will be optimized out */ + if (is_ip_over_ddp(skb)) + return handle_ip_over_ddp(skb); + /* + * Which socket - atalk_search_socket() looks for a *full match* + * of the tuple. + */ + tosat.sat_addr.s_net = ddp->deh_dnet; + tosat.sat_addr.s_node = ddp->deh_dnode; + tosat.sat_port = ddp->deh_dport; + + sock = atalk_search_socket(&tosat, atif); + if (!sock) /* But not one of our sockets */ + goto drop; + + /* Queue packet (standard) */ + skb->sk = sock; + + if (sock_queue_rcv_skb(sock, skb) < 0) + goto drop; + + return NET_RX_SUCCESS; + +drop: + kfree_skb(skb); +out: + return NET_RX_DROP; + +} + +/* + * Receive a LocalTalk frame. We make some demands on the caller here. + * Caller must provide enough headroom on the packet to pull the short + * header and append a long one. + */ +static int ltalk_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + if (!net_eq(dev_net(dev), &init_net)) + goto freeit; + + /* Expand any short form frames */ + if (skb_mac_header(skb)[2] == 1) { + struct ddpehdr *ddp; + /* Find our address */ + struct atalk_addr *ap = atalk_find_dev_addr(dev); + + if (!ap || skb->len < sizeof(__be16) || skb->len > 1023) + goto freeit; + + /* Don't mangle buffer if shared */ + if (!(skb = skb_share_check(skb, GFP_ATOMIC))) + return 0; + + /* + * The push leaves us with a ddephdr not an shdr, and + * handily the port bytes in the right place preset. + */ + ddp = (struct ddpehdr *) skb_push(skb, sizeof(*ddp) - 4); + + /* Now fill in the long header */ + + /* + * These two first. The mac overlays the new source/dest + * network information so we MUST copy these before + * we write the network numbers ! + */ + + ddp->deh_dnode = skb_mac_header(skb)[0]; /* From physical header */ + ddp->deh_snode = skb_mac_header(skb)[1]; /* From physical header */ + + ddp->deh_dnet = ap->s_net; /* Network number */ + ddp->deh_snet = ap->s_net; + ddp->deh_sum = 0; /* No checksum */ + /* + * Not sure about this bit... + */ + /* Non routable, so force a drop if we slip up later */ + ddp->deh_len_hops = htons(skb->len + (DDP_MAXHOPS << 10)); + } + skb_reset_transport_header(skb); + + return atalk_rcv(skb, dev, pt, orig_dev); +freeit: + kfree_skb(skb); + return 0; +} + +static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, + size_t len) +{ + struct sock *sk = sock->sk; + struct atalk_sock *at = at_sk(sk); + struct sockaddr_at *usat = (struct sockaddr_at *)msg->msg_name; + int flags = msg->msg_flags; + int loopback = 0; + struct sockaddr_at local_satalk, gsat; + struct sk_buff *skb; + struct net_device *dev; + struct ddpehdr *ddp; + int size; + struct atalk_route *rt; + int err; + + if (flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT)) + return -EINVAL; + + if (len > DDP_MAXSZ) + return -EMSGSIZE; + + lock_kernel(); + if (usat) { + err = -EBUSY; + if (sock_flag(sk, SOCK_ZAPPED)) + if (atalk_autobind(sk) < 0) + goto out; + + err = -EINVAL; + if (msg->msg_namelen < sizeof(*usat) || + usat->sat_family != AF_APPLETALK) + goto out; + + err = -EPERM; + /* netatalk didn't implement this check */ + if (usat->sat_addr.s_node == ATADDR_BCAST && + !sock_flag(sk, SOCK_BROADCAST)) { + goto out; + } + } else { + err = -ENOTCONN; + if (sk->sk_state != TCP_ESTABLISHED) + goto out; + usat = &local_satalk; + usat->sat_family = AF_APPLETALK; + usat->sat_port = at->dest_port; + usat->sat_addr.s_node = at->dest_node; + usat->sat_addr.s_net = at->dest_net; + } + + /* Build a packet */ + SOCK_DEBUG(sk, "SK %p: Got address.\n", sk); + + /* For headers */ + size = sizeof(struct ddpehdr) + len + ddp_dl->header_length; + + if (usat->sat_addr.s_net || usat->sat_addr.s_node == ATADDR_ANYNODE) { + rt = atrtr_find(&usat->sat_addr); + } else { + struct atalk_addr at_hint; + + at_hint.s_node = 0; + at_hint.s_net = at->src_net; + + rt = atrtr_find(&at_hint); + } + err = ENETUNREACH; + if (!rt) + goto out; + + dev = rt->dev; + + SOCK_DEBUG(sk, "SK %p: Size needed %d, device %s\n", + sk, size, dev->name); + + size += dev->hard_header_len; + skb = sock_alloc_send_skb(sk, size, (flags & MSG_DONTWAIT), &err); + if (!skb) + goto out; + + skb->sk = sk; + skb_reserve(skb, ddp_dl->header_length); + skb_reserve(skb, dev->hard_header_len); + skb->dev = dev; + + SOCK_DEBUG(sk, "SK %p: Begin build.\n", sk); + + ddp = (struct ddpehdr *)skb_put(skb, sizeof(struct ddpehdr)); + ddp->deh_len_hops = htons(len + sizeof(*ddp)); + ddp->deh_dnet = usat->sat_addr.s_net; + ddp->deh_snet = at->src_net; + ddp->deh_dnode = usat->sat_addr.s_node; + ddp->deh_snode = at->src_node; + ddp->deh_dport = usat->sat_port; + ddp->deh_sport = at->src_port; + + SOCK_DEBUG(sk, "SK %p: Copy user data (%Zd bytes).\n", sk, len); + + err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); + if (err) { + kfree_skb(skb); + err = -EFAULT; + goto out; + } + + if (sk->sk_no_check == 1) + ddp->deh_sum = 0; + else + ddp->deh_sum = atalk_checksum(skb, len + sizeof(*ddp)); + + /* + * Loopback broadcast packets to non gateway targets (ie routes + * to group we are in) + */ + if (ddp->deh_dnode == ATADDR_BCAST && + !(rt->flags & RTF_GATEWAY) && !(dev->flags & IFF_LOOPBACK)) { + struct sk_buff *skb2 = skb_copy(skb, GFP_KERNEL); + + if (skb2) { + loopback = 1; + SOCK_DEBUG(sk, "SK %p: send out(copy).\n", sk); + /* + * If it fails it is queued/sent above in the aarp queue + */ + aarp_send_ddp(dev, skb2, &usat->sat_addr, NULL); + } + } + + if (dev->flags & IFF_LOOPBACK || loopback) { + SOCK_DEBUG(sk, "SK %p: Loop back.\n", sk); + /* loop back */ + skb_orphan(skb); + if (ddp->deh_dnode == ATADDR_BCAST) { + struct atalk_addr at_lo; + + at_lo.s_node = 0; + at_lo.s_net = 0; + + rt = atrtr_find(&at_lo); + if (!rt) { + kfree_skb(skb); + err = -ENETUNREACH; + goto out; + } + dev = rt->dev; + skb->dev = dev; + } + ddp_dl->request(ddp_dl, skb, dev->dev_addr); + } else { + SOCK_DEBUG(sk, "SK %p: send out.\n", sk); + if (rt->flags & RTF_GATEWAY) { + gsat.sat_addr = rt->gateway; + usat = &gsat; + } + + /* + * If it fails it is queued/sent above in the aarp queue + */ + aarp_send_ddp(dev, skb, &usat->sat_addr, NULL); + } + SOCK_DEBUG(sk, "SK %p: Done write (%Zd).\n", sk, len); + +out: + unlock_kernel(); + return err ? : len; +} + +static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, + size_t size, int flags) +{ + struct sock *sk = sock->sk; + struct sockaddr_at *sat = (struct sockaddr_at *)msg->msg_name; + struct ddpehdr *ddp; + int copied = 0; + int offset = 0; + int err = 0; + struct sk_buff *skb; + + lock_kernel(); + skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, + flags & MSG_DONTWAIT, &err); + if (!skb) + goto out; + + /* FIXME: use skb->cb to be able to use shared skbs */ + ddp = ddp_hdr(skb); + copied = ntohs(ddp->deh_len_hops) & 1023; + + if (sk->sk_type != SOCK_RAW) { + offset = sizeof(*ddp); + copied -= offset; + } + + if (copied > size) { + copied = size; + msg->msg_flags |= MSG_TRUNC; + } + err = skb_copy_datagram_iovec(skb, offset, msg->msg_iov, copied); + + if (!err) { + if (sat) { + sat->sat_family = AF_APPLETALK; + sat->sat_port = ddp->deh_sport; + sat->sat_addr.s_node = ddp->deh_snode; + sat->sat_addr.s_net = ddp->deh_snet; + } + msg->msg_namelen = sizeof(*sat); + } + + skb_free_datagram(sk, skb); /* Free the datagram. */ + +out: + unlock_kernel(); + return err ? : copied; +} + + +/* + * AppleTalk ioctl calls. + */ +static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + int rc = -ENOIOCTLCMD; + struct sock *sk = sock->sk; + void __user *argp = (void __user *)arg; + + switch (cmd) { + /* Protocol layer */ + case TIOCOUTQ: { + long amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); + + if (amount < 0) + amount = 0; + rc = put_user(amount, (int __user *)argp); + break; + } + case TIOCINQ: { + /* + * These two are safe on a single CPU system as only + * user tasks fiddle here + */ + struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); + long amount = 0; + + if (skb) + amount = skb->len - sizeof(struct ddpehdr); + rc = put_user(amount, (int __user *)argp); + break; + } + case SIOCGSTAMP: + rc = sock_get_timestamp(sk, argp); + break; + case SIOCGSTAMPNS: + rc = sock_get_timestampns(sk, argp); + break; + /* Routing */ + case SIOCADDRT: + case SIOCDELRT: + rc = -EPERM; + if (capable(CAP_NET_ADMIN)) + rc = atrtr_ioctl(cmd, argp); + break; + /* Interface */ + case SIOCGIFADDR: + case SIOCSIFADDR: + case SIOCGIFBRDADDR: + case SIOCATALKDIFADDR: + case SIOCDIFADDR: + case SIOCSARP: /* proxy AARP */ + case SIOCDARP: /* proxy AARP */ + rtnl_lock(); + rc = atif_ioctl(cmd, argp); + rtnl_unlock(); + break; + } + + return rc; +} + + +#ifdef CONFIG_COMPAT +static int atalk_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + /* + * SIOCATALKDIFADDR is a SIOCPROTOPRIVATE ioctl number, so we + * cannot handle it in common code. The data we access if ifreq + * here is compatible, so we can simply call the native + * handler. + */ + if (cmd == SIOCATALKDIFADDR) + return atalk_ioctl(sock, cmd, (unsigned long)compat_ptr(arg)); + + return -ENOIOCTLCMD; +} +#endif + + +static const struct net_proto_family atalk_family_ops = { + .family = PF_APPLETALK, + .create = atalk_create, + .owner = THIS_MODULE, +}; + +static const struct proto_ops atalk_dgram_ops = { + .family = PF_APPLETALK, + .owner = THIS_MODULE, + .release = atalk_release, + .bind = atalk_bind, + .connect = atalk_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = atalk_getname, + .poll = atalk_poll, + .ioctl = atalk_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = atalk_compat_ioctl, +#endif + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = atalk_sendmsg, + .recvmsg = atalk_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +static struct notifier_block ddp_notifier = { + .notifier_call = ddp_device_event, +}; + +static struct packet_type ltalk_packet_type __read_mostly = { + .type = cpu_to_be16(ETH_P_LOCALTALK), + .func = ltalk_rcv, +}; + +static struct packet_type ppptalk_packet_type __read_mostly = { + .type = cpu_to_be16(ETH_P_PPPTALK), + .func = atalk_rcv, +}; + +static unsigned char ddp_snap_id[] = { 0x08, 0x00, 0x07, 0x80, 0x9B }; + +/* Export symbols for use by drivers when AppleTalk is a module */ +EXPORT_SYMBOL(atrtr_get_dev); +EXPORT_SYMBOL(atalk_find_dev_addr); + +static const char atalk_err_snap[] __initconst = + KERN_CRIT "Unable to register DDP with SNAP.\n"; + +/* Called by proto.c on kernel start up */ +static int __init atalk_init(void) +{ + int rc = proto_register(&ddp_proto, 0); + + if (rc != 0) + goto out; + + (void)sock_register(&atalk_family_ops); + ddp_dl = register_snap_client(ddp_snap_id, atalk_rcv); + if (!ddp_dl) + printk(atalk_err_snap); + + dev_add_pack(<alk_packet_type); + dev_add_pack(&ppptalk_packet_type); + + register_netdevice_notifier(&ddp_notifier); + aarp_proto_init(); + atalk_proc_init(); + atalk_register_sysctl(); +out: + return rc; +} +module_init(atalk_init); + +/* + * No explicit module reference count manipulation is needed in the + * protocol. Socket layer sets module reference count for us + * and interfaces reference counting is done + * by the network device layer. + * + * Ergo, before the AppleTalk module can be removed, all AppleTalk + * sockets be closed from user space. + */ +static void __exit atalk_exit(void) +{ +#ifdef CONFIG_SYSCTL + atalk_unregister_sysctl(); +#endif /* CONFIG_SYSCTL */ + atalk_proc_exit(); + aarp_cleanup_module(); /* General aarp clean-up. */ + unregister_netdevice_notifier(&ddp_notifier); + dev_remove_pack(<alk_packet_type); + dev_remove_pack(&ppptalk_packet_type); + unregister_snap_client(ddp_dl); + sock_unregister(PF_APPLETALK); + proto_unregister(&ddp_proto); +} +module_exit(atalk_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alan Cox "); +MODULE_DESCRIPTION("AppleTalk 0.20\n"); +MODULE_ALIAS_NETPROTO(PF_APPLETALK); diff --git a/drivers/staging/appletalk/dev.c b/drivers/staging/appletalk/dev.c new file mode 100644 index 000000000000..6c8016f61866 --- /dev/null +++ b/drivers/staging/appletalk/dev.c @@ -0,0 +1,44 @@ +/* + * Moved here from drivers/net/net_init.c, which is: + * Written 1993,1994,1995 by Donald Becker. + */ + +#include +#include +#include +#include +#include + +static void ltalk_setup(struct net_device *dev) +{ + /* Fill in the fields of the device structure with localtalk-generic values. */ + + dev->type = ARPHRD_LOCALTLK; + dev->hard_header_len = LTALK_HLEN; + dev->mtu = LTALK_MTU; + dev->addr_len = LTALK_ALEN; + dev->tx_queue_len = 10; + + dev->broadcast[0] = 0xFF; + + dev->flags = IFF_BROADCAST|IFF_MULTICAST|IFF_NOARP; +} + +/** + * alloc_ltalkdev - Allocates and sets up an localtalk device + * @sizeof_priv: Size of additional driver-private structure to be allocated + * for this localtalk device + * + * Fill in the fields of the device structure with localtalk-generic + * values. Basically does everything except registering the device. + * + * Constructs a new net device, complete with a private data area of + * size @sizeof_priv. A 32-byte (not bit) alignment is enforced for + * this private data area. + */ + +struct net_device *alloc_ltalkdev(int sizeof_priv) +{ + return alloc_netdev(sizeof_priv, "lt%d", ltalk_setup); +} +EXPORT_SYMBOL(alloc_ltalkdev); diff --git a/drivers/staging/appletalk/ipddp.c b/drivers/staging/appletalk/ipddp.c new file mode 100644 index 000000000000..58b4e6098ad4 --- /dev/null +++ b/drivers/staging/appletalk/ipddp.c @@ -0,0 +1,335 @@ +/* + * ipddp.c: IP to Appletalk-IP Encapsulation driver for Linux + * Appletalk-IP to IP Decapsulation driver for Linux + * + * Authors: + * - DDP-IP Encap by: Bradford W. Johnson + * - DDP-IP Decap by: Jay Schulist + * + * Derived from: + * - Almost all code already existed in net/appletalk/ddp.c I just + * moved/reorginized it into a driver file. Original IP-over-DDP code + * was done by Bradford W. Johnson + * - skeleton.c: A network driver outline for linux. + * Written 1993-94 by Donald Becker. + * - dummy.c: A dummy net driver. By Nick Holloway. + * - MacGate: A user space Daemon for Appletalk-IP Decap for + * Linux by Jay Schulist + * + * Copyright 1993 United States Government as represented by the + * Director, National Security Agency. + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atalk.h" +#include "ipddp.h" /* Our stuff */ + +static const char version[] = KERN_INFO "ipddp.c:v0.01 8/28/97 Bradford W. Johnson \n"; + +static struct ipddp_route *ipddp_route_list; +static DEFINE_SPINLOCK(ipddp_route_lock); + +#ifdef CONFIG_IPDDP_ENCAP +static int ipddp_mode = IPDDP_ENCAP; +#else +static int ipddp_mode = IPDDP_DECAP; +#endif + +/* Index to functions, as function prototypes. */ +static netdev_tx_t ipddp_xmit(struct sk_buff *skb, + struct net_device *dev); +static int ipddp_create(struct ipddp_route *new_rt); +static int ipddp_delete(struct ipddp_route *rt); +static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt); +static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); + +static const struct net_device_ops ipddp_netdev_ops = { + .ndo_start_xmit = ipddp_xmit, + .ndo_do_ioctl = ipddp_ioctl, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static struct net_device * __init ipddp_init(void) +{ + static unsigned version_printed; + struct net_device *dev; + int err; + + dev = alloc_etherdev(0); + if (!dev) + return ERR_PTR(-ENOMEM); + + dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; + strcpy(dev->name, "ipddp%d"); + + if (version_printed++ == 0) + printk(version); + + /* Initialize the device structure. */ + dev->netdev_ops = &ipddp_netdev_ops; + + dev->type = ARPHRD_IPDDP; /* IP over DDP tunnel */ + dev->mtu = 585; + dev->flags |= IFF_NOARP; + + /* + * The worst case header we will need is currently a + * ethernet header (14 bytes) and a ddp header (sizeof ddpehdr+1) + * We send over SNAP so that takes another 8 bytes. + */ + dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1; + + err = register_netdev(dev); + if (err) { + free_netdev(dev); + return ERR_PTR(err); + } + + /* Let the user now what mode we are in */ + if(ipddp_mode == IPDDP_ENCAP) + printk("%s: Appletalk-IP Encap. mode by Bradford W. Johnson \n", + dev->name); + if(ipddp_mode == IPDDP_DECAP) + printk("%s: Appletalk-IP Decap. mode by Jay Schulist \n", + dev->name); + + return dev; +} + + +/* + * Transmit LLAP/ELAP frame using aarp_send_ddp. + */ +static netdev_tx_t ipddp_xmit(struct sk_buff *skb, struct net_device *dev) +{ + __be32 paddr = skb_rtable(skb)->rt_gateway; + struct ddpehdr *ddp; + struct ipddp_route *rt; + struct atalk_addr *our_addr; + + spin_lock(&ipddp_route_lock); + + /* + * Find appropriate route to use, based only on IP number. + */ + for(rt = ipddp_route_list; rt != NULL; rt = rt->next) + { + if(rt->ip == paddr) + break; + } + if(rt == NULL) { + spin_unlock(&ipddp_route_lock); + return NETDEV_TX_OK; + } + + our_addr = atalk_find_dev_addr(rt->dev); + + if(ipddp_mode == IPDDP_DECAP) + /* + * Pull off the excess room that should not be there. + * This is due to a hard-header problem. This is the + * quick fix for now though, till it breaks. + */ + skb_pull(skb, 35-(sizeof(struct ddpehdr)+1)); + + /* Create the Extended DDP header */ + ddp = (struct ddpehdr *)skb->data; + ddp->deh_len_hops = htons(skb->len + (1<<10)); + ddp->deh_sum = 0; + + /* + * For Localtalk we need aarp_send_ddp to strip the + * long DDP header and place a shot DDP header on it. + */ + if(rt->dev->type == ARPHRD_LOCALTLK) + { + ddp->deh_dnet = 0; /* FIXME more hops?? */ + ddp->deh_snet = 0; + } + else + { + ddp->deh_dnet = rt->at.s_net; /* FIXME more hops?? */ + ddp->deh_snet = our_addr->s_net; + } + ddp->deh_dnode = rt->at.s_node; + ddp->deh_snode = our_addr->s_node; + ddp->deh_dport = 72; + ddp->deh_sport = 72; + + *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */ + + skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */ + + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + aarp_send_ddp(rt->dev, skb, &rt->at, NULL); + + spin_unlock(&ipddp_route_lock); + + return NETDEV_TX_OK; +} + +/* + * Create a routing entry. We first verify that the + * record does not already exist. If it does we return -EEXIST + */ +static int ipddp_create(struct ipddp_route *new_rt) +{ + struct ipddp_route *rt = kmalloc(sizeof(*rt), GFP_KERNEL); + + if (rt == NULL) + return -ENOMEM; + + rt->ip = new_rt->ip; + rt->at = new_rt->at; + rt->next = NULL; + if ((rt->dev = atrtr_get_dev(&rt->at)) == NULL) { + kfree(rt); + return -ENETUNREACH; + } + + spin_lock_bh(&ipddp_route_lock); + if (__ipddp_find_route(rt)) { + spin_unlock_bh(&ipddp_route_lock); + kfree(rt); + return -EEXIST; + } + + rt->next = ipddp_route_list; + ipddp_route_list = rt; + + spin_unlock_bh(&ipddp_route_lock); + + return 0; +} + +/* + * Delete a route, we only delete a FULL match. + * If route does not exist we return -ENOENT. + */ +static int ipddp_delete(struct ipddp_route *rt) +{ + struct ipddp_route **r = &ipddp_route_list; + struct ipddp_route *tmp; + + spin_lock_bh(&ipddp_route_lock); + while((tmp = *r) != NULL) + { + if(tmp->ip == rt->ip && + tmp->at.s_net == rt->at.s_net && + tmp->at.s_node == rt->at.s_node) + { + *r = tmp->next; + spin_unlock_bh(&ipddp_route_lock); + kfree(tmp); + return 0; + } + r = &tmp->next; + } + + spin_unlock_bh(&ipddp_route_lock); + return -ENOENT; +} + +/* + * Find a routing entry, we only return a FULL match + */ +static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt) +{ + struct ipddp_route *f; + + for(f = ipddp_route_list; f != NULL; f = f->next) + { + if(f->ip == rt->ip && + f->at.s_net == rt->at.s_net && + f->at.s_node == rt->at.s_node) + return f; + } + + return NULL; +} + +static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct ipddp_route __user *rt = ifr->ifr_data; + struct ipddp_route rcp, rcp2, *rp; + + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(copy_from_user(&rcp, rt, sizeof(rcp))) + return -EFAULT; + + switch(cmd) + { + case SIOCADDIPDDPRT: + return ipddp_create(&rcp); + + case SIOCFINDIPDDPRT: + spin_lock_bh(&ipddp_route_lock); + rp = __ipddp_find_route(&rcp); + if (rp) + memcpy(&rcp2, rp, sizeof(rcp2)); + spin_unlock_bh(&ipddp_route_lock); + + if (rp) { + if (copy_to_user(rt, &rcp2, + sizeof(struct ipddp_route))) + return -EFAULT; + return 0; + } else + return -ENOENT; + + case SIOCDELIPDDPRT: + return ipddp_delete(&rcp); + + default: + return -EINVAL; + } +} + +static struct net_device *dev_ipddp; + +MODULE_LICENSE("GPL"); +module_param(ipddp_mode, int, 0); + +static int __init ipddp_init_module(void) +{ + dev_ipddp = ipddp_init(); + if (IS_ERR(dev_ipddp)) + return PTR_ERR(dev_ipddp); + return 0; +} + +static void __exit ipddp_cleanup_module(void) +{ + struct ipddp_route *p; + + unregister_netdev(dev_ipddp); + free_netdev(dev_ipddp); + + while (ipddp_route_list) { + p = ipddp_route_list->next; + kfree(ipddp_route_list); + ipddp_route_list = p; + } +} + +module_init(ipddp_init_module); +module_exit(ipddp_cleanup_module); diff --git a/drivers/staging/appletalk/ipddp.h b/drivers/staging/appletalk/ipddp.h new file mode 100644 index 000000000000..531519da99a3 --- /dev/null +++ b/drivers/staging/appletalk/ipddp.h @@ -0,0 +1,27 @@ +/* + * ipddp.h: Header for IP-over-DDP driver for Linux. + */ + +#ifndef __LINUX_IPDDP_H +#define __LINUX_IPDDP_H + +#ifdef __KERNEL__ + +#define SIOCADDIPDDPRT (SIOCDEVPRIVATE) +#define SIOCDELIPDDPRT (SIOCDEVPRIVATE+1) +#define SIOCFINDIPDDPRT (SIOCDEVPRIVATE+2) + +struct ipddp_route +{ + struct net_device *dev; /* Carrier device */ + __be32 ip; /* IP address */ + struct atalk_addr at; /* Gateway appletalk address */ + int flags; + struct ipddp_route *next; +}; + +#define IPDDP_ENCAP 1 +#define IPDDP_DECAP 2 + +#endif /* __KERNEL__ */ +#endif /* __LINUX_IPDDP_H */ diff --git a/drivers/staging/appletalk/ltpc.c b/drivers/staging/appletalk/ltpc.c new file mode 100644 index 000000000000..60caf892695e --- /dev/null +++ b/drivers/staging/appletalk/ltpc.c @@ -0,0 +1,1288 @@ +/*** ltpc.c -- a driver for the LocalTalk PC card. + * + * Copyright (c) 1995,1996 Bradford W. Johnson + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * This is ALPHA code at best. It may not work for you. It may + * damage your equipment. It may damage your relations with other + * users of your network. Use it at your own risk! + * + * Based in part on: + * skeleton.c by Donald Becker + * dummy.c by Nick Holloway and Alan Cox + * loopback.c by Ross Biro, Fred van Kampen, Donald Becker + * the netatalk source code (UMICH) + * lots of work on the card... + * + * I do not have access to the (proprietary) SDK that goes with the card. + * If you do, I don't want to know about it, and you can probably write + * a better driver yourself anyway. This does mean that the pieces that + * talk to the card are guesswork on my part, so use at your own risk! + * + * This is my first try at writing Linux networking code, and is also + * guesswork. Again, use at your own risk! (Although on this part, I'd + * welcome suggestions) + * + * This is a loadable kernel module which seems to work at my site + * consisting of a 1.2.13 linux box running netatalk 1.3.3, and with + * the kernel support from 1.3.3b2 including patches routing.patch + * and ddp.disappears.from.chooser. In order to run it, you will need + * to patch ddp.c and aarp.c in the kernel, but only a little... + * + * I'm fairly confident that while this is arguably badly written, the + * problems that people experience will be "higher level", that is, with + * complications in the netatalk code. The driver itself doesn't do + * anything terribly complicated -- it pretends to be an ether device + * as far as netatalk is concerned, strips the DDP data out of the ether + * frame and builds a LLAP packet to send out the card. In the other + * direction, it receives LLAP frames from the card and builds a fake + * ether packet that it then tosses up to the networking code. You can + * argue (correctly) that this is an ugly way to do things, but it + * requires a minimal amount of fooling with the code in ddp.c and aarp.c. + * + * The card will do a lot more than is used here -- I *think* it has the + * layers up through ATP. Even if you knew how that part works (which I + * don't) it would be a big job to carve up the kernel ddp code to insert + * things at a higher level, and probably a bad idea... + * + * There are a number of other cards that do LocalTalk on the PC. If + * nobody finds any insurmountable (at the netatalk level) problems + * here, this driver should encourage people to put some work into the + * other cards (some of which I gather are still commercially available) + * and also to put hooks for LocalTalk into the official ddp code. + * + * I welcome comments and suggestions. This is my first try at Linux + * networking stuff, and there are probably lots of things that I did + * suboptimally. + * + ***/ + +/*** + * + * $Log: ltpc.c,v $ + * Revision 1.1.2.1 2000/03/01 05:35:07 jgarzik + * at and tr cleanup + * + * Revision 1.8 1997/01/28 05:44:54 bradford + * Clean up for non-module a little. + * Hacked about a bit to clean things up - Alan Cox + * Probably broken it from the origina 1.8 + * + + * 1998/11/09: David Huggins-Daines + * Cleaned up the initialization code to use the standard autoirq methods, + and to probe for things in the standard order of i/o, irq, dma. This + removes the "reset the reset" hack, because I couldn't figure out an + easy way to get the card to trigger an interrupt after it. + * Added support for passing configuration parameters on the kernel command + line and through insmod + * Changed the device name from "ltalk0" to "lt0", both to conform with the + other localtalk driver, and to clear up the inconsistency between the + module and the non-module versions of the driver :-) + * Added a bunch of comments (I was going to make some enums for the state + codes and the register offsets, but I'm still not sure exactly what their + semantics are) + * Don't poll anymore in interrupt-driven mode + * It seems to work as a module now (as of 2.1.127), but I don't think + I'm responsible for that... + + * + * Revision 1.7 1996/12/12 03:42:33 bradford + * DMA alloc cribbed from 3c505.c. + * + * Revision 1.6 1996/12/12 03:18:58 bradford + * Added virt_to_bus; works in 2.1.13. + * + * Revision 1.5 1996/12/12 03:13:22 root + * xmitQel initialization -- think through better though. + * + * Revision 1.4 1996/06/18 14:55:55 root + * Change names to ltpc. Tabs. Took a shot at dma alloc, + * although more needs to be done eventually. + * + * Revision 1.3 1996/05/22 14:59:39 root + * Change dev->open, dev->close to track dummy.c in 1.99.(around 7) + * + * Revision 1.2 1996/05/22 14:58:24 root + * Change tabs mostly. + * + * Revision 1.1 1996/04/23 04:45:09 root + * Initial revision + * + * Revision 0.16 1996/03/05 15:59:56 root + * Change ARPHRD_LOCALTLK definition to the "real" one. + * + * Revision 0.15 1996/03/05 06:28:30 root + * Changes for kernel 1.3.70. Still need a few patches to kernel, but + * it's getting closer. + * + * Revision 0.14 1996/02/25 17:38:32 root + * More cleanups. Removed query to card on get_stats. + * + * Revision 0.13 1996/02/21 16:27:40 root + * Refix debug_print_skb. Fix mac.raw gotcha that appeared in 1.3.65. + * Clean up receive code a little. + * + * Revision 0.12 1996/02/19 16:34:53 root + * Fix debug_print_skb. Kludge outgoing snet to 0 when using startup + * range. Change debug to mask: 1 for verbose, 2 for higher level stuff + * including packet printing, 4 for lower level (card i/o) stuff. + * + * Revision 0.11 1996/02/12 15:53:38 root + * Added router sends (requires new aarp.c patch) + * + * Revision 0.10 1996/02/11 00:19:35 root + * Change source LTALK_LOGGING debug switch to insmod ... debug=2. + * + * Revision 0.9 1996/02/10 23:59:35 root + * Fixed those fixes for 1.2 -- DANGER! The at.h that comes with netatalk + * has a *different* definition of struct sockaddr_at than the Linux kernel + * does. This is an "insidious and invidious" bug... + * (Actually the preceding comment is false -- it's the atalk.h in the + * ancient atalk-0.06 that's the problem) + * + * Revision 0.8 1996/02/10 19:09:00 root + * Merge 1.3 changes. Tested OK under 1.3.60. + * + * Revision 0.7 1996/02/10 17:56:56 root + * Added debug=1 parameter on insmod for debugging prints. Tried + * to fix timer unload on rmmod, but I don't think that's the problem. + * + * Revision 0.6 1995/12/31 19:01:09 root + * Clean up rmmod, irq comments per feedback from Corin Anderson (Thanks Corey!) + * Clean up initial probing -- sometimes the card wakes up latched in reset. + * + * Revision 0.5 1995/12/22 06:03:44 root + * Added comments in front and cleaned up a bit. + * This version sent out to people. + * + * Revision 0.4 1995/12/18 03:46:44 root + * Return shortDDP to longDDP fake to 0/0. Added command structs. + * + ***/ + +/* ltpc jumpers are: +* +* Interrupts -- set at most one. If none are set, the driver uses +* polled mode. Because the card was developed in the XT era, the +* original documentation refers to IRQ2. Since you'll be running +* this on an AT (or later) class machine, that really means IRQ9. +* +* SW1 IRQ 4 +* SW2 IRQ 3 +* SW3 IRQ 9 (2 in original card documentation only applies to XT) +* +* +* DMA -- choose DMA 1 or 3, and set both corresponding switches. +* +* SW4 DMA 3 +* SW5 DMA 1 +* SW6 DMA 3 +* SW7 DMA 1 +* +* +* I/O address -- choose one. +* +* SW8 220 / 240 +*/ + +/* To have some stuff logged, do +* insmod ltpc.o debug=1 +* +* For a whole bunch of stuff, use higher numbers. +* +* The default is 0, i.e. no messages except for the probe results. +*/ + +/* insmod-tweakable variables */ +static int debug; +#define DEBUG_VERBOSE 1 +#define DEBUG_UPPER 2 +#define DEBUG_LOWER 4 + +static int io; +static int irq; +static int dma; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* our stuff */ +#include "atalk.h" +#include "ltpc.h" + +static DEFINE_SPINLOCK(txqueue_lock); +static DEFINE_SPINLOCK(mbox_lock); + +/* function prototypes */ +static int do_read(struct net_device *dev, void *cbuf, int cbuflen, + void *dbuf, int dbuflen); +static int sendup_buffer (struct net_device *dev); + +/* Dma Memory related stuff, cribbed directly from 3c505.c */ + +static unsigned long dma_mem_alloc(int size) +{ + int order = get_order(size); + + return __get_dma_pages(GFP_KERNEL, order); +} + +/* DMA data buffer, DMA command buffer */ +static unsigned char *ltdmabuf; +static unsigned char *ltdmacbuf; + +/* private struct, holds our appletalk address */ + +struct ltpc_private +{ + struct atalk_addr my_addr; +}; + +/* transmit queue element struct */ + +struct xmitQel { + struct xmitQel *next; + /* command buffer */ + unsigned char *cbuf; + short cbuflen; + /* data buffer */ + unsigned char *dbuf; + short dbuflen; + unsigned char QWrite; /* read or write data */ + unsigned char mailbox; +}; + +/* the transmit queue itself */ + +static struct xmitQel *xmQhd, *xmQtl; + +static void enQ(struct xmitQel *qel) +{ + unsigned long flags; + qel->next = NULL; + + spin_lock_irqsave(&txqueue_lock, flags); + if (xmQtl) { + xmQtl->next = qel; + } else { + xmQhd = qel; + } + xmQtl = qel; + spin_unlock_irqrestore(&txqueue_lock, flags); + + if (debug & DEBUG_LOWER) + printk("enqueued a 0x%02x command\n",qel->cbuf[0]); +} + +static struct xmitQel *deQ(void) +{ + unsigned long flags; + int i; + struct xmitQel *qel=NULL; + + spin_lock_irqsave(&txqueue_lock, flags); + if (xmQhd) { + qel = xmQhd; + xmQhd = qel->next; + if(!xmQhd) xmQtl = NULL; + } + spin_unlock_irqrestore(&txqueue_lock, flags); + + if ((debug & DEBUG_LOWER) && qel) { + int n; + printk(KERN_DEBUG "ltpc: dequeued command "); + n = qel->cbuflen; + if (n>100) n=100; + for(i=0;icbuf[i]); + printk("\n"); + } + + return qel; +} + +/* and... the queue elements we'll be using */ +static struct xmitQel qels[16]; + +/* and their corresponding mailboxes */ +static unsigned char mailbox[16]; +static unsigned char mboxinuse[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +static int wait_timeout(struct net_device *dev, int c) +{ + /* returns true if it stayed c */ + /* this uses base+6, but it's ok */ + int i; + + /* twenty second or so total */ + + for(i=0;i<200000;i++) { + if ( c != inb_p(dev->base_addr+6) ) return 0; + udelay(100); + } + return 1; /* timed out */ +} + +/* get the first free mailbox */ + +static int getmbox(void) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&mbox_lock, flags); + for(i=1;i<16;i++) if(!mboxinuse[i]) { + mboxinuse[i]=1; + spin_unlock_irqrestore(&mbox_lock, flags); + return i; + } + spin_unlock_irqrestore(&mbox_lock, flags); + return 0; +} + +/* read a command from the card */ +static void handlefc(struct net_device *dev) +{ + /* called *only* from idle, non-reentrant */ + int dma = dev->dma; + int base = dev->base_addr; + unsigned long flags; + + + flags=claim_dma_lock(); + disable_dma(dma); + clear_dma_ff(dma); + set_dma_mode(dma,DMA_MODE_READ); + set_dma_addr(dma,virt_to_bus(ltdmacbuf)); + set_dma_count(dma,50); + enable_dma(dma); + release_dma_lock(flags); + + inb_p(base+3); + inb_p(base+2); + + if ( wait_timeout(dev,0xfc) ) printk("timed out in handlefc\n"); +} + +/* read data from the card */ +static void handlefd(struct net_device *dev) +{ + int dma = dev->dma; + int base = dev->base_addr; + unsigned long flags; + + flags=claim_dma_lock(); + disable_dma(dma); + clear_dma_ff(dma); + set_dma_mode(dma,DMA_MODE_READ); + set_dma_addr(dma,virt_to_bus(ltdmabuf)); + set_dma_count(dma,800); + enable_dma(dma); + release_dma_lock(flags); + + inb_p(base+3); + inb_p(base+2); + + if ( wait_timeout(dev,0xfd) ) printk("timed out in handlefd\n"); + sendup_buffer(dev); +} + +static void handlewrite(struct net_device *dev) +{ + /* called *only* from idle, non-reentrant */ + /* on entry, 0xfb and ltdmabuf holds data */ + int dma = dev->dma; + int base = dev->base_addr; + unsigned long flags; + + flags=claim_dma_lock(); + disable_dma(dma); + clear_dma_ff(dma); + set_dma_mode(dma,DMA_MODE_WRITE); + set_dma_addr(dma,virt_to_bus(ltdmabuf)); + set_dma_count(dma,800); + enable_dma(dma); + release_dma_lock(flags); + + inb_p(base+3); + inb_p(base+2); + + if ( wait_timeout(dev,0xfb) ) { + flags=claim_dma_lock(); + printk("timed out in handlewrite, dma res %d\n", + get_dma_residue(dev->dma) ); + release_dma_lock(flags); + } +} + +static void handleread(struct net_device *dev) +{ + /* on entry, 0xfb */ + /* on exit, ltdmabuf holds data */ + int dma = dev->dma; + int base = dev->base_addr; + unsigned long flags; + + + flags=claim_dma_lock(); + disable_dma(dma); + clear_dma_ff(dma); + set_dma_mode(dma,DMA_MODE_READ); + set_dma_addr(dma,virt_to_bus(ltdmabuf)); + set_dma_count(dma,800); + enable_dma(dma); + release_dma_lock(flags); + + inb_p(base+3); + inb_p(base+2); + if ( wait_timeout(dev,0xfb) ) printk("timed out in handleread\n"); +} + +static void handlecommand(struct net_device *dev) +{ + /* on entry, 0xfa and ltdmacbuf holds command */ + int dma = dev->dma; + int base = dev->base_addr; + unsigned long flags; + + flags=claim_dma_lock(); + disable_dma(dma); + clear_dma_ff(dma); + set_dma_mode(dma,DMA_MODE_WRITE); + set_dma_addr(dma,virt_to_bus(ltdmacbuf)); + set_dma_count(dma,50); + enable_dma(dma); + release_dma_lock(flags); + inb_p(base+3); + inb_p(base+2); + if ( wait_timeout(dev,0xfa) ) printk("timed out in handlecommand\n"); +} + +/* ready made command for getting the result from the card */ +static unsigned char rescbuf[2] = {LT_GETRESULT,0}; +static unsigned char resdbuf[2]; + +static int QInIdle; + +/* idle expects to be called with the IRQ line high -- either because of + * an interrupt, or because the line is tri-stated + */ + +static void idle(struct net_device *dev) +{ + unsigned long flags; + int state; + /* FIXME This is initialized to shut the warning up, but I need to + * think this through again. + */ + struct xmitQel *q = NULL; + int oops; + int i; + int base = dev->base_addr; + + spin_lock_irqsave(&txqueue_lock, flags); + if(QInIdle) { + spin_unlock_irqrestore(&txqueue_lock, flags); + return; + } + QInIdle = 1; + spin_unlock_irqrestore(&txqueue_lock, flags); + + /* this tri-states the IRQ line */ + (void) inb_p(base+6); + + oops = 100; + +loop: + if (0>oops--) { + printk("idle: looped too many times\n"); + goto done; + } + + state = inb_p(base+6); + if (state != inb_p(base+6)) goto loop; + + switch(state) { + case 0xfc: + /* incoming command */ + if (debug & DEBUG_LOWER) printk("idle: fc\n"); + handlefc(dev); + break; + case 0xfd: + /* incoming data */ + if(debug & DEBUG_LOWER) printk("idle: fd\n"); + handlefd(dev); + break; + case 0xf9: + /* result ready */ + if (debug & DEBUG_LOWER) printk("idle: f9\n"); + if(!mboxinuse[0]) { + mboxinuse[0] = 1; + qels[0].cbuf = rescbuf; + qels[0].cbuflen = 2; + qels[0].dbuf = resdbuf; + qels[0].dbuflen = 2; + qels[0].QWrite = 0; + qels[0].mailbox = 0; + enQ(&qels[0]); + } + inb_p(dev->base_addr+1); + inb_p(dev->base_addr+0); + if( wait_timeout(dev,0xf9) ) + printk("timed out idle f9\n"); + break; + case 0xf8: + /* ?? */ + if (xmQhd) { + inb_p(dev->base_addr+1); + inb_p(dev->base_addr+0); + if(wait_timeout(dev,0xf8) ) + printk("timed out idle f8\n"); + } else { + goto done; + } + break; + case 0xfa: + /* waiting for command */ + if(debug & DEBUG_LOWER) printk("idle: fa\n"); + if (xmQhd) { + q=deQ(); + memcpy(ltdmacbuf,q->cbuf,q->cbuflen); + ltdmacbuf[1] = q->mailbox; + if (debug>1) { + int n; + printk("ltpc: sent command "); + n = q->cbuflen; + if (n>100) n=100; + for(i=0;iQWrite) { + memcpy(ltdmabuf,q->dbuf,q->dbuflen); + handlewrite(dev); + } else { + handleread(dev); + /* non-zero mailbox numbers are for + commmands, 0 is for GETRESULT + requests */ + if(q->mailbox) { + memcpy(q->dbuf,ltdmabuf,q->dbuflen); + } else { + /* this was a result */ + mailbox[ 0x0f & ltdmabuf[0] ] = ltdmabuf[1]; + mboxinuse[0]=0; + } + } + break; + } + goto loop; + +done: + QInIdle=0; + + /* now set the interrupts back as appropriate */ + /* the first read takes it out of tri-state (but still high) */ + /* the second resets it */ + /* note that after this point, any read of base+6 will + trigger an interrupt */ + + if (dev->irq) { + inb_p(base+7); + inb_p(base+7); + } +} + + +static int do_write(struct net_device *dev, void *cbuf, int cbuflen, + void *dbuf, int dbuflen) +{ + + int i = getmbox(); + int ret; + + if(i) { + qels[i].cbuf = (unsigned char *) cbuf; + qels[i].cbuflen = cbuflen; + qels[i].dbuf = (unsigned char *) dbuf; + qels[i].dbuflen = dbuflen; + qels[i].QWrite = 1; + qels[i].mailbox = i; /* this should be initted rather */ + enQ(&qels[i]); + idle(dev); + ret = mailbox[i]; + mboxinuse[i]=0; + return ret; + } + printk("ltpc: could not allocate mbox\n"); + return -1; +} + +static int do_read(struct net_device *dev, void *cbuf, int cbuflen, + void *dbuf, int dbuflen) +{ + + int i = getmbox(); + int ret; + + if(i) { + qels[i].cbuf = (unsigned char *) cbuf; + qels[i].cbuflen = cbuflen; + qels[i].dbuf = (unsigned char *) dbuf; + qels[i].dbuflen = dbuflen; + qels[i].QWrite = 0; + qels[i].mailbox = i; /* this should be initted rather */ + enQ(&qels[i]); + idle(dev); + ret = mailbox[i]; + mboxinuse[i]=0; + return ret; + } + printk("ltpc: could not allocate mbox\n"); + return -1; +} + +/* end of idle handlers -- what should be seen is do_read, do_write */ + +static struct timer_list ltpc_timer; + +static netdev_tx_t ltpc_xmit(struct sk_buff *skb, struct net_device *dev); + +static int read_30 ( struct net_device *dev) +{ + lt_command c; + c.getflags.command = LT_GETFLAGS; + return do_read(dev, &c, sizeof(c.getflags),&c,0); +} + +static int set_30 (struct net_device *dev,int x) +{ + lt_command c; + c.setflags.command = LT_SETFLAGS; + c.setflags.flags = x; + return do_write(dev, &c, sizeof(c.setflags),&c,0); +} + +/* LLAP to DDP translation */ + +static int sendup_buffer (struct net_device *dev) +{ + /* on entry, command is in ltdmacbuf, data in ltdmabuf */ + /* called from idle, non-reentrant */ + + int dnode, snode, llaptype, len; + int sklen; + struct sk_buff *skb; + struct lt_rcvlap *ltc = (struct lt_rcvlap *) ltdmacbuf; + + if (ltc->command != LT_RCVLAP) { + printk("unknown command 0x%02x from ltpc card\n",ltc->command); + return -1; + } + dnode = ltc->dnode; + snode = ltc->snode; + llaptype = ltc->laptype; + len = ltc->length; + + sklen = len; + if (llaptype == 1) + sklen += 8; /* correct for short ddp */ + if(sklen > 800) { + printk(KERN_INFO "%s: nonsense length in ltpc command 0x14: 0x%08x\n", + dev->name,sklen); + return -1; + } + + if ( (llaptype==0) || (llaptype>2) ) { + printk(KERN_INFO "%s: unknown LLAP type: %d\n",dev->name,llaptype); + return -1; + } + + + skb = dev_alloc_skb(3+sklen); + if (skb == NULL) + { + printk("%s: dropping packet due to memory squeeze.\n", + dev->name); + return -1; + } + skb->dev = dev; + + if (sklen > len) + skb_reserve(skb,8); + skb_put(skb,len+3); + skb->protocol = htons(ETH_P_LOCALTALK); + /* add LLAP header */ + skb->data[0] = dnode; + skb->data[1] = snode; + skb->data[2] = llaptype; + skb_reset_mac_header(skb); /* save pointer to llap header */ + skb_pull(skb,3); + + /* copy ddp(s,e)hdr + contents */ + skb_copy_to_linear_data(skb, ltdmabuf, len); + + skb_reset_transport_header(skb); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + + /* toss it onwards */ + netif_rx(skb); + return 0; +} + +/* the handler for the board interrupt */ + +static irqreturn_t +ltpc_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + + if (dev==NULL) { + printk("ltpc_interrupt: unknown device.\n"); + return IRQ_NONE; + } + + inb_p(dev->base_addr+6); /* disable further interrupts from board */ + + idle(dev); /* handle whatever is coming in */ + + /* idle re-enables interrupts from board */ + + return IRQ_HANDLED; +} + +/*** + * + * The ioctls that the driver responds to are: + * + * SIOCSIFADDR -- do probe using the passed node hint. + * SIOCGIFADDR -- return net, node. + * + * some of this stuff should be done elsewhere. + * + ***/ + +static int ltpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct sockaddr_at *sa = (struct sockaddr_at *) &ifr->ifr_addr; + /* we'll keep the localtalk node address in dev->pa_addr */ + struct ltpc_private *ltpc_priv = netdev_priv(dev); + struct atalk_addr *aa = <pc_priv->my_addr; + struct lt_init c; + int ltflags; + + if(debug & DEBUG_VERBOSE) printk("ltpc_ioctl called\n"); + + switch(cmd) { + case SIOCSIFADDR: + + aa->s_net = sa->sat_addr.s_net; + + /* this does the probe and returns the node addr */ + c.command = LT_INIT; + c.hint = sa->sat_addr.s_node; + + aa->s_node = do_read(dev,&c,sizeof(c),&c,0); + + /* get all llap frames raw */ + ltflags = read_30(dev); + ltflags |= LT_FLAG_ALLLAP; + set_30 (dev,ltflags); + + dev->broadcast[0] = 0xFF; + dev->dev_addr[0] = aa->s_node; + + dev->addr_len=1; + + return 0; + + case SIOCGIFADDR: + + sa->sat_addr.s_net = aa->s_net; + sa->sat_addr.s_node = aa->s_node; + + return 0; + + default: + return -EINVAL; + } +} + +static void set_multicast_list(struct net_device *dev) +{ + /* This needs to be present to keep netatalk happy. */ + /* Actually netatalk needs fixing! */ +} + +static int ltpc_poll_counter; + +static void ltpc_poll(unsigned long l) +{ + struct net_device *dev = (struct net_device *) l; + + del_timer(<pc_timer); + + if(debug & DEBUG_VERBOSE) { + if (!ltpc_poll_counter) { + ltpc_poll_counter = 50; + printk("ltpc poll is alive\n"); + } + ltpc_poll_counter--; + } + + if (!dev) + return; /* we've been downed */ + + /* poll 20 times per second */ + idle(dev); + ltpc_timer.expires = jiffies + HZ/20; + + add_timer(<pc_timer); +} + +/* DDP to LLAP translation */ + +static netdev_tx_t ltpc_xmit(struct sk_buff *skb, struct net_device *dev) +{ + /* in kernel 1.3.xx, on entry skb->data points to ddp header, + * and skb->len is the length of the ddp data + ddp header + */ + int i; + struct lt_sendlap cbuf; + unsigned char *hdr; + + cbuf.command = LT_SENDLAP; + cbuf.dnode = skb->data[0]; + cbuf.laptype = skb->data[2]; + skb_pull(skb,3); /* skip past LLAP header */ + cbuf.length = skb->len; /* this is host order */ + skb_reset_transport_header(skb); + + if(debug & DEBUG_UPPER) { + printk("command "); + for(i=0;i<6;i++) + printk("%02x ",((unsigned char *)&cbuf)[i]); + printk("\n"); + } + + hdr = skb_transport_header(skb); + do_write(dev, &cbuf, sizeof(cbuf), hdr, skb->len); + + if(debug & DEBUG_UPPER) { + printk("sent %d ddp bytes\n",skb->len); + for (i = 0; i < skb->len; i++) + printk("%02x ", hdr[i]); + printk("\n"); + } + + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +/* initialization stuff */ + +static int __init ltpc_probe_dma(int base, int dma) +{ + int want = (dma == 3) ? 2 : (dma == 1) ? 1 : 3; + unsigned long timeout; + unsigned long f; + + if (want & 1) { + if (request_dma(1,"ltpc")) { + want &= ~1; + } else { + f=claim_dma_lock(); + disable_dma(1); + clear_dma_ff(1); + set_dma_mode(1,DMA_MODE_WRITE); + set_dma_addr(1,virt_to_bus(ltdmabuf)); + set_dma_count(1,sizeof(struct lt_mem)); + enable_dma(1); + release_dma_lock(f); + } + } + if (want & 2) { + if (request_dma(3,"ltpc")) { + want &= ~2; + } else { + f=claim_dma_lock(); + disable_dma(3); + clear_dma_ff(3); + set_dma_mode(3,DMA_MODE_WRITE); + set_dma_addr(3,virt_to_bus(ltdmabuf)); + set_dma_count(3,sizeof(struct lt_mem)); + enable_dma(3); + release_dma_lock(f); + } + } + /* set up request */ + + /* FIXME -- do timings better! */ + + ltdmabuf[0] = LT_READMEM; + ltdmabuf[1] = 1; /* mailbox */ + ltdmabuf[2] = 0; ltdmabuf[3] = 0; /* address */ + ltdmabuf[4] = 0; ltdmabuf[5] = 1; /* read 0x0100 bytes */ + ltdmabuf[6] = 0; /* dunno if this is necessary */ + + inb_p(io+1); + inb_p(io+0); + timeout = jiffies+100*HZ/100; + while(time_before(jiffies, timeout)) { + if ( 0xfa == inb_p(io+6) ) break; + } + + inb_p(io+3); + inb_p(io+2); + while(time_before(jiffies, timeout)) { + if ( 0xfb == inb_p(io+6) ) break; + } + + /* release the other dma channel (if we opened both of them) */ + + if ((want & 2) && (get_dma_residue(3)==sizeof(struct lt_mem))) { + want &= ~2; + free_dma(3); + } + + if ((want & 1) && (get_dma_residue(1)==sizeof(struct lt_mem))) { + want &= ~1; + free_dma(1); + } + + if (!want) + return 0; + + return (want & 2) ? 3 : 1; +} + +static const struct net_device_ops ltpc_netdev = { + .ndo_start_xmit = ltpc_xmit, + .ndo_do_ioctl = ltpc_ioctl, + .ndo_set_multicast_list = set_multicast_list, +}; + +struct net_device * __init ltpc_probe(void) +{ + struct net_device *dev; + int err = -ENOMEM; + int x=0,y=0; + int autoirq; + unsigned long f; + unsigned long timeout; + + dev = alloc_ltalkdev(sizeof(struct ltpc_private)); + if (!dev) + goto out; + + /* probe for the I/O port address */ + + if (io != 0x240 && request_region(0x220,8,"ltpc")) { + x = inb_p(0x220+6); + if ( (x!=0xff) && (x>=0xf0) ) { + io = 0x220; + goto got_port; + } + release_region(0x220,8); + } + if (io != 0x220 && request_region(0x240,8,"ltpc")) { + y = inb_p(0x240+6); + if ( (y!=0xff) && (y>=0xf0) ){ + io = 0x240; + goto got_port; + } + release_region(0x240,8); + } + + /* give up in despair */ + printk(KERN_ERR "LocalTalk card not found; 220 = %02x, 240 = %02x.\n", x,y); + err = -ENODEV; + goto out1; + + got_port: + /* probe for the IRQ line */ + if (irq < 2) { + unsigned long irq_mask; + + irq_mask = probe_irq_on(); + /* reset the interrupt line */ + inb_p(io+7); + inb_p(io+7); + /* trigger an interrupt (I hope) */ + inb_p(io+6); + mdelay(2); + autoirq = probe_irq_off(irq_mask); + + if (autoirq == 0) { + printk(KERN_ERR "ltpc: probe at %#x failed to detect IRQ line.\n", io); + } else { + irq = autoirq; + } + } + + /* allocate a DMA buffer */ + ltdmabuf = (unsigned char *) dma_mem_alloc(1000); + if (!ltdmabuf) { + printk(KERN_ERR "ltpc: mem alloc failed\n"); + err = -ENOMEM; + goto out2; + } + + ltdmacbuf = <dmabuf[800]; + + if(debug & DEBUG_VERBOSE) { + printk("ltdmabuf pointer %08lx\n",(unsigned long) ltdmabuf); + } + + /* reset the card */ + + inb_p(io+1); + inb_p(io+3); + + msleep(20); + + inb_p(io+0); + inb_p(io+2); + inb_p(io+7); /* clear reset */ + inb_p(io+4); + inb_p(io+5); + inb_p(io+5); /* enable dma */ + inb_p(io+6); /* tri-state interrupt line */ + + ssleep(1); + + /* now, figure out which dma channel we're using, unless it's + already been specified */ + /* well, 0 is a legal DMA channel, but the LTPC card doesn't + use it... */ + dma = ltpc_probe_dma(io, dma); + if (!dma) { /* no dma channel */ + printk(KERN_ERR "No DMA channel found on ltpc card.\n"); + err = -ENODEV; + goto out3; + } + + /* print out friendly message */ + if(irq) + printk(KERN_INFO "Apple/Farallon LocalTalk-PC card at %03x, IR%d, DMA%d.\n",io,irq,dma); + else + printk(KERN_INFO "Apple/Farallon LocalTalk-PC card at %03x, DMA%d. Using polled mode.\n",io,dma); + + dev->netdev_ops = <pc_netdev; + dev->base_addr = io; + dev->irq = irq; + dev->dma = dma; + + /* the card will want to send a result at this point */ + /* (I think... leaving out this part makes the kernel crash, + so I put it back in...) */ + + f=claim_dma_lock(); + disable_dma(dma); + clear_dma_ff(dma); + set_dma_mode(dma,DMA_MODE_READ); + set_dma_addr(dma,virt_to_bus(ltdmabuf)); + set_dma_count(dma,0x100); + enable_dma(dma); + release_dma_lock(f); + + (void) inb_p(io+3); + (void) inb_p(io+2); + timeout = jiffies+100*HZ/100; + + while(time_before(jiffies, timeout)) { + if( 0xf9 == inb_p(io+6)) + break; + schedule(); + } + + if(debug & DEBUG_VERBOSE) { + printk("setting up timer and irq\n"); + } + + /* grab it and don't let go :-) */ + if (irq && request_irq( irq, ltpc_interrupt, 0, "ltpc", dev) >= 0) + { + (void) inb_p(io+7); /* enable interrupts from board */ + (void) inb_p(io+7); /* and reset irq line */ + } else { + if( irq ) + printk(KERN_ERR "ltpc: IRQ already in use, using polled mode.\n"); + dev->irq = 0; + /* polled mode -- 20 times per second */ + /* this is really, really slow... should it poll more often? */ + init_timer(<pc_timer); + ltpc_timer.function=ltpc_poll; + ltpc_timer.data = (unsigned long) dev; + + ltpc_timer.expires = jiffies + HZ/20; + add_timer(<pc_timer); + } + err = register_netdev(dev); + if (err) + goto out4; + + return NULL; +out4: + del_timer_sync(<pc_timer); + if (dev->irq) + free_irq(dev->irq, dev); +out3: + free_pages((unsigned long)ltdmabuf, get_order(1000)); +out2: + release_region(io, 8); +out1: + free_netdev(dev); +out: + return ERR_PTR(err); +} + +#ifndef MODULE +/* handles "ltpc=io,irq,dma" kernel command lines */ +static int __init ltpc_setup(char *str) +{ + int ints[5]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + if (ints[0] == 0) { + if (str && !strncmp(str, "auto", 4)) { + /* do nothing :-) */ + } + else { + /* usage message */ + printk (KERN_ERR + "ltpc: usage: ltpc=auto|iobase[,irq[,dma]]\n"); + return 0; + } + } else { + io = ints[1]; + if (ints[0] > 1) { + irq = ints[2]; + } + if (ints[0] > 2) { + dma = ints[3]; + } + /* ignore any other parameters */ + } + return 1; +} + +__setup("ltpc=", ltpc_setup); +#endif /* MODULE */ + +static struct net_device *dev_ltpc; + +#ifdef MODULE + +MODULE_LICENSE("GPL"); +module_param(debug, int, 0); +module_param(io, int, 0); +module_param(irq, int, 0); +module_param(dma, int, 0); + + +static int __init ltpc_module_init(void) +{ + if(io == 0) + printk(KERN_NOTICE + "ltpc: Autoprobing is not recommended for modules\n"); + + dev_ltpc = ltpc_probe(); + if (IS_ERR(dev_ltpc)) + return PTR_ERR(dev_ltpc); + return 0; +} +module_init(ltpc_module_init); +#endif + +static void __exit ltpc_cleanup(void) +{ + + if(debug & DEBUG_VERBOSE) printk("unregister_netdev\n"); + unregister_netdev(dev_ltpc); + + ltpc_timer.data = 0; /* signal the poll routine that we're done */ + + del_timer_sync(<pc_timer); + + if(debug & DEBUG_VERBOSE) printk("freeing irq\n"); + + if (dev_ltpc->irq) + free_irq(dev_ltpc->irq, dev_ltpc); + + if(debug & DEBUG_VERBOSE) printk("freeing dma\n"); + + if (dev_ltpc->dma) + free_dma(dev_ltpc->dma); + + if(debug & DEBUG_VERBOSE) printk("freeing ioaddr\n"); + + if (dev_ltpc->base_addr) + release_region(dev_ltpc->base_addr,8); + + free_netdev(dev_ltpc); + + if(debug & DEBUG_VERBOSE) printk("free_pages\n"); + + free_pages( (unsigned long) ltdmabuf, get_order(1000)); + + if(debug & DEBUG_VERBOSE) printk("returning from cleanup_module\n"); +} + +module_exit(ltpc_cleanup); diff --git a/drivers/staging/appletalk/ltpc.h b/drivers/staging/appletalk/ltpc.h new file mode 100644 index 000000000000..cd30544a3729 --- /dev/null +++ b/drivers/staging/appletalk/ltpc.h @@ -0,0 +1,73 @@ +/*** ltpc.h + * + * + ***/ + +#define LT_GETRESULT 0x00 +#define LT_WRITEMEM 0x01 +#define LT_READMEM 0x02 +#define LT_GETFLAGS 0x04 +#define LT_SETFLAGS 0x05 +#define LT_INIT 0x10 +#define LT_SENDLAP 0x13 +#define LT_RCVLAP 0x14 + +/* the flag that we care about */ +#define LT_FLAG_ALLLAP 0x04 + +struct lt_getresult { + unsigned char command; + unsigned char mailbox; +}; + +struct lt_mem { + unsigned char command; + unsigned char mailbox; + unsigned short addr; /* host order */ + unsigned short length; /* host order */ +}; + +struct lt_setflags { + unsigned char command; + unsigned char mailbox; + unsigned char flags; +}; + +struct lt_getflags { + unsigned char command; + unsigned char mailbox; +}; + +struct lt_init { + unsigned char command; + unsigned char mailbox; + unsigned char hint; +}; + +struct lt_sendlap { + unsigned char command; + unsigned char mailbox; + unsigned char dnode; + unsigned char laptype; + unsigned short length; /* host order */ +}; + +struct lt_rcvlap { + unsigned char command; + unsigned char dnode; + unsigned char snode; + unsigned char laptype; + unsigned short length; /* host order */ +}; + +union lt_command { + struct lt_getresult getresult; + struct lt_mem mem; + struct lt_setflags setflags; + struct lt_getflags getflags; + struct lt_init init; + struct lt_sendlap sendlap; + struct lt_rcvlap rcvlap; +}; +typedef union lt_command lt_command; + diff --git a/drivers/staging/appletalk/sysctl_net_atalk.c b/drivers/staging/appletalk/sysctl_net_atalk.c new file mode 100644 index 000000000000..4c896b625b2b --- /dev/null +++ b/drivers/staging/appletalk/sysctl_net_atalk.c @@ -0,0 +1,61 @@ +/* + * sysctl_net_atalk.c: sysctl interface to net AppleTalk subsystem. + * + * Begun April 1, 1996, Mike Shaver. + * Added /proc/sys/net/atalk directory entry (empty =) ). [MS] + * Dynamic registration, added aarp entries. (5/30/97 Chris Horn) + */ + +#include +#include +#include "atalk.h" + +static struct ctl_table atalk_table[] = { + { + .procname = "aarp-expiry-time", + .data = &sysctl_aarp_expiry_time, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_jiffies, + }, + { + .procname = "aarp-tick-time", + .data = &sysctl_aarp_tick_time, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_jiffies, + }, + { + .procname = "aarp-retransmit-limit", + .data = &sysctl_aarp_retransmit_limit, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { + .procname = "aarp-resolve-time", + .data = &sysctl_aarp_resolve_time, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_jiffies, + }, + { }, +}; + +static struct ctl_path atalk_path[] = { + { .procname = "net", }, + { .procname = "appletalk", }, + { } +}; + +static struct ctl_table_header *atalk_table_header; + +void atalk_register_sysctl(void) +{ + atalk_table_header = register_sysctl_paths(atalk_path, atalk_table); +} + +void atalk_unregister_sysctl(void) +{ + unregister_sysctl_table(atalk_table_header); +} diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 61abb638b4bf..86a2d7d905c0 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -56,7 +56,6 @@ #include #include #include -#include #include #include diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 2296d8b1931f..362041b73a2f 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -43,7 +43,6 @@ header-y += agpgart.h header-y += aio_abi.h header-y += apm_bios.h header-y += arcfb.h -header-y += atalk.h header-y += atm.h header-y += atm_eni.h header-y += atm_he.h diff --git a/include/linux/atalk.h b/include/linux/atalk.h deleted file mode 100644 index d34c187432ed..000000000000 --- a/include/linux/atalk.h +++ /dev/null @@ -1,208 +0,0 @@ -#ifndef __LINUX_ATALK_H__ -#define __LINUX_ATALK_H__ - -#include -#include - -/* - * AppleTalk networking structures - * - * The following are directly referenced from the University Of Michigan - * netatalk for compatibility reasons. - */ -#define ATPORT_FIRST 1 -#define ATPORT_RESERVED 128 -#define ATPORT_LAST 254 /* 254 is only legal on localtalk */ -#define ATADDR_ANYNET (__u16)0 -#define ATADDR_ANYNODE (__u8)0 -#define ATADDR_ANYPORT (__u8)0 -#define ATADDR_BCAST (__u8)255 -#define DDP_MAXSZ 587 -#define DDP_MAXHOPS 15 /* 4 bits of hop counter */ - -#define SIOCATALKDIFADDR (SIOCPROTOPRIVATE + 0) - -struct atalk_addr { - __be16 s_net; - __u8 s_node; -}; - -struct sockaddr_at { - sa_family_t sat_family; - __u8 sat_port; - struct atalk_addr sat_addr; - char sat_zero[8]; -}; - -struct atalk_netrange { - __u8 nr_phase; - __be16 nr_firstnet; - __be16 nr_lastnet; -}; - -#ifdef __KERNEL__ - -#include - -struct atalk_route { - struct net_device *dev; - struct atalk_addr target; - struct atalk_addr gateway; - int flags; - struct atalk_route *next; -}; - -/** - * struct atalk_iface - AppleTalk Interface - * @dev - Network device associated with this interface - * @address - Our address - * @status - What are we doing? - * @nets - Associated direct netrange - * @next - next element in the list of interfaces - */ -struct atalk_iface { - struct net_device *dev; - struct atalk_addr address; - int status; -#define ATIF_PROBE 1 /* Probing for an address */ -#define ATIF_PROBE_FAIL 2 /* Probe collided */ - struct atalk_netrange nets; - struct atalk_iface *next; -}; - -struct atalk_sock { - /* struct sock has to be the first member of atalk_sock */ - struct sock sk; - __be16 dest_net; - __be16 src_net; - unsigned char dest_node; - unsigned char src_node; - unsigned char dest_port; - unsigned char src_port; -}; - -static inline struct atalk_sock *at_sk(struct sock *sk) -{ - return (struct atalk_sock *)sk; -} - -struct ddpehdr { - __be16 deh_len_hops; /* lower 10 bits are length, next 4 - hops */ - __be16 deh_sum; - __be16 deh_dnet; - __be16 deh_snet; - __u8 deh_dnode; - __u8 deh_snode; - __u8 deh_dport; - __u8 deh_sport; - /* And netatalk apps expect to stick the type in themselves */ -}; - -static __inline__ struct ddpehdr *ddp_hdr(struct sk_buff *skb) -{ - return (struct ddpehdr *)skb_transport_header(skb); -} - -/* AppleTalk AARP headers */ -struct elapaarp { - __be16 hw_type; -#define AARP_HW_TYPE_ETHERNET 1 -#define AARP_HW_TYPE_TOKENRING 2 - __be16 pa_type; - __u8 hw_len; - __u8 pa_len; -#define AARP_PA_ALEN 4 - __be16 function; -#define AARP_REQUEST 1 -#define AARP_REPLY 2 -#define AARP_PROBE 3 - __u8 hw_src[ETH_ALEN]; - __u8 pa_src_zero; - __be16 pa_src_net; - __u8 pa_src_node; - __u8 hw_dst[ETH_ALEN]; - __u8 pa_dst_zero; - __be16 pa_dst_net; - __u8 pa_dst_node; -} __attribute__ ((packed)); - -static __inline__ struct elapaarp *aarp_hdr(struct sk_buff *skb) -{ - return (struct elapaarp *)skb_transport_header(skb); -} - -/* Not specified - how long till we drop a resolved entry */ -#define AARP_EXPIRY_TIME (5 * 60 * HZ) -/* Size of hash table */ -#define AARP_HASH_SIZE 16 -/* Fast retransmission timer when resolving */ -#define AARP_TICK_TIME (HZ / 5) -/* Send 10 requests then give up (2 seconds) */ -#define AARP_RETRANSMIT_LIMIT 10 -/* - * Some value bigger than total retransmit time + a bit for last reply to - * appear and to stop continual requests - */ -#define AARP_RESOLVE_TIME (10 * HZ) - -extern struct datalink_proto *ddp_dl, *aarp_dl; -extern void aarp_proto_init(void); - -/* Inter module exports */ - -/* Give a device find its atif control structure */ -static inline struct atalk_iface *atalk_find_dev(struct net_device *dev) -{ - return dev->atalk_ptr; -} - -extern struct atalk_addr *atalk_find_dev_addr(struct net_device *dev); -extern struct net_device *atrtr_get_dev(struct atalk_addr *sa); -extern int aarp_send_ddp(struct net_device *dev, - struct sk_buff *skb, - struct atalk_addr *sa, void *hwaddr); -extern void aarp_device_down(struct net_device *dev); -extern void aarp_probe_network(struct atalk_iface *atif); -extern int aarp_proxy_probe_network(struct atalk_iface *atif, - struct atalk_addr *sa); -extern void aarp_proxy_remove(struct net_device *dev, - struct atalk_addr *sa); - -extern void aarp_cleanup_module(void); - -extern struct hlist_head atalk_sockets; -extern rwlock_t atalk_sockets_lock; - -extern struct atalk_route *atalk_routes; -extern rwlock_t atalk_routes_lock; - -extern struct atalk_iface *atalk_interfaces; -extern rwlock_t atalk_interfaces_lock; - -extern struct atalk_route atrtr_default; - -extern const struct file_operations atalk_seq_arp_fops; - -extern int sysctl_aarp_expiry_time; -extern int sysctl_aarp_tick_time; -extern int sysctl_aarp_retransmit_limit; -extern int sysctl_aarp_resolve_time; - -#ifdef CONFIG_SYSCTL -extern void atalk_register_sysctl(void); -extern void atalk_unregister_sysctl(void); -#else -#define atalk_register_sysctl() do { } while(0) -#define atalk_unregister_sysctl() do { } while(0) -#endif - -#ifdef CONFIG_PROC_FS -extern int atalk_proc_init(void); -extern void atalk_proc_exit(void); -#else -#define atalk_proc_init() ({ 0; }) -#define atalk_proc_exit() do { } while(0) -#endif /* CONFIG_PROC_FS */ - -#endif /* __KERNEL__ */ -#endif /* __LINUX_ATALK_H__ */ diff --git a/net/Kconfig b/net/Kconfig index 72840626284b..082c8bc977e1 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -204,7 +204,6 @@ source "net/8021q/Kconfig" source "net/decnet/Kconfig" source "net/llc/Kconfig" source "net/ipx/Kconfig" -source "drivers/net/appletalk/Kconfig" source "net/x25/Kconfig" source "net/lapb/Kconfig" source "net/econet/Kconfig" diff --git a/net/Makefile b/net/Makefile index a3330ebe2c53..16d9947b4b95 100644 --- a/net/Makefile +++ b/net/Makefile @@ -27,7 +27,6 @@ obj-$(CONFIG_NET_KEY) += key/ obj-$(CONFIG_BRIDGE) += bridge/ obj-$(CONFIG_NET_DSA) += dsa/ obj-$(CONFIG_IPX) += ipx/ -obj-$(CONFIG_ATALK) += appletalk/ obj-$(CONFIG_WAN_ROUTER) += wanrouter/ obj-$(CONFIG_X25) += x25/ obj-$(CONFIG_LAPB) += lapb/ diff --git a/net/appletalk/Makefile b/net/appletalk/Makefile deleted file mode 100644 index 5cda56edef57..000000000000 --- a/net/appletalk/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# Makefile for the Linux AppleTalk layer. -# - -obj-$(CONFIG_ATALK) += appletalk.o - -appletalk-y := aarp.o ddp.o dev.o -appletalk-$(CONFIG_PROC_FS) += atalk_proc.o -appletalk-$(CONFIG_SYSCTL) += sysctl_net_atalk.o diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c deleted file mode 100644 index 50dce7981321..000000000000 --- a/net/appletalk/aarp.c +++ /dev/null @@ -1,1063 +0,0 @@ -/* - * AARP: An implementation of the AppleTalk AARP protocol for - * Ethernet 'ELAP'. - * - * Alan Cox - * - * This doesn't fit cleanly with the IP arp. Potentially we can use - * the generic neighbour discovery code to clean this up. - * - * FIXME: - * We ought to handle the retransmits with a single list and a - * separate fast timer for when it is needed. - * Use neighbour discovery code. - * Token Ring Support. - * - * 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. - * - * - * References: - * Inside AppleTalk (2nd Ed). - * Fixes: - * Jaume Grau - flush caches on AARP_PROBE - * Rob Newberry - Added proxy AARP and AARP proc fs, - * moved probing from DDP module. - * Arnaldo C. Melo - don't mangle rx packets - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int sysctl_aarp_expiry_time = AARP_EXPIRY_TIME; -int sysctl_aarp_tick_time = AARP_TICK_TIME; -int sysctl_aarp_retransmit_limit = AARP_RETRANSMIT_LIMIT; -int sysctl_aarp_resolve_time = AARP_RESOLVE_TIME; - -/* Lists of aarp entries */ -/** - * struct aarp_entry - AARP entry - * @last_sent - Last time we xmitted the aarp request - * @packet_queue - Queue of frames wait for resolution - * @status - Used for proxy AARP - * expires_at - Entry expiry time - * target_addr - DDP Address - * dev - Device to use - * hwaddr - Physical i/f address of target/router - * xmit_count - When this hits 10 we give up - * next - Next entry in chain - */ -struct aarp_entry { - /* These first two are only used for unresolved entries */ - unsigned long last_sent; - struct sk_buff_head packet_queue; - int status; - unsigned long expires_at; - struct atalk_addr target_addr; - struct net_device *dev; - char hwaddr[6]; - unsigned short xmit_count; - struct aarp_entry *next; -}; - -/* Hashed list of resolved, unresolved and proxy entries */ -static struct aarp_entry *resolved[AARP_HASH_SIZE]; -static struct aarp_entry *unresolved[AARP_HASH_SIZE]; -static struct aarp_entry *proxies[AARP_HASH_SIZE]; -static int unresolved_count; - -/* One lock protects it all. */ -static DEFINE_RWLOCK(aarp_lock); - -/* Used to walk the list and purge/kick entries. */ -static struct timer_list aarp_timer; - -/* - * Delete an aarp queue - * - * Must run under aarp_lock. - */ -static void __aarp_expire(struct aarp_entry *a) -{ - skb_queue_purge(&a->packet_queue); - kfree(a); -} - -/* - * Send an aarp queue entry request - * - * Must run under aarp_lock. - */ -static void __aarp_send_query(struct aarp_entry *a) -{ - static unsigned char aarp_eth_multicast[ETH_ALEN] = - { 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; - struct net_device *dev = a->dev; - struct elapaarp *eah; - int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length; - struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); - struct atalk_addr *sat = atalk_find_dev_addr(dev); - - if (!skb) - return; - - if (!sat) { - kfree_skb(skb); - return; - } - - /* Set up the buffer */ - skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - skb_put(skb, sizeof(*eah)); - skb->protocol = htons(ETH_P_ATALK); - skb->dev = dev; - eah = aarp_hdr(skb); - - /* Set up the ARP */ - eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); - eah->pa_type = htons(ETH_P_ATALK); - eah->hw_len = ETH_ALEN; - eah->pa_len = AARP_PA_ALEN; - eah->function = htons(AARP_REQUEST); - - memcpy(eah->hw_src, dev->dev_addr, ETH_ALEN); - - eah->pa_src_zero = 0; - eah->pa_src_net = sat->s_net; - eah->pa_src_node = sat->s_node; - - memset(eah->hw_dst, '\0', ETH_ALEN); - - eah->pa_dst_zero = 0; - eah->pa_dst_net = a->target_addr.s_net; - eah->pa_dst_node = a->target_addr.s_node; - - /* Send it */ - aarp_dl->request(aarp_dl, skb, aarp_eth_multicast); - /* Update the sending count */ - a->xmit_count++; - a->last_sent = jiffies; -} - -/* This runs under aarp_lock and in softint context, so only atomic memory - * allocations can be used. */ -static void aarp_send_reply(struct net_device *dev, struct atalk_addr *us, - struct atalk_addr *them, unsigned char *sha) -{ - struct elapaarp *eah; - int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length; - struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); - - if (!skb) - return; - - /* Set up the buffer */ - skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - skb_put(skb, sizeof(*eah)); - skb->protocol = htons(ETH_P_ATALK); - skb->dev = dev; - eah = aarp_hdr(skb); - - /* Set up the ARP */ - eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); - eah->pa_type = htons(ETH_P_ATALK); - eah->hw_len = ETH_ALEN; - eah->pa_len = AARP_PA_ALEN; - eah->function = htons(AARP_REPLY); - - memcpy(eah->hw_src, dev->dev_addr, ETH_ALEN); - - eah->pa_src_zero = 0; - eah->pa_src_net = us->s_net; - eah->pa_src_node = us->s_node; - - if (!sha) - memset(eah->hw_dst, '\0', ETH_ALEN); - else - memcpy(eah->hw_dst, sha, ETH_ALEN); - - eah->pa_dst_zero = 0; - eah->pa_dst_net = them->s_net; - eah->pa_dst_node = them->s_node; - - /* Send it */ - aarp_dl->request(aarp_dl, skb, sha); -} - -/* - * Send probe frames. Called from aarp_probe_network and - * aarp_proxy_probe_network. - */ - -static void aarp_send_probe(struct net_device *dev, struct atalk_addr *us) -{ - struct elapaarp *eah; - int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length; - struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); - static unsigned char aarp_eth_multicast[ETH_ALEN] = - { 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; - - if (!skb) - return; - - /* Set up the buffer */ - skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - skb_put(skb, sizeof(*eah)); - skb->protocol = htons(ETH_P_ATALK); - skb->dev = dev; - eah = aarp_hdr(skb); - - /* Set up the ARP */ - eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); - eah->pa_type = htons(ETH_P_ATALK); - eah->hw_len = ETH_ALEN; - eah->pa_len = AARP_PA_ALEN; - eah->function = htons(AARP_PROBE); - - memcpy(eah->hw_src, dev->dev_addr, ETH_ALEN); - - eah->pa_src_zero = 0; - eah->pa_src_net = us->s_net; - eah->pa_src_node = us->s_node; - - memset(eah->hw_dst, '\0', ETH_ALEN); - - eah->pa_dst_zero = 0; - eah->pa_dst_net = us->s_net; - eah->pa_dst_node = us->s_node; - - /* Send it */ - aarp_dl->request(aarp_dl, skb, aarp_eth_multicast); -} - -/* - * Handle an aarp timer expire - * - * Must run under the aarp_lock. - */ - -static void __aarp_expire_timer(struct aarp_entry **n) -{ - struct aarp_entry *t; - - while (*n) - /* Expired ? */ - if (time_after(jiffies, (*n)->expires_at)) { - t = *n; - *n = (*n)->next; - __aarp_expire(t); - } else - n = &((*n)->next); -} - -/* - * Kick all pending requests 5 times a second. - * - * Must run under the aarp_lock. - */ -static void __aarp_kick(struct aarp_entry **n) -{ - struct aarp_entry *t; - - while (*n) - /* Expired: if this will be the 11th tx, we delete instead. */ - if ((*n)->xmit_count >= sysctl_aarp_retransmit_limit) { - t = *n; - *n = (*n)->next; - __aarp_expire(t); - } else { - __aarp_send_query(*n); - n = &((*n)->next); - } -} - -/* - * A device has gone down. Take all entries referring to the device - * and remove them. - * - * Must run under the aarp_lock. - */ -static void __aarp_expire_device(struct aarp_entry **n, struct net_device *dev) -{ - struct aarp_entry *t; - - while (*n) - if ((*n)->dev == dev) { - t = *n; - *n = (*n)->next; - __aarp_expire(t); - } else - n = &((*n)->next); -} - -/* Handle the timer event */ -static void aarp_expire_timeout(unsigned long unused) -{ - int ct; - - write_lock_bh(&aarp_lock); - - for (ct = 0; ct < AARP_HASH_SIZE; ct++) { - __aarp_expire_timer(&resolved[ct]); - __aarp_kick(&unresolved[ct]); - __aarp_expire_timer(&unresolved[ct]); - __aarp_expire_timer(&proxies[ct]); - } - - write_unlock_bh(&aarp_lock); - mod_timer(&aarp_timer, jiffies + - (unresolved_count ? sysctl_aarp_tick_time : - sysctl_aarp_expiry_time)); -} - -/* Network device notifier chain handler. */ -static int aarp_device_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - struct net_device *dev = ptr; - int ct; - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (event == NETDEV_DOWN) { - write_lock_bh(&aarp_lock); - - for (ct = 0; ct < AARP_HASH_SIZE; ct++) { - __aarp_expire_device(&resolved[ct], dev); - __aarp_expire_device(&unresolved[ct], dev); - __aarp_expire_device(&proxies[ct], dev); - } - - write_unlock_bh(&aarp_lock); - } - return NOTIFY_DONE; -} - -/* Expire all entries in a hash chain */ -static void __aarp_expire_all(struct aarp_entry **n) -{ - struct aarp_entry *t; - - while (*n) { - t = *n; - *n = (*n)->next; - __aarp_expire(t); - } -} - -/* Cleanup all hash chains -- module unloading */ -static void aarp_purge(void) -{ - int ct; - - write_lock_bh(&aarp_lock); - for (ct = 0; ct < AARP_HASH_SIZE; ct++) { - __aarp_expire_all(&resolved[ct]); - __aarp_expire_all(&unresolved[ct]); - __aarp_expire_all(&proxies[ct]); - } - write_unlock_bh(&aarp_lock); -} - -/* - * Create a new aarp entry. This must use GFP_ATOMIC because it - * runs while holding spinlocks. - */ -static struct aarp_entry *aarp_alloc(void) -{ - struct aarp_entry *a = kmalloc(sizeof(*a), GFP_ATOMIC); - - if (a) - skb_queue_head_init(&a->packet_queue); - return a; -} - -/* - * Find an entry. We might return an expired but not yet purged entry. We - * don't care as it will do no harm. - * - * This must run under the aarp_lock. - */ -static struct aarp_entry *__aarp_find_entry(struct aarp_entry *list, - struct net_device *dev, - struct atalk_addr *sat) -{ - while (list) { - if (list->target_addr.s_net == sat->s_net && - list->target_addr.s_node == sat->s_node && - list->dev == dev) - break; - list = list->next; - } - - return list; -} - -/* Called from the DDP code, and thus must be exported. */ -void aarp_proxy_remove(struct net_device *dev, struct atalk_addr *sa) -{ - int hash = sa->s_node % (AARP_HASH_SIZE - 1); - struct aarp_entry *a; - - write_lock_bh(&aarp_lock); - - a = __aarp_find_entry(proxies[hash], dev, sa); - if (a) - a->expires_at = jiffies - 1; - - write_unlock_bh(&aarp_lock); -} - -/* This must run under aarp_lock. */ -static struct atalk_addr *__aarp_proxy_find(struct net_device *dev, - struct atalk_addr *sa) -{ - int hash = sa->s_node % (AARP_HASH_SIZE - 1); - struct aarp_entry *a = __aarp_find_entry(proxies[hash], dev, sa); - - return a ? sa : NULL; -} - -/* - * Probe a Phase 1 device or a device that requires its Net:Node to - * be set via an ioctl. - */ -static void aarp_send_probe_phase1(struct atalk_iface *iface) -{ - struct ifreq atreq; - struct sockaddr_at *sa = (struct sockaddr_at *)&atreq.ifr_addr; - const struct net_device_ops *ops = iface->dev->netdev_ops; - - sa->sat_addr.s_node = iface->address.s_node; - sa->sat_addr.s_net = ntohs(iface->address.s_net); - - /* We pass the Net:Node to the drivers/cards by a Device ioctl. */ - if (!(ops->ndo_do_ioctl(iface->dev, &atreq, SIOCSIFADDR))) { - ops->ndo_do_ioctl(iface->dev, &atreq, SIOCGIFADDR); - if (iface->address.s_net != htons(sa->sat_addr.s_net) || - iface->address.s_node != sa->sat_addr.s_node) - iface->status |= ATIF_PROBE_FAIL; - - iface->address.s_net = htons(sa->sat_addr.s_net); - iface->address.s_node = sa->sat_addr.s_node; - } -} - - -void aarp_probe_network(struct atalk_iface *atif) -{ - if (atif->dev->type == ARPHRD_LOCALTLK || - atif->dev->type == ARPHRD_PPP) - aarp_send_probe_phase1(atif); - else { - unsigned int count; - - for (count = 0; count < AARP_RETRANSMIT_LIMIT; count++) { - aarp_send_probe(atif->dev, &atif->address); - - /* Defer 1/10th */ - msleep(100); - - if (atif->status & ATIF_PROBE_FAIL) - break; - } - } -} - -int aarp_proxy_probe_network(struct atalk_iface *atif, struct atalk_addr *sa) -{ - int hash, retval = -EPROTONOSUPPORT; - struct aarp_entry *entry; - unsigned int count; - - /* - * we don't currently support LocalTalk or PPP for proxy AARP; - * if someone wants to try and add it, have fun - */ - if (atif->dev->type == ARPHRD_LOCALTLK || - atif->dev->type == ARPHRD_PPP) - goto out; - - /* - * create a new AARP entry with the flags set to be published -- - * we need this one to hang around even if it's in use - */ - entry = aarp_alloc(); - retval = -ENOMEM; - if (!entry) - goto out; - - entry->expires_at = -1; - entry->status = ATIF_PROBE; - entry->target_addr.s_node = sa->s_node; - entry->target_addr.s_net = sa->s_net; - entry->dev = atif->dev; - - write_lock_bh(&aarp_lock); - - hash = sa->s_node % (AARP_HASH_SIZE - 1); - entry->next = proxies[hash]; - proxies[hash] = entry; - - for (count = 0; count < AARP_RETRANSMIT_LIMIT; count++) { - aarp_send_probe(atif->dev, sa); - - /* Defer 1/10th */ - write_unlock_bh(&aarp_lock); - msleep(100); - write_lock_bh(&aarp_lock); - - if (entry->status & ATIF_PROBE_FAIL) - break; - } - - if (entry->status & ATIF_PROBE_FAIL) { - entry->expires_at = jiffies - 1; /* free the entry */ - retval = -EADDRINUSE; /* return network full */ - } else { /* clear the probing flag */ - entry->status &= ~ATIF_PROBE; - retval = 1; - } - - write_unlock_bh(&aarp_lock); -out: - return retval; -} - -/* Send a DDP frame */ -int aarp_send_ddp(struct net_device *dev, struct sk_buff *skb, - struct atalk_addr *sa, void *hwaddr) -{ - static char ddp_eth_multicast[ETH_ALEN] = - { 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; - int hash; - struct aarp_entry *a; - - skb_reset_network_header(skb); - - /* Check for LocalTalk first */ - if (dev->type == ARPHRD_LOCALTLK) { - struct atalk_addr *at = atalk_find_dev_addr(dev); - struct ddpehdr *ddp = (struct ddpehdr *)skb->data; - int ft = 2; - - /* - * Compressible ? - * - * IFF: src_net == dest_net == device_net - * (zero matches anything) - */ - - if ((!ddp->deh_snet || at->s_net == ddp->deh_snet) && - (!ddp->deh_dnet || at->s_net == ddp->deh_dnet)) { - skb_pull(skb, sizeof(*ddp) - 4); - - /* - * The upper two remaining bytes are the port - * numbers we just happen to need. Now put the - * length in the lower two. - */ - *((__be16 *)skb->data) = htons(skb->len); - ft = 1; - } - /* - * Nice and easy. No AARP type protocols occur here so we can - * just shovel it out with a 3 byte LLAP header - */ - - skb_push(skb, 3); - skb->data[0] = sa->s_node; - skb->data[1] = at->s_node; - skb->data[2] = ft; - skb->dev = dev; - goto sendit; - } - - /* On a PPP link we neither compress nor aarp. */ - if (dev->type == ARPHRD_PPP) { - skb->protocol = htons(ETH_P_PPPTALK); - skb->dev = dev; - goto sendit; - } - - /* Non ELAP we cannot do. */ - if (dev->type != ARPHRD_ETHER) - goto free_it; - - skb->dev = dev; - skb->protocol = htons(ETH_P_ATALK); - hash = sa->s_node % (AARP_HASH_SIZE - 1); - - /* Do we have a resolved entry? */ - if (sa->s_node == ATADDR_BCAST) { - /* Send it */ - ddp_dl->request(ddp_dl, skb, ddp_eth_multicast); - goto sent; - } - - write_lock_bh(&aarp_lock); - a = __aarp_find_entry(resolved[hash], dev, sa); - - if (a) { /* Return 1 and fill in the address */ - a->expires_at = jiffies + (sysctl_aarp_expiry_time * 10); - ddp_dl->request(ddp_dl, skb, a->hwaddr); - write_unlock_bh(&aarp_lock); - goto sent; - } - - /* Do we have an unresolved entry: This is the less common path */ - a = __aarp_find_entry(unresolved[hash], dev, sa); - if (a) { /* Queue onto the unresolved queue */ - skb_queue_tail(&a->packet_queue, skb); - goto out_unlock; - } - - /* Allocate a new entry */ - a = aarp_alloc(); - if (!a) { - /* Whoops slipped... good job it's an unreliable protocol 8) */ - write_unlock_bh(&aarp_lock); - goto free_it; - } - - /* Set up the queue */ - skb_queue_tail(&a->packet_queue, skb); - a->expires_at = jiffies + sysctl_aarp_resolve_time; - a->dev = dev; - a->next = unresolved[hash]; - a->target_addr = *sa; - a->xmit_count = 0; - unresolved[hash] = a; - unresolved_count++; - - /* Send an initial request for the address */ - __aarp_send_query(a); - - /* - * Switch to fast timer if needed (That is if this is the first - * unresolved entry to get added) - */ - - if (unresolved_count == 1) - mod_timer(&aarp_timer, jiffies + sysctl_aarp_tick_time); - - /* Now finally, it is safe to drop the lock. */ -out_unlock: - write_unlock_bh(&aarp_lock); - - /* Tell the ddp layer we have taken over for this frame. */ - goto sent; - -sendit: - if (skb->sk) - skb->priority = skb->sk->sk_priority; - if (dev_queue_xmit(skb)) - goto drop; -sent: - return NET_XMIT_SUCCESS; -free_it: - kfree_skb(skb); -drop: - return NET_XMIT_DROP; -} -EXPORT_SYMBOL(aarp_send_ddp); - -/* - * An entry in the aarp unresolved queue has become resolved. Send - * all the frames queued under it. - * - * Must run under aarp_lock. - */ -static void __aarp_resolved(struct aarp_entry **list, struct aarp_entry *a, - int hash) -{ - struct sk_buff *skb; - - while (*list) - if (*list == a) { - unresolved_count--; - *list = a->next; - - /* Move into the resolved list */ - a->next = resolved[hash]; - resolved[hash] = a; - - /* Kick frames off */ - while ((skb = skb_dequeue(&a->packet_queue)) != NULL) { - a->expires_at = jiffies + - sysctl_aarp_expiry_time * 10; - ddp_dl->request(ddp_dl, skb, a->hwaddr); - } - } else - list = &((*list)->next); -} - -/* - * This is called by the SNAP driver whenever we see an AARP SNAP - * frame. We currently only support Ethernet. - */ -static int aarp_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) -{ - struct elapaarp *ea = aarp_hdr(skb); - int hash, ret = 0; - __u16 function; - struct aarp_entry *a; - struct atalk_addr sa, *ma, da; - struct atalk_iface *ifa; - - if (!net_eq(dev_net(dev), &init_net)) - goto out0; - - /* We only do Ethernet SNAP AARP. */ - if (dev->type != ARPHRD_ETHER) - goto out0; - - /* Frame size ok? */ - if (!skb_pull(skb, sizeof(*ea))) - goto out0; - - function = ntohs(ea->function); - - /* Sanity check fields. */ - if (function < AARP_REQUEST || function > AARP_PROBE || - ea->hw_len != ETH_ALEN || ea->pa_len != AARP_PA_ALEN || - ea->pa_src_zero || ea->pa_dst_zero) - goto out0; - - /* Looks good. */ - hash = ea->pa_src_node % (AARP_HASH_SIZE - 1); - - /* Build an address. */ - sa.s_node = ea->pa_src_node; - sa.s_net = ea->pa_src_net; - - /* Process the packet. Check for replies of me. */ - ifa = atalk_find_dev(dev); - if (!ifa) - goto out1; - - if (ifa->status & ATIF_PROBE && - ifa->address.s_node == ea->pa_dst_node && - ifa->address.s_net == ea->pa_dst_net) { - ifa->status |= ATIF_PROBE_FAIL; /* Fail the probe (in use) */ - goto out1; - } - - /* Check for replies of proxy AARP entries */ - da.s_node = ea->pa_dst_node; - da.s_net = ea->pa_dst_net; - - write_lock_bh(&aarp_lock); - a = __aarp_find_entry(proxies[hash], dev, &da); - - if (a && a->status & ATIF_PROBE) { - a->status |= ATIF_PROBE_FAIL; - /* - * we do not respond to probe or request packets for - * this address while we are probing this address - */ - goto unlock; - } - - switch (function) { - case AARP_REPLY: - if (!unresolved_count) /* Speed up */ - break; - - /* Find the entry. */ - a = __aarp_find_entry(unresolved[hash], dev, &sa); - if (!a || dev != a->dev) - break; - - /* We can fill one in - this is good. */ - memcpy(a->hwaddr, ea->hw_src, ETH_ALEN); - __aarp_resolved(&unresolved[hash], a, hash); - if (!unresolved_count) - mod_timer(&aarp_timer, - jiffies + sysctl_aarp_expiry_time); - break; - - case AARP_REQUEST: - case AARP_PROBE: - - /* - * If it is my address set ma to my address and reply. - * We can treat probe and request the same. Probe - * simply means we shouldn't cache the querying host, - * as in a probe they are proposing an address not - * using one. - * - * Support for proxy-AARP added. We check if the - * address is one of our proxies before we toss the - * packet out. - */ - - sa.s_node = ea->pa_dst_node; - sa.s_net = ea->pa_dst_net; - - /* See if we have a matching proxy. */ - ma = __aarp_proxy_find(dev, &sa); - if (!ma) - ma = &ifa->address; - else { /* We need to make a copy of the entry. */ - da.s_node = sa.s_node; - da.s_net = sa.s_net; - ma = &da; - } - - if (function == AARP_PROBE) { - /* - * A probe implies someone trying to get an - * address. So as a precaution flush any - * entries we have for this address. - */ - a = __aarp_find_entry(resolved[sa.s_node % - (AARP_HASH_SIZE - 1)], - skb->dev, &sa); - - /* - * Make it expire next tick - that avoids us - * getting into a probe/flush/learn/probe/ - * flush/learn cycle during probing of a slow - * to respond host addr. - */ - if (a) { - a->expires_at = jiffies - 1; - mod_timer(&aarp_timer, jiffies + - sysctl_aarp_tick_time); - } - } - - if (sa.s_node != ma->s_node) - break; - - if (sa.s_net && ma->s_net && sa.s_net != ma->s_net) - break; - - sa.s_node = ea->pa_src_node; - sa.s_net = ea->pa_src_net; - - /* aarp_my_address has found the address to use for us. - */ - aarp_send_reply(dev, ma, &sa, ea->hw_src); - break; - } - -unlock: - write_unlock_bh(&aarp_lock); -out1: - ret = 1; -out0: - kfree_skb(skb); - return ret; -} - -static struct notifier_block aarp_notifier = { - .notifier_call = aarp_device_event, -}; - -static unsigned char aarp_snap_id[] = { 0x00, 0x00, 0x00, 0x80, 0xF3 }; - -void __init aarp_proto_init(void) -{ - aarp_dl = register_snap_client(aarp_snap_id, aarp_rcv); - if (!aarp_dl) - printk(KERN_CRIT "Unable to register AARP with SNAP.\n"); - setup_timer(&aarp_timer, aarp_expire_timeout, 0); - aarp_timer.expires = jiffies + sysctl_aarp_expiry_time; - add_timer(&aarp_timer); - register_netdevice_notifier(&aarp_notifier); -} - -/* Remove the AARP entries associated with a device. */ -void aarp_device_down(struct net_device *dev) -{ - int ct; - - write_lock_bh(&aarp_lock); - - for (ct = 0; ct < AARP_HASH_SIZE; ct++) { - __aarp_expire_device(&resolved[ct], dev); - __aarp_expire_device(&unresolved[ct], dev); - __aarp_expire_device(&proxies[ct], dev); - } - - write_unlock_bh(&aarp_lock); -} - -#ifdef CONFIG_PROC_FS -struct aarp_iter_state { - int bucket; - struct aarp_entry **table; -}; - -/* - * Get the aarp entry that is in the chain described - * by the iterator. - * If pos is set then skip till that index. - * pos = 1 is the first entry - */ -static struct aarp_entry *iter_next(struct aarp_iter_state *iter, loff_t *pos) -{ - int ct = iter->bucket; - struct aarp_entry **table = iter->table; - loff_t off = 0; - struct aarp_entry *entry; - - rescan: - while(ct < AARP_HASH_SIZE) { - for (entry = table[ct]; entry; entry = entry->next) { - if (!pos || ++off == *pos) { - iter->table = table; - iter->bucket = ct; - return entry; - } - } - ++ct; - } - - if (table == resolved) { - ct = 0; - table = unresolved; - goto rescan; - } - if (table == unresolved) { - ct = 0; - table = proxies; - goto rescan; - } - return NULL; -} - -static void *aarp_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(aarp_lock) -{ - struct aarp_iter_state *iter = seq->private; - - read_lock_bh(&aarp_lock); - iter->table = resolved; - iter->bucket = 0; - - return *pos ? iter_next(iter, pos) : SEQ_START_TOKEN; -} - -static void *aarp_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct aarp_entry *entry = v; - struct aarp_iter_state *iter = seq->private; - - ++*pos; - - /* first line after header */ - if (v == SEQ_START_TOKEN) - entry = iter_next(iter, NULL); - - /* next entry in current bucket */ - else if (entry->next) - entry = entry->next; - - /* next bucket or table */ - else { - ++iter->bucket; - entry = iter_next(iter, NULL); - } - return entry; -} - -static void aarp_seq_stop(struct seq_file *seq, void *v) - __releases(aarp_lock) -{ - read_unlock_bh(&aarp_lock); -} - -static const char *dt2str(unsigned long ticks) -{ - static char buf[32]; - - sprintf(buf, "%ld.%02ld", ticks / HZ, ((ticks % HZ) * 100 ) / HZ); - - return buf; -} - -static int aarp_seq_show(struct seq_file *seq, void *v) -{ - struct aarp_iter_state *iter = seq->private; - struct aarp_entry *entry = v; - unsigned long now = jiffies; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, - "Address Interface Hardware Address" - " Expires LastSend Retry Status\n"); - else { - seq_printf(seq, "%04X:%02X %-12s", - ntohs(entry->target_addr.s_net), - (unsigned int) entry->target_addr.s_node, - entry->dev ? entry->dev->name : "????"); - seq_printf(seq, "%pM", entry->hwaddr); - seq_printf(seq, " %8s", - dt2str((long)entry->expires_at - (long)now)); - if (iter->table == unresolved) - seq_printf(seq, " %8s %6hu", - dt2str(now - entry->last_sent), - entry->xmit_count); - else - seq_puts(seq, " "); - seq_printf(seq, " %s\n", - (iter->table == resolved) ? "resolved" - : (iter->table == unresolved) ? "unresolved" - : (iter->table == proxies) ? "proxies" - : "unknown"); - } - return 0; -} - -static const struct seq_operations aarp_seq_ops = { - .start = aarp_seq_start, - .next = aarp_seq_next, - .stop = aarp_seq_stop, - .show = aarp_seq_show, -}; - -static int aarp_seq_open(struct inode *inode, struct file *file) -{ - return seq_open_private(file, &aarp_seq_ops, - sizeof(struct aarp_iter_state)); -} - -const struct file_operations atalk_seq_arp_fops = { - .owner = THIS_MODULE, - .open = aarp_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, -}; -#endif - -/* General module cleanup. Called from cleanup_module() in ddp.c. */ -void aarp_cleanup_module(void) -{ - del_timer_sync(&aarp_timer); - unregister_netdevice_notifier(&aarp_notifier); - unregister_snap_client(aarp_dl); - aarp_purge(); -} diff --git a/net/appletalk/atalk_proc.c b/net/appletalk/atalk_proc.c deleted file mode 100644 index 6ef0e761e5de..000000000000 --- a/net/appletalk/atalk_proc.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - * atalk_proc.c - proc support for Appletalk - * - * Copyright(c) Arnaldo Carvalho de Melo - * - * 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, version 2. - */ - -#include -#include -#include -#include -#include -#include - - -static __inline__ struct atalk_iface *atalk_get_interface_idx(loff_t pos) -{ - struct atalk_iface *i; - - for (i = atalk_interfaces; pos && i; i = i->next) - --pos; - - return i; -} - -static void *atalk_seq_interface_start(struct seq_file *seq, loff_t *pos) - __acquires(atalk_interfaces_lock) -{ - loff_t l = *pos; - - read_lock_bh(&atalk_interfaces_lock); - return l ? atalk_get_interface_idx(--l) : SEQ_START_TOKEN; -} - -static void *atalk_seq_interface_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct atalk_iface *i; - - ++*pos; - if (v == SEQ_START_TOKEN) { - i = NULL; - if (atalk_interfaces) - i = atalk_interfaces; - goto out; - } - i = v; - i = i->next; -out: - return i; -} - -static void atalk_seq_interface_stop(struct seq_file *seq, void *v) - __releases(atalk_interfaces_lock) -{ - read_unlock_bh(&atalk_interfaces_lock); -} - -static int atalk_seq_interface_show(struct seq_file *seq, void *v) -{ - struct atalk_iface *iface; - - if (v == SEQ_START_TOKEN) { - seq_puts(seq, "Interface Address Networks " - "Status\n"); - goto out; - } - - iface = v; - seq_printf(seq, "%-16s %04X:%02X %04X-%04X %d\n", - iface->dev->name, ntohs(iface->address.s_net), - iface->address.s_node, ntohs(iface->nets.nr_firstnet), - ntohs(iface->nets.nr_lastnet), iface->status); -out: - return 0; -} - -static __inline__ struct atalk_route *atalk_get_route_idx(loff_t pos) -{ - struct atalk_route *r; - - for (r = atalk_routes; pos && r; r = r->next) - --pos; - - return r; -} - -static void *atalk_seq_route_start(struct seq_file *seq, loff_t *pos) - __acquires(atalk_routes_lock) -{ - loff_t l = *pos; - - read_lock_bh(&atalk_routes_lock); - return l ? atalk_get_route_idx(--l) : SEQ_START_TOKEN; -} - -static void *atalk_seq_route_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct atalk_route *r; - - ++*pos; - if (v == SEQ_START_TOKEN) { - r = NULL; - if (atalk_routes) - r = atalk_routes; - goto out; - } - r = v; - r = r->next; -out: - return r; -} - -static void atalk_seq_route_stop(struct seq_file *seq, void *v) - __releases(atalk_routes_lock) -{ - read_unlock_bh(&atalk_routes_lock); -} - -static int atalk_seq_route_show(struct seq_file *seq, void *v) -{ - struct atalk_route *rt; - - if (v == SEQ_START_TOKEN) { - seq_puts(seq, "Target Router Flags Dev\n"); - goto out; - } - - if (atrtr_default.dev) { - rt = &atrtr_default; - seq_printf(seq, "Default %04X:%02X %-4d %s\n", - ntohs(rt->gateway.s_net), rt->gateway.s_node, - rt->flags, rt->dev->name); - } - - rt = v; - seq_printf(seq, "%04X:%02X %04X:%02X %-4d %s\n", - ntohs(rt->target.s_net), rt->target.s_node, - ntohs(rt->gateway.s_net), rt->gateway.s_node, - rt->flags, rt->dev->name); -out: - return 0; -} - -static void *atalk_seq_socket_start(struct seq_file *seq, loff_t *pos) - __acquires(atalk_sockets_lock) -{ - read_lock_bh(&atalk_sockets_lock); - return seq_hlist_start_head(&atalk_sockets, *pos); -} - -static void *atalk_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_hlist_next(v, &atalk_sockets, pos); -} - -static void atalk_seq_socket_stop(struct seq_file *seq, void *v) - __releases(atalk_sockets_lock) -{ - read_unlock_bh(&atalk_sockets_lock); -} - -static int atalk_seq_socket_show(struct seq_file *seq, void *v) -{ - struct sock *s; - struct atalk_sock *at; - - if (v == SEQ_START_TOKEN) { - seq_printf(seq, "Type Local_addr Remote_addr Tx_queue " - "Rx_queue St UID\n"); - goto out; - } - - s = sk_entry(v); - at = at_sk(s); - - seq_printf(seq, "%02X %04X:%02X:%02X %04X:%02X:%02X %08X:%08X " - "%02X %d\n", - s->sk_type, ntohs(at->src_net), at->src_node, at->src_port, - ntohs(at->dest_net), at->dest_node, at->dest_port, - sk_wmem_alloc_get(s), - sk_rmem_alloc_get(s), - s->sk_state, SOCK_INODE(s->sk_socket)->i_uid); -out: - return 0; -} - -static const struct seq_operations atalk_seq_interface_ops = { - .start = atalk_seq_interface_start, - .next = atalk_seq_interface_next, - .stop = atalk_seq_interface_stop, - .show = atalk_seq_interface_show, -}; - -static const struct seq_operations atalk_seq_route_ops = { - .start = atalk_seq_route_start, - .next = atalk_seq_route_next, - .stop = atalk_seq_route_stop, - .show = atalk_seq_route_show, -}; - -static const struct seq_operations atalk_seq_socket_ops = { - .start = atalk_seq_socket_start, - .next = atalk_seq_socket_next, - .stop = atalk_seq_socket_stop, - .show = atalk_seq_socket_show, -}; - -static int atalk_seq_interface_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &atalk_seq_interface_ops); -} - -static int atalk_seq_route_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &atalk_seq_route_ops); -} - -static int atalk_seq_socket_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &atalk_seq_socket_ops); -} - -static const struct file_operations atalk_seq_interface_fops = { - .owner = THIS_MODULE, - .open = atalk_seq_interface_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static const struct file_operations atalk_seq_route_fops = { - .owner = THIS_MODULE, - .open = atalk_seq_route_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static const struct file_operations atalk_seq_socket_fops = { - .owner = THIS_MODULE, - .open = atalk_seq_socket_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static struct proc_dir_entry *atalk_proc_dir; - -int __init atalk_proc_init(void) -{ - struct proc_dir_entry *p; - int rc = -ENOMEM; - - atalk_proc_dir = proc_mkdir("atalk", init_net.proc_net); - if (!atalk_proc_dir) - goto out; - - p = proc_create("interface", S_IRUGO, atalk_proc_dir, - &atalk_seq_interface_fops); - if (!p) - goto out_interface; - - p = proc_create("route", S_IRUGO, atalk_proc_dir, - &atalk_seq_route_fops); - if (!p) - goto out_route; - - p = proc_create("socket", S_IRUGO, atalk_proc_dir, - &atalk_seq_socket_fops); - if (!p) - goto out_socket; - - p = proc_create("arp", S_IRUGO, atalk_proc_dir, &atalk_seq_arp_fops); - if (!p) - goto out_arp; - - rc = 0; -out: - return rc; -out_arp: - remove_proc_entry("socket", atalk_proc_dir); -out_socket: - remove_proc_entry("route", atalk_proc_dir); -out_route: - remove_proc_entry("interface", atalk_proc_dir); -out_interface: - remove_proc_entry("atalk", init_net.proc_net); - goto out; -} - -void __exit atalk_proc_exit(void) -{ - remove_proc_entry("interface", atalk_proc_dir); - remove_proc_entry("route", atalk_proc_dir); - remove_proc_entry("socket", atalk_proc_dir); - remove_proc_entry("arp", atalk_proc_dir); - remove_proc_entry("atalk", init_net.proc_net); -} diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c deleted file mode 100644 index c410b93fda2e..000000000000 --- a/net/appletalk/ddp.c +++ /dev/null @@ -1,1981 +0,0 @@ -/* - * DDP: An implementation of the AppleTalk DDP protocol for - * Ethernet 'ELAP'. - * - * Alan Cox - * - * With more than a little assistance from - * - * Wesley Craig - * - * Fixes: - * Neil Horman : Added missing device ioctls - * Michael Callahan : Made routing work - * Wesley Craig : Fix probing to listen to a - * passed node id. - * Alan Cox : Added send/recvmsg support - * Alan Cox : Moved at. to protinfo in - * socket. - * Alan Cox : Added firewall hooks. - * Alan Cox : Supports new ARPHRD_LOOPBACK - * Christer Weinigel : Routing and /proc fixes. - * Bradford Johnson : LocalTalk. - * Tom Dyas : Module support. - * Alan Cox : Hooks for PPP (based on the - * LocalTalk hook). - * Alan Cox : Posix bits - * Alan Cox/Mike Freeman : Possible fix to NBP problems - * Bradford Johnson : IP-over-DDP (experimental) - * Jay Schulist : Moved IP-over-DDP to its own - * driver file. (ipddp.c & ipddp.h) - * Jay Schulist : Made work as module with - * AppleTalk drivers, cleaned it. - * Rob Newberry : Added proxy AARP and AARP - * procfs, moved probing to AARP - * module. - * Adrian Sun/ - * Michael Zuelsdorff : fix for net.0 packets. don't - * allow illegal ether/tokentalk - * port assignment. we lose a - * valid localtalk port as a - * result. - * Arnaldo C. de Melo : Cleanup, in preparation for - * shared skb support 8) - * Arnaldo C. de Melo : Move proc stuff to atalk_proc.c, - * use seq_file - * - * 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 /* For TIOCOUTQ/INQ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "../core/kmap_skb.h" - -struct datalink_proto *ddp_dl, *aarp_dl; -static const struct proto_ops atalk_dgram_ops; - -/**************************************************************************\ -* * -* Handlers for the socket list. * -* * -\**************************************************************************/ - -HLIST_HEAD(atalk_sockets); -DEFINE_RWLOCK(atalk_sockets_lock); - -static inline void __atalk_insert_socket(struct sock *sk) -{ - sk_add_node(sk, &atalk_sockets); -} - -static inline void atalk_remove_socket(struct sock *sk) -{ - write_lock_bh(&atalk_sockets_lock); - sk_del_node_init(sk); - write_unlock_bh(&atalk_sockets_lock); -} - -static struct sock *atalk_search_socket(struct sockaddr_at *to, - struct atalk_iface *atif) -{ - struct sock *s; - struct hlist_node *node; - - read_lock_bh(&atalk_sockets_lock); - sk_for_each(s, node, &atalk_sockets) { - struct atalk_sock *at = at_sk(s); - - if (to->sat_port != at->src_port) - continue; - - if (to->sat_addr.s_net == ATADDR_ANYNET && - to->sat_addr.s_node == ATADDR_BCAST) - goto found; - - if (to->sat_addr.s_net == at->src_net && - (to->sat_addr.s_node == at->src_node || - to->sat_addr.s_node == ATADDR_BCAST || - to->sat_addr.s_node == ATADDR_ANYNODE)) - goto found; - - /* XXXX.0 -- we got a request for this router. make sure - * that the node is appropriately set. */ - if (to->sat_addr.s_node == ATADDR_ANYNODE && - to->sat_addr.s_net != ATADDR_ANYNET && - atif->address.s_node == at->src_node) { - to->sat_addr.s_node = atif->address.s_node; - goto found; - } - } - s = NULL; -found: - read_unlock_bh(&atalk_sockets_lock); - return s; -} - -/** - * atalk_find_or_insert_socket - Try to find a socket matching ADDR - * @sk - socket to insert in the list if it is not there already - * @sat - address to search for - * - * Try to find a socket matching ADDR in the socket list, if found then return - * it. If not, insert SK into the socket list. - * - * This entire operation must execute atomically. - */ -static struct sock *atalk_find_or_insert_socket(struct sock *sk, - struct sockaddr_at *sat) -{ - struct sock *s; - struct hlist_node *node; - struct atalk_sock *at; - - write_lock_bh(&atalk_sockets_lock); - sk_for_each(s, node, &atalk_sockets) { - at = at_sk(s); - - if (at->src_net == sat->sat_addr.s_net && - at->src_node == sat->sat_addr.s_node && - at->src_port == sat->sat_port) - goto found; - } - s = NULL; - __atalk_insert_socket(sk); /* Wheee, it's free, assign and insert. */ -found: - write_unlock_bh(&atalk_sockets_lock); - return s; -} - -static void atalk_destroy_timer(unsigned long data) -{ - struct sock *sk = (struct sock *)data; - - if (sk_has_allocations(sk)) { - sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME; - add_timer(&sk->sk_timer); - } else - sock_put(sk); -} - -static inline void atalk_destroy_socket(struct sock *sk) -{ - atalk_remove_socket(sk); - skb_queue_purge(&sk->sk_receive_queue); - - if (sk_has_allocations(sk)) { - setup_timer(&sk->sk_timer, atalk_destroy_timer, - (unsigned long)sk); - sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME; - add_timer(&sk->sk_timer); - } else - sock_put(sk); -} - -/**************************************************************************\ -* * -* Routing tables for the AppleTalk socket layer. * -* * -\**************************************************************************/ - -/* Anti-deadlock ordering is atalk_routes_lock --> iface_lock -DaveM */ -struct atalk_route *atalk_routes; -DEFINE_RWLOCK(atalk_routes_lock); - -struct atalk_iface *atalk_interfaces; -DEFINE_RWLOCK(atalk_interfaces_lock); - -/* For probing devices or in a routerless network */ -struct atalk_route atrtr_default; - -/* AppleTalk interface control */ -/* - * Drop a device. Doesn't drop any of its routes - that is the caller's - * problem. Called when we down the interface or delete the address. - */ -static void atif_drop_device(struct net_device *dev) -{ - struct atalk_iface **iface = &atalk_interfaces; - struct atalk_iface *tmp; - - write_lock_bh(&atalk_interfaces_lock); - while ((tmp = *iface) != NULL) { - if (tmp->dev == dev) { - *iface = tmp->next; - dev_put(dev); - kfree(tmp); - dev->atalk_ptr = NULL; - } else - iface = &tmp->next; - } - write_unlock_bh(&atalk_interfaces_lock); -} - -static struct atalk_iface *atif_add_device(struct net_device *dev, - struct atalk_addr *sa) -{ - struct atalk_iface *iface = kzalloc(sizeof(*iface), GFP_KERNEL); - - if (!iface) - goto out; - - dev_hold(dev); - iface->dev = dev; - dev->atalk_ptr = iface; - iface->address = *sa; - iface->status = 0; - - write_lock_bh(&atalk_interfaces_lock); - iface->next = atalk_interfaces; - atalk_interfaces = iface; - write_unlock_bh(&atalk_interfaces_lock); -out: - return iface; -} - -/* Perform phase 2 AARP probing on our tentative address */ -static int atif_probe_device(struct atalk_iface *atif) -{ - int netrange = ntohs(atif->nets.nr_lastnet) - - ntohs(atif->nets.nr_firstnet) + 1; - int probe_net = ntohs(atif->address.s_net); - int probe_node = atif->address.s_node; - int netct, nodect; - - /* Offset the network we start probing with */ - if (probe_net == ATADDR_ANYNET) { - probe_net = ntohs(atif->nets.nr_firstnet); - if (netrange) - probe_net += jiffies % netrange; - } - if (probe_node == ATADDR_ANYNODE) - probe_node = jiffies & 0xFF; - - /* Scan the networks */ - atif->status |= ATIF_PROBE; - for (netct = 0; netct <= netrange; netct++) { - /* Sweep the available nodes from a given start */ - atif->address.s_net = htons(probe_net); - for (nodect = 0; nodect < 256; nodect++) { - atif->address.s_node = (nodect + probe_node) & 0xFF; - if (atif->address.s_node > 0 && - atif->address.s_node < 254) { - /* Probe a proposed address */ - aarp_probe_network(atif); - - if (!(atif->status & ATIF_PROBE_FAIL)) { - atif->status &= ~ATIF_PROBE; - return 0; - } - } - atif->status &= ~ATIF_PROBE_FAIL; - } - probe_net++; - if (probe_net > ntohs(atif->nets.nr_lastnet)) - probe_net = ntohs(atif->nets.nr_firstnet); - } - atif->status &= ~ATIF_PROBE; - - return -EADDRINUSE; /* Network is full... */ -} - - -/* Perform AARP probing for a proxy address */ -static int atif_proxy_probe_device(struct atalk_iface *atif, - struct atalk_addr* proxy_addr) -{ - int netrange = ntohs(atif->nets.nr_lastnet) - - ntohs(atif->nets.nr_firstnet) + 1; - /* we probe the interface's network */ - int probe_net = ntohs(atif->address.s_net); - int probe_node = ATADDR_ANYNODE; /* we'll take anything */ - int netct, nodect; - - /* Offset the network we start probing with */ - if (probe_net == ATADDR_ANYNET) { - probe_net = ntohs(atif->nets.nr_firstnet); - if (netrange) - probe_net += jiffies % netrange; - } - - if (probe_node == ATADDR_ANYNODE) - probe_node = jiffies & 0xFF; - - /* Scan the networks */ - for (netct = 0; netct <= netrange; netct++) { - /* Sweep the available nodes from a given start */ - proxy_addr->s_net = htons(probe_net); - for (nodect = 0; nodect < 256; nodect++) { - proxy_addr->s_node = (nodect + probe_node) & 0xFF; - if (proxy_addr->s_node > 0 && - proxy_addr->s_node < 254) { - /* Tell AARP to probe a proposed address */ - int ret = aarp_proxy_probe_network(atif, - proxy_addr); - - if (ret != -EADDRINUSE) - return ret; - } - } - probe_net++; - if (probe_net > ntohs(atif->nets.nr_lastnet)) - probe_net = ntohs(atif->nets.nr_firstnet); - } - - return -EADDRINUSE; /* Network is full... */ -} - - -struct atalk_addr *atalk_find_dev_addr(struct net_device *dev) -{ - struct atalk_iface *iface = dev->atalk_ptr; - return iface ? &iface->address : NULL; -} - -static struct atalk_addr *atalk_find_primary(void) -{ - struct atalk_iface *fiface = NULL; - struct atalk_addr *retval; - struct atalk_iface *iface; - - /* - * Return a point-to-point interface only if - * there is no non-ptp interface available. - */ - read_lock_bh(&atalk_interfaces_lock); - for (iface = atalk_interfaces; iface; iface = iface->next) { - if (!fiface && !(iface->dev->flags & IFF_LOOPBACK)) - fiface = iface; - if (!(iface->dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) { - retval = &iface->address; - goto out; - } - } - - if (fiface) - retval = &fiface->address; - else if (atalk_interfaces) - retval = &atalk_interfaces->address; - else - retval = NULL; -out: - read_unlock_bh(&atalk_interfaces_lock); - return retval; -} - -/* - * Find a match for 'any network' - ie any of our interfaces with that - * node number will do just nicely. - */ -static struct atalk_iface *atalk_find_anynet(int node, struct net_device *dev) -{ - struct atalk_iface *iface = dev->atalk_ptr; - - if (!iface || iface->status & ATIF_PROBE) - goto out_err; - - if (node != ATADDR_BCAST && - iface->address.s_node != node && - node != ATADDR_ANYNODE) - goto out_err; -out: - return iface; -out_err: - iface = NULL; - goto out; -} - -/* Find a match for a specific network:node pair */ -static struct atalk_iface *atalk_find_interface(__be16 net, int node) -{ - struct atalk_iface *iface; - - read_lock_bh(&atalk_interfaces_lock); - for (iface = atalk_interfaces; iface; iface = iface->next) { - if ((node == ATADDR_BCAST || - node == ATADDR_ANYNODE || - iface->address.s_node == node) && - iface->address.s_net == net && - !(iface->status & ATIF_PROBE)) - break; - - /* XXXX.0 -- net.0 returns the iface associated with net */ - if (node == ATADDR_ANYNODE && net != ATADDR_ANYNET && - ntohs(iface->nets.nr_firstnet) <= ntohs(net) && - ntohs(net) <= ntohs(iface->nets.nr_lastnet)) - break; - } - read_unlock_bh(&atalk_interfaces_lock); - return iface; -} - - -/* - * Find a route for an AppleTalk packet. This ought to get cached in - * the socket (later on...). We know about host routes and the fact - * that a route must be direct to broadcast. - */ -static struct atalk_route *atrtr_find(struct atalk_addr *target) -{ - /* - * we must search through all routes unless we find a - * host route, because some host routes might overlap - * network routes - */ - struct atalk_route *net_route = NULL; - struct atalk_route *r; - - read_lock_bh(&atalk_routes_lock); - for (r = atalk_routes; r; r = r->next) { - if (!(r->flags & RTF_UP)) - continue; - - if (r->target.s_net == target->s_net) { - if (r->flags & RTF_HOST) { - /* - * if this host route is for the target, - * the we're done - */ - if (r->target.s_node == target->s_node) - goto out; - } else - /* - * this route will work if there isn't a - * direct host route, so cache it - */ - net_route = r; - } - } - - /* - * if we found a network route but not a direct host - * route, then return it - */ - if (net_route) - r = net_route; - else if (atrtr_default.dev) - r = &atrtr_default; - else /* No route can be found */ - r = NULL; -out: - read_unlock_bh(&atalk_routes_lock); - return r; -} - - -/* - * Given an AppleTalk network, find the device to use. This can be - * a simple lookup. - */ -struct net_device *atrtr_get_dev(struct atalk_addr *sa) -{ - struct atalk_route *atr = atrtr_find(sa); - return atr ? atr->dev : NULL; -} - -/* Set up a default router */ -static void atrtr_set_default(struct net_device *dev) -{ - atrtr_default.dev = dev; - atrtr_default.flags = RTF_UP; - atrtr_default.gateway.s_net = htons(0); - atrtr_default.gateway.s_node = 0; -} - -/* - * Add a router. Basically make sure it looks valid and stuff the - * entry in the list. While it uses netranges we always set them to one - * entry to work like netatalk. - */ -static int atrtr_create(struct rtentry *r, struct net_device *devhint) -{ - struct sockaddr_at *ta = (struct sockaddr_at *)&r->rt_dst; - struct sockaddr_at *ga = (struct sockaddr_at *)&r->rt_gateway; - struct atalk_route *rt; - struct atalk_iface *iface, *riface; - int retval = -EINVAL; - - /* - * Fixme: Raise/Lower a routing change semaphore for these - * operations. - */ - - /* Validate the request */ - if (ta->sat_family != AF_APPLETALK || - (!devhint && ga->sat_family != AF_APPLETALK)) - goto out; - - /* Now walk the routing table and make our decisions */ - write_lock_bh(&atalk_routes_lock); - for (rt = atalk_routes; rt; rt = rt->next) { - if (r->rt_flags != rt->flags) - continue; - - if (ta->sat_addr.s_net == rt->target.s_net) { - if (!(rt->flags & RTF_HOST)) - break; - if (ta->sat_addr.s_node == rt->target.s_node) - break; - } - } - - if (!devhint) { - riface = NULL; - - read_lock_bh(&atalk_interfaces_lock); - for (iface = atalk_interfaces; iface; iface = iface->next) { - if (!riface && - ntohs(ga->sat_addr.s_net) >= - ntohs(iface->nets.nr_firstnet) && - ntohs(ga->sat_addr.s_net) <= - ntohs(iface->nets.nr_lastnet)) - riface = iface; - - if (ga->sat_addr.s_net == iface->address.s_net && - ga->sat_addr.s_node == iface->address.s_node) - riface = iface; - } - read_unlock_bh(&atalk_interfaces_lock); - - retval = -ENETUNREACH; - if (!riface) - goto out_unlock; - - devhint = riface->dev; - } - - if (!rt) { - rt = kzalloc(sizeof(*rt), GFP_ATOMIC); - - retval = -ENOBUFS; - if (!rt) - goto out_unlock; - - rt->next = atalk_routes; - atalk_routes = rt; - } - - /* Fill in the routing entry */ - rt->target = ta->sat_addr; - dev_hold(devhint); - rt->dev = devhint; - rt->flags = r->rt_flags; - rt->gateway = ga->sat_addr; - - retval = 0; -out_unlock: - write_unlock_bh(&atalk_routes_lock); -out: - return retval; -} - -/* Delete a route. Find it and discard it */ -static int atrtr_delete(struct atalk_addr * addr) -{ - struct atalk_route **r = &atalk_routes; - int retval = 0; - struct atalk_route *tmp; - - write_lock_bh(&atalk_routes_lock); - while ((tmp = *r) != NULL) { - if (tmp->target.s_net == addr->s_net && - (!(tmp->flags&RTF_GATEWAY) || - tmp->target.s_node == addr->s_node)) { - *r = tmp->next; - dev_put(tmp->dev); - kfree(tmp); - goto out; - } - r = &tmp->next; - } - retval = -ENOENT; -out: - write_unlock_bh(&atalk_routes_lock); - return retval; -} - -/* - * Called when a device is downed. Just throw away any routes - * via it. - */ -static void atrtr_device_down(struct net_device *dev) -{ - struct atalk_route **r = &atalk_routes; - struct atalk_route *tmp; - - write_lock_bh(&atalk_routes_lock); - while ((tmp = *r) != NULL) { - if (tmp->dev == dev) { - *r = tmp->next; - dev_put(dev); - kfree(tmp); - } else - r = &tmp->next; - } - write_unlock_bh(&atalk_routes_lock); - - if (atrtr_default.dev == dev) - atrtr_set_default(NULL); -} - -/* Actually down the interface */ -static inline void atalk_dev_down(struct net_device *dev) -{ - atrtr_device_down(dev); /* Remove all routes for the device */ - aarp_device_down(dev); /* Remove AARP entries for the device */ - atif_drop_device(dev); /* Remove the device */ -} - -/* - * A device event has occurred. Watch for devices going down and - * delete our use of them (iface and route). - */ -static int ddp_device_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - struct net_device *dev = ptr; - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (event == NETDEV_DOWN) - /* Discard any use of this */ - atalk_dev_down(dev); - - return NOTIFY_DONE; -} - -/* ioctl calls. Shouldn't even need touching */ -/* Device configuration ioctl calls */ -static int atif_ioctl(int cmd, void __user *arg) -{ - static char aarp_mcast[6] = { 0x09, 0x00, 0x00, 0xFF, 0xFF, 0xFF }; - struct ifreq atreq; - struct atalk_netrange *nr; - struct sockaddr_at *sa; - struct net_device *dev; - struct atalk_iface *atif; - int ct; - int limit; - struct rtentry rtdef; - int add_route; - - if (copy_from_user(&atreq, arg, sizeof(atreq))) - return -EFAULT; - - dev = __dev_get_by_name(&init_net, atreq.ifr_name); - if (!dev) - return -ENODEV; - - sa = (struct sockaddr_at *)&atreq.ifr_addr; - atif = atalk_find_dev(dev); - - switch (cmd) { - case SIOCSIFADDR: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (sa->sat_family != AF_APPLETALK) - return -EINVAL; - if (dev->type != ARPHRD_ETHER && - dev->type != ARPHRD_LOOPBACK && - dev->type != ARPHRD_LOCALTLK && - dev->type != ARPHRD_PPP) - return -EPROTONOSUPPORT; - - nr = (struct atalk_netrange *)&sa->sat_zero[0]; - add_route = 1; - - /* - * if this is a point-to-point iface, and we already - * have an iface for this AppleTalk address, then we - * should not add a route - */ - if ((dev->flags & IFF_POINTOPOINT) && - atalk_find_interface(sa->sat_addr.s_net, - sa->sat_addr.s_node)) { - printk(KERN_DEBUG "AppleTalk: point-to-point " - "interface added with " - "existing address\n"); - add_route = 0; - } - - /* - * Phase 1 is fine on LocalTalk but we don't do - * EtherTalk phase 1. Anyone wanting to add it go ahead. - */ - if (dev->type == ARPHRD_ETHER && nr->nr_phase != 2) - return -EPROTONOSUPPORT; - if (sa->sat_addr.s_node == ATADDR_BCAST || - sa->sat_addr.s_node == 254) - return -EINVAL; - if (atif) { - /* Already setting address */ - if (atif->status & ATIF_PROBE) - return -EBUSY; - - atif->address.s_net = sa->sat_addr.s_net; - atif->address.s_node = sa->sat_addr.s_node; - atrtr_device_down(dev); /* Flush old routes */ - } else { - atif = atif_add_device(dev, &sa->sat_addr); - if (!atif) - return -ENOMEM; - } - atif->nets = *nr; - - /* - * Check if the chosen address is used. If so we - * error and atalkd will try another. - */ - - if (!(dev->flags & IFF_LOOPBACK) && - !(dev->flags & IFF_POINTOPOINT) && - atif_probe_device(atif) < 0) { - atif_drop_device(dev); - return -EADDRINUSE; - } - - /* Hey it worked - add the direct routes */ - sa = (struct sockaddr_at *)&rtdef.rt_gateway; - sa->sat_family = AF_APPLETALK; - sa->sat_addr.s_net = atif->address.s_net; - sa->sat_addr.s_node = atif->address.s_node; - sa = (struct sockaddr_at *)&rtdef.rt_dst; - rtdef.rt_flags = RTF_UP; - sa->sat_family = AF_APPLETALK; - sa->sat_addr.s_node = ATADDR_ANYNODE; - if (dev->flags & IFF_LOOPBACK || - dev->flags & IFF_POINTOPOINT) - rtdef.rt_flags |= RTF_HOST; - - /* Routerless initial state */ - if (nr->nr_firstnet == htons(0) && - nr->nr_lastnet == htons(0xFFFE)) { - sa->sat_addr.s_net = atif->address.s_net; - atrtr_create(&rtdef, dev); - atrtr_set_default(dev); - } else { - limit = ntohs(nr->nr_lastnet); - if (limit - ntohs(nr->nr_firstnet) > 4096) { - printk(KERN_WARNING "Too many routes/" - "iface.\n"); - return -EINVAL; - } - if (add_route) - for (ct = ntohs(nr->nr_firstnet); - ct <= limit; ct++) { - sa->sat_addr.s_net = htons(ct); - atrtr_create(&rtdef, dev); - } - } - dev_mc_add_global(dev, aarp_mcast); - return 0; - - case SIOCGIFADDR: - if (!atif) - return -EADDRNOTAVAIL; - - sa->sat_family = AF_APPLETALK; - sa->sat_addr = atif->address; - break; - - case SIOCGIFBRDADDR: - if (!atif) - return -EADDRNOTAVAIL; - - sa->sat_family = AF_APPLETALK; - sa->sat_addr.s_net = atif->address.s_net; - sa->sat_addr.s_node = ATADDR_BCAST; - break; - - case SIOCATALKDIFADDR: - case SIOCDIFADDR: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (sa->sat_family != AF_APPLETALK) - return -EINVAL; - atalk_dev_down(dev); - break; - - case SIOCSARP: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (sa->sat_family != AF_APPLETALK) - return -EINVAL; - /* - * for now, we only support proxy AARP on ELAP; - * we should be able to do it for LocalTalk, too. - */ - if (dev->type != ARPHRD_ETHER) - return -EPROTONOSUPPORT; - - /* - * atif points to the current interface on this network; - * we aren't concerned about its current status (at - * least for now), but it has all the settings about - * the network we're going to probe. Consequently, it - * must exist. - */ - if (!atif) - return -EADDRNOTAVAIL; - - nr = (struct atalk_netrange *)&(atif->nets); - /* - * Phase 1 is fine on Localtalk but we don't do - * Ethertalk phase 1. Anyone wanting to add it go ahead. - */ - if (dev->type == ARPHRD_ETHER && nr->nr_phase != 2) - return -EPROTONOSUPPORT; - - if (sa->sat_addr.s_node == ATADDR_BCAST || - sa->sat_addr.s_node == 254) - return -EINVAL; - - /* - * Check if the chosen address is used. If so we - * error and ATCP will try another. - */ - if (atif_proxy_probe_device(atif, &(sa->sat_addr)) < 0) - return -EADDRINUSE; - - /* - * We now have an address on the local network, and - * the AARP code will defend it for us until we take it - * down. We don't set up any routes right now, because - * ATCP will install them manually via SIOCADDRT. - */ - break; - - case SIOCDARP: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (sa->sat_family != AF_APPLETALK) - return -EINVAL; - if (!atif) - return -EADDRNOTAVAIL; - - /* give to aarp module to remove proxy entry */ - aarp_proxy_remove(atif->dev, &(sa->sat_addr)); - return 0; - } - - return copy_to_user(arg, &atreq, sizeof(atreq)) ? -EFAULT : 0; -} - -/* Routing ioctl() calls */ -static int atrtr_ioctl(unsigned int cmd, void __user *arg) -{ - struct rtentry rt; - - if (copy_from_user(&rt, arg, sizeof(rt))) - return -EFAULT; - - switch (cmd) { - case SIOCDELRT: - if (rt.rt_dst.sa_family != AF_APPLETALK) - return -EINVAL; - return atrtr_delete(&((struct sockaddr_at *) - &rt.rt_dst)->sat_addr); - - case SIOCADDRT: { - struct net_device *dev = NULL; - if (rt.rt_dev) { - char name[IFNAMSIZ]; - if (copy_from_user(name, rt.rt_dev, IFNAMSIZ-1)) - return -EFAULT; - name[IFNAMSIZ-1] = '\0'; - dev = __dev_get_by_name(&init_net, name); - if (!dev) - return -ENODEV; - } - return atrtr_create(&rt, dev); - } - } - return -EINVAL; -} - -/**************************************************************************\ -* * -* Handling for system calls applied via the various interfaces to an * -* AppleTalk socket object. * -* * -\**************************************************************************/ - -/* - * Checksum: This is 'optional'. It's quite likely also a good - * candidate for assembler hackery 8) - */ -static unsigned long atalk_sum_partial(const unsigned char *data, - int len, unsigned long sum) -{ - /* This ought to be unwrapped neatly. I'll trust gcc for now */ - while (len--) { - sum += *data++; - sum = rol16(sum, 1); - } - return sum; -} - -/* Checksum skb data -- similar to skb_checksum */ -static unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset, - int len, unsigned long sum) -{ - int start = skb_headlen(skb); - struct sk_buff *frag_iter; - int i, copy; - - /* checksum stuff in header space */ - if ( (copy = start - offset) > 0) { - if (copy > len) - copy = len; - sum = atalk_sum_partial(skb->data + offset, copy, sum); - if ( (len -= copy) == 0) - return sum; - - offset += copy; - } - - /* checksum stuff in frags */ - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - int end; - - WARN_ON(start > offset + len); - - end = start + skb_shinfo(skb)->frags[i].size; - if ((copy = end - offset) > 0) { - u8 *vaddr; - skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - - if (copy > len) - copy = len; - vaddr = kmap_skb_frag(frag); - sum = atalk_sum_partial(vaddr + frag->page_offset + - offset - start, copy, sum); - kunmap_skb_frag(vaddr); - - if (!(len -= copy)) - return sum; - offset += copy; - } - start = end; - } - - skb_walk_frags(skb, frag_iter) { - int end; - - WARN_ON(start > offset + len); - - end = start + frag_iter->len; - if ((copy = end - offset) > 0) { - if (copy > len) - copy = len; - sum = atalk_sum_skb(frag_iter, offset - start, - copy, sum); - if ((len -= copy) == 0) - return sum; - offset += copy; - } - start = end; - } - - BUG_ON(len > 0); - - return sum; -} - -static __be16 atalk_checksum(const struct sk_buff *skb, int len) -{ - unsigned long sum; - - /* skip header 4 bytes */ - sum = atalk_sum_skb(skb, 4, len-4, 0); - - /* Use 0xFFFF for 0. 0 itself means none */ - return sum ? htons((unsigned short)sum) : htons(0xFFFF); -} - -static struct proto ddp_proto = { - .name = "DDP", - .owner = THIS_MODULE, - .obj_size = sizeof(struct atalk_sock), -}; - -/* - * Create a socket. Initialise the socket, blank the addresses - * set the state. - */ -static int atalk_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - int rc = -ESOCKTNOSUPPORT; - - if (!net_eq(net, &init_net)) - return -EAFNOSUPPORT; - - /* - * We permit SOCK_DGRAM and RAW is an extension. It is trivial to do - * and gives you the full ELAP frame. Should be handy for CAP 8) - */ - if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM) - goto out; - rc = -ENOMEM; - sk = sk_alloc(net, PF_APPLETALK, GFP_KERNEL, &ddp_proto); - if (!sk) - goto out; - rc = 0; - sock->ops = &atalk_dgram_ops; - sock_init_data(sock, sk); - - /* Checksums on by default */ - sock_set_flag(sk, SOCK_ZAPPED); -out: - return rc; -} - -/* Free a socket. No work needed */ -static int atalk_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - lock_kernel(); - if (sk) { - sock_orphan(sk); - sock->sk = NULL; - atalk_destroy_socket(sk); - } - unlock_kernel(); - return 0; -} - -/** - * atalk_pick_and_bind_port - Pick a source port when one is not given - * @sk - socket to insert into the tables - * @sat - address to search for - * - * Pick a source port when one is not given. If we can find a suitable free - * one, we insert the socket into the tables using it. - * - * This whole operation must be atomic. - */ -static int atalk_pick_and_bind_port(struct sock *sk, struct sockaddr_at *sat) -{ - int retval; - - write_lock_bh(&atalk_sockets_lock); - - for (sat->sat_port = ATPORT_RESERVED; - sat->sat_port < ATPORT_LAST; - sat->sat_port++) { - struct sock *s; - struct hlist_node *node; - - sk_for_each(s, node, &atalk_sockets) { - struct atalk_sock *at = at_sk(s); - - if (at->src_net == sat->sat_addr.s_net && - at->src_node == sat->sat_addr.s_node && - at->src_port == sat->sat_port) - goto try_next_port; - } - - /* Wheee, it's free, assign and insert. */ - __atalk_insert_socket(sk); - at_sk(sk)->src_port = sat->sat_port; - retval = 0; - goto out; - -try_next_port:; - } - - retval = -EBUSY; -out: - write_unlock_bh(&atalk_sockets_lock); - return retval; -} - -static int atalk_autobind(struct sock *sk) -{ - struct atalk_sock *at = at_sk(sk); - struct sockaddr_at sat; - struct atalk_addr *ap = atalk_find_primary(); - int n = -EADDRNOTAVAIL; - - if (!ap || ap->s_net == htons(ATADDR_ANYNET)) - goto out; - - at->src_net = sat.sat_addr.s_net = ap->s_net; - at->src_node = sat.sat_addr.s_node = ap->s_node; - - n = atalk_pick_and_bind_port(sk, &sat); - if (!n) - sock_reset_flag(sk, SOCK_ZAPPED); -out: - return n; -} - -/* Set the address 'our end' of the connection */ -static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) -{ - struct sockaddr_at *addr = (struct sockaddr_at *)uaddr; - struct sock *sk = sock->sk; - struct atalk_sock *at = at_sk(sk); - int err; - - if (!sock_flag(sk, SOCK_ZAPPED) || - addr_len != sizeof(struct sockaddr_at)) - return -EINVAL; - - if (addr->sat_family != AF_APPLETALK) - return -EAFNOSUPPORT; - - lock_kernel(); - if (addr->sat_addr.s_net == htons(ATADDR_ANYNET)) { - struct atalk_addr *ap = atalk_find_primary(); - - err = -EADDRNOTAVAIL; - if (!ap) - goto out; - - at->src_net = addr->sat_addr.s_net = ap->s_net; - at->src_node = addr->sat_addr.s_node= ap->s_node; - } else { - err = -EADDRNOTAVAIL; - if (!atalk_find_interface(addr->sat_addr.s_net, - addr->sat_addr.s_node)) - goto out; - - at->src_net = addr->sat_addr.s_net; - at->src_node = addr->sat_addr.s_node; - } - - if (addr->sat_port == ATADDR_ANYPORT) { - err = atalk_pick_and_bind_port(sk, addr); - - if (err < 0) - goto out; - } else { - at->src_port = addr->sat_port; - - err = -EADDRINUSE; - if (atalk_find_or_insert_socket(sk, addr)) - goto out; - } - - sock_reset_flag(sk, SOCK_ZAPPED); - err = 0; -out: - unlock_kernel(); - return err; -} - -/* Set the address we talk to */ -static int atalk_connect(struct socket *sock, struct sockaddr *uaddr, - int addr_len, int flags) -{ - struct sock *sk = sock->sk; - struct atalk_sock *at = at_sk(sk); - struct sockaddr_at *addr; - int err; - - sk->sk_state = TCP_CLOSE; - sock->state = SS_UNCONNECTED; - - if (addr_len != sizeof(*addr)) - return -EINVAL; - - addr = (struct sockaddr_at *)uaddr; - - if (addr->sat_family != AF_APPLETALK) - return -EAFNOSUPPORT; - - if (addr->sat_addr.s_node == ATADDR_BCAST && - !sock_flag(sk, SOCK_BROADCAST)) { -#if 1 - printk(KERN_WARNING "%s is broken and did not set " - "SO_BROADCAST. It will break when 2.2 is " - "released.\n", - current->comm); -#else - return -EACCES; -#endif - } - - lock_kernel(); - err = -EBUSY; - if (sock_flag(sk, SOCK_ZAPPED)) - if (atalk_autobind(sk) < 0) - goto out; - - err = -ENETUNREACH; - if (!atrtr_get_dev(&addr->sat_addr)) - goto out; - - at->dest_port = addr->sat_port; - at->dest_net = addr->sat_addr.s_net; - at->dest_node = addr->sat_addr.s_node; - - sock->state = SS_CONNECTED; - sk->sk_state = TCP_ESTABLISHED; - err = 0; -out: - unlock_kernel(); - return err; -} - -/* - * Find the name of an AppleTalk socket. Just copy the right - * fields into the sockaddr. - */ -static int atalk_getname(struct socket *sock, struct sockaddr *uaddr, - int *uaddr_len, int peer) -{ - struct sockaddr_at sat; - struct sock *sk = sock->sk; - struct atalk_sock *at = at_sk(sk); - int err; - - lock_kernel(); - err = -ENOBUFS; - if (sock_flag(sk, SOCK_ZAPPED)) - if (atalk_autobind(sk) < 0) - goto out; - - *uaddr_len = sizeof(struct sockaddr_at); - memset(&sat.sat_zero, 0, sizeof(sat.sat_zero)); - - if (peer) { - err = -ENOTCONN; - if (sk->sk_state != TCP_ESTABLISHED) - goto out; - - sat.sat_addr.s_net = at->dest_net; - sat.sat_addr.s_node = at->dest_node; - sat.sat_port = at->dest_port; - } else { - sat.sat_addr.s_net = at->src_net; - sat.sat_addr.s_node = at->src_node; - sat.sat_port = at->src_port; - } - - err = 0; - sat.sat_family = AF_APPLETALK; - memcpy(uaddr, &sat, sizeof(sat)); - -out: - unlock_kernel(); - return err; -} - -static unsigned int atalk_poll(struct file *file, struct socket *sock, - poll_table *wait) -{ - int err; - lock_kernel(); - err = datagram_poll(file, sock, wait); - unlock_kernel(); - return err; -} - -#if defined(CONFIG_IPDDP) || defined(CONFIG_IPDDP_MODULE) -static __inline__ int is_ip_over_ddp(struct sk_buff *skb) -{ - return skb->data[12] == 22; -} - -static int handle_ip_over_ddp(struct sk_buff *skb) -{ - struct net_device *dev = __dev_get_by_name(&init_net, "ipddp0"); - struct net_device_stats *stats; - - /* This needs to be able to handle ipddp"N" devices */ - if (!dev) { - kfree_skb(skb); - return NET_RX_DROP; - } - - skb->protocol = htons(ETH_P_IP); - skb_pull(skb, 13); - skb->dev = dev; - skb_reset_transport_header(skb); - - stats = netdev_priv(dev); - stats->rx_packets++; - stats->rx_bytes += skb->len + 13; - return netif_rx(skb); /* Send the SKB up to a higher place. */ -} -#else -/* make it easy for gcc to optimize this test out, i.e. kill the code */ -#define is_ip_over_ddp(skb) 0 -#define handle_ip_over_ddp(skb) 0 -#endif - -static int atalk_route_packet(struct sk_buff *skb, struct net_device *dev, - struct ddpehdr *ddp, __u16 len_hops, int origlen) -{ - struct atalk_route *rt; - struct atalk_addr ta; - - /* - * Don't route multicast, etc., packets, or packets sent to "this - * network" - */ - if (skb->pkt_type != PACKET_HOST || !ddp->deh_dnet) { - /* - * FIXME: - * - * Can it ever happen that a packet is from a PPP iface and - * needs to be broadcast onto the default network? - */ - if (dev->type == ARPHRD_PPP) - printk(KERN_DEBUG "AppleTalk: didn't forward broadcast " - "packet received from PPP iface\n"); - goto free_it; - } - - ta.s_net = ddp->deh_dnet; - ta.s_node = ddp->deh_dnode; - - /* Route the packet */ - rt = atrtr_find(&ta); - /* increment hops count */ - len_hops += 1 << 10; - if (!rt || !(len_hops & (15 << 10))) - goto free_it; - - /* FIXME: use skb->cb to be able to use shared skbs */ - - /* - * Route goes through another gateway, so set the target to the - * gateway instead. - */ - - if (rt->flags & RTF_GATEWAY) { - ta.s_net = rt->gateway.s_net; - ta.s_node = rt->gateway.s_node; - } - - /* Fix up skb->len field */ - skb_trim(skb, min_t(unsigned int, origlen, - (rt->dev->hard_header_len + - ddp_dl->header_length + (len_hops & 1023)))); - - /* FIXME: use skb->cb to be able to use shared skbs */ - ddp->deh_len_hops = htons(len_hops); - - /* - * Send the buffer onwards - * - * Now we must always be careful. If it's come from LocalTalk to - * EtherTalk it might not fit - * - * Order matters here: If a packet has to be copied to make a new - * headroom (rare hopefully) then it won't need unsharing. - * - * Note. ddp-> becomes invalid at the realloc. - */ - if (skb_headroom(skb) < 22) { - /* 22 bytes - 12 ether, 2 len, 3 802.2 5 snap */ - struct sk_buff *nskb = skb_realloc_headroom(skb, 32); - kfree_skb(skb); - skb = nskb; - } else - skb = skb_unshare(skb, GFP_ATOMIC); - - /* - * If the buffer didn't vanish into the lack of space bitbucket we can - * send it. - */ - if (skb == NULL) - goto drop; - - if (aarp_send_ddp(rt->dev, skb, &ta, NULL) == NET_XMIT_DROP) - return NET_RX_DROP; - return NET_RX_SUCCESS; -free_it: - kfree_skb(skb); -drop: - return NET_RX_DROP; -} - -/** - * atalk_rcv - Receive a packet (in skb) from device dev - * @skb - packet received - * @dev - network device where the packet comes from - * @pt - packet type - * - * Receive a packet (in skb) from device dev. This has come from the SNAP - * decoder, and on entry skb->transport_header is the DDP header, skb->len - * is the DDP header, skb->len is the DDP length. The physical headers - * have been extracted. PPP should probably pass frames marked as for this - * layer. [ie ARPHRD_ETHERTALK] - */ -static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) -{ - struct ddpehdr *ddp; - struct sock *sock; - struct atalk_iface *atif; - struct sockaddr_at tosat; - int origlen; - __u16 len_hops; - - if (!net_eq(dev_net(dev), &init_net)) - goto drop; - - /* Don't mangle buffer if shared */ - if (!(skb = skb_share_check(skb, GFP_ATOMIC))) - goto out; - - /* Size check and make sure header is contiguous */ - if (!pskb_may_pull(skb, sizeof(*ddp))) - goto drop; - - ddp = ddp_hdr(skb); - - len_hops = ntohs(ddp->deh_len_hops); - - /* Trim buffer in case of stray trailing data */ - origlen = skb->len; - skb_trim(skb, min_t(unsigned int, skb->len, len_hops & 1023)); - - /* - * Size check to see if ddp->deh_len was crap - * (Otherwise we'll detonate most spectacularly - * in the middle of atalk_checksum() or recvmsg()). - */ - if (skb->len < sizeof(*ddp) || skb->len < (len_hops & 1023)) { - pr_debug("AppleTalk: dropping corrupted frame (deh_len=%u, " - "skb->len=%u)\n", len_hops & 1023, skb->len); - goto drop; - } - - /* - * Any checksums. Note we don't do htons() on this == is assumed to be - * valid for net byte orders all over the networking code... - */ - if (ddp->deh_sum && - atalk_checksum(skb, len_hops & 1023) != ddp->deh_sum) - /* Not a valid AppleTalk frame - dustbin time */ - goto drop; - - /* Check the packet is aimed at us */ - if (!ddp->deh_dnet) /* Net 0 is 'this network' */ - atif = atalk_find_anynet(ddp->deh_dnode, dev); - else - atif = atalk_find_interface(ddp->deh_dnet, ddp->deh_dnode); - - if (!atif) { - /* Not ours, so we route the packet via the correct - * AppleTalk iface - */ - return atalk_route_packet(skb, dev, ddp, len_hops, origlen); - } - - /* if IP over DDP is not selected this code will be optimized out */ - if (is_ip_over_ddp(skb)) - return handle_ip_over_ddp(skb); - /* - * Which socket - atalk_search_socket() looks for a *full match* - * of the tuple. - */ - tosat.sat_addr.s_net = ddp->deh_dnet; - tosat.sat_addr.s_node = ddp->deh_dnode; - tosat.sat_port = ddp->deh_dport; - - sock = atalk_search_socket(&tosat, atif); - if (!sock) /* But not one of our sockets */ - goto drop; - - /* Queue packet (standard) */ - skb->sk = sock; - - if (sock_queue_rcv_skb(sock, skb) < 0) - goto drop; - - return NET_RX_SUCCESS; - -drop: - kfree_skb(skb); -out: - return NET_RX_DROP; - -} - -/* - * Receive a LocalTalk frame. We make some demands on the caller here. - * Caller must provide enough headroom on the packet to pull the short - * header and append a long one. - */ -static int ltalk_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) -{ - if (!net_eq(dev_net(dev), &init_net)) - goto freeit; - - /* Expand any short form frames */ - if (skb_mac_header(skb)[2] == 1) { - struct ddpehdr *ddp; - /* Find our address */ - struct atalk_addr *ap = atalk_find_dev_addr(dev); - - if (!ap || skb->len < sizeof(__be16) || skb->len > 1023) - goto freeit; - - /* Don't mangle buffer if shared */ - if (!(skb = skb_share_check(skb, GFP_ATOMIC))) - return 0; - - /* - * The push leaves us with a ddephdr not an shdr, and - * handily the port bytes in the right place preset. - */ - ddp = (struct ddpehdr *) skb_push(skb, sizeof(*ddp) - 4); - - /* Now fill in the long header */ - - /* - * These two first. The mac overlays the new source/dest - * network information so we MUST copy these before - * we write the network numbers ! - */ - - ddp->deh_dnode = skb_mac_header(skb)[0]; /* From physical header */ - ddp->deh_snode = skb_mac_header(skb)[1]; /* From physical header */ - - ddp->deh_dnet = ap->s_net; /* Network number */ - ddp->deh_snet = ap->s_net; - ddp->deh_sum = 0; /* No checksum */ - /* - * Not sure about this bit... - */ - /* Non routable, so force a drop if we slip up later */ - ddp->deh_len_hops = htons(skb->len + (DDP_MAXHOPS << 10)); - } - skb_reset_transport_header(skb); - - return atalk_rcv(skb, dev, pt, orig_dev); -freeit: - kfree_skb(skb); - return 0; -} - -static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, - size_t len) -{ - struct sock *sk = sock->sk; - struct atalk_sock *at = at_sk(sk); - struct sockaddr_at *usat = (struct sockaddr_at *)msg->msg_name; - int flags = msg->msg_flags; - int loopback = 0; - struct sockaddr_at local_satalk, gsat; - struct sk_buff *skb; - struct net_device *dev; - struct ddpehdr *ddp; - int size; - struct atalk_route *rt; - int err; - - if (flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT)) - return -EINVAL; - - if (len > DDP_MAXSZ) - return -EMSGSIZE; - - lock_kernel(); - if (usat) { - err = -EBUSY; - if (sock_flag(sk, SOCK_ZAPPED)) - if (atalk_autobind(sk) < 0) - goto out; - - err = -EINVAL; - if (msg->msg_namelen < sizeof(*usat) || - usat->sat_family != AF_APPLETALK) - goto out; - - err = -EPERM; - /* netatalk didn't implement this check */ - if (usat->sat_addr.s_node == ATADDR_BCAST && - !sock_flag(sk, SOCK_BROADCAST)) { - goto out; - } - } else { - err = -ENOTCONN; - if (sk->sk_state != TCP_ESTABLISHED) - goto out; - usat = &local_satalk; - usat->sat_family = AF_APPLETALK; - usat->sat_port = at->dest_port; - usat->sat_addr.s_node = at->dest_node; - usat->sat_addr.s_net = at->dest_net; - } - - /* Build a packet */ - SOCK_DEBUG(sk, "SK %p: Got address.\n", sk); - - /* For headers */ - size = sizeof(struct ddpehdr) + len + ddp_dl->header_length; - - if (usat->sat_addr.s_net || usat->sat_addr.s_node == ATADDR_ANYNODE) { - rt = atrtr_find(&usat->sat_addr); - } else { - struct atalk_addr at_hint; - - at_hint.s_node = 0; - at_hint.s_net = at->src_net; - - rt = atrtr_find(&at_hint); - } - err = ENETUNREACH; - if (!rt) - goto out; - - dev = rt->dev; - - SOCK_DEBUG(sk, "SK %p: Size needed %d, device %s\n", - sk, size, dev->name); - - size += dev->hard_header_len; - skb = sock_alloc_send_skb(sk, size, (flags & MSG_DONTWAIT), &err); - if (!skb) - goto out; - - skb->sk = sk; - skb_reserve(skb, ddp_dl->header_length); - skb_reserve(skb, dev->hard_header_len); - skb->dev = dev; - - SOCK_DEBUG(sk, "SK %p: Begin build.\n", sk); - - ddp = (struct ddpehdr *)skb_put(skb, sizeof(struct ddpehdr)); - ddp->deh_len_hops = htons(len + sizeof(*ddp)); - ddp->deh_dnet = usat->sat_addr.s_net; - ddp->deh_snet = at->src_net; - ddp->deh_dnode = usat->sat_addr.s_node; - ddp->deh_snode = at->src_node; - ddp->deh_dport = usat->sat_port; - ddp->deh_sport = at->src_port; - - SOCK_DEBUG(sk, "SK %p: Copy user data (%Zd bytes).\n", sk, len); - - err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); - if (err) { - kfree_skb(skb); - err = -EFAULT; - goto out; - } - - if (sk->sk_no_check == 1) - ddp->deh_sum = 0; - else - ddp->deh_sum = atalk_checksum(skb, len + sizeof(*ddp)); - - /* - * Loopback broadcast packets to non gateway targets (ie routes - * to group we are in) - */ - if (ddp->deh_dnode == ATADDR_BCAST && - !(rt->flags & RTF_GATEWAY) && !(dev->flags & IFF_LOOPBACK)) { - struct sk_buff *skb2 = skb_copy(skb, GFP_KERNEL); - - if (skb2) { - loopback = 1; - SOCK_DEBUG(sk, "SK %p: send out(copy).\n", sk); - /* - * If it fails it is queued/sent above in the aarp queue - */ - aarp_send_ddp(dev, skb2, &usat->sat_addr, NULL); - } - } - - if (dev->flags & IFF_LOOPBACK || loopback) { - SOCK_DEBUG(sk, "SK %p: Loop back.\n", sk); - /* loop back */ - skb_orphan(skb); - if (ddp->deh_dnode == ATADDR_BCAST) { - struct atalk_addr at_lo; - - at_lo.s_node = 0; - at_lo.s_net = 0; - - rt = atrtr_find(&at_lo); - if (!rt) { - kfree_skb(skb); - err = -ENETUNREACH; - goto out; - } - dev = rt->dev; - skb->dev = dev; - } - ddp_dl->request(ddp_dl, skb, dev->dev_addr); - } else { - SOCK_DEBUG(sk, "SK %p: send out.\n", sk); - if (rt->flags & RTF_GATEWAY) { - gsat.sat_addr = rt->gateway; - usat = &gsat; - } - - /* - * If it fails it is queued/sent above in the aarp queue - */ - aarp_send_ddp(dev, skb, &usat->sat_addr, NULL); - } - SOCK_DEBUG(sk, "SK %p: Done write (%Zd).\n", sk, len); - -out: - unlock_kernel(); - return err ? : len; -} - -static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, - size_t size, int flags) -{ - struct sock *sk = sock->sk; - struct sockaddr_at *sat = (struct sockaddr_at *)msg->msg_name; - struct ddpehdr *ddp; - int copied = 0; - int offset = 0; - int err = 0; - struct sk_buff *skb; - - lock_kernel(); - skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, - flags & MSG_DONTWAIT, &err); - if (!skb) - goto out; - - /* FIXME: use skb->cb to be able to use shared skbs */ - ddp = ddp_hdr(skb); - copied = ntohs(ddp->deh_len_hops) & 1023; - - if (sk->sk_type != SOCK_RAW) { - offset = sizeof(*ddp); - copied -= offset; - } - - if (copied > size) { - copied = size; - msg->msg_flags |= MSG_TRUNC; - } - err = skb_copy_datagram_iovec(skb, offset, msg->msg_iov, copied); - - if (!err) { - if (sat) { - sat->sat_family = AF_APPLETALK; - sat->sat_port = ddp->deh_sport; - sat->sat_addr.s_node = ddp->deh_snode; - sat->sat_addr.s_net = ddp->deh_snet; - } - msg->msg_namelen = sizeof(*sat); - } - - skb_free_datagram(sk, skb); /* Free the datagram. */ - -out: - unlock_kernel(); - return err ? : copied; -} - - -/* - * AppleTalk ioctl calls. - */ -static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - int rc = -ENOIOCTLCMD; - struct sock *sk = sock->sk; - void __user *argp = (void __user *)arg; - - switch (cmd) { - /* Protocol layer */ - case TIOCOUTQ: { - long amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); - - if (amount < 0) - amount = 0; - rc = put_user(amount, (int __user *)argp); - break; - } - case TIOCINQ: { - /* - * These two are safe on a single CPU system as only - * user tasks fiddle here - */ - struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); - long amount = 0; - - if (skb) - amount = skb->len - sizeof(struct ddpehdr); - rc = put_user(amount, (int __user *)argp); - break; - } - case SIOCGSTAMP: - rc = sock_get_timestamp(sk, argp); - break; - case SIOCGSTAMPNS: - rc = sock_get_timestampns(sk, argp); - break; - /* Routing */ - case SIOCADDRT: - case SIOCDELRT: - rc = -EPERM; - if (capable(CAP_NET_ADMIN)) - rc = atrtr_ioctl(cmd, argp); - break; - /* Interface */ - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFBRDADDR: - case SIOCATALKDIFADDR: - case SIOCDIFADDR: - case SIOCSARP: /* proxy AARP */ - case SIOCDARP: /* proxy AARP */ - rtnl_lock(); - rc = atif_ioctl(cmd, argp); - rtnl_unlock(); - break; - } - - return rc; -} - - -#ifdef CONFIG_COMPAT -static int atalk_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - /* - * SIOCATALKDIFADDR is a SIOCPROTOPRIVATE ioctl number, so we - * cannot handle it in common code. The data we access if ifreq - * here is compatible, so we can simply call the native - * handler. - */ - if (cmd == SIOCATALKDIFADDR) - return atalk_ioctl(sock, cmd, (unsigned long)compat_ptr(arg)); - - return -ENOIOCTLCMD; -} -#endif - - -static const struct net_proto_family atalk_family_ops = { - .family = PF_APPLETALK, - .create = atalk_create, - .owner = THIS_MODULE, -}; - -static const struct proto_ops atalk_dgram_ops = { - .family = PF_APPLETALK, - .owner = THIS_MODULE, - .release = atalk_release, - .bind = atalk_bind, - .connect = atalk_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .getname = atalk_getname, - .poll = atalk_poll, - .ioctl = atalk_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = atalk_compat_ioctl, -#endif - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = sock_no_setsockopt, - .getsockopt = sock_no_getsockopt, - .sendmsg = atalk_sendmsg, - .recvmsg = atalk_recvmsg, - .mmap = sock_no_mmap, - .sendpage = sock_no_sendpage, -}; - -static struct notifier_block ddp_notifier = { - .notifier_call = ddp_device_event, -}; - -static struct packet_type ltalk_packet_type __read_mostly = { - .type = cpu_to_be16(ETH_P_LOCALTALK), - .func = ltalk_rcv, -}; - -static struct packet_type ppptalk_packet_type __read_mostly = { - .type = cpu_to_be16(ETH_P_PPPTALK), - .func = atalk_rcv, -}; - -static unsigned char ddp_snap_id[] = { 0x08, 0x00, 0x07, 0x80, 0x9B }; - -/* Export symbols for use by drivers when AppleTalk is a module */ -EXPORT_SYMBOL(atrtr_get_dev); -EXPORT_SYMBOL(atalk_find_dev_addr); - -static const char atalk_err_snap[] __initconst = - KERN_CRIT "Unable to register DDP with SNAP.\n"; - -/* Called by proto.c on kernel start up */ -static int __init atalk_init(void) -{ - int rc = proto_register(&ddp_proto, 0); - - if (rc != 0) - goto out; - - (void)sock_register(&atalk_family_ops); - ddp_dl = register_snap_client(ddp_snap_id, atalk_rcv); - if (!ddp_dl) - printk(atalk_err_snap); - - dev_add_pack(<alk_packet_type); - dev_add_pack(&ppptalk_packet_type); - - register_netdevice_notifier(&ddp_notifier); - aarp_proto_init(); - atalk_proc_init(); - atalk_register_sysctl(); -out: - return rc; -} -module_init(atalk_init); - -/* - * No explicit module reference count manipulation is needed in the - * protocol. Socket layer sets module reference count for us - * and interfaces reference counting is done - * by the network device layer. - * - * Ergo, before the AppleTalk module can be removed, all AppleTalk - * sockets be closed from user space. - */ -static void __exit atalk_exit(void) -{ -#ifdef CONFIG_SYSCTL - atalk_unregister_sysctl(); -#endif /* CONFIG_SYSCTL */ - atalk_proc_exit(); - aarp_cleanup_module(); /* General aarp clean-up. */ - unregister_netdevice_notifier(&ddp_notifier); - dev_remove_pack(<alk_packet_type); - dev_remove_pack(&ppptalk_packet_type); - unregister_snap_client(ddp_dl); - sock_unregister(PF_APPLETALK); - proto_unregister(&ddp_proto); -} -module_exit(atalk_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Alan Cox "); -MODULE_DESCRIPTION("AppleTalk 0.20\n"); -MODULE_ALIAS_NETPROTO(PF_APPLETALK); diff --git a/net/appletalk/dev.c b/net/appletalk/dev.c deleted file mode 100644 index 6c8016f61866..000000000000 --- a/net/appletalk/dev.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Moved here from drivers/net/net_init.c, which is: - * Written 1993,1994,1995 by Donald Becker. - */ - -#include -#include -#include -#include -#include - -static void ltalk_setup(struct net_device *dev) -{ - /* Fill in the fields of the device structure with localtalk-generic values. */ - - dev->type = ARPHRD_LOCALTLK; - dev->hard_header_len = LTALK_HLEN; - dev->mtu = LTALK_MTU; - dev->addr_len = LTALK_ALEN; - dev->tx_queue_len = 10; - - dev->broadcast[0] = 0xFF; - - dev->flags = IFF_BROADCAST|IFF_MULTICAST|IFF_NOARP; -} - -/** - * alloc_ltalkdev - Allocates and sets up an localtalk device - * @sizeof_priv: Size of additional driver-private structure to be allocated - * for this localtalk device - * - * Fill in the fields of the device structure with localtalk-generic - * values. Basically does everything except registering the device. - * - * Constructs a new net device, complete with a private data area of - * size @sizeof_priv. A 32-byte (not bit) alignment is enforced for - * this private data area. - */ - -struct net_device *alloc_ltalkdev(int sizeof_priv) -{ - return alloc_netdev(sizeof_priv, "lt%d", ltalk_setup); -} -EXPORT_SYMBOL(alloc_ltalkdev); diff --git a/net/appletalk/sysctl_net_atalk.c b/net/appletalk/sysctl_net_atalk.c deleted file mode 100644 index 04e9c0da7aa9..000000000000 --- a/net/appletalk/sysctl_net_atalk.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * sysctl_net_atalk.c: sysctl interface to net AppleTalk subsystem. - * - * Begun April 1, 1996, Mike Shaver. - * Added /proc/sys/net/atalk directory entry (empty =) ). [MS] - * Dynamic registration, added aarp entries. (5/30/97 Chris Horn) - */ - -#include -#include -#include - -static struct ctl_table atalk_table[] = { - { - .procname = "aarp-expiry-time", - .data = &sysctl_aarp_expiry_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - { - .procname = "aarp-tick-time", - .data = &sysctl_aarp_tick_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - { - .procname = "aarp-retransmit-limit", - .data = &sysctl_aarp_retransmit_limit, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, - { - .procname = "aarp-resolve-time", - .data = &sysctl_aarp_resolve_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - { }, -}; - -static struct ctl_path atalk_path[] = { - { .procname = "net", }, - { .procname = "appletalk", }, - { } -}; - -static struct ctl_table_header *atalk_table_header; - -void atalk_register_sysctl(void) -{ - atalk_table_header = register_sysctl_paths(atalk_path, atalk_table); -} - -void atalk_unregister_sysctl(void) -{ - unregister_sysctl_table(atalk_table_header); -} diff --git a/net/socket.c b/net/socket.c index ac2219f90d5d..26f7bcf36810 100644 --- a/net/socket.c +++ b/net/socket.c @@ -103,7 +103,6 @@ #include #include #include -#include static int sock_no_open(struct inode *irrelevant, struct file *dontcare); static ssize_t sock_aio_read(struct kiocb *iocb, const struct iovec *iov, -- cgit v1.2.3 From 0ffbf8bf21db0186e9fbb024a1796c3148790578 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 31 Jan 2011 14:03:00 -0800 Subject: Revert "appletalk: move to staging" This reverts commit a6238f21736af3f47bdebf3895f477f5f23f1af9 Appletalk got some patches to fix up the BLK usage in it in the network tree, so this removal isn't needed. Cc: Arnd Bergmann Cc: Cc: netdev@vger.kernel.org, Cc: David Miller Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 3 +- drivers/net/Makefile | 1 + drivers/net/appletalk/Kconfig | 126 ++ drivers/net/appletalk/Makefile | 7 + drivers/net/appletalk/cops.c | 1013 +++++++++++++ drivers/net/appletalk/cops.h | 60 + drivers/net/appletalk/cops_ffdrv.h | 532 +++++++ drivers/net/appletalk/cops_ltdrv.h | 241 ++++ drivers/net/appletalk/ipddp.c | 335 +++++ drivers/net/appletalk/ipddp.h | 27 + drivers/net/appletalk/ltpc.c | 1288 +++++++++++++++++ drivers/net/appletalk/ltpc.h | 73 + drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/appletalk/Kconfig | 126 -- drivers/staging/appletalk/Makefile | 12 - drivers/staging/appletalk/aarp.c | 1063 -------------- drivers/staging/appletalk/atalk.h | 208 --- drivers/staging/appletalk/atalk_proc.c | 301 ---- drivers/staging/appletalk/cops.c | 1013 ------------- drivers/staging/appletalk/cops.h | 60 - drivers/staging/appletalk/cops_ffdrv.h | 532 ------- drivers/staging/appletalk/cops_ltdrv.h | 241 ---- drivers/staging/appletalk/ddp.c | 1981 -------------------------- drivers/staging/appletalk/dev.c | 44 - drivers/staging/appletalk/ipddp.c | 335 ----- drivers/staging/appletalk/ipddp.h | 27 - drivers/staging/appletalk/ltpc.c | 1288 ----------------- drivers/staging/appletalk/ltpc.h | 73 - drivers/staging/appletalk/sysctl_net_atalk.c | 61 - fs/compat_ioctl.c | 1 + include/linux/Kbuild | 1 + include/linux/atalk.h | 208 +++ net/Kconfig | 1 + net/Makefile | 1 + net/appletalk/Makefile | 9 + net/appletalk/aarp.c | 1063 ++++++++++++++ net/appletalk/atalk_proc.c | 301 ++++ net/appletalk/ddp.c | 1981 ++++++++++++++++++++++++++ net/appletalk/dev.c | 44 + net/appletalk/sysctl_net_atalk.c | 61 + net/socket.c | 1 + 42 files changed, 7377 insertions(+), 7369 deletions(-) create mode 100644 drivers/net/appletalk/Kconfig create mode 100644 drivers/net/appletalk/Makefile create mode 100644 drivers/net/appletalk/cops.c create mode 100644 drivers/net/appletalk/cops.h create mode 100644 drivers/net/appletalk/cops_ffdrv.h create mode 100644 drivers/net/appletalk/cops_ltdrv.h create mode 100644 drivers/net/appletalk/ipddp.c create mode 100644 drivers/net/appletalk/ipddp.h create mode 100644 drivers/net/appletalk/ltpc.c create mode 100644 drivers/net/appletalk/ltpc.h delete mode 100644 drivers/staging/appletalk/Kconfig delete mode 100644 drivers/staging/appletalk/Makefile delete mode 100644 drivers/staging/appletalk/aarp.c delete mode 100644 drivers/staging/appletalk/atalk.h delete mode 100644 drivers/staging/appletalk/atalk_proc.c delete mode 100644 drivers/staging/appletalk/cops.c delete mode 100644 drivers/staging/appletalk/cops.h delete mode 100644 drivers/staging/appletalk/cops_ffdrv.h delete mode 100644 drivers/staging/appletalk/cops_ltdrv.h delete mode 100644 drivers/staging/appletalk/ddp.c delete mode 100644 drivers/staging/appletalk/dev.c delete mode 100644 drivers/staging/appletalk/ipddp.c delete mode 100644 drivers/staging/appletalk/ipddp.h delete mode 100644 drivers/staging/appletalk/ltpc.c delete mode 100644 drivers/staging/appletalk/ltpc.h delete mode 100644 drivers/staging/appletalk/sysctl_net_atalk.c create mode 100644 include/linux/atalk.h create mode 100644 net/appletalk/Makefile create mode 100644 net/appletalk/aarp.c create mode 100644 net/appletalk/atalk_proc.c create mode 100644 net/appletalk/ddp.c create mode 100644 net/appletalk/dev.c create mode 100644 net/appletalk/sysctl_net_atalk.c (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 3118d67d68fb..dd6ca456cde3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -554,7 +554,8 @@ F: drivers/hwmon/applesmc.c APPLETALK NETWORK LAYER M: Arnaldo Carvalho de Melo S: Maintained -F: drivers/staging/appletalk/ +F: drivers/net/appletalk/ +F: net/appletalk/ ARC FRAMEBUFFER DRIVER M: Jaya Kumar diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 11a9c053f0c8..b90738d13994 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -265,6 +265,7 @@ obj-$(CONFIG_MACB) += macb.o obj-$(CONFIG_S6GMAC) += s6gmac.o obj-$(CONFIG_ARM) += arm/ +obj-$(CONFIG_DEV_APPLETALK) += appletalk/ obj-$(CONFIG_TR) += tokenring/ obj-$(CONFIG_WAN) += wan/ obj-$(CONFIG_ARCNET) += arcnet/ diff --git a/drivers/net/appletalk/Kconfig b/drivers/net/appletalk/Kconfig new file mode 100644 index 000000000000..0b376a990972 --- /dev/null +++ b/drivers/net/appletalk/Kconfig @@ -0,0 +1,126 @@ +# +# Appletalk driver configuration +# +config ATALK + tristate "Appletalk protocol support" + depends on BKL # waiting to be removed from net/appletalk/ddp.c + select LLC + ---help--- + AppleTalk is the protocol that Apple computers can use to communicate + on a network. If your Linux box is connected to such a network and you + wish to connect to it, say Y. You will need to use the netatalk package + so that your Linux box can act as a print and file server for Macs as + well as access AppleTalk printers. Check out + on the WWW for details. + EtherTalk is the name used for AppleTalk over Ethernet and the + cheaper and slower LocalTalk is AppleTalk over a proprietary Apple + network using serial links. EtherTalk and LocalTalk are fully + supported by Linux. + + General information about how to connect Linux, Windows machines and + Macs is on the WWW at . The + NET3-4-HOWTO, available from + , contains valuable + information as well. + + To compile this driver as a module, choose M here: the module will be + called appletalk. You almost certainly want to compile it as a + module so you can restart your AppleTalk stack without rebooting + your machine. I hear that the GNU boycott of Apple is over, so + even politically correct people are allowed to say Y here. + +config DEV_APPLETALK + tristate "Appletalk interfaces support" + depends on ATALK + help + AppleTalk is the protocol that Apple computers can use to communicate + on a network. If your Linux box is connected to such a network, and wish + to do IP over it, or you have a LocalTalk card and wish to use it to + connect to the AppleTalk network, say Y. + + +config LTPC + tristate "Apple/Farallon LocalTalk PC support" + depends on DEV_APPLETALK && (ISA || EISA) && ISA_DMA_API + help + This allows you to use the AppleTalk PC card to connect to LocalTalk + networks. The card is also known as the Farallon PhoneNet PC card. + If you are in doubt, this card is the one with the 65C02 chip on it. + You also need version 1.3.3 or later of the netatalk package. + This driver is experimental, which means that it may not work. + See the file . + +config COPS + tristate "COPS LocalTalk PC support" + depends on DEV_APPLETALK && (ISA || EISA) + help + This allows you to use COPS AppleTalk cards to connect to LocalTalk + networks. You also need version 1.3.3 or later of the netatalk + package. This driver is experimental, which means that it may not + work. This driver will only work if you choose "AppleTalk DDP" + networking support, above. + Please read the file . + +config COPS_DAYNA + bool "Dayna firmware support" + depends on COPS + help + Support COPS compatible cards with Dayna style firmware (Dayna + DL2000/ Daynatalk/PC (half length), COPS LT-95, Farallon PhoneNET PC + III, Farallon PhoneNET PC II). + +config COPS_TANGENT + bool "Tangent firmware support" + depends on COPS + help + Support COPS compatible cards with Tangent style firmware (Tangent + ATB_II, Novell NL-1000, Daystar Digital LT-200. + +config IPDDP + tristate "Appletalk-IP driver support" + depends on DEV_APPLETALK && ATALK + ---help--- + This allows IP networking for users who only have AppleTalk + networking available. This feature is experimental. With this + driver, you can encapsulate IP inside AppleTalk (e.g. if your Linux + box is stuck on an AppleTalk only network) or decapsulate (e.g. if + you want your Linux box to act as an Internet gateway for a zoo of + AppleTalk connected Macs). Please see the file + for more information. + + If you say Y here, the AppleTalk-IP support will be compiled into + the kernel. In this case, you can either use encapsulation or + decapsulation, but not both. With the following two questions, you + decide which one you want. + + To compile the AppleTalk-IP support as a module, choose M here: the + module will be called ipddp. + In this case, you will be able to use both encapsulation and + decapsulation simultaneously, by loading two copies of the module + and specifying different values for the module option ipddp_mode. + +config IPDDP_ENCAP + bool "IP to Appletalk-IP Encapsulation support" + depends on IPDDP + help + If you say Y here, the AppleTalk-IP code will be able to encapsulate + IP packets inside AppleTalk frames; this is useful if your Linux box + is stuck on an AppleTalk network (which hopefully contains a + decapsulator somewhere). Please see + for more information. If + you said Y to "AppleTalk-IP driver support" above and you say Y + here, then you cannot say Y to "AppleTalk-IP to IP Decapsulation + support", below. + +config IPDDP_DECAP + bool "Appletalk-IP to IP Decapsulation support" + depends on IPDDP + help + If you say Y here, the AppleTalk-IP code will be able to decapsulate + AppleTalk-IP frames to IP packets; this is useful if you want your + Linux box to act as an Internet gateway for an AppleTalk network. + Please see for more + information. If you said Y to "AppleTalk-IP driver support" above + and you say Y here, then you cannot say Y to "IP to AppleTalk-IP + Encapsulation support", above. + diff --git a/drivers/net/appletalk/Makefile b/drivers/net/appletalk/Makefile new file mode 100644 index 000000000000..6cfc705f7c5c --- /dev/null +++ b/drivers/net/appletalk/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for drivers/net/appletalk +# + +obj-$(CONFIG_IPDDP) += ipddp.o +obj-$(CONFIG_COPS) += cops.o +obj-$(CONFIG_LTPC) += ltpc.o diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c new file mode 100644 index 000000000000..748c9f526e71 --- /dev/null +++ b/drivers/net/appletalk/cops.c @@ -0,0 +1,1013 @@ +/* cops.c: LocalTalk driver for Linux. + * + * Authors: + * - Jay Schulist + * + * With more than a little help from; + * - Alan Cox + * + * Derived from: + * - skeleton.c: A network driver outline for linux. + * Written 1993-94 by Donald Becker. + * - ltpc.c: A driver for the LocalTalk PC card. + * Written by Bradford W. Johnson. + * + * Copyright 1993 United States Government as represented by the + * Director, National Security Agency. + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * Changes: + * 19970608 Alan Cox Allowed dual card type support + * Can set board type in insmod + * Hooks for cops_setup routine + * (not yet implemented). + * 19971101 Jay Schulist Fixes for multiple lt* devices. + * 19980607 Steven Hirsch Fixed the badly broken support + * for Tangent type cards. Only + * tested on Daystar LT200. Some + * cleanup of formatting and program + * logic. Added emacs 'local-vars' + * setup for Jay's brace style. + * 20000211 Alan Cox Cleaned up for softnet + */ + +static const char *version = +"cops.c:v0.04 6/7/98 Jay Schulist \n"; +/* + * Sources: + * COPS Localtalk SDK. This provides almost all of the information + * needed. + */ + +/* + * insmod/modprobe configurable stuff. + * - IO Port, choose one your card supports or 0 if you dare. + * - IRQ, also choose one your card supports or nothing and let + * the driver figure it out. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* For udelay() */ +#include +#include +#include +#include + +#include +#include +#include + +#include "cops.h" /* Our Stuff */ +#include "cops_ltdrv.h" /* Firmware code for Tangent type cards. */ +#include "cops_ffdrv.h" /* Firmware code for Dayna type cards. */ + +/* + * The name of the card. Is used for messages and in the requests for + * io regions, irqs and dma channels + */ + +static const char *cardname = "cops"; + +#ifdef CONFIG_COPS_DAYNA +static int board_type = DAYNA; /* Module exported */ +#else +static int board_type = TANGENT; +#endif + +static int io = 0x240; /* Default IO for Dayna */ +static int irq = 5; /* Default IRQ */ + +/* + * COPS Autoprobe information. + * Right now if port address is right but IRQ is not 5 this will + * return a 5 no matter what since we will still get a status response. + * Need one more additional check to narrow down after we have gotten + * the ioaddr. But since only other possible IRQs is 3 and 4 so no real + * hurry on this. I *STRONGLY* recommend using IRQ 5 for your card with + * this driver. + * + * This driver has 2 modes and they are: Dayna mode and Tangent mode. + * Each mode corresponds with the type of card. It has been found + * that there are 2 main types of cards and all other cards are + * the same and just have different names or only have minor differences + * such as more IO ports. As this driver is tested it will + * become more clear on exactly what cards are supported. The driver + * defaults to using Dayna mode. To change the drivers mode, simply + * select Dayna or Tangent mode when configuring the kernel. + * + * This driver should support: + * TANGENT driver mode: + * Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200, + * COPS LT-1 + * DAYNA driver mode: + * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, + * Farallon PhoneNET PC III, Farallon PhoneNET PC II + * Other cards possibly supported mode unknown though: + * Dayna DL2000 (Full length), COPS LT/M (Micro-Channel) + * + * Cards NOT supported by this driver but supported by the ltpc.c + * driver written by Bradford W. Johnson + * Farallon PhoneNET PC + * Original Apple LocalTalk PC card + * + * N.B. + * + * The Daystar Digital LT200 boards do not support interrupt-driven + * IO. You must specify 'irq=0xff' as a module parameter to invoke + * polled mode. I also believe that the port probing logic is quite + * dangerous at best and certainly hopeless for a polled card. Best to + * specify both. - Steve H. + * + */ + +/* + * Zero terminated list of IO ports to probe. + */ + +static unsigned int ports[] = { + 0x240, 0x340, 0x200, 0x210, 0x220, 0x230, 0x260, + 0x2A0, 0x300, 0x310, 0x320, 0x330, 0x350, 0x360, + 0 +}; + +/* + * Zero terminated list of IRQ ports to probe. + */ + +static int cops_irqlist[] = { + 5, 4, 3, 0 +}; + +static struct timer_list cops_timer; + +/* use 0 for production, 1 for verification, 2 for debug, 3 for verbose debug */ +#ifndef COPS_DEBUG +#define COPS_DEBUG 1 +#endif +static unsigned int cops_debug = COPS_DEBUG; + +/* The number of low I/O ports used by the card. */ +#define COPS_IO_EXTENT 8 + +/* Information that needs to be kept for each board. */ + +struct cops_local +{ + int board; /* Holds what board type is. */ + int nodeid; /* Set to 1 once have nodeid. */ + unsigned char node_acquire; /* Node ID when acquired. */ + struct atalk_addr node_addr; /* Full node address */ + spinlock_t lock; /* RX/TX lock */ +}; + +/* Index to functions, as function prototypes. */ +static int cops_probe1 (struct net_device *dev, int ioaddr); +static int cops_irq (int ioaddr, int board); + +static int cops_open (struct net_device *dev); +static int cops_jumpstart (struct net_device *dev); +static void cops_reset (struct net_device *dev, int sleep); +static void cops_load (struct net_device *dev); +static int cops_nodeid (struct net_device *dev, int nodeid); + +static irqreturn_t cops_interrupt (int irq, void *dev_id); +static void cops_poll (unsigned long ltdev); +static void cops_timeout(struct net_device *dev); +static void cops_rx (struct net_device *dev); +static netdev_tx_t cops_send_packet (struct sk_buff *skb, + struct net_device *dev); +static void set_multicast_list (struct net_device *dev); +static int cops_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); +static int cops_close (struct net_device *dev); + +static void cleanup_card(struct net_device *dev) +{ + if (dev->irq) + free_irq(dev->irq, dev); + release_region(dev->base_addr, COPS_IO_EXTENT); +} + +/* + * Check for a network adaptor of this type, and return '0' iff one exists. + * If dev->base_addr == 0, probe all likely locations. + * If dev->base_addr in [1..0x1ff], always return failure. + * otherwise go with what we pass in. + */ +struct net_device * __init cops_probe(int unit) +{ + struct net_device *dev; + unsigned *port; + int base_addr; + int err = 0; + + dev = alloc_ltalkdev(sizeof(struct cops_local)); + if (!dev) + return ERR_PTR(-ENOMEM); + + if (unit >= 0) { + sprintf(dev->name, "lt%d", unit); + netdev_boot_setup_check(dev); + irq = dev->irq; + base_addr = dev->base_addr; + } else { + base_addr = dev->base_addr = io; + } + + if (base_addr > 0x1ff) { /* Check a single specified location. */ + err = cops_probe1(dev, base_addr); + } else if (base_addr != 0) { /* Don't probe at all. */ + err = -ENXIO; + } else { + /* FIXME Does this really work for cards which generate irq? + * It's definitely N.G. for polled Tangent. sh + * Dayna cards don't autoprobe well at all, but if your card is + * at IRQ 5 & IO 0x240 we find it every time. ;) JS + */ + for (port = ports; *port && cops_probe1(dev, *port) < 0; port++) + ; + if (!*port) + err = -ENODEV; + } + if (err) + goto out; + err = register_netdev(dev); + if (err) + goto out1; + return dev; +out1: + cleanup_card(dev); +out: + free_netdev(dev); + return ERR_PTR(err); +} + +static const struct net_device_ops cops_netdev_ops = { + .ndo_open = cops_open, + .ndo_stop = cops_close, + .ndo_start_xmit = cops_send_packet, + .ndo_tx_timeout = cops_timeout, + .ndo_do_ioctl = cops_ioctl, + .ndo_set_multicast_list = set_multicast_list, +}; + +/* + * This is the real probe routine. Linux has a history of friendly device + * probes on the ISA bus. A good device probes avoids doing writes, and + * verifies that the correct device exists and functions. + */ +static int __init cops_probe1(struct net_device *dev, int ioaddr) +{ + struct cops_local *lp; + static unsigned version_printed; + int board = board_type; + int retval; + + if(cops_debug && version_printed++ == 0) + printk("%s", version); + + /* Grab the region so no one else tries to probe our ioports. */ + if (!request_region(ioaddr, COPS_IO_EXTENT, dev->name)) + return -EBUSY; + + /* + * Since this board has jumpered interrupts, allocate the interrupt + * vector now. There is no point in waiting since no other device + * can use the interrupt, and this marks the irq as busy. Jumpered + * interrupts are typically not reported by the boards, and we must + * used AutoIRQ to find them. + */ + dev->irq = irq; + switch (dev->irq) + { + case 0: + /* COPS AutoIRQ routine */ + dev->irq = cops_irq(ioaddr, board); + if (dev->irq) + break; + /* No IRQ found on this port, fallthrough */ + case 1: + retval = -EINVAL; + goto err_out; + + /* Fixup for users that don't know that IRQ 2 is really + * IRQ 9, or don't know which one to set. + */ + case 2: + dev->irq = 9; + break; + + /* Polled operation requested. Although irq of zero passed as + * a parameter tells the init routines to probe, we'll + * overload it to denote polled operation at runtime. + */ + case 0xff: + dev->irq = 0; + break; + + default: + break; + } + + /* Reserve any actual interrupt. */ + if (dev->irq) { + retval = request_irq(dev->irq, cops_interrupt, 0, dev->name, dev); + if (retval) + goto err_out; + } + + dev->base_addr = ioaddr; + + lp = netdev_priv(dev); + spin_lock_init(&lp->lock); + + /* Copy local board variable to lp struct. */ + lp->board = board; + + dev->netdev_ops = &cops_netdev_ops; + dev->watchdog_timeo = HZ * 2; + + + /* Tell the user where the card is and what mode we're in. */ + if(board==DAYNA) + printk("%s: %s at %#3x, using IRQ %d, in Dayna mode.\n", + dev->name, cardname, ioaddr, dev->irq); + if(board==TANGENT) { + if(dev->irq) + printk("%s: %s at %#3x, IRQ %d, in Tangent mode\n", + dev->name, cardname, ioaddr, dev->irq); + else + printk("%s: %s at %#3x, using polled IO, in Tangent mode.\n", + dev->name, cardname, ioaddr); + + } + return 0; + +err_out: + release_region(ioaddr, COPS_IO_EXTENT); + return retval; +} + +static int __init cops_irq (int ioaddr, int board) +{ /* + * This does not use the IRQ to determine where the IRQ is. We just + * assume that when we get a correct status response that it's the IRQ. + * This really just verifies the IO port but since we only have access + * to such a small number of IRQs (5, 4, 3) this is not bad. + * This will probably not work for more than one card. + */ + int irqaddr=0; + int i, x, status; + + if(board==DAYNA) + { + outb(0, ioaddr+DAYNA_RESET); + inb(ioaddr+DAYNA_RESET); + mdelay(333); + } + if(board==TANGENT) + { + inb(ioaddr); + outb(0, ioaddr); + outb(0, ioaddr+TANG_RESET); + } + + for(i=0; cops_irqlist[i] !=0; i++) + { + irqaddr = cops_irqlist[i]; + for(x = 0xFFFF; x>0; x --) /* wait for response */ + { + if(board==DAYNA) + { + status = (inb(ioaddr+DAYNA_CARD_STATUS)&3); + if(status == 1) + return irqaddr; + } + if(board==TANGENT) + { + if((inb(ioaddr+TANG_CARD_STATUS)& TANG_TX_READY) !=0) + return irqaddr; + } + } + } + return 0; /* no IRQ found */ +} + +/* + * Open/initialize the board. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + */ +static int cops_open(struct net_device *dev) +{ + struct cops_local *lp = netdev_priv(dev); + + if(dev->irq==0) + { + /* + * I don't know if the Dayna-style boards support polled + * operation. For now, only allow it for Tangent. + */ + if(lp->board==TANGENT) /* Poll 20 times per second */ + { + init_timer(&cops_timer); + cops_timer.function = cops_poll; + cops_timer.data = (unsigned long)dev; + cops_timer.expires = jiffies + HZ/20; + add_timer(&cops_timer); + } + else + { + printk(KERN_WARNING "%s: No irq line set\n", dev->name); + return -EAGAIN; + } + } + + cops_jumpstart(dev); /* Start the card up. */ + + netif_start_queue(dev); + return 0; +} + +/* + * This allows for a dynamic start/restart of the entire card. + */ +static int cops_jumpstart(struct net_device *dev) +{ + struct cops_local *lp = netdev_priv(dev); + + /* + * Once the card has the firmware loaded and has acquired + * the nodeid, if it is reset it will lose it all. + */ + cops_reset(dev,1); /* Need to reset card before load firmware. */ + cops_load(dev); /* Load the firmware. */ + + /* + * If atalkd already gave us a nodeid we will use that + * one again, else we wait for atalkd to give us a nodeid + * in cops_ioctl. This may cause a problem if someone steals + * our nodeid while we are resetting. + */ + if(lp->nodeid == 1) + cops_nodeid(dev,lp->node_acquire); + + return 0; +} + +static void tangent_wait_reset(int ioaddr) +{ + int timeout=0; + + while(timeout++ < 5 && (inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) + mdelay(1); /* Wait 1 second */ +} + +/* + * Reset the LocalTalk board. + */ +static void cops_reset(struct net_device *dev, int sleep) +{ + struct cops_local *lp = netdev_priv(dev); + int ioaddr=dev->base_addr; + + if(lp->board==TANGENT) + { + inb(ioaddr); /* Clear request latch. */ + outb(0,ioaddr); /* Clear the TANG_TX_READY flop. */ + outb(0, ioaddr+TANG_RESET); /* Reset the adapter. */ + + tangent_wait_reset(ioaddr); + outb(0, ioaddr+TANG_CLEAR_INT); + } + if(lp->board==DAYNA) + { + outb(0, ioaddr+DAYNA_RESET); /* Assert the reset port */ + inb(ioaddr+DAYNA_RESET); /* Clear the reset */ + if (sleep) + msleep(333); + else + mdelay(333); + } + + netif_wake_queue(dev); +} + +static void cops_load (struct net_device *dev) +{ + struct ifreq ifr; + struct ltfirmware *ltf= (struct ltfirmware *)&ifr.ifr_ifru; + struct cops_local *lp = netdev_priv(dev); + int ioaddr=dev->base_addr; + int length, i = 0; + + strcpy(ifr.ifr_name,"lt0"); + + /* Get card's firmware code and do some checks on it. */ +#ifdef CONFIG_COPS_DAYNA + if(lp->board==DAYNA) + { + ltf->length=sizeof(ffdrv_code); + ltf->data=ffdrv_code; + } + else +#endif +#ifdef CONFIG_COPS_TANGENT + if(lp->board==TANGENT) + { + ltf->length=sizeof(ltdrv_code); + ltf->data=ltdrv_code; + } + else +#endif + { + printk(KERN_INFO "%s; unsupported board type.\n", dev->name); + return; + } + + /* Check to make sure firmware is correct length. */ + if(lp->board==DAYNA && ltf->length!=5983) + { + printk(KERN_WARNING "%s: Firmware is not length of FFDRV.BIN.\n", dev->name); + return; + } + if(lp->board==TANGENT && ltf->length!=2501) + { + printk(KERN_WARNING "%s: Firmware is not length of DRVCODE.BIN.\n", dev->name); + return; + } + + if(lp->board==DAYNA) + { + /* + * We must wait for a status response + * with the DAYNA board. + */ + while(++i<65536) + { + if((inb(ioaddr+DAYNA_CARD_STATUS)&3)==1) + break; + } + + if(i==65536) + return; + } + + /* + * Upload the firmware and kick. Byte-by-byte works nicely here. + */ + i=0; + length = ltf->length; + while(length--) + { + outb(ltf->data[i], ioaddr); + i++; + } + + if(cops_debug > 1) + printk("%s: Uploaded firmware - %d bytes of %d bytes.\n", + dev->name, i, ltf->length); + + if(lp->board==DAYNA) /* Tell Dayna to run the firmware code. */ + outb(1, ioaddr+DAYNA_INT_CARD); + else /* Tell Tang to run the firmware code. */ + inb(ioaddr); + + if(lp->board==TANGENT) + { + tangent_wait_reset(ioaddr); + inb(ioaddr); /* Clear initial ready signal. */ + } +} + +/* + * Get the LocalTalk Nodeid from the card. We can suggest + * any nodeid 1-254. The card will try and get that exact + * address else we can specify 0 as the nodeid and the card + * will autoprobe for a nodeid. + */ +static int cops_nodeid (struct net_device *dev, int nodeid) +{ + struct cops_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + + if(lp->board == DAYNA) + { + /* Empty any pending adapter responses. */ + while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0) + { + outb(0, ioaddr+COPS_CLEAR_INT); /* Clear interrupts. */ + if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST) + cops_rx(dev); /* Kick any packets waiting. */ + schedule(); + } + + outb(2, ioaddr); /* Output command packet length as 2. */ + outb(0, ioaddr); + outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */ + outb(nodeid, ioaddr); /* Suggest node address. */ + } + + if(lp->board == TANGENT) + { + /* Empty any pending adapter responses. */ + while(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY) + { + outb(0, ioaddr+COPS_CLEAR_INT); /* Clear interrupt. */ + cops_rx(dev); /* Kick out packets waiting. */ + schedule(); + } + + /* Not sure what Tangent does if nodeid picked is used. */ + if(nodeid == 0) /* Seed. */ + nodeid = jiffies&0xFF; /* Get a random try */ + outb(2, ioaddr); /* Command length LSB */ + outb(0, ioaddr); /* Command length MSB */ + outb(LAP_INIT, ioaddr); /* Send LAP_INIT byte */ + outb(nodeid, ioaddr); /* LAP address hint. */ + outb(0xFF, ioaddr); /* Int. level to use */ + } + + lp->node_acquire=0; /* Set nodeid holder to 0. */ + while(lp->node_acquire==0) /* Get *True* nodeid finally. */ + { + outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */ + + if(lp->board == DAYNA) + { + if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST) + cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */ + } + if(lp->board == TANGENT) + { + if(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY) + cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */ + } + schedule(); + } + + if(cops_debug > 1) + printk(KERN_DEBUG "%s: Node ID %d has been acquired.\n", + dev->name, lp->node_acquire); + + lp->nodeid=1; /* Set got nodeid to 1. */ + + return 0; +} + +/* + * Poll the Tangent type cards to see if we have work. + */ + +static void cops_poll(unsigned long ltdev) +{ + int ioaddr, status; + int boguscount = 0; + + struct net_device *dev = (struct net_device *)ltdev; + + del_timer(&cops_timer); + + if(dev == NULL) + return; /* We've been downed */ + + ioaddr = dev->base_addr; + do { + status=inb(ioaddr+TANG_CARD_STATUS); + if(status & TANG_RX_READY) + cops_rx(dev); + if(status & TANG_TX_READY) + netif_wake_queue(dev); + status = inb(ioaddr+TANG_CARD_STATUS); + } while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY))); + + /* poll 20 times per second */ + cops_timer.expires = jiffies + HZ/20; + add_timer(&cops_timer); +} + +/* + * The typical workload of the driver: + * Handle the network interface interrupts. + */ +static irqreturn_t cops_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct cops_local *lp; + int ioaddr, status; + int boguscount = 0; + + ioaddr = dev->base_addr; + lp = netdev_priv(dev); + + if(lp->board==DAYNA) + { + do { + outb(0, ioaddr + COPS_CLEAR_INT); + status=inb(ioaddr+DAYNA_CARD_STATUS); + if((status&0x03)==DAYNA_RX_REQUEST) + cops_rx(dev); + netif_wake_queue(dev); + } while(++boguscount < 20); + } + else + { + do { + status=inb(ioaddr+TANG_CARD_STATUS); + if(status & TANG_RX_READY) + cops_rx(dev); + if(status & TANG_TX_READY) + netif_wake_queue(dev); + status=inb(ioaddr+TANG_CARD_STATUS); + } while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY))); + } + + return IRQ_HANDLED; +} + +/* + * We have a good packet(s), get it/them out of the buffers. + */ +static void cops_rx(struct net_device *dev) +{ + int pkt_len = 0; + int rsp_type = 0; + struct sk_buff *skb = NULL; + struct cops_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + int boguscount = 0; + unsigned long flags; + + + spin_lock_irqsave(&lp->lock, flags); + + if(lp->board==DAYNA) + { + outb(0, ioaddr); /* Send out Zero length. */ + outb(0, ioaddr); + outb(DATA_READ, ioaddr); /* Send read command out. */ + + /* Wait for DMA to turn around. */ + while(++boguscount<1000000) + { + barrier(); + if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_READY) + break; + } + + if(boguscount==1000000) + { + printk(KERN_WARNING "%s: DMA timed out.\n",dev->name); + spin_unlock_irqrestore(&lp->lock, flags); + return; + } + } + + /* Get response length. */ + if(lp->board==DAYNA) + pkt_len = inb(ioaddr) & 0xFF; + else + pkt_len = inb(ioaddr) & 0x00FF; + pkt_len |= (inb(ioaddr) << 8); + /* Input IO code. */ + rsp_type=inb(ioaddr); + + /* Malloc up new buffer. */ + skb = dev_alloc_skb(pkt_len); + if(skb == NULL) + { + printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", + dev->name); + dev->stats.rx_dropped++; + while(pkt_len--) /* Discard packet */ + inb(ioaddr); + spin_unlock_irqrestore(&lp->lock, flags); + return; + } + skb->dev = dev; + skb_put(skb, pkt_len); + skb->protocol = htons(ETH_P_LOCALTALK); + + insb(ioaddr, skb->data, pkt_len); /* Eat the Data */ + + if(lp->board==DAYNA) + outb(1, ioaddr+DAYNA_INT_CARD); /* Interrupt the card */ + + spin_unlock_irqrestore(&lp->lock, flags); /* Restore interrupts. */ + + /* Check for bad response length */ + if(pkt_len < 0 || pkt_len > MAX_LLAP_SIZE) + { + printk(KERN_WARNING "%s: Bad packet length of %d bytes.\n", + dev->name, pkt_len); + dev->stats.tx_errors++; + dev_kfree_skb_any(skb); + return; + } + + /* Set nodeid and then get out. */ + if(rsp_type == LAP_INIT_RSP) + { /* Nodeid taken from received packet. */ + lp->node_acquire = skb->data[0]; + dev_kfree_skb_any(skb); + return; + } + + /* One last check to make sure we have a good packet. */ + if(rsp_type != LAP_RESPONSE) + { + printk(KERN_WARNING "%s: Bad packet type %d.\n", dev->name, rsp_type); + dev->stats.tx_errors++; + dev_kfree_skb_any(skb); + return; + } + + skb_reset_mac_header(skb); /* Point to entire packet. */ + skb_pull(skb,3); + skb_reset_transport_header(skb); /* Point to data (Skip header). */ + + /* Update the counters. */ + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + + /* Send packet to a higher place. */ + netif_rx(skb); +} + +static void cops_timeout(struct net_device *dev) +{ + struct cops_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + + dev->stats.tx_errors++; + if(lp->board==TANGENT) + { + if((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) + printk(KERN_WARNING "%s: No TX complete interrupt.\n", dev->name); + } + printk(KERN_WARNING "%s: Transmit timed out.\n", dev->name); + cops_jumpstart(dev); /* Restart the card. */ + dev->trans_start = jiffies; /* prevent tx timeout */ + netif_wake_queue(dev); +} + + +/* + * Make the card transmit a LocalTalk packet. + */ + +static netdev_tx_t cops_send_packet(struct sk_buff *skb, + struct net_device *dev) +{ + struct cops_local *lp = netdev_priv(dev); + int ioaddr = dev->base_addr; + unsigned long flags; + + /* + * Block a timer-based transmit from overlapping. + */ + + netif_stop_queue(dev); + + spin_lock_irqsave(&lp->lock, flags); + if(lp->board == DAYNA) /* Wait for adapter transmit buffer. */ + while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0) + cpu_relax(); + if(lp->board == TANGENT) /* Wait for adapter transmit buffer. */ + while((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) + cpu_relax(); + + /* Output IO length. */ + outb(skb->len, ioaddr); + if(lp->board == DAYNA) + outb(skb->len >> 8, ioaddr); + else + outb((skb->len >> 8)&0x0FF, ioaddr); + + /* Output IO code. */ + outb(LAP_WRITE, ioaddr); + + if(lp->board == DAYNA) /* Check the transmit buffer again. */ + while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0); + + outsb(ioaddr, skb->data, skb->len); /* Send out the data. */ + + if(lp->board==DAYNA) /* Dayna requires you kick the card */ + outb(1, ioaddr+DAYNA_INT_CARD); + + spin_unlock_irqrestore(&lp->lock, flags); /* Restore interrupts. */ + + /* Done sending packet, update counters and cleanup. */ + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + dev_kfree_skb (skb); + return NETDEV_TX_OK; +} + +/* + * Dummy function to keep the Appletalk layer happy. + */ + +static void set_multicast_list(struct net_device *dev) +{ + if(cops_debug >= 3) + printk("%s: set_multicast_list executed\n", dev->name); +} + +/* + * System ioctls for the COPS LocalTalk card. + */ + +static int cops_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct cops_local *lp = netdev_priv(dev); + struct sockaddr_at *sa = (struct sockaddr_at *)&ifr->ifr_addr; + struct atalk_addr *aa = (struct atalk_addr *)&lp->node_addr; + + switch(cmd) + { + case SIOCSIFADDR: + /* Get and set the nodeid and network # atalkd wants. */ + cops_nodeid(dev, sa->sat_addr.s_node); + aa->s_net = sa->sat_addr.s_net; + aa->s_node = lp->node_acquire; + + /* Set broardcast address. */ + dev->broadcast[0] = 0xFF; + + /* Set hardware address. */ + dev->dev_addr[0] = aa->s_node; + dev->addr_len = 1; + return 0; + + case SIOCGIFADDR: + sa->sat_addr.s_net = aa->s_net; + sa->sat_addr.s_node = aa->s_node; + return 0; + + default: + return -EOPNOTSUPP; + } +} + +/* + * The inverse routine to cops_open(). + */ + +static int cops_close(struct net_device *dev) +{ + struct cops_local *lp = netdev_priv(dev); + + /* If we were running polled, yank the timer. + */ + if(lp->board==TANGENT && dev->irq==0) + del_timer(&cops_timer); + + netif_stop_queue(dev); + return 0; +} + + +#ifdef MODULE +static struct net_device *cops_dev; + +MODULE_LICENSE("GPL"); +module_param(io, int, 0); +module_param(irq, int, 0); +module_param(board_type, int, 0); + +static int __init cops_module_init(void) +{ + if (io == 0) + printk(KERN_WARNING "%s: You shouldn't autoprobe with insmod\n", + cardname); + cops_dev = cops_probe(-1); + if (IS_ERR(cops_dev)) + return PTR_ERR(cops_dev); + return 0; +} + +static void __exit cops_module_exit(void) +{ + unregister_netdev(cops_dev); + cleanup_card(cops_dev); + free_netdev(cops_dev); +} +module_init(cops_module_init); +module_exit(cops_module_exit); +#endif /* MODULE */ diff --git a/drivers/net/appletalk/cops.h b/drivers/net/appletalk/cops.h new file mode 100644 index 000000000000..fd2750b269c8 --- /dev/null +++ b/drivers/net/appletalk/cops.h @@ -0,0 +1,60 @@ +/* cops.h: LocalTalk driver for Linux. + * + * Authors: + * - Jay Schulist + */ + +#ifndef __LINUX_COPSLTALK_H +#define __LINUX_COPSLTALK_H + +#ifdef __KERNEL__ + +/* Max LLAP size we will accept. */ +#define MAX_LLAP_SIZE 603 + +/* Tangent */ +#define TANG_CARD_STATUS 1 +#define TANG_CLEAR_INT 1 +#define TANG_RESET 3 + +#define TANG_TX_READY 1 +#define TANG_RX_READY 2 + +/* Dayna */ +#define DAYNA_CMD_DATA 0 +#define DAYNA_CLEAR_INT 1 +#define DAYNA_CARD_STATUS 2 +#define DAYNA_INT_CARD 3 +#define DAYNA_RESET 4 + +#define DAYNA_RX_READY 0 +#define DAYNA_TX_READY 1 +#define DAYNA_RX_REQUEST 3 + +/* Same on both card types */ +#define COPS_CLEAR_INT 1 + +/* LAP response codes received from the cards. */ +#define LAP_INIT 1 /* Init cmd */ +#define LAP_INIT_RSP 2 /* Init response */ +#define LAP_WRITE 3 /* Write cmd */ +#define DATA_READ 4 /* Data read */ +#define LAP_RESPONSE 4 /* Received ALAP frame response */ +#define LAP_GETSTAT 5 /* Get LAP and HW status */ +#define LAP_RSPSTAT 6 /* Status response */ + +#endif + +/* + * Structure to hold the firmware information. + */ +struct ltfirmware +{ + unsigned int length; + const unsigned char *data; +}; + +#define DAYNA 1 +#define TANGENT 2 + +#endif diff --git a/drivers/net/appletalk/cops_ffdrv.h b/drivers/net/appletalk/cops_ffdrv.h new file mode 100644 index 000000000000..b02005087c1b --- /dev/null +++ b/drivers/net/appletalk/cops_ffdrv.h @@ -0,0 +1,532 @@ + +/* + * The firmware this driver downloads into the Localtalk card is a + * separate program and is not GPL'd source code, even though the Linux + * side driver and the routine that loads this data into the card are. + * + * It is taken from the COPS SDK and is under the following license + * + * This material is licensed to you strictly for use in conjunction with + * the use of COPS LocalTalk adapters. + * There is no charge for this SDK. And no waranty express or implied + * about its fitness for any purpose. However, we will cheerefully + * refund every penny you paid for this SDK... + * Regards, + * + * Thomas F. Divine + * Chief Scientist + */ + + +/* cops_ffdrv.h: LocalTalk driver firmware dump for Linux. + * + * Authors: + * - Jay Schulist + */ + + +#ifdef CONFIG_COPS_DAYNA + +static const unsigned char ffdrv_code[] = { + 58,3,0,50,228,149,33,255,255,34,226,149, + 249,17,40,152,33,202,154,183,237,82,77,68, + 11,107,98,19,54,0,237,176,175,50,80,0, + 62,128,237,71,62,32,237,57,51,62,12,237, + 57,50,237,57,54,62,6,237,57,52,62,12, + 237,57,49,33,107,137,34,32,128,33,83,130, + 34,40,128,33,86,130,34,42,128,33,112,130, + 34,36,128,33,211,130,34,38,128,62,0,237, + 57,16,33,63,148,34,34,128,237,94,205,15, + 130,251,205,168,145,24,141,67,111,112,121,114, + 105,103,104,116,32,40,67,41,32,49,57,56, + 56,32,45,32,68,97,121,110,97,32,67,111, + 109,109,117,110,105,99,97,116,105,111,110,115, + 32,32,32,65,108,108,32,114,105,103,104,116, + 115,32,114,101,115,101,114,118,101,100,46,32, + 32,40,68,40,68,7,16,8,34,7,22,6, + 16,5,12,4,8,3,6,140,0,16,39,128, + 0,4,96,10,224,6,0,7,126,2,64,11, + 118,12,6,13,0,14,193,15,0,5,96,3, + 192,1,64,9,8,62,9,211,66,62,192,211, + 66,62,100,61,32,253,6,28,33,205,129,14, + 66,237,163,194,253,129,6,28,33,205,129,14, + 64,237,163,194,9,130,201,62,47,50,71,152, + 62,47,211,68,58,203,129,237,57,20,58,204, + 129,237,57,21,33,77,152,54,132,205,233,129, + 58,228,149,254,209,40,6,56,4,62,0,24, + 2,219,96,33,233,149,119,230,62,33,232,149, + 119,213,33,8,152,17,7,0,25,119,19,25, + 119,209,201,251,237,77,245,197,213,229,221,229, + 205,233,129,62,1,50,106,137,205,158,139,221, + 225,225,209,193,241,251,237,77,245,197,213,219, + 72,237,56,16,230,46,237,57,16,237,56,12, + 58,72,152,183,32,26,6,20,17,128,2,237, + 56,46,187,32,35,237,56,47,186,32,29,219, + 72,230,1,32,3,5,32,232,175,50,72,152, + 229,221,229,62,1,50,106,137,205,158,139,221, + 225,225,24,25,62,1,50,72,152,58,201,129, + 237,57,12,58,202,129,237,57,13,237,56,16, + 246,17,237,57,16,209,193,241,251,237,77,245, + 197,229,213,221,229,237,56,16,230,17,237,57, + 16,237,56,20,58,34,152,246,16,246,8,211, + 68,62,6,61,32,253,58,34,152,246,8,211, + 68,58,203,129,237,57,20,58,204,129,237,57, + 21,237,56,16,246,34,237,57,16,221,225,209, + 225,193,241,251,237,77,33,2,0,57,126,230, + 3,237,100,1,40,2,246,128,230,130,245,62, + 5,211,64,241,211,64,201,229,213,243,237,56, + 16,230,46,237,57,16,237,56,12,251,70,35, + 35,126,254,175,202,77,133,254,129,202,15,133, + 230,128,194,191,132,43,58,44,152,119,33,76, + 152,119,35,62,132,119,120,254,255,40,4,58, + 49,152,119,219,72,43,43,112,17,3,0,237, + 56,52,230,248,237,57,52,219,72,230,1,194, + 141,131,209,225,237,56,52,246,6,237,57,52, + 62,1,55,251,201,62,3,211,66,62,192,211, + 66,62,48,211,66,0,0,219,66,230,1,40, + 4,219,67,24,240,205,203,135,58,75,152,254, + 255,202,128,132,58,49,152,254,161,250,207,131, + 58,34,152,211,68,62,10,211,66,62,128,211, + 66,62,11,211,66,62,6,211,66,24,0,62, + 14,211,66,62,33,211,66,62,1,211,66,62, + 64,211,66,62,3,211,66,62,209,211,66,62, + 100,71,219,66,230,1,32,6,5,32,247,195, + 248,132,219,67,71,58,44,152,184,194,248,132, + 62,100,71,219,66,230,1,32,6,5,32,247, + 195,248,132,219,67,62,100,71,219,66,230,1, + 32,6,5,32,247,195,248,132,219,67,254,133, + 32,7,62,0,50,74,152,24,17,254,173,32, + 7,62,1,50,74,152,24,6,254,141,194,248, + 132,71,209,225,58,49,152,254,132,32,10,62, + 50,205,2,134,205,144,135,24,27,254,140,32, + 15,62,110,205,2,134,62,141,184,32,5,205, + 144,135,24,8,62,10,205,2,134,205,8,134, + 62,1,50,106,137,205,158,139,237,56,52,246, + 6,237,57,52,175,183,251,201,62,20,135,237, + 57,20,175,237,57,21,237,56,16,246,2,237, + 57,16,237,56,20,95,237,56,21,123,254,10, + 48,244,237,56,16,230,17,237,57,16,209,225, + 205,144,135,62,1,50,106,137,205,158,139,237, + 56,52,246,6,237,57,52,175,183,251,201,209, + 225,243,219,72,230,1,40,13,62,10,211,66, + 0,0,219,66,230,192,202,226,132,237,56,52, + 246,6,237,57,52,62,1,55,251,201,205,203, + 135,62,1,50,106,137,205,158,139,237,56,52, + 246,6,237,57,52,183,251,201,209,225,62,1, + 50,106,137,205,158,139,237,56,52,246,6,237, + 57,52,62,2,55,251,201,209,225,243,219,72, + 230,1,202,213,132,62,10,211,66,0,0,219, + 66,230,192,194,213,132,229,62,1,50,106,137, + 42,40,152,205,65,143,225,17,3,0,205,111, + 136,62,6,211,66,58,44,152,211,66,237,56, + 52,246,6,237,57,52,183,251,201,209,197,237, + 56,52,230,248,237,57,52,219,72,230,1,32, + 15,193,225,237,56,52,246,6,237,57,52,62, + 1,55,251,201,14,23,58,37,152,254,0,40, + 14,14,2,254,1,32,5,62,140,119,24,3, + 62,132,119,43,43,197,205,203,135,193,62,1, + 211,66,62,64,211,66,62,3,211,66,62,193, + 211,66,62,100,203,39,71,219,66,230,1,32, + 6,5,32,247,195,229,133,33,238,151,219,67, + 71,58,44,152,184,194,229,133,119,62,100,71, + 219,66,230,1,32,6,5,32,247,195,229,133, + 219,67,35,119,13,32,234,193,225,62,1,50, + 106,137,205,158,139,237,56,52,246,6,237,57, + 52,175,183,251,201,33,234,151,35,35,62,255, + 119,193,225,62,1,50,106,137,205,158,139,237, + 56,52,246,6,237,57,52,175,251,201,243,61, + 32,253,251,201,62,3,211,66,62,192,211,66, + 58,49,152,254,140,32,19,197,229,213,17,181, + 129,33,185,129,1,2,0,237,176,209,225,193, + 24,27,229,213,33,187,129,58,49,152,230,15, + 87,30,2,237,92,25,17,181,129,126,18,19, + 35,126,18,209,225,58,34,152,246,8,211,68, + 58,49,152,254,165,40,14,254,164,40,10,62, + 10,211,66,62,224,211,66,24,25,58,74,152, + 254,0,40,10,62,10,211,66,62,160,211,66, + 24,8,62,10,211,66,62,128,211,66,62,11, + 211,66,62,6,211,66,205,147,143,62,5,211, + 66,62,224,211,66,62,5,211,66,62,96,211, + 66,62,5,61,32,253,62,5,211,66,62,224, + 211,66,62,14,61,32,253,62,5,211,66,62, + 233,211,66,62,128,211,66,58,181,129,61,32, + 253,62,1,211,66,62,192,211,66,1,254,19, + 237,56,46,187,32,6,13,32,247,195,226,134, + 62,192,211,66,0,0,219,66,203,119,40,250, + 219,66,203,87,40,250,243,237,56,16,230,17, + 237,57,16,237,56,20,251,62,5,211,66,62, + 224,211,66,58,182,129,61,32,253,229,33,181, + 129,58,183,129,203,63,119,35,58,184,129,119, + 225,62,10,211,66,62,224,211,66,62,11,211, + 66,62,118,211,66,62,47,211,68,62,5,211, + 66,62,233,211,66,58,181,129,61,32,253,62, + 5,211,66,62,224,211,66,58,182,129,61,32, + 253,62,5,211,66,62,96,211,66,201,229,213, + 58,50,152,230,15,87,30,2,237,92,33,187, + 129,25,17,181,129,126,18,35,19,126,18,209, + 225,58,71,152,246,8,211,68,58,50,152,254, + 165,40,14,254,164,40,10,62,10,211,66,62, + 224,211,66,24,8,62,10,211,66,62,128,211, + 66,62,11,211,66,62,6,211,66,195,248,135, + 62,3,211,66,62,192,211,66,197,229,213,17, + 181,129,33,183,129,1,2,0,237,176,209,225, + 193,62,47,211,68,62,10,211,66,62,224,211, + 66,62,11,211,66,62,118,211,66,62,1,211, + 66,62,0,211,66,205,147,143,195,16,136,62, + 3,211,66,62,192,211,66,197,229,213,17,181, + 129,33,183,129,1,2,0,237,176,209,225,193, + 62,47,211,68,62,10,211,66,62,224,211,66, + 62,11,211,66,62,118,211,66,205,147,143,62, + 5,211,66,62,224,211,66,62,5,211,66,62, + 96,211,66,62,5,61,32,253,62,5,211,66, + 62,224,211,66,62,14,61,32,253,62,5,211, + 66,62,233,211,66,62,128,211,66,58,181,129, + 61,32,253,62,1,211,66,62,192,211,66,1, + 254,19,237,56,46,187,32,6,13,32,247,195, + 88,136,62,192,211,66,0,0,219,66,203,119, + 40,250,219,66,203,87,40,250,62,5,211,66, + 62,224,211,66,58,182,129,61,32,253,62,5, + 211,66,62,96,211,66,201,197,14,67,6,0, + 62,3,211,66,62,192,211,66,62,48,211,66, + 0,0,219,66,230,1,40,4,219,67,24,240, + 62,5,211,66,62,233,211,66,62,128,211,66, + 58,181,129,61,32,253,237,163,29,62,192,211, + 66,219,66,230,4,40,250,237,163,29,32,245, + 219,66,230,4,40,250,62,255,71,219,66,230, + 4,40,3,5,32,247,219,66,230,4,40,250, + 62,5,211,66,62,224,211,66,58,182,129,61, + 32,253,62,5,211,66,62,96,211,66,58,71, + 152,254,1,202,18,137,62,16,211,66,62,56, + 211,66,62,14,211,66,62,33,211,66,62,1, + 211,66,62,248,211,66,237,56,48,246,153,230, + 207,237,57,48,62,3,211,66,62,221,211,66, + 193,201,58,71,152,211,68,62,10,211,66,62, + 128,211,66,62,11,211,66,62,6,211,66,62, + 6,211,66,58,44,152,211,66,62,16,211,66, + 62,56,211,66,62,48,211,66,0,0,62,14, + 211,66,62,33,211,66,62,1,211,66,62,248, + 211,66,237,56,48,246,145,246,8,230,207,237, + 57,48,62,3,211,66,62,221,211,66,193,201, + 44,3,1,0,70,69,1,245,197,213,229,175, + 50,72,152,237,56,16,230,46,237,57,16,237, + 56,12,62,1,211,66,0,0,219,66,95,230, + 160,32,3,195,20,139,123,230,96,194,72,139, + 62,48,211,66,62,1,211,66,62,64,211,66, + 237,91,40,152,205,207,143,25,43,55,237,82, + 218,70,139,34,42,152,98,107,58,44,152,190, + 194,210,138,35,35,62,130,190,194,200,137,62, + 1,50,48,152,62,175,190,202,82,139,62,132, + 190,32,44,50,50,152,62,47,50,71,152,229, + 175,50,106,137,42,40,152,205,65,143,225,54, + 133,43,70,58,44,152,119,43,112,17,3,0, + 62,10,205,2,134,205,111,136,195,158,138,62, + 140,190,32,19,50,50,152,58,233,149,230,4, + 202,222,138,62,1,50,71,152,195,219,137,126, + 254,160,250,185,138,254,166,242,185,138,50,50, + 152,43,126,35,229,213,33,234,149,95,22,0, + 25,126,254,132,40,18,254,140,40,14,58,50, + 152,230,15,87,126,31,21,242,65,138,56,2, + 175,119,58,50,152,230,15,87,58,233,149,230, + 62,31,21,242,85,138,218,98,138,209,225,195, + 20,139,58,50,152,33,100,137,230,15,95,22, + 0,25,126,50,71,152,209,225,58,50,152,254, + 164,250,135,138,58,73,152,254,0,40,4,54, + 173,24,2,54,133,43,70,58,44,152,119,43, + 112,17,3,0,205,70,135,175,50,106,137,205, + 208,139,58,199,129,237,57,12,58,200,129,237, + 57,13,237,56,16,246,17,237,57,16,225,209, + 193,241,251,237,77,62,129,190,194,227,138,54, + 130,43,70,58,44,152,119,43,112,17,3,0, + 205,144,135,195,20,139,35,35,126,254,132,194, + 227,138,175,50,106,137,205,158,139,24,42,58, + 201,154,254,1,40,7,62,1,50,106,137,24, + 237,58,106,137,254,1,202,222,138,62,128,166, + 194,222,138,221,229,221,33,67,152,205,127,142, + 205,109,144,221,225,225,209,193,241,251,237,77, + 58,106,137,254,1,202,44,139,58,50,152,254, + 164,250,44,139,58,73,152,238,1,50,73,152, + 221,229,221,33,51,152,205,127,142,221,225,62, + 1,50,106,137,205,158,139,195,13,139,24,208, + 24,206,24,204,230,64,40,3,195,20,139,195, + 20,139,43,126,33,8,152,119,35,58,44,152, + 119,43,237,91,35,152,205,203,135,205,158,139, + 195,13,139,175,50,78,152,62,3,211,66,62, + 192,211,66,201,197,33,4,0,57,126,35,102, + 111,62,1,50,106,137,219,72,205,141,139,193, + 201,62,1,50,78,152,34,40,152,54,0,35, + 35,54,0,195,163,139,58,78,152,183,200,229, + 33,181,129,58,183,129,119,35,58,184,129,119, + 225,62,47,211,68,62,14,211,66,62,193,211, + 66,62,10,211,66,62,224,211,66,62,11,211, + 66,62,118,211,66,195,3,140,58,78,152,183, + 200,58,71,152,211,68,254,69,40,4,254,70, + 32,17,58,73,152,254,0,40,10,62,10,211, + 66,62,160,211,66,24,8,62,10,211,66,62, + 128,211,66,62,11,211,66,62,6,211,66,62, + 6,211,66,58,44,152,211,66,62,16,211,66, + 62,56,211,66,62,48,211,66,0,0,219,66, + 230,1,40,4,219,67,24,240,62,14,211,66, + 62,33,211,66,42,40,152,205,65,143,62,1, + 211,66,62,248,211,66,237,56,48,246,145,246, + 8,230,207,237,57,48,62,3,211,66,62,221, + 211,66,201,62,16,211,66,62,56,211,66,62, + 48,211,66,0,0,219,66,230,1,40,4,219, + 67,24,240,62,14,211,66,62,33,211,66,62, + 1,211,66,62,248,211,66,237,56,48,246,153, + 230,207,237,57,48,62,3,211,66,62,221,211, + 66,201,229,213,33,234,149,95,22,0,25,126, + 254,132,40,4,254,140,32,2,175,119,123,209, + 225,201,6,8,14,0,31,48,1,12,16,250, + 121,201,33,4,0,57,94,35,86,33,2,0, + 57,126,35,102,111,221,229,34,89,152,237,83, + 91,152,221,33,63,152,205,127,142,58,81,152, + 50,82,152,58,80,152,135,50,80,152,205,162, + 140,254,3,56,16,58,81,152,135,60,230,15, + 50,81,152,175,50,80,152,24,23,58,79,152, + 205,162,140,254,3,48,13,58,81,152,203,63, + 50,81,152,62,255,50,79,152,58,81,152,50, + 82,152,58,79,152,135,50,79,152,62,32,50, + 83,152,50,84,152,237,56,16,230,17,237,57, + 16,219,72,62,192,50,93,152,62,93,50,94, + 152,58,93,152,61,50,93,152,32,9,58,94, + 152,61,50,94,152,40,44,62,170,237,57,20, + 175,237,57,21,237,56,16,246,2,237,57,16, + 219,72,230,1,202,29,141,237,56,20,71,237, + 56,21,120,254,10,48,237,237,56,16,230,17, + 237,57,16,243,62,14,211,66,62,65,211,66, + 251,58,39,152,23,23,60,50,39,152,71,58, + 82,152,160,230,15,40,22,71,14,10,219,66, + 230,16,202,186,141,219,72,230,1,202,186,141, + 13,32,239,16,235,42,89,152,237,91,91,152, + 205,47,131,48,7,61,202,186,141,195,227,141, + 221,225,33,0,0,201,221,33,55,152,205,127, + 142,58,84,152,61,50,84,152,40,19,58,82, + 152,246,1,50,82,152,58,79,152,246,1,50, + 79,152,195,29,141,221,225,33,1,0,201,221, + 33,59,152,205,127,142,58,80,152,246,1,50, + 80,152,58,82,152,135,246,1,50,82,152,58, + 83,152,61,50,83,152,194,29,141,221,225,33, + 2,0,201,221,229,33,0,0,57,17,4,0, + 25,126,50,44,152,230,128,50,85,152,58,85, + 152,183,40,6,221,33,88,2,24,4,221,33, + 150,0,58,44,152,183,40,53,60,40,50,60, + 40,47,61,61,33,86,152,119,35,119,35,54, + 129,175,50,48,152,221,43,221,229,225,124,181, + 40,42,33,86,152,17,3,0,205,189,140,17, + 232,3,27,123,178,32,251,58,48,152,183,40, + 224,58,44,152,71,62,7,128,230,127,71,58, + 85,152,176,50,44,152,24,162,221,225,201,183, + 221,52,0,192,221,52,1,192,221,52,2,192, + 221,52,3,192,55,201,245,62,1,211,100,241, + 201,245,62,1,211,96,241,201,33,2,0,57, + 126,35,102,111,237,56,48,230,175,237,57,48, + 62,48,237,57,49,125,237,57,32,124,237,57, + 33,62,0,237,57,34,62,88,237,57,35,62, + 0,237,57,36,237,57,37,33,128,2,125,237, + 57,38,124,237,57,39,237,56,48,246,97,230, + 207,237,57,48,62,0,237,57,0,62,0,211, + 96,211,100,201,33,2,0,57,126,35,102,111, + 237,56,48,230,175,237,57,48,62,12,237,57, + 49,62,76,237,57,32,62,0,237,57,33,237, + 57,34,125,237,57,35,124,237,57,36,62,0, + 237,57,37,33,128,2,125,237,57,38,124,237, + 57,39,237,56,48,246,97,230,207,237,57,48, + 62,1,211,96,201,33,2,0,57,126,35,102, + 111,229,237,56,48,230,87,237,57,48,125,237, + 57,40,124,237,57,41,62,0,237,57,42,62, + 67,237,57,43,62,0,237,57,44,58,106,137, + 254,1,32,5,33,6,0,24,3,33,128,2, + 125,237,57,46,124,237,57,47,237,56,50,230, + 252,246,2,237,57,50,225,201,33,4,0,57, + 94,35,86,33,2,0,57,126,35,102,111,237, + 56,48,230,87,237,57,48,125,237,57,40,124, + 237,57,41,62,0,237,57,42,62,67,237,57, + 43,62,0,237,57,44,123,237,57,46,122,237, + 57,47,237,56,50,230,244,246,0,237,57,50, + 237,56,48,246,145,230,207,237,57,48,201,213, + 237,56,46,95,237,56,47,87,237,56,46,111, + 237,56,47,103,183,237,82,32,235,33,128,2, + 183,237,82,209,201,213,237,56,38,95,237,56, + 39,87,237,56,38,111,237,56,39,103,183,237, + 82,32,235,33,128,2,183,237,82,209,201,245, + 197,1,52,0,237,120,230,253,237,121,193,241, + 201,245,197,1,52,0,237,120,246,2,237,121, + 193,241,201,33,2,0,57,126,35,102,111,126, + 35,110,103,201,33,0,0,34,102,152,34,96, + 152,34,98,152,33,202,154,34,104,152,237,91, + 104,152,42,226,149,183,237,82,17,0,255,25, + 34,100,152,203,124,40,6,33,0,125,34,100, + 152,42,104,152,35,35,35,229,205,120,139,193, + 201,205,186,149,229,42,40,152,35,35,35,229, + 205,39,144,193,124,230,3,103,221,117,254,221, + 116,255,237,91,42,152,35,35,35,183,237,82, + 32,12,17,5,0,42,42,152,205,171,149,242, + 169,144,42,40,152,229,205,120,139,193,195,198, + 149,237,91,42,152,42,98,152,25,34,98,152, + 19,19,19,42,102,152,25,34,102,152,237,91, + 100,152,33,158,253,25,237,91,102,152,205,171, + 149,242,214,144,33,0,0,34,102,152,62,1, + 50,95,152,205,225,144,195,198,149,58,95,152, + 183,200,237,91,96,152,42,102,152,205,171,149, + 242,5,145,237,91,102,152,33,98,2,25,237, + 91,96,152,205,171,149,250,37,145,237,91,96, + 152,42,102,152,183,237,82,32,7,42,98,152, + 125,180,40,13,237,91,102,152,42,96,152,205, + 171,149,242,58,145,237,91,104,152,42,102,152, + 25,35,35,35,229,205,120,139,193,175,50,95, + 152,201,195,107,139,205,206,149,250,255,243,205, + 225,144,251,58,230,149,183,194,198,149,17,1, + 0,42,98,152,205,171,149,250,198,149,62,1, + 50,230,149,237,91,96,152,42,104,152,25,221, + 117,252,221,116,253,237,91,104,152,42,96,152, + 25,35,35,35,221,117,254,221,116,255,35,35, + 35,229,205,39,144,124,230,3,103,35,35,35, + 221,117,250,221,116,251,235,221,110,252,221,102, + 253,115,35,114,35,54,4,62,1,211,100,211, + 84,195,198,149,33,0,0,34,102,152,34,96, + 152,34,98,152,33,202,154,34,104,152,237,91, + 104,152,42,226,149,183,237,82,17,0,255,25, + 34,100,152,33,109,152,54,0,33,107,152,229, + 205,240,142,193,62,47,50,34,152,62,132,50, + 49,152,205,241,145,205,61,145,58,39,152,60, + 50,39,152,24,241,205,206,149,251,255,33,109, + 152,126,183,202,198,149,110,221,117,251,33,109, + 152,54,0,221,126,251,254,1,40,28,254,3, + 40,101,254,4,202,190,147,254,5,202,147,147, + 254,8,40,87,33,107,152,229,205,240,142,195, + 198,149,58,201,154,183,32,21,33,111,152,126, + 50,229,149,205,52,144,33,110,152,110,38,0, + 229,205,11,142,193,237,91,96,152,42,104,152, + 25,221,117,254,221,116,255,35,35,54,2,17, + 2,0,43,43,115,35,114,58,44,152,35,35, + 119,58,228,149,35,119,62,1,211,100,211,84, + 62,1,50,201,154,24,169,205,153,142,58,231, + 149,183,40,250,175,50,231,149,33,110,152,126, + 254,255,40,91,58,233,149,230,63,183,40,83, + 94,22,0,33,234,149,25,126,183,40,13,33, + 110,152,94,33,234,150,25,126,254,3,32,36, + 205,81,148,125,180,33,110,152,94,22,0,40, + 17,33,234,149,25,54,0,33,107,152,229,205, + 240,142,193,195,198,149,33,234,150,25,54,0, + 33,110,152,94,22,0,33,234,149,25,126,50, + 49,152,254,132,32,37,62,47,50,34,152,42, + 107,152,229,33,110,152,229,205,174,140,193,193, + 125,180,33,110,152,94,22,0,33,234,150,202, + 117,147,25,52,195,120,147,58,49,152,254,140, + 32,7,62,1,50,34,152,24,210,62,32,50, + 106,152,24,19,58,49,152,95,58,106,152,163, + 183,58,106,152,32,11,203,63,50,106,152,58, + 106,152,183,32,231,254,2,40,51,254,4,40, + 38,254,8,40,26,254,16,40,13,254,32,32, + 158,62,165,50,49,152,62,69,24,190,62,164, + 50,49,152,62,70,24,181,62,163,50,49,152, + 175,24,173,62,162,50,49,152,62,1,24,164, + 62,161,50,49,152,62,3,24,155,25,54,0, + 221,126,251,254,8,40,7,58,230,149,183,202, + 32,146,33,107,152,229,205,240,142,193,211,84, + 195,198,149,237,91,96,152,42,104,152,25,221, + 117,254,221,116,255,35,35,54,6,17,2,0, + 43,43,115,35,114,58,228,149,35,35,119,58, + 233,149,35,119,205,146,142,195,32,146,237,91, + 96,152,42,104,152,25,229,205,160,142,193,58, + 231,149,183,40,250,175,50,231,149,243,237,91, + 96,152,42,104,152,25,221,117,254,221,116,255, + 78,35,70,221,113,252,221,112,253,89,80,42, + 98,152,183,237,82,34,98,152,203,124,40,19, + 33,0,0,34,98,152,34,102,152,34,96,152, + 62,1,50,95,152,24,40,221,94,252,221,86, + 253,19,19,19,42,96,152,25,34,96,152,237, + 91,100,152,33,158,253,25,237,91,96,152,205, + 171,149,242,55,148,33,0,0,34,96,152,175, + 50,230,149,251,195,32,146,245,62,1,50,231, + 149,62,16,237,57,0,211,80,241,251,237,77, + 201,205,186,149,229,229,33,0,0,34,37,152, + 33,110,152,126,50,234,151,58,44,152,33,235, + 151,119,221,54,253,0,221,54,254,0,195,230, + 148,33,236,151,54,175,33,3,0,229,33,234, + 151,229,205,174,140,193,193,33,236,151,126,254, + 255,40,74,33,245,151,110,221,117,255,33,249, + 151,126,221,166,255,221,119,255,33,253,151,126, + 221,166,255,221,119,255,58,232,149,95,221,126, + 255,163,221,119,255,183,40,15,230,191,33,110, + 152,94,22,0,33,234,149,25,119,24,12,33, + 110,152,94,22,0,33,234,149,25,54,132,33, + 0,0,195,198,149,221,110,253,221,102,254,35, + 221,117,253,221,116,254,17,32,0,221,110,253, + 221,102,254,205,171,149,250,117,148,58,233,149, + 203,87,40,84,33,1,0,34,37,152,221,54, + 253,0,221,54,254,0,24,53,33,236,151,54, + 175,33,3,0,229,33,234,151,229,205,174,140, + 193,193,33,236,151,126,254,255,40,14,33,110, + 152,94,22,0,33,234,149,25,54,140,24,159, + 221,110,253,221,102,254,35,221,117,253,221,116, + 254,17,32,0,221,110,253,221,102,254,205,171, + 149,250,12,149,33,2,0,34,37,152,221,54, + 253,0,221,54,254,0,24,54,33,236,151,54, + 175,33,3,0,229,33,234,151,229,205,174,140, + 193,193,33,236,151,126,254,255,40,15,33,110, + 152,94,22,0,33,234,149,25,54,132,195,211, + 148,221,110,253,221,102,254,35,221,117,253,221, + 116,254,17,32,0,221,110,253,221,102,254,205, + 171,149,250,96,149,33,1,0,195,198,149,124, + 170,250,179,149,237,82,201,124,230,128,237,82, + 60,201,225,253,229,221,229,221,33,0,0,221, + 57,233,221,249,221,225,253,225,201,233,225,253, + 229,221,229,221,33,0,0,221,57,94,35,86, + 35,235,57,249,235,233,0,0,0,0,0,0, + 62,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 175,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,133,1,0,0,0,63, + 255,255,255,255,0,0,0,63,0,0,0,0, + 0,0,0,0,0,0,0,24,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0 + } ; + +#endif diff --git a/drivers/net/appletalk/cops_ltdrv.h b/drivers/net/appletalk/cops_ltdrv.h new file mode 100644 index 000000000000..c699b1ad31da --- /dev/null +++ b/drivers/net/appletalk/cops_ltdrv.h @@ -0,0 +1,241 @@ +/* + * The firmware this driver downloads into the Localtalk card is a + * separate program and is not GPL'd source code, even though the Linux + * side driver and the routine that loads this data into the card are. + * + * It is taken from the COPS SDK and is under the following license + * + * This material is licensed to you strictly for use in conjunction with + * the use of COPS LocalTalk adapters. + * There is no charge for this SDK. And no waranty express or implied + * about its fitness for any purpose. However, we will cheerefully + * refund every penny you paid for this SDK... + * Regards, + * + * Thomas F. Divine + * Chief Scientist + */ + + +/* cops_ltdrv.h: LocalTalk driver firmware dump for Linux. + * + * Authors: + * - Jay Schulist + */ + + +#ifdef CONFIG_COPS_TANGENT + +static const unsigned char ltdrv_code[] = { + 58,3,0,50,148,10,33,143,15,62,85,119, + 190,32,9,62,170,119,190,32,3,35,24,241, + 34,146,10,249,17,150,10,33,143,15,183,237, + 82,77,68,11,107,98,19,54,0,237,176,62, + 16,237,57,51,62,0,237,57,50,237,57,54, + 62,12,237,57,49,62,195,33,39,2,50,56, + 0,34,57,0,237,86,205,30,2,251,205,60, + 10,24,169,67,111,112,121,114,105,103,104,116, + 32,40,99,41,32,49,57,56,56,45,49,57, + 57,50,44,32,80,114,105,110,116,105,110,103, + 32,67,111,109,109,117,110,105,99,97,116,105, + 111,110,115,32,65,115,115,111,99,105,97,116, + 101,115,44,32,73,110,99,46,65,108,108,32, + 114,105,103,104,116,115,32,114,101,115,101,114, + 118,101,100,46,32,32,4,4,22,40,255,60, + 4,96,10,224,6,0,7,126,2,64,11,246, + 12,6,13,0,14,193,15,0,5,96,3,192, + 1,0,9,8,62,3,211,82,62,192,211,82, + 201,62,3,211,82,62,213,211,82,201,62,5, + 211,82,62,224,211,82,201,62,5,211,82,62, + 224,211,82,201,62,5,211,82,62,96,211,82, + 201,6,28,33,180,1,14,82,237,163,194,4, + 2,33,39,2,34,64,0,58,3,0,230,1, + 192,62,11,237,121,62,118,237,121,201,33,182, + 10,54,132,205,253,1,201,245,197,213,229,42, + 150,10,14,83,17,98,2,67,20,237,162,58, + 179,1,95,219,82,230,1,32,6,29,32,247, + 195,17,3,62,1,211,82,219,82,95,230,160, + 32,10,237,162,32,225,21,32,222,195,15,3, + 237,162,123,230,96,194,21,3,62,48,211,82, + 62,1,211,82,175,211,82,237,91,150,10,43, + 55,237,82,218,19,3,34,152,10,98,107,58, + 154,10,190,32,81,62,1,50,158,10,35,35, + 62,132,190,32,44,54,133,43,70,58,154,10, + 119,43,112,17,3,0,205,137,3,62,16,211, + 82,62,56,211,82,205,217,1,42,150,10,14, + 83,17,98,2,67,20,58,178,1,95,195,59, + 2,62,129,190,194,227,2,54,130,43,70,58, + 154,10,119,43,112,17,3,0,205,137,3,195, + 254,2,35,35,126,254,132,194,227,2,205,61, + 3,24,20,62,128,166,194,222,2,221,229,221, + 33,175,10,205,93,6,205,144,7,221,225,225, + 209,193,241,251,237,77,221,229,221,33,159,10, + 205,93,6,221,225,205,61,3,195,247,2,24, + 237,24,235,24,233,230,64,40,2,24,227,24, + 225,175,50,179,10,205,208,1,201,197,33,4, + 0,57,126,35,102,111,205,51,3,193,201,62, + 1,50,179,10,34,150,10,54,0,58,179,10, + 183,200,62,14,211,82,62,193,211,82,62,10, + 211,82,62,224,211,82,62,6,211,82,58,154, + 10,211,82,62,16,211,82,62,56,211,82,62, + 48,211,82,219,82,230,1,40,4,219,83,24, + 242,62,14,211,82,62,33,211,82,62,1,211, + 82,62,9,211,82,62,32,211,82,205,217,1, + 201,14,83,205,208,1,24,23,14,83,205,208, + 1,205,226,1,58,174,1,61,32,253,205,244, + 1,58,174,1,61,32,253,205,226,1,58,175, + 1,61,32,253,62,5,211,82,62,233,211,82, + 62,128,211,82,58,176,1,61,32,253,237,163, + 27,62,192,211,82,219,82,230,4,40,250,237, + 163,27,122,179,32,243,219,82,230,4,40,250, + 58,178,1,71,219,82,230,4,40,3,5,32, + 247,219,82,230,4,40,250,205,235,1,58,177, + 1,61,32,253,205,244,1,201,229,213,35,35, + 126,230,128,194,145,4,43,58,154,10,119,43, + 70,33,181,10,119,43,112,17,3,0,243,62, + 10,211,82,219,82,230,128,202,41,4,209,225, + 62,1,55,251,201,205,144,3,58,180,10,254, + 255,202,127,4,205,217,1,58,178,1,71,219, + 82,230,1,32,6,5,32,247,195,173,4,219, + 83,71,58,154,10,184,194,173,4,58,178,1, + 71,219,82,230,1,32,6,5,32,247,195,173, + 4,219,83,58,178,1,71,219,82,230,1,32, + 6,5,32,247,195,173,4,219,83,254,133,194, + 173,4,58,179,1,24,4,58,179,1,135,61, + 32,253,209,225,205,137,3,205,61,3,183,251, + 201,209,225,243,62,10,211,82,219,82,230,128, + 202,164,4,62,1,55,251,201,205,144,3,205, + 61,3,183,251,201,209,225,62,2,55,251,201, + 243,62,14,211,82,62,33,211,82,251,201,33, + 4,0,57,94,35,86,33,2,0,57,126,35, + 102,111,221,229,34,193,10,237,83,195,10,221, + 33,171,10,205,93,6,58,185,10,50,186,10, + 58,184,10,135,50,184,10,205,112,6,254,3, + 56,16,58,185,10,135,60,230,15,50,185,10, + 175,50,184,10,24,23,58,183,10,205,112,6, + 254,3,48,13,58,185,10,203,63,50,185,10, + 62,255,50,183,10,58,185,10,50,186,10,58, + 183,10,135,50,183,10,62,32,50,187,10,50, + 188,10,6,255,219,82,230,16,32,3,5,32, + 247,205,180,4,6,40,219,82,230,16,40,3, + 5,32,247,62,10,211,82,219,82,230,128,194, + 46,5,219,82,230,16,40,214,237,95,71,58, + 186,10,160,230,15,40,32,71,14,10,62,10, + 211,82,219,82,230,128,202,119,5,205,180,4, + 195,156,5,219,82,230,16,202,156,5,13,32, + 229,16,225,42,193,10,237,91,195,10,205,252, + 3,48,7,61,202,156,5,195,197,5,221,225, + 33,0,0,201,221,33,163,10,205,93,6,58, + 188,10,61,50,188,10,40,19,58,186,10,246, + 1,50,186,10,58,183,10,246,1,50,183,10, + 195,46,5,221,225,33,1,0,201,221,33,167, + 10,205,93,6,58,184,10,246,1,50,184,10, + 58,186,10,135,246,1,50,186,10,58,187,10, + 61,50,187,10,194,46,5,221,225,33,2,0, + 201,221,229,33,0,0,57,17,4,0,25,126, + 50,154,10,230,128,50,189,10,58,189,10,183, + 40,6,221,33,88,2,24,4,221,33,150,0, + 58,154,10,183,40,49,60,40,46,61,33,190, + 10,119,35,119,35,54,129,175,50,158,10,221, + 43,221,229,225,124,181,40,42,33,190,10,17, + 3,0,205,206,4,17,232,3,27,123,178,32, + 251,58,158,10,183,40,224,58,154,10,71,62, + 7,128,230,127,71,58,189,10,176,50,154,10, + 24,166,221,225,201,183,221,52,0,192,221,52, + 1,192,221,52,2,192,221,52,3,192,55,201, + 6,8,14,0,31,48,1,12,16,250,121,201, + 33,2,0,57,94,35,86,35,78,35,70,35, + 126,35,102,105,79,120,68,103,237,176,201,33, + 2,0,57,126,35,102,111,62,17,237,57,48, + 125,237,57,40,124,237,57,41,62,0,237,57, + 42,62,64,237,57,43,62,0,237,57,44,33, + 128,2,125,237,57,46,124,237,57,47,62,145, + 237,57,48,211,68,58,149,10,211,66,201,33, + 2,0,57,126,35,102,111,62,33,237,57,48, + 62,64,237,57,32,62,0,237,57,33,237,57, + 34,125,237,57,35,124,237,57,36,62,0,237, + 57,37,33,128,2,125,237,57,38,124,237,57, + 39,62,97,237,57,48,211,67,58,149,10,211, + 66,201,237,56,46,95,237,56,47,87,237,56, + 46,111,237,56,47,103,183,237,82,32,235,33, + 128,2,183,237,82,201,237,56,38,95,237,56, + 39,87,237,56,38,111,237,56,39,103,183,237, + 82,32,235,33,128,2,183,237,82,201,205,106, + 10,221,110,6,221,102,7,126,35,110,103,195, + 118,10,205,106,10,33,0,0,34,205,10,34, + 198,10,34,200,10,33,143,15,34,207,10,237, + 91,207,10,42,146,10,183,237,82,17,0,255, + 25,34,203,10,203,124,40,6,33,0,125,34, + 203,10,42,207,10,229,205,37,3,195,118,10, + 205,106,10,229,42,150,10,35,35,35,229,205, + 70,7,193,124,230,3,103,221,117,254,221,116, + 255,237,91,152,10,35,35,35,183,237,82,32, + 12,17,5,0,42,152,10,205,91,10,242,203, + 7,42,150,10,229,205,37,3,195,118,10,237, + 91,152,10,42,200,10,25,34,200,10,42,205, + 10,25,34,205,10,237,91,203,10,33,158,253, + 25,237,91,205,10,205,91,10,242,245,7,33, + 0,0,34,205,10,62,1,50,197,10,205,5, + 8,33,0,0,57,249,195,118,10,205,106,10, + 58,197,10,183,202,118,10,237,91,198,10,42, + 205,10,205,91,10,242,46,8,237,91,205,10, + 33,98,2,25,237,91,198,10,205,91,10,250, + 78,8,237,91,198,10,42,205,10,183,237,82, + 32,7,42,200,10,125,180,40,13,237,91,205, + 10,42,198,10,205,91,10,242,97,8,237,91, + 207,10,42,205,10,25,229,205,37,3,175,50, + 197,10,195,118,10,205,29,3,33,0,0,57, + 249,195,118,10,205,106,10,58,202,10,183,40, + 22,205,14,7,237,91,209,10,19,19,19,205, + 91,10,242,139,8,33,1,0,195,118,10,33, + 0,0,195,118,10,205,126,10,252,255,205,108, + 8,125,180,194,118,10,237,91,200,10,33,0, + 0,205,91,10,242,118,10,237,91,207,10,42, + 198,10,25,221,117,254,221,116,255,35,35,35, + 229,205,70,7,193,124,230,3,103,35,35,35, + 221,117,252,221,116,253,229,221,110,254,221,102, + 255,229,33,212,10,229,205,124,6,193,193,221, + 110,252,221,102,253,34,209,10,33,211,10,54, + 4,33,209,10,227,205,147,6,193,62,1,50, + 202,10,243,221,94,252,221,86,253,42,200,10, + 183,237,82,34,200,10,203,124,40,17,33,0, + 0,34,200,10,34,205,10,34,198,10,50,197, + 10,24,37,221,94,252,221,86,253,42,198,10, + 25,34,198,10,237,91,203,10,33,158,253,25, + 237,91,198,10,205,91,10,242,68,9,33,0, + 0,34,198,10,205,5,8,33,0,0,57,249, + 251,195,118,10,205,106,10,33,49,13,126,183, + 40,16,205,42,7,237,91,47,13,19,19,19, + 205,91,10,242,117,9,58,142,15,198,1,50, + 142,15,195,118,10,33,49,13,126,254,1,40, + 25,254,3,202,7,10,254,5,202,21,10,33, + 49,13,54,0,33,47,13,229,205,207,6,195, + 118,10,58,141,15,183,32,72,33,51,13,126, + 50,149,10,205,86,7,33,50,13,126,230,127, + 183,32,40,58,142,15,230,127,50,142,15,183, + 32,5,198,1,50,142,15,33,50,13,126,111, + 23,159,103,203,125,58,142,15,40,5,198,128, + 50,142,15,33,50,13,119,33,50,13,126,111, + 23,159,103,229,205,237,5,193,33,211,10,54, + 2,33,2,0,34,209,10,58,154,10,33,212, + 10,119,58,148,10,33,213,10,119,33,209,10, + 229,205,147,6,193,24,128,42,47,13,229,33, + 50,13,229,205,191,4,193,24,239,33,211,10, + 54,6,33,3,0,34,209,10,58,154,10,33, + 212,10,119,58,148,10,33,213,10,119,33,214, + 10,54,5,33,209,10,229,205,147,6,24,200, + 205,106,10,33,49,13,54,0,33,47,13,229, + 205,207,6,33,209,10,227,205,147,6,193,205, + 80,9,205,145,8,24,248,124,170,250,99,10, + 237,82,201,124,230,128,237,82,60,201,225,253, + 229,221,229,221,33,0,0,221,57,233,221,249, + 221,225,253,225,201,233,225,253,229,221,229,221, + 33,0,0,221,57,94,35,86,35,235,57,249, + 235,233,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0 + } ; + +#endif diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c new file mode 100644 index 000000000000..10d0dba572c2 --- /dev/null +++ b/drivers/net/appletalk/ipddp.c @@ -0,0 +1,335 @@ +/* + * ipddp.c: IP to Appletalk-IP Encapsulation driver for Linux + * Appletalk-IP to IP Decapsulation driver for Linux + * + * Authors: + * - DDP-IP Encap by: Bradford W. Johnson + * - DDP-IP Decap by: Jay Schulist + * + * Derived from: + * - Almost all code already existed in net/appletalk/ddp.c I just + * moved/reorginized it into a driver file. Original IP-over-DDP code + * was done by Bradford W. Johnson + * - skeleton.c: A network driver outline for linux. + * Written 1993-94 by Donald Becker. + * - dummy.c: A dummy net driver. By Nick Holloway. + * - MacGate: A user space Daemon for Appletalk-IP Decap for + * Linux by Jay Schulist + * + * Copyright 1993 United States Government as represented by the + * Director, National Security Agency. + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipddp.h" /* Our stuff */ + +static const char version[] = KERN_INFO "ipddp.c:v0.01 8/28/97 Bradford W. Johnson \n"; + +static struct ipddp_route *ipddp_route_list; +static DEFINE_SPINLOCK(ipddp_route_lock); + +#ifdef CONFIG_IPDDP_ENCAP +static int ipddp_mode = IPDDP_ENCAP; +#else +static int ipddp_mode = IPDDP_DECAP; +#endif + +/* Index to functions, as function prototypes. */ +static netdev_tx_t ipddp_xmit(struct sk_buff *skb, + struct net_device *dev); +static int ipddp_create(struct ipddp_route *new_rt); +static int ipddp_delete(struct ipddp_route *rt); +static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt); +static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); + +static const struct net_device_ops ipddp_netdev_ops = { + .ndo_start_xmit = ipddp_xmit, + .ndo_do_ioctl = ipddp_ioctl, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static struct net_device * __init ipddp_init(void) +{ + static unsigned version_printed; + struct net_device *dev; + int err; + + dev = alloc_etherdev(0); + if (!dev) + return ERR_PTR(-ENOMEM); + + dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; + strcpy(dev->name, "ipddp%d"); + + if (version_printed++ == 0) + printk(version); + + /* Initialize the device structure. */ + dev->netdev_ops = &ipddp_netdev_ops; + + dev->type = ARPHRD_IPDDP; /* IP over DDP tunnel */ + dev->mtu = 585; + dev->flags |= IFF_NOARP; + + /* + * The worst case header we will need is currently a + * ethernet header (14 bytes) and a ddp header (sizeof ddpehdr+1) + * We send over SNAP so that takes another 8 bytes. + */ + dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1; + + err = register_netdev(dev); + if (err) { + free_netdev(dev); + return ERR_PTR(err); + } + + /* Let the user now what mode we are in */ + if(ipddp_mode == IPDDP_ENCAP) + printk("%s: Appletalk-IP Encap. mode by Bradford W. Johnson \n", + dev->name); + if(ipddp_mode == IPDDP_DECAP) + printk("%s: Appletalk-IP Decap. mode by Jay Schulist \n", + dev->name); + + return dev; +} + + +/* + * Transmit LLAP/ELAP frame using aarp_send_ddp. + */ +static netdev_tx_t ipddp_xmit(struct sk_buff *skb, struct net_device *dev) +{ + __be32 paddr = skb_rtable(skb)->rt_gateway; + struct ddpehdr *ddp; + struct ipddp_route *rt; + struct atalk_addr *our_addr; + + spin_lock(&ipddp_route_lock); + + /* + * Find appropriate route to use, based only on IP number. + */ + for(rt = ipddp_route_list; rt != NULL; rt = rt->next) + { + if(rt->ip == paddr) + break; + } + if(rt == NULL) { + spin_unlock(&ipddp_route_lock); + return NETDEV_TX_OK; + } + + our_addr = atalk_find_dev_addr(rt->dev); + + if(ipddp_mode == IPDDP_DECAP) + /* + * Pull off the excess room that should not be there. + * This is due to a hard-header problem. This is the + * quick fix for now though, till it breaks. + */ + skb_pull(skb, 35-(sizeof(struct ddpehdr)+1)); + + /* Create the Extended DDP header */ + ddp = (struct ddpehdr *)skb->data; + ddp->deh_len_hops = htons(skb->len + (1<<10)); + ddp->deh_sum = 0; + + /* + * For Localtalk we need aarp_send_ddp to strip the + * long DDP header and place a shot DDP header on it. + */ + if(rt->dev->type == ARPHRD_LOCALTLK) + { + ddp->deh_dnet = 0; /* FIXME more hops?? */ + ddp->deh_snet = 0; + } + else + { + ddp->deh_dnet = rt->at.s_net; /* FIXME more hops?? */ + ddp->deh_snet = our_addr->s_net; + } + ddp->deh_dnode = rt->at.s_node; + ddp->deh_snode = our_addr->s_node; + ddp->deh_dport = 72; + ddp->deh_sport = 72; + + *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */ + + skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */ + + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + aarp_send_ddp(rt->dev, skb, &rt->at, NULL); + + spin_unlock(&ipddp_route_lock); + + return NETDEV_TX_OK; +} + +/* + * Create a routing entry. We first verify that the + * record does not already exist. If it does we return -EEXIST + */ +static int ipddp_create(struct ipddp_route *new_rt) +{ + struct ipddp_route *rt = kmalloc(sizeof(*rt), GFP_KERNEL); + + if (rt == NULL) + return -ENOMEM; + + rt->ip = new_rt->ip; + rt->at = new_rt->at; + rt->next = NULL; + if ((rt->dev = atrtr_get_dev(&rt->at)) == NULL) { + kfree(rt); + return -ENETUNREACH; + } + + spin_lock_bh(&ipddp_route_lock); + if (__ipddp_find_route(rt)) { + spin_unlock_bh(&ipddp_route_lock); + kfree(rt); + return -EEXIST; + } + + rt->next = ipddp_route_list; + ipddp_route_list = rt; + + spin_unlock_bh(&ipddp_route_lock); + + return 0; +} + +/* + * Delete a route, we only delete a FULL match. + * If route does not exist we return -ENOENT. + */ +static int ipddp_delete(struct ipddp_route *rt) +{ + struct ipddp_route **r = &ipddp_route_list; + struct ipddp_route *tmp; + + spin_lock_bh(&ipddp_route_lock); + while((tmp = *r) != NULL) + { + if(tmp->ip == rt->ip && + tmp->at.s_net == rt->at.s_net && + tmp->at.s_node == rt->at.s_node) + { + *r = tmp->next; + spin_unlock_bh(&ipddp_route_lock); + kfree(tmp); + return 0; + } + r = &tmp->next; + } + + spin_unlock_bh(&ipddp_route_lock); + return -ENOENT; +} + +/* + * Find a routing entry, we only return a FULL match + */ +static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt) +{ + struct ipddp_route *f; + + for(f = ipddp_route_list; f != NULL; f = f->next) + { + if(f->ip == rt->ip && + f->at.s_net == rt->at.s_net && + f->at.s_node == rt->at.s_node) + return f; + } + + return NULL; +} + +static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct ipddp_route __user *rt = ifr->ifr_data; + struct ipddp_route rcp, rcp2, *rp; + + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + + if(copy_from_user(&rcp, rt, sizeof(rcp))) + return -EFAULT; + + switch(cmd) + { + case SIOCADDIPDDPRT: + return ipddp_create(&rcp); + + case SIOCFINDIPDDPRT: + spin_lock_bh(&ipddp_route_lock); + rp = __ipddp_find_route(&rcp); + if (rp) + memcpy(&rcp2, rp, sizeof(rcp2)); + spin_unlock_bh(&ipddp_route_lock); + + if (rp) { + if (copy_to_user(rt, &rcp2, + sizeof(struct ipddp_route))) + return -EFAULT; + return 0; + } else + return -ENOENT; + + case SIOCDELIPDDPRT: + return ipddp_delete(&rcp); + + default: + return -EINVAL; + } +} + +static struct net_device *dev_ipddp; + +MODULE_LICENSE("GPL"); +module_param(ipddp_mode, int, 0); + +static int __init ipddp_init_module(void) +{ + dev_ipddp = ipddp_init(); + if (IS_ERR(dev_ipddp)) + return PTR_ERR(dev_ipddp); + return 0; +} + +static void __exit ipddp_cleanup_module(void) +{ + struct ipddp_route *p; + + unregister_netdev(dev_ipddp); + free_netdev(dev_ipddp); + + while (ipddp_route_list) { + p = ipddp_route_list->next; + kfree(ipddp_route_list); + ipddp_route_list = p; + } +} + +module_init(ipddp_init_module); +module_exit(ipddp_cleanup_module); diff --git a/drivers/net/appletalk/ipddp.h b/drivers/net/appletalk/ipddp.h new file mode 100644 index 000000000000..531519da99a3 --- /dev/null +++ b/drivers/net/appletalk/ipddp.h @@ -0,0 +1,27 @@ +/* + * ipddp.h: Header for IP-over-DDP driver for Linux. + */ + +#ifndef __LINUX_IPDDP_H +#define __LINUX_IPDDP_H + +#ifdef __KERNEL__ + +#define SIOCADDIPDDPRT (SIOCDEVPRIVATE) +#define SIOCDELIPDDPRT (SIOCDEVPRIVATE+1) +#define SIOCFINDIPDDPRT (SIOCDEVPRIVATE+2) + +struct ipddp_route +{ + struct net_device *dev; /* Carrier device */ + __be32 ip; /* IP address */ + struct atalk_addr at; /* Gateway appletalk address */ + int flags; + struct ipddp_route *next; +}; + +#define IPDDP_ENCAP 1 +#define IPDDP_DECAP 2 + +#endif /* __KERNEL__ */ +#endif /* __LINUX_IPDDP_H */ diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c new file mode 100644 index 000000000000..e69eead12ec7 --- /dev/null +++ b/drivers/net/appletalk/ltpc.c @@ -0,0 +1,1288 @@ +/*** ltpc.c -- a driver for the LocalTalk PC card. + * + * Copyright (c) 1995,1996 Bradford W. Johnson + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * This is ALPHA code at best. It may not work for you. It may + * damage your equipment. It may damage your relations with other + * users of your network. Use it at your own risk! + * + * Based in part on: + * skeleton.c by Donald Becker + * dummy.c by Nick Holloway and Alan Cox + * loopback.c by Ross Biro, Fred van Kampen, Donald Becker + * the netatalk source code (UMICH) + * lots of work on the card... + * + * I do not have access to the (proprietary) SDK that goes with the card. + * If you do, I don't want to know about it, and you can probably write + * a better driver yourself anyway. This does mean that the pieces that + * talk to the card are guesswork on my part, so use at your own risk! + * + * This is my first try at writing Linux networking code, and is also + * guesswork. Again, use at your own risk! (Although on this part, I'd + * welcome suggestions) + * + * This is a loadable kernel module which seems to work at my site + * consisting of a 1.2.13 linux box running netatalk 1.3.3, and with + * the kernel support from 1.3.3b2 including patches routing.patch + * and ddp.disappears.from.chooser. In order to run it, you will need + * to patch ddp.c and aarp.c in the kernel, but only a little... + * + * I'm fairly confident that while this is arguably badly written, the + * problems that people experience will be "higher level", that is, with + * complications in the netatalk code. The driver itself doesn't do + * anything terribly complicated -- it pretends to be an ether device + * as far as netatalk is concerned, strips the DDP data out of the ether + * frame and builds a LLAP packet to send out the card. In the other + * direction, it receives LLAP frames from the card and builds a fake + * ether packet that it then tosses up to the networking code. You can + * argue (correctly) that this is an ugly way to do things, but it + * requires a minimal amount of fooling with the code in ddp.c and aarp.c. + * + * The card will do a lot more than is used here -- I *think* it has the + * layers up through ATP. Even if you knew how that part works (which I + * don't) it would be a big job to carve up the kernel ddp code to insert + * things at a higher level, and probably a bad idea... + * + * There are a number of other cards that do LocalTalk on the PC. If + * nobody finds any insurmountable (at the netatalk level) problems + * here, this driver should encourage people to put some work into the + * other cards (some of which I gather are still commercially available) + * and also to put hooks for LocalTalk into the official ddp code. + * + * I welcome comments and suggestions. This is my first try at Linux + * networking stuff, and there are probably lots of things that I did + * suboptimally. + * + ***/ + +/*** + * + * $Log: ltpc.c,v $ + * Revision 1.1.2.1 2000/03/01 05:35:07 jgarzik + * at and tr cleanup + * + * Revision 1.8 1997/01/28 05:44:54 bradford + * Clean up for non-module a little. + * Hacked about a bit to clean things up - Alan Cox + * Probably broken it from the origina 1.8 + * + + * 1998/11/09: David Huggins-Daines + * Cleaned up the initialization code to use the standard autoirq methods, + and to probe for things in the standard order of i/o, irq, dma. This + removes the "reset the reset" hack, because I couldn't figure out an + easy way to get the card to trigger an interrupt after it. + * Added support for passing configuration parameters on the kernel command + line and through insmod + * Changed the device name from "ltalk0" to "lt0", both to conform with the + other localtalk driver, and to clear up the inconsistency between the + module and the non-module versions of the driver :-) + * Added a bunch of comments (I was going to make some enums for the state + codes and the register offsets, but I'm still not sure exactly what their + semantics are) + * Don't poll anymore in interrupt-driven mode + * It seems to work as a module now (as of 2.1.127), but I don't think + I'm responsible for that... + + * + * Revision 1.7 1996/12/12 03:42:33 bradford + * DMA alloc cribbed from 3c505.c. + * + * Revision 1.6 1996/12/12 03:18:58 bradford + * Added virt_to_bus; works in 2.1.13. + * + * Revision 1.5 1996/12/12 03:13:22 root + * xmitQel initialization -- think through better though. + * + * Revision 1.4 1996/06/18 14:55:55 root + * Change names to ltpc. Tabs. Took a shot at dma alloc, + * although more needs to be done eventually. + * + * Revision 1.3 1996/05/22 14:59:39 root + * Change dev->open, dev->close to track dummy.c in 1.99.(around 7) + * + * Revision 1.2 1996/05/22 14:58:24 root + * Change tabs mostly. + * + * Revision 1.1 1996/04/23 04:45:09 root + * Initial revision + * + * Revision 0.16 1996/03/05 15:59:56 root + * Change ARPHRD_LOCALTLK definition to the "real" one. + * + * Revision 0.15 1996/03/05 06:28:30 root + * Changes for kernel 1.3.70. Still need a few patches to kernel, but + * it's getting closer. + * + * Revision 0.14 1996/02/25 17:38:32 root + * More cleanups. Removed query to card on get_stats. + * + * Revision 0.13 1996/02/21 16:27:40 root + * Refix debug_print_skb. Fix mac.raw gotcha that appeared in 1.3.65. + * Clean up receive code a little. + * + * Revision 0.12 1996/02/19 16:34:53 root + * Fix debug_print_skb. Kludge outgoing snet to 0 when using startup + * range. Change debug to mask: 1 for verbose, 2 for higher level stuff + * including packet printing, 4 for lower level (card i/o) stuff. + * + * Revision 0.11 1996/02/12 15:53:38 root + * Added router sends (requires new aarp.c patch) + * + * Revision 0.10 1996/02/11 00:19:35 root + * Change source LTALK_LOGGING debug switch to insmod ... debug=2. + * + * Revision 0.9 1996/02/10 23:59:35 root + * Fixed those fixes for 1.2 -- DANGER! The at.h that comes with netatalk + * has a *different* definition of struct sockaddr_at than the Linux kernel + * does. This is an "insidious and invidious" bug... + * (Actually the preceding comment is false -- it's the atalk.h in the + * ancient atalk-0.06 that's the problem) + * + * Revision 0.8 1996/02/10 19:09:00 root + * Merge 1.3 changes. Tested OK under 1.3.60. + * + * Revision 0.7 1996/02/10 17:56:56 root + * Added debug=1 parameter on insmod for debugging prints. Tried + * to fix timer unload on rmmod, but I don't think that's the problem. + * + * Revision 0.6 1995/12/31 19:01:09 root + * Clean up rmmod, irq comments per feedback from Corin Anderson (Thanks Corey!) + * Clean up initial probing -- sometimes the card wakes up latched in reset. + * + * Revision 0.5 1995/12/22 06:03:44 root + * Added comments in front and cleaned up a bit. + * This version sent out to people. + * + * Revision 0.4 1995/12/18 03:46:44 root + * Return shortDDP to longDDP fake to 0/0. Added command structs. + * + ***/ + +/* ltpc jumpers are: +* +* Interrupts -- set at most one. If none are set, the driver uses +* polled mode. Because the card was developed in the XT era, the +* original documentation refers to IRQ2. Since you'll be running +* this on an AT (or later) class machine, that really means IRQ9. +* +* SW1 IRQ 4 +* SW2 IRQ 3 +* SW3 IRQ 9 (2 in original card documentation only applies to XT) +* +* +* DMA -- choose DMA 1 or 3, and set both corresponding switches. +* +* SW4 DMA 3 +* SW5 DMA 1 +* SW6 DMA 3 +* SW7 DMA 1 +* +* +* I/O address -- choose one. +* +* SW8 220 / 240 +*/ + +/* To have some stuff logged, do +* insmod ltpc.o debug=1 +* +* For a whole bunch of stuff, use higher numbers. +* +* The default is 0, i.e. no messages except for the probe results. +*/ + +/* insmod-tweakable variables */ +static int debug; +#define DEBUG_VERBOSE 1 +#define DEBUG_UPPER 2 +#define DEBUG_LOWER 4 + +static int io; +static int irq; +static int dma; + +#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 + +/* our stuff */ +#include "ltpc.h" + +static DEFINE_SPINLOCK(txqueue_lock); +static DEFINE_SPINLOCK(mbox_lock); + +/* function prototypes */ +static int do_read(struct net_device *dev, void *cbuf, int cbuflen, + void *dbuf, int dbuflen); +static int sendup_buffer (struct net_device *dev); + +/* Dma Memory related stuff, cribbed directly from 3c505.c */ + +static unsigned long dma_mem_alloc(int size) +{ + int order = get_order(size); + + return __get_dma_pages(GFP_KERNEL, order); +} + +/* DMA data buffer, DMA command buffer */ +static unsigned char *ltdmabuf; +static unsigned char *ltdmacbuf; + +/* private struct, holds our appletalk address */ + +struct ltpc_private +{ + struct atalk_addr my_addr; +}; + +/* transmit queue element struct */ + +struct xmitQel { + struct xmitQel *next; + /* command buffer */ + unsigned char *cbuf; + short cbuflen; + /* data buffer */ + unsigned char *dbuf; + short dbuflen; + unsigned char QWrite; /* read or write data */ + unsigned char mailbox; +}; + +/* the transmit queue itself */ + +static struct xmitQel *xmQhd, *xmQtl; + +static void enQ(struct xmitQel *qel) +{ + unsigned long flags; + qel->next = NULL; + + spin_lock_irqsave(&txqueue_lock, flags); + if (xmQtl) { + xmQtl->next = qel; + } else { + xmQhd = qel; + } + xmQtl = qel; + spin_unlock_irqrestore(&txqueue_lock, flags); + + if (debug & DEBUG_LOWER) + printk("enqueued a 0x%02x command\n",qel->cbuf[0]); +} + +static struct xmitQel *deQ(void) +{ + unsigned long flags; + int i; + struct xmitQel *qel=NULL; + + spin_lock_irqsave(&txqueue_lock, flags); + if (xmQhd) { + qel = xmQhd; + xmQhd = qel->next; + if(!xmQhd) xmQtl = NULL; + } + spin_unlock_irqrestore(&txqueue_lock, flags); + + if ((debug & DEBUG_LOWER) && qel) { + int n; + printk(KERN_DEBUG "ltpc: dequeued command "); + n = qel->cbuflen; + if (n>100) n=100; + for(i=0;icbuf[i]); + printk("\n"); + } + + return qel; +} + +/* and... the queue elements we'll be using */ +static struct xmitQel qels[16]; + +/* and their corresponding mailboxes */ +static unsigned char mailbox[16]; +static unsigned char mboxinuse[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +static int wait_timeout(struct net_device *dev, int c) +{ + /* returns true if it stayed c */ + /* this uses base+6, but it's ok */ + int i; + + /* twenty second or so total */ + + for(i=0;i<200000;i++) { + if ( c != inb_p(dev->base_addr+6) ) return 0; + udelay(100); + } + return 1; /* timed out */ +} + +/* get the first free mailbox */ + +static int getmbox(void) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&mbox_lock, flags); + for(i=1;i<16;i++) if(!mboxinuse[i]) { + mboxinuse[i]=1; + spin_unlock_irqrestore(&mbox_lock, flags); + return i; + } + spin_unlock_irqrestore(&mbox_lock, flags); + return 0; +} + +/* read a command from the card */ +static void handlefc(struct net_device *dev) +{ + /* called *only* from idle, non-reentrant */ + int dma = dev->dma; + int base = dev->base_addr; + unsigned long flags; + + + flags=claim_dma_lock(); + disable_dma(dma); + clear_dma_ff(dma); + set_dma_mode(dma,DMA_MODE_READ); + set_dma_addr(dma,virt_to_bus(ltdmacbuf)); + set_dma_count(dma,50); + enable_dma(dma); + release_dma_lock(flags); + + inb_p(base+3); + inb_p(base+2); + + if ( wait_timeout(dev,0xfc) ) printk("timed out in handlefc\n"); +} + +/* read data from the card */ +static void handlefd(struct net_device *dev) +{ + int dma = dev->dma; + int base = dev->base_addr; + unsigned long flags; + + flags=claim_dma_lock(); + disable_dma(dma); + clear_dma_ff(dma); + set_dma_mode(dma,DMA_MODE_READ); + set_dma_addr(dma,virt_to_bus(ltdmabuf)); + set_dma_count(dma,800); + enable_dma(dma); + release_dma_lock(flags); + + inb_p(base+3); + inb_p(base+2); + + if ( wait_timeout(dev,0xfd) ) printk("timed out in handlefd\n"); + sendup_buffer(dev); +} + +static void handlewrite(struct net_device *dev) +{ + /* called *only* from idle, non-reentrant */ + /* on entry, 0xfb and ltdmabuf holds data */ + int dma = dev->dma; + int base = dev->base_addr; + unsigned long flags; + + flags=claim_dma_lock(); + disable_dma(dma); + clear_dma_ff(dma); + set_dma_mode(dma,DMA_MODE_WRITE); + set_dma_addr(dma,virt_to_bus(ltdmabuf)); + set_dma_count(dma,800); + enable_dma(dma); + release_dma_lock(flags); + + inb_p(base+3); + inb_p(base+2); + + if ( wait_timeout(dev,0xfb) ) { + flags=claim_dma_lock(); + printk("timed out in handlewrite, dma res %d\n", + get_dma_residue(dev->dma) ); + release_dma_lock(flags); + } +} + +static void handleread(struct net_device *dev) +{ + /* on entry, 0xfb */ + /* on exit, ltdmabuf holds data */ + int dma = dev->dma; + int base = dev->base_addr; + unsigned long flags; + + + flags=claim_dma_lock(); + disable_dma(dma); + clear_dma_ff(dma); + set_dma_mode(dma,DMA_MODE_READ); + set_dma_addr(dma,virt_to_bus(ltdmabuf)); + set_dma_count(dma,800); + enable_dma(dma); + release_dma_lock(flags); + + inb_p(base+3); + inb_p(base+2); + if ( wait_timeout(dev,0xfb) ) printk("timed out in handleread\n"); +} + +static void handlecommand(struct net_device *dev) +{ + /* on entry, 0xfa and ltdmacbuf holds command */ + int dma = dev->dma; + int base = dev->base_addr; + unsigned long flags; + + flags=claim_dma_lock(); + disable_dma(dma); + clear_dma_ff(dma); + set_dma_mode(dma,DMA_MODE_WRITE); + set_dma_addr(dma,virt_to_bus(ltdmacbuf)); + set_dma_count(dma,50); + enable_dma(dma); + release_dma_lock(flags); + inb_p(base+3); + inb_p(base+2); + if ( wait_timeout(dev,0xfa) ) printk("timed out in handlecommand\n"); +} + +/* ready made command for getting the result from the card */ +static unsigned char rescbuf[2] = {LT_GETRESULT,0}; +static unsigned char resdbuf[2]; + +static int QInIdle; + +/* idle expects to be called with the IRQ line high -- either because of + * an interrupt, or because the line is tri-stated + */ + +static void idle(struct net_device *dev) +{ + unsigned long flags; + int state; + /* FIXME This is initialized to shut the warning up, but I need to + * think this through again. + */ + struct xmitQel *q = NULL; + int oops; + int i; + int base = dev->base_addr; + + spin_lock_irqsave(&txqueue_lock, flags); + if(QInIdle) { + spin_unlock_irqrestore(&txqueue_lock, flags); + return; + } + QInIdle = 1; + spin_unlock_irqrestore(&txqueue_lock, flags); + + /* this tri-states the IRQ line */ + (void) inb_p(base+6); + + oops = 100; + +loop: + if (0>oops--) { + printk("idle: looped too many times\n"); + goto done; + } + + state = inb_p(base+6); + if (state != inb_p(base+6)) goto loop; + + switch(state) { + case 0xfc: + /* incoming command */ + if (debug & DEBUG_LOWER) printk("idle: fc\n"); + handlefc(dev); + break; + case 0xfd: + /* incoming data */ + if(debug & DEBUG_LOWER) printk("idle: fd\n"); + handlefd(dev); + break; + case 0xf9: + /* result ready */ + if (debug & DEBUG_LOWER) printk("idle: f9\n"); + if(!mboxinuse[0]) { + mboxinuse[0] = 1; + qels[0].cbuf = rescbuf; + qels[0].cbuflen = 2; + qels[0].dbuf = resdbuf; + qels[0].dbuflen = 2; + qels[0].QWrite = 0; + qels[0].mailbox = 0; + enQ(&qels[0]); + } + inb_p(dev->base_addr+1); + inb_p(dev->base_addr+0); + if( wait_timeout(dev,0xf9) ) + printk("timed out idle f9\n"); + break; + case 0xf8: + /* ?? */ + if (xmQhd) { + inb_p(dev->base_addr+1); + inb_p(dev->base_addr+0); + if(wait_timeout(dev,0xf8) ) + printk("timed out idle f8\n"); + } else { + goto done; + } + break; + case 0xfa: + /* waiting for command */ + if(debug & DEBUG_LOWER) printk("idle: fa\n"); + if (xmQhd) { + q=deQ(); + memcpy(ltdmacbuf,q->cbuf,q->cbuflen); + ltdmacbuf[1] = q->mailbox; + if (debug>1) { + int n; + printk("ltpc: sent command "); + n = q->cbuflen; + if (n>100) n=100; + for(i=0;iQWrite) { + memcpy(ltdmabuf,q->dbuf,q->dbuflen); + handlewrite(dev); + } else { + handleread(dev); + /* non-zero mailbox numbers are for + commmands, 0 is for GETRESULT + requests */ + if(q->mailbox) { + memcpy(q->dbuf,ltdmabuf,q->dbuflen); + } else { + /* this was a result */ + mailbox[ 0x0f & ltdmabuf[0] ] = ltdmabuf[1]; + mboxinuse[0]=0; + } + } + break; + } + goto loop; + +done: + QInIdle=0; + + /* now set the interrupts back as appropriate */ + /* the first read takes it out of tri-state (but still high) */ + /* the second resets it */ + /* note that after this point, any read of base+6 will + trigger an interrupt */ + + if (dev->irq) { + inb_p(base+7); + inb_p(base+7); + } +} + + +static int do_write(struct net_device *dev, void *cbuf, int cbuflen, + void *dbuf, int dbuflen) +{ + + int i = getmbox(); + int ret; + + if(i) { + qels[i].cbuf = (unsigned char *) cbuf; + qels[i].cbuflen = cbuflen; + qels[i].dbuf = (unsigned char *) dbuf; + qels[i].dbuflen = dbuflen; + qels[i].QWrite = 1; + qels[i].mailbox = i; /* this should be initted rather */ + enQ(&qels[i]); + idle(dev); + ret = mailbox[i]; + mboxinuse[i]=0; + return ret; + } + printk("ltpc: could not allocate mbox\n"); + return -1; +} + +static int do_read(struct net_device *dev, void *cbuf, int cbuflen, + void *dbuf, int dbuflen) +{ + + int i = getmbox(); + int ret; + + if(i) { + qels[i].cbuf = (unsigned char *) cbuf; + qels[i].cbuflen = cbuflen; + qels[i].dbuf = (unsigned char *) dbuf; + qels[i].dbuflen = dbuflen; + qels[i].QWrite = 0; + qels[i].mailbox = i; /* this should be initted rather */ + enQ(&qels[i]); + idle(dev); + ret = mailbox[i]; + mboxinuse[i]=0; + return ret; + } + printk("ltpc: could not allocate mbox\n"); + return -1; +} + +/* end of idle handlers -- what should be seen is do_read, do_write */ + +static struct timer_list ltpc_timer; + +static netdev_tx_t ltpc_xmit(struct sk_buff *skb, struct net_device *dev); + +static int read_30 ( struct net_device *dev) +{ + lt_command c; + c.getflags.command = LT_GETFLAGS; + return do_read(dev, &c, sizeof(c.getflags),&c,0); +} + +static int set_30 (struct net_device *dev,int x) +{ + lt_command c; + c.setflags.command = LT_SETFLAGS; + c.setflags.flags = x; + return do_write(dev, &c, sizeof(c.setflags),&c,0); +} + +/* LLAP to DDP translation */ + +static int sendup_buffer (struct net_device *dev) +{ + /* on entry, command is in ltdmacbuf, data in ltdmabuf */ + /* called from idle, non-reentrant */ + + int dnode, snode, llaptype, len; + int sklen; + struct sk_buff *skb; + struct lt_rcvlap *ltc = (struct lt_rcvlap *) ltdmacbuf; + + if (ltc->command != LT_RCVLAP) { + printk("unknown command 0x%02x from ltpc card\n",ltc->command); + return -1; + } + dnode = ltc->dnode; + snode = ltc->snode; + llaptype = ltc->laptype; + len = ltc->length; + + sklen = len; + if (llaptype == 1) + sklen += 8; /* correct for short ddp */ + if(sklen > 800) { + printk(KERN_INFO "%s: nonsense length in ltpc command 0x14: 0x%08x\n", + dev->name,sklen); + return -1; + } + + if ( (llaptype==0) || (llaptype>2) ) { + printk(KERN_INFO "%s: unknown LLAP type: %d\n",dev->name,llaptype); + return -1; + } + + + skb = dev_alloc_skb(3+sklen); + if (skb == NULL) + { + printk("%s: dropping packet due to memory squeeze.\n", + dev->name); + return -1; + } + skb->dev = dev; + + if (sklen > len) + skb_reserve(skb,8); + skb_put(skb,len+3); + skb->protocol = htons(ETH_P_LOCALTALK); + /* add LLAP header */ + skb->data[0] = dnode; + skb->data[1] = snode; + skb->data[2] = llaptype; + skb_reset_mac_header(skb); /* save pointer to llap header */ + skb_pull(skb,3); + + /* copy ddp(s,e)hdr + contents */ + skb_copy_to_linear_data(skb, ltdmabuf, len); + + skb_reset_transport_header(skb); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + + /* toss it onwards */ + netif_rx(skb); + return 0; +} + +/* the handler for the board interrupt */ + +static irqreturn_t +ltpc_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + + if (dev==NULL) { + printk("ltpc_interrupt: unknown device.\n"); + return IRQ_NONE; + } + + inb_p(dev->base_addr+6); /* disable further interrupts from board */ + + idle(dev); /* handle whatever is coming in */ + + /* idle re-enables interrupts from board */ + + return IRQ_HANDLED; +} + +/*** + * + * The ioctls that the driver responds to are: + * + * SIOCSIFADDR -- do probe using the passed node hint. + * SIOCGIFADDR -- return net, node. + * + * some of this stuff should be done elsewhere. + * + ***/ + +static int ltpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct sockaddr_at *sa = (struct sockaddr_at *) &ifr->ifr_addr; + /* we'll keep the localtalk node address in dev->pa_addr */ + struct ltpc_private *ltpc_priv = netdev_priv(dev); + struct atalk_addr *aa = <pc_priv->my_addr; + struct lt_init c; + int ltflags; + + if(debug & DEBUG_VERBOSE) printk("ltpc_ioctl called\n"); + + switch(cmd) { + case SIOCSIFADDR: + + aa->s_net = sa->sat_addr.s_net; + + /* this does the probe and returns the node addr */ + c.command = LT_INIT; + c.hint = sa->sat_addr.s_node; + + aa->s_node = do_read(dev,&c,sizeof(c),&c,0); + + /* get all llap frames raw */ + ltflags = read_30(dev); + ltflags |= LT_FLAG_ALLLAP; + set_30 (dev,ltflags); + + dev->broadcast[0] = 0xFF; + dev->dev_addr[0] = aa->s_node; + + dev->addr_len=1; + + return 0; + + case SIOCGIFADDR: + + sa->sat_addr.s_net = aa->s_net; + sa->sat_addr.s_node = aa->s_node; + + return 0; + + default: + return -EINVAL; + } +} + +static void set_multicast_list(struct net_device *dev) +{ + /* This needs to be present to keep netatalk happy. */ + /* Actually netatalk needs fixing! */ +} + +static int ltpc_poll_counter; + +static void ltpc_poll(unsigned long l) +{ + struct net_device *dev = (struct net_device *) l; + + del_timer(<pc_timer); + + if(debug & DEBUG_VERBOSE) { + if (!ltpc_poll_counter) { + ltpc_poll_counter = 50; + printk("ltpc poll is alive\n"); + } + ltpc_poll_counter--; + } + + if (!dev) + return; /* we've been downed */ + + /* poll 20 times per second */ + idle(dev); + ltpc_timer.expires = jiffies + HZ/20; + + add_timer(<pc_timer); +} + +/* DDP to LLAP translation */ + +static netdev_tx_t ltpc_xmit(struct sk_buff *skb, struct net_device *dev) +{ + /* in kernel 1.3.xx, on entry skb->data points to ddp header, + * and skb->len is the length of the ddp data + ddp header + */ + int i; + struct lt_sendlap cbuf; + unsigned char *hdr; + + cbuf.command = LT_SENDLAP; + cbuf.dnode = skb->data[0]; + cbuf.laptype = skb->data[2]; + skb_pull(skb,3); /* skip past LLAP header */ + cbuf.length = skb->len; /* this is host order */ + skb_reset_transport_header(skb); + + if(debug & DEBUG_UPPER) { + printk("command "); + for(i=0;i<6;i++) + printk("%02x ",((unsigned char *)&cbuf)[i]); + printk("\n"); + } + + hdr = skb_transport_header(skb); + do_write(dev, &cbuf, sizeof(cbuf), hdr, skb->len); + + if(debug & DEBUG_UPPER) { + printk("sent %d ddp bytes\n",skb->len); + for (i = 0; i < skb->len; i++) + printk("%02x ", hdr[i]); + printk("\n"); + } + + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +/* initialization stuff */ + +static int __init ltpc_probe_dma(int base, int dma) +{ + int want = (dma == 3) ? 2 : (dma == 1) ? 1 : 3; + unsigned long timeout; + unsigned long f; + + if (want & 1) { + if (request_dma(1,"ltpc")) { + want &= ~1; + } else { + f=claim_dma_lock(); + disable_dma(1); + clear_dma_ff(1); + set_dma_mode(1,DMA_MODE_WRITE); + set_dma_addr(1,virt_to_bus(ltdmabuf)); + set_dma_count(1,sizeof(struct lt_mem)); + enable_dma(1); + release_dma_lock(f); + } + } + if (want & 2) { + if (request_dma(3,"ltpc")) { + want &= ~2; + } else { + f=claim_dma_lock(); + disable_dma(3); + clear_dma_ff(3); + set_dma_mode(3,DMA_MODE_WRITE); + set_dma_addr(3,virt_to_bus(ltdmabuf)); + set_dma_count(3,sizeof(struct lt_mem)); + enable_dma(3); + release_dma_lock(f); + } + } + /* set up request */ + + /* FIXME -- do timings better! */ + + ltdmabuf[0] = LT_READMEM; + ltdmabuf[1] = 1; /* mailbox */ + ltdmabuf[2] = 0; ltdmabuf[3] = 0; /* address */ + ltdmabuf[4] = 0; ltdmabuf[5] = 1; /* read 0x0100 bytes */ + ltdmabuf[6] = 0; /* dunno if this is necessary */ + + inb_p(io+1); + inb_p(io+0); + timeout = jiffies+100*HZ/100; + while(time_before(jiffies, timeout)) { + if ( 0xfa == inb_p(io+6) ) break; + } + + inb_p(io+3); + inb_p(io+2); + while(time_before(jiffies, timeout)) { + if ( 0xfb == inb_p(io+6) ) break; + } + + /* release the other dma channel (if we opened both of them) */ + + if ((want & 2) && (get_dma_residue(3)==sizeof(struct lt_mem))) { + want &= ~2; + free_dma(3); + } + + if ((want & 1) && (get_dma_residue(1)==sizeof(struct lt_mem))) { + want &= ~1; + free_dma(1); + } + + if (!want) + return 0; + + return (want & 2) ? 3 : 1; +} + +static const struct net_device_ops ltpc_netdev = { + .ndo_start_xmit = ltpc_xmit, + .ndo_do_ioctl = ltpc_ioctl, + .ndo_set_multicast_list = set_multicast_list, +}; + +struct net_device * __init ltpc_probe(void) +{ + struct net_device *dev; + int err = -ENOMEM; + int x=0,y=0; + int autoirq; + unsigned long f; + unsigned long timeout; + + dev = alloc_ltalkdev(sizeof(struct ltpc_private)); + if (!dev) + goto out; + + /* probe for the I/O port address */ + + if (io != 0x240 && request_region(0x220,8,"ltpc")) { + x = inb_p(0x220+6); + if ( (x!=0xff) && (x>=0xf0) ) { + io = 0x220; + goto got_port; + } + release_region(0x220,8); + } + if (io != 0x220 && request_region(0x240,8,"ltpc")) { + y = inb_p(0x240+6); + if ( (y!=0xff) && (y>=0xf0) ){ + io = 0x240; + goto got_port; + } + release_region(0x240,8); + } + + /* give up in despair */ + printk(KERN_ERR "LocalTalk card not found; 220 = %02x, 240 = %02x.\n", x,y); + err = -ENODEV; + goto out1; + + got_port: + /* probe for the IRQ line */ + if (irq < 2) { + unsigned long irq_mask; + + irq_mask = probe_irq_on(); + /* reset the interrupt line */ + inb_p(io+7); + inb_p(io+7); + /* trigger an interrupt (I hope) */ + inb_p(io+6); + mdelay(2); + autoirq = probe_irq_off(irq_mask); + + if (autoirq == 0) { + printk(KERN_ERR "ltpc: probe at %#x failed to detect IRQ line.\n", io); + } else { + irq = autoirq; + } + } + + /* allocate a DMA buffer */ + ltdmabuf = (unsigned char *) dma_mem_alloc(1000); + if (!ltdmabuf) { + printk(KERN_ERR "ltpc: mem alloc failed\n"); + err = -ENOMEM; + goto out2; + } + + ltdmacbuf = <dmabuf[800]; + + if(debug & DEBUG_VERBOSE) { + printk("ltdmabuf pointer %08lx\n",(unsigned long) ltdmabuf); + } + + /* reset the card */ + + inb_p(io+1); + inb_p(io+3); + + msleep(20); + + inb_p(io+0); + inb_p(io+2); + inb_p(io+7); /* clear reset */ + inb_p(io+4); + inb_p(io+5); + inb_p(io+5); /* enable dma */ + inb_p(io+6); /* tri-state interrupt line */ + + ssleep(1); + + /* now, figure out which dma channel we're using, unless it's + already been specified */ + /* well, 0 is a legal DMA channel, but the LTPC card doesn't + use it... */ + dma = ltpc_probe_dma(io, dma); + if (!dma) { /* no dma channel */ + printk(KERN_ERR "No DMA channel found on ltpc card.\n"); + err = -ENODEV; + goto out3; + } + + /* print out friendly message */ + if(irq) + printk(KERN_INFO "Apple/Farallon LocalTalk-PC card at %03x, IR%d, DMA%d.\n",io,irq,dma); + else + printk(KERN_INFO "Apple/Farallon LocalTalk-PC card at %03x, DMA%d. Using polled mode.\n",io,dma); + + dev->netdev_ops = <pc_netdev; + dev->base_addr = io; + dev->irq = irq; + dev->dma = dma; + + /* the card will want to send a result at this point */ + /* (I think... leaving out this part makes the kernel crash, + so I put it back in...) */ + + f=claim_dma_lock(); + disable_dma(dma); + clear_dma_ff(dma); + set_dma_mode(dma,DMA_MODE_READ); + set_dma_addr(dma,virt_to_bus(ltdmabuf)); + set_dma_count(dma,0x100); + enable_dma(dma); + release_dma_lock(f); + + (void) inb_p(io+3); + (void) inb_p(io+2); + timeout = jiffies+100*HZ/100; + + while(time_before(jiffies, timeout)) { + if( 0xf9 == inb_p(io+6)) + break; + schedule(); + } + + if(debug & DEBUG_VERBOSE) { + printk("setting up timer and irq\n"); + } + + /* grab it and don't let go :-) */ + if (irq && request_irq( irq, ltpc_interrupt, 0, "ltpc", dev) >= 0) + { + (void) inb_p(io+7); /* enable interrupts from board */ + (void) inb_p(io+7); /* and reset irq line */ + } else { + if( irq ) + printk(KERN_ERR "ltpc: IRQ already in use, using polled mode.\n"); + dev->irq = 0; + /* polled mode -- 20 times per second */ + /* this is really, really slow... should it poll more often? */ + init_timer(<pc_timer); + ltpc_timer.function=ltpc_poll; + ltpc_timer.data = (unsigned long) dev; + + ltpc_timer.expires = jiffies + HZ/20; + add_timer(<pc_timer); + } + err = register_netdev(dev); + if (err) + goto out4; + + return NULL; +out4: + del_timer_sync(<pc_timer); + if (dev->irq) + free_irq(dev->irq, dev); +out3: + free_pages((unsigned long)ltdmabuf, get_order(1000)); +out2: + release_region(io, 8); +out1: + free_netdev(dev); +out: + return ERR_PTR(err); +} + +#ifndef MODULE +/* handles "ltpc=io,irq,dma" kernel command lines */ +static int __init ltpc_setup(char *str) +{ + int ints[5]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + if (ints[0] == 0) { + if (str && !strncmp(str, "auto", 4)) { + /* do nothing :-) */ + } + else { + /* usage message */ + printk (KERN_ERR + "ltpc: usage: ltpc=auto|iobase[,irq[,dma]]\n"); + return 0; + } + } else { + io = ints[1]; + if (ints[0] > 1) { + irq = ints[2]; + } + if (ints[0] > 2) { + dma = ints[3]; + } + /* ignore any other parameters */ + } + return 1; +} + +__setup("ltpc=", ltpc_setup); +#endif /* MODULE */ + +static struct net_device *dev_ltpc; + +#ifdef MODULE + +MODULE_LICENSE("GPL"); +module_param(debug, int, 0); +module_param(io, int, 0); +module_param(irq, int, 0); +module_param(dma, int, 0); + + +static int __init ltpc_module_init(void) +{ + if(io == 0) + printk(KERN_NOTICE + "ltpc: Autoprobing is not recommended for modules\n"); + + dev_ltpc = ltpc_probe(); + if (IS_ERR(dev_ltpc)) + return PTR_ERR(dev_ltpc); + return 0; +} +module_init(ltpc_module_init); +#endif + +static void __exit ltpc_cleanup(void) +{ + + if(debug & DEBUG_VERBOSE) printk("unregister_netdev\n"); + unregister_netdev(dev_ltpc); + + ltpc_timer.data = 0; /* signal the poll routine that we're done */ + + del_timer_sync(<pc_timer); + + if(debug & DEBUG_VERBOSE) printk("freeing irq\n"); + + if (dev_ltpc->irq) + free_irq(dev_ltpc->irq, dev_ltpc); + + if(debug & DEBUG_VERBOSE) printk("freeing dma\n"); + + if (dev_ltpc->dma) + free_dma(dev_ltpc->dma); + + if(debug & DEBUG_VERBOSE) printk("freeing ioaddr\n"); + + if (dev_ltpc->base_addr) + release_region(dev_ltpc->base_addr,8); + + free_netdev(dev_ltpc); + + if(debug & DEBUG_VERBOSE) printk("free_pages\n"); + + free_pages( (unsigned long) ltdmabuf, get_order(1000)); + + if(debug & DEBUG_VERBOSE) printk("returning from cleanup_module\n"); +} + +module_exit(ltpc_cleanup); diff --git a/drivers/net/appletalk/ltpc.h b/drivers/net/appletalk/ltpc.h new file mode 100644 index 000000000000..cd30544a3729 --- /dev/null +++ b/drivers/net/appletalk/ltpc.h @@ -0,0 +1,73 @@ +/*** ltpc.h + * + * + ***/ + +#define LT_GETRESULT 0x00 +#define LT_WRITEMEM 0x01 +#define LT_READMEM 0x02 +#define LT_GETFLAGS 0x04 +#define LT_SETFLAGS 0x05 +#define LT_INIT 0x10 +#define LT_SENDLAP 0x13 +#define LT_RCVLAP 0x14 + +/* the flag that we care about */ +#define LT_FLAG_ALLLAP 0x04 + +struct lt_getresult { + unsigned char command; + unsigned char mailbox; +}; + +struct lt_mem { + unsigned char command; + unsigned char mailbox; + unsigned short addr; /* host order */ + unsigned short length; /* host order */ +}; + +struct lt_setflags { + unsigned char command; + unsigned char mailbox; + unsigned char flags; +}; + +struct lt_getflags { + unsigned char command; + unsigned char mailbox; +}; + +struct lt_init { + unsigned char command; + unsigned char mailbox; + unsigned char hint; +}; + +struct lt_sendlap { + unsigned char command; + unsigned char mailbox; + unsigned char dnode; + unsigned char laptype; + unsigned short length; /* host order */ +}; + +struct lt_rcvlap { + unsigned char command; + unsigned char dnode; + unsigned char snode; + unsigned char laptype; + unsigned short length; /* host order */ +}; + +union lt_command { + struct lt_getresult getresult; + struct lt_mem mem; + struct lt_setflags setflags; + struct lt_getflags getflags; + struct lt_init init; + struct lt_sendlap sendlap; + struct lt_rcvlap rcvlap; +}; +typedef union lt_command lt_command; + diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 584d4e264807..b80755da5394 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -169,8 +169,6 @@ source "drivers/staging/bcm/Kconfig" source "drivers/staging/ft1000/Kconfig" -source "drivers/staging/appletalk/Kconfig" - source "drivers/staging/intel_sst/Kconfig" source "drivers/staging/speakup/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 88f0c64e7e58..fd26509e5efa 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -65,7 +65,6 @@ obj-$(CONFIG_ATH6K_LEGACY) += ath6kl/ obj-$(CONFIG_USB_ENESTORAGE) += keucr/ obj-$(CONFIG_BCM_WIMAX) += bcm/ obj-$(CONFIG_FT1000) += ft1000/ -obj-$(CONFIG_DEV_APPLETALK) += appletalk/ obj-$(CONFIG_SND_INTEL_SST) += intel_sst/ obj-$(CONFIG_SPEAKUP) += speakup/ obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/ diff --git a/drivers/staging/appletalk/Kconfig b/drivers/staging/appletalk/Kconfig deleted file mode 100644 index 0b376a990972..000000000000 --- a/drivers/staging/appletalk/Kconfig +++ /dev/null @@ -1,126 +0,0 @@ -# -# Appletalk driver configuration -# -config ATALK - tristate "Appletalk protocol support" - depends on BKL # waiting to be removed from net/appletalk/ddp.c - select LLC - ---help--- - AppleTalk is the protocol that Apple computers can use to communicate - on a network. If your Linux box is connected to such a network and you - wish to connect to it, say Y. You will need to use the netatalk package - so that your Linux box can act as a print and file server for Macs as - well as access AppleTalk printers. Check out - on the WWW for details. - EtherTalk is the name used for AppleTalk over Ethernet and the - cheaper and slower LocalTalk is AppleTalk over a proprietary Apple - network using serial links. EtherTalk and LocalTalk are fully - supported by Linux. - - General information about how to connect Linux, Windows machines and - Macs is on the WWW at . The - NET3-4-HOWTO, available from - , contains valuable - information as well. - - To compile this driver as a module, choose M here: the module will be - called appletalk. You almost certainly want to compile it as a - module so you can restart your AppleTalk stack without rebooting - your machine. I hear that the GNU boycott of Apple is over, so - even politically correct people are allowed to say Y here. - -config DEV_APPLETALK - tristate "Appletalk interfaces support" - depends on ATALK - help - AppleTalk is the protocol that Apple computers can use to communicate - on a network. If your Linux box is connected to such a network, and wish - to do IP over it, or you have a LocalTalk card and wish to use it to - connect to the AppleTalk network, say Y. - - -config LTPC - tristate "Apple/Farallon LocalTalk PC support" - depends on DEV_APPLETALK && (ISA || EISA) && ISA_DMA_API - help - This allows you to use the AppleTalk PC card to connect to LocalTalk - networks. The card is also known as the Farallon PhoneNet PC card. - If you are in doubt, this card is the one with the 65C02 chip on it. - You also need version 1.3.3 or later of the netatalk package. - This driver is experimental, which means that it may not work. - See the file . - -config COPS - tristate "COPS LocalTalk PC support" - depends on DEV_APPLETALK && (ISA || EISA) - help - This allows you to use COPS AppleTalk cards to connect to LocalTalk - networks. You also need version 1.3.3 or later of the netatalk - package. This driver is experimental, which means that it may not - work. This driver will only work if you choose "AppleTalk DDP" - networking support, above. - Please read the file . - -config COPS_DAYNA - bool "Dayna firmware support" - depends on COPS - help - Support COPS compatible cards with Dayna style firmware (Dayna - DL2000/ Daynatalk/PC (half length), COPS LT-95, Farallon PhoneNET PC - III, Farallon PhoneNET PC II). - -config COPS_TANGENT - bool "Tangent firmware support" - depends on COPS - help - Support COPS compatible cards with Tangent style firmware (Tangent - ATB_II, Novell NL-1000, Daystar Digital LT-200. - -config IPDDP - tristate "Appletalk-IP driver support" - depends on DEV_APPLETALK && ATALK - ---help--- - This allows IP networking for users who only have AppleTalk - networking available. This feature is experimental. With this - driver, you can encapsulate IP inside AppleTalk (e.g. if your Linux - box is stuck on an AppleTalk only network) or decapsulate (e.g. if - you want your Linux box to act as an Internet gateway for a zoo of - AppleTalk connected Macs). Please see the file - for more information. - - If you say Y here, the AppleTalk-IP support will be compiled into - the kernel. In this case, you can either use encapsulation or - decapsulation, but not both. With the following two questions, you - decide which one you want. - - To compile the AppleTalk-IP support as a module, choose M here: the - module will be called ipddp. - In this case, you will be able to use both encapsulation and - decapsulation simultaneously, by loading two copies of the module - and specifying different values for the module option ipddp_mode. - -config IPDDP_ENCAP - bool "IP to Appletalk-IP Encapsulation support" - depends on IPDDP - help - If you say Y here, the AppleTalk-IP code will be able to encapsulate - IP packets inside AppleTalk frames; this is useful if your Linux box - is stuck on an AppleTalk network (which hopefully contains a - decapsulator somewhere). Please see - for more information. If - you said Y to "AppleTalk-IP driver support" above and you say Y - here, then you cannot say Y to "AppleTalk-IP to IP Decapsulation - support", below. - -config IPDDP_DECAP - bool "Appletalk-IP to IP Decapsulation support" - depends on IPDDP - help - If you say Y here, the AppleTalk-IP code will be able to decapsulate - AppleTalk-IP frames to IP packets; this is useful if you want your - Linux box to act as an Internet gateway for an AppleTalk network. - Please see for more - information. If you said Y to "AppleTalk-IP driver support" above - and you say Y here, then you cannot say Y to "IP to AppleTalk-IP - Encapsulation support", above. - diff --git a/drivers/staging/appletalk/Makefile b/drivers/staging/appletalk/Makefile deleted file mode 100644 index 2a5129a5c6bc..000000000000 --- a/drivers/staging/appletalk/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# -# Makefile for drivers/staging/appletalk -# -obj-$(CONFIG_ATALK) += appletalk.o - -appletalk-y := aarp.o ddp.o dev.o -appletalk-$(CONFIG_PROC_FS) += atalk_proc.o -appletalk-$(CONFIG_SYSCTL) += sysctl_net_atalk.o - -obj-$(CONFIG_IPDDP) += ipddp.o -obj-$(CONFIG_COPS) += cops.o -obj-$(CONFIG_LTPC) += ltpc.o diff --git a/drivers/staging/appletalk/aarp.c b/drivers/staging/appletalk/aarp.c deleted file mode 100644 index 7163a1dd501f..000000000000 --- a/drivers/staging/appletalk/aarp.c +++ /dev/null @@ -1,1063 +0,0 @@ -/* - * AARP: An implementation of the AppleTalk AARP protocol for - * Ethernet 'ELAP'. - * - * Alan Cox - * - * This doesn't fit cleanly with the IP arp. Potentially we can use - * the generic neighbour discovery code to clean this up. - * - * FIXME: - * We ought to handle the retransmits with a single list and a - * separate fast timer for when it is needed. - * Use neighbour discovery code. - * Token Ring Support. - * - * 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. - * - * - * References: - * Inside AppleTalk (2nd Ed). - * Fixes: - * Jaume Grau - flush caches on AARP_PROBE - * Rob Newberry - Added proxy AARP and AARP proc fs, - * moved probing from DDP module. - * Arnaldo C. Melo - don't mangle rx packets - * - */ - -#include -#include -#include -#include -#include -#include "atalk.h" -#include -#include -#include -#include - -int sysctl_aarp_expiry_time = AARP_EXPIRY_TIME; -int sysctl_aarp_tick_time = AARP_TICK_TIME; -int sysctl_aarp_retransmit_limit = AARP_RETRANSMIT_LIMIT; -int sysctl_aarp_resolve_time = AARP_RESOLVE_TIME; - -/* Lists of aarp entries */ -/** - * struct aarp_entry - AARP entry - * @last_sent - Last time we xmitted the aarp request - * @packet_queue - Queue of frames wait for resolution - * @status - Used for proxy AARP - * expires_at - Entry expiry time - * target_addr - DDP Address - * dev - Device to use - * hwaddr - Physical i/f address of target/router - * xmit_count - When this hits 10 we give up - * next - Next entry in chain - */ -struct aarp_entry { - /* These first two are only used for unresolved entries */ - unsigned long last_sent; - struct sk_buff_head packet_queue; - int status; - unsigned long expires_at; - struct atalk_addr target_addr; - struct net_device *dev; - char hwaddr[6]; - unsigned short xmit_count; - struct aarp_entry *next; -}; - -/* Hashed list of resolved, unresolved and proxy entries */ -static struct aarp_entry *resolved[AARP_HASH_SIZE]; -static struct aarp_entry *unresolved[AARP_HASH_SIZE]; -static struct aarp_entry *proxies[AARP_HASH_SIZE]; -static int unresolved_count; - -/* One lock protects it all. */ -static DEFINE_RWLOCK(aarp_lock); - -/* Used to walk the list and purge/kick entries. */ -static struct timer_list aarp_timer; - -/* - * Delete an aarp queue - * - * Must run under aarp_lock. - */ -static void __aarp_expire(struct aarp_entry *a) -{ - skb_queue_purge(&a->packet_queue); - kfree(a); -} - -/* - * Send an aarp queue entry request - * - * Must run under aarp_lock. - */ -static void __aarp_send_query(struct aarp_entry *a) -{ - static unsigned char aarp_eth_multicast[ETH_ALEN] = - { 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; - struct net_device *dev = a->dev; - struct elapaarp *eah; - int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length; - struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); - struct atalk_addr *sat = atalk_find_dev_addr(dev); - - if (!skb) - return; - - if (!sat) { - kfree_skb(skb); - return; - } - - /* Set up the buffer */ - skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - skb_put(skb, sizeof(*eah)); - skb->protocol = htons(ETH_P_ATALK); - skb->dev = dev; - eah = aarp_hdr(skb); - - /* Set up the ARP */ - eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); - eah->pa_type = htons(ETH_P_ATALK); - eah->hw_len = ETH_ALEN; - eah->pa_len = AARP_PA_ALEN; - eah->function = htons(AARP_REQUEST); - - memcpy(eah->hw_src, dev->dev_addr, ETH_ALEN); - - eah->pa_src_zero = 0; - eah->pa_src_net = sat->s_net; - eah->pa_src_node = sat->s_node; - - memset(eah->hw_dst, '\0', ETH_ALEN); - - eah->pa_dst_zero = 0; - eah->pa_dst_net = a->target_addr.s_net; - eah->pa_dst_node = a->target_addr.s_node; - - /* Send it */ - aarp_dl->request(aarp_dl, skb, aarp_eth_multicast); - /* Update the sending count */ - a->xmit_count++; - a->last_sent = jiffies; -} - -/* This runs under aarp_lock and in softint context, so only atomic memory - * allocations can be used. */ -static void aarp_send_reply(struct net_device *dev, struct atalk_addr *us, - struct atalk_addr *them, unsigned char *sha) -{ - struct elapaarp *eah; - int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length; - struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); - - if (!skb) - return; - - /* Set up the buffer */ - skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - skb_put(skb, sizeof(*eah)); - skb->protocol = htons(ETH_P_ATALK); - skb->dev = dev; - eah = aarp_hdr(skb); - - /* Set up the ARP */ - eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); - eah->pa_type = htons(ETH_P_ATALK); - eah->hw_len = ETH_ALEN; - eah->pa_len = AARP_PA_ALEN; - eah->function = htons(AARP_REPLY); - - memcpy(eah->hw_src, dev->dev_addr, ETH_ALEN); - - eah->pa_src_zero = 0; - eah->pa_src_net = us->s_net; - eah->pa_src_node = us->s_node; - - if (!sha) - memset(eah->hw_dst, '\0', ETH_ALEN); - else - memcpy(eah->hw_dst, sha, ETH_ALEN); - - eah->pa_dst_zero = 0; - eah->pa_dst_net = them->s_net; - eah->pa_dst_node = them->s_node; - - /* Send it */ - aarp_dl->request(aarp_dl, skb, sha); -} - -/* - * Send probe frames. Called from aarp_probe_network and - * aarp_proxy_probe_network. - */ - -static void aarp_send_probe(struct net_device *dev, struct atalk_addr *us) -{ - struct elapaarp *eah; - int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length; - struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); - static unsigned char aarp_eth_multicast[ETH_ALEN] = - { 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; - - if (!skb) - return; - - /* Set up the buffer */ - skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - skb_put(skb, sizeof(*eah)); - skb->protocol = htons(ETH_P_ATALK); - skb->dev = dev; - eah = aarp_hdr(skb); - - /* Set up the ARP */ - eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); - eah->pa_type = htons(ETH_P_ATALK); - eah->hw_len = ETH_ALEN; - eah->pa_len = AARP_PA_ALEN; - eah->function = htons(AARP_PROBE); - - memcpy(eah->hw_src, dev->dev_addr, ETH_ALEN); - - eah->pa_src_zero = 0; - eah->pa_src_net = us->s_net; - eah->pa_src_node = us->s_node; - - memset(eah->hw_dst, '\0', ETH_ALEN); - - eah->pa_dst_zero = 0; - eah->pa_dst_net = us->s_net; - eah->pa_dst_node = us->s_node; - - /* Send it */ - aarp_dl->request(aarp_dl, skb, aarp_eth_multicast); -} - -/* - * Handle an aarp timer expire - * - * Must run under the aarp_lock. - */ - -static void __aarp_expire_timer(struct aarp_entry **n) -{ - struct aarp_entry *t; - - while (*n) - /* Expired ? */ - if (time_after(jiffies, (*n)->expires_at)) { - t = *n; - *n = (*n)->next; - __aarp_expire(t); - } else - n = &((*n)->next); -} - -/* - * Kick all pending requests 5 times a second. - * - * Must run under the aarp_lock. - */ -static void __aarp_kick(struct aarp_entry **n) -{ - struct aarp_entry *t; - - while (*n) - /* Expired: if this will be the 11th tx, we delete instead. */ - if ((*n)->xmit_count >= sysctl_aarp_retransmit_limit) { - t = *n; - *n = (*n)->next; - __aarp_expire(t); - } else { - __aarp_send_query(*n); - n = &((*n)->next); - } -} - -/* - * A device has gone down. Take all entries referring to the device - * and remove them. - * - * Must run under the aarp_lock. - */ -static void __aarp_expire_device(struct aarp_entry **n, struct net_device *dev) -{ - struct aarp_entry *t; - - while (*n) - if ((*n)->dev == dev) { - t = *n; - *n = (*n)->next; - __aarp_expire(t); - } else - n = &((*n)->next); -} - -/* Handle the timer event */ -static void aarp_expire_timeout(unsigned long unused) -{ - int ct; - - write_lock_bh(&aarp_lock); - - for (ct = 0; ct < AARP_HASH_SIZE; ct++) { - __aarp_expire_timer(&resolved[ct]); - __aarp_kick(&unresolved[ct]); - __aarp_expire_timer(&unresolved[ct]); - __aarp_expire_timer(&proxies[ct]); - } - - write_unlock_bh(&aarp_lock); - mod_timer(&aarp_timer, jiffies + - (unresolved_count ? sysctl_aarp_tick_time : - sysctl_aarp_expiry_time)); -} - -/* Network device notifier chain handler. */ -static int aarp_device_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - struct net_device *dev = ptr; - int ct; - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (event == NETDEV_DOWN) { - write_lock_bh(&aarp_lock); - - for (ct = 0; ct < AARP_HASH_SIZE; ct++) { - __aarp_expire_device(&resolved[ct], dev); - __aarp_expire_device(&unresolved[ct], dev); - __aarp_expire_device(&proxies[ct], dev); - } - - write_unlock_bh(&aarp_lock); - } - return NOTIFY_DONE; -} - -/* Expire all entries in a hash chain */ -static void __aarp_expire_all(struct aarp_entry **n) -{ - struct aarp_entry *t; - - while (*n) { - t = *n; - *n = (*n)->next; - __aarp_expire(t); - } -} - -/* Cleanup all hash chains -- module unloading */ -static void aarp_purge(void) -{ - int ct; - - write_lock_bh(&aarp_lock); - for (ct = 0; ct < AARP_HASH_SIZE; ct++) { - __aarp_expire_all(&resolved[ct]); - __aarp_expire_all(&unresolved[ct]); - __aarp_expire_all(&proxies[ct]); - } - write_unlock_bh(&aarp_lock); -} - -/* - * Create a new aarp entry. This must use GFP_ATOMIC because it - * runs while holding spinlocks. - */ -static struct aarp_entry *aarp_alloc(void) -{ - struct aarp_entry *a = kmalloc(sizeof(*a), GFP_ATOMIC); - - if (a) - skb_queue_head_init(&a->packet_queue); - return a; -} - -/* - * Find an entry. We might return an expired but not yet purged entry. We - * don't care as it will do no harm. - * - * This must run under the aarp_lock. - */ -static struct aarp_entry *__aarp_find_entry(struct aarp_entry *list, - struct net_device *dev, - struct atalk_addr *sat) -{ - while (list) { - if (list->target_addr.s_net == sat->s_net && - list->target_addr.s_node == sat->s_node && - list->dev == dev) - break; - list = list->next; - } - - return list; -} - -/* Called from the DDP code, and thus must be exported. */ -void aarp_proxy_remove(struct net_device *dev, struct atalk_addr *sa) -{ - int hash = sa->s_node % (AARP_HASH_SIZE - 1); - struct aarp_entry *a; - - write_lock_bh(&aarp_lock); - - a = __aarp_find_entry(proxies[hash], dev, sa); - if (a) - a->expires_at = jiffies - 1; - - write_unlock_bh(&aarp_lock); -} - -/* This must run under aarp_lock. */ -static struct atalk_addr *__aarp_proxy_find(struct net_device *dev, - struct atalk_addr *sa) -{ - int hash = sa->s_node % (AARP_HASH_SIZE - 1); - struct aarp_entry *a = __aarp_find_entry(proxies[hash], dev, sa); - - return a ? sa : NULL; -} - -/* - * Probe a Phase 1 device or a device that requires its Net:Node to - * be set via an ioctl. - */ -static void aarp_send_probe_phase1(struct atalk_iface *iface) -{ - struct ifreq atreq; - struct sockaddr_at *sa = (struct sockaddr_at *)&atreq.ifr_addr; - const struct net_device_ops *ops = iface->dev->netdev_ops; - - sa->sat_addr.s_node = iface->address.s_node; - sa->sat_addr.s_net = ntohs(iface->address.s_net); - - /* We pass the Net:Node to the drivers/cards by a Device ioctl. */ - if (!(ops->ndo_do_ioctl(iface->dev, &atreq, SIOCSIFADDR))) { - ops->ndo_do_ioctl(iface->dev, &atreq, SIOCGIFADDR); - if (iface->address.s_net != htons(sa->sat_addr.s_net) || - iface->address.s_node != sa->sat_addr.s_node) - iface->status |= ATIF_PROBE_FAIL; - - iface->address.s_net = htons(sa->sat_addr.s_net); - iface->address.s_node = sa->sat_addr.s_node; - } -} - - -void aarp_probe_network(struct atalk_iface *atif) -{ - if (atif->dev->type == ARPHRD_LOCALTLK || - atif->dev->type == ARPHRD_PPP) - aarp_send_probe_phase1(atif); - else { - unsigned int count; - - for (count = 0; count < AARP_RETRANSMIT_LIMIT; count++) { - aarp_send_probe(atif->dev, &atif->address); - - /* Defer 1/10th */ - msleep(100); - - if (atif->status & ATIF_PROBE_FAIL) - break; - } - } -} - -int aarp_proxy_probe_network(struct atalk_iface *atif, struct atalk_addr *sa) -{ - int hash, retval = -EPROTONOSUPPORT; - struct aarp_entry *entry; - unsigned int count; - - /* - * we don't currently support LocalTalk or PPP for proxy AARP; - * if someone wants to try and add it, have fun - */ - if (atif->dev->type == ARPHRD_LOCALTLK || - atif->dev->type == ARPHRD_PPP) - goto out; - - /* - * create a new AARP entry with the flags set to be published -- - * we need this one to hang around even if it's in use - */ - entry = aarp_alloc(); - retval = -ENOMEM; - if (!entry) - goto out; - - entry->expires_at = -1; - entry->status = ATIF_PROBE; - entry->target_addr.s_node = sa->s_node; - entry->target_addr.s_net = sa->s_net; - entry->dev = atif->dev; - - write_lock_bh(&aarp_lock); - - hash = sa->s_node % (AARP_HASH_SIZE - 1); - entry->next = proxies[hash]; - proxies[hash] = entry; - - for (count = 0; count < AARP_RETRANSMIT_LIMIT; count++) { - aarp_send_probe(atif->dev, sa); - - /* Defer 1/10th */ - write_unlock_bh(&aarp_lock); - msleep(100); - write_lock_bh(&aarp_lock); - - if (entry->status & ATIF_PROBE_FAIL) - break; - } - - if (entry->status & ATIF_PROBE_FAIL) { - entry->expires_at = jiffies - 1; /* free the entry */ - retval = -EADDRINUSE; /* return network full */ - } else { /* clear the probing flag */ - entry->status &= ~ATIF_PROBE; - retval = 1; - } - - write_unlock_bh(&aarp_lock); -out: - return retval; -} - -/* Send a DDP frame */ -int aarp_send_ddp(struct net_device *dev, struct sk_buff *skb, - struct atalk_addr *sa, void *hwaddr) -{ - static char ddp_eth_multicast[ETH_ALEN] = - { 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; - int hash; - struct aarp_entry *a; - - skb_reset_network_header(skb); - - /* Check for LocalTalk first */ - if (dev->type == ARPHRD_LOCALTLK) { - struct atalk_addr *at = atalk_find_dev_addr(dev); - struct ddpehdr *ddp = (struct ddpehdr *)skb->data; - int ft = 2; - - /* - * Compressible ? - * - * IFF: src_net == dest_net == device_net - * (zero matches anything) - */ - - if ((!ddp->deh_snet || at->s_net == ddp->deh_snet) && - (!ddp->deh_dnet || at->s_net == ddp->deh_dnet)) { - skb_pull(skb, sizeof(*ddp) - 4); - - /* - * The upper two remaining bytes are the port - * numbers we just happen to need. Now put the - * length in the lower two. - */ - *((__be16 *)skb->data) = htons(skb->len); - ft = 1; - } - /* - * Nice and easy. No AARP type protocols occur here so we can - * just shovel it out with a 3 byte LLAP header - */ - - skb_push(skb, 3); - skb->data[0] = sa->s_node; - skb->data[1] = at->s_node; - skb->data[2] = ft; - skb->dev = dev; - goto sendit; - } - - /* On a PPP link we neither compress nor aarp. */ - if (dev->type == ARPHRD_PPP) { - skb->protocol = htons(ETH_P_PPPTALK); - skb->dev = dev; - goto sendit; - } - - /* Non ELAP we cannot do. */ - if (dev->type != ARPHRD_ETHER) - goto free_it; - - skb->dev = dev; - skb->protocol = htons(ETH_P_ATALK); - hash = sa->s_node % (AARP_HASH_SIZE - 1); - - /* Do we have a resolved entry? */ - if (sa->s_node == ATADDR_BCAST) { - /* Send it */ - ddp_dl->request(ddp_dl, skb, ddp_eth_multicast); - goto sent; - } - - write_lock_bh(&aarp_lock); - a = __aarp_find_entry(resolved[hash], dev, sa); - - if (a) { /* Return 1 and fill in the address */ - a->expires_at = jiffies + (sysctl_aarp_expiry_time * 10); - ddp_dl->request(ddp_dl, skb, a->hwaddr); - write_unlock_bh(&aarp_lock); - goto sent; - } - - /* Do we have an unresolved entry: This is the less common path */ - a = __aarp_find_entry(unresolved[hash], dev, sa); - if (a) { /* Queue onto the unresolved queue */ - skb_queue_tail(&a->packet_queue, skb); - goto out_unlock; - } - - /* Allocate a new entry */ - a = aarp_alloc(); - if (!a) { - /* Whoops slipped... good job it's an unreliable protocol 8) */ - write_unlock_bh(&aarp_lock); - goto free_it; - } - - /* Set up the queue */ - skb_queue_tail(&a->packet_queue, skb); - a->expires_at = jiffies + sysctl_aarp_resolve_time; - a->dev = dev; - a->next = unresolved[hash]; - a->target_addr = *sa; - a->xmit_count = 0; - unresolved[hash] = a; - unresolved_count++; - - /* Send an initial request for the address */ - __aarp_send_query(a); - - /* - * Switch to fast timer if needed (That is if this is the first - * unresolved entry to get added) - */ - - if (unresolved_count == 1) - mod_timer(&aarp_timer, jiffies + sysctl_aarp_tick_time); - - /* Now finally, it is safe to drop the lock. */ -out_unlock: - write_unlock_bh(&aarp_lock); - - /* Tell the ddp layer we have taken over for this frame. */ - goto sent; - -sendit: - if (skb->sk) - skb->priority = skb->sk->sk_priority; - if (dev_queue_xmit(skb)) - goto drop; -sent: - return NET_XMIT_SUCCESS; -free_it: - kfree_skb(skb); -drop: - return NET_XMIT_DROP; -} -EXPORT_SYMBOL(aarp_send_ddp); - -/* - * An entry in the aarp unresolved queue has become resolved. Send - * all the frames queued under it. - * - * Must run under aarp_lock. - */ -static void __aarp_resolved(struct aarp_entry **list, struct aarp_entry *a, - int hash) -{ - struct sk_buff *skb; - - while (*list) - if (*list == a) { - unresolved_count--; - *list = a->next; - - /* Move into the resolved list */ - a->next = resolved[hash]; - resolved[hash] = a; - - /* Kick frames off */ - while ((skb = skb_dequeue(&a->packet_queue)) != NULL) { - a->expires_at = jiffies + - sysctl_aarp_expiry_time * 10; - ddp_dl->request(ddp_dl, skb, a->hwaddr); - } - } else - list = &((*list)->next); -} - -/* - * This is called by the SNAP driver whenever we see an AARP SNAP - * frame. We currently only support Ethernet. - */ -static int aarp_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) -{ - struct elapaarp *ea = aarp_hdr(skb); - int hash, ret = 0; - __u16 function; - struct aarp_entry *a; - struct atalk_addr sa, *ma, da; - struct atalk_iface *ifa; - - if (!net_eq(dev_net(dev), &init_net)) - goto out0; - - /* We only do Ethernet SNAP AARP. */ - if (dev->type != ARPHRD_ETHER) - goto out0; - - /* Frame size ok? */ - if (!skb_pull(skb, sizeof(*ea))) - goto out0; - - function = ntohs(ea->function); - - /* Sanity check fields. */ - if (function < AARP_REQUEST || function > AARP_PROBE || - ea->hw_len != ETH_ALEN || ea->pa_len != AARP_PA_ALEN || - ea->pa_src_zero || ea->pa_dst_zero) - goto out0; - - /* Looks good. */ - hash = ea->pa_src_node % (AARP_HASH_SIZE - 1); - - /* Build an address. */ - sa.s_node = ea->pa_src_node; - sa.s_net = ea->pa_src_net; - - /* Process the packet. Check for replies of me. */ - ifa = atalk_find_dev(dev); - if (!ifa) - goto out1; - - if (ifa->status & ATIF_PROBE && - ifa->address.s_node == ea->pa_dst_node && - ifa->address.s_net == ea->pa_dst_net) { - ifa->status |= ATIF_PROBE_FAIL; /* Fail the probe (in use) */ - goto out1; - } - - /* Check for replies of proxy AARP entries */ - da.s_node = ea->pa_dst_node; - da.s_net = ea->pa_dst_net; - - write_lock_bh(&aarp_lock); - a = __aarp_find_entry(proxies[hash], dev, &da); - - if (a && a->status & ATIF_PROBE) { - a->status |= ATIF_PROBE_FAIL; - /* - * we do not respond to probe or request packets for - * this address while we are probing this address - */ - goto unlock; - } - - switch (function) { - case AARP_REPLY: - if (!unresolved_count) /* Speed up */ - break; - - /* Find the entry. */ - a = __aarp_find_entry(unresolved[hash], dev, &sa); - if (!a || dev != a->dev) - break; - - /* We can fill one in - this is good. */ - memcpy(a->hwaddr, ea->hw_src, ETH_ALEN); - __aarp_resolved(&unresolved[hash], a, hash); - if (!unresolved_count) - mod_timer(&aarp_timer, - jiffies + sysctl_aarp_expiry_time); - break; - - case AARP_REQUEST: - case AARP_PROBE: - - /* - * If it is my address set ma to my address and reply. - * We can treat probe and request the same. Probe - * simply means we shouldn't cache the querying host, - * as in a probe they are proposing an address not - * using one. - * - * Support for proxy-AARP added. We check if the - * address is one of our proxies before we toss the - * packet out. - */ - - sa.s_node = ea->pa_dst_node; - sa.s_net = ea->pa_dst_net; - - /* See if we have a matching proxy. */ - ma = __aarp_proxy_find(dev, &sa); - if (!ma) - ma = &ifa->address; - else { /* We need to make a copy of the entry. */ - da.s_node = sa.s_node; - da.s_net = sa.s_net; - ma = &da; - } - - if (function == AARP_PROBE) { - /* - * A probe implies someone trying to get an - * address. So as a precaution flush any - * entries we have for this address. - */ - a = __aarp_find_entry(resolved[sa.s_node % - (AARP_HASH_SIZE - 1)], - skb->dev, &sa); - - /* - * Make it expire next tick - that avoids us - * getting into a probe/flush/learn/probe/ - * flush/learn cycle during probing of a slow - * to respond host addr. - */ - if (a) { - a->expires_at = jiffies - 1; - mod_timer(&aarp_timer, jiffies + - sysctl_aarp_tick_time); - } - } - - if (sa.s_node != ma->s_node) - break; - - if (sa.s_net && ma->s_net && sa.s_net != ma->s_net) - break; - - sa.s_node = ea->pa_src_node; - sa.s_net = ea->pa_src_net; - - /* aarp_my_address has found the address to use for us. - */ - aarp_send_reply(dev, ma, &sa, ea->hw_src); - break; - } - -unlock: - write_unlock_bh(&aarp_lock); -out1: - ret = 1; -out0: - kfree_skb(skb); - return ret; -} - -static struct notifier_block aarp_notifier = { - .notifier_call = aarp_device_event, -}; - -static unsigned char aarp_snap_id[] = { 0x00, 0x00, 0x00, 0x80, 0xF3 }; - -void __init aarp_proto_init(void) -{ - aarp_dl = register_snap_client(aarp_snap_id, aarp_rcv); - if (!aarp_dl) - printk(KERN_CRIT "Unable to register AARP with SNAP.\n"); - setup_timer(&aarp_timer, aarp_expire_timeout, 0); - aarp_timer.expires = jiffies + sysctl_aarp_expiry_time; - add_timer(&aarp_timer); - register_netdevice_notifier(&aarp_notifier); -} - -/* Remove the AARP entries associated with a device. */ -void aarp_device_down(struct net_device *dev) -{ - int ct; - - write_lock_bh(&aarp_lock); - - for (ct = 0; ct < AARP_HASH_SIZE; ct++) { - __aarp_expire_device(&resolved[ct], dev); - __aarp_expire_device(&unresolved[ct], dev); - __aarp_expire_device(&proxies[ct], dev); - } - - write_unlock_bh(&aarp_lock); -} - -#ifdef CONFIG_PROC_FS -struct aarp_iter_state { - int bucket; - struct aarp_entry **table; -}; - -/* - * Get the aarp entry that is in the chain described - * by the iterator. - * If pos is set then skip till that index. - * pos = 1 is the first entry - */ -static struct aarp_entry *iter_next(struct aarp_iter_state *iter, loff_t *pos) -{ - int ct = iter->bucket; - struct aarp_entry **table = iter->table; - loff_t off = 0; - struct aarp_entry *entry; - - rescan: - while(ct < AARP_HASH_SIZE) { - for (entry = table[ct]; entry; entry = entry->next) { - if (!pos || ++off == *pos) { - iter->table = table; - iter->bucket = ct; - return entry; - } - } - ++ct; - } - - if (table == resolved) { - ct = 0; - table = unresolved; - goto rescan; - } - if (table == unresolved) { - ct = 0; - table = proxies; - goto rescan; - } - return NULL; -} - -static void *aarp_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(aarp_lock) -{ - struct aarp_iter_state *iter = seq->private; - - read_lock_bh(&aarp_lock); - iter->table = resolved; - iter->bucket = 0; - - return *pos ? iter_next(iter, pos) : SEQ_START_TOKEN; -} - -static void *aarp_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct aarp_entry *entry = v; - struct aarp_iter_state *iter = seq->private; - - ++*pos; - - /* first line after header */ - if (v == SEQ_START_TOKEN) - entry = iter_next(iter, NULL); - - /* next entry in current bucket */ - else if (entry->next) - entry = entry->next; - - /* next bucket or table */ - else { - ++iter->bucket; - entry = iter_next(iter, NULL); - } - return entry; -} - -static void aarp_seq_stop(struct seq_file *seq, void *v) - __releases(aarp_lock) -{ - read_unlock_bh(&aarp_lock); -} - -static const char *dt2str(unsigned long ticks) -{ - static char buf[32]; - - sprintf(buf, "%ld.%02ld", ticks / HZ, ((ticks % HZ) * 100 ) / HZ); - - return buf; -} - -static int aarp_seq_show(struct seq_file *seq, void *v) -{ - struct aarp_iter_state *iter = seq->private; - struct aarp_entry *entry = v; - unsigned long now = jiffies; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, - "Address Interface Hardware Address" - " Expires LastSend Retry Status\n"); - else { - seq_printf(seq, "%04X:%02X %-12s", - ntohs(entry->target_addr.s_net), - (unsigned int) entry->target_addr.s_node, - entry->dev ? entry->dev->name : "????"); - seq_printf(seq, "%pM", entry->hwaddr); - seq_printf(seq, " %8s", - dt2str((long)entry->expires_at - (long)now)); - if (iter->table == unresolved) - seq_printf(seq, " %8s %6hu", - dt2str(now - entry->last_sent), - entry->xmit_count); - else - seq_puts(seq, " "); - seq_printf(seq, " %s\n", - (iter->table == resolved) ? "resolved" - : (iter->table == unresolved) ? "unresolved" - : (iter->table == proxies) ? "proxies" - : "unknown"); - } - return 0; -} - -static const struct seq_operations aarp_seq_ops = { - .start = aarp_seq_start, - .next = aarp_seq_next, - .stop = aarp_seq_stop, - .show = aarp_seq_show, -}; - -static int aarp_seq_open(struct inode *inode, struct file *file) -{ - return seq_open_private(file, &aarp_seq_ops, - sizeof(struct aarp_iter_state)); -} - -const struct file_operations atalk_seq_arp_fops = { - .owner = THIS_MODULE, - .open = aarp_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, -}; -#endif - -/* General module cleanup. Called from cleanup_module() in ddp.c. */ -void aarp_cleanup_module(void) -{ - del_timer_sync(&aarp_timer); - unregister_netdevice_notifier(&aarp_notifier); - unregister_snap_client(aarp_dl); - aarp_purge(); -} diff --git a/drivers/staging/appletalk/atalk.h b/drivers/staging/appletalk/atalk.h deleted file mode 100644 index d34c187432ed..000000000000 --- a/drivers/staging/appletalk/atalk.h +++ /dev/null @@ -1,208 +0,0 @@ -#ifndef __LINUX_ATALK_H__ -#define __LINUX_ATALK_H__ - -#include -#include - -/* - * AppleTalk networking structures - * - * The following are directly referenced from the University Of Michigan - * netatalk for compatibility reasons. - */ -#define ATPORT_FIRST 1 -#define ATPORT_RESERVED 128 -#define ATPORT_LAST 254 /* 254 is only legal on localtalk */ -#define ATADDR_ANYNET (__u16)0 -#define ATADDR_ANYNODE (__u8)0 -#define ATADDR_ANYPORT (__u8)0 -#define ATADDR_BCAST (__u8)255 -#define DDP_MAXSZ 587 -#define DDP_MAXHOPS 15 /* 4 bits of hop counter */ - -#define SIOCATALKDIFADDR (SIOCPROTOPRIVATE + 0) - -struct atalk_addr { - __be16 s_net; - __u8 s_node; -}; - -struct sockaddr_at { - sa_family_t sat_family; - __u8 sat_port; - struct atalk_addr sat_addr; - char sat_zero[8]; -}; - -struct atalk_netrange { - __u8 nr_phase; - __be16 nr_firstnet; - __be16 nr_lastnet; -}; - -#ifdef __KERNEL__ - -#include - -struct atalk_route { - struct net_device *dev; - struct atalk_addr target; - struct atalk_addr gateway; - int flags; - struct atalk_route *next; -}; - -/** - * struct atalk_iface - AppleTalk Interface - * @dev - Network device associated with this interface - * @address - Our address - * @status - What are we doing? - * @nets - Associated direct netrange - * @next - next element in the list of interfaces - */ -struct atalk_iface { - struct net_device *dev; - struct atalk_addr address; - int status; -#define ATIF_PROBE 1 /* Probing for an address */ -#define ATIF_PROBE_FAIL 2 /* Probe collided */ - struct atalk_netrange nets; - struct atalk_iface *next; -}; - -struct atalk_sock { - /* struct sock has to be the first member of atalk_sock */ - struct sock sk; - __be16 dest_net; - __be16 src_net; - unsigned char dest_node; - unsigned char src_node; - unsigned char dest_port; - unsigned char src_port; -}; - -static inline struct atalk_sock *at_sk(struct sock *sk) -{ - return (struct atalk_sock *)sk; -} - -struct ddpehdr { - __be16 deh_len_hops; /* lower 10 bits are length, next 4 - hops */ - __be16 deh_sum; - __be16 deh_dnet; - __be16 deh_snet; - __u8 deh_dnode; - __u8 deh_snode; - __u8 deh_dport; - __u8 deh_sport; - /* And netatalk apps expect to stick the type in themselves */ -}; - -static __inline__ struct ddpehdr *ddp_hdr(struct sk_buff *skb) -{ - return (struct ddpehdr *)skb_transport_header(skb); -} - -/* AppleTalk AARP headers */ -struct elapaarp { - __be16 hw_type; -#define AARP_HW_TYPE_ETHERNET 1 -#define AARP_HW_TYPE_TOKENRING 2 - __be16 pa_type; - __u8 hw_len; - __u8 pa_len; -#define AARP_PA_ALEN 4 - __be16 function; -#define AARP_REQUEST 1 -#define AARP_REPLY 2 -#define AARP_PROBE 3 - __u8 hw_src[ETH_ALEN]; - __u8 pa_src_zero; - __be16 pa_src_net; - __u8 pa_src_node; - __u8 hw_dst[ETH_ALEN]; - __u8 pa_dst_zero; - __be16 pa_dst_net; - __u8 pa_dst_node; -} __attribute__ ((packed)); - -static __inline__ struct elapaarp *aarp_hdr(struct sk_buff *skb) -{ - return (struct elapaarp *)skb_transport_header(skb); -} - -/* Not specified - how long till we drop a resolved entry */ -#define AARP_EXPIRY_TIME (5 * 60 * HZ) -/* Size of hash table */ -#define AARP_HASH_SIZE 16 -/* Fast retransmission timer when resolving */ -#define AARP_TICK_TIME (HZ / 5) -/* Send 10 requests then give up (2 seconds) */ -#define AARP_RETRANSMIT_LIMIT 10 -/* - * Some value bigger than total retransmit time + a bit for last reply to - * appear and to stop continual requests - */ -#define AARP_RESOLVE_TIME (10 * HZ) - -extern struct datalink_proto *ddp_dl, *aarp_dl; -extern void aarp_proto_init(void); - -/* Inter module exports */ - -/* Give a device find its atif control structure */ -static inline struct atalk_iface *atalk_find_dev(struct net_device *dev) -{ - return dev->atalk_ptr; -} - -extern struct atalk_addr *atalk_find_dev_addr(struct net_device *dev); -extern struct net_device *atrtr_get_dev(struct atalk_addr *sa); -extern int aarp_send_ddp(struct net_device *dev, - struct sk_buff *skb, - struct atalk_addr *sa, void *hwaddr); -extern void aarp_device_down(struct net_device *dev); -extern void aarp_probe_network(struct atalk_iface *atif); -extern int aarp_proxy_probe_network(struct atalk_iface *atif, - struct atalk_addr *sa); -extern void aarp_proxy_remove(struct net_device *dev, - struct atalk_addr *sa); - -extern void aarp_cleanup_module(void); - -extern struct hlist_head atalk_sockets; -extern rwlock_t atalk_sockets_lock; - -extern struct atalk_route *atalk_routes; -extern rwlock_t atalk_routes_lock; - -extern struct atalk_iface *atalk_interfaces; -extern rwlock_t atalk_interfaces_lock; - -extern struct atalk_route atrtr_default; - -extern const struct file_operations atalk_seq_arp_fops; - -extern int sysctl_aarp_expiry_time; -extern int sysctl_aarp_tick_time; -extern int sysctl_aarp_retransmit_limit; -extern int sysctl_aarp_resolve_time; - -#ifdef CONFIG_SYSCTL -extern void atalk_register_sysctl(void); -extern void atalk_unregister_sysctl(void); -#else -#define atalk_register_sysctl() do { } while(0) -#define atalk_unregister_sysctl() do { } while(0) -#endif - -#ifdef CONFIG_PROC_FS -extern int atalk_proc_init(void); -extern void atalk_proc_exit(void); -#else -#define atalk_proc_init() ({ 0; }) -#define atalk_proc_exit() do { } while(0) -#endif /* CONFIG_PROC_FS */ - -#endif /* __KERNEL__ */ -#endif /* __LINUX_ATALK_H__ */ diff --git a/drivers/staging/appletalk/atalk_proc.c b/drivers/staging/appletalk/atalk_proc.c deleted file mode 100644 index d012ba2e67d1..000000000000 --- a/drivers/staging/appletalk/atalk_proc.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - * atalk_proc.c - proc support for Appletalk - * - * Copyright(c) Arnaldo Carvalho de Melo - * - * 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, version 2. - */ - -#include -#include -#include -#include -#include -#include "atalk.h" - - -static __inline__ struct atalk_iface *atalk_get_interface_idx(loff_t pos) -{ - struct atalk_iface *i; - - for (i = atalk_interfaces; pos && i; i = i->next) - --pos; - - return i; -} - -static void *atalk_seq_interface_start(struct seq_file *seq, loff_t *pos) - __acquires(atalk_interfaces_lock) -{ - loff_t l = *pos; - - read_lock_bh(&atalk_interfaces_lock); - return l ? atalk_get_interface_idx(--l) : SEQ_START_TOKEN; -} - -static void *atalk_seq_interface_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct atalk_iface *i; - - ++*pos; - if (v == SEQ_START_TOKEN) { - i = NULL; - if (atalk_interfaces) - i = atalk_interfaces; - goto out; - } - i = v; - i = i->next; -out: - return i; -} - -static void atalk_seq_interface_stop(struct seq_file *seq, void *v) - __releases(atalk_interfaces_lock) -{ - read_unlock_bh(&atalk_interfaces_lock); -} - -static int atalk_seq_interface_show(struct seq_file *seq, void *v) -{ - struct atalk_iface *iface; - - if (v == SEQ_START_TOKEN) { - seq_puts(seq, "Interface Address Networks " - "Status\n"); - goto out; - } - - iface = v; - seq_printf(seq, "%-16s %04X:%02X %04X-%04X %d\n", - iface->dev->name, ntohs(iface->address.s_net), - iface->address.s_node, ntohs(iface->nets.nr_firstnet), - ntohs(iface->nets.nr_lastnet), iface->status); -out: - return 0; -} - -static __inline__ struct atalk_route *atalk_get_route_idx(loff_t pos) -{ - struct atalk_route *r; - - for (r = atalk_routes; pos && r; r = r->next) - --pos; - - return r; -} - -static void *atalk_seq_route_start(struct seq_file *seq, loff_t *pos) - __acquires(atalk_routes_lock) -{ - loff_t l = *pos; - - read_lock_bh(&atalk_routes_lock); - return l ? atalk_get_route_idx(--l) : SEQ_START_TOKEN; -} - -static void *atalk_seq_route_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct atalk_route *r; - - ++*pos; - if (v == SEQ_START_TOKEN) { - r = NULL; - if (atalk_routes) - r = atalk_routes; - goto out; - } - r = v; - r = r->next; -out: - return r; -} - -static void atalk_seq_route_stop(struct seq_file *seq, void *v) - __releases(atalk_routes_lock) -{ - read_unlock_bh(&atalk_routes_lock); -} - -static int atalk_seq_route_show(struct seq_file *seq, void *v) -{ - struct atalk_route *rt; - - if (v == SEQ_START_TOKEN) { - seq_puts(seq, "Target Router Flags Dev\n"); - goto out; - } - - if (atrtr_default.dev) { - rt = &atrtr_default; - seq_printf(seq, "Default %04X:%02X %-4d %s\n", - ntohs(rt->gateway.s_net), rt->gateway.s_node, - rt->flags, rt->dev->name); - } - - rt = v; - seq_printf(seq, "%04X:%02X %04X:%02X %-4d %s\n", - ntohs(rt->target.s_net), rt->target.s_node, - ntohs(rt->gateway.s_net), rt->gateway.s_node, - rt->flags, rt->dev->name); -out: - return 0; -} - -static void *atalk_seq_socket_start(struct seq_file *seq, loff_t *pos) - __acquires(atalk_sockets_lock) -{ - read_lock_bh(&atalk_sockets_lock); - return seq_hlist_start_head(&atalk_sockets, *pos); -} - -static void *atalk_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_hlist_next(v, &atalk_sockets, pos); -} - -static void atalk_seq_socket_stop(struct seq_file *seq, void *v) - __releases(atalk_sockets_lock) -{ - read_unlock_bh(&atalk_sockets_lock); -} - -static int atalk_seq_socket_show(struct seq_file *seq, void *v) -{ - struct sock *s; - struct atalk_sock *at; - - if (v == SEQ_START_TOKEN) { - seq_printf(seq, "Type Local_addr Remote_addr Tx_queue " - "Rx_queue St UID\n"); - goto out; - } - - s = sk_entry(v); - at = at_sk(s); - - seq_printf(seq, "%02X %04X:%02X:%02X %04X:%02X:%02X %08X:%08X " - "%02X %d\n", - s->sk_type, ntohs(at->src_net), at->src_node, at->src_port, - ntohs(at->dest_net), at->dest_node, at->dest_port, - sk_wmem_alloc_get(s), - sk_rmem_alloc_get(s), - s->sk_state, SOCK_INODE(s->sk_socket)->i_uid); -out: - return 0; -} - -static const struct seq_operations atalk_seq_interface_ops = { - .start = atalk_seq_interface_start, - .next = atalk_seq_interface_next, - .stop = atalk_seq_interface_stop, - .show = atalk_seq_interface_show, -}; - -static const struct seq_operations atalk_seq_route_ops = { - .start = atalk_seq_route_start, - .next = atalk_seq_route_next, - .stop = atalk_seq_route_stop, - .show = atalk_seq_route_show, -}; - -static const struct seq_operations atalk_seq_socket_ops = { - .start = atalk_seq_socket_start, - .next = atalk_seq_socket_next, - .stop = atalk_seq_socket_stop, - .show = atalk_seq_socket_show, -}; - -static int atalk_seq_interface_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &atalk_seq_interface_ops); -} - -static int atalk_seq_route_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &atalk_seq_route_ops); -} - -static int atalk_seq_socket_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &atalk_seq_socket_ops); -} - -static const struct file_operations atalk_seq_interface_fops = { - .owner = THIS_MODULE, - .open = atalk_seq_interface_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static const struct file_operations atalk_seq_route_fops = { - .owner = THIS_MODULE, - .open = atalk_seq_route_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static const struct file_operations atalk_seq_socket_fops = { - .owner = THIS_MODULE, - .open = atalk_seq_socket_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static struct proc_dir_entry *atalk_proc_dir; - -int __init atalk_proc_init(void) -{ - struct proc_dir_entry *p; - int rc = -ENOMEM; - - atalk_proc_dir = proc_mkdir("atalk", init_net.proc_net); - if (!atalk_proc_dir) - goto out; - - p = proc_create("interface", S_IRUGO, atalk_proc_dir, - &atalk_seq_interface_fops); - if (!p) - goto out_interface; - - p = proc_create("route", S_IRUGO, atalk_proc_dir, - &atalk_seq_route_fops); - if (!p) - goto out_route; - - p = proc_create("socket", S_IRUGO, atalk_proc_dir, - &atalk_seq_socket_fops); - if (!p) - goto out_socket; - - p = proc_create("arp", S_IRUGO, atalk_proc_dir, &atalk_seq_arp_fops); - if (!p) - goto out_arp; - - rc = 0; -out: - return rc; -out_arp: - remove_proc_entry("socket", atalk_proc_dir); -out_socket: - remove_proc_entry("route", atalk_proc_dir); -out_route: - remove_proc_entry("interface", atalk_proc_dir); -out_interface: - remove_proc_entry("atalk", init_net.proc_net); - goto out; -} - -void __exit atalk_proc_exit(void) -{ - remove_proc_entry("interface", atalk_proc_dir); - remove_proc_entry("route", atalk_proc_dir); - remove_proc_entry("socket", atalk_proc_dir); - remove_proc_entry("arp", atalk_proc_dir); - remove_proc_entry("atalk", init_net.proc_net); -} diff --git a/drivers/staging/appletalk/cops.c b/drivers/staging/appletalk/cops.c deleted file mode 100644 index 661d42eff7d8..000000000000 --- a/drivers/staging/appletalk/cops.c +++ /dev/null @@ -1,1013 +0,0 @@ -/* cops.c: LocalTalk driver for Linux. - * - * Authors: - * - Jay Schulist - * - * With more than a little help from; - * - Alan Cox - * - * Derived from: - * - skeleton.c: A network driver outline for linux. - * Written 1993-94 by Donald Becker. - * - ltpc.c: A driver for the LocalTalk PC card. - * Written by Bradford W. Johnson. - * - * Copyright 1993 United States Government as represented by the - * Director, National Security Agency. - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - * Changes: - * 19970608 Alan Cox Allowed dual card type support - * Can set board type in insmod - * Hooks for cops_setup routine - * (not yet implemented). - * 19971101 Jay Schulist Fixes for multiple lt* devices. - * 19980607 Steven Hirsch Fixed the badly broken support - * for Tangent type cards. Only - * tested on Daystar LT200. Some - * cleanup of formatting and program - * logic. Added emacs 'local-vars' - * setup for Jay's brace style. - * 20000211 Alan Cox Cleaned up for softnet - */ - -static const char *version = -"cops.c:v0.04 6/7/98 Jay Schulist \n"; -/* - * Sources: - * COPS Localtalk SDK. This provides almost all of the information - * needed. - */ - -/* - * insmod/modprobe configurable stuff. - * - IO Port, choose one your card supports or 0 if you dare. - * - IRQ, also choose one your card supports or nothing and let - * the driver figure it out. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* For udelay() */ -#include -#include -#include - -#include -#include -#include - -#include "atalk.h" -#include "cops.h" /* Our Stuff */ -#include "cops_ltdrv.h" /* Firmware code for Tangent type cards. */ -#include "cops_ffdrv.h" /* Firmware code for Dayna type cards. */ - -/* - * The name of the card. Is used for messages and in the requests for - * io regions, irqs and dma channels - */ - -static const char *cardname = "cops"; - -#ifdef CONFIG_COPS_DAYNA -static int board_type = DAYNA; /* Module exported */ -#else -static int board_type = TANGENT; -#endif - -static int io = 0x240; /* Default IO for Dayna */ -static int irq = 5; /* Default IRQ */ - -/* - * COPS Autoprobe information. - * Right now if port address is right but IRQ is not 5 this will - * return a 5 no matter what since we will still get a status response. - * Need one more additional check to narrow down after we have gotten - * the ioaddr. But since only other possible IRQs is 3 and 4 so no real - * hurry on this. I *STRONGLY* recommend using IRQ 5 for your card with - * this driver. - * - * This driver has 2 modes and they are: Dayna mode and Tangent mode. - * Each mode corresponds with the type of card. It has been found - * that there are 2 main types of cards and all other cards are - * the same and just have different names or only have minor differences - * such as more IO ports. As this driver is tested it will - * become more clear on exactly what cards are supported. The driver - * defaults to using Dayna mode. To change the drivers mode, simply - * select Dayna or Tangent mode when configuring the kernel. - * - * This driver should support: - * TANGENT driver mode: - * Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200, - * COPS LT-1 - * DAYNA driver mode: - * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, - * Farallon PhoneNET PC III, Farallon PhoneNET PC II - * Other cards possibly supported mode unknown though: - * Dayna DL2000 (Full length), COPS LT/M (Micro-Channel) - * - * Cards NOT supported by this driver but supported by the ltpc.c - * driver written by Bradford W. Johnson - * Farallon PhoneNET PC - * Original Apple LocalTalk PC card - * - * N.B. - * - * The Daystar Digital LT200 boards do not support interrupt-driven - * IO. You must specify 'irq=0xff' as a module parameter to invoke - * polled mode. I also believe that the port probing logic is quite - * dangerous at best and certainly hopeless for a polled card. Best to - * specify both. - Steve H. - * - */ - -/* - * Zero terminated list of IO ports to probe. - */ - -static unsigned int ports[] = { - 0x240, 0x340, 0x200, 0x210, 0x220, 0x230, 0x260, - 0x2A0, 0x300, 0x310, 0x320, 0x330, 0x350, 0x360, - 0 -}; - -/* - * Zero terminated list of IRQ ports to probe. - */ - -static int cops_irqlist[] = { - 5, 4, 3, 0 -}; - -static struct timer_list cops_timer; - -/* use 0 for production, 1 for verification, 2 for debug, 3 for verbose debug */ -#ifndef COPS_DEBUG -#define COPS_DEBUG 1 -#endif -static unsigned int cops_debug = COPS_DEBUG; - -/* The number of low I/O ports used by the card. */ -#define COPS_IO_EXTENT 8 - -/* Information that needs to be kept for each board. */ - -struct cops_local -{ - int board; /* Holds what board type is. */ - int nodeid; /* Set to 1 once have nodeid. */ - unsigned char node_acquire; /* Node ID when acquired. */ - struct atalk_addr node_addr; /* Full node address */ - spinlock_t lock; /* RX/TX lock */ -}; - -/* Index to functions, as function prototypes. */ -static int cops_probe1 (struct net_device *dev, int ioaddr); -static int cops_irq (int ioaddr, int board); - -static int cops_open (struct net_device *dev); -static int cops_jumpstart (struct net_device *dev); -static void cops_reset (struct net_device *dev, int sleep); -static void cops_load (struct net_device *dev); -static int cops_nodeid (struct net_device *dev, int nodeid); - -static irqreturn_t cops_interrupt (int irq, void *dev_id); -static void cops_poll (unsigned long ltdev); -static void cops_timeout(struct net_device *dev); -static void cops_rx (struct net_device *dev); -static netdev_tx_t cops_send_packet (struct sk_buff *skb, - struct net_device *dev); -static void set_multicast_list (struct net_device *dev); -static int cops_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); -static int cops_close (struct net_device *dev); - -static void cleanup_card(struct net_device *dev) -{ - if (dev->irq) - free_irq(dev->irq, dev); - release_region(dev->base_addr, COPS_IO_EXTENT); -} - -/* - * Check for a network adaptor of this type, and return '0' iff one exists. - * If dev->base_addr == 0, probe all likely locations. - * If dev->base_addr in [1..0x1ff], always return failure. - * otherwise go with what we pass in. - */ -struct net_device * __init cops_probe(int unit) -{ - struct net_device *dev; - unsigned *port; - int base_addr; - int err = 0; - - dev = alloc_ltalkdev(sizeof(struct cops_local)); - if (!dev) - return ERR_PTR(-ENOMEM); - - if (unit >= 0) { - sprintf(dev->name, "lt%d", unit); - netdev_boot_setup_check(dev); - irq = dev->irq; - base_addr = dev->base_addr; - } else { - base_addr = dev->base_addr = io; - } - - if (base_addr > 0x1ff) { /* Check a single specified location. */ - err = cops_probe1(dev, base_addr); - } else if (base_addr != 0) { /* Don't probe at all. */ - err = -ENXIO; - } else { - /* FIXME Does this really work for cards which generate irq? - * It's definitely N.G. for polled Tangent. sh - * Dayna cards don't autoprobe well at all, but if your card is - * at IRQ 5 & IO 0x240 we find it every time. ;) JS - */ - for (port = ports; *port && cops_probe1(dev, *port) < 0; port++) - ; - if (!*port) - err = -ENODEV; - } - if (err) - goto out; - err = register_netdev(dev); - if (err) - goto out1; - return dev; -out1: - cleanup_card(dev); -out: - free_netdev(dev); - return ERR_PTR(err); -} - -static const struct net_device_ops cops_netdev_ops = { - .ndo_open = cops_open, - .ndo_stop = cops_close, - .ndo_start_xmit = cops_send_packet, - .ndo_tx_timeout = cops_timeout, - .ndo_do_ioctl = cops_ioctl, - .ndo_set_multicast_list = set_multicast_list, -}; - -/* - * This is the real probe routine. Linux has a history of friendly device - * probes on the ISA bus. A good device probes avoids doing writes, and - * verifies that the correct device exists and functions. - */ -static int __init cops_probe1(struct net_device *dev, int ioaddr) -{ - struct cops_local *lp; - static unsigned version_printed; - int board = board_type; - int retval; - - if(cops_debug && version_printed++ == 0) - printk("%s", version); - - /* Grab the region so no one else tries to probe our ioports. */ - if (!request_region(ioaddr, COPS_IO_EXTENT, dev->name)) - return -EBUSY; - - /* - * Since this board has jumpered interrupts, allocate the interrupt - * vector now. There is no point in waiting since no other device - * can use the interrupt, and this marks the irq as busy. Jumpered - * interrupts are typically not reported by the boards, and we must - * used AutoIRQ to find them. - */ - dev->irq = irq; - switch (dev->irq) - { - case 0: - /* COPS AutoIRQ routine */ - dev->irq = cops_irq(ioaddr, board); - if (dev->irq) - break; - /* No IRQ found on this port, fallthrough */ - case 1: - retval = -EINVAL; - goto err_out; - - /* Fixup for users that don't know that IRQ 2 is really - * IRQ 9, or don't know which one to set. - */ - case 2: - dev->irq = 9; - break; - - /* Polled operation requested. Although irq of zero passed as - * a parameter tells the init routines to probe, we'll - * overload it to denote polled operation at runtime. - */ - case 0xff: - dev->irq = 0; - break; - - default: - break; - } - - /* Reserve any actual interrupt. */ - if (dev->irq) { - retval = request_irq(dev->irq, cops_interrupt, 0, dev->name, dev); - if (retval) - goto err_out; - } - - dev->base_addr = ioaddr; - - lp = netdev_priv(dev); - spin_lock_init(&lp->lock); - - /* Copy local board variable to lp struct. */ - lp->board = board; - - dev->netdev_ops = &cops_netdev_ops; - dev->watchdog_timeo = HZ * 2; - - - /* Tell the user where the card is and what mode we're in. */ - if(board==DAYNA) - printk("%s: %s at %#3x, using IRQ %d, in Dayna mode.\n", - dev->name, cardname, ioaddr, dev->irq); - if(board==TANGENT) { - if(dev->irq) - printk("%s: %s at %#3x, IRQ %d, in Tangent mode\n", - dev->name, cardname, ioaddr, dev->irq); - else - printk("%s: %s at %#3x, using polled IO, in Tangent mode.\n", - dev->name, cardname, ioaddr); - - } - return 0; - -err_out: - release_region(ioaddr, COPS_IO_EXTENT); - return retval; -} - -static int __init cops_irq (int ioaddr, int board) -{ /* - * This does not use the IRQ to determine where the IRQ is. We just - * assume that when we get a correct status response that it's the IRQ. - * This really just verifies the IO port but since we only have access - * to such a small number of IRQs (5, 4, 3) this is not bad. - * This will probably not work for more than one card. - */ - int irqaddr=0; - int i, x, status; - - if(board==DAYNA) - { - outb(0, ioaddr+DAYNA_RESET); - inb(ioaddr+DAYNA_RESET); - mdelay(333); - } - if(board==TANGENT) - { - inb(ioaddr); - outb(0, ioaddr); - outb(0, ioaddr+TANG_RESET); - } - - for(i=0; cops_irqlist[i] !=0; i++) - { - irqaddr = cops_irqlist[i]; - for(x = 0xFFFF; x>0; x --) /* wait for response */ - { - if(board==DAYNA) - { - status = (inb(ioaddr+DAYNA_CARD_STATUS)&3); - if(status == 1) - return irqaddr; - } - if(board==TANGENT) - { - if((inb(ioaddr+TANG_CARD_STATUS)& TANG_TX_READY) !=0) - return irqaddr; - } - } - } - return 0; /* no IRQ found */ -} - -/* - * Open/initialize the board. This is called (in the current kernel) - * sometime after booting when the 'ifconfig' program is run. - */ -static int cops_open(struct net_device *dev) -{ - struct cops_local *lp = netdev_priv(dev); - - if(dev->irq==0) - { - /* - * I don't know if the Dayna-style boards support polled - * operation. For now, only allow it for Tangent. - */ - if(lp->board==TANGENT) /* Poll 20 times per second */ - { - init_timer(&cops_timer); - cops_timer.function = cops_poll; - cops_timer.data = (unsigned long)dev; - cops_timer.expires = jiffies + HZ/20; - add_timer(&cops_timer); - } - else - { - printk(KERN_WARNING "%s: No irq line set\n", dev->name); - return -EAGAIN; - } - } - - cops_jumpstart(dev); /* Start the card up. */ - - netif_start_queue(dev); - return 0; -} - -/* - * This allows for a dynamic start/restart of the entire card. - */ -static int cops_jumpstart(struct net_device *dev) -{ - struct cops_local *lp = netdev_priv(dev); - - /* - * Once the card has the firmware loaded and has acquired - * the nodeid, if it is reset it will lose it all. - */ - cops_reset(dev,1); /* Need to reset card before load firmware. */ - cops_load(dev); /* Load the firmware. */ - - /* - * If atalkd already gave us a nodeid we will use that - * one again, else we wait for atalkd to give us a nodeid - * in cops_ioctl. This may cause a problem if someone steals - * our nodeid while we are resetting. - */ - if(lp->nodeid == 1) - cops_nodeid(dev,lp->node_acquire); - - return 0; -} - -static void tangent_wait_reset(int ioaddr) -{ - int timeout=0; - - while(timeout++ < 5 && (inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) - mdelay(1); /* Wait 1 second */ -} - -/* - * Reset the LocalTalk board. - */ -static void cops_reset(struct net_device *dev, int sleep) -{ - struct cops_local *lp = netdev_priv(dev); - int ioaddr=dev->base_addr; - - if(lp->board==TANGENT) - { - inb(ioaddr); /* Clear request latch. */ - outb(0,ioaddr); /* Clear the TANG_TX_READY flop. */ - outb(0, ioaddr+TANG_RESET); /* Reset the adapter. */ - - tangent_wait_reset(ioaddr); - outb(0, ioaddr+TANG_CLEAR_INT); - } - if(lp->board==DAYNA) - { - outb(0, ioaddr+DAYNA_RESET); /* Assert the reset port */ - inb(ioaddr+DAYNA_RESET); /* Clear the reset */ - if (sleep) - msleep(333); - else - mdelay(333); - } - - netif_wake_queue(dev); -} - -static void cops_load (struct net_device *dev) -{ - struct ifreq ifr; - struct ltfirmware *ltf= (struct ltfirmware *)&ifr.ifr_ifru; - struct cops_local *lp = netdev_priv(dev); - int ioaddr=dev->base_addr; - int length, i = 0; - - strcpy(ifr.ifr_name,"lt0"); - - /* Get card's firmware code and do some checks on it. */ -#ifdef CONFIG_COPS_DAYNA - if(lp->board==DAYNA) - { - ltf->length=sizeof(ffdrv_code); - ltf->data=ffdrv_code; - } - else -#endif -#ifdef CONFIG_COPS_TANGENT - if(lp->board==TANGENT) - { - ltf->length=sizeof(ltdrv_code); - ltf->data=ltdrv_code; - } - else -#endif - { - printk(KERN_INFO "%s; unsupported board type.\n", dev->name); - return; - } - - /* Check to make sure firmware is correct length. */ - if(lp->board==DAYNA && ltf->length!=5983) - { - printk(KERN_WARNING "%s: Firmware is not length of FFDRV.BIN.\n", dev->name); - return; - } - if(lp->board==TANGENT && ltf->length!=2501) - { - printk(KERN_WARNING "%s: Firmware is not length of DRVCODE.BIN.\n", dev->name); - return; - } - - if(lp->board==DAYNA) - { - /* - * We must wait for a status response - * with the DAYNA board. - */ - while(++i<65536) - { - if((inb(ioaddr+DAYNA_CARD_STATUS)&3)==1) - break; - } - - if(i==65536) - return; - } - - /* - * Upload the firmware and kick. Byte-by-byte works nicely here. - */ - i=0; - length = ltf->length; - while(length--) - { - outb(ltf->data[i], ioaddr); - i++; - } - - if(cops_debug > 1) - printk("%s: Uploaded firmware - %d bytes of %d bytes.\n", - dev->name, i, ltf->length); - - if(lp->board==DAYNA) /* Tell Dayna to run the firmware code. */ - outb(1, ioaddr+DAYNA_INT_CARD); - else /* Tell Tang to run the firmware code. */ - inb(ioaddr); - - if(lp->board==TANGENT) - { - tangent_wait_reset(ioaddr); - inb(ioaddr); /* Clear initial ready signal. */ - } -} - -/* - * Get the LocalTalk Nodeid from the card. We can suggest - * any nodeid 1-254. The card will try and get that exact - * address else we can specify 0 as the nodeid and the card - * will autoprobe for a nodeid. - */ -static int cops_nodeid (struct net_device *dev, int nodeid) -{ - struct cops_local *lp = netdev_priv(dev); - int ioaddr = dev->base_addr; - - if(lp->board == DAYNA) - { - /* Empty any pending adapter responses. */ - while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0) - { - outb(0, ioaddr+COPS_CLEAR_INT); /* Clear interrupts. */ - if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST) - cops_rx(dev); /* Kick any packets waiting. */ - schedule(); - } - - outb(2, ioaddr); /* Output command packet length as 2. */ - outb(0, ioaddr); - outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */ - outb(nodeid, ioaddr); /* Suggest node address. */ - } - - if(lp->board == TANGENT) - { - /* Empty any pending adapter responses. */ - while(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY) - { - outb(0, ioaddr+COPS_CLEAR_INT); /* Clear interrupt. */ - cops_rx(dev); /* Kick out packets waiting. */ - schedule(); - } - - /* Not sure what Tangent does if nodeid picked is used. */ - if(nodeid == 0) /* Seed. */ - nodeid = jiffies&0xFF; /* Get a random try */ - outb(2, ioaddr); /* Command length LSB */ - outb(0, ioaddr); /* Command length MSB */ - outb(LAP_INIT, ioaddr); /* Send LAP_INIT byte */ - outb(nodeid, ioaddr); /* LAP address hint. */ - outb(0xFF, ioaddr); /* Int. level to use */ - } - - lp->node_acquire=0; /* Set nodeid holder to 0. */ - while(lp->node_acquire==0) /* Get *True* nodeid finally. */ - { - outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */ - - if(lp->board == DAYNA) - { - if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST) - cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */ - } - if(lp->board == TANGENT) - { - if(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY) - cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */ - } - schedule(); - } - - if(cops_debug > 1) - printk(KERN_DEBUG "%s: Node ID %d has been acquired.\n", - dev->name, lp->node_acquire); - - lp->nodeid=1; /* Set got nodeid to 1. */ - - return 0; -} - -/* - * Poll the Tangent type cards to see if we have work. - */ - -static void cops_poll(unsigned long ltdev) -{ - int ioaddr, status; - int boguscount = 0; - - struct net_device *dev = (struct net_device *)ltdev; - - del_timer(&cops_timer); - - if(dev == NULL) - return; /* We've been downed */ - - ioaddr = dev->base_addr; - do { - status=inb(ioaddr+TANG_CARD_STATUS); - if(status & TANG_RX_READY) - cops_rx(dev); - if(status & TANG_TX_READY) - netif_wake_queue(dev); - status = inb(ioaddr+TANG_CARD_STATUS); - } while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY))); - - /* poll 20 times per second */ - cops_timer.expires = jiffies + HZ/20; - add_timer(&cops_timer); -} - -/* - * The typical workload of the driver: - * Handle the network interface interrupts. - */ -static irqreturn_t cops_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct cops_local *lp; - int ioaddr, status; - int boguscount = 0; - - ioaddr = dev->base_addr; - lp = netdev_priv(dev); - - if(lp->board==DAYNA) - { - do { - outb(0, ioaddr + COPS_CLEAR_INT); - status=inb(ioaddr+DAYNA_CARD_STATUS); - if((status&0x03)==DAYNA_RX_REQUEST) - cops_rx(dev); - netif_wake_queue(dev); - } while(++boguscount < 20); - } - else - { - do { - status=inb(ioaddr+TANG_CARD_STATUS); - if(status & TANG_RX_READY) - cops_rx(dev); - if(status & TANG_TX_READY) - netif_wake_queue(dev); - status=inb(ioaddr+TANG_CARD_STATUS); - } while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY))); - } - - return IRQ_HANDLED; -} - -/* - * We have a good packet(s), get it/them out of the buffers. - */ -static void cops_rx(struct net_device *dev) -{ - int pkt_len = 0; - int rsp_type = 0; - struct sk_buff *skb = NULL; - struct cops_local *lp = netdev_priv(dev); - int ioaddr = dev->base_addr; - int boguscount = 0; - unsigned long flags; - - - spin_lock_irqsave(&lp->lock, flags); - - if(lp->board==DAYNA) - { - outb(0, ioaddr); /* Send out Zero length. */ - outb(0, ioaddr); - outb(DATA_READ, ioaddr); /* Send read command out. */ - - /* Wait for DMA to turn around. */ - while(++boguscount<1000000) - { - barrier(); - if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_READY) - break; - } - - if(boguscount==1000000) - { - printk(KERN_WARNING "%s: DMA timed out.\n",dev->name); - spin_unlock_irqrestore(&lp->lock, flags); - return; - } - } - - /* Get response length. */ - if(lp->board==DAYNA) - pkt_len = inb(ioaddr) & 0xFF; - else - pkt_len = inb(ioaddr) & 0x00FF; - pkt_len |= (inb(ioaddr) << 8); - /* Input IO code. */ - rsp_type=inb(ioaddr); - - /* Malloc up new buffer. */ - skb = dev_alloc_skb(pkt_len); - if(skb == NULL) - { - printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", - dev->name); - dev->stats.rx_dropped++; - while(pkt_len--) /* Discard packet */ - inb(ioaddr); - spin_unlock_irqrestore(&lp->lock, flags); - return; - } - skb->dev = dev; - skb_put(skb, pkt_len); - skb->protocol = htons(ETH_P_LOCALTALK); - - insb(ioaddr, skb->data, pkt_len); /* Eat the Data */ - - if(lp->board==DAYNA) - outb(1, ioaddr+DAYNA_INT_CARD); /* Interrupt the card */ - - spin_unlock_irqrestore(&lp->lock, flags); /* Restore interrupts. */ - - /* Check for bad response length */ - if(pkt_len < 0 || pkt_len > MAX_LLAP_SIZE) - { - printk(KERN_WARNING "%s: Bad packet length of %d bytes.\n", - dev->name, pkt_len); - dev->stats.tx_errors++; - dev_kfree_skb_any(skb); - return; - } - - /* Set nodeid and then get out. */ - if(rsp_type == LAP_INIT_RSP) - { /* Nodeid taken from received packet. */ - lp->node_acquire = skb->data[0]; - dev_kfree_skb_any(skb); - return; - } - - /* One last check to make sure we have a good packet. */ - if(rsp_type != LAP_RESPONSE) - { - printk(KERN_WARNING "%s: Bad packet type %d.\n", dev->name, rsp_type); - dev->stats.tx_errors++; - dev_kfree_skb_any(skb); - return; - } - - skb_reset_mac_header(skb); /* Point to entire packet. */ - skb_pull(skb,3); - skb_reset_transport_header(skb); /* Point to data (Skip header). */ - - /* Update the counters. */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - - /* Send packet to a higher place. */ - netif_rx(skb); -} - -static void cops_timeout(struct net_device *dev) -{ - struct cops_local *lp = netdev_priv(dev); - int ioaddr = dev->base_addr; - - dev->stats.tx_errors++; - if(lp->board==TANGENT) - { - if((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) - printk(KERN_WARNING "%s: No TX complete interrupt.\n", dev->name); - } - printk(KERN_WARNING "%s: Transmit timed out.\n", dev->name); - cops_jumpstart(dev); /* Restart the card. */ - dev->trans_start = jiffies; /* prevent tx timeout */ - netif_wake_queue(dev); -} - - -/* - * Make the card transmit a LocalTalk packet. - */ - -static netdev_tx_t cops_send_packet(struct sk_buff *skb, - struct net_device *dev) -{ - struct cops_local *lp = netdev_priv(dev); - int ioaddr = dev->base_addr; - unsigned long flags; - - /* - * Block a timer-based transmit from overlapping. - */ - - netif_stop_queue(dev); - - spin_lock_irqsave(&lp->lock, flags); - if(lp->board == DAYNA) /* Wait for adapter transmit buffer. */ - while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0) - cpu_relax(); - if(lp->board == TANGENT) /* Wait for adapter transmit buffer. */ - while((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) - cpu_relax(); - - /* Output IO length. */ - outb(skb->len, ioaddr); - if(lp->board == DAYNA) - outb(skb->len >> 8, ioaddr); - else - outb((skb->len >> 8)&0x0FF, ioaddr); - - /* Output IO code. */ - outb(LAP_WRITE, ioaddr); - - if(lp->board == DAYNA) /* Check the transmit buffer again. */ - while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0); - - outsb(ioaddr, skb->data, skb->len); /* Send out the data. */ - - if(lp->board==DAYNA) /* Dayna requires you kick the card */ - outb(1, ioaddr+DAYNA_INT_CARD); - - spin_unlock_irqrestore(&lp->lock, flags); /* Restore interrupts. */ - - /* Done sending packet, update counters and cleanup. */ - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - dev_kfree_skb (skb); - return NETDEV_TX_OK; -} - -/* - * Dummy function to keep the Appletalk layer happy. - */ - -static void set_multicast_list(struct net_device *dev) -{ - if(cops_debug >= 3) - printk("%s: set_multicast_list executed\n", dev->name); -} - -/* - * System ioctls for the COPS LocalTalk card. - */ - -static int cops_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - struct cops_local *lp = netdev_priv(dev); - struct sockaddr_at *sa = (struct sockaddr_at *)&ifr->ifr_addr; - struct atalk_addr *aa = (struct atalk_addr *)&lp->node_addr; - - switch(cmd) - { - case SIOCSIFADDR: - /* Get and set the nodeid and network # atalkd wants. */ - cops_nodeid(dev, sa->sat_addr.s_node); - aa->s_net = sa->sat_addr.s_net; - aa->s_node = lp->node_acquire; - - /* Set broardcast address. */ - dev->broadcast[0] = 0xFF; - - /* Set hardware address. */ - dev->dev_addr[0] = aa->s_node; - dev->addr_len = 1; - return 0; - - case SIOCGIFADDR: - sa->sat_addr.s_net = aa->s_net; - sa->sat_addr.s_node = aa->s_node; - return 0; - - default: - return -EOPNOTSUPP; - } -} - -/* - * The inverse routine to cops_open(). - */ - -static int cops_close(struct net_device *dev) -{ - struct cops_local *lp = netdev_priv(dev); - - /* If we were running polled, yank the timer. - */ - if(lp->board==TANGENT && dev->irq==0) - del_timer(&cops_timer); - - netif_stop_queue(dev); - return 0; -} - - -#ifdef MODULE -static struct net_device *cops_dev; - -MODULE_LICENSE("GPL"); -module_param(io, int, 0); -module_param(irq, int, 0); -module_param(board_type, int, 0); - -static int __init cops_module_init(void) -{ - if (io == 0) - printk(KERN_WARNING "%s: You shouldn't autoprobe with insmod\n", - cardname); - cops_dev = cops_probe(-1); - if (IS_ERR(cops_dev)) - return PTR_ERR(cops_dev); - return 0; -} - -static void __exit cops_module_exit(void) -{ - unregister_netdev(cops_dev); - cleanup_card(cops_dev); - free_netdev(cops_dev); -} -module_init(cops_module_init); -module_exit(cops_module_exit); -#endif /* MODULE */ diff --git a/drivers/staging/appletalk/cops.h b/drivers/staging/appletalk/cops.h deleted file mode 100644 index fd2750b269c8..000000000000 --- a/drivers/staging/appletalk/cops.h +++ /dev/null @@ -1,60 +0,0 @@ -/* cops.h: LocalTalk driver for Linux. - * - * Authors: - * - Jay Schulist - */ - -#ifndef __LINUX_COPSLTALK_H -#define __LINUX_COPSLTALK_H - -#ifdef __KERNEL__ - -/* Max LLAP size we will accept. */ -#define MAX_LLAP_SIZE 603 - -/* Tangent */ -#define TANG_CARD_STATUS 1 -#define TANG_CLEAR_INT 1 -#define TANG_RESET 3 - -#define TANG_TX_READY 1 -#define TANG_RX_READY 2 - -/* Dayna */ -#define DAYNA_CMD_DATA 0 -#define DAYNA_CLEAR_INT 1 -#define DAYNA_CARD_STATUS 2 -#define DAYNA_INT_CARD 3 -#define DAYNA_RESET 4 - -#define DAYNA_RX_READY 0 -#define DAYNA_TX_READY 1 -#define DAYNA_RX_REQUEST 3 - -/* Same on both card types */ -#define COPS_CLEAR_INT 1 - -/* LAP response codes received from the cards. */ -#define LAP_INIT 1 /* Init cmd */ -#define LAP_INIT_RSP 2 /* Init response */ -#define LAP_WRITE 3 /* Write cmd */ -#define DATA_READ 4 /* Data read */ -#define LAP_RESPONSE 4 /* Received ALAP frame response */ -#define LAP_GETSTAT 5 /* Get LAP and HW status */ -#define LAP_RSPSTAT 6 /* Status response */ - -#endif - -/* - * Structure to hold the firmware information. - */ -struct ltfirmware -{ - unsigned int length; - const unsigned char *data; -}; - -#define DAYNA 1 -#define TANGENT 2 - -#endif diff --git a/drivers/staging/appletalk/cops_ffdrv.h b/drivers/staging/appletalk/cops_ffdrv.h deleted file mode 100644 index b02005087c1b..000000000000 --- a/drivers/staging/appletalk/cops_ffdrv.h +++ /dev/null @@ -1,532 +0,0 @@ - -/* - * The firmware this driver downloads into the Localtalk card is a - * separate program and is not GPL'd source code, even though the Linux - * side driver and the routine that loads this data into the card are. - * - * It is taken from the COPS SDK and is under the following license - * - * This material is licensed to you strictly for use in conjunction with - * the use of COPS LocalTalk adapters. - * There is no charge for this SDK. And no waranty express or implied - * about its fitness for any purpose. However, we will cheerefully - * refund every penny you paid for this SDK... - * Regards, - * - * Thomas F. Divine - * Chief Scientist - */ - - -/* cops_ffdrv.h: LocalTalk driver firmware dump for Linux. - * - * Authors: - * - Jay Schulist - */ - - -#ifdef CONFIG_COPS_DAYNA - -static const unsigned char ffdrv_code[] = { - 58,3,0,50,228,149,33,255,255,34,226,149, - 249,17,40,152,33,202,154,183,237,82,77,68, - 11,107,98,19,54,0,237,176,175,50,80,0, - 62,128,237,71,62,32,237,57,51,62,12,237, - 57,50,237,57,54,62,6,237,57,52,62,12, - 237,57,49,33,107,137,34,32,128,33,83,130, - 34,40,128,33,86,130,34,42,128,33,112,130, - 34,36,128,33,211,130,34,38,128,62,0,237, - 57,16,33,63,148,34,34,128,237,94,205,15, - 130,251,205,168,145,24,141,67,111,112,121,114, - 105,103,104,116,32,40,67,41,32,49,57,56, - 56,32,45,32,68,97,121,110,97,32,67,111, - 109,109,117,110,105,99,97,116,105,111,110,115, - 32,32,32,65,108,108,32,114,105,103,104,116, - 115,32,114,101,115,101,114,118,101,100,46,32, - 32,40,68,40,68,7,16,8,34,7,22,6, - 16,5,12,4,8,3,6,140,0,16,39,128, - 0,4,96,10,224,6,0,7,126,2,64,11, - 118,12,6,13,0,14,193,15,0,5,96,3, - 192,1,64,9,8,62,9,211,66,62,192,211, - 66,62,100,61,32,253,6,28,33,205,129,14, - 66,237,163,194,253,129,6,28,33,205,129,14, - 64,237,163,194,9,130,201,62,47,50,71,152, - 62,47,211,68,58,203,129,237,57,20,58,204, - 129,237,57,21,33,77,152,54,132,205,233,129, - 58,228,149,254,209,40,6,56,4,62,0,24, - 2,219,96,33,233,149,119,230,62,33,232,149, - 119,213,33,8,152,17,7,0,25,119,19,25, - 119,209,201,251,237,77,245,197,213,229,221,229, - 205,233,129,62,1,50,106,137,205,158,139,221, - 225,225,209,193,241,251,237,77,245,197,213,219, - 72,237,56,16,230,46,237,57,16,237,56,12, - 58,72,152,183,32,26,6,20,17,128,2,237, - 56,46,187,32,35,237,56,47,186,32,29,219, - 72,230,1,32,3,5,32,232,175,50,72,152, - 229,221,229,62,1,50,106,137,205,158,139,221, - 225,225,24,25,62,1,50,72,152,58,201,129, - 237,57,12,58,202,129,237,57,13,237,56,16, - 246,17,237,57,16,209,193,241,251,237,77,245, - 197,229,213,221,229,237,56,16,230,17,237,57, - 16,237,56,20,58,34,152,246,16,246,8,211, - 68,62,6,61,32,253,58,34,152,246,8,211, - 68,58,203,129,237,57,20,58,204,129,237,57, - 21,237,56,16,246,34,237,57,16,221,225,209, - 225,193,241,251,237,77,33,2,0,57,126,230, - 3,237,100,1,40,2,246,128,230,130,245,62, - 5,211,64,241,211,64,201,229,213,243,237,56, - 16,230,46,237,57,16,237,56,12,251,70,35, - 35,126,254,175,202,77,133,254,129,202,15,133, - 230,128,194,191,132,43,58,44,152,119,33,76, - 152,119,35,62,132,119,120,254,255,40,4,58, - 49,152,119,219,72,43,43,112,17,3,0,237, - 56,52,230,248,237,57,52,219,72,230,1,194, - 141,131,209,225,237,56,52,246,6,237,57,52, - 62,1,55,251,201,62,3,211,66,62,192,211, - 66,62,48,211,66,0,0,219,66,230,1,40, - 4,219,67,24,240,205,203,135,58,75,152,254, - 255,202,128,132,58,49,152,254,161,250,207,131, - 58,34,152,211,68,62,10,211,66,62,128,211, - 66,62,11,211,66,62,6,211,66,24,0,62, - 14,211,66,62,33,211,66,62,1,211,66,62, - 64,211,66,62,3,211,66,62,209,211,66,62, - 100,71,219,66,230,1,32,6,5,32,247,195, - 248,132,219,67,71,58,44,152,184,194,248,132, - 62,100,71,219,66,230,1,32,6,5,32,247, - 195,248,132,219,67,62,100,71,219,66,230,1, - 32,6,5,32,247,195,248,132,219,67,254,133, - 32,7,62,0,50,74,152,24,17,254,173,32, - 7,62,1,50,74,152,24,6,254,141,194,248, - 132,71,209,225,58,49,152,254,132,32,10,62, - 50,205,2,134,205,144,135,24,27,254,140,32, - 15,62,110,205,2,134,62,141,184,32,5,205, - 144,135,24,8,62,10,205,2,134,205,8,134, - 62,1,50,106,137,205,158,139,237,56,52,246, - 6,237,57,52,175,183,251,201,62,20,135,237, - 57,20,175,237,57,21,237,56,16,246,2,237, - 57,16,237,56,20,95,237,56,21,123,254,10, - 48,244,237,56,16,230,17,237,57,16,209,225, - 205,144,135,62,1,50,106,137,205,158,139,237, - 56,52,246,6,237,57,52,175,183,251,201,209, - 225,243,219,72,230,1,40,13,62,10,211,66, - 0,0,219,66,230,192,202,226,132,237,56,52, - 246,6,237,57,52,62,1,55,251,201,205,203, - 135,62,1,50,106,137,205,158,139,237,56,52, - 246,6,237,57,52,183,251,201,209,225,62,1, - 50,106,137,205,158,139,237,56,52,246,6,237, - 57,52,62,2,55,251,201,209,225,243,219,72, - 230,1,202,213,132,62,10,211,66,0,0,219, - 66,230,192,194,213,132,229,62,1,50,106,137, - 42,40,152,205,65,143,225,17,3,0,205,111, - 136,62,6,211,66,58,44,152,211,66,237,56, - 52,246,6,237,57,52,183,251,201,209,197,237, - 56,52,230,248,237,57,52,219,72,230,1,32, - 15,193,225,237,56,52,246,6,237,57,52,62, - 1,55,251,201,14,23,58,37,152,254,0,40, - 14,14,2,254,1,32,5,62,140,119,24,3, - 62,132,119,43,43,197,205,203,135,193,62,1, - 211,66,62,64,211,66,62,3,211,66,62,193, - 211,66,62,100,203,39,71,219,66,230,1,32, - 6,5,32,247,195,229,133,33,238,151,219,67, - 71,58,44,152,184,194,229,133,119,62,100,71, - 219,66,230,1,32,6,5,32,247,195,229,133, - 219,67,35,119,13,32,234,193,225,62,1,50, - 106,137,205,158,139,237,56,52,246,6,237,57, - 52,175,183,251,201,33,234,151,35,35,62,255, - 119,193,225,62,1,50,106,137,205,158,139,237, - 56,52,246,6,237,57,52,175,251,201,243,61, - 32,253,251,201,62,3,211,66,62,192,211,66, - 58,49,152,254,140,32,19,197,229,213,17,181, - 129,33,185,129,1,2,0,237,176,209,225,193, - 24,27,229,213,33,187,129,58,49,152,230,15, - 87,30,2,237,92,25,17,181,129,126,18,19, - 35,126,18,209,225,58,34,152,246,8,211,68, - 58,49,152,254,165,40,14,254,164,40,10,62, - 10,211,66,62,224,211,66,24,25,58,74,152, - 254,0,40,10,62,10,211,66,62,160,211,66, - 24,8,62,10,211,66,62,128,211,66,62,11, - 211,66,62,6,211,66,205,147,143,62,5,211, - 66,62,224,211,66,62,5,211,66,62,96,211, - 66,62,5,61,32,253,62,5,211,66,62,224, - 211,66,62,14,61,32,253,62,5,211,66,62, - 233,211,66,62,128,211,66,58,181,129,61,32, - 253,62,1,211,66,62,192,211,66,1,254,19, - 237,56,46,187,32,6,13,32,247,195,226,134, - 62,192,211,66,0,0,219,66,203,119,40,250, - 219,66,203,87,40,250,243,237,56,16,230,17, - 237,57,16,237,56,20,251,62,5,211,66,62, - 224,211,66,58,182,129,61,32,253,229,33,181, - 129,58,183,129,203,63,119,35,58,184,129,119, - 225,62,10,211,66,62,224,211,66,62,11,211, - 66,62,118,211,66,62,47,211,68,62,5,211, - 66,62,233,211,66,58,181,129,61,32,253,62, - 5,211,66,62,224,211,66,58,182,129,61,32, - 253,62,5,211,66,62,96,211,66,201,229,213, - 58,50,152,230,15,87,30,2,237,92,33,187, - 129,25,17,181,129,126,18,35,19,126,18,209, - 225,58,71,152,246,8,211,68,58,50,152,254, - 165,40,14,254,164,40,10,62,10,211,66,62, - 224,211,66,24,8,62,10,211,66,62,128,211, - 66,62,11,211,66,62,6,211,66,195,248,135, - 62,3,211,66,62,192,211,66,197,229,213,17, - 181,129,33,183,129,1,2,0,237,176,209,225, - 193,62,47,211,68,62,10,211,66,62,224,211, - 66,62,11,211,66,62,118,211,66,62,1,211, - 66,62,0,211,66,205,147,143,195,16,136,62, - 3,211,66,62,192,211,66,197,229,213,17,181, - 129,33,183,129,1,2,0,237,176,209,225,193, - 62,47,211,68,62,10,211,66,62,224,211,66, - 62,11,211,66,62,118,211,66,205,147,143,62, - 5,211,66,62,224,211,66,62,5,211,66,62, - 96,211,66,62,5,61,32,253,62,5,211,66, - 62,224,211,66,62,14,61,32,253,62,5,211, - 66,62,233,211,66,62,128,211,66,58,181,129, - 61,32,253,62,1,211,66,62,192,211,66,1, - 254,19,237,56,46,187,32,6,13,32,247,195, - 88,136,62,192,211,66,0,0,219,66,203,119, - 40,250,219,66,203,87,40,250,62,5,211,66, - 62,224,211,66,58,182,129,61,32,253,62,5, - 211,66,62,96,211,66,201,197,14,67,6,0, - 62,3,211,66,62,192,211,66,62,48,211,66, - 0,0,219,66,230,1,40,4,219,67,24,240, - 62,5,211,66,62,233,211,66,62,128,211,66, - 58,181,129,61,32,253,237,163,29,62,192,211, - 66,219,66,230,4,40,250,237,163,29,32,245, - 219,66,230,4,40,250,62,255,71,219,66,230, - 4,40,3,5,32,247,219,66,230,4,40,250, - 62,5,211,66,62,224,211,66,58,182,129,61, - 32,253,62,5,211,66,62,96,211,66,58,71, - 152,254,1,202,18,137,62,16,211,66,62,56, - 211,66,62,14,211,66,62,33,211,66,62,1, - 211,66,62,248,211,66,237,56,48,246,153,230, - 207,237,57,48,62,3,211,66,62,221,211,66, - 193,201,58,71,152,211,68,62,10,211,66,62, - 128,211,66,62,11,211,66,62,6,211,66,62, - 6,211,66,58,44,152,211,66,62,16,211,66, - 62,56,211,66,62,48,211,66,0,0,62,14, - 211,66,62,33,211,66,62,1,211,66,62,248, - 211,66,237,56,48,246,145,246,8,230,207,237, - 57,48,62,3,211,66,62,221,211,66,193,201, - 44,3,1,0,70,69,1,245,197,213,229,175, - 50,72,152,237,56,16,230,46,237,57,16,237, - 56,12,62,1,211,66,0,0,219,66,95,230, - 160,32,3,195,20,139,123,230,96,194,72,139, - 62,48,211,66,62,1,211,66,62,64,211,66, - 237,91,40,152,205,207,143,25,43,55,237,82, - 218,70,139,34,42,152,98,107,58,44,152,190, - 194,210,138,35,35,62,130,190,194,200,137,62, - 1,50,48,152,62,175,190,202,82,139,62,132, - 190,32,44,50,50,152,62,47,50,71,152,229, - 175,50,106,137,42,40,152,205,65,143,225,54, - 133,43,70,58,44,152,119,43,112,17,3,0, - 62,10,205,2,134,205,111,136,195,158,138,62, - 140,190,32,19,50,50,152,58,233,149,230,4, - 202,222,138,62,1,50,71,152,195,219,137,126, - 254,160,250,185,138,254,166,242,185,138,50,50, - 152,43,126,35,229,213,33,234,149,95,22,0, - 25,126,254,132,40,18,254,140,40,14,58,50, - 152,230,15,87,126,31,21,242,65,138,56,2, - 175,119,58,50,152,230,15,87,58,233,149,230, - 62,31,21,242,85,138,218,98,138,209,225,195, - 20,139,58,50,152,33,100,137,230,15,95,22, - 0,25,126,50,71,152,209,225,58,50,152,254, - 164,250,135,138,58,73,152,254,0,40,4,54, - 173,24,2,54,133,43,70,58,44,152,119,43, - 112,17,3,0,205,70,135,175,50,106,137,205, - 208,139,58,199,129,237,57,12,58,200,129,237, - 57,13,237,56,16,246,17,237,57,16,225,209, - 193,241,251,237,77,62,129,190,194,227,138,54, - 130,43,70,58,44,152,119,43,112,17,3,0, - 205,144,135,195,20,139,35,35,126,254,132,194, - 227,138,175,50,106,137,205,158,139,24,42,58, - 201,154,254,1,40,7,62,1,50,106,137,24, - 237,58,106,137,254,1,202,222,138,62,128,166, - 194,222,138,221,229,221,33,67,152,205,127,142, - 205,109,144,221,225,225,209,193,241,251,237,77, - 58,106,137,254,1,202,44,139,58,50,152,254, - 164,250,44,139,58,73,152,238,1,50,73,152, - 221,229,221,33,51,152,205,127,142,221,225,62, - 1,50,106,137,205,158,139,195,13,139,24,208, - 24,206,24,204,230,64,40,3,195,20,139,195, - 20,139,43,126,33,8,152,119,35,58,44,152, - 119,43,237,91,35,152,205,203,135,205,158,139, - 195,13,139,175,50,78,152,62,3,211,66,62, - 192,211,66,201,197,33,4,0,57,126,35,102, - 111,62,1,50,106,137,219,72,205,141,139,193, - 201,62,1,50,78,152,34,40,152,54,0,35, - 35,54,0,195,163,139,58,78,152,183,200,229, - 33,181,129,58,183,129,119,35,58,184,129,119, - 225,62,47,211,68,62,14,211,66,62,193,211, - 66,62,10,211,66,62,224,211,66,62,11,211, - 66,62,118,211,66,195,3,140,58,78,152,183, - 200,58,71,152,211,68,254,69,40,4,254,70, - 32,17,58,73,152,254,0,40,10,62,10,211, - 66,62,160,211,66,24,8,62,10,211,66,62, - 128,211,66,62,11,211,66,62,6,211,66,62, - 6,211,66,58,44,152,211,66,62,16,211,66, - 62,56,211,66,62,48,211,66,0,0,219,66, - 230,1,40,4,219,67,24,240,62,14,211,66, - 62,33,211,66,42,40,152,205,65,143,62,1, - 211,66,62,248,211,66,237,56,48,246,145,246, - 8,230,207,237,57,48,62,3,211,66,62,221, - 211,66,201,62,16,211,66,62,56,211,66,62, - 48,211,66,0,0,219,66,230,1,40,4,219, - 67,24,240,62,14,211,66,62,33,211,66,62, - 1,211,66,62,248,211,66,237,56,48,246,153, - 230,207,237,57,48,62,3,211,66,62,221,211, - 66,201,229,213,33,234,149,95,22,0,25,126, - 254,132,40,4,254,140,32,2,175,119,123,209, - 225,201,6,8,14,0,31,48,1,12,16,250, - 121,201,33,4,0,57,94,35,86,33,2,0, - 57,126,35,102,111,221,229,34,89,152,237,83, - 91,152,221,33,63,152,205,127,142,58,81,152, - 50,82,152,58,80,152,135,50,80,152,205,162, - 140,254,3,56,16,58,81,152,135,60,230,15, - 50,81,152,175,50,80,152,24,23,58,79,152, - 205,162,140,254,3,48,13,58,81,152,203,63, - 50,81,152,62,255,50,79,152,58,81,152,50, - 82,152,58,79,152,135,50,79,152,62,32,50, - 83,152,50,84,152,237,56,16,230,17,237,57, - 16,219,72,62,192,50,93,152,62,93,50,94, - 152,58,93,152,61,50,93,152,32,9,58,94, - 152,61,50,94,152,40,44,62,170,237,57,20, - 175,237,57,21,237,56,16,246,2,237,57,16, - 219,72,230,1,202,29,141,237,56,20,71,237, - 56,21,120,254,10,48,237,237,56,16,230,17, - 237,57,16,243,62,14,211,66,62,65,211,66, - 251,58,39,152,23,23,60,50,39,152,71,58, - 82,152,160,230,15,40,22,71,14,10,219,66, - 230,16,202,186,141,219,72,230,1,202,186,141, - 13,32,239,16,235,42,89,152,237,91,91,152, - 205,47,131,48,7,61,202,186,141,195,227,141, - 221,225,33,0,0,201,221,33,55,152,205,127, - 142,58,84,152,61,50,84,152,40,19,58,82, - 152,246,1,50,82,152,58,79,152,246,1,50, - 79,152,195,29,141,221,225,33,1,0,201,221, - 33,59,152,205,127,142,58,80,152,246,1,50, - 80,152,58,82,152,135,246,1,50,82,152,58, - 83,152,61,50,83,152,194,29,141,221,225,33, - 2,0,201,221,229,33,0,0,57,17,4,0, - 25,126,50,44,152,230,128,50,85,152,58,85, - 152,183,40,6,221,33,88,2,24,4,221,33, - 150,0,58,44,152,183,40,53,60,40,50,60, - 40,47,61,61,33,86,152,119,35,119,35,54, - 129,175,50,48,152,221,43,221,229,225,124,181, - 40,42,33,86,152,17,3,0,205,189,140,17, - 232,3,27,123,178,32,251,58,48,152,183,40, - 224,58,44,152,71,62,7,128,230,127,71,58, - 85,152,176,50,44,152,24,162,221,225,201,183, - 221,52,0,192,221,52,1,192,221,52,2,192, - 221,52,3,192,55,201,245,62,1,211,100,241, - 201,245,62,1,211,96,241,201,33,2,0,57, - 126,35,102,111,237,56,48,230,175,237,57,48, - 62,48,237,57,49,125,237,57,32,124,237,57, - 33,62,0,237,57,34,62,88,237,57,35,62, - 0,237,57,36,237,57,37,33,128,2,125,237, - 57,38,124,237,57,39,237,56,48,246,97,230, - 207,237,57,48,62,0,237,57,0,62,0,211, - 96,211,100,201,33,2,0,57,126,35,102,111, - 237,56,48,230,175,237,57,48,62,12,237,57, - 49,62,76,237,57,32,62,0,237,57,33,237, - 57,34,125,237,57,35,124,237,57,36,62,0, - 237,57,37,33,128,2,125,237,57,38,124,237, - 57,39,237,56,48,246,97,230,207,237,57,48, - 62,1,211,96,201,33,2,0,57,126,35,102, - 111,229,237,56,48,230,87,237,57,48,125,237, - 57,40,124,237,57,41,62,0,237,57,42,62, - 67,237,57,43,62,0,237,57,44,58,106,137, - 254,1,32,5,33,6,0,24,3,33,128,2, - 125,237,57,46,124,237,57,47,237,56,50,230, - 252,246,2,237,57,50,225,201,33,4,0,57, - 94,35,86,33,2,0,57,126,35,102,111,237, - 56,48,230,87,237,57,48,125,237,57,40,124, - 237,57,41,62,0,237,57,42,62,67,237,57, - 43,62,0,237,57,44,123,237,57,46,122,237, - 57,47,237,56,50,230,244,246,0,237,57,50, - 237,56,48,246,145,230,207,237,57,48,201,213, - 237,56,46,95,237,56,47,87,237,56,46,111, - 237,56,47,103,183,237,82,32,235,33,128,2, - 183,237,82,209,201,213,237,56,38,95,237,56, - 39,87,237,56,38,111,237,56,39,103,183,237, - 82,32,235,33,128,2,183,237,82,209,201,245, - 197,1,52,0,237,120,230,253,237,121,193,241, - 201,245,197,1,52,0,237,120,246,2,237,121, - 193,241,201,33,2,0,57,126,35,102,111,126, - 35,110,103,201,33,0,0,34,102,152,34,96, - 152,34,98,152,33,202,154,34,104,152,237,91, - 104,152,42,226,149,183,237,82,17,0,255,25, - 34,100,152,203,124,40,6,33,0,125,34,100, - 152,42,104,152,35,35,35,229,205,120,139,193, - 201,205,186,149,229,42,40,152,35,35,35,229, - 205,39,144,193,124,230,3,103,221,117,254,221, - 116,255,237,91,42,152,35,35,35,183,237,82, - 32,12,17,5,0,42,42,152,205,171,149,242, - 169,144,42,40,152,229,205,120,139,193,195,198, - 149,237,91,42,152,42,98,152,25,34,98,152, - 19,19,19,42,102,152,25,34,102,152,237,91, - 100,152,33,158,253,25,237,91,102,152,205,171, - 149,242,214,144,33,0,0,34,102,152,62,1, - 50,95,152,205,225,144,195,198,149,58,95,152, - 183,200,237,91,96,152,42,102,152,205,171,149, - 242,5,145,237,91,102,152,33,98,2,25,237, - 91,96,152,205,171,149,250,37,145,237,91,96, - 152,42,102,152,183,237,82,32,7,42,98,152, - 125,180,40,13,237,91,102,152,42,96,152,205, - 171,149,242,58,145,237,91,104,152,42,102,152, - 25,35,35,35,229,205,120,139,193,175,50,95, - 152,201,195,107,139,205,206,149,250,255,243,205, - 225,144,251,58,230,149,183,194,198,149,17,1, - 0,42,98,152,205,171,149,250,198,149,62,1, - 50,230,149,237,91,96,152,42,104,152,25,221, - 117,252,221,116,253,237,91,104,152,42,96,152, - 25,35,35,35,221,117,254,221,116,255,35,35, - 35,229,205,39,144,124,230,3,103,35,35,35, - 221,117,250,221,116,251,235,221,110,252,221,102, - 253,115,35,114,35,54,4,62,1,211,100,211, - 84,195,198,149,33,0,0,34,102,152,34,96, - 152,34,98,152,33,202,154,34,104,152,237,91, - 104,152,42,226,149,183,237,82,17,0,255,25, - 34,100,152,33,109,152,54,0,33,107,152,229, - 205,240,142,193,62,47,50,34,152,62,132,50, - 49,152,205,241,145,205,61,145,58,39,152,60, - 50,39,152,24,241,205,206,149,251,255,33,109, - 152,126,183,202,198,149,110,221,117,251,33,109, - 152,54,0,221,126,251,254,1,40,28,254,3, - 40,101,254,4,202,190,147,254,5,202,147,147, - 254,8,40,87,33,107,152,229,205,240,142,195, - 198,149,58,201,154,183,32,21,33,111,152,126, - 50,229,149,205,52,144,33,110,152,110,38,0, - 229,205,11,142,193,237,91,96,152,42,104,152, - 25,221,117,254,221,116,255,35,35,54,2,17, - 2,0,43,43,115,35,114,58,44,152,35,35, - 119,58,228,149,35,119,62,1,211,100,211,84, - 62,1,50,201,154,24,169,205,153,142,58,231, - 149,183,40,250,175,50,231,149,33,110,152,126, - 254,255,40,91,58,233,149,230,63,183,40,83, - 94,22,0,33,234,149,25,126,183,40,13,33, - 110,152,94,33,234,150,25,126,254,3,32,36, - 205,81,148,125,180,33,110,152,94,22,0,40, - 17,33,234,149,25,54,0,33,107,152,229,205, - 240,142,193,195,198,149,33,234,150,25,54,0, - 33,110,152,94,22,0,33,234,149,25,126,50, - 49,152,254,132,32,37,62,47,50,34,152,42, - 107,152,229,33,110,152,229,205,174,140,193,193, - 125,180,33,110,152,94,22,0,33,234,150,202, - 117,147,25,52,195,120,147,58,49,152,254,140, - 32,7,62,1,50,34,152,24,210,62,32,50, - 106,152,24,19,58,49,152,95,58,106,152,163, - 183,58,106,152,32,11,203,63,50,106,152,58, - 106,152,183,32,231,254,2,40,51,254,4,40, - 38,254,8,40,26,254,16,40,13,254,32,32, - 158,62,165,50,49,152,62,69,24,190,62,164, - 50,49,152,62,70,24,181,62,163,50,49,152, - 175,24,173,62,162,50,49,152,62,1,24,164, - 62,161,50,49,152,62,3,24,155,25,54,0, - 221,126,251,254,8,40,7,58,230,149,183,202, - 32,146,33,107,152,229,205,240,142,193,211,84, - 195,198,149,237,91,96,152,42,104,152,25,221, - 117,254,221,116,255,35,35,54,6,17,2,0, - 43,43,115,35,114,58,228,149,35,35,119,58, - 233,149,35,119,205,146,142,195,32,146,237,91, - 96,152,42,104,152,25,229,205,160,142,193,58, - 231,149,183,40,250,175,50,231,149,243,237,91, - 96,152,42,104,152,25,221,117,254,221,116,255, - 78,35,70,221,113,252,221,112,253,89,80,42, - 98,152,183,237,82,34,98,152,203,124,40,19, - 33,0,0,34,98,152,34,102,152,34,96,152, - 62,1,50,95,152,24,40,221,94,252,221,86, - 253,19,19,19,42,96,152,25,34,96,152,237, - 91,100,152,33,158,253,25,237,91,96,152,205, - 171,149,242,55,148,33,0,0,34,96,152,175, - 50,230,149,251,195,32,146,245,62,1,50,231, - 149,62,16,237,57,0,211,80,241,251,237,77, - 201,205,186,149,229,229,33,0,0,34,37,152, - 33,110,152,126,50,234,151,58,44,152,33,235, - 151,119,221,54,253,0,221,54,254,0,195,230, - 148,33,236,151,54,175,33,3,0,229,33,234, - 151,229,205,174,140,193,193,33,236,151,126,254, - 255,40,74,33,245,151,110,221,117,255,33,249, - 151,126,221,166,255,221,119,255,33,253,151,126, - 221,166,255,221,119,255,58,232,149,95,221,126, - 255,163,221,119,255,183,40,15,230,191,33,110, - 152,94,22,0,33,234,149,25,119,24,12,33, - 110,152,94,22,0,33,234,149,25,54,132,33, - 0,0,195,198,149,221,110,253,221,102,254,35, - 221,117,253,221,116,254,17,32,0,221,110,253, - 221,102,254,205,171,149,250,117,148,58,233,149, - 203,87,40,84,33,1,0,34,37,152,221,54, - 253,0,221,54,254,0,24,53,33,236,151,54, - 175,33,3,0,229,33,234,151,229,205,174,140, - 193,193,33,236,151,126,254,255,40,14,33,110, - 152,94,22,0,33,234,149,25,54,140,24,159, - 221,110,253,221,102,254,35,221,117,253,221,116, - 254,17,32,0,221,110,253,221,102,254,205,171, - 149,250,12,149,33,2,0,34,37,152,221,54, - 253,0,221,54,254,0,24,54,33,236,151,54, - 175,33,3,0,229,33,234,151,229,205,174,140, - 193,193,33,236,151,126,254,255,40,15,33,110, - 152,94,22,0,33,234,149,25,54,132,195,211, - 148,221,110,253,221,102,254,35,221,117,253,221, - 116,254,17,32,0,221,110,253,221,102,254,205, - 171,149,250,96,149,33,1,0,195,198,149,124, - 170,250,179,149,237,82,201,124,230,128,237,82, - 60,201,225,253,229,221,229,221,33,0,0,221, - 57,233,221,249,221,225,253,225,201,233,225,253, - 229,221,229,221,33,0,0,221,57,94,35,86, - 35,235,57,249,235,233,0,0,0,0,0,0, - 62,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 175,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,133,1,0,0,0,63, - 255,255,255,255,0,0,0,63,0,0,0,0, - 0,0,0,0,0,0,0,24,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0 - } ; - -#endif diff --git a/drivers/staging/appletalk/cops_ltdrv.h b/drivers/staging/appletalk/cops_ltdrv.h deleted file mode 100644 index c699b1ad31da..000000000000 --- a/drivers/staging/appletalk/cops_ltdrv.h +++ /dev/null @@ -1,241 +0,0 @@ -/* - * The firmware this driver downloads into the Localtalk card is a - * separate program and is not GPL'd source code, even though the Linux - * side driver and the routine that loads this data into the card are. - * - * It is taken from the COPS SDK and is under the following license - * - * This material is licensed to you strictly for use in conjunction with - * the use of COPS LocalTalk adapters. - * There is no charge for this SDK. And no waranty express or implied - * about its fitness for any purpose. However, we will cheerefully - * refund every penny you paid for this SDK... - * Regards, - * - * Thomas F. Divine - * Chief Scientist - */ - - -/* cops_ltdrv.h: LocalTalk driver firmware dump for Linux. - * - * Authors: - * - Jay Schulist - */ - - -#ifdef CONFIG_COPS_TANGENT - -static const unsigned char ltdrv_code[] = { - 58,3,0,50,148,10,33,143,15,62,85,119, - 190,32,9,62,170,119,190,32,3,35,24,241, - 34,146,10,249,17,150,10,33,143,15,183,237, - 82,77,68,11,107,98,19,54,0,237,176,62, - 16,237,57,51,62,0,237,57,50,237,57,54, - 62,12,237,57,49,62,195,33,39,2,50,56, - 0,34,57,0,237,86,205,30,2,251,205,60, - 10,24,169,67,111,112,121,114,105,103,104,116, - 32,40,99,41,32,49,57,56,56,45,49,57, - 57,50,44,32,80,114,105,110,116,105,110,103, - 32,67,111,109,109,117,110,105,99,97,116,105, - 111,110,115,32,65,115,115,111,99,105,97,116, - 101,115,44,32,73,110,99,46,65,108,108,32, - 114,105,103,104,116,115,32,114,101,115,101,114, - 118,101,100,46,32,32,4,4,22,40,255,60, - 4,96,10,224,6,0,7,126,2,64,11,246, - 12,6,13,0,14,193,15,0,5,96,3,192, - 1,0,9,8,62,3,211,82,62,192,211,82, - 201,62,3,211,82,62,213,211,82,201,62,5, - 211,82,62,224,211,82,201,62,5,211,82,62, - 224,211,82,201,62,5,211,82,62,96,211,82, - 201,6,28,33,180,1,14,82,237,163,194,4, - 2,33,39,2,34,64,0,58,3,0,230,1, - 192,62,11,237,121,62,118,237,121,201,33,182, - 10,54,132,205,253,1,201,245,197,213,229,42, - 150,10,14,83,17,98,2,67,20,237,162,58, - 179,1,95,219,82,230,1,32,6,29,32,247, - 195,17,3,62,1,211,82,219,82,95,230,160, - 32,10,237,162,32,225,21,32,222,195,15,3, - 237,162,123,230,96,194,21,3,62,48,211,82, - 62,1,211,82,175,211,82,237,91,150,10,43, - 55,237,82,218,19,3,34,152,10,98,107,58, - 154,10,190,32,81,62,1,50,158,10,35,35, - 62,132,190,32,44,54,133,43,70,58,154,10, - 119,43,112,17,3,0,205,137,3,62,16,211, - 82,62,56,211,82,205,217,1,42,150,10,14, - 83,17,98,2,67,20,58,178,1,95,195,59, - 2,62,129,190,194,227,2,54,130,43,70,58, - 154,10,119,43,112,17,3,0,205,137,3,195, - 254,2,35,35,126,254,132,194,227,2,205,61, - 3,24,20,62,128,166,194,222,2,221,229,221, - 33,175,10,205,93,6,205,144,7,221,225,225, - 209,193,241,251,237,77,221,229,221,33,159,10, - 205,93,6,221,225,205,61,3,195,247,2,24, - 237,24,235,24,233,230,64,40,2,24,227,24, - 225,175,50,179,10,205,208,1,201,197,33,4, - 0,57,126,35,102,111,205,51,3,193,201,62, - 1,50,179,10,34,150,10,54,0,58,179,10, - 183,200,62,14,211,82,62,193,211,82,62,10, - 211,82,62,224,211,82,62,6,211,82,58,154, - 10,211,82,62,16,211,82,62,56,211,82,62, - 48,211,82,219,82,230,1,40,4,219,83,24, - 242,62,14,211,82,62,33,211,82,62,1,211, - 82,62,9,211,82,62,32,211,82,205,217,1, - 201,14,83,205,208,1,24,23,14,83,205,208, - 1,205,226,1,58,174,1,61,32,253,205,244, - 1,58,174,1,61,32,253,205,226,1,58,175, - 1,61,32,253,62,5,211,82,62,233,211,82, - 62,128,211,82,58,176,1,61,32,253,237,163, - 27,62,192,211,82,219,82,230,4,40,250,237, - 163,27,122,179,32,243,219,82,230,4,40,250, - 58,178,1,71,219,82,230,4,40,3,5,32, - 247,219,82,230,4,40,250,205,235,1,58,177, - 1,61,32,253,205,244,1,201,229,213,35,35, - 126,230,128,194,145,4,43,58,154,10,119,43, - 70,33,181,10,119,43,112,17,3,0,243,62, - 10,211,82,219,82,230,128,202,41,4,209,225, - 62,1,55,251,201,205,144,3,58,180,10,254, - 255,202,127,4,205,217,1,58,178,1,71,219, - 82,230,1,32,6,5,32,247,195,173,4,219, - 83,71,58,154,10,184,194,173,4,58,178,1, - 71,219,82,230,1,32,6,5,32,247,195,173, - 4,219,83,58,178,1,71,219,82,230,1,32, - 6,5,32,247,195,173,4,219,83,254,133,194, - 173,4,58,179,1,24,4,58,179,1,135,61, - 32,253,209,225,205,137,3,205,61,3,183,251, - 201,209,225,243,62,10,211,82,219,82,230,128, - 202,164,4,62,1,55,251,201,205,144,3,205, - 61,3,183,251,201,209,225,62,2,55,251,201, - 243,62,14,211,82,62,33,211,82,251,201,33, - 4,0,57,94,35,86,33,2,0,57,126,35, - 102,111,221,229,34,193,10,237,83,195,10,221, - 33,171,10,205,93,6,58,185,10,50,186,10, - 58,184,10,135,50,184,10,205,112,6,254,3, - 56,16,58,185,10,135,60,230,15,50,185,10, - 175,50,184,10,24,23,58,183,10,205,112,6, - 254,3,48,13,58,185,10,203,63,50,185,10, - 62,255,50,183,10,58,185,10,50,186,10,58, - 183,10,135,50,183,10,62,32,50,187,10,50, - 188,10,6,255,219,82,230,16,32,3,5,32, - 247,205,180,4,6,40,219,82,230,16,40,3, - 5,32,247,62,10,211,82,219,82,230,128,194, - 46,5,219,82,230,16,40,214,237,95,71,58, - 186,10,160,230,15,40,32,71,14,10,62,10, - 211,82,219,82,230,128,202,119,5,205,180,4, - 195,156,5,219,82,230,16,202,156,5,13,32, - 229,16,225,42,193,10,237,91,195,10,205,252, - 3,48,7,61,202,156,5,195,197,5,221,225, - 33,0,0,201,221,33,163,10,205,93,6,58, - 188,10,61,50,188,10,40,19,58,186,10,246, - 1,50,186,10,58,183,10,246,1,50,183,10, - 195,46,5,221,225,33,1,0,201,221,33,167, - 10,205,93,6,58,184,10,246,1,50,184,10, - 58,186,10,135,246,1,50,186,10,58,187,10, - 61,50,187,10,194,46,5,221,225,33,2,0, - 201,221,229,33,0,0,57,17,4,0,25,126, - 50,154,10,230,128,50,189,10,58,189,10,183, - 40,6,221,33,88,2,24,4,221,33,150,0, - 58,154,10,183,40,49,60,40,46,61,33,190, - 10,119,35,119,35,54,129,175,50,158,10,221, - 43,221,229,225,124,181,40,42,33,190,10,17, - 3,0,205,206,4,17,232,3,27,123,178,32, - 251,58,158,10,183,40,224,58,154,10,71,62, - 7,128,230,127,71,58,189,10,176,50,154,10, - 24,166,221,225,201,183,221,52,0,192,221,52, - 1,192,221,52,2,192,221,52,3,192,55,201, - 6,8,14,0,31,48,1,12,16,250,121,201, - 33,2,0,57,94,35,86,35,78,35,70,35, - 126,35,102,105,79,120,68,103,237,176,201,33, - 2,0,57,126,35,102,111,62,17,237,57,48, - 125,237,57,40,124,237,57,41,62,0,237,57, - 42,62,64,237,57,43,62,0,237,57,44,33, - 128,2,125,237,57,46,124,237,57,47,62,145, - 237,57,48,211,68,58,149,10,211,66,201,33, - 2,0,57,126,35,102,111,62,33,237,57,48, - 62,64,237,57,32,62,0,237,57,33,237,57, - 34,125,237,57,35,124,237,57,36,62,0,237, - 57,37,33,128,2,125,237,57,38,124,237,57, - 39,62,97,237,57,48,211,67,58,149,10,211, - 66,201,237,56,46,95,237,56,47,87,237,56, - 46,111,237,56,47,103,183,237,82,32,235,33, - 128,2,183,237,82,201,237,56,38,95,237,56, - 39,87,237,56,38,111,237,56,39,103,183,237, - 82,32,235,33,128,2,183,237,82,201,205,106, - 10,221,110,6,221,102,7,126,35,110,103,195, - 118,10,205,106,10,33,0,0,34,205,10,34, - 198,10,34,200,10,33,143,15,34,207,10,237, - 91,207,10,42,146,10,183,237,82,17,0,255, - 25,34,203,10,203,124,40,6,33,0,125,34, - 203,10,42,207,10,229,205,37,3,195,118,10, - 205,106,10,229,42,150,10,35,35,35,229,205, - 70,7,193,124,230,3,103,221,117,254,221,116, - 255,237,91,152,10,35,35,35,183,237,82,32, - 12,17,5,0,42,152,10,205,91,10,242,203, - 7,42,150,10,229,205,37,3,195,118,10,237, - 91,152,10,42,200,10,25,34,200,10,42,205, - 10,25,34,205,10,237,91,203,10,33,158,253, - 25,237,91,205,10,205,91,10,242,245,7,33, - 0,0,34,205,10,62,1,50,197,10,205,5, - 8,33,0,0,57,249,195,118,10,205,106,10, - 58,197,10,183,202,118,10,237,91,198,10,42, - 205,10,205,91,10,242,46,8,237,91,205,10, - 33,98,2,25,237,91,198,10,205,91,10,250, - 78,8,237,91,198,10,42,205,10,183,237,82, - 32,7,42,200,10,125,180,40,13,237,91,205, - 10,42,198,10,205,91,10,242,97,8,237,91, - 207,10,42,205,10,25,229,205,37,3,175,50, - 197,10,195,118,10,205,29,3,33,0,0,57, - 249,195,118,10,205,106,10,58,202,10,183,40, - 22,205,14,7,237,91,209,10,19,19,19,205, - 91,10,242,139,8,33,1,0,195,118,10,33, - 0,0,195,118,10,205,126,10,252,255,205,108, - 8,125,180,194,118,10,237,91,200,10,33,0, - 0,205,91,10,242,118,10,237,91,207,10,42, - 198,10,25,221,117,254,221,116,255,35,35,35, - 229,205,70,7,193,124,230,3,103,35,35,35, - 221,117,252,221,116,253,229,221,110,254,221,102, - 255,229,33,212,10,229,205,124,6,193,193,221, - 110,252,221,102,253,34,209,10,33,211,10,54, - 4,33,209,10,227,205,147,6,193,62,1,50, - 202,10,243,221,94,252,221,86,253,42,200,10, - 183,237,82,34,200,10,203,124,40,17,33,0, - 0,34,200,10,34,205,10,34,198,10,50,197, - 10,24,37,221,94,252,221,86,253,42,198,10, - 25,34,198,10,237,91,203,10,33,158,253,25, - 237,91,198,10,205,91,10,242,68,9,33,0, - 0,34,198,10,205,5,8,33,0,0,57,249, - 251,195,118,10,205,106,10,33,49,13,126,183, - 40,16,205,42,7,237,91,47,13,19,19,19, - 205,91,10,242,117,9,58,142,15,198,1,50, - 142,15,195,118,10,33,49,13,126,254,1,40, - 25,254,3,202,7,10,254,5,202,21,10,33, - 49,13,54,0,33,47,13,229,205,207,6,195, - 118,10,58,141,15,183,32,72,33,51,13,126, - 50,149,10,205,86,7,33,50,13,126,230,127, - 183,32,40,58,142,15,230,127,50,142,15,183, - 32,5,198,1,50,142,15,33,50,13,126,111, - 23,159,103,203,125,58,142,15,40,5,198,128, - 50,142,15,33,50,13,119,33,50,13,126,111, - 23,159,103,229,205,237,5,193,33,211,10,54, - 2,33,2,0,34,209,10,58,154,10,33,212, - 10,119,58,148,10,33,213,10,119,33,209,10, - 229,205,147,6,193,24,128,42,47,13,229,33, - 50,13,229,205,191,4,193,24,239,33,211,10, - 54,6,33,3,0,34,209,10,58,154,10,33, - 212,10,119,58,148,10,33,213,10,119,33,214, - 10,54,5,33,209,10,229,205,147,6,24,200, - 205,106,10,33,49,13,54,0,33,47,13,229, - 205,207,6,33,209,10,227,205,147,6,193,205, - 80,9,205,145,8,24,248,124,170,250,99,10, - 237,82,201,124,230,128,237,82,60,201,225,253, - 229,221,229,221,33,0,0,221,57,233,221,249, - 221,225,253,225,201,233,225,253,229,221,229,221, - 33,0,0,221,57,94,35,86,35,235,57,249, - 235,233,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0 - } ; - -#endif diff --git a/drivers/staging/appletalk/ddp.c b/drivers/staging/appletalk/ddp.c deleted file mode 100644 index 940dd1908339..000000000000 --- a/drivers/staging/appletalk/ddp.c +++ /dev/null @@ -1,1981 +0,0 @@ -/* - * DDP: An implementation of the AppleTalk DDP protocol for - * Ethernet 'ELAP'. - * - * Alan Cox - * - * With more than a little assistance from - * - * Wesley Craig - * - * Fixes: - * Neil Horman : Added missing device ioctls - * Michael Callahan : Made routing work - * Wesley Craig : Fix probing to listen to a - * passed node id. - * Alan Cox : Added send/recvmsg support - * Alan Cox : Moved at. to protinfo in - * socket. - * Alan Cox : Added firewall hooks. - * Alan Cox : Supports new ARPHRD_LOOPBACK - * Christer Weinigel : Routing and /proc fixes. - * Bradford Johnson : LocalTalk. - * Tom Dyas : Module support. - * Alan Cox : Hooks for PPP (based on the - * LocalTalk hook). - * Alan Cox : Posix bits - * Alan Cox/Mike Freeman : Possible fix to NBP problems - * Bradford Johnson : IP-over-DDP (experimental) - * Jay Schulist : Moved IP-over-DDP to its own - * driver file. (ipddp.c & ipddp.h) - * Jay Schulist : Made work as module with - * AppleTalk drivers, cleaned it. - * Rob Newberry : Added proxy AARP and AARP - * procfs, moved probing to AARP - * module. - * Adrian Sun/ - * Michael Zuelsdorff : fix for net.0 packets. don't - * allow illegal ether/tokentalk - * port assignment. we lose a - * valid localtalk port as a - * result. - * Arnaldo C. de Melo : Cleanup, in preparation for - * shared skb support 8) - * Arnaldo C. de Melo : Move proc stuff to atalk_proc.c, - * use seq_file - * - * 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 /* For TIOCOUTQ/INQ */ -#include -#include -#include -#include -#include -#include -#include -#include "atalk.h" -#include "../../net/core/kmap_skb.h" - -struct datalink_proto *ddp_dl, *aarp_dl; -static const struct proto_ops atalk_dgram_ops; - -/**************************************************************************\ -* * -* Handlers for the socket list. * -* * -\**************************************************************************/ - -HLIST_HEAD(atalk_sockets); -DEFINE_RWLOCK(atalk_sockets_lock); - -static inline void __atalk_insert_socket(struct sock *sk) -{ - sk_add_node(sk, &atalk_sockets); -} - -static inline void atalk_remove_socket(struct sock *sk) -{ - write_lock_bh(&atalk_sockets_lock); - sk_del_node_init(sk); - write_unlock_bh(&atalk_sockets_lock); -} - -static struct sock *atalk_search_socket(struct sockaddr_at *to, - struct atalk_iface *atif) -{ - struct sock *s; - struct hlist_node *node; - - read_lock_bh(&atalk_sockets_lock); - sk_for_each(s, node, &atalk_sockets) { - struct atalk_sock *at = at_sk(s); - - if (to->sat_port != at->src_port) - continue; - - if (to->sat_addr.s_net == ATADDR_ANYNET && - to->sat_addr.s_node == ATADDR_BCAST) - goto found; - - if (to->sat_addr.s_net == at->src_net && - (to->sat_addr.s_node == at->src_node || - to->sat_addr.s_node == ATADDR_BCAST || - to->sat_addr.s_node == ATADDR_ANYNODE)) - goto found; - - /* XXXX.0 -- we got a request for this router. make sure - * that the node is appropriately set. */ - if (to->sat_addr.s_node == ATADDR_ANYNODE && - to->sat_addr.s_net != ATADDR_ANYNET && - atif->address.s_node == at->src_node) { - to->sat_addr.s_node = atif->address.s_node; - goto found; - } - } - s = NULL; -found: - read_unlock_bh(&atalk_sockets_lock); - return s; -} - -/** - * atalk_find_or_insert_socket - Try to find a socket matching ADDR - * @sk - socket to insert in the list if it is not there already - * @sat - address to search for - * - * Try to find a socket matching ADDR in the socket list, if found then return - * it. If not, insert SK into the socket list. - * - * This entire operation must execute atomically. - */ -static struct sock *atalk_find_or_insert_socket(struct sock *sk, - struct sockaddr_at *sat) -{ - struct sock *s; - struct hlist_node *node; - struct atalk_sock *at; - - write_lock_bh(&atalk_sockets_lock); - sk_for_each(s, node, &atalk_sockets) { - at = at_sk(s); - - if (at->src_net == sat->sat_addr.s_net && - at->src_node == sat->sat_addr.s_node && - at->src_port == sat->sat_port) - goto found; - } - s = NULL; - __atalk_insert_socket(sk); /* Wheee, it's free, assign and insert. */ -found: - write_unlock_bh(&atalk_sockets_lock); - return s; -} - -static void atalk_destroy_timer(unsigned long data) -{ - struct sock *sk = (struct sock *)data; - - if (sk_has_allocations(sk)) { - sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME; - add_timer(&sk->sk_timer); - } else - sock_put(sk); -} - -static inline void atalk_destroy_socket(struct sock *sk) -{ - atalk_remove_socket(sk); - skb_queue_purge(&sk->sk_receive_queue); - - if (sk_has_allocations(sk)) { - setup_timer(&sk->sk_timer, atalk_destroy_timer, - (unsigned long)sk); - sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME; - add_timer(&sk->sk_timer); - } else - sock_put(sk); -} - -/**************************************************************************\ -* * -* Routing tables for the AppleTalk socket layer. * -* * -\**************************************************************************/ - -/* Anti-deadlock ordering is atalk_routes_lock --> iface_lock -DaveM */ -struct atalk_route *atalk_routes; -DEFINE_RWLOCK(atalk_routes_lock); - -struct atalk_iface *atalk_interfaces; -DEFINE_RWLOCK(atalk_interfaces_lock); - -/* For probing devices or in a routerless network */ -struct atalk_route atrtr_default; - -/* AppleTalk interface control */ -/* - * Drop a device. Doesn't drop any of its routes - that is the caller's - * problem. Called when we down the interface or delete the address. - */ -static void atif_drop_device(struct net_device *dev) -{ - struct atalk_iface **iface = &atalk_interfaces; - struct atalk_iface *tmp; - - write_lock_bh(&atalk_interfaces_lock); - while ((tmp = *iface) != NULL) { - if (tmp->dev == dev) { - *iface = tmp->next; - dev_put(dev); - kfree(tmp); - dev->atalk_ptr = NULL; - } else - iface = &tmp->next; - } - write_unlock_bh(&atalk_interfaces_lock); -} - -static struct atalk_iface *atif_add_device(struct net_device *dev, - struct atalk_addr *sa) -{ - struct atalk_iface *iface = kzalloc(sizeof(*iface), GFP_KERNEL); - - if (!iface) - goto out; - - dev_hold(dev); - iface->dev = dev; - dev->atalk_ptr = iface; - iface->address = *sa; - iface->status = 0; - - write_lock_bh(&atalk_interfaces_lock); - iface->next = atalk_interfaces; - atalk_interfaces = iface; - write_unlock_bh(&atalk_interfaces_lock); -out: - return iface; -} - -/* Perform phase 2 AARP probing on our tentative address */ -static int atif_probe_device(struct atalk_iface *atif) -{ - int netrange = ntohs(atif->nets.nr_lastnet) - - ntohs(atif->nets.nr_firstnet) + 1; - int probe_net = ntohs(atif->address.s_net); - int probe_node = atif->address.s_node; - int netct, nodect; - - /* Offset the network we start probing with */ - if (probe_net == ATADDR_ANYNET) { - probe_net = ntohs(atif->nets.nr_firstnet); - if (netrange) - probe_net += jiffies % netrange; - } - if (probe_node == ATADDR_ANYNODE) - probe_node = jiffies & 0xFF; - - /* Scan the networks */ - atif->status |= ATIF_PROBE; - for (netct = 0; netct <= netrange; netct++) { - /* Sweep the available nodes from a given start */ - atif->address.s_net = htons(probe_net); - for (nodect = 0; nodect < 256; nodect++) { - atif->address.s_node = (nodect + probe_node) & 0xFF; - if (atif->address.s_node > 0 && - atif->address.s_node < 254) { - /* Probe a proposed address */ - aarp_probe_network(atif); - - if (!(atif->status & ATIF_PROBE_FAIL)) { - atif->status &= ~ATIF_PROBE; - return 0; - } - } - atif->status &= ~ATIF_PROBE_FAIL; - } - probe_net++; - if (probe_net > ntohs(atif->nets.nr_lastnet)) - probe_net = ntohs(atif->nets.nr_firstnet); - } - atif->status &= ~ATIF_PROBE; - - return -EADDRINUSE; /* Network is full... */ -} - - -/* Perform AARP probing for a proxy address */ -static int atif_proxy_probe_device(struct atalk_iface *atif, - struct atalk_addr* proxy_addr) -{ - int netrange = ntohs(atif->nets.nr_lastnet) - - ntohs(atif->nets.nr_firstnet) + 1; - /* we probe the interface's network */ - int probe_net = ntohs(atif->address.s_net); - int probe_node = ATADDR_ANYNODE; /* we'll take anything */ - int netct, nodect; - - /* Offset the network we start probing with */ - if (probe_net == ATADDR_ANYNET) { - probe_net = ntohs(atif->nets.nr_firstnet); - if (netrange) - probe_net += jiffies % netrange; - } - - if (probe_node == ATADDR_ANYNODE) - probe_node = jiffies & 0xFF; - - /* Scan the networks */ - for (netct = 0; netct <= netrange; netct++) { - /* Sweep the available nodes from a given start */ - proxy_addr->s_net = htons(probe_net); - for (nodect = 0; nodect < 256; nodect++) { - proxy_addr->s_node = (nodect + probe_node) & 0xFF; - if (proxy_addr->s_node > 0 && - proxy_addr->s_node < 254) { - /* Tell AARP to probe a proposed address */ - int ret = aarp_proxy_probe_network(atif, - proxy_addr); - - if (ret != -EADDRINUSE) - return ret; - } - } - probe_net++; - if (probe_net > ntohs(atif->nets.nr_lastnet)) - probe_net = ntohs(atif->nets.nr_firstnet); - } - - return -EADDRINUSE; /* Network is full... */ -} - - -struct atalk_addr *atalk_find_dev_addr(struct net_device *dev) -{ - struct atalk_iface *iface = dev->atalk_ptr; - return iface ? &iface->address : NULL; -} - -static struct atalk_addr *atalk_find_primary(void) -{ - struct atalk_iface *fiface = NULL; - struct atalk_addr *retval; - struct atalk_iface *iface; - - /* - * Return a point-to-point interface only if - * there is no non-ptp interface available. - */ - read_lock_bh(&atalk_interfaces_lock); - for (iface = atalk_interfaces; iface; iface = iface->next) { - if (!fiface && !(iface->dev->flags & IFF_LOOPBACK)) - fiface = iface; - if (!(iface->dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) { - retval = &iface->address; - goto out; - } - } - - if (fiface) - retval = &fiface->address; - else if (atalk_interfaces) - retval = &atalk_interfaces->address; - else - retval = NULL; -out: - read_unlock_bh(&atalk_interfaces_lock); - return retval; -} - -/* - * Find a match for 'any network' - ie any of our interfaces with that - * node number will do just nicely. - */ -static struct atalk_iface *atalk_find_anynet(int node, struct net_device *dev) -{ - struct atalk_iface *iface = dev->atalk_ptr; - - if (!iface || iface->status & ATIF_PROBE) - goto out_err; - - if (node != ATADDR_BCAST && - iface->address.s_node != node && - node != ATADDR_ANYNODE) - goto out_err; -out: - return iface; -out_err: - iface = NULL; - goto out; -} - -/* Find a match for a specific network:node pair */ -static struct atalk_iface *atalk_find_interface(__be16 net, int node) -{ - struct atalk_iface *iface; - - read_lock_bh(&atalk_interfaces_lock); - for (iface = atalk_interfaces; iface; iface = iface->next) { - if ((node == ATADDR_BCAST || - node == ATADDR_ANYNODE || - iface->address.s_node == node) && - iface->address.s_net == net && - !(iface->status & ATIF_PROBE)) - break; - - /* XXXX.0 -- net.0 returns the iface associated with net */ - if (node == ATADDR_ANYNODE && net != ATADDR_ANYNET && - ntohs(iface->nets.nr_firstnet) <= ntohs(net) && - ntohs(net) <= ntohs(iface->nets.nr_lastnet)) - break; - } - read_unlock_bh(&atalk_interfaces_lock); - return iface; -} - - -/* - * Find a route for an AppleTalk packet. This ought to get cached in - * the socket (later on...). We know about host routes and the fact - * that a route must be direct to broadcast. - */ -static struct atalk_route *atrtr_find(struct atalk_addr *target) -{ - /* - * we must search through all routes unless we find a - * host route, because some host routes might overlap - * network routes - */ - struct atalk_route *net_route = NULL; - struct atalk_route *r; - - read_lock_bh(&atalk_routes_lock); - for (r = atalk_routes; r; r = r->next) { - if (!(r->flags & RTF_UP)) - continue; - - if (r->target.s_net == target->s_net) { - if (r->flags & RTF_HOST) { - /* - * if this host route is for the target, - * the we're done - */ - if (r->target.s_node == target->s_node) - goto out; - } else - /* - * this route will work if there isn't a - * direct host route, so cache it - */ - net_route = r; - } - } - - /* - * if we found a network route but not a direct host - * route, then return it - */ - if (net_route) - r = net_route; - else if (atrtr_default.dev) - r = &atrtr_default; - else /* No route can be found */ - r = NULL; -out: - read_unlock_bh(&atalk_routes_lock); - return r; -} - - -/* - * Given an AppleTalk network, find the device to use. This can be - * a simple lookup. - */ -struct net_device *atrtr_get_dev(struct atalk_addr *sa) -{ - struct atalk_route *atr = atrtr_find(sa); - return atr ? atr->dev : NULL; -} - -/* Set up a default router */ -static void atrtr_set_default(struct net_device *dev) -{ - atrtr_default.dev = dev; - atrtr_default.flags = RTF_UP; - atrtr_default.gateway.s_net = htons(0); - atrtr_default.gateway.s_node = 0; -} - -/* - * Add a router. Basically make sure it looks valid and stuff the - * entry in the list. While it uses netranges we always set them to one - * entry to work like netatalk. - */ -static int atrtr_create(struct rtentry *r, struct net_device *devhint) -{ - struct sockaddr_at *ta = (struct sockaddr_at *)&r->rt_dst; - struct sockaddr_at *ga = (struct sockaddr_at *)&r->rt_gateway; - struct atalk_route *rt; - struct atalk_iface *iface, *riface; - int retval = -EINVAL; - - /* - * Fixme: Raise/Lower a routing change semaphore for these - * operations. - */ - - /* Validate the request */ - if (ta->sat_family != AF_APPLETALK || - (!devhint && ga->sat_family != AF_APPLETALK)) - goto out; - - /* Now walk the routing table and make our decisions */ - write_lock_bh(&atalk_routes_lock); - for (rt = atalk_routes; rt; rt = rt->next) { - if (r->rt_flags != rt->flags) - continue; - - if (ta->sat_addr.s_net == rt->target.s_net) { - if (!(rt->flags & RTF_HOST)) - break; - if (ta->sat_addr.s_node == rt->target.s_node) - break; - } - } - - if (!devhint) { - riface = NULL; - - read_lock_bh(&atalk_interfaces_lock); - for (iface = atalk_interfaces; iface; iface = iface->next) { - if (!riface && - ntohs(ga->sat_addr.s_net) >= - ntohs(iface->nets.nr_firstnet) && - ntohs(ga->sat_addr.s_net) <= - ntohs(iface->nets.nr_lastnet)) - riface = iface; - - if (ga->sat_addr.s_net == iface->address.s_net && - ga->sat_addr.s_node == iface->address.s_node) - riface = iface; - } - read_unlock_bh(&atalk_interfaces_lock); - - retval = -ENETUNREACH; - if (!riface) - goto out_unlock; - - devhint = riface->dev; - } - - if (!rt) { - rt = kzalloc(sizeof(*rt), GFP_ATOMIC); - - retval = -ENOBUFS; - if (!rt) - goto out_unlock; - - rt->next = atalk_routes; - atalk_routes = rt; - } - - /* Fill in the routing entry */ - rt->target = ta->sat_addr; - dev_hold(devhint); - rt->dev = devhint; - rt->flags = r->rt_flags; - rt->gateway = ga->sat_addr; - - retval = 0; -out_unlock: - write_unlock_bh(&atalk_routes_lock); -out: - return retval; -} - -/* Delete a route. Find it and discard it */ -static int atrtr_delete(struct atalk_addr * addr) -{ - struct atalk_route **r = &atalk_routes; - int retval = 0; - struct atalk_route *tmp; - - write_lock_bh(&atalk_routes_lock); - while ((tmp = *r) != NULL) { - if (tmp->target.s_net == addr->s_net && - (!(tmp->flags&RTF_GATEWAY) || - tmp->target.s_node == addr->s_node)) { - *r = tmp->next; - dev_put(tmp->dev); - kfree(tmp); - goto out; - } - r = &tmp->next; - } - retval = -ENOENT; -out: - write_unlock_bh(&atalk_routes_lock); - return retval; -} - -/* - * Called when a device is downed. Just throw away any routes - * via it. - */ -static void atrtr_device_down(struct net_device *dev) -{ - struct atalk_route **r = &atalk_routes; - struct atalk_route *tmp; - - write_lock_bh(&atalk_routes_lock); - while ((tmp = *r) != NULL) { - if (tmp->dev == dev) { - *r = tmp->next; - dev_put(dev); - kfree(tmp); - } else - r = &tmp->next; - } - write_unlock_bh(&atalk_routes_lock); - - if (atrtr_default.dev == dev) - atrtr_set_default(NULL); -} - -/* Actually down the interface */ -static inline void atalk_dev_down(struct net_device *dev) -{ - atrtr_device_down(dev); /* Remove all routes for the device */ - aarp_device_down(dev); /* Remove AARP entries for the device */ - atif_drop_device(dev); /* Remove the device */ -} - -/* - * A device event has occurred. Watch for devices going down and - * delete our use of them (iface and route). - */ -static int ddp_device_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - struct net_device *dev = ptr; - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (event == NETDEV_DOWN) - /* Discard any use of this */ - atalk_dev_down(dev); - - return NOTIFY_DONE; -} - -/* ioctl calls. Shouldn't even need touching */ -/* Device configuration ioctl calls */ -static int atif_ioctl(int cmd, void __user *arg) -{ - static char aarp_mcast[6] = { 0x09, 0x00, 0x00, 0xFF, 0xFF, 0xFF }; - struct ifreq atreq; - struct atalk_netrange *nr; - struct sockaddr_at *sa; - struct net_device *dev; - struct atalk_iface *atif; - int ct; - int limit; - struct rtentry rtdef; - int add_route; - - if (copy_from_user(&atreq, arg, sizeof(atreq))) - return -EFAULT; - - dev = __dev_get_by_name(&init_net, atreq.ifr_name); - if (!dev) - return -ENODEV; - - sa = (struct sockaddr_at *)&atreq.ifr_addr; - atif = atalk_find_dev(dev); - - switch (cmd) { - case SIOCSIFADDR: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (sa->sat_family != AF_APPLETALK) - return -EINVAL; - if (dev->type != ARPHRD_ETHER && - dev->type != ARPHRD_LOOPBACK && - dev->type != ARPHRD_LOCALTLK && - dev->type != ARPHRD_PPP) - return -EPROTONOSUPPORT; - - nr = (struct atalk_netrange *)&sa->sat_zero[0]; - add_route = 1; - - /* - * if this is a point-to-point iface, and we already - * have an iface for this AppleTalk address, then we - * should not add a route - */ - if ((dev->flags & IFF_POINTOPOINT) && - atalk_find_interface(sa->sat_addr.s_net, - sa->sat_addr.s_node)) { - printk(KERN_DEBUG "AppleTalk: point-to-point " - "interface added with " - "existing address\n"); - add_route = 0; - } - - /* - * Phase 1 is fine on LocalTalk but we don't do - * EtherTalk phase 1. Anyone wanting to add it go ahead. - */ - if (dev->type == ARPHRD_ETHER && nr->nr_phase != 2) - return -EPROTONOSUPPORT; - if (sa->sat_addr.s_node == ATADDR_BCAST || - sa->sat_addr.s_node == 254) - return -EINVAL; - if (atif) { - /* Already setting address */ - if (atif->status & ATIF_PROBE) - return -EBUSY; - - atif->address.s_net = sa->sat_addr.s_net; - atif->address.s_node = sa->sat_addr.s_node; - atrtr_device_down(dev); /* Flush old routes */ - } else { - atif = atif_add_device(dev, &sa->sat_addr); - if (!atif) - return -ENOMEM; - } - atif->nets = *nr; - - /* - * Check if the chosen address is used. If so we - * error and atalkd will try another. - */ - - if (!(dev->flags & IFF_LOOPBACK) && - !(dev->flags & IFF_POINTOPOINT) && - atif_probe_device(atif) < 0) { - atif_drop_device(dev); - return -EADDRINUSE; - } - - /* Hey it worked - add the direct routes */ - sa = (struct sockaddr_at *)&rtdef.rt_gateway; - sa->sat_family = AF_APPLETALK; - sa->sat_addr.s_net = atif->address.s_net; - sa->sat_addr.s_node = atif->address.s_node; - sa = (struct sockaddr_at *)&rtdef.rt_dst; - rtdef.rt_flags = RTF_UP; - sa->sat_family = AF_APPLETALK; - sa->sat_addr.s_node = ATADDR_ANYNODE; - if (dev->flags & IFF_LOOPBACK || - dev->flags & IFF_POINTOPOINT) - rtdef.rt_flags |= RTF_HOST; - - /* Routerless initial state */ - if (nr->nr_firstnet == htons(0) && - nr->nr_lastnet == htons(0xFFFE)) { - sa->sat_addr.s_net = atif->address.s_net; - atrtr_create(&rtdef, dev); - atrtr_set_default(dev); - } else { - limit = ntohs(nr->nr_lastnet); - if (limit - ntohs(nr->nr_firstnet) > 4096) { - printk(KERN_WARNING "Too many routes/" - "iface.\n"); - return -EINVAL; - } - if (add_route) - for (ct = ntohs(nr->nr_firstnet); - ct <= limit; ct++) { - sa->sat_addr.s_net = htons(ct); - atrtr_create(&rtdef, dev); - } - } - dev_mc_add_global(dev, aarp_mcast); - return 0; - - case SIOCGIFADDR: - if (!atif) - return -EADDRNOTAVAIL; - - sa->sat_family = AF_APPLETALK; - sa->sat_addr = atif->address; - break; - - case SIOCGIFBRDADDR: - if (!atif) - return -EADDRNOTAVAIL; - - sa->sat_family = AF_APPLETALK; - sa->sat_addr.s_net = atif->address.s_net; - sa->sat_addr.s_node = ATADDR_BCAST; - break; - - case SIOCATALKDIFADDR: - case SIOCDIFADDR: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (sa->sat_family != AF_APPLETALK) - return -EINVAL; - atalk_dev_down(dev); - break; - - case SIOCSARP: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (sa->sat_family != AF_APPLETALK) - return -EINVAL; - /* - * for now, we only support proxy AARP on ELAP; - * we should be able to do it for LocalTalk, too. - */ - if (dev->type != ARPHRD_ETHER) - return -EPROTONOSUPPORT; - - /* - * atif points to the current interface on this network; - * we aren't concerned about its current status (at - * least for now), but it has all the settings about - * the network we're going to probe. Consequently, it - * must exist. - */ - if (!atif) - return -EADDRNOTAVAIL; - - nr = (struct atalk_netrange *)&(atif->nets); - /* - * Phase 1 is fine on Localtalk but we don't do - * Ethertalk phase 1. Anyone wanting to add it go ahead. - */ - if (dev->type == ARPHRD_ETHER && nr->nr_phase != 2) - return -EPROTONOSUPPORT; - - if (sa->sat_addr.s_node == ATADDR_BCAST || - sa->sat_addr.s_node == 254) - return -EINVAL; - - /* - * Check if the chosen address is used. If so we - * error and ATCP will try another. - */ - if (atif_proxy_probe_device(atif, &(sa->sat_addr)) < 0) - return -EADDRINUSE; - - /* - * We now have an address on the local network, and - * the AARP code will defend it for us until we take it - * down. We don't set up any routes right now, because - * ATCP will install them manually via SIOCADDRT. - */ - break; - - case SIOCDARP: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (sa->sat_family != AF_APPLETALK) - return -EINVAL; - if (!atif) - return -EADDRNOTAVAIL; - - /* give to aarp module to remove proxy entry */ - aarp_proxy_remove(atif->dev, &(sa->sat_addr)); - return 0; - } - - return copy_to_user(arg, &atreq, sizeof(atreq)) ? -EFAULT : 0; -} - -/* Routing ioctl() calls */ -static int atrtr_ioctl(unsigned int cmd, void __user *arg) -{ - struct rtentry rt; - - if (copy_from_user(&rt, arg, sizeof(rt))) - return -EFAULT; - - switch (cmd) { - case SIOCDELRT: - if (rt.rt_dst.sa_family != AF_APPLETALK) - return -EINVAL; - return atrtr_delete(&((struct sockaddr_at *) - &rt.rt_dst)->sat_addr); - - case SIOCADDRT: { - struct net_device *dev = NULL; - if (rt.rt_dev) { - char name[IFNAMSIZ]; - if (copy_from_user(name, rt.rt_dev, IFNAMSIZ-1)) - return -EFAULT; - name[IFNAMSIZ-1] = '\0'; - dev = __dev_get_by_name(&init_net, name); - if (!dev) - return -ENODEV; - } - return atrtr_create(&rt, dev); - } - } - return -EINVAL; -} - -/**************************************************************************\ -* * -* Handling for system calls applied via the various interfaces to an * -* AppleTalk socket object. * -* * -\**************************************************************************/ - -/* - * Checksum: This is 'optional'. It's quite likely also a good - * candidate for assembler hackery 8) - */ -static unsigned long atalk_sum_partial(const unsigned char *data, - int len, unsigned long sum) -{ - /* This ought to be unwrapped neatly. I'll trust gcc for now */ - while (len--) { - sum += *data++; - sum = rol16(sum, 1); - } - return sum; -} - -/* Checksum skb data -- similar to skb_checksum */ -static unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset, - int len, unsigned long sum) -{ - int start = skb_headlen(skb); - struct sk_buff *frag_iter; - int i, copy; - - /* checksum stuff in header space */ - if ( (copy = start - offset) > 0) { - if (copy > len) - copy = len; - sum = atalk_sum_partial(skb->data + offset, copy, sum); - if ( (len -= copy) == 0) - return sum; - - offset += copy; - } - - /* checksum stuff in frags */ - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - int end; - - WARN_ON(start > offset + len); - - end = start + skb_shinfo(skb)->frags[i].size; - if ((copy = end - offset) > 0) { - u8 *vaddr; - skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - - if (copy > len) - copy = len; - vaddr = kmap_skb_frag(frag); - sum = atalk_sum_partial(vaddr + frag->page_offset + - offset - start, copy, sum); - kunmap_skb_frag(vaddr); - - if (!(len -= copy)) - return sum; - offset += copy; - } - start = end; - } - - skb_walk_frags(skb, frag_iter) { - int end; - - WARN_ON(start > offset + len); - - end = start + frag_iter->len; - if ((copy = end - offset) > 0) { - if (copy > len) - copy = len; - sum = atalk_sum_skb(frag_iter, offset - start, - copy, sum); - if ((len -= copy) == 0) - return sum; - offset += copy; - } - start = end; - } - - BUG_ON(len > 0); - - return sum; -} - -static __be16 atalk_checksum(const struct sk_buff *skb, int len) -{ - unsigned long sum; - - /* skip header 4 bytes */ - sum = atalk_sum_skb(skb, 4, len-4, 0); - - /* Use 0xFFFF for 0. 0 itself means none */ - return sum ? htons((unsigned short)sum) : htons(0xFFFF); -} - -static struct proto ddp_proto = { - .name = "DDP", - .owner = THIS_MODULE, - .obj_size = sizeof(struct atalk_sock), -}; - -/* - * Create a socket. Initialise the socket, blank the addresses - * set the state. - */ -static int atalk_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - int rc = -ESOCKTNOSUPPORT; - - if (!net_eq(net, &init_net)) - return -EAFNOSUPPORT; - - /* - * We permit SOCK_DGRAM and RAW is an extension. It is trivial to do - * and gives you the full ELAP frame. Should be handy for CAP 8) - */ - if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM) - goto out; - rc = -ENOMEM; - sk = sk_alloc(net, PF_APPLETALK, GFP_KERNEL, &ddp_proto); - if (!sk) - goto out; - rc = 0; - sock->ops = &atalk_dgram_ops; - sock_init_data(sock, sk); - - /* Checksums on by default */ - sock_set_flag(sk, SOCK_ZAPPED); -out: - return rc; -} - -/* Free a socket. No work needed */ -static int atalk_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - lock_kernel(); - if (sk) { - sock_orphan(sk); - sock->sk = NULL; - atalk_destroy_socket(sk); - } - unlock_kernel(); - return 0; -} - -/** - * atalk_pick_and_bind_port - Pick a source port when one is not given - * @sk - socket to insert into the tables - * @sat - address to search for - * - * Pick a source port when one is not given. If we can find a suitable free - * one, we insert the socket into the tables using it. - * - * This whole operation must be atomic. - */ -static int atalk_pick_and_bind_port(struct sock *sk, struct sockaddr_at *sat) -{ - int retval; - - write_lock_bh(&atalk_sockets_lock); - - for (sat->sat_port = ATPORT_RESERVED; - sat->sat_port < ATPORT_LAST; - sat->sat_port++) { - struct sock *s; - struct hlist_node *node; - - sk_for_each(s, node, &atalk_sockets) { - struct atalk_sock *at = at_sk(s); - - if (at->src_net == sat->sat_addr.s_net && - at->src_node == sat->sat_addr.s_node && - at->src_port == sat->sat_port) - goto try_next_port; - } - - /* Wheee, it's free, assign and insert. */ - __atalk_insert_socket(sk); - at_sk(sk)->src_port = sat->sat_port; - retval = 0; - goto out; - -try_next_port:; - } - - retval = -EBUSY; -out: - write_unlock_bh(&atalk_sockets_lock); - return retval; -} - -static int atalk_autobind(struct sock *sk) -{ - struct atalk_sock *at = at_sk(sk); - struct sockaddr_at sat; - struct atalk_addr *ap = atalk_find_primary(); - int n = -EADDRNOTAVAIL; - - if (!ap || ap->s_net == htons(ATADDR_ANYNET)) - goto out; - - at->src_net = sat.sat_addr.s_net = ap->s_net; - at->src_node = sat.sat_addr.s_node = ap->s_node; - - n = atalk_pick_and_bind_port(sk, &sat); - if (!n) - sock_reset_flag(sk, SOCK_ZAPPED); -out: - return n; -} - -/* Set the address 'our end' of the connection */ -static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) -{ - struct sockaddr_at *addr = (struct sockaddr_at *)uaddr; - struct sock *sk = sock->sk; - struct atalk_sock *at = at_sk(sk); - int err; - - if (!sock_flag(sk, SOCK_ZAPPED) || - addr_len != sizeof(struct sockaddr_at)) - return -EINVAL; - - if (addr->sat_family != AF_APPLETALK) - return -EAFNOSUPPORT; - - lock_kernel(); - if (addr->sat_addr.s_net == htons(ATADDR_ANYNET)) { - struct atalk_addr *ap = atalk_find_primary(); - - err = -EADDRNOTAVAIL; - if (!ap) - goto out; - - at->src_net = addr->sat_addr.s_net = ap->s_net; - at->src_node = addr->sat_addr.s_node= ap->s_node; - } else { - err = -EADDRNOTAVAIL; - if (!atalk_find_interface(addr->sat_addr.s_net, - addr->sat_addr.s_node)) - goto out; - - at->src_net = addr->sat_addr.s_net; - at->src_node = addr->sat_addr.s_node; - } - - if (addr->sat_port == ATADDR_ANYPORT) { - err = atalk_pick_and_bind_port(sk, addr); - - if (err < 0) - goto out; - } else { - at->src_port = addr->sat_port; - - err = -EADDRINUSE; - if (atalk_find_or_insert_socket(sk, addr)) - goto out; - } - - sock_reset_flag(sk, SOCK_ZAPPED); - err = 0; -out: - unlock_kernel(); - return err; -} - -/* Set the address we talk to */ -static int atalk_connect(struct socket *sock, struct sockaddr *uaddr, - int addr_len, int flags) -{ - struct sock *sk = sock->sk; - struct atalk_sock *at = at_sk(sk); - struct sockaddr_at *addr; - int err; - - sk->sk_state = TCP_CLOSE; - sock->state = SS_UNCONNECTED; - - if (addr_len != sizeof(*addr)) - return -EINVAL; - - addr = (struct sockaddr_at *)uaddr; - - if (addr->sat_family != AF_APPLETALK) - return -EAFNOSUPPORT; - - if (addr->sat_addr.s_node == ATADDR_BCAST && - !sock_flag(sk, SOCK_BROADCAST)) { -#if 1 - printk(KERN_WARNING "%s is broken and did not set " - "SO_BROADCAST. It will break when 2.2 is " - "released.\n", - current->comm); -#else - return -EACCES; -#endif - } - - lock_kernel(); - err = -EBUSY; - if (sock_flag(sk, SOCK_ZAPPED)) - if (atalk_autobind(sk) < 0) - goto out; - - err = -ENETUNREACH; - if (!atrtr_get_dev(&addr->sat_addr)) - goto out; - - at->dest_port = addr->sat_port; - at->dest_net = addr->sat_addr.s_net; - at->dest_node = addr->sat_addr.s_node; - - sock->state = SS_CONNECTED; - sk->sk_state = TCP_ESTABLISHED; - err = 0; -out: - unlock_kernel(); - return err; -} - -/* - * Find the name of an AppleTalk socket. Just copy the right - * fields into the sockaddr. - */ -static int atalk_getname(struct socket *sock, struct sockaddr *uaddr, - int *uaddr_len, int peer) -{ - struct sockaddr_at sat; - struct sock *sk = sock->sk; - struct atalk_sock *at = at_sk(sk); - int err; - - lock_kernel(); - err = -ENOBUFS; - if (sock_flag(sk, SOCK_ZAPPED)) - if (atalk_autobind(sk) < 0) - goto out; - - *uaddr_len = sizeof(struct sockaddr_at); - memset(&sat.sat_zero, 0, sizeof(sat.sat_zero)); - - if (peer) { - err = -ENOTCONN; - if (sk->sk_state != TCP_ESTABLISHED) - goto out; - - sat.sat_addr.s_net = at->dest_net; - sat.sat_addr.s_node = at->dest_node; - sat.sat_port = at->dest_port; - } else { - sat.sat_addr.s_net = at->src_net; - sat.sat_addr.s_node = at->src_node; - sat.sat_port = at->src_port; - } - - err = 0; - sat.sat_family = AF_APPLETALK; - memcpy(uaddr, &sat, sizeof(sat)); - -out: - unlock_kernel(); - return err; -} - -static unsigned int atalk_poll(struct file *file, struct socket *sock, - poll_table *wait) -{ - int err; - lock_kernel(); - err = datagram_poll(file, sock, wait); - unlock_kernel(); - return err; -} - -#if defined(CONFIG_IPDDP) || defined(CONFIG_IPDDP_MODULE) -static __inline__ int is_ip_over_ddp(struct sk_buff *skb) -{ - return skb->data[12] == 22; -} - -static int handle_ip_over_ddp(struct sk_buff *skb) -{ - struct net_device *dev = __dev_get_by_name(&init_net, "ipddp0"); - struct net_device_stats *stats; - - /* This needs to be able to handle ipddp"N" devices */ - if (!dev) { - kfree_skb(skb); - return NET_RX_DROP; - } - - skb->protocol = htons(ETH_P_IP); - skb_pull(skb, 13); - skb->dev = dev; - skb_reset_transport_header(skb); - - stats = netdev_priv(dev); - stats->rx_packets++; - stats->rx_bytes += skb->len + 13; - return netif_rx(skb); /* Send the SKB up to a higher place. */ -} -#else -/* make it easy for gcc to optimize this test out, i.e. kill the code */ -#define is_ip_over_ddp(skb) 0 -#define handle_ip_over_ddp(skb) 0 -#endif - -static int atalk_route_packet(struct sk_buff *skb, struct net_device *dev, - struct ddpehdr *ddp, __u16 len_hops, int origlen) -{ - struct atalk_route *rt; - struct atalk_addr ta; - - /* - * Don't route multicast, etc., packets, or packets sent to "this - * network" - */ - if (skb->pkt_type != PACKET_HOST || !ddp->deh_dnet) { - /* - * FIXME: - * - * Can it ever happen that a packet is from a PPP iface and - * needs to be broadcast onto the default network? - */ - if (dev->type == ARPHRD_PPP) - printk(KERN_DEBUG "AppleTalk: didn't forward broadcast " - "packet received from PPP iface\n"); - goto free_it; - } - - ta.s_net = ddp->deh_dnet; - ta.s_node = ddp->deh_dnode; - - /* Route the packet */ - rt = atrtr_find(&ta); - /* increment hops count */ - len_hops += 1 << 10; - if (!rt || !(len_hops & (15 << 10))) - goto free_it; - - /* FIXME: use skb->cb to be able to use shared skbs */ - - /* - * Route goes through another gateway, so set the target to the - * gateway instead. - */ - - if (rt->flags & RTF_GATEWAY) { - ta.s_net = rt->gateway.s_net; - ta.s_node = rt->gateway.s_node; - } - - /* Fix up skb->len field */ - skb_trim(skb, min_t(unsigned int, origlen, - (rt->dev->hard_header_len + - ddp_dl->header_length + (len_hops & 1023)))); - - /* FIXME: use skb->cb to be able to use shared skbs */ - ddp->deh_len_hops = htons(len_hops); - - /* - * Send the buffer onwards - * - * Now we must always be careful. If it's come from LocalTalk to - * EtherTalk it might not fit - * - * Order matters here: If a packet has to be copied to make a new - * headroom (rare hopefully) then it won't need unsharing. - * - * Note. ddp-> becomes invalid at the realloc. - */ - if (skb_headroom(skb) < 22) { - /* 22 bytes - 12 ether, 2 len, 3 802.2 5 snap */ - struct sk_buff *nskb = skb_realloc_headroom(skb, 32); - kfree_skb(skb); - skb = nskb; - } else - skb = skb_unshare(skb, GFP_ATOMIC); - - /* - * If the buffer didn't vanish into the lack of space bitbucket we can - * send it. - */ - if (skb == NULL) - goto drop; - - if (aarp_send_ddp(rt->dev, skb, &ta, NULL) == NET_XMIT_DROP) - return NET_RX_DROP; - return NET_RX_SUCCESS; -free_it: - kfree_skb(skb); -drop: - return NET_RX_DROP; -} - -/** - * atalk_rcv - Receive a packet (in skb) from device dev - * @skb - packet received - * @dev - network device where the packet comes from - * @pt - packet type - * - * Receive a packet (in skb) from device dev. This has come from the SNAP - * decoder, and on entry skb->transport_header is the DDP header, skb->len - * is the DDP header, skb->len is the DDP length. The physical headers - * have been extracted. PPP should probably pass frames marked as for this - * layer. [ie ARPHRD_ETHERTALK] - */ -static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) -{ - struct ddpehdr *ddp; - struct sock *sock; - struct atalk_iface *atif; - struct sockaddr_at tosat; - int origlen; - __u16 len_hops; - - if (!net_eq(dev_net(dev), &init_net)) - goto drop; - - /* Don't mangle buffer if shared */ - if (!(skb = skb_share_check(skb, GFP_ATOMIC))) - goto out; - - /* Size check and make sure header is contiguous */ - if (!pskb_may_pull(skb, sizeof(*ddp))) - goto drop; - - ddp = ddp_hdr(skb); - - len_hops = ntohs(ddp->deh_len_hops); - - /* Trim buffer in case of stray trailing data */ - origlen = skb->len; - skb_trim(skb, min_t(unsigned int, skb->len, len_hops & 1023)); - - /* - * Size check to see if ddp->deh_len was crap - * (Otherwise we'll detonate most spectacularly - * in the middle of atalk_checksum() or recvmsg()). - */ - if (skb->len < sizeof(*ddp) || skb->len < (len_hops & 1023)) { - pr_debug("AppleTalk: dropping corrupted frame (deh_len=%u, " - "skb->len=%u)\n", len_hops & 1023, skb->len); - goto drop; - } - - /* - * Any checksums. Note we don't do htons() on this == is assumed to be - * valid for net byte orders all over the networking code... - */ - if (ddp->deh_sum && - atalk_checksum(skb, len_hops & 1023) != ddp->deh_sum) - /* Not a valid AppleTalk frame - dustbin time */ - goto drop; - - /* Check the packet is aimed at us */ - if (!ddp->deh_dnet) /* Net 0 is 'this network' */ - atif = atalk_find_anynet(ddp->deh_dnode, dev); - else - atif = atalk_find_interface(ddp->deh_dnet, ddp->deh_dnode); - - if (!atif) { - /* Not ours, so we route the packet via the correct - * AppleTalk iface - */ - return atalk_route_packet(skb, dev, ddp, len_hops, origlen); - } - - /* if IP over DDP is not selected this code will be optimized out */ - if (is_ip_over_ddp(skb)) - return handle_ip_over_ddp(skb); - /* - * Which socket - atalk_search_socket() looks for a *full match* - * of the tuple. - */ - tosat.sat_addr.s_net = ddp->deh_dnet; - tosat.sat_addr.s_node = ddp->deh_dnode; - tosat.sat_port = ddp->deh_dport; - - sock = atalk_search_socket(&tosat, atif); - if (!sock) /* But not one of our sockets */ - goto drop; - - /* Queue packet (standard) */ - skb->sk = sock; - - if (sock_queue_rcv_skb(sock, skb) < 0) - goto drop; - - return NET_RX_SUCCESS; - -drop: - kfree_skb(skb); -out: - return NET_RX_DROP; - -} - -/* - * Receive a LocalTalk frame. We make some demands on the caller here. - * Caller must provide enough headroom on the packet to pull the short - * header and append a long one. - */ -static int ltalk_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) -{ - if (!net_eq(dev_net(dev), &init_net)) - goto freeit; - - /* Expand any short form frames */ - if (skb_mac_header(skb)[2] == 1) { - struct ddpehdr *ddp; - /* Find our address */ - struct atalk_addr *ap = atalk_find_dev_addr(dev); - - if (!ap || skb->len < sizeof(__be16) || skb->len > 1023) - goto freeit; - - /* Don't mangle buffer if shared */ - if (!(skb = skb_share_check(skb, GFP_ATOMIC))) - return 0; - - /* - * The push leaves us with a ddephdr not an shdr, and - * handily the port bytes in the right place preset. - */ - ddp = (struct ddpehdr *) skb_push(skb, sizeof(*ddp) - 4); - - /* Now fill in the long header */ - - /* - * These two first. The mac overlays the new source/dest - * network information so we MUST copy these before - * we write the network numbers ! - */ - - ddp->deh_dnode = skb_mac_header(skb)[0]; /* From physical header */ - ddp->deh_snode = skb_mac_header(skb)[1]; /* From physical header */ - - ddp->deh_dnet = ap->s_net; /* Network number */ - ddp->deh_snet = ap->s_net; - ddp->deh_sum = 0; /* No checksum */ - /* - * Not sure about this bit... - */ - /* Non routable, so force a drop if we slip up later */ - ddp->deh_len_hops = htons(skb->len + (DDP_MAXHOPS << 10)); - } - skb_reset_transport_header(skb); - - return atalk_rcv(skb, dev, pt, orig_dev); -freeit: - kfree_skb(skb); - return 0; -} - -static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, - size_t len) -{ - struct sock *sk = sock->sk; - struct atalk_sock *at = at_sk(sk); - struct sockaddr_at *usat = (struct sockaddr_at *)msg->msg_name; - int flags = msg->msg_flags; - int loopback = 0; - struct sockaddr_at local_satalk, gsat; - struct sk_buff *skb; - struct net_device *dev; - struct ddpehdr *ddp; - int size; - struct atalk_route *rt; - int err; - - if (flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT)) - return -EINVAL; - - if (len > DDP_MAXSZ) - return -EMSGSIZE; - - lock_kernel(); - if (usat) { - err = -EBUSY; - if (sock_flag(sk, SOCK_ZAPPED)) - if (atalk_autobind(sk) < 0) - goto out; - - err = -EINVAL; - if (msg->msg_namelen < sizeof(*usat) || - usat->sat_family != AF_APPLETALK) - goto out; - - err = -EPERM; - /* netatalk didn't implement this check */ - if (usat->sat_addr.s_node == ATADDR_BCAST && - !sock_flag(sk, SOCK_BROADCAST)) { - goto out; - } - } else { - err = -ENOTCONN; - if (sk->sk_state != TCP_ESTABLISHED) - goto out; - usat = &local_satalk; - usat->sat_family = AF_APPLETALK; - usat->sat_port = at->dest_port; - usat->sat_addr.s_node = at->dest_node; - usat->sat_addr.s_net = at->dest_net; - } - - /* Build a packet */ - SOCK_DEBUG(sk, "SK %p: Got address.\n", sk); - - /* For headers */ - size = sizeof(struct ddpehdr) + len + ddp_dl->header_length; - - if (usat->sat_addr.s_net || usat->sat_addr.s_node == ATADDR_ANYNODE) { - rt = atrtr_find(&usat->sat_addr); - } else { - struct atalk_addr at_hint; - - at_hint.s_node = 0; - at_hint.s_net = at->src_net; - - rt = atrtr_find(&at_hint); - } - err = ENETUNREACH; - if (!rt) - goto out; - - dev = rt->dev; - - SOCK_DEBUG(sk, "SK %p: Size needed %d, device %s\n", - sk, size, dev->name); - - size += dev->hard_header_len; - skb = sock_alloc_send_skb(sk, size, (flags & MSG_DONTWAIT), &err); - if (!skb) - goto out; - - skb->sk = sk; - skb_reserve(skb, ddp_dl->header_length); - skb_reserve(skb, dev->hard_header_len); - skb->dev = dev; - - SOCK_DEBUG(sk, "SK %p: Begin build.\n", sk); - - ddp = (struct ddpehdr *)skb_put(skb, sizeof(struct ddpehdr)); - ddp->deh_len_hops = htons(len + sizeof(*ddp)); - ddp->deh_dnet = usat->sat_addr.s_net; - ddp->deh_snet = at->src_net; - ddp->deh_dnode = usat->sat_addr.s_node; - ddp->deh_snode = at->src_node; - ddp->deh_dport = usat->sat_port; - ddp->deh_sport = at->src_port; - - SOCK_DEBUG(sk, "SK %p: Copy user data (%Zd bytes).\n", sk, len); - - err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); - if (err) { - kfree_skb(skb); - err = -EFAULT; - goto out; - } - - if (sk->sk_no_check == 1) - ddp->deh_sum = 0; - else - ddp->deh_sum = atalk_checksum(skb, len + sizeof(*ddp)); - - /* - * Loopback broadcast packets to non gateway targets (ie routes - * to group we are in) - */ - if (ddp->deh_dnode == ATADDR_BCAST && - !(rt->flags & RTF_GATEWAY) && !(dev->flags & IFF_LOOPBACK)) { - struct sk_buff *skb2 = skb_copy(skb, GFP_KERNEL); - - if (skb2) { - loopback = 1; - SOCK_DEBUG(sk, "SK %p: send out(copy).\n", sk); - /* - * If it fails it is queued/sent above in the aarp queue - */ - aarp_send_ddp(dev, skb2, &usat->sat_addr, NULL); - } - } - - if (dev->flags & IFF_LOOPBACK || loopback) { - SOCK_DEBUG(sk, "SK %p: Loop back.\n", sk); - /* loop back */ - skb_orphan(skb); - if (ddp->deh_dnode == ATADDR_BCAST) { - struct atalk_addr at_lo; - - at_lo.s_node = 0; - at_lo.s_net = 0; - - rt = atrtr_find(&at_lo); - if (!rt) { - kfree_skb(skb); - err = -ENETUNREACH; - goto out; - } - dev = rt->dev; - skb->dev = dev; - } - ddp_dl->request(ddp_dl, skb, dev->dev_addr); - } else { - SOCK_DEBUG(sk, "SK %p: send out.\n", sk); - if (rt->flags & RTF_GATEWAY) { - gsat.sat_addr = rt->gateway; - usat = &gsat; - } - - /* - * If it fails it is queued/sent above in the aarp queue - */ - aarp_send_ddp(dev, skb, &usat->sat_addr, NULL); - } - SOCK_DEBUG(sk, "SK %p: Done write (%Zd).\n", sk, len); - -out: - unlock_kernel(); - return err ? : len; -} - -static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, - size_t size, int flags) -{ - struct sock *sk = sock->sk; - struct sockaddr_at *sat = (struct sockaddr_at *)msg->msg_name; - struct ddpehdr *ddp; - int copied = 0; - int offset = 0; - int err = 0; - struct sk_buff *skb; - - lock_kernel(); - skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, - flags & MSG_DONTWAIT, &err); - if (!skb) - goto out; - - /* FIXME: use skb->cb to be able to use shared skbs */ - ddp = ddp_hdr(skb); - copied = ntohs(ddp->deh_len_hops) & 1023; - - if (sk->sk_type != SOCK_RAW) { - offset = sizeof(*ddp); - copied -= offset; - } - - if (copied > size) { - copied = size; - msg->msg_flags |= MSG_TRUNC; - } - err = skb_copy_datagram_iovec(skb, offset, msg->msg_iov, copied); - - if (!err) { - if (sat) { - sat->sat_family = AF_APPLETALK; - sat->sat_port = ddp->deh_sport; - sat->sat_addr.s_node = ddp->deh_snode; - sat->sat_addr.s_net = ddp->deh_snet; - } - msg->msg_namelen = sizeof(*sat); - } - - skb_free_datagram(sk, skb); /* Free the datagram. */ - -out: - unlock_kernel(); - return err ? : copied; -} - - -/* - * AppleTalk ioctl calls. - */ -static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - int rc = -ENOIOCTLCMD; - struct sock *sk = sock->sk; - void __user *argp = (void __user *)arg; - - switch (cmd) { - /* Protocol layer */ - case TIOCOUTQ: { - long amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); - - if (amount < 0) - amount = 0; - rc = put_user(amount, (int __user *)argp); - break; - } - case TIOCINQ: { - /* - * These two are safe on a single CPU system as only - * user tasks fiddle here - */ - struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); - long amount = 0; - - if (skb) - amount = skb->len - sizeof(struct ddpehdr); - rc = put_user(amount, (int __user *)argp); - break; - } - case SIOCGSTAMP: - rc = sock_get_timestamp(sk, argp); - break; - case SIOCGSTAMPNS: - rc = sock_get_timestampns(sk, argp); - break; - /* Routing */ - case SIOCADDRT: - case SIOCDELRT: - rc = -EPERM; - if (capable(CAP_NET_ADMIN)) - rc = atrtr_ioctl(cmd, argp); - break; - /* Interface */ - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFBRDADDR: - case SIOCATALKDIFADDR: - case SIOCDIFADDR: - case SIOCSARP: /* proxy AARP */ - case SIOCDARP: /* proxy AARP */ - rtnl_lock(); - rc = atif_ioctl(cmd, argp); - rtnl_unlock(); - break; - } - - return rc; -} - - -#ifdef CONFIG_COMPAT -static int atalk_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - /* - * SIOCATALKDIFADDR is a SIOCPROTOPRIVATE ioctl number, so we - * cannot handle it in common code. The data we access if ifreq - * here is compatible, so we can simply call the native - * handler. - */ - if (cmd == SIOCATALKDIFADDR) - return atalk_ioctl(sock, cmd, (unsigned long)compat_ptr(arg)); - - return -ENOIOCTLCMD; -} -#endif - - -static const struct net_proto_family atalk_family_ops = { - .family = PF_APPLETALK, - .create = atalk_create, - .owner = THIS_MODULE, -}; - -static const struct proto_ops atalk_dgram_ops = { - .family = PF_APPLETALK, - .owner = THIS_MODULE, - .release = atalk_release, - .bind = atalk_bind, - .connect = atalk_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .getname = atalk_getname, - .poll = atalk_poll, - .ioctl = atalk_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = atalk_compat_ioctl, -#endif - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = sock_no_setsockopt, - .getsockopt = sock_no_getsockopt, - .sendmsg = atalk_sendmsg, - .recvmsg = atalk_recvmsg, - .mmap = sock_no_mmap, - .sendpage = sock_no_sendpage, -}; - -static struct notifier_block ddp_notifier = { - .notifier_call = ddp_device_event, -}; - -static struct packet_type ltalk_packet_type __read_mostly = { - .type = cpu_to_be16(ETH_P_LOCALTALK), - .func = ltalk_rcv, -}; - -static struct packet_type ppptalk_packet_type __read_mostly = { - .type = cpu_to_be16(ETH_P_PPPTALK), - .func = atalk_rcv, -}; - -static unsigned char ddp_snap_id[] = { 0x08, 0x00, 0x07, 0x80, 0x9B }; - -/* Export symbols for use by drivers when AppleTalk is a module */ -EXPORT_SYMBOL(atrtr_get_dev); -EXPORT_SYMBOL(atalk_find_dev_addr); - -static const char atalk_err_snap[] __initconst = - KERN_CRIT "Unable to register DDP with SNAP.\n"; - -/* Called by proto.c on kernel start up */ -static int __init atalk_init(void) -{ - int rc = proto_register(&ddp_proto, 0); - - if (rc != 0) - goto out; - - (void)sock_register(&atalk_family_ops); - ddp_dl = register_snap_client(ddp_snap_id, atalk_rcv); - if (!ddp_dl) - printk(atalk_err_snap); - - dev_add_pack(<alk_packet_type); - dev_add_pack(&ppptalk_packet_type); - - register_netdevice_notifier(&ddp_notifier); - aarp_proto_init(); - atalk_proc_init(); - atalk_register_sysctl(); -out: - return rc; -} -module_init(atalk_init); - -/* - * No explicit module reference count manipulation is needed in the - * protocol. Socket layer sets module reference count for us - * and interfaces reference counting is done - * by the network device layer. - * - * Ergo, before the AppleTalk module can be removed, all AppleTalk - * sockets be closed from user space. - */ -static void __exit atalk_exit(void) -{ -#ifdef CONFIG_SYSCTL - atalk_unregister_sysctl(); -#endif /* CONFIG_SYSCTL */ - atalk_proc_exit(); - aarp_cleanup_module(); /* General aarp clean-up. */ - unregister_netdevice_notifier(&ddp_notifier); - dev_remove_pack(<alk_packet_type); - dev_remove_pack(&ppptalk_packet_type); - unregister_snap_client(ddp_dl); - sock_unregister(PF_APPLETALK); - proto_unregister(&ddp_proto); -} -module_exit(atalk_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Alan Cox "); -MODULE_DESCRIPTION("AppleTalk 0.20\n"); -MODULE_ALIAS_NETPROTO(PF_APPLETALK); diff --git a/drivers/staging/appletalk/dev.c b/drivers/staging/appletalk/dev.c deleted file mode 100644 index 6c8016f61866..000000000000 --- a/drivers/staging/appletalk/dev.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Moved here from drivers/net/net_init.c, which is: - * Written 1993,1994,1995 by Donald Becker. - */ - -#include -#include -#include -#include -#include - -static void ltalk_setup(struct net_device *dev) -{ - /* Fill in the fields of the device structure with localtalk-generic values. */ - - dev->type = ARPHRD_LOCALTLK; - dev->hard_header_len = LTALK_HLEN; - dev->mtu = LTALK_MTU; - dev->addr_len = LTALK_ALEN; - dev->tx_queue_len = 10; - - dev->broadcast[0] = 0xFF; - - dev->flags = IFF_BROADCAST|IFF_MULTICAST|IFF_NOARP; -} - -/** - * alloc_ltalkdev - Allocates and sets up an localtalk device - * @sizeof_priv: Size of additional driver-private structure to be allocated - * for this localtalk device - * - * Fill in the fields of the device structure with localtalk-generic - * values. Basically does everything except registering the device. - * - * Constructs a new net device, complete with a private data area of - * size @sizeof_priv. A 32-byte (not bit) alignment is enforced for - * this private data area. - */ - -struct net_device *alloc_ltalkdev(int sizeof_priv) -{ - return alloc_netdev(sizeof_priv, "lt%d", ltalk_setup); -} -EXPORT_SYMBOL(alloc_ltalkdev); diff --git a/drivers/staging/appletalk/ipddp.c b/drivers/staging/appletalk/ipddp.c deleted file mode 100644 index 58b4e6098ad4..000000000000 --- a/drivers/staging/appletalk/ipddp.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * ipddp.c: IP to Appletalk-IP Encapsulation driver for Linux - * Appletalk-IP to IP Decapsulation driver for Linux - * - * Authors: - * - DDP-IP Encap by: Bradford W. Johnson - * - DDP-IP Decap by: Jay Schulist - * - * Derived from: - * - Almost all code already existed in net/appletalk/ddp.c I just - * moved/reorginized it into a driver file. Original IP-over-DDP code - * was done by Bradford W. Johnson - * - skeleton.c: A network driver outline for linux. - * Written 1993-94 by Donald Becker. - * - dummy.c: A dummy net driver. By Nick Holloway. - * - MacGate: A user space Daemon for Appletalk-IP Decap for - * Linux by Jay Schulist - * - * Copyright 1993 United States Government as represented by the - * Director, National Security Agency. - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "atalk.h" -#include "ipddp.h" /* Our stuff */ - -static const char version[] = KERN_INFO "ipddp.c:v0.01 8/28/97 Bradford W. Johnson \n"; - -static struct ipddp_route *ipddp_route_list; -static DEFINE_SPINLOCK(ipddp_route_lock); - -#ifdef CONFIG_IPDDP_ENCAP -static int ipddp_mode = IPDDP_ENCAP; -#else -static int ipddp_mode = IPDDP_DECAP; -#endif - -/* Index to functions, as function prototypes. */ -static netdev_tx_t ipddp_xmit(struct sk_buff *skb, - struct net_device *dev); -static int ipddp_create(struct ipddp_route *new_rt); -static int ipddp_delete(struct ipddp_route *rt); -static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt); -static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); - -static const struct net_device_ops ipddp_netdev_ops = { - .ndo_start_xmit = ipddp_xmit, - .ndo_do_ioctl = ipddp_ioctl, - .ndo_change_mtu = eth_change_mtu, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static struct net_device * __init ipddp_init(void) -{ - static unsigned version_printed; - struct net_device *dev; - int err; - - dev = alloc_etherdev(0); - if (!dev) - return ERR_PTR(-ENOMEM); - - dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; - strcpy(dev->name, "ipddp%d"); - - if (version_printed++ == 0) - printk(version); - - /* Initialize the device structure. */ - dev->netdev_ops = &ipddp_netdev_ops; - - dev->type = ARPHRD_IPDDP; /* IP over DDP tunnel */ - dev->mtu = 585; - dev->flags |= IFF_NOARP; - - /* - * The worst case header we will need is currently a - * ethernet header (14 bytes) and a ddp header (sizeof ddpehdr+1) - * We send over SNAP so that takes another 8 bytes. - */ - dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1; - - err = register_netdev(dev); - if (err) { - free_netdev(dev); - return ERR_PTR(err); - } - - /* Let the user now what mode we are in */ - if(ipddp_mode == IPDDP_ENCAP) - printk("%s: Appletalk-IP Encap. mode by Bradford W. Johnson \n", - dev->name); - if(ipddp_mode == IPDDP_DECAP) - printk("%s: Appletalk-IP Decap. mode by Jay Schulist \n", - dev->name); - - return dev; -} - - -/* - * Transmit LLAP/ELAP frame using aarp_send_ddp. - */ -static netdev_tx_t ipddp_xmit(struct sk_buff *skb, struct net_device *dev) -{ - __be32 paddr = skb_rtable(skb)->rt_gateway; - struct ddpehdr *ddp; - struct ipddp_route *rt; - struct atalk_addr *our_addr; - - spin_lock(&ipddp_route_lock); - - /* - * Find appropriate route to use, based only on IP number. - */ - for(rt = ipddp_route_list; rt != NULL; rt = rt->next) - { - if(rt->ip == paddr) - break; - } - if(rt == NULL) { - spin_unlock(&ipddp_route_lock); - return NETDEV_TX_OK; - } - - our_addr = atalk_find_dev_addr(rt->dev); - - if(ipddp_mode == IPDDP_DECAP) - /* - * Pull off the excess room that should not be there. - * This is due to a hard-header problem. This is the - * quick fix for now though, till it breaks. - */ - skb_pull(skb, 35-(sizeof(struct ddpehdr)+1)); - - /* Create the Extended DDP header */ - ddp = (struct ddpehdr *)skb->data; - ddp->deh_len_hops = htons(skb->len + (1<<10)); - ddp->deh_sum = 0; - - /* - * For Localtalk we need aarp_send_ddp to strip the - * long DDP header and place a shot DDP header on it. - */ - if(rt->dev->type == ARPHRD_LOCALTLK) - { - ddp->deh_dnet = 0; /* FIXME more hops?? */ - ddp->deh_snet = 0; - } - else - { - ddp->deh_dnet = rt->at.s_net; /* FIXME more hops?? */ - ddp->deh_snet = our_addr->s_net; - } - ddp->deh_dnode = rt->at.s_node; - ddp->deh_snode = our_addr->s_node; - ddp->deh_dport = 72; - ddp->deh_sport = 72; - - *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */ - - skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */ - - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - aarp_send_ddp(rt->dev, skb, &rt->at, NULL); - - spin_unlock(&ipddp_route_lock); - - return NETDEV_TX_OK; -} - -/* - * Create a routing entry. We first verify that the - * record does not already exist. If it does we return -EEXIST - */ -static int ipddp_create(struct ipddp_route *new_rt) -{ - struct ipddp_route *rt = kmalloc(sizeof(*rt), GFP_KERNEL); - - if (rt == NULL) - return -ENOMEM; - - rt->ip = new_rt->ip; - rt->at = new_rt->at; - rt->next = NULL; - if ((rt->dev = atrtr_get_dev(&rt->at)) == NULL) { - kfree(rt); - return -ENETUNREACH; - } - - spin_lock_bh(&ipddp_route_lock); - if (__ipddp_find_route(rt)) { - spin_unlock_bh(&ipddp_route_lock); - kfree(rt); - return -EEXIST; - } - - rt->next = ipddp_route_list; - ipddp_route_list = rt; - - spin_unlock_bh(&ipddp_route_lock); - - return 0; -} - -/* - * Delete a route, we only delete a FULL match. - * If route does not exist we return -ENOENT. - */ -static int ipddp_delete(struct ipddp_route *rt) -{ - struct ipddp_route **r = &ipddp_route_list; - struct ipddp_route *tmp; - - spin_lock_bh(&ipddp_route_lock); - while((tmp = *r) != NULL) - { - if(tmp->ip == rt->ip && - tmp->at.s_net == rt->at.s_net && - tmp->at.s_node == rt->at.s_node) - { - *r = tmp->next; - spin_unlock_bh(&ipddp_route_lock); - kfree(tmp); - return 0; - } - r = &tmp->next; - } - - spin_unlock_bh(&ipddp_route_lock); - return -ENOENT; -} - -/* - * Find a routing entry, we only return a FULL match - */ -static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt) -{ - struct ipddp_route *f; - - for(f = ipddp_route_list; f != NULL; f = f->next) - { - if(f->ip == rt->ip && - f->at.s_net == rt->at.s_net && - f->at.s_node == rt->at.s_node) - return f; - } - - return NULL; -} - -static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - struct ipddp_route __user *rt = ifr->ifr_data; - struct ipddp_route rcp, rcp2, *rp; - - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - - if(copy_from_user(&rcp, rt, sizeof(rcp))) - return -EFAULT; - - switch(cmd) - { - case SIOCADDIPDDPRT: - return ipddp_create(&rcp); - - case SIOCFINDIPDDPRT: - spin_lock_bh(&ipddp_route_lock); - rp = __ipddp_find_route(&rcp); - if (rp) - memcpy(&rcp2, rp, sizeof(rcp2)); - spin_unlock_bh(&ipddp_route_lock); - - if (rp) { - if (copy_to_user(rt, &rcp2, - sizeof(struct ipddp_route))) - return -EFAULT; - return 0; - } else - return -ENOENT; - - case SIOCDELIPDDPRT: - return ipddp_delete(&rcp); - - default: - return -EINVAL; - } -} - -static struct net_device *dev_ipddp; - -MODULE_LICENSE("GPL"); -module_param(ipddp_mode, int, 0); - -static int __init ipddp_init_module(void) -{ - dev_ipddp = ipddp_init(); - if (IS_ERR(dev_ipddp)) - return PTR_ERR(dev_ipddp); - return 0; -} - -static void __exit ipddp_cleanup_module(void) -{ - struct ipddp_route *p; - - unregister_netdev(dev_ipddp); - free_netdev(dev_ipddp); - - while (ipddp_route_list) { - p = ipddp_route_list->next; - kfree(ipddp_route_list); - ipddp_route_list = p; - } -} - -module_init(ipddp_init_module); -module_exit(ipddp_cleanup_module); diff --git a/drivers/staging/appletalk/ipddp.h b/drivers/staging/appletalk/ipddp.h deleted file mode 100644 index 531519da99a3..000000000000 --- a/drivers/staging/appletalk/ipddp.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * ipddp.h: Header for IP-over-DDP driver for Linux. - */ - -#ifndef __LINUX_IPDDP_H -#define __LINUX_IPDDP_H - -#ifdef __KERNEL__ - -#define SIOCADDIPDDPRT (SIOCDEVPRIVATE) -#define SIOCDELIPDDPRT (SIOCDEVPRIVATE+1) -#define SIOCFINDIPDDPRT (SIOCDEVPRIVATE+2) - -struct ipddp_route -{ - struct net_device *dev; /* Carrier device */ - __be32 ip; /* IP address */ - struct atalk_addr at; /* Gateway appletalk address */ - int flags; - struct ipddp_route *next; -}; - -#define IPDDP_ENCAP 1 -#define IPDDP_DECAP 2 - -#endif /* __KERNEL__ */ -#endif /* __LINUX_IPDDP_H */ diff --git a/drivers/staging/appletalk/ltpc.c b/drivers/staging/appletalk/ltpc.c deleted file mode 100644 index 60caf892695e..000000000000 --- a/drivers/staging/appletalk/ltpc.c +++ /dev/null @@ -1,1288 +0,0 @@ -/*** ltpc.c -- a driver for the LocalTalk PC card. - * - * Copyright (c) 1995,1996 Bradford W. Johnson - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - * This is ALPHA code at best. It may not work for you. It may - * damage your equipment. It may damage your relations with other - * users of your network. Use it at your own risk! - * - * Based in part on: - * skeleton.c by Donald Becker - * dummy.c by Nick Holloway and Alan Cox - * loopback.c by Ross Biro, Fred van Kampen, Donald Becker - * the netatalk source code (UMICH) - * lots of work on the card... - * - * I do not have access to the (proprietary) SDK that goes with the card. - * If you do, I don't want to know about it, and you can probably write - * a better driver yourself anyway. This does mean that the pieces that - * talk to the card are guesswork on my part, so use at your own risk! - * - * This is my first try at writing Linux networking code, and is also - * guesswork. Again, use at your own risk! (Although on this part, I'd - * welcome suggestions) - * - * This is a loadable kernel module which seems to work at my site - * consisting of a 1.2.13 linux box running netatalk 1.3.3, and with - * the kernel support from 1.3.3b2 including patches routing.patch - * and ddp.disappears.from.chooser. In order to run it, you will need - * to patch ddp.c and aarp.c in the kernel, but only a little... - * - * I'm fairly confident that while this is arguably badly written, the - * problems that people experience will be "higher level", that is, with - * complications in the netatalk code. The driver itself doesn't do - * anything terribly complicated -- it pretends to be an ether device - * as far as netatalk is concerned, strips the DDP data out of the ether - * frame and builds a LLAP packet to send out the card. In the other - * direction, it receives LLAP frames from the card and builds a fake - * ether packet that it then tosses up to the networking code. You can - * argue (correctly) that this is an ugly way to do things, but it - * requires a minimal amount of fooling with the code in ddp.c and aarp.c. - * - * The card will do a lot more than is used here -- I *think* it has the - * layers up through ATP. Even if you knew how that part works (which I - * don't) it would be a big job to carve up the kernel ddp code to insert - * things at a higher level, and probably a bad idea... - * - * There are a number of other cards that do LocalTalk on the PC. If - * nobody finds any insurmountable (at the netatalk level) problems - * here, this driver should encourage people to put some work into the - * other cards (some of which I gather are still commercially available) - * and also to put hooks for LocalTalk into the official ddp code. - * - * I welcome comments and suggestions. This is my first try at Linux - * networking stuff, and there are probably lots of things that I did - * suboptimally. - * - ***/ - -/*** - * - * $Log: ltpc.c,v $ - * Revision 1.1.2.1 2000/03/01 05:35:07 jgarzik - * at and tr cleanup - * - * Revision 1.8 1997/01/28 05:44:54 bradford - * Clean up for non-module a little. - * Hacked about a bit to clean things up - Alan Cox - * Probably broken it from the origina 1.8 - * - - * 1998/11/09: David Huggins-Daines - * Cleaned up the initialization code to use the standard autoirq methods, - and to probe for things in the standard order of i/o, irq, dma. This - removes the "reset the reset" hack, because I couldn't figure out an - easy way to get the card to trigger an interrupt after it. - * Added support for passing configuration parameters on the kernel command - line and through insmod - * Changed the device name from "ltalk0" to "lt0", both to conform with the - other localtalk driver, and to clear up the inconsistency between the - module and the non-module versions of the driver :-) - * Added a bunch of comments (I was going to make some enums for the state - codes and the register offsets, but I'm still not sure exactly what their - semantics are) - * Don't poll anymore in interrupt-driven mode - * It seems to work as a module now (as of 2.1.127), but I don't think - I'm responsible for that... - - * - * Revision 1.7 1996/12/12 03:42:33 bradford - * DMA alloc cribbed from 3c505.c. - * - * Revision 1.6 1996/12/12 03:18:58 bradford - * Added virt_to_bus; works in 2.1.13. - * - * Revision 1.5 1996/12/12 03:13:22 root - * xmitQel initialization -- think through better though. - * - * Revision 1.4 1996/06/18 14:55:55 root - * Change names to ltpc. Tabs. Took a shot at dma alloc, - * although more needs to be done eventually. - * - * Revision 1.3 1996/05/22 14:59:39 root - * Change dev->open, dev->close to track dummy.c in 1.99.(around 7) - * - * Revision 1.2 1996/05/22 14:58:24 root - * Change tabs mostly. - * - * Revision 1.1 1996/04/23 04:45:09 root - * Initial revision - * - * Revision 0.16 1996/03/05 15:59:56 root - * Change ARPHRD_LOCALTLK definition to the "real" one. - * - * Revision 0.15 1996/03/05 06:28:30 root - * Changes for kernel 1.3.70. Still need a few patches to kernel, but - * it's getting closer. - * - * Revision 0.14 1996/02/25 17:38:32 root - * More cleanups. Removed query to card on get_stats. - * - * Revision 0.13 1996/02/21 16:27:40 root - * Refix debug_print_skb. Fix mac.raw gotcha that appeared in 1.3.65. - * Clean up receive code a little. - * - * Revision 0.12 1996/02/19 16:34:53 root - * Fix debug_print_skb. Kludge outgoing snet to 0 when using startup - * range. Change debug to mask: 1 for verbose, 2 for higher level stuff - * including packet printing, 4 for lower level (card i/o) stuff. - * - * Revision 0.11 1996/02/12 15:53:38 root - * Added router sends (requires new aarp.c patch) - * - * Revision 0.10 1996/02/11 00:19:35 root - * Change source LTALK_LOGGING debug switch to insmod ... debug=2. - * - * Revision 0.9 1996/02/10 23:59:35 root - * Fixed those fixes for 1.2 -- DANGER! The at.h that comes with netatalk - * has a *different* definition of struct sockaddr_at than the Linux kernel - * does. This is an "insidious and invidious" bug... - * (Actually the preceding comment is false -- it's the atalk.h in the - * ancient atalk-0.06 that's the problem) - * - * Revision 0.8 1996/02/10 19:09:00 root - * Merge 1.3 changes. Tested OK under 1.3.60. - * - * Revision 0.7 1996/02/10 17:56:56 root - * Added debug=1 parameter on insmod for debugging prints. Tried - * to fix timer unload on rmmod, but I don't think that's the problem. - * - * Revision 0.6 1995/12/31 19:01:09 root - * Clean up rmmod, irq comments per feedback from Corin Anderson (Thanks Corey!) - * Clean up initial probing -- sometimes the card wakes up latched in reset. - * - * Revision 0.5 1995/12/22 06:03:44 root - * Added comments in front and cleaned up a bit. - * This version sent out to people. - * - * Revision 0.4 1995/12/18 03:46:44 root - * Return shortDDP to longDDP fake to 0/0. Added command structs. - * - ***/ - -/* ltpc jumpers are: -* -* Interrupts -- set at most one. If none are set, the driver uses -* polled mode. Because the card was developed in the XT era, the -* original documentation refers to IRQ2. Since you'll be running -* this on an AT (or later) class machine, that really means IRQ9. -* -* SW1 IRQ 4 -* SW2 IRQ 3 -* SW3 IRQ 9 (2 in original card documentation only applies to XT) -* -* -* DMA -- choose DMA 1 or 3, and set both corresponding switches. -* -* SW4 DMA 3 -* SW5 DMA 1 -* SW6 DMA 3 -* SW7 DMA 1 -* -* -* I/O address -- choose one. -* -* SW8 220 / 240 -*/ - -/* To have some stuff logged, do -* insmod ltpc.o debug=1 -* -* For a whole bunch of stuff, use higher numbers. -* -* The default is 0, i.e. no messages except for the probe results. -*/ - -/* insmod-tweakable variables */ -static int debug; -#define DEBUG_VERBOSE 1 -#define DEBUG_UPPER 2 -#define DEBUG_LOWER 4 - -static int io; -static int irq; -static int dma; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* our stuff */ -#include "atalk.h" -#include "ltpc.h" - -static DEFINE_SPINLOCK(txqueue_lock); -static DEFINE_SPINLOCK(mbox_lock); - -/* function prototypes */ -static int do_read(struct net_device *dev, void *cbuf, int cbuflen, - void *dbuf, int dbuflen); -static int sendup_buffer (struct net_device *dev); - -/* Dma Memory related stuff, cribbed directly from 3c505.c */ - -static unsigned long dma_mem_alloc(int size) -{ - int order = get_order(size); - - return __get_dma_pages(GFP_KERNEL, order); -} - -/* DMA data buffer, DMA command buffer */ -static unsigned char *ltdmabuf; -static unsigned char *ltdmacbuf; - -/* private struct, holds our appletalk address */ - -struct ltpc_private -{ - struct atalk_addr my_addr; -}; - -/* transmit queue element struct */ - -struct xmitQel { - struct xmitQel *next; - /* command buffer */ - unsigned char *cbuf; - short cbuflen; - /* data buffer */ - unsigned char *dbuf; - short dbuflen; - unsigned char QWrite; /* read or write data */ - unsigned char mailbox; -}; - -/* the transmit queue itself */ - -static struct xmitQel *xmQhd, *xmQtl; - -static void enQ(struct xmitQel *qel) -{ - unsigned long flags; - qel->next = NULL; - - spin_lock_irqsave(&txqueue_lock, flags); - if (xmQtl) { - xmQtl->next = qel; - } else { - xmQhd = qel; - } - xmQtl = qel; - spin_unlock_irqrestore(&txqueue_lock, flags); - - if (debug & DEBUG_LOWER) - printk("enqueued a 0x%02x command\n",qel->cbuf[0]); -} - -static struct xmitQel *deQ(void) -{ - unsigned long flags; - int i; - struct xmitQel *qel=NULL; - - spin_lock_irqsave(&txqueue_lock, flags); - if (xmQhd) { - qel = xmQhd; - xmQhd = qel->next; - if(!xmQhd) xmQtl = NULL; - } - spin_unlock_irqrestore(&txqueue_lock, flags); - - if ((debug & DEBUG_LOWER) && qel) { - int n; - printk(KERN_DEBUG "ltpc: dequeued command "); - n = qel->cbuflen; - if (n>100) n=100; - for(i=0;icbuf[i]); - printk("\n"); - } - - return qel; -} - -/* and... the queue elements we'll be using */ -static struct xmitQel qels[16]; - -/* and their corresponding mailboxes */ -static unsigned char mailbox[16]; -static unsigned char mboxinuse[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - -static int wait_timeout(struct net_device *dev, int c) -{ - /* returns true if it stayed c */ - /* this uses base+6, but it's ok */ - int i; - - /* twenty second or so total */ - - for(i=0;i<200000;i++) { - if ( c != inb_p(dev->base_addr+6) ) return 0; - udelay(100); - } - return 1; /* timed out */ -} - -/* get the first free mailbox */ - -static int getmbox(void) -{ - unsigned long flags; - int i; - - spin_lock_irqsave(&mbox_lock, flags); - for(i=1;i<16;i++) if(!mboxinuse[i]) { - mboxinuse[i]=1; - spin_unlock_irqrestore(&mbox_lock, flags); - return i; - } - spin_unlock_irqrestore(&mbox_lock, flags); - return 0; -} - -/* read a command from the card */ -static void handlefc(struct net_device *dev) -{ - /* called *only* from idle, non-reentrant */ - int dma = dev->dma; - int base = dev->base_addr; - unsigned long flags; - - - flags=claim_dma_lock(); - disable_dma(dma); - clear_dma_ff(dma); - set_dma_mode(dma,DMA_MODE_READ); - set_dma_addr(dma,virt_to_bus(ltdmacbuf)); - set_dma_count(dma,50); - enable_dma(dma); - release_dma_lock(flags); - - inb_p(base+3); - inb_p(base+2); - - if ( wait_timeout(dev,0xfc) ) printk("timed out in handlefc\n"); -} - -/* read data from the card */ -static void handlefd(struct net_device *dev) -{ - int dma = dev->dma; - int base = dev->base_addr; - unsigned long flags; - - flags=claim_dma_lock(); - disable_dma(dma); - clear_dma_ff(dma); - set_dma_mode(dma,DMA_MODE_READ); - set_dma_addr(dma,virt_to_bus(ltdmabuf)); - set_dma_count(dma,800); - enable_dma(dma); - release_dma_lock(flags); - - inb_p(base+3); - inb_p(base+2); - - if ( wait_timeout(dev,0xfd) ) printk("timed out in handlefd\n"); - sendup_buffer(dev); -} - -static void handlewrite(struct net_device *dev) -{ - /* called *only* from idle, non-reentrant */ - /* on entry, 0xfb and ltdmabuf holds data */ - int dma = dev->dma; - int base = dev->base_addr; - unsigned long flags; - - flags=claim_dma_lock(); - disable_dma(dma); - clear_dma_ff(dma); - set_dma_mode(dma,DMA_MODE_WRITE); - set_dma_addr(dma,virt_to_bus(ltdmabuf)); - set_dma_count(dma,800); - enable_dma(dma); - release_dma_lock(flags); - - inb_p(base+3); - inb_p(base+2); - - if ( wait_timeout(dev,0xfb) ) { - flags=claim_dma_lock(); - printk("timed out in handlewrite, dma res %d\n", - get_dma_residue(dev->dma) ); - release_dma_lock(flags); - } -} - -static void handleread(struct net_device *dev) -{ - /* on entry, 0xfb */ - /* on exit, ltdmabuf holds data */ - int dma = dev->dma; - int base = dev->base_addr; - unsigned long flags; - - - flags=claim_dma_lock(); - disable_dma(dma); - clear_dma_ff(dma); - set_dma_mode(dma,DMA_MODE_READ); - set_dma_addr(dma,virt_to_bus(ltdmabuf)); - set_dma_count(dma,800); - enable_dma(dma); - release_dma_lock(flags); - - inb_p(base+3); - inb_p(base+2); - if ( wait_timeout(dev,0xfb) ) printk("timed out in handleread\n"); -} - -static void handlecommand(struct net_device *dev) -{ - /* on entry, 0xfa and ltdmacbuf holds command */ - int dma = dev->dma; - int base = dev->base_addr; - unsigned long flags; - - flags=claim_dma_lock(); - disable_dma(dma); - clear_dma_ff(dma); - set_dma_mode(dma,DMA_MODE_WRITE); - set_dma_addr(dma,virt_to_bus(ltdmacbuf)); - set_dma_count(dma,50); - enable_dma(dma); - release_dma_lock(flags); - inb_p(base+3); - inb_p(base+2); - if ( wait_timeout(dev,0xfa) ) printk("timed out in handlecommand\n"); -} - -/* ready made command for getting the result from the card */ -static unsigned char rescbuf[2] = {LT_GETRESULT,0}; -static unsigned char resdbuf[2]; - -static int QInIdle; - -/* idle expects to be called with the IRQ line high -- either because of - * an interrupt, or because the line is tri-stated - */ - -static void idle(struct net_device *dev) -{ - unsigned long flags; - int state; - /* FIXME This is initialized to shut the warning up, but I need to - * think this through again. - */ - struct xmitQel *q = NULL; - int oops; - int i; - int base = dev->base_addr; - - spin_lock_irqsave(&txqueue_lock, flags); - if(QInIdle) { - spin_unlock_irqrestore(&txqueue_lock, flags); - return; - } - QInIdle = 1; - spin_unlock_irqrestore(&txqueue_lock, flags); - - /* this tri-states the IRQ line */ - (void) inb_p(base+6); - - oops = 100; - -loop: - if (0>oops--) { - printk("idle: looped too many times\n"); - goto done; - } - - state = inb_p(base+6); - if (state != inb_p(base+6)) goto loop; - - switch(state) { - case 0xfc: - /* incoming command */ - if (debug & DEBUG_LOWER) printk("idle: fc\n"); - handlefc(dev); - break; - case 0xfd: - /* incoming data */ - if(debug & DEBUG_LOWER) printk("idle: fd\n"); - handlefd(dev); - break; - case 0xf9: - /* result ready */ - if (debug & DEBUG_LOWER) printk("idle: f9\n"); - if(!mboxinuse[0]) { - mboxinuse[0] = 1; - qels[0].cbuf = rescbuf; - qels[0].cbuflen = 2; - qels[0].dbuf = resdbuf; - qels[0].dbuflen = 2; - qels[0].QWrite = 0; - qels[0].mailbox = 0; - enQ(&qels[0]); - } - inb_p(dev->base_addr+1); - inb_p(dev->base_addr+0); - if( wait_timeout(dev,0xf9) ) - printk("timed out idle f9\n"); - break; - case 0xf8: - /* ?? */ - if (xmQhd) { - inb_p(dev->base_addr+1); - inb_p(dev->base_addr+0); - if(wait_timeout(dev,0xf8) ) - printk("timed out idle f8\n"); - } else { - goto done; - } - break; - case 0xfa: - /* waiting for command */ - if(debug & DEBUG_LOWER) printk("idle: fa\n"); - if (xmQhd) { - q=deQ(); - memcpy(ltdmacbuf,q->cbuf,q->cbuflen); - ltdmacbuf[1] = q->mailbox; - if (debug>1) { - int n; - printk("ltpc: sent command "); - n = q->cbuflen; - if (n>100) n=100; - for(i=0;iQWrite) { - memcpy(ltdmabuf,q->dbuf,q->dbuflen); - handlewrite(dev); - } else { - handleread(dev); - /* non-zero mailbox numbers are for - commmands, 0 is for GETRESULT - requests */ - if(q->mailbox) { - memcpy(q->dbuf,ltdmabuf,q->dbuflen); - } else { - /* this was a result */ - mailbox[ 0x0f & ltdmabuf[0] ] = ltdmabuf[1]; - mboxinuse[0]=0; - } - } - break; - } - goto loop; - -done: - QInIdle=0; - - /* now set the interrupts back as appropriate */ - /* the first read takes it out of tri-state (but still high) */ - /* the second resets it */ - /* note that after this point, any read of base+6 will - trigger an interrupt */ - - if (dev->irq) { - inb_p(base+7); - inb_p(base+7); - } -} - - -static int do_write(struct net_device *dev, void *cbuf, int cbuflen, - void *dbuf, int dbuflen) -{ - - int i = getmbox(); - int ret; - - if(i) { - qels[i].cbuf = (unsigned char *) cbuf; - qels[i].cbuflen = cbuflen; - qels[i].dbuf = (unsigned char *) dbuf; - qels[i].dbuflen = dbuflen; - qels[i].QWrite = 1; - qels[i].mailbox = i; /* this should be initted rather */ - enQ(&qels[i]); - idle(dev); - ret = mailbox[i]; - mboxinuse[i]=0; - return ret; - } - printk("ltpc: could not allocate mbox\n"); - return -1; -} - -static int do_read(struct net_device *dev, void *cbuf, int cbuflen, - void *dbuf, int dbuflen) -{ - - int i = getmbox(); - int ret; - - if(i) { - qels[i].cbuf = (unsigned char *) cbuf; - qels[i].cbuflen = cbuflen; - qels[i].dbuf = (unsigned char *) dbuf; - qels[i].dbuflen = dbuflen; - qels[i].QWrite = 0; - qels[i].mailbox = i; /* this should be initted rather */ - enQ(&qels[i]); - idle(dev); - ret = mailbox[i]; - mboxinuse[i]=0; - return ret; - } - printk("ltpc: could not allocate mbox\n"); - return -1; -} - -/* end of idle handlers -- what should be seen is do_read, do_write */ - -static struct timer_list ltpc_timer; - -static netdev_tx_t ltpc_xmit(struct sk_buff *skb, struct net_device *dev); - -static int read_30 ( struct net_device *dev) -{ - lt_command c; - c.getflags.command = LT_GETFLAGS; - return do_read(dev, &c, sizeof(c.getflags),&c,0); -} - -static int set_30 (struct net_device *dev,int x) -{ - lt_command c; - c.setflags.command = LT_SETFLAGS; - c.setflags.flags = x; - return do_write(dev, &c, sizeof(c.setflags),&c,0); -} - -/* LLAP to DDP translation */ - -static int sendup_buffer (struct net_device *dev) -{ - /* on entry, command is in ltdmacbuf, data in ltdmabuf */ - /* called from idle, non-reentrant */ - - int dnode, snode, llaptype, len; - int sklen; - struct sk_buff *skb; - struct lt_rcvlap *ltc = (struct lt_rcvlap *) ltdmacbuf; - - if (ltc->command != LT_RCVLAP) { - printk("unknown command 0x%02x from ltpc card\n",ltc->command); - return -1; - } - dnode = ltc->dnode; - snode = ltc->snode; - llaptype = ltc->laptype; - len = ltc->length; - - sklen = len; - if (llaptype == 1) - sklen += 8; /* correct for short ddp */ - if(sklen > 800) { - printk(KERN_INFO "%s: nonsense length in ltpc command 0x14: 0x%08x\n", - dev->name,sklen); - return -1; - } - - if ( (llaptype==0) || (llaptype>2) ) { - printk(KERN_INFO "%s: unknown LLAP type: %d\n",dev->name,llaptype); - return -1; - } - - - skb = dev_alloc_skb(3+sklen); - if (skb == NULL) - { - printk("%s: dropping packet due to memory squeeze.\n", - dev->name); - return -1; - } - skb->dev = dev; - - if (sklen > len) - skb_reserve(skb,8); - skb_put(skb,len+3); - skb->protocol = htons(ETH_P_LOCALTALK); - /* add LLAP header */ - skb->data[0] = dnode; - skb->data[1] = snode; - skb->data[2] = llaptype; - skb_reset_mac_header(skb); /* save pointer to llap header */ - skb_pull(skb,3); - - /* copy ddp(s,e)hdr + contents */ - skb_copy_to_linear_data(skb, ltdmabuf, len); - - skb_reset_transport_header(skb); - - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - - /* toss it onwards */ - netif_rx(skb); - return 0; -} - -/* the handler for the board interrupt */ - -static irqreturn_t -ltpc_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - - if (dev==NULL) { - printk("ltpc_interrupt: unknown device.\n"); - return IRQ_NONE; - } - - inb_p(dev->base_addr+6); /* disable further interrupts from board */ - - idle(dev); /* handle whatever is coming in */ - - /* idle re-enables interrupts from board */ - - return IRQ_HANDLED; -} - -/*** - * - * The ioctls that the driver responds to are: - * - * SIOCSIFADDR -- do probe using the passed node hint. - * SIOCGIFADDR -- return net, node. - * - * some of this stuff should be done elsewhere. - * - ***/ - -static int ltpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - struct sockaddr_at *sa = (struct sockaddr_at *) &ifr->ifr_addr; - /* we'll keep the localtalk node address in dev->pa_addr */ - struct ltpc_private *ltpc_priv = netdev_priv(dev); - struct atalk_addr *aa = <pc_priv->my_addr; - struct lt_init c; - int ltflags; - - if(debug & DEBUG_VERBOSE) printk("ltpc_ioctl called\n"); - - switch(cmd) { - case SIOCSIFADDR: - - aa->s_net = sa->sat_addr.s_net; - - /* this does the probe and returns the node addr */ - c.command = LT_INIT; - c.hint = sa->sat_addr.s_node; - - aa->s_node = do_read(dev,&c,sizeof(c),&c,0); - - /* get all llap frames raw */ - ltflags = read_30(dev); - ltflags |= LT_FLAG_ALLLAP; - set_30 (dev,ltflags); - - dev->broadcast[0] = 0xFF; - dev->dev_addr[0] = aa->s_node; - - dev->addr_len=1; - - return 0; - - case SIOCGIFADDR: - - sa->sat_addr.s_net = aa->s_net; - sa->sat_addr.s_node = aa->s_node; - - return 0; - - default: - return -EINVAL; - } -} - -static void set_multicast_list(struct net_device *dev) -{ - /* This needs to be present to keep netatalk happy. */ - /* Actually netatalk needs fixing! */ -} - -static int ltpc_poll_counter; - -static void ltpc_poll(unsigned long l) -{ - struct net_device *dev = (struct net_device *) l; - - del_timer(<pc_timer); - - if(debug & DEBUG_VERBOSE) { - if (!ltpc_poll_counter) { - ltpc_poll_counter = 50; - printk("ltpc poll is alive\n"); - } - ltpc_poll_counter--; - } - - if (!dev) - return; /* we've been downed */ - - /* poll 20 times per second */ - idle(dev); - ltpc_timer.expires = jiffies + HZ/20; - - add_timer(<pc_timer); -} - -/* DDP to LLAP translation */ - -static netdev_tx_t ltpc_xmit(struct sk_buff *skb, struct net_device *dev) -{ - /* in kernel 1.3.xx, on entry skb->data points to ddp header, - * and skb->len is the length of the ddp data + ddp header - */ - int i; - struct lt_sendlap cbuf; - unsigned char *hdr; - - cbuf.command = LT_SENDLAP; - cbuf.dnode = skb->data[0]; - cbuf.laptype = skb->data[2]; - skb_pull(skb,3); /* skip past LLAP header */ - cbuf.length = skb->len; /* this is host order */ - skb_reset_transport_header(skb); - - if(debug & DEBUG_UPPER) { - printk("command "); - for(i=0;i<6;i++) - printk("%02x ",((unsigned char *)&cbuf)[i]); - printk("\n"); - } - - hdr = skb_transport_header(skb); - do_write(dev, &cbuf, sizeof(cbuf), hdr, skb->len); - - if(debug & DEBUG_UPPER) { - printk("sent %d ddp bytes\n",skb->len); - for (i = 0; i < skb->len; i++) - printk("%02x ", hdr[i]); - printk("\n"); - } - - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -/* initialization stuff */ - -static int __init ltpc_probe_dma(int base, int dma) -{ - int want = (dma == 3) ? 2 : (dma == 1) ? 1 : 3; - unsigned long timeout; - unsigned long f; - - if (want & 1) { - if (request_dma(1,"ltpc")) { - want &= ~1; - } else { - f=claim_dma_lock(); - disable_dma(1); - clear_dma_ff(1); - set_dma_mode(1,DMA_MODE_WRITE); - set_dma_addr(1,virt_to_bus(ltdmabuf)); - set_dma_count(1,sizeof(struct lt_mem)); - enable_dma(1); - release_dma_lock(f); - } - } - if (want & 2) { - if (request_dma(3,"ltpc")) { - want &= ~2; - } else { - f=claim_dma_lock(); - disable_dma(3); - clear_dma_ff(3); - set_dma_mode(3,DMA_MODE_WRITE); - set_dma_addr(3,virt_to_bus(ltdmabuf)); - set_dma_count(3,sizeof(struct lt_mem)); - enable_dma(3); - release_dma_lock(f); - } - } - /* set up request */ - - /* FIXME -- do timings better! */ - - ltdmabuf[0] = LT_READMEM; - ltdmabuf[1] = 1; /* mailbox */ - ltdmabuf[2] = 0; ltdmabuf[3] = 0; /* address */ - ltdmabuf[4] = 0; ltdmabuf[5] = 1; /* read 0x0100 bytes */ - ltdmabuf[6] = 0; /* dunno if this is necessary */ - - inb_p(io+1); - inb_p(io+0); - timeout = jiffies+100*HZ/100; - while(time_before(jiffies, timeout)) { - if ( 0xfa == inb_p(io+6) ) break; - } - - inb_p(io+3); - inb_p(io+2); - while(time_before(jiffies, timeout)) { - if ( 0xfb == inb_p(io+6) ) break; - } - - /* release the other dma channel (if we opened both of them) */ - - if ((want & 2) && (get_dma_residue(3)==sizeof(struct lt_mem))) { - want &= ~2; - free_dma(3); - } - - if ((want & 1) && (get_dma_residue(1)==sizeof(struct lt_mem))) { - want &= ~1; - free_dma(1); - } - - if (!want) - return 0; - - return (want & 2) ? 3 : 1; -} - -static const struct net_device_ops ltpc_netdev = { - .ndo_start_xmit = ltpc_xmit, - .ndo_do_ioctl = ltpc_ioctl, - .ndo_set_multicast_list = set_multicast_list, -}; - -struct net_device * __init ltpc_probe(void) -{ - struct net_device *dev; - int err = -ENOMEM; - int x=0,y=0; - int autoirq; - unsigned long f; - unsigned long timeout; - - dev = alloc_ltalkdev(sizeof(struct ltpc_private)); - if (!dev) - goto out; - - /* probe for the I/O port address */ - - if (io != 0x240 && request_region(0x220,8,"ltpc")) { - x = inb_p(0x220+6); - if ( (x!=0xff) && (x>=0xf0) ) { - io = 0x220; - goto got_port; - } - release_region(0x220,8); - } - if (io != 0x220 && request_region(0x240,8,"ltpc")) { - y = inb_p(0x240+6); - if ( (y!=0xff) && (y>=0xf0) ){ - io = 0x240; - goto got_port; - } - release_region(0x240,8); - } - - /* give up in despair */ - printk(KERN_ERR "LocalTalk card not found; 220 = %02x, 240 = %02x.\n", x,y); - err = -ENODEV; - goto out1; - - got_port: - /* probe for the IRQ line */ - if (irq < 2) { - unsigned long irq_mask; - - irq_mask = probe_irq_on(); - /* reset the interrupt line */ - inb_p(io+7); - inb_p(io+7); - /* trigger an interrupt (I hope) */ - inb_p(io+6); - mdelay(2); - autoirq = probe_irq_off(irq_mask); - - if (autoirq == 0) { - printk(KERN_ERR "ltpc: probe at %#x failed to detect IRQ line.\n", io); - } else { - irq = autoirq; - } - } - - /* allocate a DMA buffer */ - ltdmabuf = (unsigned char *) dma_mem_alloc(1000); - if (!ltdmabuf) { - printk(KERN_ERR "ltpc: mem alloc failed\n"); - err = -ENOMEM; - goto out2; - } - - ltdmacbuf = <dmabuf[800]; - - if(debug & DEBUG_VERBOSE) { - printk("ltdmabuf pointer %08lx\n",(unsigned long) ltdmabuf); - } - - /* reset the card */ - - inb_p(io+1); - inb_p(io+3); - - msleep(20); - - inb_p(io+0); - inb_p(io+2); - inb_p(io+7); /* clear reset */ - inb_p(io+4); - inb_p(io+5); - inb_p(io+5); /* enable dma */ - inb_p(io+6); /* tri-state interrupt line */ - - ssleep(1); - - /* now, figure out which dma channel we're using, unless it's - already been specified */ - /* well, 0 is a legal DMA channel, but the LTPC card doesn't - use it... */ - dma = ltpc_probe_dma(io, dma); - if (!dma) { /* no dma channel */ - printk(KERN_ERR "No DMA channel found on ltpc card.\n"); - err = -ENODEV; - goto out3; - } - - /* print out friendly message */ - if(irq) - printk(KERN_INFO "Apple/Farallon LocalTalk-PC card at %03x, IR%d, DMA%d.\n",io,irq,dma); - else - printk(KERN_INFO "Apple/Farallon LocalTalk-PC card at %03x, DMA%d. Using polled mode.\n",io,dma); - - dev->netdev_ops = <pc_netdev; - dev->base_addr = io; - dev->irq = irq; - dev->dma = dma; - - /* the card will want to send a result at this point */ - /* (I think... leaving out this part makes the kernel crash, - so I put it back in...) */ - - f=claim_dma_lock(); - disable_dma(dma); - clear_dma_ff(dma); - set_dma_mode(dma,DMA_MODE_READ); - set_dma_addr(dma,virt_to_bus(ltdmabuf)); - set_dma_count(dma,0x100); - enable_dma(dma); - release_dma_lock(f); - - (void) inb_p(io+3); - (void) inb_p(io+2); - timeout = jiffies+100*HZ/100; - - while(time_before(jiffies, timeout)) { - if( 0xf9 == inb_p(io+6)) - break; - schedule(); - } - - if(debug & DEBUG_VERBOSE) { - printk("setting up timer and irq\n"); - } - - /* grab it and don't let go :-) */ - if (irq && request_irq( irq, ltpc_interrupt, 0, "ltpc", dev) >= 0) - { - (void) inb_p(io+7); /* enable interrupts from board */ - (void) inb_p(io+7); /* and reset irq line */ - } else { - if( irq ) - printk(KERN_ERR "ltpc: IRQ already in use, using polled mode.\n"); - dev->irq = 0; - /* polled mode -- 20 times per second */ - /* this is really, really slow... should it poll more often? */ - init_timer(<pc_timer); - ltpc_timer.function=ltpc_poll; - ltpc_timer.data = (unsigned long) dev; - - ltpc_timer.expires = jiffies + HZ/20; - add_timer(<pc_timer); - } - err = register_netdev(dev); - if (err) - goto out4; - - return NULL; -out4: - del_timer_sync(<pc_timer); - if (dev->irq) - free_irq(dev->irq, dev); -out3: - free_pages((unsigned long)ltdmabuf, get_order(1000)); -out2: - release_region(io, 8); -out1: - free_netdev(dev); -out: - return ERR_PTR(err); -} - -#ifndef MODULE -/* handles "ltpc=io,irq,dma" kernel command lines */ -static int __init ltpc_setup(char *str) -{ - int ints[5]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - if (ints[0] == 0) { - if (str && !strncmp(str, "auto", 4)) { - /* do nothing :-) */ - } - else { - /* usage message */ - printk (KERN_ERR - "ltpc: usage: ltpc=auto|iobase[,irq[,dma]]\n"); - return 0; - } - } else { - io = ints[1]; - if (ints[0] > 1) { - irq = ints[2]; - } - if (ints[0] > 2) { - dma = ints[3]; - } - /* ignore any other parameters */ - } - return 1; -} - -__setup("ltpc=", ltpc_setup); -#endif /* MODULE */ - -static struct net_device *dev_ltpc; - -#ifdef MODULE - -MODULE_LICENSE("GPL"); -module_param(debug, int, 0); -module_param(io, int, 0); -module_param(irq, int, 0); -module_param(dma, int, 0); - - -static int __init ltpc_module_init(void) -{ - if(io == 0) - printk(KERN_NOTICE - "ltpc: Autoprobing is not recommended for modules\n"); - - dev_ltpc = ltpc_probe(); - if (IS_ERR(dev_ltpc)) - return PTR_ERR(dev_ltpc); - return 0; -} -module_init(ltpc_module_init); -#endif - -static void __exit ltpc_cleanup(void) -{ - - if(debug & DEBUG_VERBOSE) printk("unregister_netdev\n"); - unregister_netdev(dev_ltpc); - - ltpc_timer.data = 0; /* signal the poll routine that we're done */ - - del_timer_sync(<pc_timer); - - if(debug & DEBUG_VERBOSE) printk("freeing irq\n"); - - if (dev_ltpc->irq) - free_irq(dev_ltpc->irq, dev_ltpc); - - if(debug & DEBUG_VERBOSE) printk("freeing dma\n"); - - if (dev_ltpc->dma) - free_dma(dev_ltpc->dma); - - if(debug & DEBUG_VERBOSE) printk("freeing ioaddr\n"); - - if (dev_ltpc->base_addr) - release_region(dev_ltpc->base_addr,8); - - free_netdev(dev_ltpc); - - if(debug & DEBUG_VERBOSE) printk("free_pages\n"); - - free_pages( (unsigned long) ltdmabuf, get_order(1000)); - - if(debug & DEBUG_VERBOSE) printk("returning from cleanup_module\n"); -} - -module_exit(ltpc_cleanup); diff --git a/drivers/staging/appletalk/ltpc.h b/drivers/staging/appletalk/ltpc.h deleted file mode 100644 index cd30544a3729..000000000000 --- a/drivers/staging/appletalk/ltpc.h +++ /dev/null @@ -1,73 +0,0 @@ -/*** ltpc.h - * - * - ***/ - -#define LT_GETRESULT 0x00 -#define LT_WRITEMEM 0x01 -#define LT_READMEM 0x02 -#define LT_GETFLAGS 0x04 -#define LT_SETFLAGS 0x05 -#define LT_INIT 0x10 -#define LT_SENDLAP 0x13 -#define LT_RCVLAP 0x14 - -/* the flag that we care about */ -#define LT_FLAG_ALLLAP 0x04 - -struct lt_getresult { - unsigned char command; - unsigned char mailbox; -}; - -struct lt_mem { - unsigned char command; - unsigned char mailbox; - unsigned short addr; /* host order */ - unsigned short length; /* host order */ -}; - -struct lt_setflags { - unsigned char command; - unsigned char mailbox; - unsigned char flags; -}; - -struct lt_getflags { - unsigned char command; - unsigned char mailbox; -}; - -struct lt_init { - unsigned char command; - unsigned char mailbox; - unsigned char hint; -}; - -struct lt_sendlap { - unsigned char command; - unsigned char mailbox; - unsigned char dnode; - unsigned char laptype; - unsigned short length; /* host order */ -}; - -struct lt_rcvlap { - unsigned char command; - unsigned char dnode; - unsigned char snode; - unsigned char laptype; - unsigned short length; /* host order */ -}; - -union lt_command { - struct lt_getresult getresult; - struct lt_mem mem; - struct lt_setflags setflags; - struct lt_getflags getflags; - struct lt_init init; - struct lt_sendlap sendlap; - struct lt_rcvlap rcvlap; -}; -typedef union lt_command lt_command; - diff --git a/drivers/staging/appletalk/sysctl_net_atalk.c b/drivers/staging/appletalk/sysctl_net_atalk.c deleted file mode 100644 index 4c896b625b2b..000000000000 --- a/drivers/staging/appletalk/sysctl_net_atalk.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * sysctl_net_atalk.c: sysctl interface to net AppleTalk subsystem. - * - * Begun April 1, 1996, Mike Shaver. - * Added /proc/sys/net/atalk directory entry (empty =) ). [MS] - * Dynamic registration, added aarp entries. (5/30/97 Chris Horn) - */ - -#include -#include -#include "atalk.h" - -static struct ctl_table atalk_table[] = { - { - .procname = "aarp-expiry-time", - .data = &sysctl_aarp_expiry_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - { - .procname = "aarp-tick-time", - .data = &sysctl_aarp_tick_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - { - .procname = "aarp-retransmit-limit", - .data = &sysctl_aarp_retransmit_limit, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, - { - .procname = "aarp-resolve-time", - .data = &sysctl_aarp_resolve_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - { }, -}; - -static struct ctl_path atalk_path[] = { - { .procname = "net", }, - { .procname = "appletalk", }, - { } -}; - -static struct ctl_table_header *atalk_table_header; - -void atalk_register_sysctl(void) -{ - atalk_table_header = register_sysctl_paths(atalk_path, atalk_table); -} - -void atalk_unregister_sysctl(void) -{ - unregister_sysctl_table(atalk_table_header); -} diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 86a2d7d905c0..61abb638b4bf 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 362041b73a2f..2296d8b1931f 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -43,6 +43,7 @@ header-y += agpgart.h header-y += aio_abi.h header-y += apm_bios.h header-y += arcfb.h +header-y += atalk.h header-y += atm.h header-y += atm_eni.h header-y += atm_he.h diff --git a/include/linux/atalk.h b/include/linux/atalk.h new file mode 100644 index 000000000000..d34c187432ed --- /dev/null +++ b/include/linux/atalk.h @@ -0,0 +1,208 @@ +#ifndef __LINUX_ATALK_H__ +#define __LINUX_ATALK_H__ + +#include +#include + +/* + * AppleTalk networking structures + * + * The following are directly referenced from the University Of Michigan + * netatalk for compatibility reasons. + */ +#define ATPORT_FIRST 1 +#define ATPORT_RESERVED 128 +#define ATPORT_LAST 254 /* 254 is only legal on localtalk */ +#define ATADDR_ANYNET (__u16)0 +#define ATADDR_ANYNODE (__u8)0 +#define ATADDR_ANYPORT (__u8)0 +#define ATADDR_BCAST (__u8)255 +#define DDP_MAXSZ 587 +#define DDP_MAXHOPS 15 /* 4 bits of hop counter */ + +#define SIOCATALKDIFADDR (SIOCPROTOPRIVATE + 0) + +struct atalk_addr { + __be16 s_net; + __u8 s_node; +}; + +struct sockaddr_at { + sa_family_t sat_family; + __u8 sat_port; + struct atalk_addr sat_addr; + char sat_zero[8]; +}; + +struct atalk_netrange { + __u8 nr_phase; + __be16 nr_firstnet; + __be16 nr_lastnet; +}; + +#ifdef __KERNEL__ + +#include + +struct atalk_route { + struct net_device *dev; + struct atalk_addr target; + struct atalk_addr gateway; + int flags; + struct atalk_route *next; +}; + +/** + * struct atalk_iface - AppleTalk Interface + * @dev - Network device associated with this interface + * @address - Our address + * @status - What are we doing? + * @nets - Associated direct netrange + * @next - next element in the list of interfaces + */ +struct atalk_iface { + struct net_device *dev; + struct atalk_addr address; + int status; +#define ATIF_PROBE 1 /* Probing for an address */ +#define ATIF_PROBE_FAIL 2 /* Probe collided */ + struct atalk_netrange nets; + struct atalk_iface *next; +}; + +struct atalk_sock { + /* struct sock has to be the first member of atalk_sock */ + struct sock sk; + __be16 dest_net; + __be16 src_net; + unsigned char dest_node; + unsigned char src_node; + unsigned char dest_port; + unsigned char src_port; +}; + +static inline struct atalk_sock *at_sk(struct sock *sk) +{ + return (struct atalk_sock *)sk; +} + +struct ddpehdr { + __be16 deh_len_hops; /* lower 10 bits are length, next 4 - hops */ + __be16 deh_sum; + __be16 deh_dnet; + __be16 deh_snet; + __u8 deh_dnode; + __u8 deh_snode; + __u8 deh_dport; + __u8 deh_sport; + /* And netatalk apps expect to stick the type in themselves */ +}; + +static __inline__ struct ddpehdr *ddp_hdr(struct sk_buff *skb) +{ + return (struct ddpehdr *)skb_transport_header(skb); +} + +/* AppleTalk AARP headers */ +struct elapaarp { + __be16 hw_type; +#define AARP_HW_TYPE_ETHERNET 1 +#define AARP_HW_TYPE_TOKENRING 2 + __be16 pa_type; + __u8 hw_len; + __u8 pa_len; +#define AARP_PA_ALEN 4 + __be16 function; +#define AARP_REQUEST 1 +#define AARP_REPLY 2 +#define AARP_PROBE 3 + __u8 hw_src[ETH_ALEN]; + __u8 pa_src_zero; + __be16 pa_src_net; + __u8 pa_src_node; + __u8 hw_dst[ETH_ALEN]; + __u8 pa_dst_zero; + __be16 pa_dst_net; + __u8 pa_dst_node; +} __attribute__ ((packed)); + +static __inline__ struct elapaarp *aarp_hdr(struct sk_buff *skb) +{ + return (struct elapaarp *)skb_transport_header(skb); +} + +/* Not specified - how long till we drop a resolved entry */ +#define AARP_EXPIRY_TIME (5 * 60 * HZ) +/* Size of hash table */ +#define AARP_HASH_SIZE 16 +/* Fast retransmission timer when resolving */ +#define AARP_TICK_TIME (HZ / 5) +/* Send 10 requests then give up (2 seconds) */ +#define AARP_RETRANSMIT_LIMIT 10 +/* + * Some value bigger than total retransmit time + a bit for last reply to + * appear and to stop continual requests + */ +#define AARP_RESOLVE_TIME (10 * HZ) + +extern struct datalink_proto *ddp_dl, *aarp_dl; +extern void aarp_proto_init(void); + +/* Inter module exports */ + +/* Give a device find its atif control structure */ +static inline struct atalk_iface *atalk_find_dev(struct net_device *dev) +{ + return dev->atalk_ptr; +} + +extern struct atalk_addr *atalk_find_dev_addr(struct net_device *dev); +extern struct net_device *atrtr_get_dev(struct atalk_addr *sa); +extern int aarp_send_ddp(struct net_device *dev, + struct sk_buff *skb, + struct atalk_addr *sa, void *hwaddr); +extern void aarp_device_down(struct net_device *dev); +extern void aarp_probe_network(struct atalk_iface *atif); +extern int aarp_proxy_probe_network(struct atalk_iface *atif, + struct atalk_addr *sa); +extern void aarp_proxy_remove(struct net_device *dev, + struct atalk_addr *sa); + +extern void aarp_cleanup_module(void); + +extern struct hlist_head atalk_sockets; +extern rwlock_t atalk_sockets_lock; + +extern struct atalk_route *atalk_routes; +extern rwlock_t atalk_routes_lock; + +extern struct atalk_iface *atalk_interfaces; +extern rwlock_t atalk_interfaces_lock; + +extern struct atalk_route atrtr_default; + +extern const struct file_operations atalk_seq_arp_fops; + +extern int sysctl_aarp_expiry_time; +extern int sysctl_aarp_tick_time; +extern int sysctl_aarp_retransmit_limit; +extern int sysctl_aarp_resolve_time; + +#ifdef CONFIG_SYSCTL +extern void atalk_register_sysctl(void); +extern void atalk_unregister_sysctl(void); +#else +#define atalk_register_sysctl() do { } while(0) +#define atalk_unregister_sysctl() do { } while(0) +#endif + +#ifdef CONFIG_PROC_FS +extern int atalk_proc_init(void); +extern void atalk_proc_exit(void); +#else +#define atalk_proc_init() ({ 0; }) +#define atalk_proc_exit() do { } while(0) +#endif /* CONFIG_PROC_FS */ + +#endif /* __KERNEL__ */ +#endif /* __LINUX_ATALK_H__ */ diff --git a/net/Kconfig b/net/Kconfig index 082c8bc977e1..72840626284b 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -204,6 +204,7 @@ source "net/8021q/Kconfig" source "net/decnet/Kconfig" source "net/llc/Kconfig" source "net/ipx/Kconfig" +source "drivers/net/appletalk/Kconfig" source "net/x25/Kconfig" source "net/lapb/Kconfig" source "net/econet/Kconfig" diff --git a/net/Makefile b/net/Makefile index 16d9947b4b95..a3330ebe2c53 100644 --- a/net/Makefile +++ b/net/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_NET_KEY) += key/ obj-$(CONFIG_BRIDGE) += bridge/ obj-$(CONFIG_NET_DSA) += dsa/ obj-$(CONFIG_IPX) += ipx/ +obj-$(CONFIG_ATALK) += appletalk/ obj-$(CONFIG_WAN_ROUTER) += wanrouter/ obj-$(CONFIG_X25) += x25/ obj-$(CONFIG_LAPB) += lapb/ diff --git a/net/appletalk/Makefile b/net/appletalk/Makefile new file mode 100644 index 000000000000..5cda56edef57 --- /dev/null +++ b/net/appletalk/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the Linux AppleTalk layer. +# + +obj-$(CONFIG_ATALK) += appletalk.o + +appletalk-y := aarp.o ddp.o dev.o +appletalk-$(CONFIG_PROC_FS) += atalk_proc.o +appletalk-$(CONFIG_SYSCTL) += sysctl_net_atalk.o diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c new file mode 100644 index 000000000000..50dce7981321 --- /dev/null +++ b/net/appletalk/aarp.c @@ -0,0 +1,1063 @@ +/* + * AARP: An implementation of the AppleTalk AARP protocol for + * Ethernet 'ELAP'. + * + * Alan Cox + * + * This doesn't fit cleanly with the IP arp. Potentially we can use + * the generic neighbour discovery code to clean this up. + * + * FIXME: + * We ought to handle the retransmits with a single list and a + * separate fast timer for when it is needed. + * Use neighbour discovery code. + * Token Ring Support. + * + * 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. + * + * + * References: + * Inside AppleTalk (2nd Ed). + * Fixes: + * Jaume Grau - flush caches on AARP_PROBE + * Rob Newberry - Added proxy AARP and AARP proc fs, + * moved probing from DDP module. + * Arnaldo C. Melo - don't mangle rx packets + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int sysctl_aarp_expiry_time = AARP_EXPIRY_TIME; +int sysctl_aarp_tick_time = AARP_TICK_TIME; +int sysctl_aarp_retransmit_limit = AARP_RETRANSMIT_LIMIT; +int sysctl_aarp_resolve_time = AARP_RESOLVE_TIME; + +/* Lists of aarp entries */ +/** + * struct aarp_entry - AARP entry + * @last_sent - Last time we xmitted the aarp request + * @packet_queue - Queue of frames wait for resolution + * @status - Used for proxy AARP + * expires_at - Entry expiry time + * target_addr - DDP Address + * dev - Device to use + * hwaddr - Physical i/f address of target/router + * xmit_count - When this hits 10 we give up + * next - Next entry in chain + */ +struct aarp_entry { + /* These first two are only used for unresolved entries */ + unsigned long last_sent; + struct sk_buff_head packet_queue; + int status; + unsigned long expires_at; + struct atalk_addr target_addr; + struct net_device *dev; + char hwaddr[6]; + unsigned short xmit_count; + struct aarp_entry *next; +}; + +/* Hashed list of resolved, unresolved and proxy entries */ +static struct aarp_entry *resolved[AARP_HASH_SIZE]; +static struct aarp_entry *unresolved[AARP_HASH_SIZE]; +static struct aarp_entry *proxies[AARP_HASH_SIZE]; +static int unresolved_count; + +/* One lock protects it all. */ +static DEFINE_RWLOCK(aarp_lock); + +/* Used to walk the list and purge/kick entries. */ +static struct timer_list aarp_timer; + +/* + * Delete an aarp queue + * + * Must run under aarp_lock. + */ +static void __aarp_expire(struct aarp_entry *a) +{ + skb_queue_purge(&a->packet_queue); + kfree(a); +} + +/* + * Send an aarp queue entry request + * + * Must run under aarp_lock. + */ +static void __aarp_send_query(struct aarp_entry *a) +{ + static unsigned char aarp_eth_multicast[ETH_ALEN] = + { 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; + struct net_device *dev = a->dev; + struct elapaarp *eah; + int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length; + struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); + struct atalk_addr *sat = atalk_find_dev_addr(dev); + + if (!skb) + return; + + if (!sat) { + kfree_skb(skb); + return; + } + + /* Set up the buffer */ + skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb_put(skb, sizeof(*eah)); + skb->protocol = htons(ETH_P_ATALK); + skb->dev = dev; + eah = aarp_hdr(skb); + + /* Set up the ARP */ + eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); + eah->pa_type = htons(ETH_P_ATALK); + eah->hw_len = ETH_ALEN; + eah->pa_len = AARP_PA_ALEN; + eah->function = htons(AARP_REQUEST); + + memcpy(eah->hw_src, dev->dev_addr, ETH_ALEN); + + eah->pa_src_zero = 0; + eah->pa_src_net = sat->s_net; + eah->pa_src_node = sat->s_node; + + memset(eah->hw_dst, '\0', ETH_ALEN); + + eah->pa_dst_zero = 0; + eah->pa_dst_net = a->target_addr.s_net; + eah->pa_dst_node = a->target_addr.s_node; + + /* Send it */ + aarp_dl->request(aarp_dl, skb, aarp_eth_multicast); + /* Update the sending count */ + a->xmit_count++; + a->last_sent = jiffies; +} + +/* This runs under aarp_lock and in softint context, so only atomic memory + * allocations can be used. */ +static void aarp_send_reply(struct net_device *dev, struct atalk_addr *us, + struct atalk_addr *them, unsigned char *sha) +{ + struct elapaarp *eah; + int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length; + struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); + + if (!skb) + return; + + /* Set up the buffer */ + skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb_put(skb, sizeof(*eah)); + skb->protocol = htons(ETH_P_ATALK); + skb->dev = dev; + eah = aarp_hdr(skb); + + /* Set up the ARP */ + eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); + eah->pa_type = htons(ETH_P_ATALK); + eah->hw_len = ETH_ALEN; + eah->pa_len = AARP_PA_ALEN; + eah->function = htons(AARP_REPLY); + + memcpy(eah->hw_src, dev->dev_addr, ETH_ALEN); + + eah->pa_src_zero = 0; + eah->pa_src_net = us->s_net; + eah->pa_src_node = us->s_node; + + if (!sha) + memset(eah->hw_dst, '\0', ETH_ALEN); + else + memcpy(eah->hw_dst, sha, ETH_ALEN); + + eah->pa_dst_zero = 0; + eah->pa_dst_net = them->s_net; + eah->pa_dst_node = them->s_node; + + /* Send it */ + aarp_dl->request(aarp_dl, skb, sha); +} + +/* + * Send probe frames. Called from aarp_probe_network and + * aarp_proxy_probe_network. + */ + +static void aarp_send_probe(struct net_device *dev, struct atalk_addr *us) +{ + struct elapaarp *eah; + int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length; + struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC); + static unsigned char aarp_eth_multicast[ETH_ALEN] = + { 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; + + if (!skb) + return; + + /* Set up the buffer */ + skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb_put(skb, sizeof(*eah)); + skb->protocol = htons(ETH_P_ATALK); + skb->dev = dev; + eah = aarp_hdr(skb); + + /* Set up the ARP */ + eah->hw_type = htons(AARP_HW_TYPE_ETHERNET); + eah->pa_type = htons(ETH_P_ATALK); + eah->hw_len = ETH_ALEN; + eah->pa_len = AARP_PA_ALEN; + eah->function = htons(AARP_PROBE); + + memcpy(eah->hw_src, dev->dev_addr, ETH_ALEN); + + eah->pa_src_zero = 0; + eah->pa_src_net = us->s_net; + eah->pa_src_node = us->s_node; + + memset(eah->hw_dst, '\0', ETH_ALEN); + + eah->pa_dst_zero = 0; + eah->pa_dst_net = us->s_net; + eah->pa_dst_node = us->s_node; + + /* Send it */ + aarp_dl->request(aarp_dl, skb, aarp_eth_multicast); +} + +/* + * Handle an aarp timer expire + * + * Must run under the aarp_lock. + */ + +static void __aarp_expire_timer(struct aarp_entry **n) +{ + struct aarp_entry *t; + + while (*n) + /* Expired ? */ + if (time_after(jiffies, (*n)->expires_at)) { + t = *n; + *n = (*n)->next; + __aarp_expire(t); + } else + n = &((*n)->next); +} + +/* + * Kick all pending requests 5 times a second. + * + * Must run under the aarp_lock. + */ +static void __aarp_kick(struct aarp_entry **n) +{ + struct aarp_entry *t; + + while (*n) + /* Expired: if this will be the 11th tx, we delete instead. */ + if ((*n)->xmit_count >= sysctl_aarp_retransmit_limit) { + t = *n; + *n = (*n)->next; + __aarp_expire(t); + } else { + __aarp_send_query(*n); + n = &((*n)->next); + } +} + +/* + * A device has gone down. Take all entries referring to the device + * and remove them. + * + * Must run under the aarp_lock. + */ +static void __aarp_expire_device(struct aarp_entry **n, struct net_device *dev) +{ + struct aarp_entry *t; + + while (*n) + if ((*n)->dev == dev) { + t = *n; + *n = (*n)->next; + __aarp_expire(t); + } else + n = &((*n)->next); +} + +/* Handle the timer event */ +static void aarp_expire_timeout(unsigned long unused) +{ + int ct; + + write_lock_bh(&aarp_lock); + + for (ct = 0; ct < AARP_HASH_SIZE; ct++) { + __aarp_expire_timer(&resolved[ct]); + __aarp_kick(&unresolved[ct]); + __aarp_expire_timer(&unresolved[ct]); + __aarp_expire_timer(&proxies[ct]); + } + + write_unlock_bh(&aarp_lock); + mod_timer(&aarp_timer, jiffies + + (unresolved_count ? sysctl_aarp_tick_time : + sysctl_aarp_expiry_time)); +} + +/* Network device notifier chain handler. */ +static int aarp_device_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct net_device *dev = ptr; + int ct; + + if (!net_eq(dev_net(dev), &init_net)) + return NOTIFY_DONE; + + if (event == NETDEV_DOWN) { + write_lock_bh(&aarp_lock); + + for (ct = 0; ct < AARP_HASH_SIZE; ct++) { + __aarp_expire_device(&resolved[ct], dev); + __aarp_expire_device(&unresolved[ct], dev); + __aarp_expire_device(&proxies[ct], dev); + } + + write_unlock_bh(&aarp_lock); + } + return NOTIFY_DONE; +} + +/* Expire all entries in a hash chain */ +static void __aarp_expire_all(struct aarp_entry **n) +{ + struct aarp_entry *t; + + while (*n) { + t = *n; + *n = (*n)->next; + __aarp_expire(t); + } +} + +/* Cleanup all hash chains -- module unloading */ +static void aarp_purge(void) +{ + int ct; + + write_lock_bh(&aarp_lock); + for (ct = 0; ct < AARP_HASH_SIZE; ct++) { + __aarp_expire_all(&resolved[ct]); + __aarp_expire_all(&unresolved[ct]); + __aarp_expire_all(&proxies[ct]); + } + write_unlock_bh(&aarp_lock); +} + +/* + * Create a new aarp entry. This must use GFP_ATOMIC because it + * runs while holding spinlocks. + */ +static struct aarp_entry *aarp_alloc(void) +{ + struct aarp_entry *a = kmalloc(sizeof(*a), GFP_ATOMIC); + + if (a) + skb_queue_head_init(&a->packet_queue); + return a; +} + +/* + * Find an entry. We might return an expired but not yet purged entry. We + * don't care as it will do no harm. + * + * This must run under the aarp_lock. + */ +static struct aarp_entry *__aarp_find_entry(struct aarp_entry *list, + struct net_device *dev, + struct atalk_addr *sat) +{ + while (list) { + if (list->target_addr.s_net == sat->s_net && + list->target_addr.s_node == sat->s_node && + list->dev == dev) + break; + list = list->next; + } + + return list; +} + +/* Called from the DDP code, and thus must be exported. */ +void aarp_proxy_remove(struct net_device *dev, struct atalk_addr *sa) +{ + int hash = sa->s_node % (AARP_HASH_SIZE - 1); + struct aarp_entry *a; + + write_lock_bh(&aarp_lock); + + a = __aarp_find_entry(proxies[hash], dev, sa); + if (a) + a->expires_at = jiffies - 1; + + write_unlock_bh(&aarp_lock); +} + +/* This must run under aarp_lock. */ +static struct atalk_addr *__aarp_proxy_find(struct net_device *dev, + struct atalk_addr *sa) +{ + int hash = sa->s_node % (AARP_HASH_SIZE - 1); + struct aarp_entry *a = __aarp_find_entry(proxies[hash], dev, sa); + + return a ? sa : NULL; +} + +/* + * Probe a Phase 1 device or a device that requires its Net:Node to + * be set via an ioctl. + */ +static void aarp_send_probe_phase1(struct atalk_iface *iface) +{ + struct ifreq atreq; + struct sockaddr_at *sa = (struct sockaddr_at *)&atreq.ifr_addr; + const struct net_device_ops *ops = iface->dev->netdev_ops; + + sa->sat_addr.s_node = iface->address.s_node; + sa->sat_addr.s_net = ntohs(iface->address.s_net); + + /* We pass the Net:Node to the drivers/cards by a Device ioctl. */ + if (!(ops->ndo_do_ioctl(iface->dev, &atreq, SIOCSIFADDR))) { + ops->ndo_do_ioctl(iface->dev, &atreq, SIOCGIFADDR); + if (iface->address.s_net != htons(sa->sat_addr.s_net) || + iface->address.s_node != sa->sat_addr.s_node) + iface->status |= ATIF_PROBE_FAIL; + + iface->address.s_net = htons(sa->sat_addr.s_net); + iface->address.s_node = sa->sat_addr.s_node; + } +} + + +void aarp_probe_network(struct atalk_iface *atif) +{ + if (atif->dev->type == ARPHRD_LOCALTLK || + atif->dev->type == ARPHRD_PPP) + aarp_send_probe_phase1(atif); + else { + unsigned int count; + + for (count = 0; count < AARP_RETRANSMIT_LIMIT; count++) { + aarp_send_probe(atif->dev, &atif->address); + + /* Defer 1/10th */ + msleep(100); + + if (atif->status & ATIF_PROBE_FAIL) + break; + } + } +} + +int aarp_proxy_probe_network(struct atalk_iface *atif, struct atalk_addr *sa) +{ + int hash, retval = -EPROTONOSUPPORT; + struct aarp_entry *entry; + unsigned int count; + + /* + * we don't currently support LocalTalk or PPP for proxy AARP; + * if someone wants to try and add it, have fun + */ + if (atif->dev->type == ARPHRD_LOCALTLK || + atif->dev->type == ARPHRD_PPP) + goto out; + + /* + * create a new AARP entry with the flags set to be published -- + * we need this one to hang around even if it's in use + */ + entry = aarp_alloc(); + retval = -ENOMEM; + if (!entry) + goto out; + + entry->expires_at = -1; + entry->status = ATIF_PROBE; + entry->target_addr.s_node = sa->s_node; + entry->target_addr.s_net = sa->s_net; + entry->dev = atif->dev; + + write_lock_bh(&aarp_lock); + + hash = sa->s_node % (AARP_HASH_SIZE - 1); + entry->next = proxies[hash]; + proxies[hash] = entry; + + for (count = 0; count < AARP_RETRANSMIT_LIMIT; count++) { + aarp_send_probe(atif->dev, sa); + + /* Defer 1/10th */ + write_unlock_bh(&aarp_lock); + msleep(100); + write_lock_bh(&aarp_lock); + + if (entry->status & ATIF_PROBE_FAIL) + break; + } + + if (entry->status & ATIF_PROBE_FAIL) { + entry->expires_at = jiffies - 1; /* free the entry */ + retval = -EADDRINUSE; /* return network full */ + } else { /* clear the probing flag */ + entry->status &= ~ATIF_PROBE; + retval = 1; + } + + write_unlock_bh(&aarp_lock); +out: + return retval; +} + +/* Send a DDP frame */ +int aarp_send_ddp(struct net_device *dev, struct sk_buff *skb, + struct atalk_addr *sa, void *hwaddr) +{ + static char ddp_eth_multicast[ETH_ALEN] = + { 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF }; + int hash; + struct aarp_entry *a; + + skb_reset_network_header(skb); + + /* Check for LocalTalk first */ + if (dev->type == ARPHRD_LOCALTLK) { + struct atalk_addr *at = atalk_find_dev_addr(dev); + struct ddpehdr *ddp = (struct ddpehdr *)skb->data; + int ft = 2; + + /* + * Compressible ? + * + * IFF: src_net == dest_net == device_net + * (zero matches anything) + */ + + if ((!ddp->deh_snet || at->s_net == ddp->deh_snet) && + (!ddp->deh_dnet || at->s_net == ddp->deh_dnet)) { + skb_pull(skb, sizeof(*ddp) - 4); + + /* + * The upper two remaining bytes are the port + * numbers we just happen to need. Now put the + * length in the lower two. + */ + *((__be16 *)skb->data) = htons(skb->len); + ft = 1; + } + /* + * Nice and easy. No AARP type protocols occur here so we can + * just shovel it out with a 3 byte LLAP header + */ + + skb_push(skb, 3); + skb->data[0] = sa->s_node; + skb->data[1] = at->s_node; + skb->data[2] = ft; + skb->dev = dev; + goto sendit; + } + + /* On a PPP link we neither compress nor aarp. */ + if (dev->type == ARPHRD_PPP) { + skb->protocol = htons(ETH_P_PPPTALK); + skb->dev = dev; + goto sendit; + } + + /* Non ELAP we cannot do. */ + if (dev->type != ARPHRD_ETHER) + goto free_it; + + skb->dev = dev; + skb->protocol = htons(ETH_P_ATALK); + hash = sa->s_node % (AARP_HASH_SIZE - 1); + + /* Do we have a resolved entry? */ + if (sa->s_node == ATADDR_BCAST) { + /* Send it */ + ddp_dl->request(ddp_dl, skb, ddp_eth_multicast); + goto sent; + } + + write_lock_bh(&aarp_lock); + a = __aarp_find_entry(resolved[hash], dev, sa); + + if (a) { /* Return 1 and fill in the address */ + a->expires_at = jiffies + (sysctl_aarp_expiry_time * 10); + ddp_dl->request(ddp_dl, skb, a->hwaddr); + write_unlock_bh(&aarp_lock); + goto sent; + } + + /* Do we have an unresolved entry: This is the less common path */ + a = __aarp_find_entry(unresolved[hash], dev, sa); + if (a) { /* Queue onto the unresolved queue */ + skb_queue_tail(&a->packet_queue, skb); + goto out_unlock; + } + + /* Allocate a new entry */ + a = aarp_alloc(); + if (!a) { + /* Whoops slipped... good job it's an unreliable protocol 8) */ + write_unlock_bh(&aarp_lock); + goto free_it; + } + + /* Set up the queue */ + skb_queue_tail(&a->packet_queue, skb); + a->expires_at = jiffies + sysctl_aarp_resolve_time; + a->dev = dev; + a->next = unresolved[hash]; + a->target_addr = *sa; + a->xmit_count = 0; + unresolved[hash] = a; + unresolved_count++; + + /* Send an initial request for the address */ + __aarp_send_query(a); + + /* + * Switch to fast timer if needed (That is if this is the first + * unresolved entry to get added) + */ + + if (unresolved_count == 1) + mod_timer(&aarp_timer, jiffies + sysctl_aarp_tick_time); + + /* Now finally, it is safe to drop the lock. */ +out_unlock: + write_unlock_bh(&aarp_lock); + + /* Tell the ddp layer we have taken over for this frame. */ + goto sent; + +sendit: + if (skb->sk) + skb->priority = skb->sk->sk_priority; + if (dev_queue_xmit(skb)) + goto drop; +sent: + return NET_XMIT_SUCCESS; +free_it: + kfree_skb(skb); +drop: + return NET_XMIT_DROP; +} +EXPORT_SYMBOL(aarp_send_ddp); + +/* + * An entry in the aarp unresolved queue has become resolved. Send + * all the frames queued under it. + * + * Must run under aarp_lock. + */ +static void __aarp_resolved(struct aarp_entry **list, struct aarp_entry *a, + int hash) +{ + struct sk_buff *skb; + + while (*list) + if (*list == a) { + unresolved_count--; + *list = a->next; + + /* Move into the resolved list */ + a->next = resolved[hash]; + resolved[hash] = a; + + /* Kick frames off */ + while ((skb = skb_dequeue(&a->packet_queue)) != NULL) { + a->expires_at = jiffies + + sysctl_aarp_expiry_time * 10; + ddp_dl->request(ddp_dl, skb, a->hwaddr); + } + } else + list = &((*list)->next); +} + +/* + * This is called by the SNAP driver whenever we see an AARP SNAP + * frame. We currently only support Ethernet. + */ +static int aarp_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct elapaarp *ea = aarp_hdr(skb); + int hash, ret = 0; + __u16 function; + struct aarp_entry *a; + struct atalk_addr sa, *ma, da; + struct atalk_iface *ifa; + + if (!net_eq(dev_net(dev), &init_net)) + goto out0; + + /* We only do Ethernet SNAP AARP. */ + if (dev->type != ARPHRD_ETHER) + goto out0; + + /* Frame size ok? */ + if (!skb_pull(skb, sizeof(*ea))) + goto out0; + + function = ntohs(ea->function); + + /* Sanity check fields. */ + if (function < AARP_REQUEST || function > AARP_PROBE || + ea->hw_len != ETH_ALEN || ea->pa_len != AARP_PA_ALEN || + ea->pa_src_zero || ea->pa_dst_zero) + goto out0; + + /* Looks good. */ + hash = ea->pa_src_node % (AARP_HASH_SIZE - 1); + + /* Build an address. */ + sa.s_node = ea->pa_src_node; + sa.s_net = ea->pa_src_net; + + /* Process the packet. Check for replies of me. */ + ifa = atalk_find_dev(dev); + if (!ifa) + goto out1; + + if (ifa->status & ATIF_PROBE && + ifa->address.s_node == ea->pa_dst_node && + ifa->address.s_net == ea->pa_dst_net) { + ifa->status |= ATIF_PROBE_FAIL; /* Fail the probe (in use) */ + goto out1; + } + + /* Check for replies of proxy AARP entries */ + da.s_node = ea->pa_dst_node; + da.s_net = ea->pa_dst_net; + + write_lock_bh(&aarp_lock); + a = __aarp_find_entry(proxies[hash], dev, &da); + + if (a && a->status & ATIF_PROBE) { + a->status |= ATIF_PROBE_FAIL; + /* + * we do not respond to probe or request packets for + * this address while we are probing this address + */ + goto unlock; + } + + switch (function) { + case AARP_REPLY: + if (!unresolved_count) /* Speed up */ + break; + + /* Find the entry. */ + a = __aarp_find_entry(unresolved[hash], dev, &sa); + if (!a || dev != a->dev) + break; + + /* We can fill one in - this is good. */ + memcpy(a->hwaddr, ea->hw_src, ETH_ALEN); + __aarp_resolved(&unresolved[hash], a, hash); + if (!unresolved_count) + mod_timer(&aarp_timer, + jiffies + sysctl_aarp_expiry_time); + break; + + case AARP_REQUEST: + case AARP_PROBE: + + /* + * If it is my address set ma to my address and reply. + * We can treat probe and request the same. Probe + * simply means we shouldn't cache the querying host, + * as in a probe they are proposing an address not + * using one. + * + * Support for proxy-AARP added. We check if the + * address is one of our proxies before we toss the + * packet out. + */ + + sa.s_node = ea->pa_dst_node; + sa.s_net = ea->pa_dst_net; + + /* See if we have a matching proxy. */ + ma = __aarp_proxy_find(dev, &sa); + if (!ma) + ma = &ifa->address; + else { /* We need to make a copy of the entry. */ + da.s_node = sa.s_node; + da.s_net = sa.s_net; + ma = &da; + } + + if (function == AARP_PROBE) { + /* + * A probe implies someone trying to get an + * address. So as a precaution flush any + * entries we have for this address. + */ + a = __aarp_find_entry(resolved[sa.s_node % + (AARP_HASH_SIZE - 1)], + skb->dev, &sa); + + /* + * Make it expire next tick - that avoids us + * getting into a probe/flush/learn/probe/ + * flush/learn cycle during probing of a slow + * to respond host addr. + */ + if (a) { + a->expires_at = jiffies - 1; + mod_timer(&aarp_timer, jiffies + + sysctl_aarp_tick_time); + } + } + + if (sa.s_node != ma->s_node) + break; + + if (sa.s_net && ma->s_net && sa.s_net != ma->s_net) + break; + + sa.s_node = ea->pa_src_node; + sa.s_net = ea->pa_src_net; + + /* aarp_my_address has found the address to use for us. + */ + aarp_send_reply(dev, ma, &sa, ea->hw_src); + break; + } + +unlock: + write_unlock_bh(&aarp_lock); +out1: + ret = 1; +out0: + kfree_skb(skb); + return ret; +} + +static struct notifier_block aarp_notifier = { + .notifier_call = aarp_device_event, +}; + +static unsigned char aarp_snap_id[] = { 0x00, 0x00, 0x00, 0x80, 0xF3 }; + +void __init aarp_proto_init(void) +{ + aarp_dl = register_snap_client(aarp_snap_id, aarp_rcv); + if (!aarp_dl) + printk(KERN_CRIT "Unable to register AARP with SNAP.\n"); + setup_timer(&aarp_timer, aarp_expire_timeout, 0); + aarp_timer.expires = jiffies + sysctl_aarp_expiry_time; + add_timer(&aarp_timer); + register_netdevice_notifier(&aarp_notifier); +} + +/* Remove the AARP entries associated with a device. */ +void aarp_device_down(struct net_device *dev) +{ + int ct; + + write_lock_bh(&aarp_lock); + + for (ct = 0; ct < AARP_HASH_SIZE; ct++) { + __aarp_expire_device(&resolved[ct], dev); + __aarp_expire_device(&unresolved[ct], dev); + __aarp_expire_device(&proxies[ct], dev); + } + + write_unlock_bh(&aarp_lock); +} + +#ifdef CONFIG_PROC_FS +struct aarp_iter_state { + int bucket; + struct aarp_entry **table; +}; + +/* + * Get the aarp entry that is in the chain described + * by the iterator. + * If pos is set then skip till that index. + * pos = 1 is the first entry + */ +static struct aarp_entry *iter_next(struct aarp_iter_state *iter, loff_t *pos) +{ + int ct = iter->bucket; + struct aarp_entry **table = iter->table; + loff_t off = 0; + struct aarp_entry *entry; + + rescan: + while(ct < AARP_HASH_SIZE) { + for (entry = table[ct]; entry; entry = entry->next) { + if (!pos || ++off == *pos) { + iter->table = table; + iter->bucket = ct; + return entry; + } + } + ++ct; + } + + if (table == resolved) { + ct = 0; + table = unresolved; + goto rescan; + } + if (table == unresolved) { + ct = 0; + table = proxies; + goto rescan; + } + return NULL; +} + +static void *aarp_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(aarp_lock) +{ + struct aarp_iter_state *iter = seq->private; + + read_lock_bh(&aarp_lock); + iter->table = resolved; + iter->bucket = 0; + + return *pos ? iter_next(iter, pos) : SEQ_START_TOKEN; +} + +static void *aarp_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct aarp_entry *entry = v; + struct aarp_iter_state *iter = seq->private; + + ++*pos; + + /* first line after header */ + if (v == SEQ_START_TOKEN) + entry = iter_next(iter, NULL); + + /* next entry in current bucket */ + else if (entry->next) + entry = entry->next; + + /* next bucket or table */ + else { + ++iter->bucket; + entry = iter_next(iter, NULL); + } + return entry; +} + +static void aarp_seq_stop(struct seq_file *seq, void *v) + __releases(aarp_lock) +{ + read_unlock_bh(&aarp_lock); +} + +static const char *dt2str(unsigned long ticks) +{ + static char buf[32]; + + sprintf(buf, "%ld.%02ld", ticks / HZ, ((ticks % HZ) * 100 ) / HZ); + + return buf; +} + +static int aarp_seq_show(struct seq_file *seq, void *v) +{ + struct aarp_iter_state *iter = seq->private; + struct aarp_entry *entry = v; + unsigned long now = jiffies; + + if (v == SEQ_START_TOKEN) + seq_puts(seq, + "Address Interface Hardware Address" + " Expires LastSend Retry Status\n"); + else { + seq_printf(seq, "%04X:%02X %-12s", + ntohs(entry->target_addr.s_net), + (unsigned int) entry->target_addr.s_node, + entry->dev ? entry->dev->name : "????"); + seq_printf(seq, "%pM", entry->hwaddr); + seq_printf(seq, " %8s", + dt2str((long)entry->expires_at - (long)now)); + if (iter->table == unresolved) + seq_printf(seq, " %8s %6hu", + dt2str(now - entry->last_sent), + entry->xmit_count); + else + seq_puts(seq, " "); + seq_printf(seq, " %s\n", + (iter->table == resolved) ? "resolved" + : (iter->table == unresolved) ? "unresolved" + : (iter->table == proxies) ? "proxies" + : "unknown"); + } + return 0; +} + +static const struct seq_operations aarp_seq_ops = { + .start = aarp_seq_start, + .next = aarp_seq_next, + .stop = aarp_seq_stop, + .show = aarp_seq_show, +}; + +static int aarp_seq_open(struct inode *inode, struct file *file) +{ + return seq_open_private(file, &aarp_seq_ops, + sizeof(struct aarp_iter_state)); +} + +const struct file_operations atalk_seq_arp_fops = { + .owner = THIS_MODULE, + .open = aarp_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; +#endif + +/* General module cleanup. Called from cleanup_module() in ddp.c. */ +void aarp_cleanup_module(void) +{ + del_timer_sync(&aarp_timer); + unregister_netdevice_notifier(&aarp_notifier); + unregister_snap_client(aarp_dl); + aarp_purge(); +} diff --git a/net/appletalk/atalk_proc.c b/net/appletalk/atalk_proc.c new file mode 100644 index 000000000000..6ef0e761e5de --- /dev/null +++ b/net/appletalk/atalk_proc.c @@ -0,0 +1,301 @@ +/* + * atalk_proc.c - proc support for Appletalk + * + * Copyright(c) Arnaldo Carvalho de Melo + * + * 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, version 2. + */ + +#include +#include +#include +#include +#include +#include + + +static __inline__ struct atalk_iface *atalk_get_interface_idx(loff_t pos) +{ + struct atalk_iface *i; + + for (i = atalk_interfaces; pos && i; i = i->next) + --pos; + + return i; +} + +static void *atalk_seq_interface_start(struct seq_file *seq, loff_t *pos) + __acquires(atalk_interfaces_lock) +{ + loff_t l = *pos; + + read_lock_bh(&atalk_interfaces_lock); + return l ? atalk_get_interface_idx(--l) : SEQ_START_TOKEN; +} + +static void *atalk_seq_interface_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct atalk_iface *i; + + ++*pos; + if (v == SEQ_START_TOKEN) { + i = NULL; + if (atalk_interfaces) + i = atalk_interfaces; + goto out; + } + i = v; + i = i->next; +out: + return i; +} + +static void atalk_seq_interface_stop(struct seq_file *seq, void *v) + __releases(atalk_interfaces_lock) +{ + read_unlock_bh(&atalk_interfaces_lock); +} + +static int atalk_seq_interface_show(struct seq_file *seq, void *v) +{ + struct atalk_iface *iface; + + if (v == SEQ_START_TOKEN) { + seq_puts(seq, "Interface Address Networks " + "Status\n"); + goto out; + } + + iface = v; + seq_printf(seq, "%-16s %04X:%02X %04X-%04X %d\n", + iface->dev->name, ntohs(iface->address.s_net), + iface->address.s_node, ntohs(iface->nets.nr_firstnet), + ntohs(iface->nets.nr_lastnet), iface->status); +out: + return 0; +} + +static __inline__ struct atalk_route *atalk_get_route_idx(loff_t pos) +{ + struct atalk_route *r; + + for (r = atalk_routes; pos && r; r = r->next) + --pos; + + return r; +} + +static void *atalk_seq_route_start(struct seq_file *seq, loff_t *pos) + __acquires(atalk_routes_lock) +{ + loff_t l = *pos; + + read_lock_bh(&atalk_routes_lock); + return l ? atalk_get_route_idx(--l) : SEQ_START_TOKEN; +} + +static void *atalk_seq_route_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct atalk_route *r; + + ++*pos; + if (v == SEQ_START_TOKEN) { + r = NULL; + if (atalk_routes) + r = atalk_routes; + goto out; + } + r = v; + r = r->next; +out: + return r; +} + +static void atalk_seq_route_stop(struct seq_file *seq, void *v) + __releases(atalk_routes_lock) +{ + read_unlock_bh(&atalk_routes_lock); +} + +static int atalk_seq_route_show(struct seq_file *seq, void *v) +{ + struct atalk_route *rt; + + if (v == SEQ_START_TOKEN) { + seq_puts(seq, "Target Router Flags Dev\n"); + goto out; + } + + if (atrtr_default.dev) { + rt = &atrtr_default; + seq_printf(seq, "Default %04X:%02X %-4d %s\n", + ntohs(rt->gateway.s_net), rt->gateway.s_node, + rt->flags, rt->dev->name); + } + + rt = v; + seq_printf(seq, "%04X:%02X %04X:%02X %-4d %s\n", + ntohs(rt->target.s_net), rt->target.s_node, + ntohs(rt->gateway.s_net), rt->gateway.s_node, + rt->flags, rt->dev->name); +out: + return 0; +} + +static void *atalk_seq_socket_start(struct seq_file *seq, loff_t *pos) + __acquires(atalk_sockets_lock) +{ + read_lock_bh(&atalk_sockets_lock); + return seq_hlist_start_head(&atalk_sockets, *pos); +} + +static void *atalk_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos) +{ + return seq_hlist_next(v, &atalk_sockets, pos); +} + +static void atalk_seq_socket_stop(struct seq_file *seq, void *v) + __releases(atalk_sockets_lock) +{ + read_unlock_bh(&atalk_sockets_lock); +} + +static int atalk_seq_socket_show(struct seq_file *seq, void *v) +{ + struct sock *s; + struct atalk_sock *at; + + if (v == SEQ_START_TOKEN) { + seq_printf(seq, "Type Local_addr Remote_addr Tx_queue " + "Rx_queue St UID\n"); + goto out; + } + + s = sk_entry(v); + at = at_sk(s); + + seq_printf(seq, "%02X %04X:%02X:%02X %04X:%02X:%02X %08X:%08X " + "%02X %d\n", + s->sk_type, ntohs(at->src_net), at->src_node, at->src_port, + ntohs(at->dest_net), at->dest_node, at->dest_port, + sk_wmem_alloc_get(s), + sk_rmem_alloc_get(s), + s->sk_state, SOCK_INODE(s->sk_socket)->i_uid); +out: + return 0; +} + +static const struct seq_operations atalk_seq_interface_ops = { + .start = atalk_seq_interface_start, + .next = atalk_seq_interface_next, + .stop = atalk_seq_interface_stop, + .show = atalk_seq_interface_show, +}; + +static const struct seq_operations atalk_seq_route_ops = { + .start = atalk_seq_route_start, + .next = atalk_seq_route_next, + .stop = atalk_seq_route_stop, + .show = atalk_seq_route_show, +}; + +static const struct seq_operations atalk_seq_socket_ops = { + .start = atalk_seq_socket_start, + .next = atalk_seq_socket_next, + .stop = atalk_seq_socket_stop, + .show = atalk_seq_socket_show, +}; + +static int atalk_seq_interface_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &atalk_seq_interface_ops); +} + +static int atalk_seq_route_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &atalk_seq_route_ops); +} + +static int atalk_seq_socket_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &atalk_seq_socket_ops); +} + +static const struct file_operations atalk_seq_interface_fops = { + .owner = THIS_MODULE, + .open = atalk_seq_interface_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static const struct file_operations atalk_seq_route_fops = { + .owner = THIS_MODULE, + .open = atalk_seq_route_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static const struct file_operations atalk_seq_socket_fops = { + .owner = THIS_MODULE, + .open = atalk_seq_socket_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static struct proc_dir_entry *atalk_proc_dir; + +int __init atalk_proc_init(void) +{ + struct proc_dir_entry *p; + int rc = -ENOMEM; + + atalk_proc_dir = proc_mkdir("atalk", init_net.proc_net); + if (!atalk_proc_dir) + goto out; + + p = proc_create("interface", S_IRUGO, atalk_proc_dir, + &atalk_seq_interface_fops); + if (!p) + goto out_interface; + + p = proc_create("route", S_IRUGO, atalk_proc_dir, + &atalk_seq_route_fops); + if (!p) + goto out_route; + + p = proc_create("socket", S_IRUGO, atalk_proc_dir, + &atalk_seq_socket_fops); + if (!p) + goto out_socket; + + p = proc_create("arp", S_IRUGO, atalk_proc_dir, &atalk_seq_arp_fops); + if (!p) + goto out_arp; + + rc = 0; +out: + return rc; +out_arp: + remove_proc_entry("socket", atalk_proc_dir); +out_socket: + remove_proc_entry("route", atalk_proc_dir); +out_route: + remove_proc_entry("interface", atalk_proc_dir); +out_interface: + remove_proc_entry("atalk", init_net.proc_net); + goto out; +} + +void __exit atalk_proc_exit(void) +{ + remove_proc_entry("interface", atalk_proc_dir); + remove_proc_entry("route", atalk_proc_dir); + remove_proc_entry("socket", atalk_proc_dir); + remove_proc_entry("arp", atalk_proc_dir); + remove_proc_entry("atalk", init_net.proc_net); +} diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c new file mode 100644 index 000000000000..c410b93fda2e --- /dev/null +++ b/net/appletalk/ddp.c @@ -0,0 +1,1981 @@ +/* + * DDP: An implementation of the AppleTalk DDP protocol for + * Ethernet 'ELAP'. + * + * Alan Cox + * + * With more than a little assistance from + * + * Wesley Craig + * + * Fixes: + * Neil Horman : Added missing device ioctls + * Michael Callahan : Made routing work + * Wesley Craig : Fix probing to listen to a + * passed node id. + * Alan Cox : Added send/recvmsg support + * Alan Cox : Moved at. to protinfo in + * socket. + * Alan Cox : Added firewall hooks. + * Alan Cox : Supports new ARPHRD_LOOPBACK + * Christer Weinigel : Routing and /proc fixes. + * Bradford Johnson : LocalTalk. + * Tom Dyas : Module support. + * Alan Cox : Hooks for PPP (based on the + * LocalTalk hook). + * Alan Cox : Posix bits + * Alan Cox/Mike Freeman : Possible fix to NBP problems + * Bradford Johnson : IP-over-DDP (experimental) + * Jay Schulist : Moved IP-over-DDP to its own + * driver file. (ipddp.c & ipddp.h) + * Jay Schulist : Made work as module with + * AppleTalk drivers, cleaned it. + * Rob Newberry : Added proxy AARP and AARP + * procfs, moved probing to AARP + * module. + * Adrian Sun/ + * Michael Zuelsdorff : fix for net.0 packets. don't + * allow illegal ether/tokentalk + * port assignment. we lose a + * valid localtalk port as a + * result. + * Arnaldo C. de Melo : Cleanup, in preparation for + * shared skb support 8) + * Arnaldo C. de Melo : Move proc stuff to atalk_proc.c, + * use seq_file + * + * 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 /* For TIOCOUTQ/INQ */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "../core/kmap_skb.h" + +struct datalink_proto *ddp_dl, *aarp_dl; +static const struct proto_ops atalk_dgram_ops; + +/**************************************************************************\ +* * +* Handlers for the socket list. * +* * +\**************************************************************************/ + +HLIST_HEAD(atalk_sockets); +DEFINE_RWLOCK(atalk_sockets_lock); + +static inline void __atalk_insert_socket(struct sock *sk) +{ + sk_add_node(sk, &atalk_sockets); +} + +static inline void atalk_remove_socket(struct sock *sk) +{ + write_lock_bh(&atalk_sockets_lock); + sk_del_node_init(sk); + write_unlock_bh(&atalk_sockets_lock); +} + +static struct sock *atalk_search_socket(struct sockaddr_at *to, + struct atalk_iface *atif) +{ + struct sock *s; + struct hlist_node *node; + + read_lock_bh(&atalk_sockets_lock); + sk_for_each(s, node, &atalk_sockets) { + struct atalk_sock *at = at_sk(s); + + if (to->sat_port != at->src_port) + continue; + + if (to->sat_addr.s_net == ATADDR_ANYNET && + to->sat_addr.s_node == ATADDR_BCAST) + goto found; + + if (to->sat_addr.s_net == at->src_net && + (to->sat_addr.s_node == at->src_node || + to->sat_addr.s_node == ATADDR_BCAST || + to->sat_addr.s_node == ATADDR_ANYNODE)) + goto found; + + /* XXXX.0 -- we got a request for this router. make sure + * that the node is appropriately set. */ + if (to->sat_addr.s_node == ATADDR_ANYNODE && + to->sat_addr.s_net != ATADDR_ANYNET && + atif->address.s_node == at->src_node) { + to->sat_addr.s_node = atif->address.s_node; + goto found; + } + } + s = NULL; +found: + read_unlock_bh(&atalk_sockets_lock); + return s; +} + +/** + * atalk_find_or_insert_socket - Try to find a socket matching ADDR + * @sk - socket to insert in the list if it is not there already + * @sat - address to search for + * + * Try to find a socket matching ADDR in the socket list, if found then return + * it. If not, insert SK into the socket list. + * + * This entire operation must execute atomically. + */ +static struct sock *atalk_find_or_insert_socket(struct sock *sk, + struct sockaddr_at *sat) +{ + struct sock *s; + struct hlist_node *node; + struct atalk_sock *at; + + write_lock_bh(&atalk_sockets_lock); + sk_for_each(s, node, &atalk_sockets) { + at = at_sk(s); + + if (at->src_net == sat->sat_addr.s_net && + at->src_node == sat->sat_addr.s_node && + at->src_port == sat->sat_port) + goto found; + } + s = NULL; + __atalk_insert_socket(sk); /* Wheee, it's free, assign and insert. */ +found: + write_unlock_bh(&atalk_sockets_lock); + return s; +} + +static void atalk_destroy_timer(unsigned long data) +{ + struct sock *sk = (struct sock *)data; + + if (sk_has_allocations(sk)) { + sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME; + add_timer(&sk->sk_timer); + } else + sock_put(sk); +} + +static inline void atalk_destroy_socket(struct sock *sk) +{ + atalk_remove_socket(sk); + skb_queue_purge(&sk->sk_receive_queue); + + if (sk_has_allocations(sk)) { + setup_timer(&sk->sk_timer, atalk_destroy_timer, + (unsigned long)sk); + sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME; + add_timer(&sk->sk_timer); + } else + sock_put(sk); +} + +/**************************************************************************\ +* * +* Routing tables for the AppleTalk socket layer. * +* * +\**************************************************************************/ + +/* Anti-deadlock ordering is atalk_routes_lock --> iface_lock -DaveM */ +struct atalk_route *atalk_routes; +DEFINE_RWLOCK(atalk_routes_lock); + +struct atalk_iface *atalk_interfaces; +DEFINE_RWLOCK(atalk_interfaces_lock); + +/* For probing devices or in a routerless network */ +struct atalk_route atrtr_default; + +/* AppleTalk interface control */ +/* + * Drop a device. Doesn't drop any of its routes - that is the caller's + * problem. Called when we down the interface or delete the address. + */ +static void atif_drop_device(struct net_device *dev) +{ + struct atalk_iface **iface = &atalk_interfaces; + struct atalk_iface *tmp; + + write_lock_bh(&atalk_interfaces_lock); + while ((tmp = *iface) != NULL) { + if (tmp->dev == dev) { + *iface = tmp->next; + dev_put(dev); + kfree(tmp); + dev->atalk_ptr = NULL; + } else + iface = &tmp->next; + } + write_unlock_bh(&atalk_interfaces_lock); +} + +static struct atalk_iface *atif_add_device(struct net_device *dev, + struct atalk_addr *sa) +{ + struct atalk_iface *iface = kzalloc(sizeof(*iface), GFP_KERNEL); + + if (!iface) + goto out; + + dev_hold(dev); + iface->dev = dev; + dev->atalk_ptr = iface; + iface->address = *sa; + iface->status = 0; + + write_lock_bh(&atalk_interfaces_lock); + iface->next = atalk_interfaces; + atalk_interfaces = iface; + write_unlock_bh(&atalk_interfaces_lock); +out: + return iface; +} + +/* Perform phase 2 AARP probing on our tentative address */ +static int atif_probe_device(struct atalk_iface *atif) +{ + int netrange = ntohs(atif->nets.nr_lastnet) - + ntohs(atif->nets.nr_firstnet) + 1; + int probe_net = ntohs(atif->address.s_net); + int probe_node = atif->address.s_node; + int netct, nodect; + + /* Offset the network we start probing with */ + if (probe_net == ATADDR_ANYNET) { + probe_net = ntohs(atif->nets.nr_firstnet); + if (netrange) + probe_net += jiffies % netrange; + } + if (probe_node == ATADDR_ANYNODE) + probe_node = jiffies & 0xFF; + + /* Scan the networks */ + atif->status |= ATIF_PROBE; + for (netct = 0; netct <= netrange; netct++) { + /* Sweep the available nodes from a given start */ + atif->address.s_net = htons(probe_net); + for (nodect = 0; nodect < 256; nodect++) { + atif->address.s_node = (nodect + probe_node) & 0xFF; + if (atif->address.s_node > 0 && + atif->address.s_node < 254) { + /* Probe a proposed address */ + aarp_probe_network(atif); + + if (!(atif->status & ATIF_PROBE_FAIL)) { + atif->status &= ~ATIF_PROBE; + return 0; + } + } + atif->status &= ~ATIF_PROBE_FAIL; + } + probe_net++; + if (probe_net > ntohs(atif->nets.nr_lastnet)) + probe_net = ntohs(atif->nets.nr_firstnet); + } + atif->status &= ~ATIF_PROBE; + + return -EADDRINUSE; /* Network is full... */ +} + + +/* Perform AARP probing for a proxy address */ +static int atif_proxy_probe_device(struct atalk_iface *atif, + struct atalk_addr* proxy_addr) +{ + int netrange = ntohs(atif->nets.nr_lastnet) - + ntohs(atif->nets.nr_firstnet) + 1; + /* we probe the interface's network */ + int probe_net = ntohs(atif->address.s_net); + int probe_node = ATADDR_ANYNODE; /* we'll take anything */ + int netct, nodect; + + /* Offset the network we start probing with */ + if (probe_net == ATADDR_ANYNET) { + probe_net = ntohs(atif->nets.nr_firstnet); + if (netrange) + probe_net += jiffies % netrange; + } + + if (probe_node == ATADDR_ANYNODE) + probe_node = jiffies & 0xFF; + + /* Scan the networks */ + for (netct = 0; netct <= netrange; netct++) { + /* Sweep the available nodes from a given start */ + proxy_addr->s_net = htons(probe_net); + for (nodect = 0; nodect < 256; nodect++) { + proxy_addr->s_node = (nodect + probe_node) & 0xFF; + if (proxy_addr->s_node > 0 && + proxy_addr->s_node < 254) { + /* Tell AARP to probe a proposed address */ + int ret = aarp_proxy_probe_network(atif, + proxy_addr); + + if (ret != -EADDRINUSE) + return ret; + } + } + probe_net++; + if (probe_net > ntohs(atif->nets.nr_lastnet)) + probe_net = ntohs(atif->nets.nr_firstnet); + } + + return -EADDRINUSE; /* Network is full... */ +} + + +struct atalk_addr *atalk_find_dev_addr(struct net_device *dev) +{ + struct atalk_iface *iface = dev->atalk_ptr; + return iface ? &iface->address : NULL; +} + +static struct atalk_addr *atalk_find_primary(void) +{ + struct atalk_iface *fiface = NULL; + struct atalk_addr *retval; + struct atalk_iface *iface; + + /* + * Return a point-to-point interface only if + * there is no non-ptp interface available. + */ + read_lock_bh(&atalk_interfaces_lock); + for (iface = atalk_interfaces; iface; iface = iface->next) { + if (!fiface && !(iface->dev->flags & IFF_LOOPBACK)) + fiface = iface; + if (!(iface->dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) { + retval = &iface->address; + goto out; + } + } + + if (fiface) + retval = &fiface->address; + else if (atalk_interfaces) + retval = &atalk_interfaces->address; + else + retval = NULL; +out: + read_unlock_bh(&atalk_interfaces_lock); + return retval; +} + +/* + * Find a match for 'any network' - ie any of our interfaces with that + * node number will do just nicely. + */ +static struct atalk_iface *atalk_find_anynet(int node, struct net_device *dev) +{ + struct atalk_iface *iface = dev->atalk_ptr; + + if (!iface || iface->status & ATIF_PROBE) + goto out_err; + + if (node != ATADDR_BCAST && + iface->address.s_node != node && + node != ATADDR_ANYNODE) + goto out_err; +out: + return iface; +out_err: + iface = NULL; + goto out; +} + +/* Find a match for a specific network:node pair */ +static struct atalk_iface *atalk_find_interface(__be16 net, int node) +{ + struct atalk_iface *iface; + + read_lock_bh(&atalk_interfaces_lock); + for (iface = atalk_interfaces; iface; iface = iface->next) { + if ((node == ATADDR_BCAST || + node == ATADDR_ANYNODE || + iface->address.s_node == node) && + iface->address.s_net == net && + !(iface->status & ATIF_PROBE)) + break; + + /* XXXX.0 -- net.0 returns the iface associated with net */ + if (node == ATADDR_ANYNODE && net != ATADDR_ANYNET && + ntohs(iface->nets.nr_firstnet) <= ntohs(net) && + ntohs(net) <= ntohs(iface->nets.nr_lastnet)) + break; + } + read_unlock_bh(&atalk_interfaces_lock); + return iface; +} + + +/* + * Find a route for an AppleTalk packet. This ought to get cached in + * the socket (later on...). We know about host routes and the fact + * that a route must be direct to broadcast. + */ +static struct atalk_route *atrtr_find(struct atalk_addr *target) +{ + /* + * we must search through all routes unless we find a + * host route, because some host routes might overlap + * network routes + */ + struct atalk_route *net_route = NULL; + struct atalk_route *r; + + read_lock_bh(&atalk_routes_lock); + for (r = atalk_routes; r; r = r->next) { + if (!(r->flags & RTF_UP)) + continue; + + if (r->target.s_net == target->s_net) { + if (r->flags & RTF_HOST) { + /* + * if this host route is for the target, + * the we're done + */ + if (r->target.s_node == target->s_node) + goto out; + } else + /* + * this route will work if there isn't a + * direct host route, so cache it + */ + net_route = r; + } + } + + /* + * if we found a network route but not a direct host + * route, then return it + */ + if (net_route) + r = net_route; + else if (atrtr_default.dev) + r = &atrtr_default; + else /* No route can be found */ + r = NULL; +out: + read_unlock_bh(&atalk_routes_lock); + return r; +} + + +/* + * Given an AppleTalk network, find the device to use. This can be + * a simple lookup. + */ +struct net_device *atrtr_get_dev(struct atalk_addr *sa) +{ + struct atalk_route *atr = atrtr_find(sa); + return atr ? atr->dev : NULL; +} + +/* Set up a default router */ +static void atrtr_set_default(struct net_device *dev) +{ + atrtr_default.dev = dev; + atrtr_default.flags = RTF_UP; + atrtr_default.gateway.s_net = htons(0); + atrtr_default.gateway.s_node = 0; +} + +/* + * Add a router. Basically make sure it looks valid and stuff the + * entry in the list. While it uses netranges we always set them to one + * entry to work like netatalk. + */ +static int atrtr_create(struct rtentry *r, struct net_device *devhint) +{ + struct sockaddr_at *ta = (struct sockaddr_at *)&r->rt_dst; + struct sockaddr_at *ga = (struct sockaddr_at *)&r->rt_gateway; + struct atalk_route *rt; + struct atalk_iface *iface, *riface; + int retval = -EINVAL; + + /* + * Fixme: Raise/Lower a routing change semaphore for these + * operations. + */ + + /* Validate the request */ + if (ta->sat_family != AF_APPLETALK || + (!devhint && ga->sat_family != AF_APPLETALK)) + goto out; + + /* Now walk the routing table and make our decisions */ + write_lock_bh(&atalk_routes_lock); + for (rt = atalk_routes; rt; rt = rt->next) { + if (r->rt_flags != rt->flags) + continue; + + if (ta->sat_addr.s_net == rt->target.s_net) { + if (!(rt->flags & RTF_HOST)) + break; + if (ta->sat_addr.s_node == rt->target.s_node) + break; + } + } + + if (!devhint) { + riface = NULL; + + read_lock_bh(&atalk_interfaces_lock); + for (iface = atalk_interfaces; iface; iface = iface->next) { + if (!riface && + ntohs(ga->sat_addr.s_net) >= + ntohs(iface->nets.nr_firstnet) && + ntohs(ga->sat_addr.s_net) <= + ntohs(iface->nets.nr_lastnet)) + riface = iface; + + if (ga->sat_addr.s_net == iface->address.s_net && + ga->sat_addr.s_node == iface->address.s_node) + riface = iface; + } + read_unlock_bh(&atalk_interfaces_lock); + + retval = -ENETUNREACH; + if (!riface) + goto out_unlock; + + devhint = riface->dev; + } + + if (!rt) { + rt = kzalloc(sizeof(*rt), GFP_ATOMIC); + + retval = -ENOBUFS; + if (!rt) + goto out_unlock; + + rt->next = atalk_routes; + atalk_routes = rt; + } + + /* Fill in the routing entry */ + rt->target = ta->sat_addr; + dev_hold(devhint); + rt->dev = devhint; + rt->flags = r->rt_flags; + rt->gateway = ga->sat_addr; + + retval = 0; +out_unlock: + write_unlock_bh(&atalk_routes_lock); +out: + return retval; +} + +/* Delete a route. Find it and discard it */ +static int atrtr_delete(struct atalk_addr * addr) +{ + struct atalk_route **r = &atalk_routes; + int retval = 0; + struct atalk_route *tmp; + + write_lock_bh(&atalk_routes_lock); + while ((tmp = *r) != NULL) { + if (tmp->target.s_net == addr->s_net && + (!(tmp->flags&RTF_GATEWAY) || + tmp->target.s_node == addr->s_node)) { + *r = tmp->next; + dev_put(tmp->dev); + kfree(tmp); + goto out; + } + r = &tmp->next; + } + retval = -ENOENT; +out: + write_unlock_bh(&atalk_routes_lock); + return retval; +} + +/* + * Called when a device is downed. Just throw away any routes + * via it. + */ +static void atrtr_device_down(struct net_device *dev) +{ + struct atalk_route **r = &atalk_routes; + struct atalk_route *tmp; + + write_lock_bh(&atalk_routes_lock); + while ((tmp = *r) != NULL) { + if (tmp->dev == dev) { + *r = tmp->next; + dev_put(dev); + kfree(tmp); + } else + r = &tmp->next; + } + write_unlock_bh(&atalk_routes_lock); + + if (atrtr_default.dev == dev) + atrtr_set_default(NULL); +} + +/* Actually down the interface */ +static inline void atalk_dev_down(struct net_device *dev) +{ + atrtr_device_down(dev); /* Remove all routes for the device */ + aarp_device_down(dev); /* Remove AARP entries for the device */ + atif_drop_device(dev); /* Remove the device */ +} + +/* + * A device event has occurred. Watch for devices going down and + * delete our use of them (iface and route). + */ +static int ddp_device_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct net_device *dev = ptr; + + if (!net_eq(dev_net(dev), &init_net)) + return NOTIFY_DONE; + + if (event == NETDEV_DOWN) + /* Discard any use of this */ + atalk_dev_down(dev); + + return NOTIFY_DONE; +} + +/* ioctl calls. Shouldn't even need touching */ +/* Device configuration ioctl calls */ +static int atif_ioctl(int cmd, void __user *arg) +{ + static char aarp_mcast[6] = { 0x09, 0x00, 0x00, 0xFF, 0xFF, 0xFF }; + struct ifreq atreq; + struct atalk_netrange *nr; + struct sockaddr_at *sa; + struct net_device *dev; + struct atalk_iface *atif; + int ct; + int limit; + struct rtentry rtdef; + int add_route; + + if (copy_from_user(&atreq, arg, sizeof(atreq))) + return -EFAULT; + + dev = __dev_get_by_name(&init_net, atreq.ifr_name); + if (!dev) + return -ENODEV; + + sa = (struct sockaddr_at *)&atreq.ifr_addr; + atif = atalk_find_dev(dev); + + switch (cmd) { + case SIOCSIFADDR: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (sa->sat_family != AF_APPLETALK) + return -EINVAL; + if (dev->type != ARPHRD_ETHER && + dev->type != ARPHRD_LOOPBACK && + dev->type != ARPHRD_LOCALTLK && + dev->type != ARPHRD_PPP) + return -EPROTONOSUPPORT; + + nr = (struct atalk_netrange *)&sa->sat_zero[0]; + add_route = 1; + + /* + * if this is a point-to-point iface, and we already + * have an iface for this AppleTalk address, then we + * should not add a route + */ + if ((dev->flags & IFF_POINTOPOINT) && + atalk_find_interface(sa->sat_addr.s_net, + sa->sat_addr.s_node)) { + printk(KERN_DEBUG "AppleTalk: point-to-point " + "interface added with " + "existing address\n"); + add_route = 0; + } + + /* + * Phase 1 is fine on LocalTalk but we don't do + * EtherTalk phase 1. Anyone wanting to add it go ahead. + */ + if (dev->type == ARPHRD_ETHER && nr->nr_phase != 2) + return -EPROTONOSUPPORT; + if (sa->sat_addr.s_node == ATADDR_BCAST || + sa->sat_addr.s_node == 254) + return -EINVAL; + if (atif) { + /* Already setting address */ + if (atif->status & ATIF_PROBE) + return -EBUSY; + + atif->address.s_net = sa->sat_addr.s_net; + atif->address.s_node = sa->sat_addr.s_node; + atrtr_device_down(dev); /* Flush old routes */ + } else { + atif = atif_add_device(dev, &sa->sat_addr); + if (!atif) + return -ENOMEM; + } + atif->nets = *nr; + + /* + * Check if the chosen address is used. If so we + * error and atalkd will try another. + */ + + if (!(dev->flags & IFF_LOOPBACK) && + !(dev->flags & IFF_POINTOPOINT) && + atif_probe_device(atif) < 0) { + atif_drop_device(dev); + return -EADDRINUSE; + } + + /* Hey it worked - add the direct routes */ + sa = (struct sockaddr_at *)&rtdef.rt_gateway; + sa->sat_family = AF_APPLETALK; + sa->sat_addr.s_net = atif->address.s_net; + sa->sat_addr.s_node = atif->address.s_node; + sa = (struct sockaddr_at *)&rtdef.rt_dst; + rtdef.rt_flags = RTF_UP; + sa->sat_family = AF_APPLETALK; + sa->sat_addr.s_node = ATADDR_ANYNODE; + if (dev->flags & IFF_LOOPBACK || + dev->flags & IFF_POINTOPOINT) + rtdef.rt_flags |= RTF_HOST; + + /* Routerless initial state */ + if (nr->nr_firstnet == htons(0) && + nr->nr_lastnet == htons(0xFFFE)) { + sa->sat_addr.s_net = atif->address.s_net; + atrtr_create(&rtdef, dev); + atrtr_set_default(dev); + } else { + limit = ntohs(nr->nr_lastnet); + if (limit - ntohs(nr->nr_firstnet) > 4096) { + printk(KERN_WARNING "Too many routes/" + "iface.\n"); + return -EINVAL; + } + if (add_route) + for (ct = ntohs(nr->nr_firstnet); + ct <= limit; ct++) { + sa->sat_addr.s_net = htons(ct); + atrtr_create(&rtdef, dev); + } + } + dev_mc_add_global(dev, aarp_mcast); + return 0; + + case SIOCGIFADDR: + if (!atif) + return -EADDRNOTAVAIL; + + sa->sat_family = AF_APPLETALK; + sa->sat_addr = atif->address; + break; + + case SIOCGIFBRDADDR: + if (!atif) + return -EADDRNOTAVAIL; + + sa->sat_family = AF_APPLETALK; + sa->sat_addr.s_net = atif->address.s_net; + sa->sat_addr.s_node = ATADDR_BCAST; + break; + + case SIOCATALKDIFADDR: + case SIOCDIFADDR: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (sa->sat_family != AF_APPLETALK) + return -EINVAL; + atalk_dev_down(dev); + break; + + case SIOCSARP: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (sa->sat_family != AF_APPLETALK) + return -EINVAL; + /* + * for now, we only support proxy AARP on ELAP; + * we should be able to do it for LocalTalk, too. + */ + if (dev->type != ARPHRD_ETHER) + return -EPROTONOSUPPORT; + + /* + * atif points to the current interface on this network; + * we aren't concerned about its current status (at + * least for now), but it has all the settings about + * the network we're going to probe. Consequently, it + * must exist. + */ + if (!atif) + return -EADDRNOTAVAIL; + + nr = (struct atalk_netrange *)&(atif->nets); + /* + * Phase 1 is fine on Localtalk but we don't do + * Ethertalk phase 1. Anyone wanting to add it go ahead. + */ + if (dev->type == ARPHRD_ETHER && nr->nr_phase != 2) + return -EPROTONOSUPPORT; + + if (sa->sat_addr.s_node == ATADDR_BCAST || + sa->sat_addr.s_node == 254) + return -EINVAL; + + /* + * Check if the chosen address is used. If so we + * error and ATCP will try another. + */ + if (atif_proxy_probe_device(atif, &(sa->sat_addr)) < 0) + return -EADDRINUSE; + + /* + * We now have an address on the local network, and + * the AARP code will defend it for us until we take it + * down. We don't set up any routes right now, because + * ATCP will install them manually via SIOCADDRT. + */ + break; + + case SIOCDARP: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (sa->sat_family != AF_APPLETALK) + return -EINVAL; + if (!atif) + return -EADDRNOTAVAIL; + + /* give to aarp module to remove proxy entry */ + aarp_proxy_remove(atif->dev, &(sa->sat_addr)); + return 0; + } + + return copy_to_user(arg, &atreq, sizeof(atreq)) ? -EFAULT : 0; +} + +/* Routing ioctl() calls */ +static int atrtr_ioctl(unsigned int cmd, void __user *arg) +{ + struct rtentry rt; + + if (copy_from_user(&rt, arg, sizeof(rt))) + return -EFAULT; + + switch (cmd) { + case SIOCDELRT: + if (rt.rt_dst.sa_family != AF_APPLETALK) + return -EINVAL; + return atrtr_delete(&((struct sockaddr_at *) + &rt.rt_dst)->sat_addr); + + case SIOCADDRT: { + struct net_device *dev = NULL; + if (rt.rt_dev) { + char name[IFNAMSIZ]; + if (copy_from_user(name, rt.rt_dev, IFNAMSIZ-1)) + return -EFAULT; + name[IFNAMSIZ-1] = '\0'; + dev = __dev_get_by_name(&init_net, name); + if (!dev) + return -ENODEV; + } + return atrtr_create(&rt, dev); + } + } + return -EINVAL; +} + +/**************************************************************************\ +* * +* Handling for system calls applied via the various interfaces to an * +* AppleTalk socket object. * +* * +\**************************************************************************/ + +/* + * Checksum: This is 'optional'. It's quite likely also a good + * candidate for assembler hackery 8) + */ +static unsigned long atalk_sum_partial(const unsigned char *data, + int len, unsigned long sum) +{ + /* This ought to be unwrapped neatly. I'll trust gcc for now */ + while (len--) { + sum += *data++; + sum = rol16(sum, 1); + } + return sum; +} + +/* Checksum skb data -- similar to skb_checksum */ +static unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset, + int len, unsigned long sum) +{ + int start = skb_headlen(skb); + struct sk_buff *frag_iter; + int i, copy; + + /* checksum stuff in header space */ + if ( (copy = start - offset) > 0) { + if (copy > len) + copy = len; + sum = atalk_sum_partial(skb->data + offset, copy, sum); + if ( (len -= copy) == 0) + return sum; + + offset += copy; + } + + /* checksum stuff in frags */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + int end; + + WARN_ON(start > offset + len); + + end = start + skb_shinfo(skb)->frags[i].size; + if ((copy = end - offset) > 0) { + u8 *vaddr; + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + if (copy > len) + copy = len; + vaddr = kmap_skb_frag(frag); + sum = atalk_sum_partial(vaddr + frag->page_offset + + offset - start, copy, sum); + kunmap_skb_frag(vaddr); + + if (!(len -= copy)) + return sum; + offset += copy; + } + start = end; + } + + skb_walk_frags(skb, frag_iter) { + int end; + + WARN_ON(start > offset + len); + + end = start + frag_iter->len; + if ((copy = end - offset) > 0) { + if (copy > len) + copy = len; + sum = atalk_sum_skb(frag_iter, offset - start, + copy, sum); + if ((len -= copy) == 0) + return sum; + offset += copy; + } + start = end; + } + + BUG_ON(len > 0); + + return sum; +} + +static __be16 atalk_checksum(const struct sk_buff *skb, int len) +{ + unsigned long sum; + + /* skip header 4 bytes */ + sum = atalk_sum_skb(skb, 4, len-4, 0); + + /* Use 0xFFFF for 0. 0 itself means none */ + return sum ? htons((unsigned short)sum) : htons(0xFFFF); +} + +static struct proto ddp_proto = { + .name = "DDP", + .owner = THIS_MODULE, + .obj_size = sizeof(struct atalk_sock), +}; + +/* + * Create a socket. Initialise the socket, blank the addresses + * set the state. + */ +static int atalk_create(struct net *net, struct socket *sock, int protocol, + int kern) +{ + struct sock *sk; + int rc = -ESOCKTNOSUPPORT; + + if (!net_eq(net, &init_net)) + return -EAFNOSUPPORT; + + /* + * We permit SOCK_DGRAM and RAW is an extension. It is trivial to do + * and gives you the full ELAP frame. Should be handy for CAP 8) + */ + if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM) + goto out; + rc = -ENOMEM; + sk = sk_alloc(net, PF_APPLETALK, GFP_KERNEL, &ddp_proto); + if (!sk) + goto out; + rc = 0; + sock->ops = &atalk_dgram_ops; + sock_init_data(sock, sk); + + /* Checksums on by default */ + sock_set_flag(sk, SOCK_ZAPPED); +out: + return rc; +} + +/* Free a socket. No work needed */ +static int atalk_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + lock_kernel(); + if (sk) { + sock_orphan(sk); + sock->sk = NULL; + atalk_destroy_socket(sk); + } + unlock_kernel(); + return 0; +} + +/** + * atalk_pick_and_bind_port - Pick a source port when one is not given + * @sk - socket to insert into the tables + * @sat - address to search for + * + * Pick a source port when one is not given. If we can find a suitable free + * one, we insert the socket into the tables using it. + * + * This whole operation must be atomic. + */ +static int atalk_pick_and_bind_port(struct sock *sk, struct sockaddr_at *sat) +{ + int retval; + + write_lock_bh(&atalk_sockets_lock); + + for (sat->sat_port = ATPORT_RESERVED; + sat->sat_port < ATPORT_LAST; + sat->sat_port++) { + struct sock *s; + struct hlist_node *node; + + sk_for_each(s, node, &atalk_sockets) { + struct atalk_sock *at = at_sk(s); + + if (at->src_net == sat->sat_addr.s_net && + at->src_node == sat->sat_addr.s_node && + at->src_port == sat->sat_port) + goto try_next_port; + } + + /* Wheee, it's free, assign and insert. */ + __atalk_insert_socket(sk); + at_sk(sk)->src_port = sat->sat_port; + retval = 0; + goto out; + +try_next_port:; + } + + retval = -EBUSY; +out: + write_unlock_bh(&atalk_sockets_lock); + return retval; +} + +static int atalk_autobind(struct sock *sk) +{ + struct atalk_sock *at = at_sk(sk); + struct sockaddr_at sat; + struct atalk_addr *ap = atalk_find_primary(); + int n = -EADDRNOTAVAIL; + + if (!ap || ap->s_net == htons(ATADDR_ANYNET)) + goto out; + + at->src_net = sat.sat_addr.s_net = ap->s_net; + at->src_node = sat.sat_addr.s_node = ap->s_node; + + n = atalk_pick_and_bind_port(sk, &sat); + if (!n) + sock_reset_flag(sk, SOCK_ZAPPED); +out: + return n; +} + +/* Set the address 'our end' of the connection */ +static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) +{ + struct sockaddr_at *addr = (struct sockaddr_at *)uaddr; + struct sock *sk = sock->sk; + struct atalk_sock *at = at_sk(sk); + int err; + + if (!sock_flag(sk, SOCK_ZAPPED) || + addr_len != sizeof(struct sockaddr_at)) + return -EINVAL; + + if (addr->sat_family != AF_APPLETALK) + return -EAFNOSUPPORT; + + lock_kernel(); + if (addr->sat_addr.s_net == htons(ATADDR_ANYNET)) { + struct atalk_addr *ap = atalk_find_primary(); + + err = -EADDRNOTAVAIL; + if (!ap) + goto out; + + at->src_net = addr->sat_addr.s_net = ap->s_net; + at->src_node = addr->sat_addr.s_node= ap->s_node; + } else { + err = -EADDRNOTAVAIL; + if (!atalk_find_interface(addr->sat_addr.s_net, + addr->sat_addr.s_node)) + goto out; + + at->src_net = addr->sat_addr.s_net; + at->src_node = addr->sat_addr.s_node; + } + + if (addr->sat_port == ATADDR_ANYPORT) { + err = atalk_pick_and_bind_port(sk, addr); + + if (err < 0) + goto out; + } else { + at->src_port = addr->sat_port; + + err = -EADDRINUSE; + if (atalk_find_or_insert_socket(sk, addr)) + goto out; + } + + sock_reset_flag(sk, SOCK_ZAPPED); + err = 0; +out: + unlock_kernel(); + return err; +} + +/* Set the address we talk to */ +static int atalk_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags) +{ + struct sock *sk = sock->sk; + struct atalk_sock *at = at_sk(sk); + struct sockaddr_at *addr; + int err; + + sk->sk_state = TCP_CLOSE; + sock->state = SS_UNCONNECTED; + + if (addr_len != sizeof(*addr)) + return -EINVAL; + + addr = (struct sockaddr_at *)uaddr; + + if (addr->sat_family != AF_APPLETALK) + return -EAFNOSUPPORT; + + if (addr->sat_addr.s_node == ATADDR_BCAST && + !sock_flag(sk, SOCK_BROADCAST)) { +#if 1 + printk(KERN_WARNING "%s is broken and did not set " + "SO_BROADCAST. It will break when 2.2 is " + "released.\n", + current->comm); +#else + return -EACCES; +#endif + } + + lock_kernel(); + err = -EBUSY; + if (sock_flag(sk, SOCK_ZAPPED)) + if (atalk_autobind(sk) < 0) + goto out; + + err = -ENETUNREACH; + if (!atrtr_get_dev(&addr->sat_addr)) + goto out; + + at->dest_port = addr->sat_port; + at->dest_net = addr->sat_addr.s_net; + at->dest_node = addr->sat_addr.s_node; + + sock->state = SS_CONNECTED; + sk->sk_state = TCP_ESTABLISHED; + err = 0; +out: + unlock_kernel(); + return err; +} + +/* + * Find the name of an AppleTalk socket. Just copy the right + * fields into the sockaddr. + */ +static int atalk_getname(struct socket *sock, struct sockaddr *uaddr, + int *uaddr_len, int peer) +{ + struct sockaddr_at sat; + struct sock *sk = sock->sk; + struct atalk_sock *at = at_sk(sk); + int err; + + lock_kernel(); + err = -ENOBUFS; + if (sock_flag(sk, SOCK_ZAPPED)) + if (atalk_autobind(sk) < 0) + goto out; + + *uaddr_len = sizeof(struct sockaddr_at); + memset(&sat.sat_zero, 0, sizeof(sat.sat_zero)); + + if (peer) { + err = -ENOTCONN; + if (sk->sk_state != TCP_ESTABLISHED) + goto out; + + sat.sat_addr.s_net = at->dest_net; + sat.sat_addr.s_node = at->dest_node; + sat.sat_port = at->dest_port; + } else { + sat.sat_addr.s_net = at->src_net; + sat.sat_addr.s_node = at->src_node; + sat.sat_port = at->src_port; + } + + err = 0; + sat.sat_family = AF_APPLETALK; + memcpy(uaddr, &sat, sizeof(sat)); + +out: + unlock_kernel(); + return err; +} + +static unsigned int atalk_poll(struct file *file, struct socket *sock, + poll_table *wait) +{ + int err; + lock_kernel(); + err = datagram_poll(file, sock, wait); + unlock_kernel(); + return err; +} + +#if defined(CONFIG_IPDDP) || defined(CONFIG_IPDDP_MODULE) +static __inline__ int is_ip_over_ddp(struct sk_buff *skb) +{ + return skb->data[12] == 22; +} + +static int handle_ip_over_ddp(struct sk_buff *skb) +{ + struct net_device *dev = __dev_get_by_name(&init_net, "ipddp0"); + struct net_device_stats *stats; + + /* This needs to be able to handle ipddp"N" devices */ + if (!dev) { + kfree_skb(skb); + return NET_RX_DROP; + } + + skb->protocol = htons(ETH_P_IP); + skb_pull(skb, 13); + skb->dev = dev; + skb_reset_transport_header(skb); + + stats = netdev_priv(dev); + stats->rx_packets++; + stats->rx_bytes += skb->len + 13; + return netif_rx(skb); /* Send the SKB up to a higher place. */ +} +#else +/* make it easy for gcc to optimize this test out, i.e. kill the code */ +#define is_ip_over_ddp(skb) 0 +#define handle_ip_over_ddp(skb) 0 +#endif + +static int atalk_route_packet(struct sk_buff *skb, struct net_device *dev, + struct ddpehdr *ddp, __u16 len_hops, int origlen) +{ + struct atalk_route *rt; + struct atalk_addr ta; + + /* + * Don't route multicast, etc., packets, or packets sent to "this + * network" + */ + if (skb->pkt_type != PACKET_HOST || !ddp->deh_dnet) { + /* + * FIXME: + * + * Can it ever happen that a packet is from a PPP iface and + * needs to be broadcast onto the default network? + */ + if (dev->type == ARPHRD_PPP) + printk(KERN_DEBUG "AppleTalk: didn't forward broadcast " + "packet received from PPP iface\n"); + goto free_it; + } + + ta.s_net = ddp->deh_dnet; + ta.s_node = ddp->deh_dnode; + + /* Route the packet */ + rt = atrtr_find(&ta); + /* increment hops count */ + len_hops += 1 << 10; + if (!rt || !(len_hops & (15 << 10))) + goto free_it; + + /* FIXME: use skb->cb to be able to use shared skbs */ + + /* + * Route goes through another gateway, so set the target to the + * gateway instead. + */ + + if (rt->flags & RTF_GATEWAY) { + ta.s_net = rt->gateway.s_net; + ta.s_node = rt->gateway.s_node; + } + + /* Fix up skb->len field */ + skb_trim(skb, min_t(unsigned int, origlen, + (rt->dev->hard_header_len + + ddp_dl->header_length + (len_hops & 1023)))); + + /* FIXME: use skb->cb to be able to use shared skbs */ + ddp->deh_len_hops = htons(len_hops); + + /* + * Send the buffer onwards + * + * Now we must always be careful. If it's come from LocalTalk to + * EtherTalk it might not fit + * + * Order matters here: If a packet has to be copied to make a new + * headroom (rare hopefully) then it won't need unsharing. + * + * Note. ddp-> becomes invalid at the realloc. + */ + if (skb_headroom(skb) < 22) { + /* 22 bytes - 12 ether, 2 len, 3 802.2 5 snap */ + struct sk_buff *nskb = skb_realloc_headroom(skb, 32); + kfree_skb(skb); + skb = nskb; + } else + skb = skb_unshare(skb, GFP_ATOMIC); + + /* + * If the buffer didn't vanish into the lack of space bitbucket we can + * send it. + */ + if (skb == NULL) + goto drop; + + if (aarp_send_ddp(rt->dev, skb, &ta, NULL) == NET_XMIT_DROP) + return NET_RX_DROP; + return NET_RX_SUCCESS; +free_it: + kfree_skb(skb); +drop: + return NET_RX_DROP; +} + +/** + * atalk_rcv - Receive a packet (in skb) from device dev + * @skb - packet received + * @dev - network device where the packet comes from + * @pt - packet type + * + * Receive a packet (in skb) from device dev. This has come from the SNAP + * decoder, and on entry skb->transport_header is the DDP header, skb->len + * is the DDP header, skb->len is the DDP length. The physical headers + * have been extracted. PPP should probably pass frames marked as for this + * layer. [ie ARPHRD_ETHERTALK] + */ +static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct ddpehdr *ddp; + struct sock *sock; + struct atalk_iface *atif; + struct sockaddr_at tosat; + int origlen; + __u16 len_hops; + + if (!net_eq(dev_net(dev), &init_net)) + goto drop; + + /* Don't mangle buffer if shared */ + if (!(skb = skb_share_check(skb, GFP_ATOMIC))) + goto out; + + /* Size check and make sure header is contiguous */ + if (!pskb_may_pull(skb, sizeof(*ddp))) + goto drop; + + ddp = ddp_hdr(skb); + + len_hops = ntohs(ddp->deh_len_hops); + + /* Trim buffer in case of stray trailing data */ + origlen = skb->len; + skb_trim(skb, min_t(unsigned int, skb->len, len_hops & 1023)); + + /* + * Size check to see if ddp->deh_len was crap + * (Otherwise we'll detonate most spectacularly + * in the middle of atalk_checksum() or recvmsg()). + */ + if (skb->len < sizeof(*ddp) || skb->len < (len_hops & 1023)) { + pr_debug("AppleTalk: dropping corrupted frame (deh_len=%u, " + "skb->len=%u)\n", len_hops & 1023, skb->len); + goto drop; + } + + /* + * Any checksums. Note we don't do htons() on this == is assumed to be + * valid for net byte orders all over the networking code... + */ + if (ddp->deh_sum && + atalk_checksum(skb, len_hops & 1023) != ddp->deh_sum) + /* Not a valid AppleTalk frame - dustbin time */ + goto drop; + + /* Check the packet is aimed at us */ + if (!ddp->deh_dnet) /* Net 0 is 'this network' */ + atif = atalk_find_anynet(ddp->deh_dnode, dev); + else + atif = atalk_find_interface(ddp->deh_dnet, ddp->deh_dnode); + + if (!atif) { + /* Not ours, so we route the packet via the correct + * AppleTalk iface + */ + return atalk_route_packet(skb, dev, ddp, len_hops, origlen); + } + + /* if IP over DDP is not selected this code will be optimized out */ + if (is_ip_over_ddp(skb)) + return handle_ip_over_ddp(skb); + /* + * Which socket - atalk_search_socket() looks for a *full match* + * of the tuple. + */ + tosat.sat_addr.s_net = ddp->deh_dnet; + tosat.sat_addr.s_node = ddp->deh_dnode; + tosat.sat_port = ddp->deh_dport; + + sock = atalk_search_socket(&tosat, atif); + if (!sock) /* But not one of our sockets */ + goto drop; + + /* Queue packet (standard) */ + skb->sk = sock; + + if (sock_queue_rcv_skb(sock, skb) < 0) + goto drop; + + return NET_RX_SUCCESS; + +drop: + kfree_skb(skb); +out: + return NET_RX_DROP; + +} + +/* + * Receive a LocalTalk frame. We make some demands on the caller here. + * Caller must provide enough headroom on the packet to pull the short + * header and append a long one. + */ +static int ltalk_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + if (!net_eq(dev_net(dev), &init_net)) + goto freeit; + + /* Expand any short form frames */ + if (skb_mac_header(skb)[2] == 1) { + struct ddpehdr *ddp; + /* Find our address */ + struct atalk_addr *ap = atalk_find_dev_addr(dev); + + if (!ap || skb->len < sizeof(__be16) || skb->len > 1023) + goto freeit; + + /* Don't mangle buffer if shared */ + if (!(skb = skb_share_check(skb, GFP_ATOMIC))) + return 0; + + /* + * The push leaves us with a ddephdr not an shdr, and + * handily the port bytes in the right place preset. + */ + ddp = (struct ddpehdr *) skb_push(skb, sizeof(*ddp) - 4); + + /* Now fill in the long header */ + + /* + * These two first. The mac overlays the new source/dest + * network information so we MUST copy these before + * we write the network numbers ! + */ + + ddp->deh_dnode = skb_mac_header(skb)[0]; /* From physical header */ + ddp->deh_snode = skb_mac_header(skb)[1]; /* From physical header */ + + ddp->deh_dnet = ap->s_net; /* Network number */ + ddp->deh_snet = ap->s_net; + ddp->deh_sum = 0; /* No checksum */ + /* + * Not sure about this bit... + */ + /* Non routable, so force a drop if we slip up later */ + ddp->deh_len_hops = htons(skb->len + (DDP_MAXHOPS << 10)); + } + skb_reset_transport_header(skb); + + return atalk_rcv(skb, dev, pt, orig_dev); +freeit: + kfree_skb(skb); + return 0; +} + +static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, + size_t len) +{ + struct sock *sk = sock->sk; + struct atalk_sock *at = at_sk(sk); + struct sockaddr_at *usat = (struct sockaddr_at *)msg->msg_name; + int flags = msg->msg_flags; + int loopback = 0; + struct sockaddr_at local_satalk, gsat; + struct sk_buff *skb; + struct net_device *dev; + struct ddpehdr *ddp; + int size; + struct atalk_route *rt; + int err; + + if (flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT)) + return -EINVAL; + + if (len > DDP_MAXSZ) + return -EMSGSIZE; + + lock_kernel(); + if (usat) { + err = -EBUSY; + if (sock_flag(sk, SOCK_ZAPPED)) + if (atalk_autobind(sk) < 0) + goto out; + + err = -EINVAL; + if (msg->msg_namelen < sizeof(*usat) || + usat->sat_family != AF_APPLETALK) + goto out; + + err = -EPERM; + /* netatalk didn't implement this check */ + if (usat->sat_addr.s_node == ATADDR_BCAST && + !sock_flag(sk, SOCK_BROADCAST)) { + goto out; + } + } else { + err = -ENOTCONN; + if (sk->sk_state != TCP_ESTABLISHED) + goto out; + usat = &local_satalk; + usat->sat_family = AF_APPLETALK; + usat->sat_port = at->dest_port; + usat->sat_addr.s_node = at->dest_node; + usat->sat_addr.s_net = at->dest_net; + } + + /* Build a packet */ + SOCK_DEBUG(sk, "SK %p: Got address.\n", sk); + + /* For headers */ + size = sizeof(struct ddpehdr) + len + ddp_dl->header_length; + + if (usat->sat_addr.s_net || usat->sat_addr.s_node == ATADDR_ANYNODE) { + rt = atrtr_find(&usat->sat_addr); + } else { + struct atalk_addr at_hint; + + at_hint.s_node = 0; + at_hint.s_net = at->src_net; + + rt = atrtr_find(&at_hint); + } + err = ENETUNREACH; + if (!rt) + goto out; + + dev = rt->dev; + + SOCK_DEBUG(sk, "SK %p: Size needed %d, device %s\n", + sk, size, dev->name); + + size += dev->hard_header_len; + skb = sock_alloc_send_skb(sk, size, (flags & MSG_DONTWAIT), &err); + if (!skb) + goto out; + + skb->sk = sk; + skb_reserve(skb, ddp_dl->header_length); + skb_reserve(skb, dev->hard_header_len); + skb->dev = dev; + + SOCK_DEBUG(sk, "SK %p: Begin build.\n", sk); + + ddp = (struct ddpehdr *)skb_put(skb, sizeof(struct ddpehdr)); + ddp->deh_len_hops = htons(len + sizeof(*ddp)); + ddp->deh_dnet = usat->sat_addr.s_net; + ddp->deh_snet = at->src_net; + ddp->deh_dnode = usat->sat_addr.s_node; + ddp->deh_snode = at->src_node; + ddp->deh_dport = usat->sat_port; + ddp->deh_sport = at->src_port; + + SOCK_DEBUG(sk, "SK %p: Copy user data (%Zd bytes).\n", sk, len); + + err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); + if (err) { + kfree_skb(skb); + err = -EFAULT; + goto out; + } + + if (sk->sk_no_check == 1) + ddp->deh_sum = 0; + else + ddp->deh_sum = atalk_checksum(skb, len + sizeof(*ddp)); + + /* + * Loopback broadcast packets to non gateway targets (ie routes + * to group we are in) + */ + if (ddp->deh_dnode == ATADDR_BCAST && + !(rt->flags & RTF_GATEWAY) && !(dev->flags & IFF_LOOPBACK)) { + struct sk_buff *skb2 = skb_copy(skb, GFP_KERNEL); + + if (skb2) { + loopback = 1; + SOCK_DEBUG(sk, "SK %p: send out(copy).\n", sk); + /* + * If it fails it is queued/sent above in the aarp queue + */ + aarp_send_ddp(dev, skb2, &usat->sat_addr, NULL); + } + } + + if (dev->flags & IFF_LOOPBACK || loopback) { + SOCK_DEBUG(sk, "SK %p: Loop back.\n", sk); + /* loop back */ + skb_orphan(skb); + if (ddp->deh_dnode == ATADDR_BCAST) { + struct atalk_addr at_lo; + + at_lo.s_node = 0; + at_lo.s_net = 0; + + rt = atrtr_find(&at_lo); + if (!rt) { + kfree_skb(skb); + err = -ENETUNREACH; + goto out; + } + dev = rt->dev; + skb->dev = dev; + } + ddp_dl->request(ddp_dl, skb, dev->dev_addr); + } else { + SOCK_DEBUG(sk, "SK %p: send out.\n", sk); + if (rt->flags & RTF_GATEWAY) { + gsat.sat_addr = rt->gateway; + usat = &gsat; + } + + /* + * If it fails it is queued/sent above in the aarp queue + */ + aarp_send_ddp(dev, skb, &usat->sat_addr, NULL); + } + SOCK_DEBUG(sk, "SK %p: Done write (%Zd).\n", sk, len); + +out: + unlock_kernel(); + return err ? : len; +} + +static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, + size_t size, int flags) +{ + struct sock *sk = sock->sk; + struct sockaddr_at *sat = (struct sockaddr_at *)msg->msg_name; + struct ddpehdr *ddp; + int copied = 0; + int offset = 0; + int err = 0; + struct sk_buff *skb; + + lock_kernel(); + skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, + flags & MSG_DONTWAIT, &err); + if (!skb) + goto out; + + /* FIXME: use skb->cb to be able to use shared skbs */ + ddp = ddp_hdr(skb); + copied = ntohs(ddp->deh_len_hops) & 1023; + + if (sk->sk_type != SOCK_RAW) { + offset = sizeof(*ddp); + copied -= offset; + } + + if (copied > size) { + copied = size; + msg->msg_flags |= MSG_TRUNC; + } + err = skb_copy_datagram_iovec(skb, offset, msg->msg_iov, copied); + + if (!err) { + if (sat) { + sat->sat_family = AF_APPLETALK; + sat->sat_port = ddp->deh_sport; + sat->sat_addr.s_node = ddp->deh_snode; + sat->sat_addr.s_net = ddp->deh_snet; + } + msg->msg_namelen = sizeof(*sat); + } + + skb_free_datagram(sk, skb); /* Free the datagram. */ + +out: + unlock_kernel(); + return err ? : copied; +} + + +/* + * AppleTalk ioctl calls. + */ +static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + int rc = -ENOIOCTLCMD; + struct sock *sk = sock->sk; + void __user *argp = (void __user *)arg; + + switch (cmd) { + /* Protocol layer */ + case TIOCOUTQ: { + long amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); + + if (amount < 0) + amount = 0; + rc = put_user(amount, (int __user *)argp); + break; + } + case TIOCINQ: { + /* + * These two are safe on a single CPU system as only + * user tasks fiddle here + */ + struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); + long amount = 0; + + if (skb) + amount = skb->len - sizeof(struct ddpehdr); + rc = put_user(amount, (int __user *)argp); + break; + } + case SIOCGSTAMP: + rc = sock_get_timestamp(sk, argp); + break; + case SIOCGSTAMPNS: + rc = sock_get_timestampns(sk, argp); + break; + /* Routing */ + case SIOCADDRT: + case SIOCDELRT: + rc = -EPERM; + if (capable(CAP_NET_ADMIN)) + rc = atrtr_ioctl(cmd, argp); + break; + /* Interface */ + case SIOCGIFADDR: + case SIOCSIFADDR: + case SIOCGIFBRDADDR: + case SIOCATALKDIFADDR: + case SIOCDIFADDR: + case SIOCSARP: /* proxy AARP */ + case SIOCDARP: /* proxy AARP */ + rtnl_lock(); + rc = atif_ioctl(cmd, argp); + rtnl_unlock(); + break; + } + + return rc; +} + + +#ifdef CONFIG_COMPAT +static int atalk_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + /* + * SIOCATALKDIFADDR is a SIOCPROTOPRIVATE ioctl number, so we + * cannot handle it in common code. The data we access if ifreq + * here is compatible, so we can simply call the native + * handler. + */ + if (cmd == SIOCATALKDIFADDR) + return atalk_ioctl(sock, cmd, (unsigned long)compat_ptr(arg)); + + return -ENOIOCTLCMD; +} +#endif + + +static const struct net_proto_family atalk_family_ops = { + .family = PF_APPLETALK, + .create = atalk_create, + .owner = THIS_MODULE, +}; + +static const struct proto_ops atalk_dgram_ops = { + .family = PF_APPLETALK, + .owner = THIS_MODULE, + .release = atalk_release, + .bind = atalk_bind, + .connect = atalk_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = atalk_getname, + .poll = atalk_poll, + .ioctl = atalk_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = atalk_compat_ioctl, +#endif + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = atalk_sendmsg, + .recvmsg = atalk_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +static struct notifier_block ddp_notifier = { + .notifier_call = ddp_device_event, +}; + +static struct packet_type ltalk_packet_type __read_mostly = { + .type = cpu_to_be16(ETH_P_LOCALTALK), + .func = ltalk_rcv, +}; + +static struct packet_type ppptalk_packet_type __read_mostly = { + .type = cpu_to_be16(ETH_P_PPPTALK), + .func = atalk_rcv, +}; + +static unsigned char ddp_snap_id[] = { 0x08, 0x00, 0x07, 0x80, 0x9B }; + +/* Export symbols for use by drivers when AppleTalk is a module */ +EXPORT_SYMBOL(atrtr_get_dev); +EXPORT_SYMBOL(atalk_find_dev_addr); + +static const char atalk_err_snap[] __initconst = + KERN_CRIT "Unable to register DDP with SNAP.\n"; + +/* Called by proto.c on kernel start up */ +static int __init atalk_init(void) +{ + int rc = proto_register(&ddp_proto, 0); + + if (rc != 0) + goto out; + + (void)sock_register(&atalk_family_ops); + ddp_dl = register_snap_client(ddp_snap_id, atalk_rcv); + if (!ddp_dl) + printk(atalk_err_snap); + + dev_add_pack(<alk_packet_type); + dev_add_pack(&ppptalk_packet_type); + + register_netdevice_notifier(&ddp_notifier); + aarp_proto_init(); + atalk_proc_init(); + atalk_register_sysctl(); +out: + return rc; +} +module_init(atalk_init); + +/* + * No explicit module reference count manipulation is needed in the + * protocol. Socket layer sets module reference count for us + * and interfaces reference counting is done + * by the network device layer. + * + * Ergo, before the AppleTalk module can be removed, all AppleTalk + * sockets be closed from user space. + */ +static void __exit atalk_exit(void) +{ +#ifdef CONFIG_SYSCTL + atalk_unregister_sysctl(); +#endif /* CONFIG_SYSCTL */ + atalk_proc_exit(); + aarp_cleanup_module(); /* General aarp clean-up. */ + unregister_netdevice_notifier(&ddp_notifier); + dev_remove_pack(<alk_packet_type); + dev_remove_pack(&ppptalk_packet_type); + unregister_snap_client(ddp_dl); + sock_unregister(PF_APPLETALK); + proto_unregister(&ddp_proto); +} +module_exit(atalk_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alan Cox "); +MODULE_DESCRIPTION("AppleTalk 0.20\n"); +MODULE_ALIAS_NETPROTO(PF_APPLETALK); diff --git a/net/appletalk/dev.c b/net/appletalk/dev.c new file mode 100644 index 000000000000..6c8016f61866 --- /dev/null +++ b/net/appletalk/dev.c @@ -0,0 +1,44 @@ +/* + * Moved here from drivers/net/net_init.c, which is: + * Written 1993,1994,1995 by Donald Becker. + */ + +#include +#include +#include +#include +#include + +static void ltalk_setup(struct net_device *dev) +{ + /* Fill in the fields of the device structure with localtalk-generic values. */ + + dev->type = ARPHRD_LOCALTLK; + dev->hard_header_len = LTALK_HLEN; + dev->mtu = LTALK_MTU; + dev->addr_len = LTALK_ALEN; + dev->tx_queue_len = 10; + + dev->broadcast[0] = 0xFF; + + dev->flags = IFF_BROADCAST|IFF_MULTICAST|IFF_NOARP; +} + +/** + * alloc_ltalkdev - Allocates and sets up an localtalk device + * @sizeof_priv: Size of additional driver-private structure to be allocated + * for this localtalk device + * + * Fill in the fields of the device structure with localtalk-generic + * values. Basically does everything except registering the device. + * + * Constructs a new net device, complete with a private data area of + * size @sizeof_priv. A 32-byte (not bit) alignment is enforced for + * this private data area. + */ + +struct net_device *alloc_ltalkdev(int sizeof_priv) +{ + return alloc_netdev(sizeof_priv, "lt%d", ltalk_setup); +} +EXPORT_SYMBOL(alloc_ltalkdev); diff --git a/net/appletalk/sysctl_net_atalk.c b/net/appletalk/sysctl_net_atalk.c new file mode 100644 index 000000000000..04e9c0da7aa9 --- /dev/null +++ b/net/appletalk/sysctl_net_atalk.c @@ -0,0 +1,61 @@ +/* + * sysctl_net_atalk.c: sysctl interface to net AppleTalk subsystem. + * + * Begun April 1, 1996, Mike Shaver. + * Added /proc/sys/net/atalk directory entry (empty =) ). [MS] + * Dynamic registration, added aarp entries. (5/30/97 Chris Horn) + */ + +#include +#include +#include + +static struct ctl_table atalk_table[] = { + { + .procname = "aarp-expiry-time", + .data = &sysctl_aarp_expiry_time, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_jiffies, + }, + { + .procname = "aarp-tick-time", + .data = &sysctl_aarp_tick_time, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_jiffies, + }, + { + .procname = "aarp-retransmit-limit", + .data = &sysctl_aarp_retransmit_limit, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { + .procname = "aarp-resolve-time", + .data = &sysctl_aarp_resolve_time, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_jiffies, + }, + { }, +}; + +static struct ctl_path atalk_path[] = { + { .procname = "net", }, + { .procname = "appletalk", }, + { } +}; + +static struct ctl_table_header *atalk_table_header; + +void atalk_register_sysctl(void) +{ + atalk_table_header = register_sysctl_paths(atalk_path, atalk_table); +} + +void atalk_unregister_sysctl(void) +{ + unregister_sysctl_table(atalk_table_header); +} diff --git a/net/socket.c b/net/socket.c index 26f7bcf36810..ac2219f90d5d 100644 --- a/net/socket.c +++ b/net/socket.c @@ -103,6 +103,7 @@ #include #include #include +#include static int sock_no_open(struct inode *irrelevant, struct file *dontcare); static ssize_t sock_aio_read(struct kiocb *iocb, const struct iovec *iov, -- cgit v1.2.3 From 5b4704419cbd0b7597a91c19f9e8e8b17c1af071 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 31 Jan 2011 16:10:03 -0800 Subject: ipv4: Remember FIB alias list head and table in lookup results. This will be used later to implement fib_select_default() in a completely generic manner, instead of the current situation where the default route is re-looked up in the TRIE/HASH table and then the available aliases are analyzed. Signed-off-by: David S. Miller --- include/net/ip_fib.h | 3 +++ net/ipv4/fib_hash.c | 2 +- net/ipv4/fib_lookup.h | 2 +- net/ipv4/fib_semantics.c | 7 +++++-- net/ipv4/fib_trie.c | 8 ++++---- 5 files changed, 14 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 2c0508a6e07c..f5199b08ba53 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -96,12 +96,15 @@ struct fib_info { struct fib_rule; #endif +struct fib_table; struct fib_result { unsigned char prefixlen; unsigned char nh_sel; unsigned char type; unsigned char scope; struct fib_info *fi; + struct fib_table *table; + struct list_head *fa_head; #ifdef CONFIG_IP_MULTIPLE_TABLES struct fib_rule *r; #endif diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c index b3acb0417b21..0a88866ad1e5 100644 --- a/net/ipv4/fib_hash.c +++ b/net/ipv4/fib_hash.c @@ -288,7 +288,7 @@ int fib_table_lookup(struct fib_table *tb, if (f->fn_key != k) continue; - err = fib_semantic_match(&f->fn_alias, + err = fib_semantic_match(tb, &f->fn_alias, flp, res, fz->fz_order, fib_flags); if (err <= 0) diff --git a/net/ipv4/fib_lookup.h b/net/ipv4/fib_lookup.h index c079cc0ec651..d5c40d8f6632 100644 --- a/net/ipv4/fib_lookup.h +++ b/net/ipv4/fib_lookup.h @@ -25,7 +25,7 @@ static inline void fib_alias_accessed(struct fib_alias *fa) } /* Exported by fib_semantics.c */ -extern int fib_semantic_match(struct list_head *head, +extern int fib_semantic_match(struct fib_table *tb, struct list_head *head, const struct flowi *flp, struct fib_result *res, int prefixlen, int fib_flags); extern void fib_release_info(struct fib_info *); diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 48e93a560077..1bf6fb906cfc 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -889,8 +889,9 @@ failure: } /* Note! fib_semantic_match intentionally uses RCU list functions. */ -int fib_semantic_match(struct list_head *head, const struct flowi *flp, - struct fib_result *res, int prefixlen, int fib_flags) +int fib_semantic_match(struct fib_table *tb, struct list_head *head, + const struct flowi *flp, struct fib_result *res, + int prefixlen, int fib_flags) { struct fib_alias *fa; int nh_sel = 0; @@ -954,6 +955,8 @@ out_fill_res: res->type = fa->fa_type; res->scope = fa->fa_scope; res->fi = fa->fa_info; + res->table = tb; + res->fa_head = head; if (!(fib_flags & FIB_LOOKUP_NOREF)) atomic_inc(&res->fi->fib_clntref); return 0; diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 0f280348e0fd..8cee5c8848ed 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1340,7 +1340,7 @@ err: } /* should be called with rcu_read_lock */ -static int check_leaf(struct trie *t, struct leaf *l, +static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l, t_key key, const struct flowi *flp, struct fib_result *res, int fib_flags) { @@ -1356,7 +1356,7 @@ static int check_leaf(struct trie *t, struct leaf *l, if (l->key != (key & ntohl(mask))) continue; - err = fib_semantic_match(&li->falh, flp, res, plen, fib_flags); + err = fib_semantic_match(tb, &li->falh, flp, res, plen, fib_flags); #ifdef CONFIG_IP_FIB_TRIE_STATS if (err <= 0) @@ -1398,7 +1398,7 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi *flp, /* Just a leaf? */ if (IS_LEAF(n)) { - ret = check_leaf(t, (struct leaf *)n, key, flp, res, fib_flags); + ret = check_leaf(tb, t, (struct leaf *)n, key, flp, res, fib_flags); goto found; } @@ -1423,7 +1423,7 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi *flp, } if (IS_LEAF(n)) { - ret = check_leaf(t, (struct leaf *)n, key, flp, res, fib_flags); + ret = check_leaf(tb, t, (struct leaf *)n, key, flp, res, fib_flags); if (ret > 0) goto backtrace; goto found; -- cgit v1.2.3 From 0c838ff1ade71162775afffd9e5c6478a60bdca6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 31 Jan 2011 16:16:50 -0800 Subject: ipv4: Consolidate all default route selection implementations. Both fib_trie and fib_hash have a local implementation of fib_table_select_default(). This is completely unnecessary code duplication. Since we now remember the fib_table and the head of the fib alias list of the default route, we can implement one single generic version of this routine. Looking at the fib_hash implementation you may get the impression that it's possible for there to be multiple top-level routes in the table for the default route. The truth is, it isn't, the insert code will only allow one entry to exist in the zero prefix hash table, because all keys evaluate to zero and all keys in a hash table must be unique. Signed-off-by: David S. Miller --- include/net/ip_fib.h | 6 +--- net/ipv4/fib_frontend.c | 15 ---------- net/ipv4/fib_hash.c | 72 ---------------------------------------------- net/ipv4/fib_semantics.c | 56 ++++++++++++++++++++++++++++++++++++ net/ipv4/fib_trie.c | 74 ------------------------------------------------ net/ipv4/route.c | 2 +- 6 files changed, 58 insertions(+), 167 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index f5199b08ba53..819d61ca25cb 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -158,9 +158,6 @@ extern int fib_table_delete(struct fib_table *, struct fib_config *); extern int fib_table_dump(struct fib_table *table, struct sk_buff *skb, struct netlink_callback *cb); extern int fib_table_flush(struct fib_table *table); -extern void fib_table_select_default(struct fib_table *table, - const struct flowi *flp, - struct fib_result *res); extern void fib_free_table(struct fib_table *tb); @@ -221,8 +218,7 @@ extern void ip_fib_init(void); extern int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif, struct net_device *dev, __be32 *spec_dst, u32 *itag, u32 mark); -extern void fib_select_default(struct net *net, const struct flowi *flp, - struct fib_result *res); +extern void fib_select_default(struct fib_result *res); /* Exported by fib_semantics.c */ extern int ip_fib_check_default(__be32 gw, struct net_device *dev); diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 1d2cdd43a878..930768ba49af 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -114,21 +114,6 @@ struct fib_table *fib_get_table(struct net *net, u32 id) } #endif /* CONFIG_IP_MULTIPLE_TABLES */ -void fib_select_default(struct net *net, - const struct flowi *flp, struct fib_result *res) -{ - struct fib_table *tb; - int table = RT_TABLE_MAIN; -#ifdef CONFIG_IP_MULTIPLE_TABLES - if (res->r == NULL || res->r->action != FR_ACT_TO_TBL) - return; - table = res->r->table; -#endif - tb = fib_get_table(net, table); - if (FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) - fib_table_select_default(tb, flp, res); -} - static void fib_flush(struct net *net) { int flushed = 0; diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c index 0a88866ad1e5..fadb6024de27 100644 --- a/net/ipv4/fib_hash.c +++ b/net/ipv4/fib_hash.c @@ -302,78 +302,6 @@ out: return err; } -void fib_table_select_default(struct fib_table *tb, - const struct flowi *flp, struct fib_result *res) -{ - int order, last_idx; - struct hlist_node *node; - struct fib_node *f; - struct fib_info *fi = NULL; - struct fib_info *last_resort; - struct fn_hash *t = (struct fn_hash *)tb->tb_data; - struct fn_zone *fz = t->fn_zones[0]; - struct hlist_head *head; - - if (fz == NULL) - return; - - last_idx = -1; - last_resort = NULL; - order = -1; - - rcu_read_lock(); - head = rcu_dereference(fz->fz_hash); - hlist_for_each_entry_rcu(f, node, head, fn_hash) { - struct fib_alias *fa; - - list_for_each_entry_rcu(fa, &f->fn_alias, fa_list) { - struct fib_info *next_fi = fa->fa_info; - - if (fa->fa_scope != res->scope || - fa->fa_type != RTN_UNICAST) - continue; - - if (next_fi->fib_priority > res->fi->fib_priority) - break; - if (!next_fi->fib_nh[0].nh_gw || - next_fi->fib_nh[0].nh_scope != RT_SCOPE_LINK) - continue; - - fib_alias_accessed(fa); - - if (fi == NULL) { - if (next_fi != res->fi) - break; - } else if (!fib_detect_death(fi, order, &last_resort, - &last_idx, tb->tb_default)) { - fib_result_assign(res, fi); - tb->tb_default = order; - goto out; - } - fi = next_fi; - order++; - } - } - - if (order <= 0 || fi == NULL) { - tb->tb_default = -1; - goto out; - } - - if (!fib_detect_death(fi, order, &last_resort, &last_idx, - tb->tb_default)) { - fib_result_assign(res, fi); - tb->tb_default = order; - goto out; - } - - if (last_idx >= 0) - fib_result_assign(res, last_resort); - tb->tb_default = last_idx; -out: - rcu_read_unlock(); -} - /* Insert node F to FZ. */ static inline void fib_insert_node(struct fn_zone *fz, struct fib_node *f) { diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 1bf6fb906cfc..b15857d2b77e 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -1136,6 +1136,62 @@ int fib_sync_down_dev(struct net_device *dev, int force) return ret; } +/* Must be invoked inside of an RCU protected region. */ +void fib_select_default(struct fib_result *res) +{ + struct fib_info *fi = NULL, *last_resort = NULL; + struct list_head *fa_head = res->fa_head; + struct fib_table *tb = res->table; + int order = -1, last_idx = -1; + struct fib_alias *fa; + + list_for_each_entry_rcu(fa, fa_head, fa_list) { + struct fib_info *next_fi = fa->fa_info; + + if (fa->fa_scope != res->scope || + fa->fa_type != RTN_UNICAST) + continue; + + if (next_fi->fib_priority > res->fi->fib_priority) + break; + if (!next_fi->fib_nh[0].nh_gw || + next_fi->fib_nh[0].nh_scope != RT_SCOPE_LINK) + continue; + + fib_alias_accessed(fa); + + if (fi == NULL) { + if (next_fi != res->fi) + break; + } else if (!fib_detect_death(fi, order, &last_resort, + &last_idx, tb->tb_default)) { + fib_result_assign(res, fi); + tb->tb_default = order; + goto out; + } + fi = next_fi; + order++; + } + + if (order <= 0 || fi == NULL) { + tb->tb_default = -1; + goto out; + } + + if (!fib_detect_death(fi, order, &last_resort, &last_idx, + tb->tb_default)) { + fib_result_assign(res, fi); + tb->tb_default = order; + goto out; + } + + if (last_idx >= 0) + fib_result_assign(res, last_resort); + tb->tb_default = last_idx; +out: + rcu_read_unlock(); +} + #ifdef CONFIG_IP_ROUTE_MULTIPATH /* diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 8cee5c8848ed..16d589c70a92 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1802,80 +1802,6 @@ void fib_free_table(struct fib_table *tb) kfree(tb); } -void fib_table_select_default(struct fib_table *tb, - const struct flowi *flp, - struct fib_result *res) -{ - struct trie *t = (struct trie *) tb->tb_data; - int order, last_idx; - struct fib_info *fi = NULL; - struct fib_info *last_resort; - struct fib_alias *fa = NULL; - struct list_head *fa_head; - struct leaf *l; - - last_idx = -1; - last_resort = NULL; - order = -1; - - rcu_read_lock(); - - l = fib_find_node(t, 0); - if (!l) - goto out; - - fa_head = get_fa_head(l, 0); - if (!fa_head) - goto out; - - if (list_empty(fa_head)) - goto out; - - list_for_each_entry_rcu(fa, fa_head, fa_list) { - struct fib_info *next_fi = fa->fa_info; - - if (fa->fa_scope != res->scope || - fa->fa_type != RTN_UNICAST) - continue; - - if (next_fi->fib_priority > res->fi->fib_priority) - break; - if (!next_fi->fib_nh[0].nh_gw || - next_fi->fib_nh[0].nh_scope != RT_SCOPE_LINK) - continue; - - fib_alias_accessed(fa); - - if (fi == NULL) { - if (next_fi != res->fi) - break; - } else if (!fib_detect_death(fi, order, &last_resort, - &last_idx, tb->tb_default)) { - fib_result_assign(res, fi); - tb->tb_default = order; - goto out; - } - fi = next_fi; - order++; - } - if (order <= 0 || fi == NULL) { - tb->tb_default = -1; - goto out; - } - - if (!fib_detect_death(fi, order, &last_resort, &last_idx, - tb->tb_default)) { - fib_result_assign(res, fi); - tb->tb_default = order; - goto out; - } - if (last_idx >= 0) - fib_result_assign(res, last_resort); - tb->tb_default = last_idx; -out: - rcu_read_unlock(); -} - static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah, struct fib_table *tb, struct sk_buff *skb, struct netlink_callback *cb) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index b1e5d3ac3460..242a3de83fbb 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2711,7 +2711,7 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp, else #endif if (!res.prefixlen && res.type == RTN_UNICAST && !fl.oif) - fib_select_default(net, &fl, &res); + fib_select_default(&res); if (!fl.fl4_src) fl.fl4_src = FIB_RES_PREFSRC(res); -- cgit v1.2.3 From f703651ef870bd6b94ddc98ae07488b7d3fd9335 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 1 Feb 2011 15:20:14 +0100 Subject: netfilter: NFNL_SUBSYS_IPSET id and NLA_PUT_NET* macros The patch adds the NFNL_SUBSYS_IPSET id and NLA_PUT_NET* macros to the vanilla kernel. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/nfnetlink.h | 3 ++- include/net/netlink.h | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index 361d6b5630ee..2b11fc1a86be 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h @@ -47,7 +47,8 @@ struct nfgenmsg { #define NFNL_SUBSYS_QUEUE 3 #define NFNL_SUBSYS_ULOG 4 #define NFNL_SUBSYS_OSF 5 -#define NFNL_SUBSYS_COUNT 6 +#define NFNL_SUBSYS_IPSET 6 +#define NFNL_SUBSYS_COUNT 7 #ifdef __KERNEL__ diff --git a/include/net/netlink.h b/include/net/netlink.h index 373f1a900cf4..8a3906a08f5f 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -856,18 +856,27 @@ static inline int nla_put_msecs(struct sk_buff *skb, int attrtype, #define NLA_PUT_BE16(skb, attrtype, value) \ NLA_PUT_TYPE(skb, __be16, attrtype, value) +#define NLA_PUT_NET16(skb, attrtype, value) \ + NLA_PUT_BE16(skb, attrtype | NLA_F_NET_BYTEORDER, value) + #define NLA_PUT_U32(skb, attrtype, value) \ NLA_PUT_TYPE(skb, u32, attrtype, value) #define NLA_PUT_BE32(skb, attrtype, value) \ NLA_PUT_TYPE(skb, __be32, attrtype, value) +#define NLA_PUT_NET32(skb, attrtype, value) \ + NLA_PUT_BE32(skb, attrtype | NLA_F_NET_BYTEORDER, value) + #define NLA_PUT_U64(skb, attrtype, value) \ NLA_PUT_TYPE(skb, u64, attrtype, value) #define NLA_PUT_BE64(skb, attrtype, value) \ NLA_PUT_TYPE(skb, __be64, attrtype, value) +#define NLA_PUT_NET64(skb, attrtype, value) \ + NLA_PUT_BE64(skb, attrtype | NLA_F_NET_BYTEORDER, value) + #define NLA_PUT_STRING(skb, attrtype, value) \ NLA_PUT(skb, attrtype, strlen(value) + 1, value) -- cgit v1.2.3 From a7b4f989a629493bb4ec4a354def784d440b32c4 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 1 Feb 2011 15:28:35 +0100 Subject: netfilter: ipset: IP set core support The patch adds the IP set core support to the kernel. The IP set core implements a netlink (nfnetlink) based protocol by which one can create, destroy, flush, rename, swap, list, save, restore sets, and add, delete, test elements from userspace. For simplicity (and backward compatibilty and for not to force ip(6)tables to be linked with a netlink library) reasons a small getsockopt-based protocol is also kept in order to communicate with the ip(6)tables match and target. The netlink protocol passes all u16, etc values in network order with NLA_F_NET_BYTEORDER flag. The protocol enforces the proper use of the NLA_F_NESTED and NLA_F_NET_BYTEORDER flags. For other kernel subsystems (netfilter match and target) the API contains the functions to add, delete and test elements in sets and the required calls to get/put refereces to the sets before those operations can be performed. The set types (which are implemented in independent modules) are stored in a simple RCU protected list. A set type may have variants: for example without timeout or with timeout support, for IPv4 or for IPv6. The sets (i.e. the pointers to the sets) are stored in an array. The sets are identified by their index in the array, which makes possible easy and fast swapping of sets. The array is protected indirectly by the nfnl mutex from nfnetlink. The content of the sets are protected by the rwlock of the set. There are functional differences between the add/del/test functions for the kernel and userspace: - kernel add/del/test: works on the current packet (i.e. one element) - kernel test: may trigger an "add" operation in order to fill out unspecified parts of the element from the packet (like MAC address) - userspace add/del: works on the netlink message and thus possibly on multiple elements from the IPSET_ATTR_ADT container attribute. - userspace add: may trigger resizing of a set Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/ipset/ip_set.h | 452 +++++++ include/linux/netfilter/ipset/ip_set_getport.h | 11 + include/linux/netfilter/ipset/pfxlen.h | 35 + net/netfilter/Kconfig | 2 + net/netfilter/Makefile | 3 + net/netfilter/ipset/Kconfig | 26 + net/netfilter/ipset/Makefile | 8 + net/netfilter/ipset/ip_set_core.c | 1662 ++++++++++++++++++++++++ net/netfilter/ipset/ip_set_getport.c | 136 ++ net/netfilter/ipset/pfxlen.c | 291 +++++ 10 files changed, 2626 insertions(+) create mode 100644 include/linux/netfilter/ipset/ip_set.h create mode 100644 include/linux/netfilter/ipset/ip_set_getport.h create mode 100644 include/linux/netfilter/ipset/pfxlen.h create mode 100644 net/netfilter/ipset/Kconfig create mode 100644 net/netfilter/ipset/Makefile create mode 100644 net/netfilter/ipset/ip_set_core.c create mode 100644 net/netfilter/ipset/ip_set_getport.c create mode 100644 net/netfilter/ipset/pfxlen.c (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h new file mode 100644 index 000000000000..ec333d83f3b4 --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set.h @@ -0,0 +1,452 @@ +#ifndef _IP_SET_H +#define _IP_SET_H + +/* Copyright (C) 2000-2002 Joakim Axelsson + * Patrick Schaaf + * Martin Josefsson + * Copyright (C) 2003-2011 Jozsef Kadlecsik + * + * 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. + */ + +/* The protocol version */ +#define IPSET_PROTOCOL 6 + +/* The max length of strings including NUL: set and type identifiers */ +#define IPSET_MAXNAMELEN 32 + +/* Message types and commands */ +enum ipset_cmd { + IPSET_CMD_NONE, + IPSET_CMD_PROTOCOL, /* 1: Return protocol version */ + IPSET_CMD_CREATE, /* 2: Create a new (empty) set */ + IPSET_CMD_DESTROY, /* 3: Destroy a (empty) set */ + IPSET_CMD_FLUSH, /* 4: Remove all elements from a set */ + IPSET_CMD_RENAME, /* 5: Rename a set */ + IPSET_CMD_SWAP, /* 6: Swap two sets */ + IPSET_CMD_LIST, /* 7: List sets */ + IPSET_CMD_SAVE, /* 8: Save sets */ + IPSET_CMD_ADD, /* 9: Add an element to a set */ + IPSET_CMD_DEL, /* 10: Delete an element from a set */ + IPSET_CMD_TEST, /* 11: Test an element in a set */ + IPSET_CMD_HEADER, /* 12: Get set header data only */ + IPSET_CMD_TYPE, /* 13: Get set type */ + IPSET_MSG_MAX, /* Netlink message commands */ + + /* Commands in userspace: */ + IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* 14: Enter restore mode */ + IPSET_CMD_HELP, /* 15: Get help */ + IPSET_CMD_VERSION, /* 16: Get program version */ + IPSET_CMD_QUIT, /* 17: Quit from interactive mode */ + + IPSET_CMD_MAX, + + IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* 18: Commit buffered commands */ +}; + +/* Attributes at command level */ +enum { + IPSET_ATTR_UNSPEC, + IPSET_ATTR_PROTOCOL, /* 1: Protocol version */ + IPSET_ATTR_SETNAME, /* 2: Name of the set */ + IPSET_ATTR_TYPENAME, /* 3: Typename */ + IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* Setname at rename/swap */ + IPSET_ATTR_REVISION, /* 4: Settype revision */ + IPSET_ATTR_FAMILY, /* 5: Settype family */ + IPSET_ATTR_FLAGS, /* 6: Flags at command level */ + IPSET_ATTR_DATA, /* 7: Nested attributes */ + IPSET_ATTR_ADT, /* 8: Multiple data containers */ + IPSET_ATTR_LINENO, /* 9: Restore lineno */ + IPSET_ATTR_PROTOCOL_MIN, /* 10: Minimal supported version number */ + IPSET_ATTR_REVISION_MIN = IPSET_ATTR_PROTOCOL_MIN, /* type rev min */ + __IPSET_ATTR_CMD_MAX, +}; +#define IPSET_ATTR_CMD_MAX (__IPSET_ATTR_CMD_MAX - 1) + +/* CADT specific attributes */ +enum { + IPSET_ATTR_IP = IPSET_ATTR_UNSPEC + 1, + IPSET_ATTR_IP_FROM = IPSET_ATTR_IP, + IPSET_ATTR_IP_TO, /* 2 */ + IPSET_ATTR_CIDR, /* 3 */ + IPSET_ATTR_PORT, /* 4 */ + IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT, + IPSET_ATTR_PORT_TO, /* 5 */ + IPSET_ATTR_TIMEOUT, /* 6 */ + IPSET_ATTR_PROTO, /* 7 */ + IPSET_ATTR_CADT_FLAGS, /* 8 */ + IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* 9 */ + /* Reserve empty slots */ + IPSET_ATTR_CADT_MAX = 16, + /* Create-only specific attributes */ + IPSET_ATTR_GC, + IPSET_ATTR_HASHSIZE, + IPSET_ATTR_MAXELEM, + IPSET_ATTR_NETMASK, + IPSET_ATTR_PROBES, + IPSET_ATTR_RESIZE, + IPSET_ATTR_SIZE, + /* Kernel-only */ + IPSET_ATTR_ELEMENTS, + IPSET_ATTR_REFERENCES, + IPSET_ATTR_MEMSIZE, + + __IPSET_ATTR_CREATE_MAX, +}; +#define IPSET_ATTR_CREATE_MAX (__IPSET_ATTR_CREATE_MAX - 1) + +/* ADT specific attributes */ +enum { + IPSET_ATTR_ETHER = IPSET_ATTR_CADT_MAX + 1, + IPSET_ATTR_NAME, + IPSET_ATTR_NAMEREF, + IPSET_ATTR_IP2, + IPSET_ATTR_CIDR2, + __IPSET_ATTR_ADT_MAX, +}; +#define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1) + +/* IP specific attributes */ +enum { + IPSET_ATTR_IPADDR_IPV4 = IPSET_ATTR_UNSPEC + 1, + IPSET_ATTR_IPADDR_IPV6, + __IPSET_ATTR_IPADDR_MAX, +}; +#define IPSET_ATTR_IPADDR_MAX (__IPSET_ATTR_IPADDR_MAX - 1) + +/* Error codes */ +enum ipset_errno { + IPSET_ERR_PRIVATE = 4096, + IPSET_ERR_PROTOCOL, + IPSET_ERR_FIND_TYPE, + IPSET_ERR_MAX_SETS, + IPSET_ERR_BUSY, + IPSET_ERR_EXIST_SETNAME2, + IPSET_ERR_TYPE_MISMATCH, + IPSET_ERR_EXIST, + IPSET_ERR_INVALID_CIDR, + IPSET_ERR_INVALID_NETMASK, + IPSET_ERR_INVALID_FAMILY, + IPSET_ERR_TIMEOUT, + IPSET_ERR_REFERENCED, + IPSET_ERR_IPADDR_IPV4, + IPSET_ERR_IPADDR_IPV6, + + /* Type specific error codes */ + IPSET_ERR_TYPE_SPECIFIC = 4352, +}; + +/* Flags at command level */ +enum ipset_cmd_flags { + IPSET_FLAG_BIT_EXIST = 0, + IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST), +}; + +/* Flags at CADT attribute level */ +enum ipset_cadt_flags { + IPSET_FLAG_BIT_BEFORE = 0, + IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE), +}; + +/* Commands with settype-specific attributes */ +enum ipset_adt { + IPSET_ADD, + IPSET_DEL, + IPSET_TEST, + IPSET_ADT_MAX, + IPSET_CREATE = IPSET_ADT_MAX, + IPSET_CADT_MAX, +}; + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include + +/* Sets are identified by an index in kernel space. Tweak with ip_set_id_t + * and IPSET_INVALID_ID if you want to increase the max number of sets. + */ +typedef u16 ip_set_id_t; + +#define IPSET_INVALID_ID 65535 + +enum ip_set_dim { + IPSET_DIM_ZERO = 0, + IPSET_DIM_ONE, + IPSET_DIM_TWO, + IPSET_DIM_THREE, + /* Max dimension in elements. + * If changed, new revision of iptables match/target is required. + */ + IPSET_DIM_MAX = 6, +}; + +/* Option flags for kernel operations */ +enum ip_set_kopt { + IPSET_INV_MATCH = (1 << IPSET_DIM_ZERO), + IPSET_DIM_ONE_SRC = (1 << IPSET_DIM_ONE), + IPSET_DIM_TWO_SRC = (1 << IPSET_DIM_TWO), + IPSET_DIM_THREE_SRC = (1 << IPSET_DIM_THREE), +}; + +/* Set features */ +enum ip_set_feature { + IPSET_TYPE_IP_FLAG = 0, + IPSET_TYPE_IP = (1 << IPSET_TYPE_IP_FLAG), + IPSET_TYPE_PORT_FLAG = 1, + IPSET_TYPE_PORT = (1 << IPSET_TYPE_PORT_FLAG), + IPSET_TYPE_MAC_FLAG = 2, + IPSET_TYPE_MAC = (1 << IPSET_TYPE_MAC_FLAG), + IPSET_TYPE_IP2_FLAG = 3, + IPSET_TYPE_IP2 = (1 << IPSET_TYPE_IP2_FLAG), + IPSET_TYPE_NAME_FLAG = 4, + IPSET_TYPE_NAME = (1 << IPSET_TYPE_NAME_FLAG), + /* Strictly speaking not a feature, but a flag for dumping: + * this settype must be dumped last */ + IPSET_DUMP_LAST_FLAG = 7, + IPSET_DUMP_LAST = (1 << IPSET_DUMP_LAST_FLAG), +}; + +struct ip_set; + +typedef int (*ipset_adtfn)(struct ip_set *set, void *value, u32 timeout); + +/* Set type, variant-specific part */ +struct ip_set_type_variant { + /* Kernelspace: test/add/del entries + * returns negative error code, + * zero for no match/success to add/delete + * positive for matching element */ + int (*kadt)(struct ip_set *set, const struct sk_buff * skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags); + + /* Userspace: test/add/del entries + * returns negative error code, + * zero for no match/success to add/delete + * positive for matching element */ + int (*uadt)(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags); + + /* Low level add/del/test functions */ + ipset_adtfn adt[IPSET_ADT_MAX]; + + /* When adding entries and set is full, try to resize the set */ + int (*resize)(struct ip_set *set, bool retried); + /* Destroy the set */ + void (*destroy)(struct ip_set *set); + /* Flush the elements */ + void (*flush)(struct ip_set *set); + /* Expire entries before listing */ + void (*expire)(struct ip_set *set); + /* List set header data */ + int (*head)(struct ip_set *set, struct sk_buff *skb); + /* List elements */ + int (*list)(const struct ip_set *set, struct sk_buff *skb, + struct netlink_callback *cb); + + /* Return true if "b" set is the same as "a" + * according to the create set parameters */ + bool (*same_set)(const struct ip_set *a, const struct ip_set *b); +}; + +/* The core set type structure */ +struct ip_set_type { + struct list_head list; + + /* Typename */ + char name[IPSET_MAXNAMELEN]; + /* Protocol version */ + u8 protocol; + /* Set features to control swapping */ + u8 features; + /* Set type dimension */ + u8 dimension; + /* Supported family: may be AF_UNSPEC for both AF_INET/AF_INET6 */ + u8 family; + /* Type revision */ + u8 revision; + + /* Create set */ + int (*create)(struct ip_set *set, struct nlattr *tb[], u32 flags); + + /* Attribute policies */ + const struct nla_policy create_policy[IPSET_ATTR_CREATE_MAX + 1]; + const struct nla_policy adt_policy[IPSET_ATTR_ADT_MAX + 1]; + + /* Set this to THIS_MODULE if you are a module, otherwise NULL */ + struct module *me; +}; + +/* register and unregister set type */ +extern int ip_set_type_register(struct ip_set_type *set_type); +extern void ip_set_type_unregister(struct ip_set_type *set_type); + +/* A generic IP set */ +struct ip_set { + /* The name of the set */ + char name[IPSET_MAXNAMELEN]; + /* Lock protecting the set data */ + rwlock_t lock; + /* References to the set */ + atomic_t ref; + /* The core set type */ + struct ip_set_type *type; + /* The type variant doing the real job */ + const struct ip_set_type_variant *variant; + /* The actual INET family of the set */ + u8 family; + /* The type specific data */ + void *data; +}; + +/* register and unregister set references */ +extern ip_set_id_t ip_set_get_byname(const char *name, struct ip_set **set); +extern void ip_set_put_byindex(ip_set_id_t index); +extern const char * ip_set_name_byindex(ip_set_id_t index); +extern ip_set_id_t ip_set_nfnl_get(const char *name); +extern ip_set_id_t ip_set_nfnl_get_byindex(ip_set_id_t index); +extern void ip_set_nfnl_put(ip_set_id_t index); + +/* API for iptables set match, and SET target */ +extern int ip_set_add(ip_set_id_t id, const struct sk_buff *skb, + u8 family, u8 dim, u8 flags); +extern int ip_set_del(ip_set_id_t id, const struct sk_buff *skb, + u8 family, u8 dim, u8 flags); +extern int ip_set_test(ip_set_id_t id, const struct sk_buff *skb, + u8 family, u8 dim, u8 flags); + +/* Utility functions */ +extern void * ip_set_alloc(size_t size); +extern void ip_set_free(void *members); +extern int ip_set_get_ipaddr4(struct nlattr *nla, __be32 *ipaddr); +extern int ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr); + +static inline int +ip_set_get_hostipaddr4(struct nlattr *nla, u32 *ipaddr) +{ + __be32 ip; + int ret = ip_set_get_ipaddr4(nla, &ip); + + if (ret) + return ret; + *ipaddr = ntohl(ip); + return 0; +} + +/* Ignore IPSET_ERR_EXIST errors if asked to do so? */ +static inline bool +ip_set_eexist(int ret, u32 flags) +{ + return ret == -IPSET_ERR_EXIST && (flags & IPSET_FLAG_EXIST); +} + +/* Check the NLA_F_NET_BYTEORDER flag */ +static inline bool +ip_set_attr_netorder(struct nlattr *tb[], int type) +{ + return tb[type] && (tb[type]->nla_type & NLA_F_NET_BYTEORDER); +} + +static inline bool +ip_set_optattr_netorder(struct nlattr *tb[], int type) +{ + return !tb[type] || (tb[type]->nla_type & NLA_F_NET_BYTEORDER); +} + +/* Useful converters */ +static inline u32 +ip_set_get_h32(const struct nlattr *attr) +{ + return ntohl(nla_get_be32(attr)); +} + +static inline u16 +ip_set_get_h16(const struct nlattr *attr) +{ + return ntohs(nla_get_be16(attr)); +} + +#define ipset_nest_start(skb, attr) nla_nest_start(skb, attr | NLA_F_NESTED) +#define ipset_nest_end(skb, start) nla_nest_end(skb, start) + +#define NLA_PUT_IPADDR4(skb, type, ipaddr) \ +do { \ + struct nlattr *__nested = ipset_nest_start(skb, type); \ + \ + if (!__nested) \ + goto nla_put_failure; \ + NLA_PUT_NET32(skb, IPSET_ATTR_IPADDR_IPV4, ipaddr); \ + ipset_nest_end(skb, __nested); \ +} while (0) + +#define NLA_PUT_IPADDR6(skb, type, ipaddrptr) \ +do { \ + struct nlattr *__nested = ipset_nest_start(skb, type); \ + \ + if (!__nested) \ + goto nla_put_failure; \ + NLA_PUT(skb, IPSET_ATTR_IPADDR_IPV6, \ + sizeof(struct in6_addr), ipaddrptr); \ + ipset_nest_end(skb, __nested); \ +} while (0) + +/* Get address from skbuff */ +static inline __be32 +ip4addr(const struct sk_buff *skb, bool src) +{ + return src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr; +} + +static inline void +ip4addrptr(const struct sk_buff *skb, bool src, __be32 *addr) +{ + *addr = src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr; +} + +static inline void +ip6addrptr(const struct sk_buff *skb, bool src, struct in6_addr *addr) +{ + memcpy(addr, src ? &ipv6_hdr(skb)->saddr : &ipv6_hdr(skb)->daddr, + sizeof(*addr)); +} + +/* Calculate the bytes required to store the inclusive range of a-b */ +static inline int +bitmap_bytes(u32 a, u32 b) +{ + return 4 * ((((b - a + 8) / 8) + 3) / 4); +} + +/* Interface to iptables/ip6tables */ + +#define SO_IP_SET 83 + +union ip_set_name_index { + char name[IPSET_MAXNAMELEN]; + ip_set_id_t index; +}; + +#define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */ +struct ip_set_req_get_set { + unsigned op; + unsigned version; + union ip_set_name_index set; +}; + +#define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */ +/* Uses ip_set_req_get_set */ + +#define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */ +struct ip_set_req_version { + unsigned op; + unsigned version; +}; + +#endif /* __KERNEL__ */ + +#endif /*_IP_SET_H */ diff --git a/include/linux/netfilter/ipset/ip_set_getport.h b/include/linux/netfilter/ipset/ip_set_getport.h new file mode 100644 index 000000000000..694c433298b8 --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set_getport.h @@ -0,0 +1,11 @@ +#ifndef _IP_SET_GETPORT_H +#define _IP_SET_GETPORT_H + +extern bool ip_set_get_ip4_port(const struct sk_buff *skb, bool src, + __be16 *port, u8 *proto); +extern bool ip_set_get_ip6_port(const struct sk_buff *skb, bool src, + __be16 *port, u8 *proto); +extern bool ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, + __be16 *port); + +#endif /*_IP_SET_GETPORT_H*/ diff --git a/include/linux/netfilter/ipset/pfxlen.h b/include/linux/netfilter/ipset/pfxlen.h new file mode 100644 index 000000000000..0e1fb50da562 --- /dev/null +++ b/include/linux/netfilter/ipset/pfxlen.h @@ -0,0 +1,35 @@ +#ifndef _PFXLEN_H +#define _PFXLEN_H + +#include +#include + +/* Prefixlen maps, by Jan Engelhardt */ +extern const union nf_inet_addr ip_set_netmask_map[]; +extern const union nf_inet_addr ip_set_hostmask_map[]; + +static inline __be32 +ip_set_netmask(u8 pfxlen) +{ + return ip_set_netmask_map[pfxlen].ip; +} + +static inline const __be32 * +ip_set_netmask6(u8 pfxlen) +{ + return &ip_set_netmask_map[pfxlen].ip6[0]; +} + +static inline u32 +ip_set_hostmask(u8 pfxlen) +{ + return (__force u32) ip_set_hostmask_map[pfxlen].ip; +} + +static inline const __be32 * +ip_set_hostmask6(u8 pfxlen) +{ + return &ip_set_hostmask_map[pfxlen].ip6[0]; +} + +#endif /*_PFXLEN_H */ diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index faf7412ea453..351abf8ace13 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -1052,4 +1052,6 @@ endif # NETFILTER_XTABLES endmenu +source "net/netfilter/ipset/Kconfig" + source "net/netfilter/ipvs/Kconfig" diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 9ae6878a85b1..510b586ccb7f 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -105,5 +105,8 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o obj-$(CONFIG_NETFILTER_XT_MATCH_TIME) += xt_time.o obj-$(CONFIG_NETFILTER_XT_MATCH_U32) += xt_u32.o +# ipset +obj-$(CONFIG_IP_SET) += ipset/ + # IPVS obj-$(CONFIG_IP_VS) += ipvs/ diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig new file mode 100644 index 000000000000..5ade156dd470 --- /dev/null +++ b/net/netfilter/ipset/Kconfig @@ -0,0 +1,26 @@ +menuconfig IP_SET + tristate "IP set support" + depends on INET && NETFILTER + help + This option adds IP set support to the kernel. + In order to define and use the sets, you need the userspace utility + ipset(8). You can use the sets in netfilter via the "set" match + and "SET" target. + + To compile it as a module, choose M here. If unsure, say N. + +if IP_SET + +config IP_SET_MAX + int "Maximum number of IP sets" + default 256 + range 2 65534 + depends on IP_SET + help + You can define here default value of the maximum number + of IP sets for the kernel. + + The value can be overriden by the 'max_sets' module + parameter of the 'ip_set' module. + +endif # IP_SET diff --git a/net/netfilter/ipset/Makefile b/net/netfilter/ipset/Makefile new file mode 100644 index 000000000000..910cd425c067 --- /dev/null +++ b/net/netfilter/ipset/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the ipset modules +# + +ip_set-y := ip_set_core.o ip_set_getport.o pfxlen.o + +# ipset core +obj-$(CONFIG_IP_SET) += ip_set.o diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c new file mode 100644 index 000000000000..8a736247e85f --- /dev/null +++ b/net/netfilter/ipset/ip_set_core.c @@ -0,0 +1,1662 @@ +/* Copyright (C) 2000-2002 Joakim Axelsson + * Patrick Schaaf + * Copyright (C) 2003-2011 Jozsef Kadlecsik + * + * 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. + */ + +/* Kernel module for IP set management */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static LIST_HEAD(ip_set_type_list); /* all registered set types */ +static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */ + +static struct ip_set **ip_set_list; /* all individual sets */ +static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX; /* max number of sets */ + +#define STREQ(a, b) (strncmp(a, b, IPSET_MAXNAMELEN) == 0) + +static unsigned int max_sets; + +module_param(max_sets, int, 0600); +MODULE_PARM_DESC(max_sets, "maximal number of sets"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("core IP set support"); +MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET); + +/* + * The set types are implemented in modules and registered set types + * can be found in ip_set_type_list. Adding/deleting types is + * serialized by ip_set_type_mutex. + */ + +static inline void +ip_set_type_lock(void) +{ + mutex_lock(&ip_set_type_mutex); +} + +static inline void +ip_set_type_unlock(void) +{ + mutex_unlock(&ip_set_type_mutex); +} + +/* Register and deregister settype */ + +static struct ip_set_type * +find_set_type(const char *name, u8 family, u8 revision) +{ + struct ip_set_type *type; + + list_for_each_entry_rcu(type, &ip_set_type_list, list) + if (STREQ(type->name, name) && + (type->family == family || type->family == AF_UNSPEC) && + type->revision == revision) + return type; + return NULL; +} + +/* Unlock, try to load a set type module and lock again */ +static int +try_to_load_type(const char *name) +{ + nfnl_unlock(); + pr_debug("try to load ip_set_%s\n", name); + if (request_module("ip_set_%s", name) < 0) { + pr_warning("Can't find ip_set type %s\n", name); + nfnl_lock(); + return -IPSET_ERR_FIND_TYPE; + } + nfnl_lock(); + return -EAGAIN; +} + +/* Find a set type and reference it */ +static int +find_set_type_get(const char *name, u8 family, u8 revision, + struct ip_set_type **found) +{ + rcu_read_lock(); + *found = find_set_type(name, family, revision); + if (*found) { + int err = !try_module_get((*found)->me); + rcu_read_unlock(); + return err ? -EFAULT : 0; + } + rcu_read_unlock(); + + return try_to_load_type(name); +} + +/* Find a given set type by name and family. + * If we succeeded, the supported minimal and maximum revisions are + * filled out. + */ +static int +find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max) +{ + struct ip_set_type *type; + bool found = false; + + *min = *max = 0; + rcu_read_lock(); + list_for_each_entry_rcu(type, &ip_set_type_list, list) + if (STREQ(type->name, name) && + (type->family == family || type->family == AF_UNSPEC)) { + found = true; + if (type->revision < *min) + *min = type->revision; + else if (type->revision > *max) + *max = type->revision; + } + rcu_read_unlock(); + if (found) + return 0; + + return try_to_load_type(name); +} + +#define family_name(f) ((f) == AF_INET ? "inet" : \ + (f) == AF_INET6 ? "inet6" : "any") + +/* Register a set type structure. The type is identified by + * the unique triple of name, family and revision. + */ +int +ip_set_type_register(struct ip_set_type *type) +{ + int ret = 0; + + if (type->protocol != IPSET_PROTOCOL) { + pr_warning("ip_set type %s, family %s, revision %u uses " + "wrong protocol version %u (want %u)\n", + type->name, family_name(type->family), + type->revision, type->protocol, IPSET_PROTOCOL); + return -EINVAL; + } + + ip_set_type_lock(); + if (find_set_type(type->name, type->family, type->revision)) { + /* Duplicate! */ + pr_warning("ip_set type %s, family %s, revision %u " + "already registered!\n", type->name, + family_name(type->family), type->revision); + ret = -EINVAL; + goto unlock; + } + list_add_rcu(&type->list, &ip_set_type_list); + pr_debug("type %s, family %s, revision %u registered.\n", + type->name, family_name(type->family), type->revision); +unlock: + ip_set_type_unlock(); + return ret; +} +EXPORT_SYMBOL_GPL(ip_set_type_register); + +/* Unregister a set type. There's a small race with ip_set_create */ +void +ip_set_type_unregister(struct ip_set_type *type) +{ + ip_set_type_lock(); + if (!find_set_type(type->name, type->family, type->revision)) { + pr_warning("ip_set type %s, family %s, revision %u " + "not registered\n", type->name, + family_name(type->family), type->revision); + goto unlock; + } + list_del_rcu(&type->list); + pr_debug("type %s, family %s, revision %u unregistered.\n", + type->name, family_name(type->family), type->revision); +unlock: + ip_set_type_unlock(); + + synchronize_rcu(); +} +EXPORT_SYMBOL_GPL(ip_set_type_unregister); + +/* Utility functions */ +void * +ip_set_alloc(size_t size) +{ + void *members = NULL; + + if (size < KMALLOC_MAX_SIZE) + members = kzalloc(size, GFP_KERNEL | __GFP_NOWARN); + + if (members) { + pr_debug("%p: allocated with kmalloc\n", members); + return members; + } + + members = vzalloc(size); + if (!members) + return NULL; + pr_debug("%p: allocated with vmalloc\n", members); + + return members; +} +EXPORT_SYMBOL_GPL(ip_set_alloc); + +void +ip_set_free(void *members) +{ + pr_debug("%p: free with %s\n", members, + is_vmalloc_addr(members) ? "vfree" : "kfree"); + if (is_vmalloc_addr(members)) + vfree(members); + else + kfree(members); +} +EXPORT_SYMBOL_GPL(ip_set_free); + +static inline bool +flag_nested(const struct nlattr *nla) +{ + return nla->nla_type & NLA_F_NESTED; +} + +static const struct nla_policy ipaddr_policy[IPSET_ATTR_IPADDR_MAX + 1] = { + [IPSET_ATTR_IPADDR_IPV4] = { .type = NLA_U32 }, + [IPSET_ATTR_IPADDR_IPV6] = { .type = NLA_BINARY, + .len = sizeof(struct in6_addr) }, +}; + +int +ip_set_get_ipaddr4(struct nlattr *nla, __be32 *ipaddr) +{ + struct nlattr *tb[IPSET_ATTR_IPADDR_MAX+1]; + + if (unlikely(!flag_nested(nla))) + return -IPSET_ERR_PROTOCOL; + if (nla_parse(tb, IPSET_ATTR_IPADDR_MAX, nla_data(nla), nla_len(nla), + ipaddr_policy)) + return -IPSET_ERR_PROTOCOL; + if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_IPADDR_IPV4))) + return -IPSET_ERR_PROTOCOL; + + *ipaddr = nla_get_be32(tb[IPSET_ATTR_IPADDR_IPV4]); + return 0; +} +EXPORT_SYMBOL_GPL(ip_set_get_ipaddr4); + +int +ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr) +{ + struct nlattr *tb[IPSET_ATTR_IPADDR_MAX+1]; + + if (unlikely(!flag_nested(nla))) + return -IPSET_ERR_PROTOCOL; + + if (nla_parse(tb, IPSET_ATTR_IPADDR_MAX, nla_data(nla), nla_len(nla), + ipaddr_policy)) + return -IPSET_ERR_PROTOCOL; + if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_IPADDR_IPV6))) + return -IPSET_ERR_PROTOCOL; + + memcpy(ipaddr, nla_data(tb[IPSET_ATTR_IPADDR_IPV6]), + sizeof(struct in6_addr)); + return 0; +} +EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6); + +/* + * Creating/destroying/renaming/swapping affect the existence and + * the properties of a set. All of these can be executed from userspace + * only and serialized by the nfnl mutex indirectly from nfnetlink. + * + * Sets are identified by their index in ip_set_list and the index + * is used by the external references (set/SET netfilter modules). + * + * The set behind an index may change by swapping only, from userspace. + */ + +static inline void +__ip_set_get(ip_set_id_t index) +{ + atomic_inc(&ip_set_list[index]->ref); +} + +static inline void +__ip_set_put(ip_set_id_t index) +{ + atomic_dec(&ip_set_list[index]->ref); +} + +/* + * Add, del and test set entries from kernel. + * + * The set behind the index must exist and must be referenced + * so it can't be destroyed (or changed) under our foot. + */ + +int +ip_set_test(ip_set_id_t index, const struct sk_buff *skb, + u8 family, u8 dim, u8 flags) +{ + struct ip_set *set = ip_set_list[index]; + int ret = 0; + + BUG_ON(set == NULL || atomic_read(&set->ref) == 0); + pr_debug("set %s, index %u\n", set->name, index); + + if (dim < set->type->dimension || + !(family == set->family || set->family == AF_UNSPEC)) + return 0; + + read_lock_bh(&set->lock); + ret = set->variant->kadt(set, skb, IPSET_TEST, family, dim, flags); + read_unlock_bh(&set->lock); + + if (ret == -EAGAIN) { + /* Type requests element to be completed */ + pr_debug("element must be competed, ADD is triggered\n"); + write_lock_bh(&set->lock); + set->variant->kadt(set, skb, IPSET_ADD, family, dim, flags); + write_unlock_bh(&set->lock); + ret = 1; + } + + /* Convert error codes to nomatch */ + return (ret < 0 ? 0 : ret); +} +EXPORT_SYMBOL_GPL(ip_set_test); + +int +ip_set_add(ip_set_id_t index, const struct sk_buff *skb, + u8 family, u8 dim, u8 flags) +{ + struct ip_set *set = ip_set_list[index]; + int ret; + + BUG_ON(set == NULL || atomic_read(&set->ref) == 0); + pr_debug("set %s, index %u\n", set->name, index); + + if (dim < set->type->dimension || + !(family == set->family || set->family == AF_UNSPEC)) + return 0; + + write_lock_bh(&set->lock); + ret = set->variant->kadt(set, skb, IPSET_ADD, family, dim, flags); + write_unlock_bh(&set->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ip_set_add); + +int +ip_set_del(ip_set_id_t index, const struct sk_buff *skb, + u8 family, u8 dim, u8 flags) +{ + struct ip_set *set = ip_set_list[index]; + int ret = 0; + + BUG_ON(set == NULL || atomic_read(&set->ref) == 0); + pr_debug("set %s, index %u\n", set->name, index); + + if (dim < set->type->dimension || + !(family == set->family || set->family == AF_UNSPEC)) + return 0; + + write_lock_bh(&set->lock); + ret = set->variant->kadt(set, skb, IPSET_DEL, family, dim, flags); + write_unlock_bh(&set->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ip_set_del); + +/* + * Find set by name, reference it once. The reference makes sure the + * thing pointed to, does not go away under our feet. + * + * The nfnl mutex must already be activated. + */ +ip_set_id_t +ip_set_get_byname(const char *name, struct ip_set **set) +{ + ip_set_id_t i, index = IPSET_INVALID_ID; + struct ip_set *s; + + for (i = 0; i < ip_set_max; i++) { + s = ip_set_list[i]; + if (s != NULL && STREQ(s->name, name)) { + __ip_set_get(i); + index = i; + *set = s; + } + } + + return index; +} +EXPORT_SYMBOL_GPL(ip_set_get_byname); + +/* + * If the given set pointer points to a valid set, decrement + * reference count by 1. The caller shall not assume the index + * to be valid, after calling this function. + * + * The nfnl mutex must already be activated. + */ +void +ip_set_put_byindex(ip_set_id_t index) +{ + if (ip_set_list[index] != NULL) { + BUG_ON(atomic_read(&ip_set_list[index]->ref) == 0); + __ip_set_put(index); + } +} +EXPORT_SYMBOL_GPL(ip_set_put_byindex); + +/* + * Get the name of a set behind a set index. + * We assume the set is referenced, so it does exist and + * can't be destroyed. The set cannot be renamed due to + * the referencing either. + * + * The nfnl mutex must already be activated. + */ +const char * +ip_set_name_byindex(ip_set_id_t index) +{ + const struct ip_set *set = ip_set_list[index]; + + BUG_ON(set == NULL); + BUG_ON(atomic_read(&set->ref) == 0); + + /* Referenced, so it's safe */ + return set->name; +} +EXPORT_SYMBOL_GPL(ip_set_name_byindex); + +/* + * Routines to call by external subsystems, which do not + * call nfnl_lock for us. + */ + +/* + * Find set by name, reference it once. The reference makes sure the + * thing pointed to, does not go away under our feet. + * + * The nfnl mutex is used in the function. + */ +ip_set_id_t +ip_set_nfnl_get(const char *name) +{ + struct ip_set *s; + ip_set_id_t index; + + nfnl_lock(); + index = ip_set_get_byname(name, &s); + nfnl_unlock(); + + return index; +} +EXPORT_SYMBOL_GPL(ip_set_nfnl_get); + +/* + * Find set by index, reference it once. The reference makes sure the + * thing pointed to, does not go away under our feet. + * + * The nfnl mutex is used in the function. + */ +ip_set_id_t +ip_set_nfnl_get_byindex(ip_set_id_t index) +{ + if (index > ip_set_max) + return IPSET_INVALID_ID; + + nfnl_lock(); + if (ip_set_list[index]) + __ip_set_get(index); + else + index = IPSET_INVALID_ID; + nfnl_unlock(); + + return index; +} +EXPORT_SYMBOL_GPL(ip_set_nfnl_get_byindex); + +/* + * If the given set pointer points to a valid set, decrement + * reference count by 1. The caller shall not assume the index + * to be valid, after calling this function. + * + * The nfnl mutex is used in the function. + */ +void +ip_set_nfnl_put(ip_set_id_t index) +{ + nfnl_lock(); + if (ip_set_list[index] != NULL) { + BUG_ON(atomic_read(&ip_set_list[index]->ref) == 0); + __ip_set_put(index); + } + nfnl_unlock(); +} +EXPORT_SYMBOL_GPL(ip_set_nfnl_put); + +/* + * Communication protocol with userspace over netlink. + * + * We already locked by nfnl_lock. + */ + +static inline bool +protocol_failed(const struct nlattr * const tb[]) +{ + return !tb[IPSET_ATTR_PROTOCOL] || + nla_get_u8(tb[IPSET_ATTR_PROTOCOL]) != IPSET_PROTOCOL; +} + +static inline u32 +flag_exist(const struct nlmsghdr *nlh) +{ + return nlh->nlmsg_flags & NLM_F_EXCL ? 0 : IPSET_FLAG_EXIST; +} + +static struct nlmsghdr * +start_msg(struct sk_buff *skb, u32 pid, u32 seq, unsigned int flags, + enum ipset_cmd cmd) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfmsg; + + nlh = nlmsg_put(skb, pid, seq, cmd | (NFNL_SUBSYS_IPSET << 8), + sizeof(*nfmsg), flags); + if (nlh == NULL) + return NULL; + + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = AF_INET; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = 0; + + return nlh; +} + +/* Create a set */ + +static const struct nla_policy ip_set_create_policy[IPSET_ATTR_CMD_MAX + 1] = { + [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, + [IPSET_ATTR_SETNAME] = { .type = NLA_NUL_STRING, + .len = IPSET_MAXNAMELEN - 1 }, + [IPSET_ATTR_TYPENAME] = { .type = NLA_NUL_STRING, + .len = IPSET_MAXNAMELEN - 1}, + [IPSET_ATTR_REVISION] = { .type = NLA_U8 }, + [IPSET_ATTR_FAMILY] = { .type = NLA_U8 }, + [IPSET_ATTR_DATA] = { .type = NLA_NESTED }, +}; + +static ip_set_id_t +find_set_id(const char *name) +{ + ip_set_id_t i, index = IPSET_INVALID_ID; + const struct ip_set *set; + + for (i = 0; index == IPSET_INVALID_ID && i < ip_set_max; i++) { + set = ip_set_list[i]; + if (set != NULL && STREQ(set->name, name)) + index = i; + } + return index; +} + +static inline struct ip_set * +find_set(const char *name) +{ + ip_set_id_t index = find_set_id(name); + + return index == IPSET_INVALID_ID ? NULL : ip_set_list[index]; +} + +static int +find_free_id(const char *name, ip_set_id_t *index, struct ip_set **set) +{ + ip_set_id_t i; + + *index = IPSET_INVALID_ID; + for (i = 0; i < ip_set_max; i++) { + if (ip_set_list[i] == NULL) { + if (*index == IPSET_INVALID_ID) + *index = i; + } else if (STREQ(name, ip_set_list[i]->name)) { + /* Name clash */ + *set = ip_set_list[i]; + return -EEXIST; + } + } + if (*index == IPSET_INVALID_ID) + /* No free slot remained */ + return -IPSET_ERR_MAX_SETS; + return 0; +} + +static int +ip_set_create(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const attr[]) +{ + struct ip_set *set, *clash; + ip_set_id_t index = IPSET_INVALID_ID; + struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1] = {}; + const char *name, *typename; + u8 family, revision; + u32 flags = flag_exist(nlh); + int ret = 0; + + if (unlikely(protocol_failed(attr) || + attr[IPSET_ATTR_SETNAME] == NULL || + attr[IPSET_ATTR_TYPENAME] == NULL || + attr[IPSET_ATTR_REVISION] == NULL || + attr[IPSET_ATTR_FAMILY] == NULL || + (attr[IPSET_ATTR_DATA] != NULL && + !flag_nested(attr[IPSET_ATTR_DATA])))) + return -IPSET_ERR_PROTOCOL; + + name = nla_data(attr[IPSET_ATTR_SETNAME]); + typename = nla_data(attr[IPSET_ATTR_TYPENAME]); + family = nla_get_u8(attr[IPSET_ATTR_FAMILY]); + revision = nla_get_u8(attr[IPSET_ATTR_REVISION]); + pr_debug("setname: %s, typename: %s, family: %s, revision: %u\n", + name, typename, family_name(family), revision); + + /* + * First, and without any locks, allocate and initialize + * a normal base set structure. + */ + set = kzalloc(sizeof(struct ip_set), GFP_KERNEL); + if (!set) + return -ENOMEM; + rwlock_init(&set->lock); + strlcpy(set->name, name, IPSET_MAXNAMELEN); + atomic_set(&set->ref, 0); + set->family = family; + + /* + * Next, check that we know the type, and take + * a reference on the type, to make sure it stays available + * while constructing our new set. + * + * After referencing the type, we try to create the type + * specific part of the set without holding any locks. + */ + ret = find_set_type_get(typename, family, revision, &(set->type)); + if (ret) + goto out; + + /* + * Without holding any locks, create private part. + */ + if (attr[IPSET_ATTR_DATA] && + nla_parse(tb, IPSET_ATTR_CREATE_MAX, + nla_data(attr[IPSET_ATTR_DATA]), + nla_len(attr[IPSET_ATTR_DATA]), + set->type->create_policy)) { + ret = -IPSET_ERR_PROTOCOL; + goto put_out; + } + + ret = set->type->create(set, tb, flags); + if (ret != 0) + goto put_out; + + /* BTW, ret==0 here. */ + + /* + * Here, we have a valid, constructed set and we are protected + * by nfnl_lock. Find the first free index in ip_set_list and + * check clashing. + */ + if ((ret = find_free_id(set->name, &index, &clash)) != 0) { + /* If this is the same set and requested, ignore error */ + if (ret == -EEXIST && + (flags & IPSET_FLAG_EXIST) && + STREQ(set->type->name, clash->type->name) && + set->type->family == clash->type->family && + set->type->revision == clash->type->revision && + set->variant->same_set(set, clash)) + ret = 0; + goto cleanup; + } + + /* + * Finally! Add our shiny new set to the list, and be done. + */ + pr_debug("create: '%s' created with index %u!\n", set->name, index); + ip_set_list[index] = set; + + return ret; + +cleanup: + set->variant->destroy(set); +put_out: + module_put(set->type->me); +out: + kfree(set); + return ret; +} + +/* Destroy sets */ + +static const struct nla_policy +ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] = { + [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, + [IPSET_ATTR_SETNAME] = { .type = NLA_NUL_STRING, + .len = IPSET_MAXNAMELEN - 1 }, +}; + +static void +ip_set_destroy_set(ip_set_id_t index) +{ + struct ip_set *set = ip_set_list[index]; + + pr_debug("set: %s\n", set->name); + ip_set_list[index] = NULL; + + /* Must call it without holding any lock */ + set->variant->destroy(set); + module_put(set->type->me); + kfree(set); +} + +static int +ip_set_destroy(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const attr[]) +{ + ip_set_id_t i; + + if (unlikely(protocol_failed(attr))) + return -IPSET_ERR_PROTOCOL; + + /* References are protected by the nfnl mutex */ + if (!attr[IPSET_ATTR_SETNAME]) { + for (i = 0; i < ip_set_max; i++) { + if (ip_set_list[i] != NULL && + (atomic_read(&ip_set_list[i]->ref))) + return -IPSET_ERR_BUSY; + } + for (i = 0; i < ip_set_max; i++) { + if (ip_set_list[i] != NULL) + ip_set_destroy_set(i); + } + } else { + i = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME])); + if (i == IPSET_INVALID_ID) + return -ENOENT; + else if (atomic_read(&ip_set_list[i]->ref)) + return -IPSET_ERR_BUSY; + + ip_set_destroy_set(i); + } + return 0; +} + +/* Flush sets */ + +static void +ip_set_flush_set(struct ip_set *set) +{ + pr_debug("set: %s\n", set->name); + + write_lock_bh(&set->lock); + set->variant->flush(set); + write_unlock_bh(&set->lock); +} + +static int +ip_set_flush(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const attr[]) +{ + ip_set_id_t i; + + if (unlikely(protocol_failed(attr))) + return -EPROTO; + + if (!attr[IPSET_ATTR_SETNAME]) { + for (i = 0; i < ip_set_max; i++) + if (ip_set_list[i] != NULL) + ip_set_flush_set(ip_set_list[i]); + } else { + i = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME])); + if (i == IPSET_INVALID_ID) + return -ENOENT; + + ip_set_flush_set(ip_set_list[i]); + } + + return 0; +} + +/* Rename a set */ + +static const struct nla_policy +ip_set_setname2_policy[IPSET_ATTR_CMD_MAX + 1] = { + [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, + [IPSET_ATTR_SETNAME] = { .type = NLA_NUL_STRING, + .len = IPSET_MAXNAMELEN - 1 }, + [IPSET_ATTR_SETNAME2] = { .type = NLA_NUL_STRING, + .len = IPSET_MAXNAMELEN - 1 }, +}; + +static int +ip_set_rename(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const attr[]) +{ + struct ip_set *set; + const char *name2; + ip_set_id_t i; + + if (unlikely(protocol_failed(attr) || + attr[IPSET_ATTR_SETNAME] == NULL || + attr[IPSET_ATTR_SETNAME2] == NULL)) + return -IPSET_ERR_PROTOCOL; + + set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); + if (set == NULL) + return -ENOENT; + if (atomic_read(&set->ref) != 0) + return -IPSET_ERR_REFERENCED; + + name2 = nla_data(attr[IPSET_ATTR_SETNAME2]); + for (i = 0; i < ip_set_max; i++) { + if (ip_set_list[i] != NULL && + STREQ(ip_set_list[i]->name, name2)) + return -IPSET_ERR_EXIST_SETNAME2; + } + strncpy(set->name, name2, IPSET_MAXNAMELEN); + + return 0; +} + +/* Swap two sets so that name/index points to the other. + * References and set names are also swapped. + * + * We are protected by the nfnl mutex and references are + * manipulated only by holding the mutex. The kernel interfaces + * do not hold the mutex but the pointer settings are atomic + * so the ip_set_list always contains valid pointers to the sets. + */ + +static int +ip_set_swap(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const attr[]) +{ + struct ip_set *from, *to; + ip_set_id_t from_id, to_id; + char from_name[IPSET_MAXNAMELEN]; + u32 from_ref; + + if (unlikely(protocol_failed(attr) || + attr[IPSET_ATTR_SETNAME] == NULL || + attr[IPSET_ATTR_SETNAME2] == NULL)) + return -IPSET_ERR_PROTOCOL; + + from_id = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME])); + if (from_id == IPSET_INVALID_ID) + return -ENOENT; + + to_id = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME2])); + if (to_id == IPSET_INVALID_ID) + return -IPSET_ERR_EXIST_SETNAME2; + + from = ip_set_list[from_id]; + to = ip_set_list[to_id]; + + /* Features must not change. + * Not an artifical restriction anymore, as we must prevent + * possible loops created by swapping in setlist type of sets. */ + if (!(from->type->features == to->type->features && + from->type->family == to->type->family)) + return -IPSET_ERR_TYPE_MISMATCH; + + /* No magic here: ref munging protected by the nfnl_lock */ + strncpy(from_name, from->name, IPSET_MAXNAMELEN); + from_ref = atomic_read(&from->ref); + + strncpy(from->name, to->name, IPSET_MAXNAMELEN); + atomic_set(&from->ref, atomic_read(&to->ref)); + strncpy(to->name, from_name, IPSET_MAXNAMELEN); + atomic_set(&to->ref, from_ref); + + ip_set_list[from_id] = to; + ip_set_list[to_id] = from; + + return 0; +} + +/* List/save set data */ + +#define DUMP_INIT 0L +#define DUMP_ALL 1L +#define DUMP_ONE 2L +#define DUMP_LAST 3L + +static int +ip_set_dump_done(struct netlink_callback *cb) +{ + if (cb->args[2]) { + pr_debug("release set %s\n", ip_set_list[cb->args[1]]->name); + __ip_set_put((ip_set_id_t) cb->args[1]); + } + return 0; +} + +static inline void +dump_attrs(struct nlmsghdr *nlh) +{ + const struct nlattr *attr; + int rem; + + pr_debug("dump nlmsg\n"); + nlmsg_for_each_attr(attr, nlh, sizeof(struct nfgenmsg), rem) { + pr_debug("type: %u, len %u\n", nla_type(attr), attr->nla_len); + } +} + +static int +dump_init(struct netlink_callback *cb) +{ + struct nlmsghdr *nlh = nlmsg_hdr(cb->skb); + int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); + struct nlattr *cda[IPSET_ATTR_CMD_MAX+1]; + struct nlattr *attr = (void *)nlh + min_len; + ip_set_id_t index; + + /* Second pass, so parser can't fail */ + nla_parse(cda, IPSET_ATTR_CMD_MAX, + attr, nlh->nlmsg_len - min_len, ip_set_setname_policy); + + /* cb->args[0] : dump single set/all sets + * [1] : set index + * [..]: type specific + */ + + if (!cda[IPSET_ATTR_SETNAME]) { + cb->args[0] = DUMP_ALL; + return 0; + } + + index = find_set_id(nla_data(cda[IPSET_ATTR_SETNAME])); + if (index == IPSET_INVALID_ID) + return -ENOENT; + + cb->args[0] = DUMP_ONE; + cb->args[1] = index; + return 0; +} + +static int +ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) +{ + ip_set_id_t index = IPSET_INVALID_ID, max; + struct ip_set *set = NULL; + struct nlmsghdr *nlh = NULL; + unsigned int flags = NETLINK_CB(cb->skb).pid ? NLM_F_MULTI : 0; + int ret = 0; + + if (cb->args[0] == DUMP_INIT) { + ret = dump_init(cb); + if (ret < 0) { + nlh = nlmsg_hdr(cb->skb); + /* We have to create and send the error message + * manually :-( */ + if (nlh->nlmsg_flags & NLM_F_ACK) + netlink_ack(cb->skb, nlh, ret); + return ret; + } + } + + if (cb->args[1] >= ip_set_max) + goto out; + + pr_debug("args[0]: %ld args[1]: %ld\n", cb->args[0], cb->args[1]); + max = cb->args[0] == DUMP_ONE ? cb->args[1] + 1 : ip_set_max; + for (; cb->args[1] < max; cb->args[1]++) { + index = (ip_set_id_t) cb->args[1]; + set = ip_set_list[index]; + if (set == NULL) { + if (cb->args[0] == DUMP_ONE) { + ret = -ENOENT; + goto out; + } + continue; + } + /* When dumping all sets, we must dump "sorted" + * so that lists (unions of sets) are dumped last. + */ + if (cb->args[0] != DUMP_ONE && + !((cb->args[0] == DUMP_ALL) ^ + (set->type->features & IPSET_DUMP_LAST))) + continue; + pr_debug("List set: %s\n", set->name); + if (!cb->args[2]) { + /* Start listing: make sure set won't be destroyed */ + pr_debug("reference set\n"); + __ip_set_get(index); + } + nlh = start_msg(skb, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, flags, + IPSET_CMD_LIST); + if (!nlh) { + ret = -EMSGSIZE; + goto release_refcount; + } + NLA_PUT_U8(skb, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL); + NLA_PUT_STRING(skb, IPSET_ATTR_SETNAME, set->name); + switch (cb->args[2]) { + case 0: + /* Core header data */ + NLA_PUT_STRING(skb, IPSET_ATTR_TYPENAME, + set->type->name); + NLA_PUT_U8(skb, IPSET_ATTR_FAMILY, + set->family); + NLA_PUT_U8(skb, IPSET_ATTR_REVISION, + set->type->revision); + ret = set->variant->head(set, skb); + if (ret < 0) + goto release_refcount; + /* Fall through and add elements */ + default: + read_lock_bh(&set->lock); + ret = set->variant->list(set, skb, cb); + read_unlock_bh(&set->lock); + if (!cb->args[2]) { + /* Set is done, proceed with next one */ + if (cb->args[0] == DUMP_ONE) + cb->args[1] = IPSET_INVALID_ID; + else + cb->args[1]++; + } + goto release_refcount; + } + } + goto out; + +nla_put_failure: + ret = -EFAULT; +release_refcount: + /* If there was an error or set is done, release set */ + if (ret || !cb->args[2]) { + pr_debug("release set %s\n", ip_set_list[index]->name); + __ip_set_put(index); + } + + /* If we dump all sets, continue with dumping last ones */ + if (cb->args[0] == DUMP_ALL && cb->args[1] >= max && !cb->args[2]) + cb->args[0] = DUMP_LAST; + +out: + if (nlh) { + nlmsg_end(skb, nlh); + pr_debug("nlmsg_len: %u\n", nlh->nlmsg_len); + dump_attrs(nlh); + } + + return ret < 0 ? ret : skb->len; +} + +static int +ip_set_dump(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const attr[]) +{ + if (unlikely(protocol_failed(attr))) + return -IPSET_ERR_PROTOCOL; + + return netlink_dump_start(ctnl, skb, nlh, + ip_set_dump_start, + ip_set_dump_done); +} + +/* Add, del and test */ + +static const struct nla_policy ip_set_adt_policy[IPSET_ATTR_CMD_MAX + 1] = { + [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, + [IPSET_ATTR_SETNAME] = { .type = NLA_NUL_STRING, + .len = IPSET_MAXNAMELEN - 1 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, + [IPSET_ATTR_DATA] = { .type = NLA_NESTED }, + [IPSET_ATTR_ADT] = { .type = NLA_NESTED }, +}; + +static int +call_ad(struct sk_buff *skb, struct ip_set *set, + struct nlattr *tb[], enum ipset_adt adt, + u32 flags, bool use_lineno) +{ + int ret, retried = 0; + u32 lineno = 0; + bool eexist = flags & IPSET_FLAG_EXIST; + + do { + write_lock_bh(&set->lock); + ret = set->variant->uadt(set, tb, adt, &lineno, flags); + write_unlock_bh(&set->lock); + } while (ret == -EAGAIN && + set->variant->resize && + (ret = set->variant->resize(set, retried++)) == 0); + + if (!ret || (ret == -IPSET_ERR_EXIST && eexist)) + return 0; + if (lineno && use_lineno) { + /* Error in restore/batch mode: send back lineno */ + struct nlmsghdr *nlh = nlmsg_hdr(skb); + int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); + struct nlattr *cda[IPSET_ATTR_CMD_MAX+1]; + struct nlattr *cmdattr = (void *)nlh + min_len; + u32 *errline; + + nla_parse(cda, IPSET_ATTR_CMD_MAX, + cmdattr, nlh->nlmsg_len - min_len, + ip_set_adt_policy); + + errline = nla_data(cda[IPSET_ATTR_LINENO]); + + *errline = lineno; + } + + return ret; +} + +static int +ip_set_uadd(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const attr[]) +{ + struct ip_set *set; + struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {}; + const struct nlattr *nla; + u32 flags = flag_exist(nlh); + bool use_lineno; + int ret = 0; + + if (unlikely(protocol_failed(attr) || + attr[IPSET_ATTR_SETNAME] == NULL || + !((attr[IPSET_ATTR_DATA] != NULL) ^ + (attr[IPSET_ATTR_ADT] != NULL)) || + (attr[IPSET_ATTR_DATA] != NULL && + !flag_nested(attr[IPSET_ATTR_DATA])) || + (attr[IPSET_ATTR_ADT] != NULL && + (!flag_nested(attr[IPSET_ATTR_ADT]) || + attr[IPSET_ATTR_LINENO] == NULL)))) + return -IPSET_ERR_PROTOCOL; + + set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); + if (set == NULL) + return -ENOENT; + + use_lineno = !!attr[IPSET_ATTR_LINENO]; + if (attr[IPSET_ATTR_DATA]) { + if (nla_parse(tb, IPSET_ATTR_ADT_MAX, + nla_data(attr[IPSET_ATTR_DATA]), + nla_len(attr[IPSET_ATTR_DATA]), + set->type->adt_policy)) + return -IPSET_ERR_PROTOCOL; + ret = call_ad(skb, set, tb, IPSET_ADD, flags, use_lineno); + } else { + int nla_rem; + + nla_for_each_nested(nla, attr[IPSET_ATTR_ADT], nla_rem) { + memset(tb, 0, sizeof(tb)); + if (nla_type(nla) != IPSET_ATTR_DATA || + !flag_nested(nla) || + nla_parse(tb, IPSET_ATTR_ADT_MAX, + nla_data(nla), nla_len(nla), + set->type->adt_policy)) + return -IPSET_ERR_PROTOCOL; + ret = call_ad(skb, set, tb, IPSET_ADD, + flags, use_lineno); + if (ret < 0) + return ret; + } + } + return ret; +} + +static int +ip_set_udel(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const attr[]) +{ + struct ip_set *set; + struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {}; + const struct nlattr *nla; + u32 flags = flag_exist(nlh); + bool use_lineno; + int ret = 0; + + if (unlikely(protocol_failed(attr) || + attr[IPSET_ATTR_SETNAME] == NULL || + !((attr[IPSET_ATTR_DATA] != NULL) ^ + (attr[IPSET_ATTR_ADT] != NULL)) || + (attr[IPSET_ATTR_DATA] != NULL && + !flag_nested(attr[IPSET_ATTR_DATA])) || + (attr[IPSET_ATTR_ADT] != NULL && + (!flag_nested(attr[IPSET_ATTR_ADT]) || + attr[IPSET_ATTR_LINENO] == NULL)))) + return -IPSET_ERR_PROTOCOL; + + set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); + if (set == NULL) + return -ENOENT; + + use_lineno = !!attr[IPSET_ATTR_LINENO]; + if (attr[IPSET_ATTR_DATA]) { + if (nla_parse(tb, IPSET_ATTR_ADT_MAX, + nla_data(attr[IPSET_ATTR_DATA]), + nla_len(attr[IPSET_ATTR_DATA]), + set->type->adt_policy)) + return -IPSET_ERR_PROTOCOL; + ret = call_ad(skb, set, tb, IPSET_DEL, flags, use_lineno); + } else { + int nla_rem; + + nla_for_each_nested(nla, attr[IPSET_ATTR_ADT], nla_rem) { + memset(tb, 0, sizeof(*tb)); + if (nla_type(nla) != IPSET_ATTR_DATA || + !flag_nested(nla) || + nla_parse(tb, IPSET_ATTR_ADT_MAX, + nla_data(nla), nla_len(nla), + set->type->adt_policy)) + return -IPSET_ERR_PROTOCOL; + ret = call_ad(skb, set, tb, IPSET_DEL, + flags, use_lineno); + if (ret < 0) + return ret; + } + } + return ret; +} + +static int +ip_set_utest(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const attr[]) +{ + struct ip_set *set; + struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {}; + int ret = 0; + + if (unlikely(protocol_failed(attr) || + attr[IPSET_ATTR_SETNAME] == NULL || + attr[IPSET_ATTR_DATA] == NULL || + !flag_nested(attr[IPSET_ATTR_DATA]))) + return -IPSET_ERR_PROTOCOL; + + set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); + if (set == NULL) + return -ENOENT; + + if (nla_parse(tb, IPSET_ATTR_ADT_MAX, + nla_data(attr[IPSET_ATTR_DATA]), + nla_len(attr[IPSET_ATTR_DATA]), + set->type->adt_policy)) + return -IPSET_ERR_PROTOCOL; + + read_lock_bh(&set->lock); + ret = set->variant->uadt(set, tb, IPSET_TEST, NULL, 0); + read_unlock_bh(&set->lock); + /* Userspace can't trigger element to be re-added */ + if (ret == -EAGAIN) + ret = 1; + + return ret < 0 ? ret : ret > 0 ? 0 : -IPSET_ERR_EXIST; +} + +/* Get headed data of a set */ + +static int +ip_set_header(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const attr[]) +{ + const struct ip_set *set; + struct sk_buff *skb2; + struct nlmsghdr *nlh2; + ip_set_id_t index; + int ret = 0; + + if (unlikely(protocol_failed(attr) || + attr[IPSET_ATTR_SETNAME] == NULL)) + return -IPSET_ERR_PROTOCOL; + + index = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME])); + if (index == IPSET_INVALID_ID) + return -ENOENT; + set = ip_set_list[index]; + + skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (skb2 == NULL) + return -ENOMEM; + + nlh2 = start_msg(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, 0, + IPSET_CMD_HEADER); + if (!nlh2) + goto nlmsg_failure; + NLA_PUT_U8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL); + NLA_PUT_STRING(skb2, IPSET_ATTR_SETNAME, set->name); + NLA_PUT_STRING(skb2, IPSET_ATTR_TYPENAME, set->type->name); + NLA_PUT_U8(skb2, IPSET_ATTR_FAMILY, set->family); + NLA_PUT_U8(skb2, IPSET_ATTR_REVISION, set->type->revision); + nlmsg_end(skb2, nlh2); + + ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); + if (ret < 0) + return ret; + + return 0; + +nla_put_failure: + nlmsg_cancel(skb2, nlh2); +nlmsg_failure: + kfree_skb(skb2); + return -EMSGSIZE; +} + +/* Get type data */ + +static const struct nla_policy ip_set_type_policy[IPSET_ATTR_CMD_MAX + 1] = { + [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, + [IPSET_ATTR_TYPENAME] = { .type = NLA_NUL_STRING, + .len = IPSET_MAXNAMELEN - 1 }, + [IPSET_ATTR_FAMILY] = { .type = NLA_U8 }, +}; + +static int +ip_set_type(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const attr[]) +{ + struct sk_buff *skb2; + struct nlmsghdr *nlh2; + u8 family, min, max; + const char *typename; + int ret = 0; + + if (unlikely(protocol_failed(attr) || + attr[IPSET_ATTR_TYPENAME] == NULL || + attr[IPSET_ATTR_FAMILY] == NULL)) + return -IPSET_ERR_PROTOCOL; + + family = nla_get_u8(attr[IPSET_ATTR_FAMILY]); + typename = nla_data(attr[IPSET_ATTR_TYPENAME]); + ret = find_set_type_minmax(typename, family, &min, &max); + if (ret) + return ret; + + skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (skb2 == NULL) + return -ENOMEM; + + nlh2 = start_msg(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, 0, + IPSET_CMD_TYPE); + if (!nlh2) + goto nlmsg_failure; + NLA_PUT_U8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL); + NLA_PUT_STRING(skb2, IPSET_ATTR_TYPENAME, typename); + NLA_PUT_U8(skb2, IPSET_ATTR_FAMILY, family); + NLA_PUT_U8(skb2, IPSET_ATTR_REVISION, max); + NLA_PUT_U8(skb2, IPSET_ATTR_REVISION_MIN, min); + nlmsg_end(skb2, nlh2); + + pr_debug("Send TYPE, nlmsg_len: %u\n", nlh2->nlmsg_len); + ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); + if (ret < 0) + return ret; + + return 0; + +nla_put_failure: + nlmsg_cancel(skb2, nlh2); +nlmsg_failure: + kfree_skb(skb2); + return -EMSGSIZE; +} + +/* Get protocol version */ + +static const struct nla_policy +ip_set_protocol_policy[IPSET_ATTR_CMD_MAX + 1] = { + [IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 }, +}; + +static int +ip_set_protocol(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const attr[]) +{ + struct sk_buff *skb2; + struct nlmsghdr *nlh2; + int ret = 0; + + if (unlikely(attr[IPSET_ATTR_PROTOCOL] == NULL)) + return -IPSET_ERR_PROTOCOL; + + skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (skb2 == NULL) + return -ENOMEM; + + nlh2 = start_msg(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, 0, + IPSET_CMD_PROTOCOL); + if (!nlh2) + goto nlmsg_failure; + NLA_PUT_U8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL); + nlmsg_end(skb2, nlh2); + + ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); + if (ret < 0) + return ret; + + return 0; + +nla_put_failure: + nlmsg_cancel(skb2, nlh2); +nlmsg_failure: + kfree_skb(skb2); + return -EMSGSIZE; +} + +static const struct nfnl_callback ip_set_netlink_subsys_cb[IPSET_MSG_MAX] = { + [IPSET_CMD_CREATE] = { + .call = ip_set_create, + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_create_policy, + }, + [IPSET_CMD_DESTROY] = { + .call = ip_set_destroy, + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_setname_policy, + }, + [IPSET_CMD_FLUSH] = { + .call = ip_set_flush, + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_setname_policy, + }, + [IPSET_CMD_RENAME] = { + .call = ip_set_rename, + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_setname2_policy, + }, + [IPSET_CMD_SWAP] = { + .call = ip_set_swap, + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_setname2_policy, + }, + [IPSET_CMD_LIST] = { + .call = ip_set_dump, + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_setname_policy, + }, + [IPSET_CMD_SAVE] = { + .call = ip_set_dump, + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_setname_policy, + }, + [IPSET_CMD_ADD] = { + .call = ip_set_uadd, + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_adt_policy, + }, + [IPSET_CMD_DEL] = { + .call = ip_set_udel, + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_adt_policy, + }, + [IPSET_CMD_TEST] = { + .call = ip_set_utest, + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_adt_policy, + }, + [IPSET_CMD_HEADER] = { + .call = ip_set_header, + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_setname_policy, + }, + [IPSET_CMD_TYPE] = { + .call = ip_set_type, + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_type_policy, + }, + [IPSET_CMD_PROTOCOL] = { + .call = ip_set_protocol, + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_protocol_policy, + }, +}; + +static struct nfnetlink_subsystem ip_set_netlink_subsys __read_mostly = { + .name = "ip_set", + .subsys_id = NFNL_SUBSYS_IPSET, + .cb_count = IPSET_MSG_MAX, + .cb = ip_set_netlink_subsys_cb, +}; + +/* Interface to iptables/ip6tables */ + +static int +ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) +{ + unsigned *op; + void *data; + int copylen = *len, ret = 0; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (optval != SO_IP_SET) + return -EBADF; + if (*len < sizeof(unsigned)) + return -EINVAL; + + data = vmalloc(*len); + if (!data) + return -ENOMEM; + if (copy_from_user(data, user, *len) != 0) { + ret = -EFAULT; + goto done; + } + op = (unsigned *) data; + + if (*op < IP_SET_OP_VERSION) { + /* Check the version at the beginning of operations */ + struct ip_set_req_version *req_version = data; + if (req_version->version != IPSET_PROTOCOL) { + ret = -EPROTO; + goto done; + } + } + + switch (*op) { + case IP_SET_OP_VERSION: { + struct ip_set_req_version *req_version = data; + + if (*len != sizeof(struct ip_set_req_version)) { + ret = -EINVAL; + goto done; + } + + req_version->version = IPSET_PROTOCOL; + ret = copy_to_user(user, req_version, + sizeof(struct ip_set_req_version)); + goto done; + } + case IP_SET_OP_GET_BYNAME: { + struct ip_set_req_get_set *req_get = data; + + if (*len != sizeof(struct ip_set_req_get_set)) { + ret = -EINVAL; + goto done; + } + req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0'; + nfnl_lock(); + req_get->set.index = find_set_id(req_get->set.name); + nfnl_unlock(); + goto copy; + } + case IP_SET_OP_GET_BYINDEX: { + struct ip_set_req_get_set *req_get = data; + + if (*len != sizeof(struct ip_set_req_get_set) || + req_get->set.index >= ip_set_max) { + ret = -EINVAL; + goto done; + } + nfnl_lock(); + strncpy(req_get->set.name, + ip_set_list[req_get->set.index] + ? ip_set_list[req_get->set.index]->name : "", + IPSET_MAXNAMELEN); + nfnl_unlock(); + goto copy; + } + default: + ret = -EBADMSG; + goto done; + } /* end of switch(op) */ + +copy: + ret = copy_to_user(user, data, copylen); + +done: + vfree(data); + if (ret > 0) + ret = 0; + return ret; +} + +static struct nf_sockopt_ops so_set __read_mostly = { + .pf = PF_INET, + .get_optmin = SO_IP_SET, + .get_optmax = SO_IP_SET + 1, + .get = &ip_set_sockfn_get, + .owner = THIS_MODULE, +}; + +static int __init +ip_set_init(void) +{ + int ret; + + if (max_sets) + ip_set_max = max_sets; + if (ip_set_max >= IPSET_INVALID_ID) + ip_set_max = IPSET_INVALID_ID - 1; + + ip_set_list = kzalloc(sizeof(struct ip_set *) * ip_set_max, + GFP_KERNEL); + if (!ip_set_list) { + pr_err("ip_set: Unable to create ip_set_list\n"); + return -ENOMEM; + } + + ret = nfnetlink_subsys_register(&ip_set_netlink_subsys); + if (ret != 0) { + pr_err("ip_set: cannot register with nfnetlink.\n"); + kfree(ip_set_list); + return ret; + } + ret = nf_register_sockopt(&so_set); + if (ret != 0) { + pr_err("SO_SET registry failed: %d\n", ret); + nfnetlink_subsys_unregister(&ip_set_netlink_subsys); + kfree(ip_set_list); + return ret; + } + + pr_notice("ip_set: protocol %u\n", IPSET_PROTOCOL); + return 0; +} + +static void __exit +ip_set_fini(void) +{ + /* There can't be any existing set */ + nf_unregister_sockopt(&so_set); + nfnetlink_subsys_unregister(&ip_set_netlink_subsys); + kfree(ip_set_list); + pr_debug("these are the famous last words\n"); +} + +module_init(ip_set_init); +module_exit(ip_set_fini); diff --git a/net/netfilter/ipset/ip_set_getport.c b/net/netfilter/ipset/ip_set_getport.c new file mode 100644 index 000000000000..76737bba28ee --- /dev/null +++ b/net/netfilter/ipset/ip_set_getport.c @@ -0,0 +1,136 @@ +/* Copyright (C) 2003-2011 Jozsef Kadlecsik + * + * 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. + */ + +/* Get Layer-4 data from the packets */ + +#include +#include +#include +#include +#include +#include + +#include + +/* We must handle non-linear skbs */ +static bool +get_port(const struct sk_buff *skb, int protocol, unsigned int protooff, + bool src, __be16 *port, u8 *proto) +{ + switch (protocol) { + case IPPROTO_TCP: { + struct tcphdr _tcph; + const struct tcphdr *th; + + th = skb_header_pointer(skb, protooff, sizeof(_tcph), &_tcph); + if (th == NULL) + /* No choice either */ + return false; + + *port = src ? th->source : th->dest; + break; + } + case IPPROTO_UDP: { + struct udphdr _udph; + const struct udphdr *uh; + + uh = skb_header_pointer(skb, protooff, sizeof(_udph), &_udph); + if (uh == NULL) + /* No choice either */ + return false; + + *port = src ? uh->source : uh->dest; + break; + } + case IPPROTO_ICMP: { + struct icmphdr _ich; + const struct icmphdr *ic; + + ic = skb_header_pointer(skb, protooff, sizeof(_ich), &_ich); + if (ic == NULL) + return false; + + *port = (__force __be16)htons((ic->type << 8) | ic->code); + break; + } + case IPPROTO_ICMPV6: { + struct icmp6hdr _ich; + const struct icmp6hdr *ic; + + ic = skb_header_pointer(skb, protooff, sizeof(_ich), &_ich); + if (ic == NULL) + return false; + + *port = (__force __be16) + htons((ic->icmp6_type << 8) | ic->icmp6_code); + break; + } + default: + break; + } + *proto = protocol; + + return true; +} + +bool +ip_set_get_ip4_port(const struct sk_buff *skb, bool src, + __be16 *port, u8 *proto) +{ + const struct iphdr *iph = ip_hdr(skb); + unsigned int protooff = ip_hdrlen(skb); + int protocol = iph->protocol; + + /* See comments at tcp_match in ip_tables.c */ + if (protocol <= 0 || (ntohs(iph->frag_off) & IP_OFFSET)) + return false; + + return get_port(skb, protocol, protooff, src, port, proto); +} +EXPORT_SYMBOL_GPL(ip_set_get_ip4_port); + +bool +ip_set_get_ip6_port(const struct sk_buff *skb, bool src, + __be16 *port, u8 *proto) +{ + unsigned int protooff = 0; + int protocol; + unsigned short fragoff; + + protocol = ipv6_find_hdr(skb, &protooff, -1, &fragoff); + if (protocol <= 0 || fragoff) + return false; + + return get_port(skb, protocol, protooff, src, port, proto); +} +EXPORT_SYMBOL_GPL(ip_set_get_ip6_port); + +bool +ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port) +{ + bool ret; + u8 proto; + + switch (pf) { + case AF_INET: + ret = ip_set_get_ip4_port(skb, src, port, &proto); + case AF_INET6: + ret = ip_set_get_ip6_port(skb, src, port, &proto); + default: + return false; + } + if (!ret) + return ret; + switch (proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_GPL(ip_set_get_ip_port); diff --git a/net/netfilter/ipset/pfxlen.c b/net/netfilter/ipset/pfxlen.c new file mode 100644 index 000000000000..23f8c8162214 --- /dev/null +++ b/net/netfilter/ipset/pfxlen.c @@ -0,0 +1,291 @@ +#include + +/* + * Prefixlen maps for fast conversions, by Jan Engelhardt. + */ + +#define E(a, b, c, d) \ + {.ip6 = { \ + __constant_htonl(a), __constant_htonl(b), \ + __constant_htonl(c), __constant_htonl(d), \ + } } + +/* + * This table works for both IPv4 and IPv6; + * just use prefixlen_netmask_map[prefixlength].ip. + */ +const union nf_inet_addr ip_set_netmask_map[] = { + E(0x00000000, 0x00000000, 0x00000000, 0x00000000), + E(0x80000000, 0x00000000, 0x00000000, 0x00000000), + E(0xC0000000, 0x00000000, 0x00000000, 0x00000000), + E(0xE0000000, 0x00000000, 0x00000000, 0x00000000), + E(0xF0000000, 0x00000000, 0x00000000, 0x00000000), + E(0xF8000000, 0x00000000, 0x00000000, 0x00000000), + E(0xFC000000, 0x00000000, 0x00000000, 0x00000000), + E(0xFE000000, 0x00000000, 0x00000000, 0x00000000), + E(0xFF000000, 0x00000000, 0x00000000, 0x00000000), + E(0xFF800000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFC00000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFE00000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFF00000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFF80000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFC0000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFE0000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFF0000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFF8000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFC000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFE000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFF000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFF800, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFC00, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFE00, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFF00, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFF80, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFFC0, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFFE0, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFFF0, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFFF8, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFFFC, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFFFE, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0x80000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xC0000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xE0000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xF0000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xF8000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFC000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFE000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFF800000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFC00000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFE00000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFF00000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFF80000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFC0000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFE0000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFF0000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFF8000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFC000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFE000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFF000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFF800, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFC00, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFE00, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFF00, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFF80, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFC0, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFE0, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFF0, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFF8, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFC, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFE, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0x80000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x80000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF), +}; +EXPORT_SYMBOL_GPL(ip_set_netmask_map); + +#undef E +#define E(a, b, c, d) \ + {.ip6 = { (__force __be32) a, (__force __be32) b, \ + (__force __be32) c, (__force __be32) d, \ + } } + +/* + * This table works for both IPv4 and IPv6; + * just use prefixlen_hostmask_map[prefixlength].ip. + */ +const union nf_inet_addr ip_set_hostmask_map[] = { + E(0x00000000, 0x00000000, 0x00000000, 0x00000000), + E(0x80000000, 0x00000000, 0x00000000, 0x00000000), + E(0xC0000000, 0x00000000, 0x00000000, 0x00000000), + E(0xE0000000, 0x00000000, 0x00000000, 0x00000000), + E(0xF0000000, 0x00000000, 0x00000000, 0x00000000), + E(0xF8000000, 0x00000000, 0x00000000, 0x00000000), + E(0xFC000000, 0x00000000, 0x00000000, 0x00000000), + E(0xFE000000, 0x00000000, 0x00000000, 0x00000000), + E(0xFF000000, 0x00000000, 0x00000000, 0x00000000), + E(0xFF800000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFC00000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFE00000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFF00000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFF80000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFC0000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFE0000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFF0000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFF8000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFC000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFE000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFF000, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFF800, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFC00, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFE00, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFF00, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFF80, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFFC0, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFFE0, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFFF0, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFFF8, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFFFC, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFFFE, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0x80000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xC0000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xE0000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xF0000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xF8000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFC000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFE000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFF800000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFC00000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFE00000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFF00000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFF80000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFC0000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFE0000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFF0000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFF8000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFC000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFE000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFF000, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFF800, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFC00, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFE00, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFF00, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFF80, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFC0, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFE0, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFF0, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFF8, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFC, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFE, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0x80000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x80000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xC0000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xE0000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF0000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xF8000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFC000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFE000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF800000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFC00000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE00000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF00000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFF80000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFC0000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFE0000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF8000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFC000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF000), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF800), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFC00), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFE00), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF80), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFC0), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFE0), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF0), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFF8), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFC), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE), + E(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF), +}; +EXPORT_SYMBOL_GPL(ip_set_hostmask_map); -- cgit v1.2.3 From 72205fc68bd13109576aa6c4c12c740962d28a6c Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 1 Feb 2011 15:33:17 +0100 Subject: netfilter: ipset: bitmap:ip set type support The module implements the bitmap:ip set type in two flavours, without and with timeout support. In this kind of set one can store IPv4 addresses (or network addresses) from a given range. In order not to waste memory, the timeout version does not rely on the kernel timer for every element to be timed out but on garbage collection. All set types use this mechanism. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/ipset/ip_set_bitmap.h | 31 ++ include/linux/netfilter/ipset/ip_set_timeout.h | 127 ++++++ net/netfilter/ipset/Kconfig | 9 + net/netfilter/ipset/Makefile | 3 + net/netfilter/ipset/ip_set_bitmap_ip.c | 588 +++++++++++++++++++++++++ 5 files changed, 758 insertions(+) create mode 100644 include/linux/netfilter/ipset/ip_set_bitmap.h create mode 100644 include/linux/netfilter/ipset/ip_set_timeout.h create mode 100644 net/netfilter/ipset/ip_set_bitmap_ip.c (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set_bitmap.h b/include/linux/netfilter/ipset/ip_set_bitmap.h new file mode 100644 index 000000000000..61a9e8746c83 --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set_bitmap.h @@ -0,0 +1,31 @@ +#ifndef __IP_SET_BITMAP_H +#define __IP_SET_BITMAP_H + +/* Bitmap type specific error codes */ +enum { + /* The element is out of the range of the set */ + IPSET_ERR_BITMAP_RANGE = IPSET_ERR_TYPE_SPECIFIC, + /* The range exceeds the size limit of the set type */ + IPSET_ERR_BITMAP_RANGE_SIZE, +}; + +#ifdef __KERNEL__ +#define IPSET_BITMAP_MAX_RANGE 0x0000FFFF + +/* Common functions */ + +static inline u32 +range_to_mask(u32 from, u32 to, u8 *bits) +{ + u32 mask = 0xFFFFFFFE; + + *bits = 32; + while (--(*bits) > 0 && mask && (to & mask) != from) + mask <<= 1; + + return mask; +} + +#endif /* __KERNEL__ */ + +#endif /* __IP_SET_BITMAP_H */ diff --git a/include/linux/netfilter/ipset/ip_set_timeout.h b/include/linux/netfilter/ipset/ip_set_timeout.h new file mode 100644 index 000000000000..9f30c5f2ec1c --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set_timeout.h @@ -0,0 +1,127 @@ +#ifndef _IP_SET_TIMEOUT_H +#define _IP_SET_TIMEOUT_H + +/* Copyright (C) 2003-2011 Jozsef Kadlecsik + * + * 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. + */ + +#ifdef __KERNEL__ + +/* How often should the gc be run by default */ +#define IPSET_GC_TIME (3 * 60) + +/* Timeout period depending on the timeout value of the given set */ +#define IPSET_GC_PERIOD(timeout) \ + ((timeout/3) ? min_t(u32, (timeout)/3, IPSET_GC_TIME) : 1) + +/* Set is defined without timeout support: timeout value may be 0 */ +#define IPSET_NO_TIMEOUT UINT_MAX + +#define with_timeout(timeout) ((timeout) != IPSET_NO_TIMEOUT) + +static inline unsigned int +ip_set_timeout_uget(struct nlattr *tb) +{ + unsigned int timeout = ip_set_get_h32(tb); + + /* Userspace supplied TIMEOUT parameter: adjust crazy size */ + return timeout == IPSET_NO_TIMEOUT ? IPSET_NO_TIMEOUT - 1 : timeout; +} + +#ifdef IP_SET_BITMAP_TIMEOUT + +/* Bitmap specific timeout constants and macros for the entries */ + +/* Bitmap entry is unset */ +#define IPSET_ELEM_UNSET 0 +/* Bitmap entry is set with no timeout value */ +#define IPSET_ELEM_PERMANENT (UINT_MAX/2) + +static inline bool +ip_set_timeout_test(unsigned long timeout) +{ + return timeout != IPSET_ELEM_UNSET && + (timeout == IPSET_ELEM_PERMANENT || + time_after(timeout, jiffies)); +} + +static inline bool +ip_set_timeout_expired(unsigned long timeout) +{ + return timeout != IPSET_ELEM_UNSET && + timeout != IPSET_ELEM_PERMANENT && + time_before(timeout, jiffies); +} + +static inline unsigned long +ip_set_timeout_set(u32 timeout) +{ + unsigned long t; + + if (!timeout) + return IPSET_ELEM_PERMANENT; + + t = timeout * HZ + jiffies; + if (t == IPSET_ELEM_UNSET || t == IPSET_ELEM_PERMANENT) + /* Bingo! */ + t++; + + return t; +} + +static inline u32 +ip_set_timeout_get(unsigned long timeout) +{ + return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ; +} + +#else + +/* Hash specific timeout constants and macros for the entries */ + +/* Hash entry is set with no timeout value */ +#define IPSET_ELEM_PERMANENT 0 + +static inline bool +ip_set_timeout_test(unsigned long timeout) +{ + return timeout == IPSET_ELEM_PERMANENT || + time_after(timeout, jiffies); +} + +static inline bool +ip_set_timeout_expired(unsigned long timeout) +{ + return timeout != IPSET_ELEM_PERMANENT && + time_before(timeout, jiffies); +} + +static inline unsigned long +ip_set_timeout_set(u32 timeout) +{ + unsigned long t; + + if (!timeout) + return IPSET_ELEM_PERMANENT; + + t = timeout * HZ + jiffies; + if (t == IPSET_ELEM_PERMANENT) + /* Bingo! :-) */ + t++; + + return t; +} + +static inline u32 +ip_set_timeout_get(unsigned long timeout) +{ + return timeout == IPSET_ELEM_PERMANENT ? 0 : (timeout - jiffies)/HZ; +} +#endif /* ! IP_SET_BITMAP_TIMEOUT */ + +#endif /* __KERNEL__ */ + +#endif /* _IP_SET_TIMEOUT_H */ diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig index 5ade156dd470..b63a8eea183c 100644 --- a/net/netfilter/ipset/Kconfig +++ b/net/netfilter/ipset/Kconfig @@ -23,4 +23,13 @@ config IP_SET_MAX The value can be overriden by the 'max_sets' module parameter of the 'ip_set' module. +config IP_SET_BITMAP_IP + tristate "bitmap:ip set support" + depends on IP_SET + help + This option adds the bitmap:ip set type support, by which one + can store IPv4 addresses (or network addresse) from a range. + + To compile it as a module, choose M here. If unsure, say N. + endif # IP_SET diff --git a/net/netfilter/ipset/Makefile b/net/netfilter/ipset/Makefile index 910cd425c067..ea1c85e28af1 100644 --- a/net/netfilter/ipset/Makefile +++ b/net/netfilter/ipset/Makefile @@ -6,3 +6,6 @@ ip_set-y := ip_set_core.o ip_set_getport.o pfxlen.o # ipset core obj-$(CONFIG_IP_SET) += ip_set.o + +# bitmap types +obj-$(CONFIG_IP_SET_BITMAP_IP) += ip_set_bitmap_ip.o diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c new file mode 100644 index 000000000000..047440085509 --- /dev/null +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c @@ -0,0 +1,588 @@ +/* Copyright (C) 2000-2002 Joakim Axelsson + * Patrick Schaaf + * Copyright (C) 2003-2011 Jozsef Kadlecsik + * + * 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. + */ + +/* Kernel module implementing an IP set type: the bitmap:ip type */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#define IP_SET_BITMAP_TIMEOUT +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("bitmap:ip type of IP sets"); +MODULE_ALIAS("ip_set_bitmap:ip"); + +/* Type structure */ +struct bitmap_ip { + void *members; /* the set members */ + u32 first_ip; /* host byte order, included in range */ + u32 last_ip; /* host byte order, included in range */ + u32 elements; /* number of max elements in the set */ + u32 hosts; /* number of hosts in a subnet */ + size_t memsize; /* members size */ + u8 netmask; /* subnet netmask */ + u32 timeout; /* timeout parameter */ + struct timer_list gc; /* garbage collection */ +}; + +/* Base variant */ + +static inline u32 +ip_to_id(const struct bitmap_ip *m, u32 ip) +{ + return ((ip & ip_set_hostmask(m->netmask)) - m->first_ip)/m->hosts; +} + +static int +bitmap_ip_test(struct ip_set *set, void *value, u32 timeout) +{ + const struct bitmap_ip *map = set->data; + u16 id = *(u16 *)value; + + return !!test_bit(id, map->members); +} + +static int +bitmap_ip_add(struct ip_set *set, void *value, u32 timeout) +{ + struct bitmap_ip *map = set->data; + u16 id = *(u16 *)value; + + if (test_and_set_bit(id, map->members)) + return -IPSET_ERR_EXIST; + + return 0; +} + +static int +bitmap_ip_del(struct ip_set *set, void *value, u32 timeout) +{ + struct bitmap_ip *map = set->data; + u16 id = *(u16 *)value; + + if (!test_and_clear_bit(id, map->members)) + return -IPSET_ERR_EXIST; + + return 0; +} + +static int +bitmap_ip_list(const struct ip_set *set, + struct sk_buff *skb, struct netlink_callback *cb) +{ + const struct bitmap_ip *map = set->data; + struct nlattr *atd, *nested; + u32 id, first = cb->args[2]; + + atd = ipset_nest_start(skb, IPSET_ATTR_ADT); + if (!atd) + return -EMSGSIZE; + for (; cb->args[2] < map->elements; cb->args[2]++) { + id = cb->args[2]; + if (!test_bit(id, map->members)) + continue; + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) { + if (id == first) { + nla_nest_cancel(skb, atd); + return -EMSGSIZE; + } else + goto nla_put_failure; + } + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, + htonl(map->first_ip + id * map->hosts)); + ipset_nest_end(skb, nested); + } + ipset_nest_end(skb, atd); + /* Set listing finished */ + cb->args[2] = 0; + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nested); + ipset_nest_end(skb, atd); + if (unlikely(id == first)) { + cb->args[2] = 0; + return -EMSGSIZE; + } + return 0; +} + +/* Timeout variant */ + +static int +bitmap_ip_ttest(struct ip_set *set, void *value, u32 timeout) +{ + const struct bitmap_ip *map = set->data; + const unsigned long *members = map->members; + u16 id = *(u16 *)value; + + return ip_set_timeout_test(members[id]); +} + +static int +bitmap_ip_tadd(struct ip_set *set, void *value, u32 timeout) +{ + struct bitmap_ip *map = set->data; + unsigned long *members = map->members; + u16 id = *(u16 *)value; + + if (ip_set_timeout_test(members[id])) + return -IPSET_ERR_EXIST; + + members[id] = ip_set_timeout_set(timeout); + + return 0; +} + +static int +bitmap_ip_tdel(struct ip_set *set, void *value, u32 timeout) +{ + struct bitmap_ip *map = set->data; + unsigned long *members = map->members; + u16 id = *(u16 *)value; + int ret = -IPSET_ERR_EXIST; + + if (ip_set_timeout_test(members[id])) + ret = 0; + + members[id] = IPSET_ELEM_UNSET; + return ret; +} + +static int +bitmap_ip_tlist(const struct ip_set *set, + struct sk_buff *skb, struct netlink_callback *cb) +{ + const struct bitmap_ip *map = set->data; + struct nlattr *adt, *nested; + u32 id, first = cb->args[2]; + const unsigned long *members = map->members; + + adt = ipset_nest_start(skb, IPSET_ATTR_ADT); + if (!adt) + return -EMSGSIZE; + for (; cb->args[2] < map->elements; cb->args[2]++) { + id = cb->args[2]; + if (!ip_set_timeout_test(members[id])) + continue; + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) { + if (id == first) { + nla_nest_cancel(skb, adt); + return -EMSGSIZE; + } else + goto nla_put_failure; + } + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, + htonl(map->first_ip + id * map->hosts)); + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(members[id]))); + ipset_nest_end(skb, nested); + } + ipset_nest_end(skb, adt); + + /* Set listing finished */ + cb->args[2] = 0; + + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nested); + ipset_nest_end(skb, adt); + if (unlikely(id == first)) { + cb->args[2] = 0; + return -EMSGSIZE; + } + return 0; +} + +static int +bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) +{ + struct bitmap_ip *map = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + u32 ip; + + ip = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC)); + if (ip < map->first_ip || ip > map->last_ip) + return -IPSET_ERR_BITMAP_RANGE; + + ip = ip_to_id(map, ip); + + return adtfn(set, &ip, map->timeout); +} + +static int +bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags) +{ + struct bitmap_ip *map = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + u32 timeout = map->timeout; + u32 ip, ip_to, id; + int ret = 0; + + if (unlikely(!tb[IPSET_ATTR_IP] || + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip); + if (ret) + return ret; + + if (ip < map->first_ip || ip > map->last_ip) + return -IPSET_ERR_BITMAP_RANGE; + + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!with_timeout(map->timeout)) + return -IPSET_ERR_TIMEOUT; + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + } + + if (adt == IPSET_TEST) { + id = ip_to_id(map, ip); + return adtfn(set, &id, timeout); + } + + if (tb[IPSET_ATTR_IP_TO]) { + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); + if (ret) + return ret; + if (ip > ip_to) { + swap(ip, ip_to); + if (ip < map->first_ip) + return -IPSET_ERR_BITMAP_RANGE; + } + } else if (tb[IPSET_ATTR_CIDR]) { + u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); + + if (cidr > 32) + return -IPSET_ERR_INVALID_CIDR; + ip &= ip_set_hostmask(cidr); + ip_to = ip | ~ip_set_hostmask(cidr); + } else + ip_to = ip; + + if (ip_to > map->last_ip) + return -IPSET_ERR_BITMAP_RANGE; + + for (; !before(ip_to, ip); ip += map->hosts) { + id = ip_to_id(map, ip); + ret = adtfn(set, &id, timeout);; + + if (ret && !ip_set_eexist(ret, flags)) + return ret; + else + ret = 0; + } + return ret; +} + +static void +bitmap_ip_destroy(struct ip_set *set) +{ + struct bitmap_ip *map = set->data; + + if (with_timeout(map->timeout)) + del_timer_sync(&map->gc); + + ip_set_free(map->members); + kfree(map); + + set->data = NULL; +} + +static void +bitmap_ip_flush(struct ip_set *set) +{ + struct bitmap_ip *map = set->data; + + memset(map->members, 0, map->memsize); +} + +static int +bitmap_ip_head(struct ip_set *set, struct sk_buff *skb) +{ + const struct bitmap_ip *map = set->data; + struct nlattr *nested; + + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) + goto nla_put_failure; + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, htonl(map->first_ip)); + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)); + if (map->netmask != 32) + NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, map->netmask); + NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, + htonl(atomic_read(&set->ref) - 1)); + NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, + htonl(sizeof(*map) + map->memsize)); + if (with_timeout(map->timeout)) + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); + ipset_nest_end(skb, nested); + + return 0; +nla_put_failure: + return -EMSGSIZE; +} + +static bool +bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b) +{ + const struct bitmap_ip *x = a->data; + const struct bitmap_ip *y = b->data; + + return x->first_ip == y->first_ip && + x->last_ip == y->last_ip && + x->netmask == y->netmask && + x->timeout == y->timeout; +} + +static const struct ip_set_type_variant bitmap_ip = { + .kadt = bitmap_ip_kadt, + .uadt = bitmap_ip_uadt, + .adt = { + [IPSET_ADD] = bitmap_ip_add, + [IPSET_DEL] = bitmap_ip_del, + [IPSET_TEST] = bitmap_ip_test, + }, + .destroy = bitmap_ip_destroy, + .flush = bitmap_ip_flush, + .head = bitmap_ip_head, + .list = bitmap_ip_list, + .same_set = bitmap_ip_same_set, +}; + +static const struct ip_set_type_variant bitmap_tip = { + .kadt = bitmap_ip_kadt, + .uadt = bitmap_ip_uadt, + .adt = { + [IPSET_ADD] = bitmap_ip_tadd, + [IPSET_DEL] = bitmap_ip_tdel, + [IPSET_TEST] = bitmap_ip_ttest, + }, + .destroy = bitmap_ip_destroy, + .flush = bitmap_ip_flush, + .head = bitmap_ip_head, + .list = bitmap_ip_tlist, + .same_set = bitmap_ip_same_set, +}; + +static void +bitmap_ip_gc(unsigned long ul_set) +{ + struct ip_set *set = (struct ip_set *) ul_set; + struct bitmap_ip *map = set->data; + unsigned long *table = map->members; + u32 id; + + /* We run parallel with other readers (test element) + * but adding/deleting new entries is locked out */ + read_lock_bh(&set->lock); + for (id = 0; id < map->elements; id++) + if (ip_set_timeout_expired(table[id])) + table[id] = IPSET_ELEM_UNSET; + read_unlock_bh(&set->lock); + + map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; + add_timer(&map->gc); +} + +static void +bitmap_ip_gc_init(struct ip_set *set) +{ + struct bitmap_ip *map = set->data; + + init_timer(&map->gc); + map->gc.data = (unsigned long) set; + map->gc.function = bitmap_ip_gc; + map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; + add_timer(&map->gc); +} + +/* Create bitmap:ip type of sets */ + +static bool +init_map_ip(struct ip_set *set, struct bitmap_ip *map, + u32 first_ip, u32 last_ip, + u32 elements, u32 hosts, u8 netmask) +{ + map->members = ip_set_alloc(map->memsize); + if (!map->members) + return false; + map->first_ip = first_ip; + map->last_ip = last_ip; + map->elements = elements; + map->hosts = hosts; + map->netmask = netmask; + map->timeout = IPSET_NO_TIMEOUT; + + set->data = map; + set->family = AF_INET; + + return true; +} + +static int +bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags) +{ + struct bitmap_ip *map; + u32 first_ip, last_ip, hosts, elements; + u8 netmask = 32; + int ret; + + if (unlikely(!tb[IPSET_ATTR_IP] || + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) + return -IPSET_ERR_PROTOCOL; + + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &first_ip); + if (ret) + return ret; + + if (tb[IPSET_ATTR_IP_TO]) { + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &last_ip); + if (ret) + return ret; + if (first_ip > last_ip) { + u32 tmp = first_ip; + + first_ip = last_ip; + last_ip = tmp; + } + } else if (tb[IPSET_ATTR_CIDR]) { + u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); + + if (cidr >= 32) + return -IPSET_ERR_INVALID_CIDR; + last_ip = first_ip | ~ip_set_hostmask(cidr); + } else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_NETMASK]) { + netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]); + + if (netmask > 32) + return -IPSET_ERR_INVALID_NETMASK; + + first_ip &= ip_set_hostmask(netmask); + last_ip |= ~ip_set_hostmask(netmask); + } + + if (netmask == 32) { + hosts = 1; + elements = last_ip - first_ip + 1; + } else { + u8 mask_bits; + u32 mask; + + mask = range_to_mask(first_ip, last_ip, &mask_bits); + + if ((!mask && (first_ip || last_ip != 0xFFFFFFFF)) || + netmask <= mask_bits) + return -IPSET_ERR_BITMAP_RANGE; + + pr_debug("mask_bits %u, netmask %u\n", mask_bits, netmask); + hosts = 2 << (32 - netmask - 1); + elements = 2 << (netmask - mask_bits - 1); + } + if (elements > IPSET_BITMAP_MAX_RANGE + 1) + return -IPSET_ERR_BITMAP_RANGE_SIZE; + + pr_debug("hosts %u, elements %u\n", hosts, elements); + + map = kzalloc(sizeof(*map), GFP_KERNEL); + if (!map) + return -ENOMEM; + + if (tb[IPSET_ATTR_TIMEOUT]) { + map->memsize = elements * sizeof(unsigned long); + + if (!init_map_ip(set, map, first_ip, last_ip, + elements, hosts, netmask)) { + kfree(map); + return -ENOMEM; + } + + map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + set->variant = &bitmap_tip; + + bitmap_ip_gc_init(set); + } else { + map->memsize = bitmap_bytes(0, elements - 1); + + if (!init_map_ip(set, map, first_ip, last_ip, + elements, hosts, netmask)) { + kfree(map); + return -ENOMEM; + } + + set->variant = &bitmap_ip; + } + return 0; +} + +static struct ip_set_type bitmap_ip_type __read_mostly = { + .name = "bitmap:ip", + .protocol = IPSET_PROTOCOL, + .features = IPSET_TYPE_IP, + .dimension = IPSET_DIM_ONE, + .family = AF_INET, + .revision = 0, + .create = bitmap_ip_create, + .create_policy = { + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, + [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, + [IPSET_ATTR_NETMASK] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + }, + .adt_policy = { + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, + [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, + }, + .me = THIS_MODULE, +}; + +static int __init +bitmap_ip_init(void) +{ + return ip_set_type_register(&bitmap_ip_type); +} + +static void __exit +bitmap_ip_fini(void) +{ + ip_set_type_unregister(&bitmap_ip_type); +} + +module_init(bitmap_ip_init); +module_exit(bitmap_ip_fini); -- cgit v1.2.3 From 6c027889696a7a694b0e2f6e3cabadefec7553b6 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 1 Feb 2011 15:38:36 +0100 Subject: netfilter: ipset: hash:ip set type support The module implements the hash:ip type support in four flavours: for IPv4 or IPv6, both without and with timeout support. All the hash types are based on the "array hash" or ahash structure and functions as a good compromise between minimal memory footprint and speed. The hashing uses arrays to resolve clashes. The hash table is resized (doubled) when searching becomes too long. Resizing can be triggered by userspace add commands only and those are serialized by the nfnl mutex. During resizing the set is read-locked, so the only possible concurrent operations are the kernel side readers. Those are protected by RCU locking. Because of the four flavours and the other hash types, the functions are implemented in general forms in the ip_set_ahash.h header file and the real functions are generated before compiling by macro expansion. Thus the dereferencing of low-level functions and void pointer arguments could be avoided: the low-level functions are inlined, the function arguments are pointers of type-specific structures. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/ipset/ip_set_ahash.h | 1074 ++++++++++++++++++++++++++ include/linux/netfilter/ipset/ip_set_hash.h | 26 + net/netfilter/ipset/Kconfig | 10 + net/netfilter/ipset/Makefile | 3 + net/netfilter/ipset/ip_set_hash_ip.c | 467 +++++++++++ 5 files changed, 1580 insertions(+) create mode 100644 include/linux/netfilter/ipset/ip_set_ahash.h create mode 100644 include/linux/netfilter/ipset/ip_set_hash.h create mode 100644 net/netfilter/ipset/ip_set_hash_ip.c (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set_ahash.h b/include/linux/netfilter/ipset/ip_set_ahash.h new file mode 100644 index 000000000000..ec9d9bea1e37 --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set_ahash.h @@ -0,0 +1,1074 @@ +#ifndef _IP_SET_AHASH_H +#define _IP_SET_AHASH_H + +#include +#include +#include + +/* Hashing which uses arrays to resolve clashing. The hash table is resized + * (doubled) when searching becomes too long. + * Internally jhash is used with the assumption that the size of the + * stored data is a multiple of sizeof(u32). If storage supports timeout, + * the timeout field must be the last one in the data structure - that field + * is ignored when computing the hash key. + * + * Readers and resizing + * + * Resizing can be triggered by userspace command only, and those + * are serialized by the nfnl mutex. During resizing the set is + * read-locked, so the only possible concurrent operations are + * the kernel side readers. Those must be protected by proper RCU locking. + */ + +/* Number of elements to store in an initial array block */ +#define AHASH_INIT_SIZE 4 +/* Max number of elements to store in an array block */ +#define AHASH_MAX_SIZE (3*4) + +/* A hash bucket */ +struct hbucket { + void *value; /* the array of the values */ + u8 size; /* size of the array */ + u8 pos; /* position of the first free entry */ +}; + +/* The hash table: the table size stored here in order to make resizing easy */ +struct htable { + u8 htable_bits; /* size of hash table == 2^htable_bits */ + struct hbucket bucket[0]; /* hashtable buckets */ +}; + +#define hbucket(h, i) &((h)->bucket[i]) + +/* Book-keeping of the prefixes added to the set */ +struct ip_set_hash_nets { + u8 cidr; /* the different cidr values in the set */ + u32 nets; /* number of elements per cidr */ +}; + +/* The generic ip_set hash structure */ +struct ip_set_hash { + struct htable *table; /* the hash table */ + u32 maxelem; /* max elements in the hash */ + u32 elements; /* current element (vs timeout) */ + u32 initval; /* random jhash init value */ + u32 timeout; /* timeout value, if enabled */ + struct timer_list gc; /* garbage collection when timeout enabled */ +#ifdef IP_SET_HASH_WITH_NETMASK + u8 netmask; /* netmask value for subnets to store */ +#endif +#ifdef IP_SET_HASH_WITH_NETS + struct ip_set_hash_nets nets[0]; /* book-keeping of prefixes */ +#endif +}; + +/* Compute htable_bits from the user input parameter hashsize */ +static u8 +htable_bits(u32 hashsize) +{ + /* Assume that hashsize == 2^htable_bits */ + u8 bits = fls(hashsize - 1); + if (jhash_size(bits) != hashsize) + /* Round up to the first 2^n value */ + bits = fls(hashsize); + + return bits; +} + +#ifdef IP_SET_HASH_WITH_NETS + +#define SET_HOST_MASK(family) (family == AF_INET ? 32 : 128) + +/* Network cidr size book keeping when the hash stores different + * sized networks */ +static void +add_cidr(struct ip_set_hash *h, u8 cidr, u8 host_mask) +{ + u8 i; + + ++h->nets[cidr-1].nets; + + pr_debug("add_cidr added %u: %u\n", cidr, h->nets[cidr-1].nets); + + if (h->nets[cidr-1].nets > 1) + return; + + /* New cidr size */ + for (i = 0; i < host_mask && h->nets[i].cidr; i++) { + /* Add in increasing prefix order, so larger cidr first */ + if (h->nets[i].cidr < cidr) + swap(h->nets[i].cidr, cidr); + } + if (i < host_mask) + h->nets[i].cidr = cidr; +} + +static void +del_cidr(struct ip_set_hash *h, u8 cidr, u8 host_mask) +{ + u8 i; + + --h->nets[cidr-1].nets; + + pr_debug("del_cidr deleted %u: %u\n", cidr, h->nets[cidr-1].nets); + + if (h->nets[cidr-1].nets != 0) + return; + + /* All entries with this cidr size deleted, so cleanup h->cidr[] */ + for (i = 0; i < host_mask - 1 && h->nets[i].cidr; i++) { + if (h->nets[i].cidr == cidr) + h->nets[i].cidr = cidr = h->nets[i+1].cidr; + } + h->nets[i - 1].cidr = 0; +} +#endif + +/* Destroy the hashtable part of the set */ +static void +ahash_destroy(struct htable *t) +{ + struct hbucket *n; + u32 i; + + for (i = 0; i < jhash_size(t->htable_bits); i++) { + n = hbucket(t, i); + if (n->size) + /* FIXME: use slab cache */ + kfree(n->value); + } + + ip_set_free(t); +} + +/* Calculate the actual memory size of the set data */ +static size_t +ahash_memsize(const struct ip_set_hash *h, size_t dsize, u8 host_mask) +{ + u32 i; + struct htable *t = h->table; + size_t memsize = sizeof(*h) + + sizeof(*t) +#ifdef IP_SET_HASH_WITH_NETS + + sizeof(struct ip_set_hash_nets) * host_mask +#endif + + jhash_size(t->htable_bits) * sizeof(struct hbucket); + + for (i = 0; i < jhash_size(t->htable_bits); i++) + memsize += t->bucket[i].size * dsize; + + return memsize; +} + +/* Flush a hash type of set: destroy all elements */ +static void +ip_set_hash_flush(struct ip_set *set) +{ + struct ip_set_hash *h = set->data; + struct htable *t = h->table; + struct hbucket *n; + u32 i; + + for (i = 0; i < jhash_size(t->htable_bits); i++) { + n = hbucket(t, i); + if (n->size) { + n->size = n->pos = 0; + /* FIXME: use slab cache */ + kfree(n->value); + } + } +#ifdef IP_SET_HASH_WITH_NETS + memset(h->nets, 0, sizeof(struct ip_set_hash_nets) + * SET_HOST_MASK(set->family)); +#endif + h->elements = 0; +} + +/* Destroy a hash type of set */ +static void +ip_set_hash_destroy(struct ip_set *set) +{ + struct ip_set_hash *h = set->data; + + if (with_timeout(h->timeout)) + del_timer_sync(&h->gc); + + ahash_destroy(h->table); + kfree(h); + + set->data = NULL; +} + +#define HKEY(data, initval, htable_bits) \ +(jhash2((u32 *)(data), sizeof(struct type_pf_elem)/sizeof(u32), initval) \ + & jhash_mask(htable_bits)) + +#endif /* _IP_SET_AHASH_H */ + +#define CONCAT(a, b, c) a##b##c +#define TOKEN(a, b, c) CONCAT(a, b, c) + +/* Type/family dependent function prototypes */ + +#define type_pf_data_equal TOKEN(TYPE, PF, _data_equal) +#define type_pf_data_isnull TOKEN(TYPE, PF, _data_isnull) +#define type_pf_data_copy TOKEN(TYPE, PF, _data_copy) +#define type_pf_data_zero_out TOKEN(TYPE, PF, _data_zero_out) +#define type_pf_data_netmask TOKEN(TYPE, PF, _data_netmask) +#define type_pf_data_list TOKEN(TYPE, PF, _data_list) +#define type_pf_data_tlist TOKEN(TYPE, PF, _data_tlist) + +#define type_pf_elem TOKEN(TYPE, PF, _elem) +#define type_pf_telem TOKEN(TYPE, PF, _telem) +#define type_pf_data_timeout TOKEN(TYPE, PF, _data_timeout) +#define type_pf_data_expired TOKEN(TYPE, PF, _data_expired) +#define type_pf_data_timeout_set TOKEN(TYPE, PF, _data_timeout_set) + +#define type_pf_elem_add TOKEN(TYPE, PF, _elem_add) +#define type_pf_add TOKEN(TYPE, PF, _add) +#define type_pf_del TOKEN(TYPE, PF, _del) +#define type_pf_test_cidrs TOKEN(TYPE, PF, _test_cidrs) +#define type_pf_test TOKEN(TYPE, PF, _test) + +#define type_pf_elem_tadd TOKEN(TYPE, PF, _elem_tadd) +#define type_pf_del_telem TOKEN(TYPE, PF, _ahash_del_telem) +#define type_pf_expire TOKEN(TYPE, PF, _expire) +#define type_pf_tadd TOKEN(TYPE, PF, _tadd) +#define type_pf_tdel TOKEN(TYPE, PF, _tdel) +#define type_pf_ttest_cidrs TOKEN(TYPE, PF, _ahash_ttest_cidrs) +#define type_pf_ttest TOKEN(TYPE, PF, _ahash_ttest) + +#define type_pf_resize TOKEN(TYPE, PF, _resize) +#define type_pf_tresize TOKEN(TYPE, PF, _tresize) +#define type_pf_flush ip_set_hash_flush +#define type_pf_destroy ip_set_hash_destroy +#define type_pf_head TOKEN(TYPE, PF, _head) +#define type_pf_list TOKEN(TYPE, PF, _list) +#define type_pf_tlist TOKEN(TYPE, PF, _tlist) +#define type_pf_same_set TOKEN(TYPE, PF, _same_set) +#define type_pf_kadt TOKEN(TYPE, PF, _kadt) +#define type_pf_uadt TOKEN(TYPE, PF, _uadt) +#define type_pf_gc TOKEN(TYPE, PF, _gc) +#define type_pf_gc_init TOKEN(TYPE, PF, _gc_init) +#define type_pf_variant TOKEN(TYPE, PF, _variant) +#define type_pf_tvariant TOKEN(TYPE, PF, _tvariant) + +/* Flavour without timeout */ + +/* Get the ith element from the array block n */ +#define ahash_data(n, i) \ + ((struct type_pf_elem *)((n)->value) + (i)) + +/* Add an element to the hash table when resizing the set: + * we spare the maintenance of the internal counters. */ +static int +type_pf_elem_add(struct hbucket *n, const struct type_pf_elem *value) +{ + if (n->pos >= n->size) { + void *tmp; + + if (n->size >= AHASH_MAX_SIZE) + /* Trigger rehashing */ + return -EAGAIN; + + tmp = kzalloc((n->size + AHASH_INIT_SIZE) + * sizeof(struct type_pf_elem), + GFP_ATOMIC); + if (!tmp) + return -ENOMEM; + if (n->size) { + memcpy(tmp, n->value, + sizeof(struct type_pf_elem) * n->size); + kfree(n->value); + } + n->value = tmp; + n->size += AHASH_INIT_SIZE; + } + type_pf_data_copy(ahash_data(n, n->pos++), value); + return 0; +} + +/* Resize a hash: create a new hash table with doubling the hashsize + * and inserting the elements to it. Repeat until we succeed or + * fail due to memory pressures. */ +static int +type_pf_resize(struct ip_set *set, bool retried) +{ + struct ip_set_hash *h = set->data; + struct htable *t, *orig = h->table; + u8 htable_bits = orig->htable_bits; + const struct type_pf_elem *data; + struct hbucket *n, *m; + u32 i, j; + int ret; + +retry: + ret = 0; + htable_bits++; + pr_debug("attempt to resize set %s from %u to %u, t %p\n", + set->name, orig->htable_bits, htable_bits, orig); + if (!htable_bits) + /* In case we have plenty of memory :-) */ + return -IPSET_ERR_HASH_FULL; + t = ip_set_alloc(sizeof(*t) + + jhash_size(htable_bits) * sizeof(struct hbucket)); + if (!t) + return -ENOMEM; + t->htable_bits = htable_bits; + + read_lock_bh(&set->lock); + for (i = 0; i < jhash_size(orig->htable_bits); i++) { + n = hbucket(orig, i); + for (j = 0; j < n->pos; j++) { + data = ahash_data(n, j); + m = hbucket(t, HKEY(data, h->initval, htable_bits)); + ret = type_pf_elem_add(m, data); + if (ret < 0) { + read_unlock_bh(&set->lock); + ahash_destroy(t); + if (ret == -EAGAIN) + goto retry; + return ret; + } + } + } + + rcu_assign_pointer(h->table, t); + read_unlock_bh(&set->lock); + + /* Give time to other readers of the set */ + synchronize_rcu_bh(); + + pr_debug("set %s resized from %u (%p) to %u (%p)\n", set->name, + orig->htable_bits, orig, t->htable_bits, t); + ahash_destroy(orig); + + return 0; +} + +/* Add an element to a hash and update the internal counters when succeeded, + * otherwise report the proper error code. */ +static int +type_pf_add(struct ip_set *set, void *value, u32 timeout) +{ + struct ip_set_hash *h = set->data; + struct htable *t; + const struct type_pf_elem *d = value; + struct hbucket *n; + int i, ret = 0; + u32 key; + + if (h->elements >= h->maxelem) + return -IPSET_ERR_HASH_FULL; + + rcu_read_lock_bh(); + t = rcu_dereference_bh(h->table); + key = HKEY(value, h->initval, t->htable_bits); + n = hbucket(t, key); + for (i = 0; i < n->pos; i++) + if (type_pf_data_equal(ahash_data(n, i), d)) { + ret = -IPSET_ERR_EXIST; + goto out; + } + + ret = type_pf_elem_add(n, value); + if (ret != 0) + goto out; + +#ifdef IP_SET_HASH_WITH_NETS + add_cidr(h, d->cidr, HOST_MASK); +#endif + h->elements++; +out: + rcu_read_unlock_bh(); + return ret; +} + +/* Delete an element from the hash: swap it with the last element + * and free up space if possible. + */ +static int +type_pf_del(struct ip_set *set, void *value, u32 timeout) +{ + struct ip_set_hash *h = set->data; + struct htable *t = h->table; + const struct type_pf_elem *d = value; + struct hbucket *n; + int i; + struct type_pf_elem *data; + u32 key; + + key = HKEY(value, h->initval, t->htable_bits); + n = hbucket(t, key); + for (i = 0; i < n->pos; i++) { + data = ahash_data(n, i); + if (!type_pf_data_equal(data, d)) + continue; + if (i != n->pos - 1) + /* Not last one */ + type_pf_data_copy(data, ahash_data(n, n->pos - 1)); + + n->pos--; + h->elements--; +#ifdef IP_SET_HASH_WITH_NETS + del_cidr(h, d->cidr, HOST_MASK); +#endif + if (n->pos + AHASH_INIT_SIZE < n->size) { + void *tmp = kzalloc((n->size - AHASH_INIT_SIZE) + * sizeof(struct type_pf_elem), + GFP_ATOMIC); + if (!tmp) + return 0; + n->size -= AHASH_INIT_SIZE; + memcpy(tmp, n->value, + n->size * sizeof(struct type_pf_elem)); + kfree(n->value); + n->value = tmp; + } + return 0; + } + + return -IPSET_ERR_EXIST; +} + +#ifdef IP_SET_HASH_WITH_NETS + +/* Special test function which takes into account the different network + * sizes added to the set */ +static int +type_pf_test_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout) +{ + struct ip_set_hash *h = set->data; + struct htable *t = h->table; + struct hbucket *n; + const struct type_pf_elem *data; + int i, j = 0; + u32 key; + u8 host_mask = SET_HOST_MASK(set->family); + + pr_debug("test by nets\n"); + for (; j < host_mask && h->nets[j].cidr; j++) { + type_pf_data_netmask(d, h->nets[j].cidr); + key = HKEY(d, h->initval, t->htable_bits); + n = hbucket(t, key); + for (i = 0; i < n->pos; i++) { + data = ahash_data(n, i); + if (type_pf_data_equal(data, d)) + return 1; + } + } + return 0; +} +#endif + +/* Test whether the element is added to the set */ +static int +type_pf_test(struct ip_set *set, void *value, u32 timeout) +{ + struct ip_set_hash *h = set->data; + struct htable *t = h->table; + struct type_pf_elem *d = value; + struct hbucket *n; + const struct type_pf_elem *data; + int i; + u32 key; + +#ifdef IP_SET_HASH_WITH_NETS + /* If we test an IP address and not a network address, + * try all possible network sizes */ + if (d->cidr == SET_HOST_MASK(set->family)) + return type_pf_test_cidrs(set, d, timeout); +#endif + + key = HKEY(d, h->initval, t->htable_bits); + n = hbucket(t, key); + for (i = 0; i < n->pos; i++) { + data = ahash_data(n, i); + if (type_pf_data_equal(data, d)) + return 1; + } + return 0; +} + +/* Reply a HEADER request: fill out the header part of the set */ +static int +type_pf_head(struct ip_set *set, struct sk_buff *skb) +{ + const struct ip_set_hash *h = set->data; + struct nlattr *nested; + size_t memsize; + + read_lock_bh(&set->lock); + memsize = ahash_memsize(h, with_timeout(h->timeout) + ? sizeof(struct type_pf_telem) + : sizeof(struct type_pf_elem), + set->family == AF_INET ? 32 : 128); + read_unlock_bh(&set->lock); + + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) + goto nla_put_failure; + NLA_PUT_NET32(skb, IPSET_ATTR_HASHSIZE, + htonl(jhash_size(h->table->htable_bits))); + NLA_PUT_NET32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem)); +#ifdef IP_SET_HASH_WITH_NETMASK + if (h->netmask != HOST_MASK) + NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, h->netmask); +#endif + NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, + htonl(atomic_read(&set->ref) - 1)); + NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)); + if (with_timeout(h->timeout)) + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout)); + ipset_nest_end(skb, nested); + + return 0; +nla_put_failure: + return -EMSGSIZE; +} + +/* Reply a LIST/SAVE request: dump the elements of the specified set */ +static int +type_pf_list(const struct ip_set *set, + struct sk_buff *skb, struct netlink_callback *cb) +{ + const struct ip_set_hash *h = set->data; + const struct htable *t = h->table; + struct nlattr *atd, *nested; + const struct hbucket *n; + const struct type_pf_elem *data; + u32 first = cb->args[2]; + /* We assume that one hash bucket fills into one page */ + void *incomplete; + int i; + + atd = ipset_nest_start(skb, IPSET_ATTR_ADT); + if (!atd) + return -EMSGSIZE; + pr_debug("list hash set %s\n", set->name); + for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) { + incomplete = skb_tail_pointer(skb); + n = hbucket(t, cb->args[2]); + pr_debug("cb->args[2]: %lu, t %p n %p\n", cb->args[2], t, n); + for (i = 0; i < n->pos; i++) { + data = ahash_data(n, i); + pr_debug("list hash %lu hbucket %p i %u, data %p\n", + cb->args[2], n, i, data); + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) { + if (cb->args[2] == first) { + nla_nest_cancel(skb, atd); + return -EMSGSIZE; + } else + goto nla_put_failure; + } + if (type_pf_data_list(skb, data)) + goto nla_put_failure; + ipset_nest_end(skb, nested); + } + } + ipset_nest_end(skb, atd); + /* Set listing finished */ + cb->args[2] = 0; + + return 0; + +nla_put_failure: + nlmsg_trim(skb, incomplete); + ipset_nest_end(skb, atd); + if (unlikely(first == cb->args[2])) { + pr_warning("Can't list set %s: one bucket does not fit into " + "a message. Please report it!\n", set->name); + cb->args[2] = 0; + return -EMSGSIZE; + } + return 0; +} + +static int +type_pf_kadt(struct ip_set *set, const struct sk_buff * skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags); +static int +type_pf_uadt(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags); + +static const struct ip_set_type_variant type_pf_variant = { + .kadt = type_pf_kadt, + .uadt = type_pf_uadt, + .adt = { + [IPSET_ADD] = type_pf_add, + [IPSET_DEL] = type_pf_del, + [IPSET_TEST] = type_pf_test, + }, + .destroy = type_pf_destroy, + .flush = type_pf_flush, + .head = type_pf_head, + .list = type_pf_list, + .resize = type_pf_resize, + .same_set = type_pf_same_set, +}; + +/* Flavour with timeout support */ + +#define ahash_tdata(n, i) \ + (struct type_pf_elem *)((struct type_pf_telem *)((n)->value) + (i)) + +static inline u32 +type_pf_data_timeout(const struct type_pf_elem *data) +{ + const struct type_pf_telem *tdata = + (const struct type_pf_telem *) data; + + return tdata->timeout; +} + +static inline bool +type_pf_data_expired(const struct type_pf_elem *data) +{ + const struct type_pf_telem *tdata = + (const struct type_pf_telem *) data; + + return ip_set_timeout_expired(tdata->timeout); +} + +static inline void +type_pf_data_timeout_set(struct type_pf_elem *data, u32 timeout) +{ + struct type_pf_telem *tdata = (struct type_pf_telem *) data; + + tdata->timeout = ip_set_timeout_set(timeout); +} + +static int +type_pf_elem_tadd(struct hbucket *n, const struct type_pf_elem *value, + u32 timeout) +{ + struct type_pf_elem *data; + + if (n->pos >= n->size) { + void *tmp; + + if (n->size >= AHASH_MAX_SIZE) + /* Trigger rehashing */ + return -EAGAIN; + + tmp = kzalloc((n->size + AHASH_INIT_SIZE) + * sizeof(struct type_pf_telem), + GFP_ATOMIC); + if (!tmp) + return -ENOMEM; + if (n->size) { + memcpy(tmp, n->value, + sizeof(struct type_pf_telem) * n->size); + kfree(n->value); + } + n->value = tmp; + n->size += AHASH_INIT_SIZE; + } + data = ahash_tdata(n, n->pos++); + type_pf_data_copy(data, value); + type_pf_data_timeout_set(data, timeout); + return 0; +} + +/* Delete expired elements from the hashtable */ +static void +type_pf_expire(struct ip_set_hash *h) +{ + struct htable *t = h->table; + struct hbucket *n; + struct type_pf_elem *data; + u32 i; + int j; + + for (i = 0; i < jhash_size(t->htable_bits); i++) { + n = hbucket(t, i); + for (j = 0; j < n->pos; j++) { + data = ahash_tdata(n, j); + if (type_pf_data_expired(data)) { + pr_debug("expired %u/%u\n", i, j); +#ifdef IP_SET_HASH_WITH_NETS + del_cidr(h, data->cidr, HOST_MASK); +#endif + if (j != n->pos - 1) + /* Not last one */ + type_pf_data_copy(data, + ahash_tdata(n, n->pos - 1)); + n->pos--; + h->elements--; + } + } + if (n->pos + AHASH_INIT_SIZE < n->size) { + void *tmp = kzalloc((n->size - AHASH_INIT_SIZE) + * sizeof(struct type_pf_telem), + GFP_ATOMIC); + if (!tmp) + /* Still try to delete expired elements */ + continue; + n->size -= AHASH_INIT_SIZE; + memcpy(tmp, n->value, + n->size * sizeof(struct type_pf_telem)); + kfree(n->value); + n->value = tmp; + } + } +} + +static int +type_pf_tresize(struct ip_set *set, bool retried) +{ + struct ip_set_hash *h = set->data; + struct htable *t, *orig = h->table; + u8 htable_bits = orig->htable_bits; + const struct type_pf_elem *data; + struct hbucket *n, *m; + u32 i, j; + int ret; + + /* Try to cleanup once */ + if (!retried) { + i = h->elements; + write_lock_bh(&set->lock); + type_pf_expire(set->data); + write_unlock_bh(&set->lock); + if (h->elements < i) + return 0; + } + +retry: + ret = 0; + htable_bits++; + if (!htable_bits) + /* In case we have plenty of memory :-) */ + return -IPSET_ERR_HASH_FULL; + t = ip_set_alloc(sizeof(*t) + + jhash_size(htable_bits) * sizeof(struct hbucket)); + if (!t) + return -ENOMEM; + t->htable_bits = htable_bits; + + read_lock_bh(&set->lock); + for (i = 0; i < jhash_size(orig->htable_bits); i++) { + n = hbucket(orig, i); + for (j = 0; j < n->pos; j++) { + data = ahash_tdata(n, j); + m = hbucket(t, HKEY(data, h->initval, htable_bits)); + ret = type_pf_elem_tadd(m, data, + type_pf_data_timeout(data)); + if (ret < 0) { + read_unlock_bh(&set->lock); + ahash_destroy(t); + if (ret == -EAGAIN) + goto retry; + return ret; + } + } + } + + rcu_assign_pointer(h->table, t); + read_unlock_bh(&set->lock); + + /* Give time to other readers of the set */ + synchronize_rcu_bh(); + + ahash_destroy(orig); + + return 0; +} + +static int +type_pf_tadd(struct ip_set *set, void *value, u32 timeout) +{ + struct ip_set_hash *h = set->data; + struct htable *t = h->table; + const struct type_pf_elem *d = value; + struct hbucket *n; + struct type_pf_elem *data; + int ret = 0, i, j = AHASH_MAX_SIZE + 1; + u32 key; + + if (h->elements >= h->maxelem) + /* FIXME: when set is full, we slow down here */ + type_pf_expire(h); + if (h->elements >= h->maxelem) + return -IPSET_ERR_HASH_FULL; + + rcu_read_lock_bh(); + t = rcu_dereference_bh(h->table); + key = HKEY(d, h->initval, t->htable_bits); + n = hbucket(t, key); + for (i = 0; i < n->pos; i++) { + data = ahash_tdata(n, i); + if (type_pf_data_equal(data, d)) { + if (type_pf_data_expired(data)) + j = i; + else { + ret = -IPSET_ERR_EXIST; + goto out; + } + } else if (j == AHASH_MAX_SIZE + 1 && + type_pf_data_expired(data)) + j = i; + } + if (j != AHASH_MAX_SIZE + 1) { + data = ahash_tdata(n, j); +#ifdef IP_SET_HASH_WITH_NETS + del_cidr(h, data->cidr, HOST_MASK); + add_cidr(h, d->cidr, HOST_MASK); +#endif + type_pf_data_copy(data, d); + type_pf_data_timeout_set(data, timeout); + goto out; + } + ret = type_pf_elem_tadd(n, d, timeout); + if (ret != 0) + goto out; + +#ifdef IP_SET_HASH_WITH_NETS + add_cidr(h, d->cidr, HOST_MASK); +#endif + h->elements++; +out: + rcu_read_unlock_bh(); + return ret; +} + +static int +type_pf_tdel(struct ip_set *set, void *value, u32 timeout) +{ + struct ip_set_hash *h = set->data; + struct htable *t = h->table; + const struct type_pf_elem *d = value; + struct hbucket *n; + int i, ret = 0; + struct type_pf_elem *data; + u32 key; + + key = HKEY(value, h->initval, t->htable_bits); + n = hbucket(t, key); + for (i = 0; i < n->pos; i++) { + data = ahash_tdata(n, i); + if (!type_pf_data_equal(data, d)) + continue; + if (type_pf_data_expired(data)) + ret = -IPSET_ERR_EXIST; + if (i != n->pos - 1) + /* Not last one */ + type_pf_data_copy(data, ahash_tdata(n, n->pos - 1)); + + n->pos--; + h->elements--; +#ifdef IP_SET_HASH_WITH_NETS + del_cidr(h, d->cidr, HOST_MASK); +#endif + if (n->pos + AHASH_INIT_SIZE < n->size) { + void *tmp = kzalloc((n->size - AHASH_INIT_SIZE) + * sizeof(struct type_pf_telem), + GFP_ATOMIC); + if (!tmp) + return 0; + n->size -= AHASH_INIT_SIZE; + memcpy(tmp, n->value, + n->size * sizeof(struct type_pf_telem)); + kfree(n->value); + n->value = tmp; + } + return 0; + } + + return -IPSET_ERR_EXIST; +} + +#ifdef IP_SET_HASH_WITH_NETS +static int +type_pf_ttest_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout) +{ + struct ip_set_hash *h = set->data; + struct htable *t = h->table; + struct type_pf_elem *data; + struct hbucket *n; + int i, j = 0; + u32 key; + u8 host_mask = SET_HOST_MASK(set->family); + + for (; j < host_mask && h->nets[j].cidr; j++) { + type_pf_data_netmask(d, h->nets[j].cidr); + key = HKEY(d, h->initval, t->htable_bits); + n = hbucket(t, key); + for (i = 0; i < n->pos; i++) { + data = ahash_tdata(n, i); + if (type_pf_data_equal(data, d)) + return !type_pf_data_expired(data); + } + } + return 0; +} +#endif + +static int +type_pf_ttest(struct ip_set *set, void *value, u32 timeout) +{ + struct ip_set_hash *h = set->data; + struct htable *t = h->table; + struct type_pf_elem *data, *d = value; + struct hbucket *n; + int i; + u32 key; + +#ifdef IP_SET_HASH_WITH_NETS + if (d->cidr == SET_HOST_MASK(set->family)) + return type_pf_ttest_cidrs(set, d, timeout); +#endif + key = HKEY(d, h->initval, t->htable_bits); + n = hbucket(t, key); + for (i = 0; i < n->pos; i++) { + data = ahash_tdata(n, i); + if (type_pf_data_equal(data, d)) + return !type_pf_data_expired(data); + } + return 0; +} + +static int +type_pf_tlist(const struct ip_set *set, + struct sk_buff *skb, struct netlink_callback *cb) +{ + const struct ip_set_hash *h = set->data; + const struct htable *t = h->table; + struct nlattr *atd, *nested; + const struct hbucket *n; + const struct type_pf_elem *data; + u32 first = cb->args[2]; + /* We assume that one hash bucket fills into one page */ + void *incomplete; + int i; + + atd = ipset_nest_start(skb, IPSET_ATTR_ADT); + if (!atd) + return -EMSGSIZE; + for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) { + incomplete = skb_tail_pointer(skb); + n = hbucket(t, cb->args[2]); + for (i = 0; i < n->pos; i++) { + data = ahash_tdata(n, i); + pr_debug("list %p %u\n", n, i); + if (type_pf_data_expired(data)) + continue; + pr_debug("do list %p %u\n", n, i); + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) { + if (cb->args[2] == first) { + nla_nest_cancel(skb, atd); + return -EMSGSIZE; + } else + goto nla_put_failure; + } + if (type_pf_data_tlist(skb, data)) + goto nla_put_failure; + ipset_nest_end(skb, nested); + } + } + ipset_nest_end(skb, atd); + /* Set listing finished */ + cb->args[2] = 0; + + return 0; + +nla_put_failure: + nlmsg_trim(skb, incomplete); + ipset_nest_end(skb, atd); + if (unlikely(first == cb->args[2])) { + pr_warning("Can't list set %s: one bucket does not fit into " + "a message. Please report it!\n", set->name); + cb->args[2] = 0; + return -EMSGSIZE; + } + return 0; +} + +static const struct ip_set_type_variant type_pf_tvariant = { + .kadt = type_pf_kadt, + .uadt = type_pf_uadt, + .adt = { + [IPSET_ADD] = type_pf_tadd, + [IPSET_DEL] = type_pf_tdel, + [IPSET_TEST] = type_pf_ttest, + }, + .destroy = type_pf_destroy, + .flush = type_pf_flush, + .head = type_pf_head, + .list = type_pf_tlist, + .resize = type_pf_tresize, + .same_set = type_pf_same_set, +}; + +static void +type_pf_gc(unsigned long ul_set) +{ + struct ip_set *set = (struct ip_set *) ul_set; + struct ip_set_hash *h = set->data; + + pr_debug("called\n"); + write_lock_bh(&set->lock); + type_pf_expire(h); + write_unlock_bh(&set->lock); + + h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ; + add_timer(&h->gc); +} + +static void +type_pf_gc_init(struct ip_set *set) +{ + struct ip_set_hash *h = set->data; + + init_timer(&h->gc); + h->gc.data = (unsigned long) set; + h->gc.function = type_pf_gc; + h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ; + add_timer(&h->gc); + pr_debug("gc initialized, run in every %u\n", + IPSET_GC_PERIOD(h->timeout)); +} + +#undef type_pf_data_equal +#undef type_pf_data_isnull +#undef type_pf_data_copy +#undef type_pf_data_zero_out +#undef type_pf_data_list +#undef type_pf_data_tlist + +#undef type_pf_elem +#undef type_pf_telem +#undef type_pf_data_timeout +#undef type_pf_data_expired +#undef type_pf_data_netmask +#undef type_pf_data_timeout_set + +#undef type_pf_elem_add +#undef type_pf_add +#undef type_pf_del +#undef type_pf_test_cidrs +#undef type_pf_test + +#undef type_pf_elem_tadd +#undef type_pf_expire +#undef type_pf_tadd +#undef type_pf_tdel +#undef type_pf_ttest_cidrs +#undef type_pf_ttest + +#undef type_pf_resize +#undef type_pf_tresize +#undef type_pf_flush +#undef type_pf_destroy +#undef type_pf_head +#undef type_pf_list +#undef type_pf_tlist +#undef type_pf_same_set +#undef type_pf_kadt +#undef type_pf_uadt +#undef type_pf_gc +#undef type_pf_gc_init +#undef type_pf_variant +#undef type_pf_tvariant diff --git a/include/linux/netfilter/ipset/ip_set_hash.h b/include/linux/netfilter/ipset/ip_set_hash.h new file mode 100644 index 000000000000..b86f15c04524 --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set_hash.h @@ -0,0 +1,26 @@ +#ifndef __IP_SET_HASH_H +#define __IP_SET_HASH_H + +/* Hash type specific error codes */ +enum { + /* Hash is full */ + IPSET_ERR_HASH_FULL = IPSET_ERR_TYPE_SPECIFIC, + /* Null-valued element */ + IPSET_ERR_HASH_ELEM, + /* Invalid protocol */ + IPSET_ERR_INVALID_PROTO, + /* Protocol missing but must be specified */ + IPSET_ERR_MISSING_PROTO, +}; + +#ifdef __KERNEL__ + +#define IPSET_DEFAULT_HASHSIZE 1024 +#define IPSET_MIMINAL_HASHSIZE 64 +#define IPSET_DEFAULT_MAXELEM 65536 +#define IPSET_DEFAULT_PROBES 4 +#define IPSET_DEFAULT_RESIZE 100 + +#endif /* __KERNEL__ */ + +#endif /* __IP_SET_HASH_H */ diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig index f401e9112703..194d89caeb3d 100644 --- a/net/netfilter/ipset/Kconfig +++ b/net/netfilter/ipset/Kconfig @@ -50,4 +50,14 @@ config IP_SET_BITMAP_PORT To compile it as a module, choose M here. If unsure, say N. +config IP_SET_HASH_IP + tristate "hash:ip set support" + depends on IP_SET + help + This option adds the hash:ip set type support, by which one + can store arbitrary IPv4 or IPv6 addresses (or network addresses) + in a set. + + To compile it as a module, choose M here. If unsure, say N. + endif # IP_SET diff --git a/net/netfilter/ipset/Makefile b/net/netfilter/ipset/Makefile index 40866e267752..5cbf00c41a98 100644 --- a/net/netfilter/ipset/Makefile +++ b/net/netfilter/ipset/Makefile @@ -11,3 +11,6 @@ obj-$(CONFIG_IP_SET) += ip_set.o obj-$(CONFIG_IP_SET_BITMAP_IP) += ip_set_bitmap_ip.o obj-$(CONFIG_IP_SET_BITMAP_IPMAC) += ip_set_bitmap_ipmac.o obj-$(CONFIG_IP_SET_BITMAP_PORT) += ip_set_bitmap_port.o + +# hash types +obj-$(CONFIG_IP_SET_HASH_IP) += ip_set_hash_ip.o diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c new file mode 100644 index 000000000000..53964bc831aa --- /dev/null +++ b/net/netfilter/ipset/ip_set_hash_ip.c @@ -0,0 +1,467 @@ +/* Copyright (C) 2003-2011 Jozsef Kadlecsik + * + * 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. + */ + +/* Kernel module implementing an IP set type: the hash:ip type */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("hash:ip type of IP sets"); +MODULE_ALIAS("ip_set_hash:ip"); + +/* Type specific function prefix */ +#define TYPE hash_ip + +static bool +hash_ip_same_set(const struct ip_set *a, const struct ip_set *b); + +#define hash_ip4_same_set hash_ip_same_set +#define hash_ip6_same_set hash_ip_same_set + +/* The type variant functions: IPv4 */ + +/* Member elements without timeout */ +struct hash_ip4_elem { + __be32 ip; +}; + +/* Member elements with timeout support */ +struct hash_ip4_telem { + __be32 ip; + unsigned long timeout; +}; + +static inline bool +hash_ip4_data_equal(const struct hash_ip4_elem *ip1, + const struct hash_ip4_elem *ip2) +{ + return ip1->ip == ip2->ip; +} + +static inline bool +hash_ip4_data_isnull(const struct hash_ip4_elem *elem) +{ + return elem->ip == 0; +} + +static inline void +hash_ip4_data_copy(struct hash_ip4_elem *dst, const struct hash_ip4_elem *src) +{ + dst->ip = src->ip; +} + +/* Zero valued IP addresses cannot be stored */ +static inline void +hash_ip4_data_zero_out(struct hash_ip4_elem *elem) +{ + elem->ip = 0; +} + +static inline bool +hash_ip4_data_list(struct sk_buff *skb, const struct hash_ip4_elem *data) +{ + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); + return 0; + +nla_put_failure: + return 1; +} + +static bool +hash_ip4_data_tlist(struct sk_buff *skb, const struct hash_ip4_elem *data) +{ + const struct hash_ip4_telem *tdata = + (const struct hash_ip4_telem *)data; + + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(tdata->timeout))); + + return 0; + +nla_put_failure: + return 1; +} + +#define IP_SET_HASH_WITH_NETMASK +#define PF 4 +#define HOST_MASK 32 +#include + +static int +hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) +{ + const struct ip_set_hash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + __be32 ip; + + ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip); + ip &= ip_set_netmask(h->netmask); + if (ip == 0) + return -EINVAL; + + return adtfn(set, &ip, h->timeout); +} + +static int +hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags) +{ + const struct ip_set_hash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + u32 ip, ip_to, hosts, timeout = h->timeout; + __be32 nip; + int ret = 0; + + if (unlikely(!tb[IPSET_ATTR_IP] || + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip); + if (ret) + return ret; + + ip &= ip_set_hostmask(h->netmask); + + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!with_timeout(h->timeout)) + return -IPSET_ERR_TIMEOUT; + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + } + + if (adt == IPSET_TEST) { + nip = htonl(ip); + if (nip == 0) + return -IPSET_ERR_HASH_ELEM; + return adtfn(set, &nip, timeout); + } + + if (tb[IPSET_ATTR_IP_TO]) { + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); + if (ret) + return ret; + if (ip > ip_to) + swap(ip, ip_to); + } else if (tb[IPSET_ATTR_CIDR]) { + u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); + + if (cidr > 32) + return -IPSET_ERR_INVALID_CIDR; + ip &= ip_set_hostmask(cidr); + ip_to = ip | ~ip_set_hostmask(cidr); + } else + ip_to = ip; + + hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1); + + for (; !before(ip_to, ip); ip += hosts) { + nip = htonl(ip); + if (nip == 0) + return -IPSET_ERR_HASH_ELEM; + ret = adtfn(set, &nip, timeout); + + if (ret && !ip_set_eexist(ret, flags)) + return ret; + else + ret = 0; + } + return ret; +} + +static bool +hash_ip_same_set(const struct ip_set *a, const struct ip_set *b) +{ + const struct ip_set_hash *x = a->data; + const struct ip_set_hash *y = b->data; + + /* Resizing changes htable_bits, so we ignore it */ + return x->maxelem == y->maxelem && + x->timeout == y->timeout && + x->netmask == y->netmask; +} + +/* The type variant functions: IPv6 */ + +struct hash_ip6_elem { + union nf_inet_addr ip; +}; + +struct hash_ip6_telem { + union nf_inet_addr ip; + unsigned long timeout; +}; + +static inline bool +hash_ip6_data_equal(const struct hash_ip6_elem *ip1, + const struct hash_ip6_elem *ip2) +{ + return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0; +} + +static inline bool +hash_ip6_data_isnull(const struct hash_ip6_elem *elem) +{ + return ipv6_addr_any(&elem->ip.in6); +} + +static inline void +hash_ip6_data_copy(struct hash_ip6_elem *dst, const struct hash_ip6_elem *src) +{ + ipv6_addr_copy(&dst->ip.in6, &src->ip.in6); +} + +static inline void +hash_ip6_data_zero_out(struct hash_ip6_elem *elem) +{ + ipv6_addr_set(&elem->ip.in6, 0, 0, 0, 0); +} + +static inline void +ip6_netmask(union nf_inet_addr *ip, u8 prefix) +{ + ip->ip6[0] &= ip_set_netmask6(prefix)[0]; + ip->ip6[1] &= ip_set_netmask6(prefix)[1]; + ip->ip6[2] &= ip_set_netmask6(prefix)[2]; + ip->ip6[3] &= ip_set_netmask6(prefix)[3]; +} + +static bool +hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *data) +{ + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); + return 0; + +nla_put_failure: + return 1; +} + +static bool +hash_ip6_data_tlist(struct sk_buff *skb, const struct hash_ip6_elem *data) +{ + const struct hash_ip6_telem *e = + (const struct hash_ip6_telem *)data; + + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(e->timeout))); + return 0; + +nla_put_failure: + return 1; +} + +#undef PF +#undef HOST_MASK + +#define PF 6 +#define HOST_MASK 128 +#include + +static int +hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) +{ + const struct ip_set_hash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + union nf_inet_addr ip; + + ip6addrptr(skb, flags & IPSET_DIM_ONE_SRC, &ip.in6); + ip6_netmask(&ip, h->netmask); + if (ipv6_addr_any(&ip.in6)) + return -EINVAL; + + return adtfn(set, &ip, h->timeout); +} + +static const struct nla_policy hash_ip6_adt_policy[IPSET_ATTR_ADT_MAX + 1] = { + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, +}; + +static int +hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags) +{ + const struct ip_set_hash *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + union nf_inet_addr ip; + u32 timeout = h->timeout; + int ret; + + if (unlikely(!tb[IPSET_ATTR_IP] || + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || + tb[IPSET_ATTR_IP_TO] || + tb[IPSET_ATTR_CIDR])) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + + ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &ip); + if (ret) + return ret; + + ip6_netmask(&ip, h->netmask); + if (ipv6_addr_any(&ip.in6)) + return -IPSET_ERR_HASH_ELEM; + + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!with_timeout(h->timeout)) + return -IPSET_ERR_TIMEOUT; + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + } + + ret = adtfn(set, &ip, timeout); + + return ip_set_eexist(ret, flags) ? 0 : ret; +} + +/* Create hash:ip type of sets */ + +static int +hash_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags) +{ + u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; + u8 netmask, hbits; + struct ip_set_hash *h; + + if (!(set->family == AF_INET || set->family == AF_INET6)) + return -IPSET_ERR_INVALID_FAMILY; + netmask = set->family == AF_INET ? 32 : 128; + pr_debug("Create set %s with family %s\n", + set->name, set->family == AF_INET ? "inet" : "inet6"); + + if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_HASHSIZE]) { + hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); + if (hashsize < IPSET_MIMINAL_HASHSIZE) + hashsize = IPSET_MIMINAL_HASHSIZE; + } + + if (tb[IPSET_ATTR_MAXELEM]) + maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); + + if (tb[IPSET_ATTR_NETMASK]) { + netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]); + + if ((set->family == AF_INET && netmask > 32) || + (set->family == AF_INET6 && netmask > 128) || + netmask == 0) + return -IPSET_ERR_INVALID_NETMASK; + } + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return -ENOMEM; + + h->maxelem = maxelem; + h->netmask = netmask; + get_random_bytes(&h->initval, sizeof(h->initval)); + h->timeout = IPSET_NO_TIMEOUT; + + hbits = htable_bits(hashsize); + h->table = ip_set_alloc( + sizeof(struct htable) + + jhash_size(hbits) * sizeof(struct hbucket)); + if (!h->table) { + kfree(h); + return -ENOMEM; + } + h->table->htable_bits = hbits; + + set->data = h; + + if (tb[IPSET_ATTR_TIMEOUT]) { + h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + + set->variant = set->family == AF_INET + ? &hash_ip4_tvariant : &hash_ip6_tvariant; + + if (set->family == AF_INET) + hash_ip4_gc_init(set); + else + hash_ip6_gc_init(set); + } else { + set->variant = set->family == AF_INET + ? &hash_ip4_variant : &hash_ip6_variant; + } + + pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n", + set->name, jhash_size(h->table->htable_bits), + h->table->htable_bits, h->maxelem, set->data, h->table); + + return 0; +} + +static struct ip_set_type hash_ip_type __read_mostly = { + .name = "hash:ip", + .protocol = IPSET_PROTOCOL, + .features = IPSET_TYPE_IP, + .dimension = IPSET_DIM_ONE, + .family = AF_UNSPEC, + .revision = 0, + .create = hash_ip_create, + .create_policy = { + [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, + [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, + [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, + [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_NETMASK] = { .type = NLA_U8 }, + }, + .adt_policy = { + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, + [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, + }, + .me = THIS_MODULE, +}; + +static int __init +hash_ip_init(void) +{ + return ip_set_type_register(&hash_ip_type); +} + +static void __exit +hash_ip_fini(void) +{ + ip_set_type_unregister(&hash_ip_type); +} + +module_init(hash_ip_init); +module_exit(hash_ip_fini); -- cgit v1.2.3 From f830837f0eed0f9e371b8fd65169365780814bb1 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 1 Feb 2011 15:54:59 +0100 Subject: netfilter: ipset: list:set set type support The module implements the list:set type support in two flavours: without and with timeout. The sets has two sides: for the userspace, they store the names of other (non list:set type of) sets: one can add, delete and test set names. For the kernel, it forms an ordered union of the member sets: the members sets are tried in order when elements are added, deleted and tested and the process stops at the first success. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/ipset/ip_set_list.h | 27 ++ net/netfilter/ipset/Kconfig | 10 + net/netfilter/ipset/Makefile | 3 + net/netfilter/ipset/ip_set_list_set.c | 584 ++++++++++++++++++++++++++++ 4 files changed, 624 insertions(+) create mode 100644 include/linux/netfilter/ipset/ip_set_list.h create mode 100644 net/netfilter/ipset/ip_set_list_set.c (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set_list.h b/include/linux/netfilter/ipset/ip_set_list.h new file mode 100644 index 000000000000..40a63f302613 --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set_list.h @@ -0,0 +1,27 @@ +#ifndef __IP_SET_LIST_H +#define __IP_SET_LIST_H + +/* List type specific error codes */ +enum { + /* Set name to be added/deleted/tested does not exist. */ + IPSET_ERR_NAME = IPSET_ERR_TYPE_SPECIFIC, + /* list:set type is not permitted to add */ + IPSET_ERR_LOOP, + /* Missing reference set */ + IPSET_ERR_BEFORE, + /* Reference set does not exist */ + IPSET_ERR_NAMEREF, + /* Set is full */ + IPSET_ERR_LIST_FULL, + /* Reference set is not added to the set */ + IPSET_ERR_REF_EXIST, +}; + +#ifdef __KERNEL__ + +#define IP_SET_LIST_DEFAULT_SIZE 8 +#define IP_SET_LIST_MIN_SIZE 4 + +#endif /* __KERNEL__ */ + +#endif /* __IP_SET_LIST_H */ diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig index 2512e7b82d12..3b970d343023 100644 --- a/net/netfilter/ipset/Kconfig +++ b/net/netfilter/ipset/Kconfig @@ -108,4 +108,14 @@ config IP_SET_HASH_NETPORT To compile it as a module, choose M here. If unsure, say N. +config IP_SET_LIST_SET + tristate "list:set set support" + depends on IP_SET + help + This option adds the list:set set type support. In this + kind of set one can store the name of other sets and it forms + an ordered union of the member sets. + + To compile it as a module, choose M here. If unsure, say N. + endif # IP_SET diff --git a/net/netfilter/ipset/Makefile b/net/netfilter/ipset/Makefile index fbbebd62bb2a..5adbdab67bd2 100644 --- a/net/netfilter/ipset/Makefile +++ b/net/netfilter/ipset/Makefile @@ -19,3 +19,6 @@ obj-$(CONFIG_IP_SET_HASH_IPPORTIP) += ip_set_hash_ipportip.o obj-$(CONFIG_IP_SET_HASH_IPPORTNET) += ip_set_hash_ipportnet.o obj-$(CONFIG_IP_SET_HASH_NET) += ip_set_hash_net.o obj-$(CONFIG_IP_SET_HASH_NETPORT) += ip_set_hash_netport.o + +# list types +obj-$(CONFIG_IP_SET_LIST_SET) += ip_set_list_set.o diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c new file mode 100644 index 000000000000..a47c32982f06 --- /dev/null +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -0,0 +1,584 @@ +/* Copyright (C) 2008-2011 Jozsef Kadlecsik + * + * 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. + */ + +/* Kernel module implementing an IP set type: the list:set type */ + +#include +#include +#include +#include + +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("list:set type of IP sets"); +MODULE_ALIAS("ip_set_list:set"); + +/* Member elements without and with timeout */ +struct set_elem { + ip_set_id_t id; +}; + +struct set_telem { + ip_set_id_t id; + unsigned long timeout; +}; + +/* Type structure */ +struct list_set { + size_t dsize; /* element size */ + u32 size; /* size of set list array */ + u32 timeout; /* timeout value */ + struct timer_list gc; /* garbage collection */ + struct set_elem members[0]; /* the set members */ +}; + +static inline struct set_elem * +list_set_elem(const struct list_set *map, u32 id) +{ + return (struct set_elem *)((char *)map->members + id * map->dsize); +} + +static inline bool +list_set_timeout(const struct list_set *map, u32 id) +{ + const struct set_telem *elem = + (const struct set_telem *) list_set_elem(map, id); + + return ip_set_timeout_test(elem->timeout); +} + +static inline bool +list_set_expired(const struct list_set *map, u32 id) +{ + const struct set_telem *elem = + (const struct set_telem *) list_set_elem(map, id); + + return ip_set_timeout_expired(elem->timeout); +} + +static inline int +list_set_exist(const struct set_telem *elem) +{ + return elem->id != IPSET_INVALID_ID && + !ip_set_timeout_expired(elem->timeout); +} + +/* Set list without and with timeout */ + +static int +list_set_kadt(struct ip_set *set, const struct sk_buff *skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) +{ + struct list_set *map = set->data; + struct set_elem *elem; + u32 i; + int ret; + + for (i = 0; i < map->size; i++) { + elem = list_set_elem(map, i); + if (elem->id == IPSET_INVALID_ID) + return 0; + if (with_timeout(map->timeout) && list_set_expired(map, i)) + continue; + switch (adt) { + case IPSET_TEST: + ret = ip_set_test(elem->id, skb, pf, dim, flags); + if (ret > 0) + return ret; + break; + case IPSET_ADD: + ret = ip_set_add(elem->id, skb, pf, dim, flags); + if (ret == 0) + return ret; + break; + case IPSET_DEL: + ret = ip_set_del(elem->id, skb, pf, dim, flags); + if (ret == 0) + return ret; + break; + default: + break; + } + } + return -EINVAL; +} + +static bool +next_id_eq(const struct list_set *map, u32 i, ip_set_id_t id) +{ + const struct set_elem *elem; + + if (i + 1 < map->size) { + elem = list_set_elem(map, i + 1); + return !!(elem->id == id && + !(with_timeout(map->timeout) && + list_set_expired(map, i + 1))); + } + + return 0; +} + +static void +list_elem_add(struct list_set *map, u32 i, ip_set_id_t id) +{ + struct set_elem *e; + + for (; i < map->size; i++) { + e = list_set_elem(map, i); + swap(e->id, id); + if (e->id == IPSET_INVALID_ID) + break; + } +} + +static void +list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id, + unsigned long timeout) +{ + struct set_telem *e; + + for (; i < map->size; i++) { + e = (struct set_telem *)list_set_elem(map, i); + swap(e->id, id); + if (e->id == IPSET_INVALID_ID) + break; + swap(e->timeout, timeout); + } +} + +static int +list_set_add(struct list_set *map, u32 i, ip_set_id_t id, + unsigned long timeout) +{ + const struct set_elem *e = list_set_elem(map, i); + + if (i == map->size - 1 && e->id != IPSET_INVALID_ID) + /* Last element replaced: e.g. add new,before,last */ + ip_set_put_byindex(e->id); + if (with_timeout(map->timeout)) + list_elem_tadd(map, i, id, timeout); + else + list_elem_add(map, i, id); + + return 0; +} + +static int +list_set_del(struct list_set *map, ip_set_id_t id, u32 i) +{ + struct set_elem *a = list_set_elem(map, i), *b; + + ip_set_put_byindex(id); + + for (; i < map->size - 1; i++) { + b = list_set_elem(map, i + 1); + a->id = b->id; + if (with_timeout(map->timeout)) + ((struct set_telem *)a)->timeout = + ((struct set_telem *)b)->timeout; + a = b; + if (a->id == IPSET_INVALID_ID) + break; + } + /* Last element */ + a->id = IPSET_INVALID_ID; + return 0; +} + +static int +list_set_uadt(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags) +{ + struct list_set *map = set->data; + bool with_timeout = with_timeout(map->timeout); + int before = 0; + u32 timeout = map->timeout; + ip_set_id_t id, refid = IPSET_INVALID_ID; + const struct set_elem *elem; + struct ip_set *s; + u32 i; + int ret = 0; + + if (unlikely(!tb[IPSET_ATTR_NAME] || + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + + id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s); + if (id == IPSET_INVALID_ID) + return -IPSET_ERR_NAME; + /* "Loop detection" */ + if (s->type->features & IPSET_TYPE_NAME) { + ret = -IPSET_ERR_LOOP; + goto finish; + } + + if (tb[IPSET_ATTR_CADT_FLAGS]) { + u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + before = f & IPSET_FLAG_BEFORE; + } + + if (before && !tb[IPSET_ATTR_NAMEREF]) { + ret = -IPSET_ERR_BEFORE; + goto finish; + } + + if (tb[IPSET_ATTR_NAMEREF]) { + refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]), + &s); + if (refid == IPSET_INVALID_ID) { + ret = -IPSET_ERR_NAMEREF; + goto finish; + } + if (!before) + before = -1; + } + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!with_timeout) { + ret = -IPSET_ERR_TIMEOUT; + goto finish; + } + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + } + + switch (adt) { + case IPSET_TEST: + for (i = 0; i < map->size && !ret; i++) { + elem = list_set_elem(map, i); + if (elem->id == IPSET_INVALID_ID || + (before != 0 && i + 1 >= map->size)) + break; + else if (with_timeout && list_set_expired(map, i)) + continue; + else if (before > 0 && elem->id == id) + ret = next_id_eq(map, i, refid); + else if (before < 0 && elem->id == refid) + ret = next_id_eq(map, i, id); + else if (before == 0 && elem->id == id) + ret = 1; + } + break; + case IPSET_ADD: + for (i = 0; i < map->size && !ret; i++) { + elem = list_set_elem(map, i); + if (elem->id == id && + !(with_timeout && list_set_expired(map, i))) + ret = -IPSET_ERR_EXIST; + } + if (ret == -IPSET_ERR_EXIST) + break; + ret = -IPSET_ERR_LIST_FULL; + for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) { + elem = list_set_elem(map, i); + if (elem->id == IPSET_INVALID_ID) + ret = before != 0 ? -IPSET_ERR_REF_EXIST + : list_set_add(map, i, id, timeout); + else if (elem->id != refid) + continue; + else if (with_timeout && list_set_expired(map, i)) + ret = -IPSET_ERR_REF_EXIST; + else if (before) + ret = list_set_add(map, i, id, timeout); + else if (i + 1 < map->size) + ret = list_set_add(map, i + 1, id, timeout); + } + break; + case IPSET_DEL: + ret = -IPSET_ERR_EXIST; + for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) { + elem = list_set_elem(map, i); + if (elem->id == IPSET_INVALID_ID) { + ret = before != 0 ? -IPSET_ERR_REF_EXIST + : -IPSET_ERR_EXIST; + break; + } else if (with_timeout && list_set_expired(map, i)) + continue; + else if (elem->id == id && + (before == 0 || + (before > 0 && + next_id_eq(map, i, refid)))) + ret = list_set_del(map, id, i); + else if (before < 0 && + elem->id == refid && + next_id_eq(map, i, id)) + ret = list_set_del(map, id, i + 1); + } + break; + default: + break; + } + +finish: + if (refid != IPSET_INVALID_ID) + ip_set_put_byindex(refid); + if (adt != IPSET_ADD || ret) + ip_set_put_byindex(id); + + return ip_set_eexist(ret, flags) ? 0 : ret; +} + +static void +list_set_flush(struct ip_set *set) +{ + struct list_set *map = set->data; + struct set_elem *elem; + u32 i; + + for (i = 0; i < map->size; i++) { + elem = list_set_elem(map, i); + if (elem->id != IPSET_INVALID_ID) { + ip_set_put_byindex(elem->id); + elem->id = IPSET_INVALID_ID; + } + } +} + +static void +list_set_destroy(struct ip_set *set) +{ + struct list_set *map = set->data; + + if (with_timeout(map->timeout)) + del_timer_sync(&map->gc); + list_set_flush(set); + kfree(map); + + set->data = NULL; +} + +static int +list_set_head(struct ip_set *set, struct sk_buff *skb) +{ + const struct list_set *map = set->data; + struct nlattr *nested; + + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) + goto nla_put_failure; + NLA_PUT_NET32(skb, IPSET_ATTR_SIZE, htonl(map->size)); + if (with_timeout(map->timeout)) + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); + NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, + htonl(atomic_read(&set->ref) - 1)); + NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, + htonl(sizeof(*map) + map->size * map->dsize)); + ipset_nest_end(skb, nested); + + return 0; +nla_put_failure: + return -EMSGSIZE; +} + +static int +list_set_list(const struct ip_set *set, + struct sk_buff *skb, struct netlink_callback *cb) +{ + const struct list_set *map = set->data; + struct nlattr *atd, *nested; + u32 i, first = cb->args[2]; + const struct set_elem *e; + + atd = ipset_nest_start(skb, IPSET_ATTR_ADT); + if (!atd) + return -EMSGSIZE; + for (; cb->args[2] < map->size; cb->args[2]++) { + i = cb->args[2]; + e = list_set_elem(map, i); + if (e->id == IPSET_INVALID_ID) + goto finish; + if (with_timeout(map->timeout) && list_set_expired(map, i)) + continue; + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) { + if (i == first) { + nla_nest_cancel(skb, atd); + return -EMSGSIZE; + } else + goto nla_put_failure; + } + NLA_PUT_STRING(skb, IPSET_ATTR_NAME, + ip_set_name_byindex(e->id)); + if (with_timeout(map->timeout)) { + const struct set_telem *te = + (const struct set_telem *) e; + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(te->timeout))); + } + ipset_nest_end(skb, nested); + } +finish: + ipset_nest_end(skb, atd); + /* Set listing finished */ + cb->args[2] = 0; + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nested); + ipset_nest_end(skb, atd); + if (unlikely(i == first)) { + cb->args[2] = 0; + return -EMSGSIZE; + } + return 0; +} + +static bool +list_set_same_set(const struct ip_set *a, const struct ip_set *b) +{ + const struct list_set *x = a->data; + const struct list_set *y = b->data; + + return x->size == y->size && + x->timeout == y->timeout; +} + +static const struct ip_set_type_variant list_set = { + .kadt = list_set_kadt, + .uadt = list_set_uadt, + .destroy = list_set_destroy, + .flush = list_set_flush, + .head = list_set_head, + .list = list_set_list, + .same_set = list_set_same_set, +}; + +static void +list_set_gc(unsigned long ul_set) +{ + struct ip_set *set = (struct ip_set *) ul_set; + struct list_set *map = set->data; + struct set_telem *e; + u32 i; + + /* We run parallel with other readers (test element) + * but adding/deleting new entries is locked out */ + read_lock_bh(&set->lock); + for (i = map->size - 1; i >= 0; i--) { + e = (struct set_telem *) list_set_elem(map, i); + if (e->id != IPSET_INVALID_ID && + list_set_expired(map, i)) + list_set_del(map, e->id, i); + } + read_unlock_bh(&set->lock); + + map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; + add_timer(&map->gc); +} + +static void +list_set_gc_init(struct ip_set *set) +{ + struct list_set *map = set->data; + + init_timer(&map->gc); + map->gc.data = (unsigned long) set; + map->gc.function = list_set_gc; + map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; + add_timer(&map->gc); +} + +/* Create list:set type of sets */ + +static bool +init_list_set(struct ip_set *set, u32 size, size_t dsize, + unsigned long timeout) +{ + struct list_set *map; + struct set_elem *e; + u32 i; + + map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL); + if (!map) + return false; + + map->size = size; + map->dsize = dsize; + map->timeout = timeout; + set->data = map; + + for (i = 0; i < size; i++) { + e = list_set_elem(map, i); + e->id = IPSET_INVALID_ID; + } + + return true; +} + +static int +list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags) +{ + u32 size = IP_SET_LIST_DEFAULT_SIZE; + + if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_SIZE]) + size = ip_set_get_h32(tb[IPSET_ATTR_SIZE]); + if (size < IP_SET_LIST_MIN_SIZE) + size = IP_SET_LIST_MIN_SIZE; + + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!init_list_set(set, size, sizeof(struct set_telem), + ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]))) + return -ENOMEM; + + list_set_gc_init(set); + } else { + if (!init_list_set(set, size, sizeof(struct set_elem), + IPSET_NO_TIMEOUT)) + return -ENOMEM; + } + set->variant = &list_set; + return 0; +} + +static struct ip_set_type list_set_type __read_mostly = { + .name = "list:set", + .protocol = IPSET_PROTOCOL, + .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST, + .dimension = IPSET_DIM_ONE, + .family = AF_UNSPEC, + .revision = 0, + .create = list_set_create, + .create_policy = { + [IPSET_ATTR_SIZE] = { .type = NLA_U32 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + }, + .adt_policy = { + [IPSET_ATTR_NAME] = { .type = NLA_STRING, + .len = IPSET_MAXNAMELEN }, + [IPSET_ATTR_NAMEREF] = { .type = NLA_STRING, + .len = IPSET_MAXNAMELEN }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, + }, + .me = THIS_MODULE, +}; + +static int __init +list_set_init(void) +{ + return ip_set_type_register(&list_set_type); +} + +static void __exit +list_set_fini(void) +{ + ip_set_type_unregister(&list_set_type); +} + +module_init(list_set_init); +module_exit(list_set_fini); -- cgit v1.2.3 From d956798d82d2d331c031301965d69e17a1a48a2b Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 1 Feb 2011 15:56:00 +0100 Subject: netfilter: xtables: "set" match and "SET" target support The patch adds the combined module of the "SET" target and "set" match to netfilter. Both the previous and the current revisions are supported. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Patrick McHardy --- include/linux/netfilter/xt_set.h | 55 ++++++ net/netfilter/Kconfig | 12 ++ net/netfilter/Makefile | 1 + net/netfilter/xt_set.c | 359 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 427 insertions(+) create mode 100644 include/linux/netfilter/xt_set.h create mode 100644 net/netfilter/xt_set.c (limited to 'include') diff --git a/include/linux/netfilter/xt_set.h b/include/linux/netfilter/xt_set.h new file mode 100644 index 000000000000..69b2bd1fb818 --- /dev/null +++ b/include/linux/netfilter/xt_set.h @@ -0,0 +1,55 @@ +#ifndef _XT_SET_H +#define _XT_SET_H + +#include + +/* Revision 0 interface: backward compatible with netfilter/iptables */ + +/* + * Option flags for kernel operations (xt_set_info_v0) + */ +#define IPSET_SRC 0x01 /* Source match/add */ +#define IPSET_DST 0x02 /* Destination match/add */ +#define IPSET_MATCH_INV 0x04 /* Inverse matching */ + +struct xt_set_info_v0 { + ip_set_id_t index; + union { + __u32 flags[IPSET_DIM_MAX + 1]; + struct { + __u32 __flags[IPSET_DIM_MAX]; + __u8 dim; + __u8 flags; + } compat; + } u; +}; + +/* match and target infos */ +struct xt_set_info_match_v0 { + struct xt_set_info_v0 match_set; +}; + +struct xt_set_info_target_v0 { + struct xt_set_info_v0 add_set; + struct xt_set_info_v0 del_set; +}; + +/* Revision 1: current interface to netfilter/iptables */ + +struct xt_set_info { + ip_set_id_t index; + __u8 dim; + __u8 flags; +}; + +/* match and target infos */ +struct xt_set_info_match { + struct xt_set_info match_set; +}; + +struct xt_set_info_target { + struct xt_set_info add_set; + struct xt_set_info del_set; +}; + +#endif /*_XT_SET_H*/ diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 351abf8ace13..06fa9e4e45c7 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -352,6 +352,18 @@ config NETFILTER_XT_CONNMARK ctmark), similarly to the packet mark (nfmark). Using this target and match, you can set and match on this mark. +config NETFILTER_XT_SET + tristate 'set target and match support' + depends on IP_SET + depends on NETFILTER_ADVANCED + help + This option adds the "SET" target and "set" match. + + Using this target and match, you can add/delete and match + elements in the sets created by ipset(8). + + To compile it as a module, choose M here. If unsure, say N. + # alphabetically ordered list of targets comment "Xtables targets" diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 510b586ccb7f..1148643559cb 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o # combos obj-$(CONFIG_NETFILTER_XT_MARK) += xt_mark.o obj-$(CONFIG_NETFILTER_XT_CONNMARK) += xt_connmark.o +obj-$(CONFIG_NETFILTER_XT_SET) += xt_set.o # targets obj-$(CONFIG_NETFILTER_XT_TARGET_AUDIT) += xt_AUDIT.o diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c new file mode 100644 index 000000000000..061d48cec137 --- /dev/null +++ b/net/netfilter/xt_set.c @@ -0,0 +1,359 @@ +/* Copyright (C) 2000-2002 Joakim Axelsson + * Patrick Schaaf + * Martin Josefsson + * Copyright (C) 2003-2011 Jozsef Kadlecsik + * + * 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. + */ + +/* Kernel module which implements the set match and SET target + * for netfilter/iptables. */ + +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("Xtables: IP set match and target module"); +MODULE_ALIAS("xt_SET"); +MODULE_ALIAS("ipt_set"); +MODULE_ALIAS("ip6t_set"); +MODULE_ALIAS("ipt_SET"); +MODULE_ALIAS("ip6t_SET"); + +static inline int +match_set(ip_set_id_t index, const struct sk_buff *skb, + u8 pf, u8 dim, u8 flags, int inv) +{ + if (ip_set_test(index, skb, pf, dim, flags)) + inv = !inv; + return inv; +} + +/* Revision 0 interface: backward compatible with netfilter/iptables */ + +static bool +set_match_v0(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_set_info_match_v0 *info = par->matchinfo; + + return match_set(info->match_set.index, skb, par->family, + info->match_set.u.compat.dim, + info->match_set.u.compat.flags, + info->match_set.u.compat.flags & IPSET_INV_MATCH); +} + +static void +compat_flags(struct xt_set_info_v0 *info) +{ + u_int8_t i; + + /* Fill out compatibility data according to enum ip_set_kopt */ + info->u.compat.dim = IPSET_DIM_ZERO; + if (info->u.flags[0] & IPSET_MATCH_INV) + info->u.compat.flags |= IPSET_INV_MATCH; + for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) { + info->u.compat.dim++; + if (info->u.flags[i] & IPSET_SRC) + info->u.compat.flags |= (1<u.compat.dim); + } +} + +static int +set_match_v0_checkentry(const struct xt_mtchk_param *par) +{ + struct xt_set_info_match_v0 *info = par->matchinfo; + ip_set_id_t index; + + index = ip_set_nfnl_get_byindex(info->match_set.index); + + if (index == IPSET_INVALID_ID) { + pr_warning("Cannot find set indentified by id %u to match\n", + info->match_set.index); + return -ENOENT; + } + if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) { + pr_warning("Protocol error: set match dimension " + "is over the limit!\n"); + return -ERANGE; + } + + /* Fill out compatibility data */ + compat_flags(&info->match_set); + + return 0; +} + +static void +set_match_v0_destroy(const struct xt_mtdtor_param *par) +{ + struct xt_set_info_match_v0 *info = par->matchinfo; + + ip_set_nfnl_put(info->match_set.index); +} + +static unsigned int +set_target_v0(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct xt_set_info_target_v0 *info = par->targinfo; + + if (info->add_set.index != IPSET_INVALID_ID) + ip_set_add(info->add_set.index, skb, par->family, + info->add_set.u.compat.dim, + info->add_set.u.compat.flags); + if (info->del_set.index != IPSET_INVALID_ID) + ip_set_del(info->del_set.index, skb, par->family, + info->del_set.u.compat.dim, + info->del_set.u.compat.flags); + + return XT_CONTINUE; +} + +static int +set_target_v0_checkentry(const struct xt_tgchk_param *par) +{ + struct xt_set_info_target_v0 *info = par->targinfo; + ip_set_id_t index; + + if (info->add_set.index != IPSET_INVALID_ID) { + index = ip_set_nfnl_get_byindex(info->add_set.index); + if (index == IPSET_INVALID_ID) { + pr_warning("Cannot find add_set index %u as target\n", + info->add_set.index); + return -ENOENT; + } + } + + if (info->del_set.index != IPSET_INVALID_ID) { + index = ip_set_nfnl_get_byindex(info->del_set.index); + if (index == IPSET_INVALID_ID) { + pr_warning("Cannot find del_set index %u as target\n", + info->del_set.index); + return -ENOENT; + } + } + if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 || + info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) { + pr_warning("Protocol error: SET target dimension " + "is over the limit!\n"); + return -ERANGE; + } + + /* Fill out compatibility data */ + compat_flags(&info->add_set); + compat_flags(&info->del_set); + + return 0; +} + +static void +set_target_v0_destroy(const struct xt_tgdtor_param *par) +{ + const struct xt_set_info_target_v0 *info = par->targinfo; + + if (info->add_set.index != IPSET_INVALID_ID) + ip_set_nfnl_put(info->add_set.index); + if (info->del_set.index != IPSET_INVALID_ID) + ip_set_nfnl_put(info->del_set.index); +} + +/* Revision 1: current interface to netfilter/iptables */ + +static bool +set_match(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_set_info_match *info = par->matchinfo; + + return match_set(info->match_set.index, skb, par->family, + info->match_set.dim, + info->match_set.flags, + info->match_set.flags & IPSET_INV_MATCH); +} + +static int +set_match_checkentry(const struct xt_mtchk_param *par) +{ + struct xt_set_info_match *info = par->matchinfo; + ip_set_id_t index; + + index = ip_set_nfnl_get_byindex(info->match_set.index); + + if (index == IPSET_INVALID_ID) { + pr_warning("Cannot find set indentified by id %u to match\n", + info->match_set.index); + return -ENOENT; + } + if (info->match_set.dim > IPSET_DIM_MAX) { + pr_warning("Protocol error: set match dimension " + "is over the limit!\n"); + return -ERANGE; + } + + return 0; +} + +static void +set_match_destroy(const struct xt_mtdtor_param *par) +{ + struct xt_set_info_match *info = par->matchinfo; + + ip_set_nfnl_put(info->match_set.index); +} + +static unsigned int +set_target(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct xt_set_info_target *info = par->targinfo; + + if (info->add_set.index != IPSET_INVALID_ID) + ip_set_add(info->add_set.index, + skb, par->family, + info->add_set.dim, + info->add_set.flags); + if (info->del_set.index != IPSET_INVALID_ID) + ip_set_del(info->del_set.index, + skb, par->family, + info->add_set.dim, + info->del_set.flags); + + return XT_CONTINUE; +} + +static int +set_target_checkentry(const struct xt_tgchk_param *par) +{ + const struct xt_set_info_target *info = par->targinfo; + ip_set_id_t index; + + if (info->add_set.index != IPSET_INVALID_ID) { + index = ip_set_nfnl_get_byindex(info->add_set.index); + if (index == IPSET_INVALID_ID) { + pr_warning("Cannot find add_set index %u as target\n", + info->add_set.index); + return -ENOENT; + } + } + + if (info->del_set.index != IPSET_INVALID_ID) { + index = ip_set_nfnl_get_byindex(info->del_set.index); + if (index == IPSET_INVALID_ID) { + pr_warning("Cannot find del_set index %u as target\n", + info->del_set.index); + return -ENOENT; + } + } + if (info->add_set.dim > IPSET_DIM_MAX || + info->del_set.flags > IPSET_DIM_MAX) { + pr_warning("Protocol error: SET target dimension " + "is over the limit!\n"); + return -ERANGE; + } + + return 0; +} + +static void +set_target_destroy(const struct xt_tgdtor_param *par) +{ + const struct xt_set_info_target *info = par->targinfo; + + if (info->add_set.index != IPSET_INVALID_ID) + ip_set_nfnl_put(info->add_set.index); + if (info->del_set.index != IPSET_INVALID_ID) + ip_set_nfnl_put(info->del_set.index); +} + +static struct xt_match set_matches[] __read_mostly = { + { + .name = "set", + .family = NFPROTO_IPV4, + .revision = 0, + .match = set_match_v0, + .matchsize = sizeof(struct xt_set_info_match_v0), + .checkentry = set_match_v0_checkentry, + .destroy = set_match_v0_destroy, + .me = THIS_MODULE + }, + { + .name = "set", + .family = NFPROTO_IPV4, + .revision = 1, + .match = set_match, + .matchsize = sizeof(struct xt_set_info_match), + .checkentry = set_match_checkentry, + .destroy = set_match_destroy, + .me = THIS_MODULE + }, + { + .name = "set", + .family = NFPROTO_IPV6, + .revision = 1, + .match = set_match, + .matchsize = sizeof(struct xt_set_info_match), + .checkentry = set_match_checkentry, + .destroy = set_match_destroy, + .me = THIS_MODULE + }, +}; + +static struct xt_target set_targets[] __read_mostly = { + { + .name = "SET", + .revision = 0, + .family = NFPROTO_IPV4, + .target = set_target_v0, + .targetsize = sizeof(struct xt_set_info_target_v0), + .checkentry = set_target_v0_checkentry, + .destroy = set_target_v0_destroy, + .me = THIS_MODULE + }, + { + .name = "SET", + .revision = 1, + .family = NFPROTO_IPV4, + .target = set_target, + .targetsize = sizeof(struct xt_set_info_target), + .checkentry = set_target_checkentry, + .destroy = set_target_destroy, + .me = THIS_MODULE + }, + { + .name = "SET", + .revision = 1, + .family = NFPROTO_IPV6, + .target = set_target, + .targetsize = sizeof(struct xt_set_info_target), + .checkentry = set_target_checkentry, + .destroy = set_target_destroy, + .me = THIS_MODULE + }, +}; + +static int __init xt_set_init(void) +{ + int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches)); + + if (!ret) { + ret = xt_register_targets(set_targets, + ARRAY_SIZE(set_targets)); + if (ret) + xt_unregister_matches(set_matches, + ARRAY_SIZE(set_matches)); + } + return ret; +} + +static void __exit xt_set_fini(void) +{ + xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches)); + xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets)); +} + +module_init(xt_set_init); +module_exit(xt_set_fini); -- cgit v1.2.3 From 3db7e93d3308fb882884b9f024235d6fbf542034 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 1 Feb 2011 16:06:30 +0100 Subject: netfilter: ecache: always set events bits, filter them later For the following rule: iptables -I PREROUTING -t raw -j CT --ctevents assured The event delivered looks like the following: [UPDATE] tcp 6 src=192.168.0.2 dst=192.168.1.2 sport=37041 dport=80 src=192.168.1.2 dst=192.168.1.100 sport=80 dport=37041 [ASSURED] Note that the TCP protocol state is not included. For that reason the CT event filtering is not very useful for conntrackd. To resolve this issue, instead of conditionally setting the CT events bits based on the ctmask, we always set them and perform the filtering in the late stage, just before the delivery. Thus, the event delivered looks like the following: [UPDATE] tcp 6 432000 ESTABLISHED src=192.168.0.2 dst=192.168.1.2 sport=37041 dport=80 src=192.168.1.2 dst=192.168.1.100 sport=80 dport=37041 [ASSURED] Signed-off-by: Pablo Neira Ayuso Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_conntrack_ecache.h | 3 --- net/netfilter/nf_conntrack_ecache.c | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h index 96ba5f7dcab6..349cefedc9f3 100644 --- a/include/net/netfilter/nf_conntrack_ecache.h +++ b/include/net/netfilter/nf_conntrack_ecache.h @@ -77,9 +77,6 @@ nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct) if (e == NULL) return; - if (!(e->ctmask & (1 << event))) - return; - set_bit(event, &e->cache); } diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c index 5702de35e2bb..63a1b915a7e4 100644 --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -63,6 +63,9 @@ void nf_ct_deliver_cached_events(struct nf_conn *ct) * this does not harm and it happens very rarely. */ unsigned long missed = e->missed; + if (!((events | missed) & e->ctmask)) + goto out_unlock; + ret = notify->fcn(events | missed, &item); if (unlikely(ret < 0 || missed)) { spin_lock_bh(&ct->lock); -- cgit v1.2.3 From 2a7dba391e5628ad665ce84ef9a6648da541ebab Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 1 Feb 2011 11:05:39 -0500 Subject: fs/vfs/security: pass last path component to LSM on inode creation SELinux would like to implement a new labeling behavior of newly created inodes. We currently label new inodes based on the parent and the creating process. This new behavior would also take into account the name of the new object when deciding the new label. This is not the (supposed) full path, just the last component of the path. This is very useful because creating /etc/shadow is different than creating /etc/passwd but the kernel hooks are unable to differentiate these operations. We currently require that userspace realize it is doing some difficult operation like that and than userspace jumps through SELinux hoops to get things set up correctly. This patch does not implement new behavior, that is obviously contained in a seperate SELinux patch, but it does pass the needed name down to the correct LSM hook. If no such name exists it is fine to pass NULL. Signed-off-by: Eric Paris --- fs/btrfs/inode.c | 13 +++++++------ fs/btrfs/xattr.c | 6 ++++-- fs/btrfs/xattr.h | 3 ++- fs/ext2/ext2.h | 2 +- fs/ext2/ialloc.c | 5 +++-- fs/ext2/namei.c | 8 ++++---- fs/ext2/xattr.h | 6 ++++-- fs/ext2/xattr_security.c | 5 +++-- fs/ext3/ialloc.c | 5 +++-- fs/ext3/namei.c | 8 ++++---- fs/ext3/xattr.h | 4 ++-- fs/ext3/xattr_security.c | 5 +++-- fs/ext4/ialloc.c | 2 +- fs/ext4/xattr.h | 4 ++-- fs/ext4/xattr_security.c | 5 +++-- fs/gfs2/inode.c | 7 ++++--- fs/jffs2/dir.c | 9 ++++----- fs/jffs2/nodelist.h | 2 +- fs/jffs2/security.c | 5 +++-- fs/jffs2/write.c | 18 ++++++++++-------- fs/jffs2/xattr.h | 5 +++-- fs/jfs/jfs_xattr.h | 5 +++-- fs/jfs/namei.c | 8 ++++---- fs/jfs/xattr.c | 6 ++++-- fs/ocfs2/namei.c | 4 ++-- fs/ocfs2/refcounttree.c | 3 ++- fs/ocfs2/xattr.c | 10 ++++++---- fs/ocfs2/xattr.h | 4 +++- fs/reiserfs/namei.c | 9 +++++---- fs/reiserfs/xattr_security.c | 3 ++- fs/xfs/linux-2.6/xfs_iops.c | 9 +++++---- include/linux/ext3_fs.h | 3 ++- include/linux/reiserfs_xattr.h | 2 ++ include/linux/security.h | 9 +++++++-- mm/shmem.c | 9 +++++---- security/capability.c | 3 ++- security/security.c | 6 ++++-- security/selinux/hooks.c | 5 +++-- security/smack/smack_lsm.c | 5 ++++- 39 files changed, 136 insertions(+), 94 deletions(-) (limited to 'include') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a0ff46a47895..49c04bec6a9d 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -90,13 +90,14 @@ static noinline int cow_file_range(struct inode *inode, unsigned long *nr_written, int unlock); static int btrfs_init_inode_security(struct btrfs_trans_handle *trans, - struct inode *inode, struct inode *dir) + struct inode *inode, struct inode *dir, + const struct qstr *qstr) { int err; err = btrfs_init_acl(trans, inode, dir); if (!err) - err = btrfs_xattr_security_init(trans, inode, dir); + err = btrfs_xattr_security_init(trans, inode, dir, qstr); return err; } @@ -4675,7 +4676,7 @@ static int btrfs_mknod(struct inode *dir, struct dentry *dentry, if (IS_ERR(inode)) goto out_unlock; - err = btrfs_init_inode_security(trans, inode, dir); + err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name); if (err) { drop_inode = 1; goto out_unlock; @@ -4736,7 +4737,7 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry, if (IS_ERR(inode)) goto out_unlock; - err = btrfs_init_inode_security(trans, inode, dir); + err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name); if (err) { drop_inode = 1; goto out_unlock; @@ -4864,7 +4865,7 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) drop_on_err = 1; - err = btrfs_init_inode_security(trans, inode, dir); + err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name); if (err) goto out_fail; @@ -6946,7 +6947,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry, if (IS_ERR(inode)) goto out_unlock; - err = btrfs_init_inode_security(trans, inode, dir); + err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name); if (err) { drop_inode = 1; goto out_unlock; diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c index 698fdd2c739c..3338a7e61d25 100644 --- a/fs/btrfs/xattr.c +++ b/fs/btrfs/xattr.c @@ -352,7 +352,8 @@ int btrfs_removexattr(struct dentry *dentry, const char *name) } int btrfs_xattr_security_init(struct btrfs_trans_handle *trans, - struct inode *inode, struct inode *dir) + struct inode *inode, struct inode *dir, + const struct qstr *qstr) { int err; size_t len; @@ -360,7 +361,8 @@ int btrfs_xattr_security_init(struct btrfs_trans_handle *trans, char *suffix; char *name; - err = security_inode_init_security(inode, dir, &suffix, &value, &len); + err = security_inode_init_security(inode, dir, qstr, &suffix, &value, + &len); if (err) { if (err == -EOPNOTSUPP) return 0; diff --git a/fs/btrfs/xattr.h b/fs/btrfs/xattr.h index 7a43fd640bbb..b3cc8039134b 100644 --- a/fs/btrfs/xattr.h +++ b/fs/btrfs/xattr.h @@ -37,6 +37,7 @@ extern int btrfs_setxattr(struct dentry *dentry, const char *name, extern int btrfs_removexattr(struct dentry *dentry, const char *name); extern int btrfs_xattr_security_init(struct btrfs_trans_handle *trans, - struct inode *inode, struct inode *dir); + struct inode *inode, struct inode *dir, + const struct qstr *qstr); #endif /* __XATTR__ */ diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 6346a2acf326..1b48c3370872 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -110,7 +110,7 @@ extern struct ext2_dir_entry_2 * ext2_dotdot (struct inode *, struct page **); extern void ext2_set_link(struct inode *, struct ext2_dir_entry_2 *, struct page *, struct inode *, int); /* ialloc.c */ -extern struct inode * ext2_new_inode (struct inode *, int); +extern struct inode * ext2_new_inode (struct inode *, int, const struct qstr *); extern void ext2_free_inode (struct inode *); extern unsigned long ext2_count_free_inodes (struct super_block *); extern void ext2_check_inodes_bitmap (struct super_block *); diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index ad70479aabff..ee9ed31948e1 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -429,7 +429,8 @@ found: return group; } -struct inode *ext2_new_inode(struct inode *dir, int mode) +struct inode *ext2_new_inode(struct inode *dir, int mode, + const struct qstr *qstr) { struct super_block *sb; struct buffer_head *bitmap_bh = NULL; @@ -585,7 +586,7 @@ got: if (err) goto fail_free_drop; - err = ext2_init_security(inode,dir); + err = ext2_init_security(inode, dir, qstr); if (err) goto fail_free_drop; diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index f8aecd2e3297..368d7049ac89 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -104,7 +104,7 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, int mode, st dquot_initialize(dir); - inode = ext2_new_inode(dir, mode); + inode = ext2_new_inode(dir, mode, &dentry->d_name); if (IS_ERR(inode)) return PTR_ERR(inode); @@ -133,7 +133,7 @@ static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, dev_ dquot_initialize(dir); - inode = ext2_new_inode (dir, mode); + inode = ext2_new_inode (dir, mode, &dentry->d_name); err = PTR_ERR(inode); if (!IS_ERR(inode)) { init_special_inode(inode, inode->i_mode, rdev); @@ -159,7 +159,7 @@ static int ext2_symlink (struct inode * dir, struct dentry * dentry, dquot_initialize(dir); - inode = ext2_new_inode (dir, S_IFLNK | S_IRWXUGO); + inode = ext2_new_inode (dir, S_IFLNK | S_IRWXUGO, &dentry->d_name); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out; @@ -230,7 +230,7 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) inode_inc_link_count(dir); - inode = ext2_new_inode (dir, S_IFDIR | mode); + inode = ext2_new_inode(dir, S_IFDIR | mode, &dentry->d_name); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out_dir; diff --git a/fs/ext2/xattr.h b/fs/ext2/xattr.h index a1a1c2184616..5e41cccff762 100644 --- a/fs/ext2/xattr.h +++ b/fs/ext2/xattr.h @@ -116,9 +116,11 @@ exit_ext2_xattr(void) # endif /* CONFIG_EXT2_FS_XATTR */ #ifdef CONFIG_EXT2_FS_SECURITY -extern int ext2_init_security(struct inode *inode, struct inode *dir); +extern int ext2_init_security(struct inode *inode, struct inode *dir, + const struct qstr *qstr); #else -static inline int ext2_init_security(struct inode *inode, struct inode *dir) +static inline int ext2_init_security(struct inode *inode, struct inode *dir, + const struct qstr *qstr) { return 0; } diff --git a/fs/ext2/xattr_security.c b/fs/ext2/xattr_security.c index 3004e15d5da5..5d979b4347b0 100644 --- a/fs/ext2/xattr_security.c +++ b/fs/ext2/xattr_security.c @@ -47,14 +47,15 @@ ext2_xattr_security_set(struct dentry *dentry, const char *name, } int -ext2_init_security(struct inode *inode, struct inode *dir) +ext2_init_security(struct inode *inode, struct inode *dir, + const struct qstr *qstr) { int err; size_t len; void *value; char *name; - err = security_inode_init_security(inode, dir, &name, &value, &len); + err = security_inode_init_security(inode, dir, qstr, &name, &value, &len); if (err) { if (err == -EOPNOTSUPP) return 0; diff --git a/fs/ext3/ialloc.c b/fs/ext3/ialloc.c index 9724aef22460..bfc2dc43681d 100644 --- a/fs/ext3/ialloc.c +++ b/fs/ext3/ialloc.c @@ -404,7 +404,8 @@ static int find_group_other(struct super_block *sb, struct inode *parent) * For other inodes, search forward from the parent directory's block * group to find a free inode. */ -struct inode *ext3_new_inode(handle_t *handle, struct inode * dir, int mode) +struct inode *ext3_new_inode(handle_t *handle, struct inode * dir, + const struct qstr *qstr, int mode) { struct super_block *sb; struct buffer_head *bitmap_bh = NULL; @@ -589,7 +590,7 @@ got: if (err) goto fail_free_drop; - err = ext3_init_security(handle,inode, dir); + err = ext3_init_security(handle, inode, dir, qstr); if (err) goto fail_free_drop; diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index bce9dce639b8..a900033efcce 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -1707,7 +1707,7 @@ retry: if (IS_DIRSYNC(dir)) handle->h_sync = 1; - inode = ext3_new_inode (handle, dir, mode); + inode = ext3_new_inode (handle, dir, &dentry->d_name, mode); err = PTR_ERR(inode); if (!IS_ERR(inode)) { inode->i_op = &ext3_file_inode_operations; @@ -1743,7 +1743,7 @@ retry: if (IS_DIRSYNC(dir)) handle->h_sync = 1; - inode = ext3_new_inode (handle, dir, mode); + inode = ext3_new_inode (handle, dir, &dentry->d_name, mode); err = PTR_ERR(inode); if (!IS_ERR(inode)) { init_special_inode(inode, inode->i_mode, rdev); @@ -1781,7 +1781,7 @@ retry: if (IS_DIRSYNC(dir)) handle->h_sync = 1; - inode = ext3_new_inode (handle, dir, S_IFDIR | mode); + inode = ext3_new_inode (handle, dir, &dentry->d_name, S_IFDIR | mode); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out_stop; @@ -2195,7 +2195,7 @@ retry: if (IS_DIRSYNC(dir)) handle->h_sync = 1; - inode = ext3_new_inode (handle, dir, S_IFLNK|S_IRWXUGO); + inode = ext3_new_inode (handle, dir, &dentry->d_name, S_IFLNK|S_IRWXUGO); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out_stop; diff --git a/fs/ext3/xattr.h b/fs/ext3/xattr.h index 377fe7201169..2be4f69bfa64 100644 --- a/fs/ext3/xattr.h +++ b/fs/ext3/xattr.h @@ -128,10 +128,10 @@ exit_ext3_xattr(void) #ifdef CONFIG_EXT3_FS_SECURITY extern int ext3_init_security(handle_t *handle, struct inode *inode, - struct inode *dir); + struct inode *dir, const struct qstr *qstr); #else static inline int ext3_init_security(handle_t *handle, struct inode *inode, - struct inode *dir) + struct inode *dir, const struct qstr *qstr) { return 0; } diff --git a/fs/ext3/xattr_security.c b/fs/ext3/xattr_security.c index 03a99bfc59f9..b8d9f83aa5c5 100644 --- a/fs/ext3/xattr_security.c +++ b/fs/ext3/xattr_security.c @@ -49,14 +49,15 @@ ext3_xattr_security_set(struct dentry *dentry, const char *name, } int -ext3_init_security(handle_t *handle, struct inode *inode, struct inode *dir) +ext3_init_security(handle_t *handle, struct inode *inode, struct inode *dir, + const struct qstr *qstr) { int err; size_t len; void *value; char *name; - err = security_inode_init_security(inode, dir, &name, &value, &len); + err = security_inode_init_security(inode, dir, qstr, &name, &value, &len); if (err) { if (err == -EOPNOTSUPP) return 0; diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 1ce240a23ebb..49b6cfd1fc47 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -1042,7 +1042,7 @@ got: if (err) goto fail_free_drop; - err = ext4_init_security(handle, inode, dir); + err = ext4_init_security(handle, inode, dir, qstr); if (err) goto fail_free_drop; diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h index 1ef16520b950..25b7387ff183 100644 --- a/fs/ext4/xattr.h +++ b/fs/ext4/xattr.h @@ -145,10 +145,10 @@ ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, #ifdef CONFIG_EXT4_FS_SECURITY extern int ext4_init_security(handle_t *handle, struct inode *inode, - struct inode *dir); + struct inode *dir, const struct qstr *qstr); #else static inline int ext4_init_security(handle_t *handle, struct inode *inode, - struct inode *dir) + struct inode *dir, const struct qstr *qstr) { return 0; } diff --git a/fs/ext4/xattr_security.c b/fs/ext4/xattr_security.c index 9b21268e121c..007c3bfbf094 100644 --- a/fs/ext4/xattr_security.c +++ b/fs/ext4/xattr_security.c @@ -49,14 +49,15 @@ ext4_xattr_security_set(struct dentry *dentry, const char *name, } int -ext4_init_security(handle_t *handle, struct inode *inode, struct inode *dir) +ext4_init_security(handle_t *handle, struct inode *inode, struct inode *dir, + const struct qstr *qstr) { int err; size_t len; void *value; char *name; - err = security_inode_init_security(inode, dir, &name, &value, &len); + err = security_inode_init_security(inode, dir, qstr, &name, &value, &len); if (err) { if (err == -EOPNOTSUPP) return 0; diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 2232b3c780bd..de35ca7d7980 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -791,14 +791,15 @@ fail: return error; } -static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip) +static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip, + const struct qstr *qstr) { int err; size_t len; void *value; char *name; - err = security_inode_init_security(&ip->i_inode, &dip->i_inode, + err = security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr, &name, &value, &len); if (err) { @@ -882,7 +883,7 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, if (error) goto fail_gunlock2; - error = gfs2_security_init(dip, GFS2_I(inode)); + error = gfs2_security_init(dip, GFS2_I(inode), name); if (error) goto fail_gunlock2; diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 92978658ed18..82faddd1f321 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -215,8 +215,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, no chance of AB-BA deadlock involving its f->sem). */ mutex_unlock(&f->sem); - ret = jffs2_do_create(c, dir_f, f, ri, - dentry->d_name.name, dentry->d_name.len); + ret = jffs2_do_create(c, dir_f, f, ri, &dentry->d_name); if (ret) goto fail; @@ -386,7 +385,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char jffs2_complete_reservation(c); - ret = jffs2_init_security(inode, dir_i); + ret = jffs2_init_security(inode, dir_i, &dentry->d_name); if (ret) goto fail; @@ -530,7 +529,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) jffs2_complete_reservation(c); - ret = jffs2_init_security(inode, dir_i); + ret = jffs2_init_security(inode, dir_i, &dentry->d_name); if (ret) goto fail; @@ -703,7 +702,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de jffs2_complete_reservation(c); - ret = jffs2_init_security(inode, dir_i); + ret = jffs2_init_security(inode, dir_i, &dentry->d_name); if (ret) goto fail; diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index 5a53d9bdb2b5..e4619b00f7c5 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -401,7 +401,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, unsigned char *buf, uint32_t offset, uint32_t writelen, uint32_t *retlen); int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, - struct jffs2_raw_inode *ri, const char *name, int namelen); + struct jffs2_raw_inode *ri, const struct qstr *qstr); int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f, uint32_t time); int jffs2_do_link(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, diff --git a/fs/jffs2/security.c b/fs/jffs2/security.c index 239f51216a68..cfeb7164b085 100644 --- a/fs/jffs2/security.c +++ b/fs/jffs2/security.c @@ -23,14 +23,15 @@ #include "nodelist.h" /* ---- Initial Security Label Attachment -------------- */ -int jffs2_init_security(struct inode *inode, struct inode *dir) +int jffs2_init_security(struct inode *inode, struct inode *dir, + const struct qstr *qstr) { int rc; size_t len; void *value; char *name; - rc = security_inode_init_security(inode, dir, &name, &value, &len); + rc = security_inode_init_security(inode, dir, qstr, &name, &value, &len); if (rc) { if (rc == -EOPNOTSUPP) return 0; diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index c819eb0e982d..30d175b6d290 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -424,7 +424,9 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, return ret; } -int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen) +int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, + struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, + const struct qstr *qstr) { struct jffs2_raw_dirent *rd; struct jffs2_full_dnode *fn; @@ -466,15 +468,15 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str mutex_unlock(&f->sem); jffs2_complete_reservation(c); - ret = jffs2_init_security(&f->vfs_inode, &dir_f->vfs_inode); + ret = jffs2_init_security(&f->vfs_inode, &dir_f->vfs_inode, qstr); if (ret) return ret; ret = jffs2_init_acl_post(&f->vfs_inode); if (ret) return ret; - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen, - ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); + ret = jffs2_reserve_space(c, sizeof(*rd)+qstr->len, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(qstr->len)); if (ret) { /* Eep. */ @@ -493,19 +495,19 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); - rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); + rd->totlen = cpu_to_je32(sizeof(*rd) + qstr->len); rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); rd->pino = cpu_to_je32(dir_f->inocache->ino); rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = ri->ino; rd->mctime = ri->ctime; - rd->nsize = namelen; + rd->nsize = qstr->len; rd->type = DT_REG; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); - rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); + rd->name_crc = cpu_to_je32(crc32(0, qstr->name, qstr->len)); - fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, ALLOC_NORMAL); + fd = jffs2_write_dirent(c, dir_f, rd, qstr->name, qstr->len, ALLOC_NORMAL); jffs2_free_raw_dirent(rd); diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h index cf4f5759b42b..7be4beb306f3 100644 --- a/fs/jffs2/xattr.h +++ b/fs/jffs2/xattr.h @@ -121,10 +121,11 @@ extern ssize_t jffs2_listxattr(struct dentry *, char *, size_t); #endif /* CONFIG_JFFS2_FS_XATTR */ #ifdef CONFIG_JFFS2_FS_SECURITY -extern int jffs2_init_security(struct inode *inode, struct inode *dir); +extern int jffs2_init_security(struct inode *inode, struct inode *dir, + const struct qstr *qstr); extern const struct xattr_handler jffs2_security_xattr_handler; #else -#define jffs2_init_security(inode,dir) (0) +#define jffs2_init_security(inode,dir,qstr) (0) #endif /* CONFIG_JFFS2_FS_SECURITY */ #endif /* _JFFS2_FS_XATTR_H_ */ diff --git a/fs/jfs/jfs_xattr.h b/fs/jfs/jfs_xattr.h index 88b6cc535bf2..e9e100fd7c09 100644 --- a/fs/jfs/jfs_xattr.h +++ b/fs/jfs/jfs_xattr.h @@ -62,10 +62,11 @@ extern ssize_t jfs_listxattr(struct dentry *, char *, size_t); extern int jfs_removexattr(struct dentry *, const char *); #ifdef CONFIG_JFS_SECURITY -extern int jfs_init_security(tid_t, struct inode *, struct inode *); +extern int jfs_init_security(tid_t, struct inode *, struct inode *, + const struct qstr *); #else static inline int jfs_init_security(tid_t tid, struct inode *inode, - struct inode *dir) + struct inode *dir, const struct qstr *qstr) { return 0; } diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 4414e3a42264..030b9174e416 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -115,7 +115,7 @@ static int jfs_create(struct inode *dip, struct dentry *dentry, int mode, if (rc) goto out3; - rc = jfs_init_security(tid, ip, dip); + rc = jfs_init_security(tid, ip, dip, &dentry->d_name); if (rc) { txAbort(tid, 0); goto out3; @@ -253,7 +253,7 @@ static int jfs_mkdir(struct inode *dip, struct dentry *dentry, int mode) if (rc) goto out3; - rc = jfs_init_security(tid, ip, dip); + rc = jfs_init_security(tid, ip, dip, &dentry->d_name); if (rc) { txAbort(tid, 0); goto out3; @@ -932,7 +932,7 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry, mutex_lock_nested(&JFS_IP(dip)->commit_mutex, COMMIT_MUTEX_PARENT); mutex_lock_nested(&JFS_IP(ip)->commit_mutex, COMMIT_MUTEX_CHILD); - rc = jfs_init_security(tid, ip, dip); + rc = jfs_init_security(tid, ip, dip, &dentry->d_name); if (rc) goto out3; @@ -1395,7 +1395,7 @@ static int jfs_mknod(struct inode *dir, struct dentry *dentry, if (rc) goto out3; - rc = jfs_init_security(tid, ip, dir); + rc = jfs_init_security(tid, ip, dir, &dentry->d_name); if (rc) { txAbort(tid, 0); goto out3; diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c index 2d7f165d0f1d..3fa4c32272df 100644 --- a/fs/jfs/xattr.c +++ b/fs/jfs/xattr.c @@ -1091,7 +1091,8 @@ int jfs_removexattr(struct dentry *dentry, const char *name) } #ifdef CONFIG_JFS_SECURITY -int jfs_init_security(tid_t tid, struct inode *inode, struct inode *dir) +int jfs_init_security(tid_t tid, struct inode *inode, struct inode *dir, + const struct qstr *qstr) { int rc; size_t len; @@ -1099,7 +1100,8 @@ int jfs_init_security(tid_t tid, struct inode *inode, struct inode *dir) char *suffix; char *name; - rc = security_inode_init_security(inode, dir, &suffix, &value, &len); + rc = security_inode_init_security(inode, dir, qstr, &suffix, &value, + &len); if (rc) { if (rc == -EOPNOTSUPP) return 0; diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index d14cad6e2e41..bd8d6461a68b 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -294,7 +294,7 @@ static int ocfs2_mknod(struct inode *dir, } /* get security xattr */ - status = ocfs2_init_security_get(inode, dir, &si); + status = ocfs2_init_security_get(inode, dir, &dentry->d_name, &si); if (status) { if (status == -EOPNOTSUPP) si.enable = 0; @@ -1665,7 +1665,7 @@ static int ocfs2_symlink(struct inode *dir, } /* get security xattr */ - status = ocfs2_init_security_get(inode, dir, &si); + status = ocfs2_init_security_get(inode, dir, &dentry->d_name, &si); if (status) { if (status == -EOPNOTSUPP) si.enable = 0; diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index b5f9160e93e9..cd3f5b4832ef 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -4325,7 +4325,8 @@ static int ocfs2_reflink(struct dentry *old_dentry, struct inode *dir, /* If the security isn't preserved, we need to re-initialize them. */ if (!preserve) { - error = ocfs2_init_security_and_acl(dir, new_orphan_inode); + error = ocfs2_init_security_and_acl(dir, new_orphan_inode, + &new_dentry->d_name); if (error) mlog_errno(error); } diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 67cd43914641..6bb602486c6b 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -7185,7 +7185,8 @@ out: * must not hold any lock expect i_mutex. */ int ocfs2_init_security_and_acl(struct inode *dir, - struct inode *inode) + struct inode *inode, + const struct qstr *qstr) { int ret = 0; struct buffer_head *dir_bh = NULL; @@ -7193,7 +7194,7 @@ int ocfs2_init_security_and_acl(struct inode *dir, .enable = 1, }; - ret = ocfs2_init_security_get(inode, dir, &si); + ret = ocfs2_init_security_get(inode, dir, qstr, &si); if (!ret) { ret = ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_SECURITY, si.name, si.value, si.value_len, @@ -7261,13 +7262,14 @@ static int ocfs2_xattr_security_set(struct dentry *dentry, const char *name, int ocfs2_init_security_get(struct inode *inode, struct inode *dir, + const struct qstr *qstr, struct ocfs2_security_xattr_info *si) { /* check whether ocfs2 support feature xattr */ if (!ocfs2_supports_xattr(OCFS2_SB(dir->i_sb))) return -EOPNOTSUPP; - return security_inode_init_security(inode, dir, &si->name, &si->value, - &si->value_len); + return security_inode_init_security(inode, dir, qstr, &si->name, + &si->value, &si->value_len); } int ocfs2_init_security_set(handle_t *handle, diff --git a/fs/ocfs2/xattr.h b/fs/ocfs2/xattr.h index aa64bb37a65b..d63cfb72316b 100644 --- a/fs/ocfs2/xattr.h +++ b/fs/ocfs2/xattr.h @@ -57,6 +57,7 @@ int ocfs2_has_inline_xattr_value_outside(struct inode *inode, struct ocfs2_dinode *di); int ocfs2_xattr_remove(struct inode *, struct buffer_head *); int ocfs2_init_security_get(struct inode *, struct inode *, + const struct qstr *, struct ocfs2_security_xattr_info *); int ocfs2_init_security_set(handle_t *, struct inode *, struct buffer_head *, @@ -94,5 +95,6 @@ int ocfs2_reflink_xattrs(struct inode *old_inode, struct buffer_head *new_bh, bool preserve_security); int ocfs2_init_security_and_acl(struct inode *dir, - struct inode *inode); + struct inode *inode, + const struct qstr *qstr); #endif /* OCFS2_XATTR_H */ diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c index ba5f51ec3458..d5b22ed06779 100644 --- a/fs/reiserfs/namei.c +++ b/fs/reiserfs/namei.c @@ -593,7 +593,7 @@ static int reiserfs_create(struct inode *dir, struct dentry *dentry, int mode, new_inode_init(inode, dir, mode); jbegin_count += reiserfs_cache_default_acl(dir); - retval = reiserfs_security_init(dir, inode, &security); + retval = reiserfs_security_init(dir, inode, &dentry->d_name, &security); if (retval < 0) { drop_new_inode(inode); return retval; @@ -667,7 +667,7 @@ static int reiserfs_mknod(struct inode *dir, struct dentry *dentry, int mode, new_inode_init(inode, dir, mode); jbegin_count += reiserfs_cache_default_acl(dir); - retval = reiserfs_security_init(dir, inode, &security); + retval = reiserfs_security_init(dir, inode, &dentry->d_name, &security); if (retval < 0) { drop_new_inode(inode); return retval; @@ -747,7 +747,7 @@ static int reiserfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) new_inode_init(inode, dir, mode); jbegin_count += reiserfs_cache_default_acl(dir); - retval = reiserfs_security_init(dir, inode, &security); + retval = reiserfs_security_init(dir, inode, &dentry->d_name, &security); if (retval < 0) { drop_new_inode(inode); return retval; @@ -1032,7 +1032,8 @@ static int reiserfs_symlink(struct inode *parent_dir, } new_inode_init(inode, parent_dir, mode); - retval = reiserfs_security_init(parent_dir, inode, &security); + retval = reiserfs_security_init(parent_dir, inode, &dentry->d_name, + &security); if (retval < 0) { drop_new_inode(inode); return retval; diff --git a/fs/reiserfs/xattr_security.c b/fs/reiserfs/xattr_security.c index 237c6928d3c6..ef66c18a9332 100644 --- a/fs/reiserfs/xattr_security.c +++ b/fs/reiserfs/xattr_security.c @@ -54,6 +54,7 @@ static size_t security_list(struct dentry *dentry, char *list, size_t list_len, * of blocks needed for the transaction. If successful, reiserfs_security * must be released using reiserfs_security_free when the caller is done. */ int reiserfs_security_init(struct inode *dir, struct inode *inode, + const struct qstr *qstr, struct reiserfs_security_handle *sec) { int blocks = 0; @@ -65,7 +66,7 @@ int reiserfs_security_init(struct inode *dir, struct inode *inode, if (IS_PRIVATE(dir)) return 0; - error = security_inode_init_security(inode, dir, &sec->name, + error = security_inode_init_security(inode, dir, qstr, &sec->name, &sec->value, &sec->length); if (error) { if (error == -EOPNOTSUPP) diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c index 94d5fd6a2973..d9298cf60266 100644 --- a/fs/xfs/linux-2.6/xfs_iops.c +++ b/fs/xfs/linux-2.6/xfs_iops.c @@ -103,7 +103,8 @@ xfs_mark_inode_dirty( STATIC int xfs_init_security( struct inode *inode, - struct inode *dir) + struct inode *dir, + const struct qstr *qstr) { struct xfs_inode *ip = XFS_I(inode); size_t length; @@ -111,7 +112,7 @@ xfs_init_security( unsigned char *name; int error; - error = security_inode_init_security(inode, dir, (char **)&name, + error = security_inode_init_security(inode, dir, qstr, (char **)&name, &value, &length); if (error) { if (error == -EOPNOTSUPP) @@ -195,7 +196,7 @@ xfs_vn_mknod( inode = VFS_I(ip); - error = xfs_init_security(inode, dir); + error = xfs_init_security(inode, dir, &dentry->d_name); if (unlikely(error)) goto out_cleanup_inode; @@ -368,7 +369,7 @@ xfs_vn_symlink( inode = VFS_I(cip); - error = xfs_init_security(inode, dir); + error = xfs_init_security(inode, dir, &dentry->d_name); if (unlikely(error)) goto out_cleanup_inode; diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index 6ce1bca01724..87312a81daba 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -874,7 +874,8 @@ extern int ext3fs_dirhash(const char *name, int len, struct dx_hash_info *hinfo); /* ialloc.c */ -extern struct inode * ext3_new_inode (handle_t *, struct inode *, int); +extern struct inode * ext3_new_inode (handle_t *, struct inode *, + const struct qstr *, int); extern void ext3_free_inode (handle_t *, struct inode *); extern struct inode * ext3_orphan_get (struct super_block *, unsigned long); extern unsigned long ext3_count_free_inodes (struct super_block *); diff --git a/include/linux/reiserfs_xattr.h b/include/linux/reiserfs_xattr.h index 3b94c91f20a6..6deef5dc95fb 100644 --- a/include/linux/reiserfs_xattr.h +++ b/include/linux/reiserfs_xattr.h @@ -63,6 +63,7 @@ extern const struct xattr_handler reiserfs_xattr_trusted_handler; extern const struct xattr_handler reiserfs_xattr_security_handler; #ifdef CONFIG_REISERFS_FS_SECURITY int reiserfs_security_init(struct inode *dir, struct inode *inode, + const struct qstr *qstr, struct reiserfs_security_handle *sec); int reiserfs_security_write(struct reiserfs_transaction_handle *th, struct inode *inode, @@ -130,6 +131,7 @@ static inline void reiserfs_init_xattr_rwsem(struct inode *inode) #ifndef CONFIG_REISERFS_FS_SECURITY static inline int reiserfs_security_init(struct inode *dir, struct inode *inode, + const struct qstr *qstr, struct reiserfs_security_handle *sec) { return 0; diff --git a/include/linux/security.h b/include/linux/security.h index c642bb8b8f5a..05dd5a64aa76 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -315,6 +316,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * then it should return -EOPNOTSUPP to skip this processing. * @inode contains the inode structure of the newly created inode. * @dir contains the inode structure of the parent directory. + * @qstr contains the last path component of the new object * @name will be set to the allocated name suffix (e.g. selinux). * @value will be set to the allocated attribute value. * @len will be set to the length of the value. @@ -1435,7 +1437,8 @@ struct security_operations { int (*inode_alloc_security) (struct inode *inode); void (*inode_free_security) (struct inode *inode); int (*inode_init_security) (struct inode *inode, struct inode *dir, - char **name, void **value, size_t *len); + const struct qstr *qstr, char **name, + void **value, size_t *len); int (*inode_create) (struct inode *dir, struct dentry *dentry, int mode); int (*inode_link) (struct dentry *old_dentry, @@ -1696,7 +1699,8 @@ int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts); int security_inode_alloc(struct inode *inode); void security_inode_free(struct inode *inode); int security_inode_init_security(struct inode *inode, struct inode *dir, - char **name, void **value, size_t *len); + const struct qstr *qstr, char **name, + void **value, size_t *len); int security_inode_create(struct inode *dir, struct dentry *dentry, int mode); int security_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry); @@ -2023,6 +2027,7 @@ static inline void security_inode_free(struct inode *inode) static inline int security_inode_init_security(struct inode *inode, struct inode *dir, + const struct qstr *qstr, char **name, void **value, size_t *len) diff --git a/mm/shmem.c b/mm/shmem.c index 5ee67c990602..7c9cdc6fe137 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1843,8 +1843,9 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE); if (inode) { - error = security_inode_init_security(inode, dir, NULL, NULL, - NULL); + error = security_inode_init_security(inode, dir, + &dentry->d_name, NULL, + NULL, NULL); if (error) { if (error != -EOPNOTSUPP) { iput(inode); @@ -1983,8 +1984,8 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s if (!inode) return -ENOSPC; - error = security_inode_init_security(inode, dir, NULL, NULL, - NULL); + error = security_inode_init_security(inode, dir, &dentry->d_name, NULL, + NULL, NULL); if (error) { if (error != -EOPNOTSUPP) { iput(inode); diff --git a/security/capability.c b/security/capability.c index 2a5df2b7da83..383d14dc12ef 100644 --- a/security/capability.c +++ b/security/capability.c @@ -118,7 +118,8 @@ static void cap_inode_free_security(struct inode *inode) } static int cap_inode_init_security(struct inode *inode, struct inode *dir, - char **name, void **value, size_t *len) + const struct qstr *qstr, char **name, + void **value, size_t *len) { return -EOPNOTSUPP; } diff --git a/security/security.c b/security/security.c index b84a89dd59c6..4830f36e1ab5 100644 --- a/security/security.c +++ b/security/security.c @@ -336,11 +336,13 @@ void security_inode_free(struct inode *inode) } int security_inode_init_security(struct inode *inode, struct inode *dir, - char **name, void **value, size_t *len) + const struct qstr *qstr, char **name, + void **value, size_t *len) { if (unlikely(IS_PRIVATE(inode))) return -EOPNOTSUPP; - return security_ops->inode_init_security(inode, dir, name, value, len); + return security_ops->inode_init_security(inode, dir, qstr, name, value, + len); } EXPORT_SYMBOL(security_inode_init_security); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e276eb468536..099bbd07732f 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -2509,8 +2510,8 @@ static void selinux_inode_free_security(struct inode *inode) } static int selinux_inode_init_security(struct inode *inode, struct inode *dir, - char **name, void **value, - size_t *len) + const struct qstr *qstr, char **name, + void **value, size_t *len) { const struct task_security_struct *tsec = current_security(); struct inode_security_struct *dsec; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 123a499ded37..0c91a906b3f4 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "smack.h" #define task_security(task) (task_cred_xxx((task), security)) @@ -501,6 +502,7 @@ static void smack_inode_free_security(struct inode *inode) * smack_inode_init_security - copy out the smack from an inode * @inode: the inode * @dir: unused + * @qstr: unused * @name: where to put the attribute name * @value: where to put the attribute value * @len: where to put the length of the attribute @@ -508,7 +510,8 @@ static void smack_inode_free_security(struct inode *inode) * Returns 0 if it all works out, -ENOMEM if there's no memory */ static int smack_inode_init_security(struct inode *inode, struct inode *dir, - char **name, void **value, size_t *len) + const struct qstr *qstr, char **name, + void **value, size_t *len) { char *isp = smk_of_inode(inode); char *dsp = smk_of_inode(dir); -- cgit v1.2.3 From 4916ca401e3051dad326ddd69765bd0e3f32fb9b Mon Sep 17 00:00:00 2001 From: Lucian Adrian Grijincu Date: Tue, 1 Feb 2011 18:44:56 +0200 Subject: security: remove unused security_sysctl hook The only user for this hook was selinux. sysctl routes every call through /proc/sys/. Selinux and other security modules use the file system checks for sysctl too, so no need for this hook any more. Signed-off-by: Lucian Adrian Grijincu Signed-off-by: Eric Paris --- include/linux/security.h | 13 ------------- kernel/sysctl.c | 5 ----- security/capability.c | 6 ------ security/security.c | 5 ----- 4 files changed, 29 deletions(-) (limited to 'include') diff --git a/include/linux/security.h b/include/linux/security.h index 05dd5a64aa76..14167f2eb35a 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1259,12 +1259,6 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @cap contains the capability . * @audit: Whether to write an audit message or not * Return 0 if the capability is granted for @tsk. - * @sysctl: - * Check permission before accessing the @table sysctl variable in the - * manner specified by @op. - * @table contains the ctl_table structure for the sysctl variable. - * @op contains the operation (001 = search, 002 = write, 004 = read). - * Return 0 if permission is granted. * @syslog: * Check permission before accessing the kernel message ring or changing * logging to the console. @@ -1385,7 +1379,6 @@ struct security_operations { const kernel_cap_t *permitted); int (*capable) (struct task_struct *tsk, const struct cred *cred, int cap, int audit); - int (*sysctl) (struct ctl_table *table, int op); int (*quotactl) (int cmds, int type, int id, struct super_block *sb); int (*quota_on) (struct dentry *dentry); int (*syslog) (int type); @@ -1668,7 +1661,6 @@ int security_capset(struct cred *new, const struct cred *old, int security_capable(int cap); int security_real_capable(struct task_struct *tsk, int cap); int security_real_capable_noaudit(struct task_struct *tsk, int cap); -int security_sysctl(struct ctl_table *table, int op); int security_quotactl(int cmds, int type, int id, struct super_block *sb); int security_quota_on(struct dentry *dentry); int security_syslog(int type); @@ -1887,11 +1879,6 @@ int security_real_capable_noaudit(struct task_struct *tsk, int cap) return ret; } -static inline int security_sysctl(struct ctl_table *table, int op) -{ - return 0; -} - static inline int security_quotactl(int cmds, int type, int id, struct super_block *sb) { diff --git a/kernel/sysctl.c b/kernel/sysctl.c index ae5cbb1e3ced..e24254c27eaf 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1691,13 +1691,8 @@ static int test_perm(int mode, int op) int sysctl_perm(struct ctl_table_root *root, struct ctl_table *table, int op) { - int error; int mode; - error = security_sysctl(table, op & (MAY_READ | MAY_WRITE | MAY_EXEC)); - if (error) - return error; - if (root->permissions) mode = root->permissions(root, current->nsproxy, table); else diff --git a/security/capability.c b/security/capability.c index 383d14dc12ef..85b67c8632df 100644 --- a/security/capability.c +++ b/security/capability.c @@ -12,11 +12,6 @@ #include -static int cap_sysctl(ctl_table *table, int op) -{ - return 0; -} - static int cap_syslog(int type) { return 0; @@ -881,7 +876,6 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, capable); set_to_cap_if_null(ops, quotactl); set_to_cap_if_null(ops, quota_on); - set_to_cap_if_null(ops, sysctl); set_to_cap_if_null(ops, syslog); set_to_cap_if_null(ops, settime); set_to_cap_if_null(ops, vm_enough_memory); diff --git a/security/security.c b/security/security.c index 4830f36e1ab5..8f28685ee0d9 100644 --- a/security/security.c +++ b/security/security.c @@ -182,11 +182,6 @@ int security_real_capable_noaudit(struct task_struct *tsk, int cap) return ret; } -int security_sysctl(struct ctl_table *table, int op) -{ - return security_ops->sysctl(table, op); -} - int security_quotactl(int cmds, int type, int id, struct super_block *sb) { return security_ops->quotactl(cmds, type, id, sb); -- cgit v1.2.3 From a13676476e289ba03a23e27df130c7f33ab00e2f Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Tue, 1 Feb 2011 18:27:51 +0100 Subject: IPVS: Remove unused variables These variables are unused as a result of the recent netns work. Signed-off-by: Simon Horman Acked-by: Randy Dunlap Signed-off-by: Hans Schillstrom Tested-by: Hans Schillstrom Signed-off-by: Patrick McHardy --- include/net/ip_vs.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index b23bea62f708..5d75feadf4f4 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -1109,8 +1109,6 @@ extern int ip_vs_icmp_xmit_v6 * we are loaded. Just set ip_vs_drop_rate to 'n' and * we start to drop 1/rate of the packets */ -extern int ip_vs_drop_rate; -extern int ip_vs_drop_counter; static inline int ip_vs_todrop(struct netns_ipvs *ipvs) { -- cgit v1.2.3 From e3e241b2769b27669d05f0a05083acd21b4faa2c Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 1 Feb 2011 18:52:42 +0100 Subject: netfilter: ipset: install ipset related header files Signed-off-by: Patrick McHardy --- include/linux/netfilter/Kbuild | 3 +++ include/linux/netfilter/ipset/Kbuild | 4 ++++ include/linux/netfilter/xt_set.h | 1 + 3 files changed, 8 insertions(+) create mode 100644 include/linux/netfilter/ipset/Kbuild (limited to 'include') diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index 89c0d1e20d72..ba19544cce94 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild @@ -1,3 +1,5 @@ +header-y += ipset/ + header-y += nf_conntrack_common.h header-y += nf_conntrack_ftp.h header-y += nf_conntrack_sctp.h @@ -55,6 +57,7 @@ header-y += xt_quota.h header-y += xt_rateest.h header-y += xt_realm.h header-y += xt_recent.h +header-y += xt_set.h header-y += xt_sctp.h header-y += xt_socket.h header-y += xt_state.h diff --git a/include/linux/netfilter/ipset/Kbuild b/include/linux/netfilter/ipset/Kbuild new file mode 100644 index 000000000000..601fe71d34d5 --- /dev/null +++ b/include/linux/netfilter/ipset/Kbuild @@ -0,0 +1,4 @@ +header-y += ip_set.h +header-y += ip_set_bitmap.h +header-y += ip_set_hash.h +header-y += ip_set_list.h diff --git a/include/linux/netfilter/xt_set.h b/include/linux/netfilter/xt_set.h index 69b2bd1fb818..081f1ded2842 100644 --- a/include/linux/netfilter/xt_set.h +++ b/include/linux/netfilter/xt_set.h @@ -1,6 +1,7 @@ #ifndef _XT_SET_H #define _XT_SET_H +#include #include /* Revision 0 interface: backward compatible with netfilter/iptables */ -- cgit v1.2.3 From 5348ba85a02ffe80a8af33a524b6610966760d3d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 1 Feb 2011 15:30:56 -0800 Subject: ipv4: Update some fib_hash centric interface names. fib_hash_init() --> fib_trie_init() fib_hash_table() --> fib_trie_table() Signed-off-by: David S. Miller --- include/net/ip_fib.h | 6 +++--- net/ipv4/fib_frontend.c | 8 ++++---- net/ipv4/fib_trie.c | 5 ++--- 3 files changed, 9 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 819d61ca25cb..08b46b8c3031 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -228,9 +228,9 @@ extern int fib_sync_up(struct net_device *dev); extern __be32 __fib_res_prefsrc(struct fib_result *res); extern void fib_select_multipath(const struct flowi *flp, struct fib_result *res); -/* Exported by fib_{hash|trie}.c */ -extern void fib_hash_init(void); -extern struct fib_table *fib_hash_table(u32 id); +/* Exported by fib_trie.c */ +extern void fib_trie_init(void); +extern struct fib_table *fib_trie_table(u32 id); static inline void fib_combine_itag(u32 *itag, struct fib_result *res) { diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 930768ba49af..2a49c061b34c 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -51,11 +51,11 @@ static int __net_init fib4_rules_init(struct net *net) { struct fib_table *local_table, *main_table; - local_table = fib_hash_table(RT_TABLE_LOCAL); + local_table = fib_trie_table(RT_TABLE_LOCAL); if (local_table == NULL) return -ENOMEM; - main_table = fib_hash_table(RT_TABLE_MAIN); + main_table = fib_trie_table(RT_TABLE_MAIN); if (main_table == NULL) goto fail; @@ -82,7 +82,7 @@ struct fib_table *fib_new_table(struct net *net, u32 id) if (tb) return tb; - tb = fib_hash_table(id); + tb = fib_trie_table(id); if (!tb) return NULL; h = id & (FIB_TABLE_HASHSZ - 1); @@ -1086,5 +1086,5 @@ void __init ip_fib_init(void) register_netdevice_notifier(&fib_netdev_notifier); register_inetaddr_notifier(&fib_inetaddr_notifier); - fib_hash_init(); + fib_trie_init(); } diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 16d589c70a92..73cb98475ce6 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1916,7 +1916,7 @@ int fib_table_dump(struct fib_table *tb, struct sk_buff *skb, return skb->len; } -void __init fib_hash_init(void) +void __init fib_trie_init(void) { fn_alias_kmem = kmem_cache_create("ip_fib_alias", sizeof(struct fib_alias), @@ -1929,8 +1929,7 @@ void __init fib_hash_init(void) } -/* Fix more generic FIB names for init later */ -struct fib_table *fib_hash_table(u32 id) +struct fib_table *fib_trie_table(u32 id) { struct fib_table *tb; struct trie *t; -- cgit v1.2.3 From 63a507800c8aca5a1891d598ae13f829346e8e39 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 1 Feb 2011 19:06:46 -0500 Subject: drm/radeon: remove 0x4243 pci id 0x4243 is a PCI bridge, not a GPU. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=33815 Signed-off-by: Alex Deucher Cc: stable@kernel.org Signed-off-by: Dave Airlie --- include/drm/drm_pciids.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index fe29ae328bd9..5ff1194dc2ea 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -28,7 +28,6 @@ {0x1002, 0x4156, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ {0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP}, \ {0x1002, 0x4242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ - {0x1002, 0x4243, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ {0x1002, 0x4336, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \ {0x1002, 0x4337, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \ {0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \ -- cgit v1.2.3 From 1e6d767924c74929c0cfe839ae8f37bcee9e544e Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Tue, 1 Feb 2011 13:50:58 +0000 Subject: time: Correct the *settime* parameters Both settimeofday() and clock_settime() promise with a 'const' attribute not to alter the arguments passed in. This patch adds the missing 'const' attribute into the various kernel functions implementing these calls. Signed-off-by: Richard Cochran Acked-by: John Stultz LKML-Reference: <20110201134417.545698637@linutronix.de> Signed-off-by: Thomas Gleixner --- drivers/char/mmtimer.c | 2 +- include/linux/posix-timers.h | 5 +++-- include/linux/security.h | 9 +++++---- include/linux/time.h | 5 +++-- kernel/posix-timers.c | 4 ++-- kernel/time.c | 2 +- kernel/time/timekeeping.c | 2 +- security/commoncap.c | 2 +- security/security.c | 2 +- 9 files changed, 18 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c index e6d75627c6c8..ecd0082502ef 100644 --- a/drivers/char/mmtimer.c +++ b/drivers/char/mmtimer.c @@ -487,7 +487,7 @@ static int sgi_clock_get(clockid_t clockid, struct timespec *tp) return 0; }; -static int sgi_clock_set(clockid_t clockid, struct timespec *tp) +static int sgi_clock_set(const clockid_t clockid, const struct timespec *tp) { u64 nsec; diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index 3e23844a6990..b2c14cbd47a6 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -69,7 +69,8 @@ struct k_itimer { struct k_clock { int res; /* in nanoseconds */ int (*clock_getres) (const clockid_t which_clock, struct timespec *tp); - int (*clock_set) (const clockid_t which_clock, struct timespec * tp); + int (*clock_set) (const clockid_t which_clock, + const struct timespec *tp); int (*clock_get) (const clockid_t which_clock, struct timespec * tp); int (*timer_create) (struct k_itimer *timer); int (*nsleep) (const clockid_t which_clock, int flags, @@ -89,7 +90,7 @@ void register_posix_clock(const clockid_t clock_id, struct k_clock *new_clock); /* error handlers for timer_create, nanosleep and settime */ int do_posix_clock_nonanosleep(const clockid_t, int flags, struct timespec *, struct timespec __user *); -int do_posix_clock_nosettime(const clockid_t, struct timespec *tp); +int do_posix_clock_nosettime(const clockid_t, const struct timespec *tp); /* function to call to trigger timer event */ int posix_timer_event(struct k_itimer *timr, int si_private); diff --git a/include/linux/security.h b/include/linux/security.h index c642bb8b8f5a..c096aa6fca60 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -53,7 +53,7 @@ struct audit_krule; */ extern int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap, int audit); -extern int cap_settime(struct timespec *ts, struct timezone *tz); +extern int cap_settime(const struct timespec *ts, const struct timezone *tz); extern int cap_ptrace_access_check(struct task_struct *child, unsigned int mode); extern int cap_ptrace_traceme(struct task_struct *parent); extern int cap_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); @@ -1387,7 +1387,7 @@ struct security_operations { int (*quotactl) (int cmds, int type, int id, struct super_block *sb); int (*quota_on) (struct dentry *dentry); int (*syslog) (int type); - int (*settime) (struct timespec *ts, struct timezone *tz); + int (*settime) (const struct timespec *ts, const struct timezone *tz); int (*vm_enough_memory) (struct mm_struct *mm, long pages); int (*bprm_set_creds) (struct linux_binprm *bprm); @@ -1669,7 +1669,7 @@ int security_sysctl(struct ctl_table *table, int op); int security_quotactl(int cmds, int type, int id, struct super_block *sb); int security_quota_on(struct dentry *dentry); int security_syslog(int type); -int security_settime(struct timespec *ts, struct timezone *tz); +int security_settime(const struct timespec *ts, const struct timezone *tz); int security_vm_enough_memory(long pages); int security_vm_enough_memory_mm(struct mm_struct *mm, long pages); int security_vm_enough_memory_kern(long pages); @@ -1904,7 +1904,8 @@ static inline int security_syslog(int type) return 0; } -static inline int security_settime(struct timespec *ts, struct timezone *tz) +static inline int security_settime(const struct timespec *ts, + const struct timezone *tz) { return cap_settime(ts, tz); } diff --git a/include/linux/time.h b/include/linux/time.h index 38c5206c2673..7c44e7778033 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -145,8 +145,9 @@ static inline u32 arch_gettimeoffset(void) { return 0; } #endif extern void do_gettimeofday(struct timeval *tv); -extern int do_settimeofday(struct timespec *tv); -extern int do_sys_settimeofday(struct timespec *tv, struct timezone *tz); +extern int do_settimeofday(const struct timespec *tv); +extern int do_sys_settimeofday(const struct timespec *tv, + const struct timezone *tz); #define do_posix_clock_monotonic_gettime(ts) ktime_get_ts(ts) extern long do_utimes(int dfd, const char __user *filename, struct timespec *times, int flags); struct itimerval; diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index 93bd2eb2bc53..21b7ca205f38 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c @@ -192,7 +192,7 @@ static int common_clock_get(clockid_t which_clock, struct timespec *tp) } static inline int common_clock_set(const clockid_t which_clock, - struct timespec *tp) + const struct timespec *tp) { return do_sys_settimeofday(tp, NULL); } @@ -928,7 +928,7 @@ void exit_itimers(struct signal_struct *sig) } /* Not available / possible... functions */ -int do_posix_clock_nosettime(const clockid_t clockid, struct timespec *tp) +int do_posix_clock_nosettime(const clockid_t clockid, const struct timespec *tp) { return -EINVAL; } diff --git a/kernel/time.c b/kernel/time.c index a31b51220ac6..5cb80533d8b5 100644 --- a/kernel/time.c +++ b/kernel/time.c @@ -150,7 +150,7 @@ static inline void warp_clock(void) * various programs will get confused when the clock gets warped. */ -int do_sys_settimeofday(struct timespec *tv, struct timezone *tz) +int do_sys_settimeofday(const struct timespec *tv, const struct timezone *tz) { static int firsttime = 1; int error = 0; diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 02c13a313d15..4f9f65b91323 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -353,7 +353,7 @@ EXPORT_SYMBOL(do_gettimeofday); * * Sets the time of day to the new time and update NTP and notify hrtimers */ -int do_settimeofday(struct timespec *tv) +int do_settimeofday(const struct timespec *tv) { struct timespec ts_delta; unsigned long flags; diff --git a/security/commoncap.c b/security/commoncap.c index 64c2ed9c9015..dbfdaed4cc66 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -93,7 +93,7 @@ int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap, * Determine whether the current process may set the system clock and timezone * information, returning 0 if permission granted, -ve if denied. */ -int cap_settime(struct timespec *ts, struct timezone *tz) +int cap_settime(const struct timespec *ts, const struct timezone *tz) { if (!capable(CAP_SYS_TIME)) return -EPERM; diff --git a/security/security.c b/security/security.c index 739e40362f44..b995428f1c96 100644 --- a/security/security.c +++ b/security/security.c @@ -202,7 +202,7 @@ int security_syslog(int type) return security_ops->syslog(type); } -int security_settime(struct timespec *ts, struct timezone *tz) +int security_settime(const struct timespec *ts, const struct timezone *tz) { return security_ops->settime(ts, tz); } -- cgit v1.2.3 From 1976945eeaab5fa461735a6225a82c3cf1e65d62 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 1 Feb 2011 13:51:06 +0000 Subject: posix-timers: Introduce clock_posix_cpu The CLOCK_DISPATCH() macro is a horrible magic. We call common functions if a function pointer is not set. That's just backwards. To support dynamic file decriptor based clocks we need to cleanup that dispatch logic. Create a k_clock struct clock_posix_cpu which has all the posix-cpu-timer functions filled in. After the cleanup the functions can be made static. Signed-off-by: Thomas Gleixner Acked-by: John Stultz Tested-by: Richard Cochran LKML-Reference: <20110201134417.841974553@linutronix.de> --- include/linux/posix-timers.h | 2 ++ kernel/posix-cpu-timers.c | 12 ++++++++++++ 2 files changed, 14 insertions(+) (limited to 'include') diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index b2c14cbd47a6..1330ff331526 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -85,6 +85,8 @@ struct k_clock { struct itimerspec * cur_setting); }; +extern struct k_clock clock_posix_cpu; + void register_posix_clock(const clockid_t clock_id, struct k_clock *new_clock); /* error handlers for timer_create, nanosleep and settime */ diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index 11b91dc0992b..816cd49a5ad9 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c @@ -1604,6 +1604,18 @@ static long thread_cpu_nsleep_restart(struct restart_block *restart_block) return -EINVAL; } +struct k_clock clock_posix_cpu = { + .clock_getres = posix_cpu_clock_getres, + .clock_set = posix_cpu_clock_set, + .clock_get = posix_cpu_clock_get, + .timer_create = posix_cpu_timer_create, + .nsleep = posix_cpu_nsleep, + .nsleep_restart = posix_cpu_nsleep_restart, + .timer_set = posix_cpu_timer_set, + .timer_del = posix_cpu_timer_del, + .timer_get = posix_cpu_timer_get, +}; + static __init int init_posix_cpu_timers(void) { struct k_clock process = { -- cgit v1.2.3 From a5cd2880106cb2c79b3fe24f1c53dadba6a542a0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 1 Feb 2011 13:51:11 +0000 Subject: posix-timers: Convert clock_nanosleep to clockid_to_kclock() Use the new kclock decoding function in clock_nanosleep and cleanup all kclocks which use the default functions. Signed-off-by: Thomas Gleixner Acked-by: John Stultz Tested-by: Richard Cochran LKML-Reference: <20110201134418.034175556@linutronix.de> --- drivers/char/mmtimer.c | 1 - include/linux/posix-timers.h | 2 -- kernel/posix-timers.c | 26 +++++++------------------- 3 files changed, 7 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c index fd51cd8ee063..262d10453cb8 100644 --- a/drivers/char/mmtimer.c +++ b/drivers/char/mmtimer.c @@ -768,7 +768,6 @@ static struct k_clock sgi_clock = { .clock_set = sgi_clock_set, .clock_get = sgi_clock_get, .timer_create = sgi_timer_create, - .nsleep = do_posix_clock_nonanosleep, .timer_set = sgi_timer_set, .timer_del = sgi_timer_del, .timer_get = sgi_timer_get diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index 1330ff331526..cd6da067bce1 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -90,8 +90,6 @@ extern struct k_clock clock_posix_cpu; void register_posix_clock(const clockid_t clock_id, struct k_clock *new_clock); /* error handlers for timer_create, nanosleep and settime */ -int do_posix_clock_nonanosleep(const clockid_t, int flags, struct timespec *, - struct timespec __user *); int do_posix_clock_nosettime(const clockid_t, const struct timespec *tp); /* function to call to trigger timer event */ diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index 14b0a70ffb1e..ee69b216d5c3 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c @@ -216,12 +216,6 @@ static int no_timer_create(struct k_itimer *new_timer) return -EOPNOTSUPP; } -static int no_nsleep(const clockid_t which_clock, int flags, - struct timespec *tsave, struct timespec __user *rmtp) -{ - return -EOPNOTSUPP; -} - /* * Return nonzero if we know a priori this clockid_t value is bogus. */ @@ -282,32 +276,31 @@ static __init int init_posix_timers(void) { struct k_clock clock_realtime = { .clock_getres = hrtimer_get_res, + .nsleep = common_nsleep, }; struct k_clock clock_monotonic = { .clock_getres = hrtimer_get_res, .clock_get = posix_ktime_get_ts, .clock_set = do_posix_clock_nosettime, + .nsleep = common_nsleep, }; struct k_clock clock_monotonic_raw = { .clock_getres = hrtimer_get_res, .clock_get = posix_get_monotonic_raw, .clock_set = do_posix_clock_nosettime, .timer_create = no_timer_create, - .nsleep = no_nsleep, }; struct k_clock clock_realtime_coarse = { .clock_getres = posix_get_coarse_res, .clock_get = posix_get_realtime_coarse, .clock_set = do_posix_clock_nosettime, .timer_create = no_timer_create, - .nsleep = no_nsleep, }; struct k_clock clock_monotonic_coarse = { .clock_getres = posix_get_coarse_res, .clock_get = posix_get_monotonic_coarse, .clock_set = do_posix_clock_nosettime, .timer_create = no_timer_create, - .nsleep = no_nsleep, }; register_posix_clock(CLOCK_REALTIME, &clock_realtime); @@ -952,13 +945,6 @@ int do_posix_clock_nosettime(const clockid_t clockid, const struct timespec *tp) } EXPORT_SYMBOL_GPL(do_posix_clock_nosettime); -int do_posix_clock_nonanosleep(const clockid_t clock, int flags, - struct timespec *t, struct timespec __user *r) -{ - return -ENANOSLEEP_NOTSUP; -} -EXPORT_SYMBOL_GPL(do_posix_clock_nonanosleep); - SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock, const struct timespec __user *, tp) { @@ -1023,10 +1009,13 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags, const struct timespec __user *, rqtp, struct timespec __user *, rmtp) { + struct k_clock *kc = clockid_to_kclock(which_clock); struct timespec t; - if (invalid_clockid(which_clock)) + if (!kc) return -EINVAL; + if (!kc->nsleep) + return -ENANOSLEEP_NOTSUP; if (copy_from_user(&t, rqtp, sizeof (struct timespec))) return -EFAULT; @@ -1034,8 +1023,7 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags, if (!timespec_valid(&t)) return -EINVAL; - return CLOCK_DISPATCH(which_clock, nsleep, - (which_clock, flags, &t, rmtp)); + return kc->nsleep(which_clock, flags, &t, rmtp); } /* -- cgit v1.2.3 From d608c18203a969e5d14572a9861c646d0bb66872 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 1 Feb 2011 13:51:43 +0000 Subject: thread_info: Remove legacy arg0-3 from restart_block posix timers were the last users of the legacy arg0-3 members of restart_block. Remove the cruft. Signed-off-by: Thomas Gleixner Acked-by: John Stultz Tested-by: Richard Cochran LKML-Reference: <20110201134418.326209775@linutronix.de> --- include/linux/thread_info.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h index c90696544176..20fc303947d3 100644 --- a/include/linux/thread_info.h +++ b/include/linux/thread_info.h @@ -18,9 +18,6 @@ struct compat_timespec; struct restart_block { long (*fn)(struct restart_block *); union { - struct { - unsigned long arg0, arg1, arg2, arg3; - }; /* For futex_wait and futex_wait_requeue_pi */ struct { u32 __user *uaddr; -- cgit v1.2.3 From 26f9a4796af330173d790c8d2b5e2efcc489e755 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 1 Feb 2011 13:51:48 +0000 Subject: posix-timers: Convert clock_settime to clockid_to_kclock() Use the new kclock decoding function in clock_settime and cleanup all kclocks which use the default functions. Rename the misnomed common_clock_set() to posix_clock_realtime_set(). Signed-off-by: Thomas Gleixner Acked-by: John Stultz Tested-by: Richard Cochran LKML-Reference: <20110201134418.518851246@linutronix.de> --- include/linux/posix-timers.h | 3 --- kernel/posix-cpu-timers.c | 2 -- kernel/posix-timers.c | 31 ++++++++++++------------------- 3 files changed, 12 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index cd6da067bce1..4aaf0c5c7cea 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -89,9 +89,6 @@ extern struct k_clock clock_posix_cpu; void register_posix_clock(const clockid_t clock_id, struct k_clock *new_clock); -/* error handlers for timer_create, nanosleep and settime */ -int do_posix_clock_nosettime(const clockid_t, const struct timespec *tp); - /* function to call to trigger timer event */ int posix_timer_event(struct k_itimer *timr, int si_private); diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index 8dc4cd7faf89..504fbab10b82 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c @@ -1604,7 +1604,6 @@ static __init int init_posix_cpu_timers(void) struct k_clock process = { .clock_getres = process_cpu_clock_getres, .clock_get = process_cpu_clock_get, - .clock_set = do_posix_clock_nosettime, .timer_create = process_cpu_timer_create, .nsleep = process_cpu_nsleep, .nsleep_restart = process_cpu_nsleep_restart, @@ -1612,7 +1611,6 @@ static __init int init_posix_cpu_timers(void) struct k_clock thread = { .clock_getres = thread_cpu_clock_getres, .clock_get = thread_cpu_clock_get, - .clock_set = do_posix_clock_nosettime, .timer_create = thread_cpu_timer_create, }; struct timespec ts; diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index 4762986814c8..49f358c37708 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c @@ -199,12 +199,6 @@ static int common_clock_get(clockid_t which_clock, struct timespec *tp) return 0; } -static inline int common_clock_set(const clockid_t which_clock, - const struct timespec *tp) -{ - return do_sys_settimeofday(tp, NULL); -} - static int common_timer_create(struct k_itimer *new_timer) { hrtimer_init(&new_timer->it.real.timer, new_timer->it_clock, 0); @@ -232,6 +226,13 @@ static inline int invalid_clockid(const clockid_t which_clock) return 1; } +/* Set clock_realtime */ +static int posix_clock_realtime_set(const clockid_t which_clock, + const struct timespec *tp) +{ + return do_sys_settimeofday(tp, NULL); +} + /* * Get monotonic time for posix timers */ @@ -276,32 +277,29 @@ static __init int init_posix_timers(void) { struct k_clock clock_realtime = { .clock_getres = hrtimer_get_res, + .clock_set = posix_clock_realtime_set, .nsleep = common_nsleep, .nsleep_restart = hrtimer_nanosleep_restart, }; struct k_clock clock_monotonic = { .clock_getres = hrtimer_get_res, .clock_get = posix_ktime_get_ts, - .clock_set = do_posix_clock_nosettime, .nsleep = common_nsleep, .nsleep_restart = hrtimer_nanosleep_restart, }; struct k_clock clock_monotonic_raw = { .clock_getres = hrtimer_get_res, .clock_get = posix_get_monotonic_raw, - .clock_set = do_posix_clock_nosettime, .timer_create = no_timer_create, }; struct k_clock clock_realtime_coarse = { .clock_getres = posix_get_coarse_res, .clock_get = posix_get_realtime_coarse, - .clock_set = do_posix_clock_nosettime, .timer_create = no_timer_create, }; struct k_clock clock_monotonic_coarse = { .clock_getres = posix_get_coarse_res, .clock_get = posix_get_monotonic_coarse, - .clock_set = do_posix_clock_nosettime, .timer_create = no_timer_create, }; @@ -940,24 +938,19 @@ void exit_itimers(struct signal_struct *sig) } } -/* Not available / possible... functions */ -int do_posix_clock_nosettime(const clockid_t clockid, const struct timespec *tp) -{ - return -EINVAL; -} -EXPORT_SYMBOL_GPL(do_posix_clock_nosettime); - SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock, const struct timespec __user *, tp) { + struct k_clock *kc = clockid_to_kclock(which_clock); struct timespec new_tp; - if (invalid_clockid(which_clock)) + if (!kc || !kc->clock_set) return -EINVAL; + if (copy_from_user(&new_tp, tp, sizeof (*tp))) return -EFAULT; - return CLOCK_DISPATCH(which_clock, clock_set, (which_clock, &new_tp)); + return kc->clock_set(which_clock, &new_tp); } SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock, -- cgit v1.2.3 From ebaac757acae0431e2c79c00e09f1debdabbddd7 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 1 Feb 2011 13:51:56 +0000 Subject: posix-timers: Remove useless res field from k_clock The res member of kclock is only used by mmtimer.c, but even there it contains redundant information. Remove the field and fixup mmtimer. Signed-off-by: Thomas Gleixner Acked-by: John Stultz Tested-by: Richard Cochran LKML-Reference: <20110201134418.808714587@linutronix.de> --- drivers/char/mmtimer.c | 5 ++--- include/linux/posix-timers.h | 1 - kernel/posix-timers.c | 2 -- 3 files changed, 2 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c index 141ffaeb976c..ff41eb3eec92 100644 --- a/drivers/char/mmtimer.c +++ b/drivers/char/mmtimer.c @@ -768,12 +768,11 @@ static int sgi_timer_set(struct k_itimer *timr, int flags, static int sgi_clock_getres(const clockid_t which_clock, struct timespec *tp) { tp->tv_sec = 0; - tp->tv_nsec = sgi_clock.res; + tp->tv_nsec = sgi_clock_period; return 0; } static struct k_clock sgi_clock = { - .res = 0, .clock_set = sgi_clock_set, .clock_get = sgi_clock_get, .clock_getres = sgi_clock_getres, @@ -840,7 +839,7 @@ static int __init mmtimer_init(void) (unsigned long) node); } - sgi_clock_period = sgi_clock.res = NSEC_PER_SEC / sn_rtc_cycles_per_second; + sgi_clock_period = NSEC_PER_SEC / sn_rtc_cycles_per_second; register_posix_clock(CLOCK_SGI_CYCLE, &sgi_clock); printk(KERN_INFO "%s: v%s, %ld MHz\n", MMTIMER_DESC, MMTIMER_VERSION, diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index 4aaf0c5c7cea..ef574d177fb6 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -67,7 +67,6 @@ struct k_itimer { }; struct k_clock { - int res; /* in nanoseconds */ int (*clock_getres) (const clockid_t which_clock, struct timespec *tp); int (*clock_set) (const clockid_t which_clock, const struct timespec *tp); diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index 748497fffd0f..f9142a99b5cb 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c @@ -204,8 +204,6 @@ static inline int invalid_clockid(const clockid_t which_clock) return 1; if (posix_clocks[which_clock].clock_getres != NULL) return 0; - if (posix_clocks[which_clock].res != 0) - return 0; return 1; } -- cgit v1.2.3 From bc2c8ea483d73e95fc88f1fc9e7755180f89b892 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 1 Feb 2011 13:52:12 +0000 Subject: posix-timers: Make posix-cpu-timers functions static All functions are accessed via clock_posix_cpu now. So make them static. Signed-off-by: Thomas Gleixner Acked-by: John Stultz Tested-by: Richard Cochran LKML-Reference: <20110201134419.389755466@linutronix.de> --- include/linux/posix-timers.h | 12 ------------ kernel/posix-cpu-timers.c | 27 +++++++++++++++------------ 2 files changed, 15 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index ef574d177fb6..8206255a547c 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -91,18 +91,6 @@ void register_posix_clock(const clockid_t clock_id, struct k_clock *new_clock); /* function to call to trigger timer event */ int posix_timer_event(struct k_itimer *timr, int si_private); -int posix_cpu_clock_getres(const clockid_t which_clock, struct timespec *ts); -int posix_cpu_clock_get(const clockid_t which_clock, struct timespec *ts); -int posix_cpu_clock_set(const clockid_t which_clock, const struct timespec *ts); -int posix_cpu_timer_create(struct k_itimer *timer); -int posix_cpu_nsleep(const clockid_t which_clock, int flags, - struct timespec *rqtp, struct timespec __user *rmtp); -long posix_cpu_nsleep_restart(struct restart_block *restart_block); -int posix_cpu_timer_set(struct k_itimer *timer, int flags, - struct itimerspec *new, struct itimerspec *old); -int posix_cpu_timer_del(struct k_itimer *timer); -void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp); - void posix_cpu_timer_schedule(struct k_itimer *timer); void run_posix_cpu_timers(struct task_struct *task); diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index 504fbab10b82..609e187f43e7 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c @@ -176,7 +176,8 @@ static inline cputime_t virt_ticks(struct task_struct *p) return p->utime; } -int posix_cpu_clock_getres(const clockid_t which_clock, struct timespec *tp) +static int +posix_cpu_clock_getres(const clockid_t which_clock, struct timespec *tp) { int error = check_clock(which_clock); if (!error) { @@ -194,7 +195,8 @@ int posix_cpu_clock_getres(const clockid_t which_clock, struct timespec *tp) return error; } -int posix_cpu_clock_set(const clockid_t which_clock, const struct timespec *tp) +static int +posix_cpu_clock_set(const clockid_t which_clock, const struct timespec *tp) { /* * You can never reset a CPU clock, but we check for other errors @@ -317,7 +319,7 @@ static int cpu_clock_sample_group(const clockid_t which_clock, } -int posix_cpu_clock_get(const clockid_t which_clock, struct timespec *tp) +static int posix_cpu_clock_get(const clockid_t which_clock, struct timespec *tp) { const pid_t pid = CPUCLOCK_PID(which_clock); int error = -EINVAL; @@ -379,7 +381,7 @@ int posix_cpu_clock_get(const clockid_t which_clock, struct timespec *tp) * This is called from sys_timer_create() and do_cpu_nanosleep() with the * new timer already all-zeros initialized. */ -int posix_cpu_timer_create(struct k_itimer *new_timer) +static int posix_cpu_timer_create(struct k_itimer *new_timer) { int ret = 0; const pid_t pid = CPUCLOCK_PID(new_timer->it_clock); @@ -425,7 +427,7 @@ int posix_cpu_timer_create(struct k_itimer *new_timer) * If we return TIMER_RETRY, it's necessary to release the timer's lock * and try again. (This happens when the timer is in the middle of firing.) */ -int posix_cpu_timer_del(struct k_itimer *timer) +static int posix_cpu_timer_del(struct k_itimer *timer) { struct task_struct *p = timer->it.cpu.task; int ret = 0; @@ -665,8 +667,8 @@ static int cpu_timer_sample_group(const clockid_t which_clock, * If we return TIMER_RETRY, it's necessary to release the timer's lock * and try again. (This happens when the timer is in the middle of firing.) */ -int posix_cpu_timer_set(struct k_itimer *timer, int flags, - struct itimerspec *new, struct itimerspec *old) +static int posix_cpu_timer_set(struct k_itimer *timer, int flags, + struct itimerspec *new, struct itimerspec *old) { struct task_struct *p = timer->it.cpu.task; union cpu_time_count old_expires, new_expires, old_incr, val; @@ -820,7 +822,7 @@ int posix_cpu_timer_set(struct k_itimer *timer, int flags, return ret; } -void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp) +static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp) { union cpu_time_count now; struct task_struct *p = timer->it.cpu.task; @@ -1481,8 +1483,10 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags, return error; } -int posix_cpu_nsleep(const clockid_t which_clock, int flags, - struct timespec *rqtp, struct timespec __user *rmtp) +static long posix_cpu_nsleep_restart(struct restart_block *restart_block); + +static int posix_cpu_nsleep(const clockid_t which_clock, int flags, + struct timespec *rqtp, struct timespec __user *rmtp) { struct restart_block *restart_block = ¤t_thread_info()->restart_block; @@ -1517,7 +1521,7 @@ int posix_cpu_nsleep(const clockid_t which_clock, int flags, return error; } -long posix_cpu_nsleep_restart(struct restart_block *restart_block) +static long posix_cpu_nsleep_restart(struct restart_block *restart_block) { clockid_t which_clock = restart_block->nanosleep.index; struct timespec t; @@ -1542,7 +1546,6 @@ long posix_cpu_nsleep_restart(struct restart_block *restart_block) } - #define PROCESS_CLOCK MAKE_PROCESS_CPUCLOCK(0, CPUCLOCK_SCHED) #define THREAD_CLOCK MAKE_THREAD_CPUCLOCK(0, CPUCLOCK_SCHED) -- cgit v1.2.3 From c528f7c6c208f1fae6b4025957173dec045e5f21 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Tue, 1 Feb 2011 13:52:17 +0000 Subject: time: Introduce timekeeping_inject_offset This adds a kernel-internal timekeeping interface to add or subtract a fixed amount from CLOCK_REALTIME. This makes it so kernel users or interfaces trying to do so do not have to read the time, then add an offset and then call settimeofday(), which adds some extra error in comparision to just simply adding the offset in the kernel timekeeping core. Signed-off-by: John Stultz Signed-off-by: Richard Cochran LKML-Reference: <20110201134419.584311693@linutronix.de> Signed-off-by: Thomas Gleixner --- include/linux/time.h | 1 + kernel/time/timekeeping.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) (limited to 'include') diff --git a/include/linux/time.h b/include/linux/time.h index 7c44e7778033..379b9037b5b4 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -166,6 +166,7 @@ extern struct timespec timespec_trunc(struct timespec t, unsigned gran); extern int timekeeping_valid_for_hres(void); extern u64 timekeeping_max_deferment(void); extern void timekeeping_leap_insert(int leapsecond); +extern int timekeeping_inject_offset(struct timespec *ts); struct tms; extern void do_sys_times(struct tms *); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 4f9f65b91323..6262c1d18397 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -387,6 +387,42 @@ int do_settimeofday(const struct timespec *tv) EXPORT_SYMBOL(do_settimeofday); + +/** + * timekeeping_inject_offset - Adds or subtracts from the current time. + * @tv: pointer to the timespec variable containing the offset + * + * Adds or subtracts an offset value from the current time. + */ +int timekeeping_inject_offset(struct timespec *ts) +{ + unsigned long flags; + + if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) + return -EINVAL; + + write_seqlock_irqsave(&xtime_lock, flags); + + timekeeping_forward_now(); + + xtime = timespec_add(xtime, *ts); + wall_to_monotonic = timespec_sub(wall_to_monotonic, *ts); + + timekeeper.ntp_error = 0; + ntp_clear(); + + update_vsyscall(&xtime, &wall_to_monotonic, timekeeper.clock, + timekeeper.mult); + + write_sequnlock_irqrestore(&xtime_lock, flags); + + /* signal hrtimers about time change */ + clock_was_set(); + + return 0; +} +EXPORT_SYMBOL(timekeeping_inject_offset); + /** * change_clocksource - Swaps clocksources if a new one is available * -- cgit v1.2.3 From 094aa1881fdc1b8889b442eb3511b31f3ec2b762 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Tue, 1 Feb 2011 13:52:20 +0000 Subject: ntp: Add ADJ_SETOFFSET mode bit This patch adds a new mode bit into the timex structure. When set, the bit instructs the kernel to add the given time value to the current time. Signed-off-by: Richard Cochran Acked-by: John Stultz LKML-Reference: <20110201134320.688829863@linutronix.de> Signed-off-by: Thomas Gleixner --- include/linux/timex.h | 3 ++- kernel/time/ntp.c | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/timex.h b/include/linux/timex.h index d23999f9499d..aa60fe7b6ed6 100644 --- a/include/linux/timex.h +++ b/include/linux/timex.h @@ -73,7 +73,7 @@ struct timex { long tolerance; /* clock frequency tolerance (ppm) * (read only) */ - struct timeval time; /* (read only) */ + struct timeval time; /* (read only, except for ADJ_SETOFFSET) */ long tick; /* (modified) usecs between clock ticks */ long ppsfreq; /* pps frequency (scaled ppm) (ro) */ @@ -102,6 +102,7 @@ struct timex { #define ADJ_STATUS 0x0010 /* clock status */ #define ADJ_TIMECONST 0x0020 /* pll time constant */ #define ADJ_TAI 0x0080 /* set TAI offset */ +#define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */ #define ADJ_MICRO 0x1000 /* select microsecond resolution */ #define ADJ_NANO 0x2000 /* select nanosecond resolution */ #define ADJ_TICK 0x4000 /* tick value */ diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index ed8cfdf16983..5ac593267a26 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -648,6 +648,17 @@ int do_adjtimex(struct timex *txc) hrtimer_cancel(&leap_timer); } + if (txc->modes & ADJ_SETOFFSET) { + struct timespec delta; + if ((unsigned long)txc->time.tv_usec >= NSEC_PER_SEC) + return -EINVAL; + delta.tv_sec = txc->time.tv_sec; + delta.tv_nsec = txc->time.tv_usec; + if (!(txc->modes & ADJ_NANO)) + delta.tv_nsec *= 1000; + timekeeping_inject_offset(&delta); + } + getnstimeofday(&ts); write_seqlock_irq(&xtime_lock); -- cgit v1.2.3 From f1f1d5ebd10ffa4242bce7a90a56a222d6b7bc77 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Tue, 1 Feb 2011 13:52:26 +0000 Subject: posix-timers: Introduce a syscall for clock tuning. A new syscall is introduced that allows tuning of a POSIX clock. The new call, clock_adjtime, takes two parameters, the clock ID and a pointer to a struct timex. Any ADJTIMEX(2) operation may be requested via this system call, but various POSIX clocks may or may not support tuning. [ tglx: Adapted to the posix-timer cleanup series. Avoid copy_to_user in the error case ] Signed-off-by: Richard Cochran Acked-by: John Stultz LKML-Reference: <20110201134419.869804645@linutronix.de> Signed-off-by: Thomas Gleixner --- include/linux/posix-timers.h | 2 ++ include/linux/syscalls.h | 2 ++ kernel/compat.c | 23 +++++++++++++++++++++++ kernel/posix-timers.c | 30 ++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+) (limited to 'include') diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index 8206255a547c..79a1cea7f6ed 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -4,6 +4,7 @@ #include #include #include +#include union cpu_time_count { cputime_t cpu; @@ -71,6 +72,7 @@ struct k_clock { int (*clock_set) (const clockid_t which_clock, const struct timespec *tp); int (*clock_get) (const clockid_t which_clock, struct timespec * tp); + int (*clock_adj) (const clockid_t which_clock, struct timex *tx); int (*timer_create) (struct k_itimer *timer); int (*nsleep) (const clockid_t which_clock, int flags, struct timespec *, struct timespec __user *); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 18cd0684fc4e..bfacab921239 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -311,6 +311,8 @@ asmlinkage long sys_clock_settime(clockid_t which_clock, const struct timespec __user *tp); asmlinkage long sys_clock_gettime(clockid_t which_clock, struct timespec __user *tp); +asmlinkage long sys_clock_adjtime(clockid_t which_clock, + struct timex __user *tx); asmlinkage long sys_clock_getres(clockid_t which_clock, struct timespec __user *tp); asmlinkage long sys_clock_nanosleep(clockid_t which_clock, int flags, diff --git a/kernel/compat.c b/kernel/compat.c index 449e853cf41d..38b1d2c1cbe8 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -675,6 +675,29 @@ long compat_sys_clock_gettime(clockid_t which_clock, return err; } +long compat_sys_clock_adjtime(clockid_t which_clock, + struct compat_timex __user *utp) +{ + struct timex txc; + mm_segment_t oldfs; + int err, ret; + + err = compat_get_timex(&txc, utp); + if (err) + return err; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_clock_adjtime(which_clock, (struct timex __user *) &txc); + set_fs(oldfs); + + err = compat_put_timex(utp, &txc); + if (err) + return err; + + return ret; +} + long compat_sys_clock_getres(clockid_t which_clock, struct compat_timespec __user *tp) { diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index a3fdfd4be0ec..5a5a4f1c0971 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c @@ -170,6 +170,12 @@ static int posix_clock_realtime_set(const clockid_t which_clock, return do_sys_settimeofday(tp, NULL); } +static int posix_clock_realtime_adj(const clockid_t which_clock, + struct timex *t) +{ + return do_adjtimex(t); +} + /* * Get monotonic time for posix timers */ @@ -216,6 +222,7 @@ static __init int init_posix_timers(void) .clock_getres = hrtimer_get_res, .clock_get = posix_clock_realtime_get, .clock_set = posix_clock_realtime_set, + .clock_adj = posix_clock_realtime_adj, .nsleep = common_nsleep, .nsleep_restart = hrtimer_nanosleep_restart, .timer_create = common_timer_create, @@ -948,6 +955,29 @@ SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock, return error; } +SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock, + struct timex __user *, utx) +{ + struct k_clock *kc = clockid_to_kclock(which_clock); + struct timex ktx; + int err; + + if (!kc) + return -EINVAL; + if (!kc->clock_adj) + return -EOPNOTSUPP; + + if (copy_from_user(&ktx, utx, sizeof(ktx))) + return -EFAULT; + + err = kc->clock_adj(which_clock, &ktx); + + if (!err && copy_to_user(utx, &ktx, sizeof(ktx))) + return -EFAULT; + + return err; +} + SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock, struct timespec __user *, tp) { -- cgit v1.2.3 From 81e294cba2596f5f10848bbe19d98b344c2a2d5c Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Tue, 1 Feb 2011 13:52:32 +0000 Subject: posix-timers: Add support for fd based clocks Extend the negative clockids which are currently used by posix cpu timers to encode the PID with a file descriptor based type which encodes the fd in the upper bits. Originally-from: Richard Cochran Signed-off-by: Thomas Gleixner Acked-by: John Stultz LKML-Reference: <20110201134420.062860200@linutronix.de> --- include/linux/posix-timers.h | 13 +++++++++++++ kernel/posix-timers.c | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index 79a1cea7f6ed..88b9256169f8 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -18,6 +18,17 @@ struct cpu_timer_list { int firing; }; +/* + * Bit fields within a clockid: + * + * The most significant 29 bits hold either a pid or a file descriptor. + * + * Bit 2 indicates whether a cpu clock refers to a thread or a process. + * + * Bits 1 and 0 give the type: PROF=0, VIRT=1, SCHED=2, or FD=3. + * + * A clockid is invalid if bits 2, 1, and 0 are all set. + */ #define CPUCLOCK_PID(clock) ((pid_t) ~((clock) >> 3)) #define CPUCLOCK_PERTHREAD(clock) \ (((clock) & (clockid_t) CPUCLOCK_PERTHREAD_MASK) != 0) @@ -29,6 +40,8 @@ struct cpu_timer_list { #define CPUCLOCK_VIRT 1 #define CPUCLOCK_SCHED 2 #define CPUCLOCK_MAX 3 +#define CLOCKFD CPUCLOCK_MAX +#define CLOCKFD_MASK (CPUCLOCK_PERTHREAD_MASK|CPUCLOCK_CLOCK_MASK) #define MAKE_PROCESS_CPUCLOCK(pid, clock) \ ((~(clockid_t) (pid) << 3) | (clockid_t) (clock)) diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index 5a5a4f1c0971..df629d853a81 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c @@ -488,7 +488,7 @@ static void release_posix_timer(struct k_itimer *tmr, int it_id_set) static struct k_clock *clockid_to_kclock(const clockid_t id) { if (id < 0) - return &clock_posix_cpu; + return (id & CLOCKFD_MASK) == CLOCKFD ? NULL : &clock_posix_cpu; if (id >= MAX_CLOCKS || !posix_clocks[id].clock_getres) return NULL; -- cgit v1.2.3 From 527087374faa488776a789375a7d6ea74fda6f71 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 2 Feb 2011 12:10:09 +0100 Subject: posix-timers: Cleanup namespace Rename register_posix_clock() to posix_timers_register_clock(). That's what the function really does. As a side effect this cleans up the posix_clock namespace for the upcoming dynamic posix_clock infrastructure. Signed-off-by: Thomas Gleixner Tested-by: Richard Cochran Cc: John Stultz LKML-Reference: --- drivers/char/mmtimer.c | 2 +- include/linux/posix-timers.h | 2 +- kernel/posix-cpu-timers.c | 4 ++-- kernel/posix-timers.c | 15 ++++++++------- 4 files changed, 12 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c index ff41eb3eec92..33dc2298af73 100644 --- a/drivers/char/mmtimer.c +++ b/drivers/char/mmtimer.c @@ -840,7 +840,7 @@ static int __init mmtimer_init(void) } sgi_clock_period = NSEC_PER_SEC / sn_rtc_cycles_per_second; - register_posix_clock(CLOCK_SGI_CYCLE, &sgi_clock); + posix_timers_register_clock(CLOCK_SGI_CYCLE, &sgi_clock); printk(KERN_INFO "%s: v%s, %ld MHz\n", MMTIMER_DESC, MMTIMER_VERSION, sn_rtc_cycles_per_second/(unsigned long)1E6); diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index 88b9256169f8..9d6ffe2c92e5 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -101,7 +101,7 @@ struct k_clock { extern struct k_clock clock_posix_cpu; -void register_posix_clock(const clockid_t clock_id, struct k_clock *new_clock); +void posix_timers_register_clock(const clockid_t clock_id, struct k_clock *new_clock); /* function to call to trigger timer event */ int posix_timer_event(struct k_itimer *timr, int si_private); diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index 609e187f43e7..67fea9d25d55 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c @@ -1618,8 +1618,8 @@ static __init int init_posix_cpu_timers(void) }; struct timespec ts; - register_posix_clock(CLOCK_PROCESS_CPUTIME_ID, &process); - register_posix_clock(CLOCK_THREAD_CPUTIME_ID, &thread); + posix_timers_register_clock(CLOCK_PROCESS_CPUTIME_ID, &process); + posix_timers_register_clock(CLOCK_THREAD_CPUTIME_ID, &thread); cputime_to_timespec(cputime_one_jiffy, &ts); onecputick = ts.tv_nsec; diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index df629d853a81..af936fd37140 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c @@ -253,11 +253,11 @@ static __init int init_posix_timers(void) .clock_get = posix_get_monotonic_coarse, }; - register_posix_clock(CLOCK_REALTIME, &clock_realtime); - register_posix_clock(CLOCK_MONOTONIC, &clock_monotonic); - register_posix_clock(CLOCK_MONOTONIC_RAW, &clock_monotonic_raw); - register_posix_clock(CLOCK_REALTIME_COARSE, &clock_realtime_coarse); - register_posix_clock(CLOCK_MONOTONIC_COARSE, &clock_monotonic_coarse); + posix_timers_register_clock(CLOCK_REALTIME, &clock_realtime); + posix_timers_register_clock(CLOCK_MONOTONIC, &clock_monotonic); + posix_timers_register_clock(CLOCK_MONOTONIC_RAW, &clock_monotonic_raw); + posix_timers_register_clock(CLOCK_REALTIME_COARSE, &clock_realtime_coarse); + posix_timers_register_clock(CLOCK_MONOTONIC_COARSE, &clock_monotonic_coarse); posix_timers_cache = kmem_cache_create("posix_timers_cache", sizeof (struct k_itimer), 0, SLAB_PANIC, @@ -433,7 +433,8 @@ static struct pid *good_sigevent(sigevent_t * event) return task_pid(rtn); } -void register_posix_clock(const clockid_t clock_id, struct k_clock *new_clock) +void posix_timers_register_clock(const clockid_t clock_id, + struct k_clock *new_clock) { if ((unsigned) clock_id >= MAX_CLOCKS) { printk(KERN_WARNING "POSIX clock register failed for clock_id %d\n", @@ -454,7 +455,7 @@ void register_posix_clock(const clockid_t clock_id, struct k_clock *new_clock) posix_clocks[clock_id] = *new_clock; } -EXPORT_SYMBOL_GPL(register_posix_clock); +EXPORT_SYMBOL_GPL(posix_timers_register_clock); static struct k_itimer * alloc_posix_timer(void) { -- cgit v1.2.3 From 0606f422b453f76c31ab2b1bd52943ff06a2dcf2 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Tue, 1 Feb 2011 13:52:35 +0000 Subject: posix clocks: Introduce dynamic clocks This patch adds support for adding and removing posix clocks. The clock lifetime cycle is patterned after usb devices. Each clock is represented by a standard character device. In addition, the driver may optionally implement custom character device operations. The posix clock and timer system calls listed below now work with dynamic posix clocks, as well as the traditional static clocks. The following system calls are affected: - clock_adjtime (brand new syscall) - clock_gettime - clock_getres - clock_settime - timer_create - timer_delete - timer_gettime - timer_settime [ tglx: Adapted to the posix-timer cleanup. Moved clock_posix_dynamic to posix-clock.c and made all referenced functions static ] Signed-off-by: Richard Cochran Acked-by: John Stultz LKML-Reference: <20110201134420.164172635@linutronix.de> Signed-off-by: Thomas Gleixner --- include/linux/posix-clock.h | 150 +++++++++++++++ include/linux/posix-timers.h | 6 +- kernel/posix-timers.c | 4 +- kernel/time/Makefile | 3 +- kernel/time/posix-clock.c | 441 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 601 insertions(+), 3 deletions(-) create mode 100644 include/linux/posix-clock.h create mode 100644 kernel/time/posix-clock.c (limited to 'include') diff --git a/include/linux/posix-clock.h b/include/linux/posix-clock.h new file mode 100644 index 000000000000..369e19d3750b --- /dev/null +++ b/include/linux/posix-clock.h @@ -0,0 +1,150 @@ +/* + * posix-clock.h - support for dynamic clock devices + * + * Copyright (C) 2010 OMICRON electronics GmbH + * + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef _LINUX_POSIX_CLOCK_H_ +#define _LINUX_POSIX_CLOCK_H_ + +#include +#include +#include +#include + +struct posix_clock; + +/** + * struct posix_clock_operations - functional interface to the clock + * + * Every posix clock is represented by a character device. Drivers may + * optionally offer extended capabilities by implementing the + * character device methods. The character device file operations are + * first handled by the clock device layer, then passed on to the + * driver by calling these functions. + * + * @owner: The clock driver should set to THIS_MODULE + * @clock_adjtime: Adjust the clock + * @clock_gettime: Read the current time + * @clock_getres: Get the clock resolution + * @clock_settime: Set the current time value + * @timer_create: Create a new timer + * @timer_delete: Remove a previously created timer + * @timer_gettime: Get remaining time and interval of a timer + * @timer_setttime: Set a timer's initial expiration and interval + * @fasync: Optional character device fasync method + * @mmap: Optional character device mmap method + * @open: Optional character device open method + * @release: Optional character device release method + * @ioctl: Optional character device ioctl method + * @read: Optional character device read method + * @poll: Optional character device poll method + */ +struct posix_clock_operations { + struct module *owner; + + int (*clock_adjtime)(struct posix_clock *pc, struct timex *tx); + + int (*clock_gettime)(struct posix_clock *pc, struct timespec *ts); + + int (*clock_getres) (struct posix_clock *pc, struct timespec *ts); + + int (*clock_settime)(struct posix_clock *pc, + const struct timespec *ts); + + int (*timer_create) (struct posix_clock *pc, struct k_itimer *kit); + + int (*timer_delete) (struct posix_clock *pc, struct k_itimer *kit); + + void (*timer_gettime)(struct posix_clock *pc, + struct k_itimer *kit, struct itimerspec *tsp); + + int (*timer_settime)(struct posix_clock *pc, + struct k_itimer *kit, int flags, + struct itimerspec *tsp, struct itimerspec *old); + /* + * Optional character device methods: + */ + int (*fasync) (struct posix_clock *pc, + int fd, struct file *file, int on); + + long (*ioctl) (struct posix_clock *pc, + unsigned int cmd, unsigned long arg); + + int (*mmap) (struct posix_clock *pc, + struct vm_area_struct *vma); + + int (*open) (struct posix_clock *pc, fmode_t f_mode); + + uint (*poll) (struct posix_clock *pc, + struct file *file, poll_table *wait); + + int (*release) (struct posix_clock *pc); + + ssize_t (*read) (struct posix_clock *pc, + uint flags, char __user *buf, size_t cnt); +}; + +/** + * struct posix_clock - represents a dynamic posix clock + * + * @ops: Functional interface to the clock + * @cdev: Character device instance for this clock + * @kref: Reference count. + * @mutex: Protects the 'zombie' field from concurrent access. + * @zombie: If 'zombie' is true, then the hardware has disappeared. + * @release: A function to free the structure when the reference count reaches + * zero. May be NULL if structure is statically allocated. + * + * Drivers should embed their struct posix_clock within a private + * structure, obtaining a reference to it during callbacks using + * container_of(). + */ +struct posix_clock { + struct posix_clock_operations ops; + struct cdev cdev; + struct kref kref; + struct mutex mutex; + bool zombie; + void (*release)(struct posix_clock *clk); +}; + +/** + * posix_clock_register() - register a new clock + * @clk: Pointer to the clock. Caller must provide 'ops' and 'release' + * @devid: Allocated device id + * + * A clock driver calls this function to register itself with the + * clock device subsystem. If 'clk' points to dynamically allocated + * memory, then the caller must provide a 'release' function to free + * that memory. + * + * Returns zero on success, non-zero otherwise. + */ +int posix_clock_register(struct posix_clock *clk, dev_t devid); + +/** + * posix_clock_unregister() - unregister a clock + * @clk: Clock instance previously registered via posix_clock_register() + * + * A clock driver calls this function to remove itself from the clock + * device subsystem. The posix_clock itself will remain (in an + * inactive state) until its reference count drops to zero, at which + * point it will be deallocated with its 'release' method. + */ +void posix_clock_unregister(struct posix_clock *clk); + +#endif diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index 9d6ffe2c92e5..d51243ae0726 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -32,7 +32,7 @@ struct cpu_timer_list { #define CPUCLOCK_PID(clock) ((pid_t) ~((clock) >> 3)) #define CPUCLOCK_PERTHREAD(clock) \ (((clock) & (clockid_t) CPUCLOCK_PERTHREAD_MASK) != 0) -#define CPUCLOCK_PID_MASK 7 + #define CPUCLOCK_PERTHREAD_MASK 4 #define CPUCLOCK_WHICH(clock) ((clock) & (clockid_t) CPUCLOCK_CLOCK_MASK) #define CPUCLOCK_CLOCK_MASK 3 @@ -48,6 +48,9 @@ struct cpu_timer_list { #define MAKE_THREAD_CPUCLOCK(tid, clock) \ MAKE_PROCESS_CPUCLOCK((tid), (clock) | CPUCLOCK_PERTHREAD_MASK) +#define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD) +#define CLOCKID_TO_FD(clk) ((unsigned int) ~((clk) >> 3)) + /* POSIX.1b interval timer structure. */ struct k_itimer { struct list_head list; /* free/ allocate list */ @@ -100,6 +103,7 @@ struct k_clock { }; extern struct k_clock clock_posix_cpu; +extern struct k_clock clock_posix_dynamic; void posix_timers_register_clock(const clockid_t clock_id, struct k_clock *new_clock); diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index af936fd37140..44fcff131b38 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -489,7 +490,8 @@ static void release_posix_timer(struct k_itimer *tmr, int it_id_set) static struct k_clock *clockid_to_kclock(const clockid_t id) { if (id < 0) - return (id & CLOCKFD_MASK) == CLOCKFD ? NULL : &clock_posix_cpu; + return (id & CLOCKFD_MASK) == CLOCKFD ? + &clock_posix_dynamic : &clock_posix_cpu; if (id >= MAX_CLOCKS || !posix_clocks[id].clock_getres) return NULL; diff --git a/kernel/time/Makefile b/kernel/time/Makefile index ee266620b06c..b0425991e9ac 100644 --- a/kernel/time/Makefile +++ b/kernel/time/Makefile @@ -1,4 +1,5 @@ -obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o timecompare.o timeconv.o +obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o timecompare.o +obj-y += timeconv.o posix-clock.o obj-$(CONFIG_GENERIC_CLOCKEVENTS_BUILD) += clockevents.o obj-$(CONFIG_GENERIC_CLOCKEVENTS) += tick-common.o diff --git a/kernel/time/posix-clock.c b/kernel/time/posix-clock.c new file mode 100644 index 000000000000..04498cbf6002 --- /dev/null +++ b/kernel/time/posix-clock.c @@ -0,0 +1,441 @@ +/* + * posix-clock.c - support for dynamic clock devices + * + * Copyright (C) 2010 OMICRON electronics GmbH + * + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include + +static void delete_clock(struct kref *kref); + +/* + * Returns NULL if the posix_clock instance attached to 'fp' is old and stale. + */ +static struct posix_clock *get_posix_clock(struct file *fp) +{ + struct posix_clock *clk = fp->private_data; + + mutex_lock(&clk->mutex); + + if (!clk->zombie) + return clk; + + mutex_unlock(&clk->mutex); + + return NULL; +} + +static void put_posix_clock(struct posix_clock *clk) +{ + mutex_unlock(&clk->mutex); +} + +static ssize_t posix_clock_read(struct file *fp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct posix_clock *clk = get_posix_clock(fp); + int err = -EINVAL; + + if (!clk) + return -ENODEV; + + if (clk->ops.read) + err = clk->ops.read(clk, fp->f_flags, buf, count); + + put_posix_clock(clk); + + return err; +} + +static unsigned int posix_clock_poll(struct file *fp, poll_table *wait) +{ + struct posix_clock *clk = get_posix_clock(fp); + int result = 0; + + if (!clk) + return -ENODEV; + + if (clk->ops.poll) + result = clk->ops.poll(clk, fp, wait); + + put_posix_clock(clk); + + return result; +} + +static int posix_clock_fasync(int fd, struct file *fp, int on) +{ + struct posix_clock *clk = get_posix_clock(fp); + int err = 0; + + if (!clk) + return -ENODEV; + + if (clk->ops.fasync) + err = clk->ops.fasync(clk, fd, fp, on); + + put_posix_clock(clk); + + return err; +} + +static int posix_clock_mmap(struct file *fp, struct vm_area_struct *vma) +{ + struct posix_clock *clk = get_posix_clock(fp); + int err = -ENODEV; + + if (!clk) + return -ENODEV; + + if (clk->ops.mmap) + err = clk->ops.mmap(clk, vma); + + put_posix_clock(clk); + + return err; +} + +static long posix_clock_ioctl(struct file *fp, + unsigned int cmd, unsigned long arg) +{ + struct posix_clock *clk = get_posix_clock(fp); + int err = -ENOTTY; + + if (!clk) + return -ENODEV; + + if (clk->ops.ioctl) + err = clk->ops.ioctl(clk, cmd, arg); + + put_posix_clock(clk); + + return err; +} + +#ifdef CONFIG_COMPAT +static long posix_clock_compat_ioctl(struct file *fp, + unsigned int cmd, unsigned long arg) +{ + struct posix_clock *clk = get_posix_clock(fp); + int err = -ENOTTY; + + if (!clk) + return -ENODEV; + + if (clk->ops.ioctl) + err = clk->ops.ioctl(clk, cmd, arg); + + put_posix_clock(clk); + + return err; +} +#endif + +static int posix_clock_open(struct inode *inode, struct file *fp) +{ + int err; + struct posix_clock *clk = + container_of(inode->i_cdev, struct posix_clock, cdev); + + mutex_lock(&clk->mutex); + + if (clk->zombie) { + err = -ENODEV; + goto out; + } + if (clk->ops.open) + err = clk->ops.open(clk, fp->f_mode); + else + err = 0; + + if (!err) { + kref_get(&clk->kref); + fp->private_data = clk; + } +out: + mutex_unlock(&clk->mutex); + return err; +} + +static int posix_clock_release(struct inode *inode, struct file *fp) +{ + struct posix_clock *clk = fp->private_data; + int err = 0; + + if (clk->ops.release) + err = clk->ops.release(clk); + + kref_put(&clk->kref, delete_clock); + + fp->private_data = NULL; + + return err; +} + +static const struct file_operations posix_clock_file_operations = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = posix_clock_read, + .poll = posix_clock_poll, + .unlocked_ioctl = posix_clock_ioctl, + .open = posix_clock_open, + .release = posix_clock_release, + .fasync = posix_clock_fasync, + .mmap = posix_clock_mmap, +#ifdef CONFIG_COMPAT + .compat_ioctl = posix_clock_compat_ioctl, +#endif +}; + +int posix_clock_register(struct posix_clock *clk, dev_t devid) +{ + int err; + + kref_init(&clk->kref); + mutex_init(&clk->mutex); + + cdev_init(&clk->cdev, &posix_clock_file_operations); + clk->cdev.owner = clk->ops.owner; + err = cdev_add(&clk->cdev, devid, 1); + if (err) + goto no_cdev; + + return err; +no_cdev: + mutex_destroy(&clk->mutex); + return err; +} +EXPORT_SYMBOL_GPL(posix_clock_register); + +static void delete_clock(struct kref *kref) +{ + struct posix_clock *clk = container_of(kref, struct posix_clock, kref); + mutex_destroy(&clk->mutex); + if (clk->release) + clk->release(clk); +} + +void posix_clock_unregister(struct posix_clock *clk) +{ + cdev_del(&clk->cdev); + + mutex_lock(&clk->mutex); + clk->zombie = true; + mutex_unlock(&clk->mutex); + + kref_put(&clk->kref, delete_clock); +} +EXPORT_SYMBOL_GPL(posix_clock_unregister); + +struct posix_clock_desc { + struct file *fp; + struct posix_clock *clk; +}; + +static int get_clock_desc(const clockid_t id, struct posix_clock_desc *cd) +{ + struct file *fp = fget(CLOCKID_TO_FD(id)); + int err = -EINVAL; + + if (!fp) + return err; + + if (fp->f_op->open != posix_clock_open || !fp->private_data) + goto out; + + cd->fp = fp; + cd->clk = get_posix_clock(fp); + + err = cd->clk ? 0 : -ENODEV; +out: + if (err) + fput(fp); + return err; +} + +static void put_clock_desc(struct posix_clock_desc *cd) +{ + put_posix_clock(cd->clk); + fput(cd->fp); +} + +static int pc_clock_adjtime(clockid_t id, struct timex *tx) +{ + struct posix_clock_desc cd; + int err; + + err = get_clock_desc(id, &cd); + if (err) + return err; + + if (cd.clk->ops.clock_adjtime) + err = cd.clk->ops.clock_adjtime(cd.clk, tx); + else + err = -EOPNOTSUPP; + + put_clock_desc(&cd); + + return err; +} + +static int pc_clock_gettime(clockid_t id, struct timespec *ts) +{ + struct posix_clock_desc cd; + int err; + + err = get_clock_desc(id, &cd); + if (err) + return err; + + if (cd.clk->ops.clock_gettime) + err = cd.clk->ops.clock_gettime(cd.clk, ts); + else + err = -EOPNOTSUPP; + + put_clock_desc(&cd); + + return err; +} + +static int pc_clock_getres(clockid_t id, struct timespec *ts) +{ + struct posix_clock_desc cd; + int err; + + err = get_clock_desc(id, &cd); + if (err) + return err; + + if (cd.clk->ops.clock_getres) + err = cd.clk->ops.clock_getres(cd.clk, ts); + else + err = -EOPNOTSUPP; + + put_clock_desc(&cd); + + return err; +} + +static int pc_clock_settime(clockid_t id, const struct timespec *ts) +{ + struct posix_clock_desc cd; + int err; + + err = get_clock_desc(id, &cd); + if (err) + return err; + + if (cd.clk->ops.clock_settime) + err = cd.clk->ops.clock_settime(cd.clk, ts); + else + err = -EOPNOTSUPP; + + put_clock_desc(&cd); + + return err; +} + +static int pc_timer_create(struct k_itimer *kit) +{ + clockid_t id = kit->it_clock; + struct posix_clock_desc cd; + int err; + + err = get_clock_desc(id, &cd); + if (err) + return err; + + if (cd.clk->ops.timer_create) + err = cd.clk->ops.timer_create(cd.clk, kit); + else + err = -EOPNOTSUPP; + + put_clock_desc(&cd); + + return err; +} + +static int pc_timer_delete(struct k_itimer *kit) +{ + clockid_t id = kit->it_clock; + struct posix_clock_desc cd; + int err; + + err = get_clock_desc(id, &cd); + if (err) + return err; + + if (cd.clk->ops.timer_delete) + err = cd.clk->ops.timer_delete(cd.clk, kit); + else + err = -EOPNOTSUPP; + + put_clock_desc(&cd); + + return err; +} + +static void pc_timer_gettime(struct k_itimer *kit, struct itimerspec *ts) +{ + clockid_t id = kit->it_clock; + struct posix_clock_desc cd; + + if (get_clock_desc(id, &cd)) + return; + + if (cd.clk->ops.timer_gettime) + cd.clk->ops.timer_gettime(cd.clk, kit, ts); + + put_clock_desc(&cd); +} + +static int pc_timer_settime(struct k_itimer *kit, int flags, + struct itimerspec *ts, struct itimerspec *old) +{ + clockid_t id = kit->it_clock; + struct posix_clock_desc cd; + int err; + + err = get_clock_desc(id, &cd); + if (err) + return err; + + if (cd.clk->ops.timer_settime) + err = cd.clk->ops.timer_settime(cd.clk, kit, flags, ts, old); + else + err = -EOPNOTSUPP; + + put_clock_desc(&cd); + + return err; +} + +struct k_clock clock_posix_dynamic = { + .clock_getres = pc_clock_getres, + .clock_set = pc_clock_settime, + .clock_get = pc_clock_gettime, + .clock_adj = pc_clock_adjtime, + .timer_create = pc_timer_create, + .timer_set = pc_timer_settime, + .timer_del = pc_timer_delete, + .timer_get = pc_timer_gettime, +}; -- cgit v1.2.3 From 724bab476bcac9f7d0b5204cb06e346216d42166 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 2 Feb 2011 23:50:01 +0100 Subject: netfilter: ipset: fix linking with CONFIG_IPV6=n Add a dummy ip_set_get_ip6_port function that unconditionally returns false for CONFIG_IPV6=n and convert the real function to ipv6_skip_exthdr() to avoid pulling in the ip6_tables module when loading ipset. Signed-off-by: Patrick McHardy --- include/linux/netfilter/ipset/ip_set_getport.h | 10 ++++++++++ net/netfilter/ipset/ip_set_getport.c | 15 +++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set_getport.h b/include/linux/netfilter/ipset/ip_set_getport.h index 694c433298b8..3882a81a3b3c 100644 --- a/include/linux/netfilter/ipset/ip_set_getport.h +++ b/include/linux/netfilter/ipset/ip_set_getport.h @@ -3,8 +3,18 @@ extern bool ip_set_get_ip4_port(const struct sk_buff *skb, bool src, __be16 *port, u8 *proto); + +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) extern bool ip_set_get_ip6_port(const struct sk_buff *skb, bool src, __be16 *port, u8 *proto); +#else +static inline bool ip_set_get_ip6_port(const struct sk_buff *skb, bool src, + __be16 *port, u8 *proto) +{ + return false; +} +#endif + extern bool ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port); diff --git a/net/netfilter/ipset/ip_set_getport.c b/net/netfilter/ipset/ip_set_getport.c index 4dd2785a5c72..8d5227212686 100644 --- a/net/netfilter/ipset/ip_set_getport.c +++ b/net/netfilter/ipset/ip_set_getport.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -93,21 +94,23 @@ ip_set_get_ip4_port(const struct sk_buff *skb, bool src, } EXPORT_SYMBOL_GPL(ip_set_get_ip4_port); +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) bool ip_set_get_ip6_port(const struct sk_buff *skb, bool src, __be16 *port, u8 *proto) { - unsigned int protooff = 0; - int protocol; - unsigned short fragoff; + int protoff; + u8 nexthdr; - protocol = ipv6_find_hdr(skb, &protooff, -1, &fragoff); - if (protocol <= 0 || fragoff) + nexthdr = ipv6_hdr(skb)->nexthdr; + protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr); + if (protoff < 0) return false; - return get_port(skb, protocol, protooff, src, port, proto); + return get_port(skb, nexthdr, protoff, src, port, proto); } EXPORT_SYMBOL_GPL(ip_set_get_ip6_port); +#endif bool ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port) -- cgit v1.2.3 From 9291747f118d6404e509747b85ff5f6dfec368d2 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 3 Feb 2011 00:05:43 +0100 Subject: netfilter: xtables: add device group match Add a new 'devgroup' match to match on the device group of the incoming and outgoing network device of a packet. Signed-off-by: Patrick McHardy --- include/linux/netfilter/Kbuild | 1 + include/linux/netfilter/xt_devgroup.h | 21 +++++++++ net/netfilter/Kconfig | 9 ++++ net/netfilter/Makefile | 1 + net/netfilter/xt_devgroup.c | 82 +++++++++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+) create mode 100644 include/linux/netfilter/xt_devgroup.h create mode 100644 net/netfilter/xt_devgroup.c (limited to 'include') diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index ba19544cce94..15e83bf3dd58 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild @@ -37,6 +37,7 @@ header-y += xt_connmark.h header-y += xt_conntrack.h header-y += xt_cpu.h header-y += xt_dccp.h +header-y += xt_devgroup.h header-y += xt_dscp.h header-y += xt_esp.h header-y += xt_hashlimit.h diff --git a/include/linux/netfilter/xt_devgroup.h b/include/linux/netfilter/xt_devgroup.h new file mode 100644 index 000000000000..1babde0ec900 --- /dev/null +++ b/include/linux/netfilter/xt_devgroup.h @@ -0,0 +1,21 @@ +#ifndef _XT_DEVGROUP_H +#define _XT_DEVGROUP_H + +#include + +enum xt_devgroup_flags { + XT_DEVGROUP_MATCH_SRC = 0x1, + XT_DEVGROUP_INVERT_SRC = 0x2, + XT_DEVGROUP_MATCH_DST = 0x4, + XT_DEVGROUP_INVERT_DST = 0x8, +}; + +struct xt_devgroup_info { + __u32 flags; + __u32 src_group; + __u32 src_mask; + __u32 dst_group; + __u32 dst_mask; +}; + +#endif /* _XT_DEVGROUP_H */ diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 06fa9e4e45c7..82a6e0d80f05 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -738,6 +738,15 @@ config NETFILTER_XT_MATCH_DCCP If you want to compile it as a module, say M here and read . If unsure, say `N'. +config NETFILTER_XT_MATCH_DEVGROUP + tristate '"devgroup" match support' + depends on NETFILTER_ADVANCED + help + This options adds a `devgroup' match, which allows to match on the + device group a network device is assigned to. + + To compile it as a module, choose M here. If unsure, say N. + config NETFILTER_XT_MATCH_DSCP tristate '"dscp" and "tos" match support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 1148643559cb..d57a890eaee5 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -77,6 +77,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLIMIT) += xt_connlimit.o obj-$(CONFIG_NETFILTER_XT_MATCH_CONNTRACK) += xt_conntrack.o obj-$(CONFIG_NETFILTER_XT_MATCH_CPU) += xt_cpu.o obj-$(CONFIG_NETFILTER_XT_MATCH_DCCP) += xt_dccp.o +obj-$(CONFIG_NETFILTER_XT_MATCH_DEVGROUP) += xt_devgroup.o obj-$(CONFIG_NETFILTER_XT_MATCH_DSCP) += xt_dscp.o obj-$(CONFIG_NETFILTER_XT_MATCH_ESP) += xt_esp.o obj-$(CONFIG_NETFILTER_XT_MATCH_HASHLIMIT) += xt_hashlimit.o diff --git a/net/netfilter/xt_devgroup.c b/net/netfilter/xt_devgroup.c new file mode 100644 index 000000000000..d9202cdd25c9 --- /dev/null +++ b/net/netfilter/xt_devgroup.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2011 Patrick McHardy + * + * 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 + +MODULE_AUTHOR("Patrick McHardy "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Xtables: Device group match"); +MODULE_ALIAS("ipt_devgroup"); +MODULE_ALIAS("ip6t_devgroup"); + +static bool devgroup_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_devgroup_info *info = par->matchinfo; + + if (info->flags & XT_DEVGROUP_MATCH_SRC && + (((info->src_group ^ par->in->group) & info->src_mask ? 1 : 0) ^ + ((info->flags & XT_DEVGROUP_INVERT_SRC) ? 1 : 0))) + return false; + + if (info->flags & XT_DEVGROUP_MATCH_DST && + (((info->dst_group ^ par->out->group) & info->dst_mask ? 1 : 0) ^ + ((info->flags & XT_DEVGROUP_INVERT_DST) ? 1 : 0))) + return false; + + return true; +} + +static int devgroup_mt_checkentry(const struct xt_mtchk_param *par) +{ + const struct xt_devgroup_info *info = par->matchinfo; + + if (info->flags & ~(XT_DEVGROUP_MATCH_SRC | XT_DEVGROUP_INVERT_SRC | + XT_DEVGROUP_MATCH_DST | XT_DEVGROUP_INVERT_DST)) + return -EINVAL; + + if (info->flags & XT_DEVGROUP_MATCH_SRC && + par->hook_mask & ~((1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_LOCAL_IN) | + (1 << NF_INET_FORWARD))) + return -EINVAL; + + if (info->flags & XT_DEVGROUP_MATCH_DST && + par->hook_mask & ~((1 << NF_INET_FORWARD) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_POST_ROUTING))) + return -EINVAL; + + return 0; +} + +static struct xt_match devgroup_mt_reg __read_mostly = { + .name = "devgroup", + .match = devgroup_mt, + .checkentry = devgroup_mt_checkentry, + .matchsize = sizeof(struct xt_devgroup_info), + .family = NFPROTO_UNSPEC, + .me = THIS_MODULE +}; + +static int __init devgroup_mt_init(void) +{ + return xt_register_match(&devgroup_mt_reg); +} + +static void __exit devgroup_mt_exit(void) +{ + xt_unregister_match(&devgroup_mt_reg); +} + +module_init(devgroup_mt_init); +module_exit(devgroup_mt_exit); -- cgit v1.2.3 From 19942822df65ee4a47c2e6d6d70cace1b7f01710 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Tue, 1 Feb 2011 15:52:43 -0800 Subject: memcg: prevent endless loop when charging huge pages to near-limit group If reclaim after a failed charging was unsuccessful, the limits are checked again, just in case they settled by means of other tasks. This is all fine as long as every charge is of size PAGE_SIZE, because in that case, being below the limit means having at least PAGE_SIZE bytes available. But with transparent huge pages, we may end up in an endless loop where charging and reclaim fail, but we keep going because the limits are not yet exceeded, although not allowing for a huge page. Fix this up by explicitely checking for enough room, not just whether we are within limits. Signed-off-by: Johannes Weiner Acked-by: KAMEZAWA Hiroyuki Reviewed-by: Minchan Kim Cc: Balbir Singh Cc: Daisuke Nishimura Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/res_counter.h | 20 ++++++++++++++++++++ mm/memcontrol.c | 35 ++++++++++++++++++++++++++++------- 2 files changed, 48 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/res_counter.h b/include/linux/res_counter.h index fcb9884df618..a5930cb66145 100644 --- a/include/linux/res_counter.h +++ b/include/linux/res_counter.h @@ -182,6 +182,26 @@ static inline bool res_counter_check_under_limit(struct res_counter *cnt) return ret; } +/** + * res_counter_check_margin - check if the counter allows charging + * @cnt: the resource counter to check + * @bytes: the number of bytes to check the remaining space against + * + * Returns a boolean value on whether the counter can be charged + * @bytes or whether this would exceed the limit. + */ +static inline bool res_counter_check_margin(struct res_counter *cnt, + unsigned long bytes) +{ + bool ret; + unsigned long flags; + + spin_lock_irqsave(&cnt->lock, flags); + ret = cnt->limit - cnt->usage >= bytes; + spin_unlock_irqrestore(&cnt->lock, flags); + return ret; +} + static inline bool res_counter_check_under_soft_limit(struct res_counter *cnt) { bool ret; diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 50eb50e100fd..0e81eb5f0aea 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1111,6 +1111,23 @@ static bool mem_cgroup_check_under_limit(struct mem_cgroup *mem) return false; } +/** + * mem_cgroup_check_margin - check if the memory cgroup allows charging + * @mem: memory cgroup to check + * @bytes: the number of bytes the caller intends to charge + * + * Returns a boolean value on whether @mem can be charged @bytes or + * whether this would exceed the limit. + */ +static bool mem_cgroup_check_margin(struct mem_cgroup *mem, unsigned long bytes) +{ + if (!res_counter_check_margin(&mem->res, bytes)) + return false; + if (do_swap_account && !res_counter_check_margin(&mem->memsw, bytes)) + return false; + return true; +} + static unsigned int get_swappiness(struct mem_cgroup *memcg) { struct cgroup *cgrp = memcg->css.cgroup; @@ -1852,15 +1869,19 @@ static int __mem_cgroup_do_charge(struct mem_cgroup *mem, gfp_t gfp_mask, return CHARGE_WOULDBLOCK; ret = mem_cgroup_hierarchical_reclaim(mem_over_limit, NULL, - gfp_mask, flags); + gfp_mask, flags); + if (mem_cgroup_check_margin(mem_over_limit, csize)) + return CHARGE_RETRY; /* - * try_to_free_mem_cgroup_pages() might not give us a full - * picture of reclaim. Some pages are reclaimed and might be - * moved to swap cache or just unmapped from the cgroup. - * Check the limit again to see if the reclaim reduced the - * current usage of the cgroup before giving up + * Even though the limit is exceeded at this point, reclaim + * may have been able to free some pages. Retry the charge + * before killing the task. + * + * Only for regular pages, though: huge pages are rather + * unlikely to succeed so close to the limit, and we fall back + * to regular pages anyway in case of failure. */ - if (ret || mem_cgroup_check_under_limit(mem_over_limit)) + if (csize == PAGE_SIZE && ret) return CHARGE_RETRY; /* -- cgit v1.2.3 From 1a44bc8c7cfe69756a116d38aef992d50fc1969d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 1 Feb 2011 15:52:46 -0800 Subject: vfs: sparse: remove a warning on OPEN_FMODE() AND-ing FMODE_* constant with normal integer results in following sparse warnings. Fix it. fs/open.c:662:21: warning: restricted fmode_t degrades to integer fs/anon_inodes.c:123:34: warning: restricted fmode_t degrades to integer Signed-off-by: Namhyung Kim Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/fs.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/fs.h b/include/linux/fs.h index 32b38cd829d3..675678a01f50 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2555,9 +2555,11 @@ int proc_nr_inodes(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); int __init get_filesystem_list(char *buf); +#define __FMODE_NONOTIFY ((__force int) FMODE_NONOTIFY) + #define ACC_MODE(x) ("\004\002\006\006"[(x)&O_ACCMODE]) #define OPEN_FMODE(flag) ((__force fmode_t)(((flag + 1) & O_ACCMODE) | \ - (flag & FMODE_NONOTIFY))) + (flag & __FMODE_NONOTIFY))) #endif /* __KERNEL__ */ #endif /* _LINUX_FS_H */ -- cgit v1.2.3 From 3cd90ea42f2c15f928b70ed66f6d8ed0a8e7aadd Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 1 Feb 2011 15:52:46 -0800 Subject: vfs: sparse: add __FMODE_EXEC FMODE_EXEC is a constant type of fmode_t but was used with normal integer constants. This results in following warnings from sparse. Fix it using new macro __FMODE_EXEC. fs/exec.c:116:58: warning: restricted fmode_t degrades to integer fs/exec.c:689:58: warning: restricted fmode_t degrades to integer fs/fcntl.c:777:9: warning: restricted fmode_t degrades to integer Signed-off-by: Namhyung Kim Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 4 ++-- fs/fcntl.c | 2 +- include/linux/fs.h | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/fs/exec.c b/fs/exec.c index c62efcb959c7..52a447d9b6ab 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -120,7 +120,7 @@ SYSCALL_DEFINE1(uselib, const char __user *, library) goto out; file = do_filp_open(AT_FDCWD, tmp, - O_LARGEFILE | O_RDONLY | FMODE_EXEC, 0, + O_LARGEFILE | O_RDONLY | __FMODE_EXEC, 0, MAY_READ | MAY_EXEC | MAY_OPEN); putname(tmp); error = PTR_ERR(file); @@ -723,7 +723,7 @@ struct file *open_exec(const char *name) int err; file = do_filp_open(AT_FDCWD, name, - O_LARGEFILE | O_RDONLY | FMODE_EXEC, 0, + O_LARGEFILE | O_RDONLY | __FMODE_EXEC, 0, MAY_EXEC | MAY_OPEN); if (IS_ERR(file)) goto out; diff --git a/fs/fcntl.c b/fs/fcntl.c index ecc8b3954ed6..cb1026181bdc 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -815,7 +815,7 @@ static int __init fcntl_init(void) __O_SYNC | O_DSYNC | FASYNC | O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | O_NOATIME | O_CLOEXEC | - FMODE_EXEC + __FMODE_EXEC )); fasync_cache = kmem_cache_create("fasync_cache", diff --git a/include/linux/fs.h b/include/linux/fs.h index 675678a01f50..bd3215940c37 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2555,6 +2555,7 @@ int proc_nr_inodes(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); int __init get_filesystem_list(char *buf); +#define __FMODE_EXEC ((__force int) FMODE_EXEC) #define __FMODE_NONOTIFY ((__force int) FMODE_NONOTIFY) #define ACC_MODE(x) ("\004\002\006\006"[(x)&O_ACCMODE]) -- cgit v1.2.3 From e4a9ea5ee7c8812a7bf0c3fb725ceeaa3d4c2fcc Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 27 Jan 2011 09:15:30 -0500 Subject: tracing: Replace trace_event struct array with pointer array Currently the trace_event structures are placed in the _ftrace_events section, and at link time, the linker makes one large array of all the trace_event structures. On boot up, this array is read (much like the initcall sections) and the events are processed. The problem is that there is no guarantee that gcc will place complex structures nicely together in an array format. Two structures in the same file may be placed awkwardly, because gcc has no clue that they are suppose to be in an array. A hack was used previous to force the alignment to 4, to pack the structures together. But this caused alignment issues with other architectures (sparc). Instead of packing the structures into an array, the structures' addresses are now put into the _ftrace_event section. As pointers are always the natural alignment, gcc should always pack them tightly together (otherwise initcall, extable, etc would also fail). By having the pointers to the structures in the section, we can still iterate the trace_events without causing unnecessary alignment problems with other architectures, or depending on the current behaviour of gcc that will likely change in the future just to tick us kernel developers off a little more. The _ftrace_event section is also moved into the .init.data section as it is now only needed at boot up. Suggested-by: David Miller Cc: Mathieu Desnoyers Acked-by: David S. Miller Signed-off-by: Steven Rostedt --- include/asm-generic/vmlinux.lds.h | 7 +++---- include/linux/module.h | 2 +- include/linux/syscalls.h | 10 ++++++---- include/trace/ftrace.h | 24 +++++++++++++----------- kernel/trace/trace_events.c | 12 ++++++------ kernel/trace/trace_export.c | 6 +++--- 6 files changed, 32 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 6ebb81030d2d..f53708be95eb 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -124,7 +124,8 @@ #endif #ifdef CONFIG_EVENT_TRACING -#define FTRACE_EVENTS() VMLINUX_SYMBOL(__start_ftrace_events) = .; \ +#define FTRACE_EVENTS() . = ALIGN(8); \ + VMLINUX_SYMBOL(__start_ftrace_events) = .; \ *(_ftrace_events) \ VMLINUX_SYMBOL(__stop_ftrace_events) = .; #else @@ -179,9 +180,6 @@ TRACE_PRINTKS() \ \ STRUCT_ALIGN(); \ - FTRACE_EVENTS() \ - \ - STRUCT_ALIGN(); \ TRACE_SYSCALLS() /* @@ -482,6 +480,7 @@ KERNEL_CTORS() \ *(.init.rodata) \ MCOUNT_REC() \ + FTRACE_EVENTS() \ DEV_DISCARD(init.rodata) \ CPU_DISCARD(init.rodata) \ MEM_DISCARD(init.rodata) \ diff --git a/include/linux/module.h b/include/linux/module.h index e7c6385c6683..7695a303bb55 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -389,7 +389,7 @@ struct module unsigned int num_trace_bprintk_fmt; #endif #ifdef CONFIG_EVENT_TRACING - struct ftrace_event_call *trace_events; + struct ftrace_event_call **trace_events; unsigned int num_trace_events; #endif #ifdef CONFIG_FTRACE_MCOUNT_RECORD diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 18cd0684fc4e..45508fec366d 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -128,28 +128,30 @@ extern struct trace_event_functions exit_syscall_print_funcs; static struct syscall_metadata \ __attribute__((__aligned__(4))) __syscall_meta_##sname; \ static struct ftrace_event_call __used \ - __attribute__((__aligned__(4))) \ - __attribute__((section("_ftrace_events"))) \ event_enter_##sname = { \ .name = "sys_enter"#sname, \ .class = &event_class_syscall_enter, \ .event.funcs = &enter_syscall_print_funcs, \ .data = (void *)&__syscall_meta_##sname,\ }; \ + static struct ftrace_event_call __used \ + __attribute__((section("_ftrace_events"))) \ + *__event_enter_##sname = &event_enter_##sname; \ __TRACE_EVENT_FLAGS(enter_##sname, TRACE_EVENT_FL_CAP_ANY) #define SYSCALL_TRACE_EXIT_EVENT(sname) \ static struct syscall_metadata \ __attribute__((__aligned__(4))) __syscall_meta_##sname; \ static struct ftrace_event_call __used \ - __attribute__((__aligned__(4))) \ - __attribute__((section("_ftrace_events"))) \ event_exit_##sname = { \ .name = "sys_exit"#sname, \ .class = &event_class_syscall_exit, \ .event.funcs = &exit_syscall_print_funcs, \ .data = (void *)&__syscall_meta_##sname,\ }; \ + static struct ftrace_event_call __used \ + __attribute__((section("_ftrace_events"))) \ + *__event_exit_##sname = &event_exit_##sname; \ __TRACE_EVENT_FLAGS(exit_##sname, TRACE_EVENT_FL_CAP_ANY) #define SYSCALL_METADATA(sname, nb) \ diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index e16610c208c9..3e68366d485a 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -446,14 +446,16 @@ static inline notrace int ftrace_get_offsets_##call( \ * .reg = ftrace_event_reg, * }; * - * static struct ftrace_event_call __used - * __attribute__((__aligned__(4))) - * __attribute__((section("_ftrace_events"))) event_ = { + * static struct ftrace_event_call event_ = { * .name = "", * .class = event_class_