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 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 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 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 00cfa730db0d8378685148e6365b9cec7384b275 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 30 Jan 2011 12:31:30 -0800 Subject: Input: wm831x - add driver for Wolfson WM831x PMIC touchscreen controllers Some of the WM831x series of PMICs from Wolfson Microelectronics include a resistive touchscreen controller. Implement support for these controllers within the input API. Platform data is supported to allow configuration of system parameters such as selection between four and five wire touchscreens and for specification of optional direct to CPU IRQs for sample availability and for pen down. Use of this feature for at least the data IRQ is strongly recommended. Thanks to Julien Boibessot for extensive testing and detailed feedback. Signed-off-by: Mark Brown Tested-by: Julien Boibessot Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 10 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/wm831x-ts.c | 355 ++++++++++++++++++++++++++++++++++ include/linux/mfd/wm831x/pdata.h | 3 +- 4 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 drivers/input/touchscreen/wm831x-ts.c (limited to 'include') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 07ac77d393a4..ec5ee98fa6f6 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -423,6 +423,16 @@ config TOUCHSCREEN_UCB1400 To compile this driver as a module, choose M here: the module will be called ucb1400_ts. +config TOUCHSCREEN_WM831X + tristate "Support for WM831x touchscreen controllers" + depends on MFD_WM831X + help + This enables support for the touchscreen controller on the WM831x + series of PMICs. + + To compile this driver as a module, choose M here: the + module will be called wm831x-ts. + config TOUCHSCREEN_WM97XX tristate "Support for WM97xx AC97 touchscreen controllers" depends on AC97_BUS diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 718bcc814952..e8dcfab7c2dc 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o +obj-$(CONFIG_TOUCHSCREEN_WM831X) += wm831x-ts.o obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c new file mode 100644 index 000000000000..a6121bdb635d --- /dev/null +++ b/drivers/input/touchscreen/wm831x-ts.c @@ -0,0 +1,355 @@ +/* + * Touchscreen driver for WM831x PMICs + * + * Copyright 2011 Wolfson Microelectronics plc. + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * R16424 (0x4028) - Touch Control 1 + */ +#define WM831X_TCH_ENA 0x8000 /* TCH_ENA */ +#define WM831X_TCH_CVT_ENA 0x4000 /* TCH_CVT_ENA */ +#define WM831X_TCH_SLPENA 0x1000 /* TCH_SLPENA */ +#define WM831X_TCH_Z_ENA 0x0400 /* TCH_Z_ENA */ +#define WM831X_TCH_Y_ENA 0x0200 /* TCH_Y_ENA */ +#define WM831X_TCH_X_ENA 0x0100 /* TCH_X_ENA */ +#define WM831X_TCH_DELAY_MASK 0x00E0 /* TCH_DELAY - [7:5] */ +#define WM831X_TCH_DELAY_SHIFT 5 /* TCH_DELAY - [7:5] */ +#define WM831X_TCH_DELAY_WIDTH 3 /* TCH_DELAY - [7:5] */ +#define WM831X_TCH_RATE_MASK 0x001F /* TCH_RATE - [4:0] */ +#define WM831X_TCH_RATE_SHIFT 0 /* TCH_RATE - [4:0] */ +#define WM831X_TCH_RATE_WIDTH 5 /* TCH_RATE - [4:0] */ + +/* + * R16425 (0x4029) - Touch Control 2 + */ +#define WM831X_TCH_PD_WK 0x2000 /* TCH_PD_WK */ +#define WM831X_TCH_5WIRE 0x1000 /* TCH_5WIRE */ +#define WM831X_TCH_PDONLY 0x0800 /* TCH_PDONLY */ +#define WM831X_TCH_ISEL 0x0100 /* TCH_ISEL */ +#define WM831X_TCH_RPU_MASK 0x000F /* TCH_RPU - [3:0] */ +#define WM831X_TCH_RPU_SHIFT 0 /* TCH_RPU - [3:0] */ +#define WM831X_TCH_RPU_WIDTH 4 /* TCH_RPU - [3:0] */ + +/* + * R16426-8 (0x402A-C) - Touch Data X/Y/X + */ +#define WM831X_TCH_PD 0x8000 /* TCH_PD1 */ +#define WM831X_TCH_DATA_MASK 0x0FFF /* TCH_DATA - [11:0] */ +#define WM831X_TCH_DATA_SHIFT 0 /* TCH_DATA - [11:0] */ +#define WM831X_TCH_DATA_WIDTH 12 /* TCH_DATA - [11:0] */ + +struct wm831x_ts { + struct input_dev *input_dev; + struct wm831x *wm831x; + unsigned int data_irq; + unsigned int pd_irq; + bool pressure; + bool pen_down; +}; + +static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data) +{ + struct wm831x_ts *wm831x_ts = irq_data; + struct wm831x *wm831x = wm831x_ts->wm831x; + static int data_types[] = { ABS_X, ABS_Y, ABS_PRESSURE }; + u16 data[3]; + int count = wm831x_ts->pressure ? 3 : 2; + int i, ret; + + wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, + WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT); + + ret = wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, + data); + if (ret != 0) { + dev_err(wm831x->dev, "Failed to read touch data: %d\n", + ret); + return IRQ_NONE; + } + + /* + * We get a pen down reading on every reading, report pen up if any + * individual reading does so. + */ + wm831x_ts->pen_down = true; + for (i = 0; i < count; i++) { + if (!(data[i] & WM831X_TCH_PD)) { + wm831x_ts->pen_down = false; + continue; + } + input_report_abs(wm831x_ts->input_dev, data_types[i], + data[i] & WM831X_TCH_DATA_MASK); + } + + if (!wm831x_ts->pen_down) { + disable_irq_nosync(wm831x_ts->data_irq); + + /* Don't need data any more */ + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | + WM831X_TCH_Z_ENA, 0); + + /* Flush any final samples that arrived while reading */ + wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, + WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT); + + wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, data); + + if (wm831x_ts->pressure) + input_report_abs(wm831x_ts->input_dev, + ABS_PRESSURE, 0); + + input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0); + } + + input_sync(wm831x_ts->input_dev); + + return IRQ_HANDLED; +} + +static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data) +{ + struct wm831x_ts *wm831x_ts = irq_data; + struct wm831x *wm831x = wm831x_ts->wm831x; + int ena; + + /* Start collecting data */ + ena = wm831x_ts->pressure ? WM831X_TCH_Z_ENA : 0; + + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, + WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena); + + input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1); + input_sync(wm831x_ts->input_dev); + + wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, + WM831X_TCHPD_EINT, WM831X_TCHPD_EINT); + + wm831x_ts->pen_down = true; + enable_irq(wm831x_ts->data_irq); + + return IRQ_HANDLED; +} + +static int wm831x_ts_input_open(struct input_dev *idev) +{ + struct wm831x_ts *wm831x_ts = input_get_drvdata(idev); + struct wm831x *wm831x = wm831x_ts->wm831x; + + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_ENA, WM831X_TCH_ENA); + + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_CVT_ENA, WM831X_TCH_CVT_ENA); + + return 0; +} + +static void wm831x_ts_input_close(struct input_dev *idev) +{ + struct wm831x_ts *wm831x_ts = input_get_drvdata(idev); + struct wm831x *wm831x = wm831x_ts->wm831x; + + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_ENA | WM831X_TCH_CVT_ENA | + WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | + WM831X_TCH_Z_ENA, 0); + + if (wm831x_ts->pen_down) + disable_irq(wm831x_ts->data_irq); +} + +static __devinit int wm831x_ts_probe(struct platform_device *pdev) +{ + struct wm831x_ts *wm831x_ts; + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *core_pdata = dev_get_platdata(pdev->dev.parent); + struct wm831x_touch_pdata *pdata = + core_pdata ? core_pdata->touch : NULL; + struct input_dev *input_dev; + int error; + + wm831x_ts = kzalloc(sizeof(struct wm831x_ts), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!wm831x_ts || !input_dev) { + error = -ENOMEM; + goto err_alloc; + } + + wm831x_ts->wm831x = wm831x; + wm831x_ts->input_dev = input_dev; + + /* + * If we have a direct IRQ use it, otherwise use the interrupt + * from the WM831x IRQ controller. + */ + if (pdata && pdata->data_irq) + wm831x_ts->data_irq = pdata->data_irq; + else + wm831x_ts->data_irq = platform_get_irq_byname(pdev, "TCHDATA"); + + if (pdata && pdata->pd_irq) + wm831x_ts->pd_irq = pdata->pd_irq; + else + wm831x_ts->pd_irq = platform_get_irq_byname(pdev, "TCHPD"); + + wm831x_ts->pressure = pdata && pdata->pressure; + + /* Five wire touchscreens can't report pressure */ + if (pdata && pdata->fivewire) { + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, + WM831X_TCH_5WIRE, WM831X_TCH_5WIRE); + + /* Pressure measurements are not possible for five wire mode */ + WARN_ON(pdata->pressure && pdata->fivewire); + wm831x_ts->pressure = false; + } else { + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, + WM831X_TCH_5WIRE, 0); + } + + if (pdata) { + switch (pdata->isel) { + default: + dev_err(&pdev->dev, "Unsupported ISEL setting: %d\n", + pdata->isel); + /* Fall through */ + case 200: + case 0: + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, + WM831X_TCH_ISEL, 0); + break; + case 400: + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, + WM831X_TCH_ISEL, WM831X_TCH_ISEL); + break; + } + } + + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, + WM831X_TCH_PDONLY, 0); + + /* Default to 96 samples/sec */ + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_RATE_MASK, 6); + + error = request_threaded_irq(wm831x_ts->data_irq, + NULL, wm831x_ts_data_irq, + IRQF_ONESHOT, + "Touchscreen data", wm831x_ts); + if (error) { + dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n", + wm831x_ts->data_irq, error); + goto err_alloc; + } + disable_irq(wm831x_ts->data_irq); + + error = request_threaded_irq(wm831x_ts->pd_irq, + NULL, wm831x_ts_pen_down_irq, + IRQF_ONESHOT, + "Touchscreen pen down", wm831x_ts); + if (error) { + dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n", + wm831x_ts->pd_irq, error); + goto err_data_irq; + } + + /* set up touch configuration */ + input_dev->name = "WM831x touchscreen"; + input_dev->phys = "wm831x"; + input_dev->open = wm831x_ts_input_open; + input_dev->close = wm831x_ts_input_close; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_X, 0, 4095, 5, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 4095, 5, 0); + if (wm831x_ts->pressure) + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 4095, 5, 0); + + input_set_drvdata(input_dev, wm831x_ts); + input_dev->dev.parent = &pdev->dev; + + error = input_register_device(input_dev); + if (error) + goto err_pd_irq; + + platform_set_drvdata(pdev, wm831x_ts); + return 0; + +err_pd_irq: + free_irq(wm831x_ts->pd_irq, wm831x_ts); +err_data_irq: + free_irq(wm831x_ts->data_irq, wm831x_ts); +err_alloc: + input_free_device(input_dev); + kfree(wm831x_ts); + + return error; +} + +static __devexit int wm831x_ts_remove(struct platform_device *pdev) +{ + struct wm831x_ts *wm831x_ts = platform_get_drvdata(pdev); + + free_irq(wm831x_ts->pd_irq, wm831x_ts); + free_irq(wm831x_ts->data_irq, wm831x_ts); + input_unregister_device(wm831x_ts->input_dev); + kfree(wm831x_ts); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver wm831x_ts_driver = { + .driver = { + .name = "wm831x-touch", + .owner = THIS_MODULE, + }, + .probe = wm831x_ts_probe, + .remove = __devexit_p(wm831x_ts_remove), +}; + +static int __init wm831x_ts_init(void) +{ + return platform_driver_register(&wm831x_ts_driver); +} +module_init(wm831x_ts_init); + +static void __exit wm831x_ts_exit(void) +{ + platform_driver_unregister(&wm831x_ts_driver); +} +module_exit(wm831x_ts_exit); + +/* Module information */ +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("WM831x PMIC touchscreen driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-touch"); diff --git a/include/linux/mfd/wm831x/pdata.h b/include/linux/mfd/wm831x/pdata.h index fd322aca33ba..173086d42af4 100644 --- a/include/linux/mfd/wm831x/pdata.h +++ b/include/linux/mfd/wm831x/pdata.h @@ -80,7 +80,8 @@ struct wm831x_touch_pdata { int isel; /** Current for pen down (uA) */ int rpu; /** Pen down sensitivity resistor divider */ int pressure; /** Report pressure (boolean) */ - int data_irq; /** Touch data ready IRQ */ + unsigned int data_irq; /** Touch data ready IRQ */ + unsigned int pd_irq; /** Touch pendown detect IRQ */ }; enum wm831x_watchdog_action { -- 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 aebd636bd60e2dda0ebc907dd5f7f4a45174411c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 31 Jan 2011 21:06:39 -0800 Subject: Input: switch completely over to the new versions of get/setkeycode All users of old style get/setkeycode methids have been converted so it is time to retire them. Acked-by: Mauro Carvalho Chehab Acked-by: Jiri Kosina Signed-off-by: Dmitry Torokhov --- drivers/hid/hid-input.c | 4 +-- drivers/input/input.c | 55 ++++-------------------------- drivers/input/misc/ati_remote2.c | 4 +-- drivers/input/sparse-keymap.c | 4 +-- drivers/media/dvb/dvb-usb/dvb-usb-remote.c | 4 +-- drivers/media/rc/rc-main.c | 4 +-- include/linux/input.h | 12 ++----- 7 files changed, 20 insertions(+), 67 deletions(-) (limited to 'include') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 7f552bfad32c..ba2aeea2cbf9 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -888,8 +888,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) hid->ll_driver->hidinput_input_event; input_dev->open = hidinput_open; input_dev->close = hidinput_close; - input_dev->setkeycode_new = hidinput_setkeycode; - input_dev->getkeycode_new = hidinput_getkeycode; + input_dev->setkeycode = hidinput_setkeycode; + input_dev->getkeycode = hidinput_getkeycode; input_dev->name = hid->name; input_dev->phys = hid->phys; diff --git a/drivers/input/input.c b/drivers/input/input.c index 7985114beac7..ee2959bd322c 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -806,22 +806,9 @@ int input_get_keycode(struct input_dev *dev, struct input_keymap_entry *ke) int retval; spin_lock_irqsave(&dev->event_lock, flags); - - if (dev->getkeycode) { - /* - * Support for legacy drivers, that don't implement the new - * ioctls - */ - u32 scancode = ke->index; - - memcpy(ke->scancode, &scancode, sizeof(scancode)); - ke->len = sizeof(scancode); - retval = dev->getkeycode(dev, scancode, &ke->keycode); - } else { - retval = dev->getkeycode_new(dev, ke); - } - + retval = dev->getkeycode(dev, ke); spin_unlock_irqrestore(&dev->event_lock, flags); + return retval; } EXPORT_SYMBOL(input_get_keycode); @@ -846,35 +833,7 @@ int input_set_keycode(struct input_dev *dev, spin_lock_irqsave(&dev->event_lock, flags); - if (dev->setkeycode) { - /* - * Support for legacy drivers, that don't implement the new - * ioctls - */ - unsigned int scancode; - - retval = input_scancode_to_scalar(ke, &scancode); - if (retval) - goto out; - - /* - * We need to know the old scancode, in order to generate a - * keyup effect, if the set operation happens successfully - */ - if (!dev->getkeycode) { - retval = -EINVAL; - goto out; - } - - retval = dev->getkeycode(dev, scancode, &old_keycode); - if (retval) - goto out; - - retval = dev->setkeycode(dev, scancode, ke->keycode); - } else { - retval = dev->setkeycode_new(dev, ke, &old_keycode); - } - + retval = dev->setkeycode(dev, ke, &old_keycode); if (retval) goto out; @@ -1861,11 +1820,11 @@ int input_register_device(struct input_dev *dev) dev->rep[REP_PERIOD] = 33; } - if (!dev->getkeycode && !dev->getkeycode_new) - dev->getkeycode_new = input_default_getkeycode; + if (!dev->getkeycode) + dev->getkeycode = input_default_getkeycode; - if (!dev->setkeycode && !dev->setkeycode_new) - dev->setkeycode_new = input_default_setkeycode; + if (!dev->setkeycode) + dev->setkeycode = input_default_setkeycode; dev_set_name(&dev->dev, "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index 0b0e9be63542..9ccdb82d869a 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -612,8 +612,8 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2) idev->open = ati_remote2_open; idev->close = ati_remote2_close; - idev->getkeycode_new = ati_remote2_getkeycode; - idev->setkeycode_new = ati_remote2_setkeycode; + idev->getkeycode = ati_remote2_getkeycode; + idev->setkeycode = ati_remote2_setkeycode; idev->name = ar2->name; idev->phys = ar2->phys; diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c index 7729e547ba65..337bf51bc984 100644 --- a/drivers/input/sparse-keymap.c +++ b/drivers/input/sparse-keymap.c @@ -210,8 +210,8 @@ int sparse_keymap_setup(struct input_dev *dev, dev->keycode = map; dev->keycodemax = map_size; - dev->getkeycode_new = sparse_keymap_getkeycode; - dev->setkeycode_new = sparse_keymap_setkeycode; + dev->getkeycode = sparse_keymap_getkeycode; + dev->setkeycode = sparse_keymap_setkeycode; return 0; diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c index 347fbd4e2d56..b2b9415d874d 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c @@ -198,8 +198,8 @@ static int legacy_dvb_usb_remote_init(struct dvb_usb_device *d) d->input_dev = input_dev; d->rc_dev = NULL; - input_dev->getkeycode_new = legacy_dvb_usb_getkeycode; - input_dev->setkeycode_new = legacy_dvb_usb_setkeycode; + input_dev->getkeycode = legacy_dvb_usb_getkeycode; + input_dev->setkeycode = legacy_dvb_usb_setkeycode; /* set the bits for the keys */ deb_rc("key map size: %d\n", d->props.rc.legacy.rc_map_size); diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 512a2f4ada0e..c3769283936f 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -966,8 +966,8 @@ struct rc_dev *rc_allocate_device(void) return NULL; } - dev->input_dev->getkeycode_new = ir_getkeycode; - dev->input_dev->setkeycode_new = ir_setkeycode; + dev->input_dev->getkeycode = ir_getkeycode; + dev->input_dev->setkeycode = ir_setkeycode; input_set_drvdata(dev->input_dev, dev); spin_lock_init(&dev->rc_map.lock); diff --git a/include/linux/input.h b/include/linux/input.h index e428382ca28a..056ae8a5bd9b 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -1154,8 +1154,6 @@ struct ff_effect { * sparse keymaps. If not supplied default mechanism will be used. * The method is being called while holding event_lock and thus must * not sleep - * @getkeycode_new: transition method - * @setkeycode_new: transition method * @ff: force feedback structure associated with the device if device * supports force feedback effects * @repeat_key: stores key code of the last key pressed; used to implement @@ -1234,14 +1232,10 @@ struct input_dev { void *keycode; int (*setkeycode)(struct input_dev *dev, - unsigned int scancode, unsigned int keycode); + const struct input_keymap_entry *ke, + unsigned int *old_keycode); int (*getkeycode)(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode); - int (*setkeycode_new)(struct input_dev *dev, - const struct input_keymap_entry *ke, - unsigned int *old_keycode); - int (*getkeycode_new)(struct input_dev *dev, - struct input_keymap_entry *ke); + struct input_keymap_entry *ke); struct ff_device *ff; -- cgit v1.2.3 From 2546bcc2d64c3bd0e7cb802cb8fc6cf757c6be0b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 31 Jan 2011 21:06:34 -0800 Subject: Input: input-polldev - fix a couple of typos Signed-off-by: Dmitry Torokhov --- drivers/input/input-polldev.c | 4 ++-- include/linux/input-polldev.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c index 0559e309bac9..3037842a60d8 100644 --- a/drivers/input/input-polldev.c +++ b/drivers/input/input-polldev.c @@ -192,7 +192,7 @@ static struct attribute_group input_polldev_attribute_group = { }; /** - * input_allocate_polled_device - allocated memory polled device + * input_allocate_polled_device - allocate memory for polled device * * The function allocates memory for a polled device and also * for an input device associated with this polled device. @@ -239,7 +239,7 @@ EXPORT_SYMBOL(input_free_polled_device); * with input layer. The device should be allocated with call to * input_allocate_polled_device(). Callers should also set up poll() * method and set up capabilities (id, name, phys, bits) of the - * corresponing input_dev structure. + * corresponding input_dev structure. */ int input_register_polled_device(struct input_polled_dev *dev) { diff --git a/include/linux/input-polldev.h b/include/linux/input-polldev.h index 5e3dddf8f562..ce0b72464eb8 100644 --- a/include/linux/input-polldev.h +++ b/include/linux/input-polldev.h @@ -22,12 +22,12 @@ * @poll: driver-supplied method that polls the device and posts * input events (mandatory). * @poll_interval: specifies how often the poll() method should be called. - * Defaults to 500 msec unless overriden when registering the device. + * Defaults to 500 msec unless overridden when registering the device. * @poll_interval_max: specifies upper bound for the poll interval. * Defaults to the initial value of @poll_interval. * @poll_interval_min: specifies lower bound for the poll interval. * Defaults to 0. - * @input: input device structire associated with the polled device. + * @input: input device structure associated with the polled device. * Must be properly initialized by the driver (id, name, phys, bits). * * Polled input device provides a skeleton for supporting simple input -- 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 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 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 442b9635c569fef038d5367a7acd906db4677ae1 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 2 Feb 2011 17:05:11 -0800 Subject: tcp: Increase the initial congestion window to 10. Signed-off-by: David S. Miller Acked-by: Nandita Dukkipati --- include/net/tcp.h | 12 +++--------- net/dccp/ccids/ccid2.c | 9 +++++++++ net/ipv4/tcp_input.c | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 917911165e3b..7118668ad534 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -196,6 +196,9 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo); /* TCP thin-stream limits */ #define TCP_THIN_LINEAR_RETRIES 6 /* After 6 linear retries, do exp. backoff */ +/* TCP initial congestion window */ +#define TCP_INIT_CWND 10 + extern struct inet_timewait_death_row tcp_death_row; /* sysctl variables for tcp */ @@ -799,15 +802,6 @@ static inline __u32 tcp_current_ssthresh(const struct sock *sk) /* Use define here intentionally to get WARN_ON location shown at the caller */ #define tcp_verify_left_out(tp) WARN_ON(tcp_left_out(tp) > tp->packets_out) -/* - * Convert RFC 3390 larger initial window into an equivalent number of packets. - * This is based on the numbers specified in RFC 5681, 3.1. - */ -static inline u32 rfc3390_bytes_to_packets(const u32 smss) -{ - return smss <= 1095 ? 4 : (smss > 2190 ? 2 : 3); -} - extern void tcp_enter_cwr(struct sock *sk, const int set_ssthresh); extern __u32 tcp_init_cwnd(struct tcp_sock *tp, struct dst_entry *dst); diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c index e96d5e810039..fadecd20d75b 100644 --- a/net/dccp/ccids/ccid2.c +++ b/net/dccp/ccids/ccid2.c @@ -583,6 +583,15 @@ done: dccp_ackvec_parsed_cleanup(&hc->tx_av_chunks); } +/* + * Convert RFC 3390 larger initial window into an equivalent number of packets. + * This is based on the numbers specified in RFC 5681, 3.1. + */ +static inline u32 rfc3390_bytes_to_packets(const u32 smss) +{ + return smss <= 1095 ? 4 : (smss > 2190 ? 2 : 3); +} + static int ccid2_hc_tx_init(struct ccid *ccid, struct sock *sk) { struct ccid2_hc_tx_sock *hc = ccid_priv(ccid); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index eb7f82ebf4a3..2f692cefd3b0 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -817,7 +817,7 @@ __u32 tcp_init_cwnd(struct tcp_sock *tp, struct dst_entry *dst) __u32 cwnd = (dst ? dst_metric(dst, RTAX_INITCWND) : 0); if (!cwnd) - cwnd = rfc3390_bytes_to_packets(tp->mss_cache); + cwnd = TCP_INIT_CWND; return min_t(__u32, cwnd, tp->snd_cwnd_clamp); } -- cgit v1.2.3 From 45e144339ac59971eb44be32e1282760aaabe861 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 2 Feb 2011 15:21:10 +0000 Subject: sched: CHOKe flow scheduler CHOKe ("CHOose and Kill" or "CHOose and Keep") is an alternative packet scheduler based on the Random Exponential Drop (RED) algorithm. The core idea is: For every packet arrival: Calculate Qave if (Qave < minth) Queue the new packet else Select randomly a packet from the queue if (both packets from same flow) then Drop both the packets else if (Qave > maxth) Drop packet else Admit packet with proability p (same as RED) See also: Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active queue management scheme for approximating fair bandwidth allocation", Proceeding of INFOCOM'2000, March 2000. Help from: Eric Dumazet Patrick McHardy Signed-off-by: Stephen Hemminger Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/pkt_sched.h | 29 ++ net/sched/Kconfig | 11 + net/sched/Makefile | 2 + net/sched/sch_choke.c | 676 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 718 insertions(+) create mode 100644 net/sched/sch_choke.c (limited to 'include') diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h index 776cd93d5f7b..d4bb6f58c90c 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -247,6 +247,35 @@ struct tc_gred_sopt { __u16 pad1; }; +/* CHOKe section */ + +enum { + TCA_CHOKE_UNSPEC, + TCA_CHOKE_PARMS, + TCA_CHOKE_STAB, + __TCA_CHOKE_MAX, +}; + +#define TCA_CHOKE_MAX (__TCA_CHOKE_MAX - 1) + +struct tc_choke_qopt { + __u32 limit; /* Hard queue length (packets) */ + __u32 qth_min; /* Min average threshold (packets) */ + __u32 qth_max; /* Max average threshold (packets) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + unsigned char flags; /* see RED flags */ +}; + +struct tc_choke_xstats { + __u32 early; /* Early drops */ + __u32 pdrop; /* Drops due to queue limits */ + __u32 other; /* Drops due to drop() calls */ + __u32 marked; /* Marked packets */ + __u32 matched; /* Drops due to flow match */ +}; + /* HTB section */ #define TC_HTB_NUMPRIO 8 #define TC_HTB_MAXDEPTH 8 diff --git a/net/sched/Kconfig b/net/sched/Kconfig index e318f458713e..8c19b6e3201e 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -217,6 +217,17 @@ config NET_SCH_MQPRIO If unsure, say N. +config NET_SCH_CHOKE + tristate "CHOose and Keep responsive flow scheduler (CHOKE)" + help + Say Y here if you want to use the CHOKe packet scheduler (CHOose + and Keep for responsive flows, CHOose and Kill for unresponsive + flows). This is a variation of RED which trys to penalize flows + that monopolize the queue. + + To compile this code as a module, choose M here: the + module will be called sch_choke. + config NET_SCH_INGRESS tristate "Ingress Qdisc" depends on NET_CLS_ACT diff --git a/net/sched/Makefile b/net/sched/Makefile index 26ce681a2c60..06c6cdfd1948 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile @@ -33,6 +33,8 @@ 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_SCH_CHOKE) += sch_choke.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_choke.c b/net/sched/sch_choke.c new file mode 100644 index 000000000000..a1cec18dd49c --- /dev/null +++ b/net/sched/sch_choke.c @@ -0,0 +1,676 @@ +/* + * net/sched/sch_choke.c CHOKE scheduler + * + * Copyright (c) 2011 Stephen Hemminger + * Copyright (c) 2011 Eric Dumazet + * + * 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 +#include +#include +#include + +/* + CHOKe stateless AQM for fair bandwidth allocation + ================================================= + + CHOKe (CHOose and Keep for responsive flows, CHOose and Kill for + unresponsive flows) is a variant of RED that penalizes misbehaving flows but + maintains no flow state. The difference from RED is an additional step + during the enqueuing process. If average queue size is over the + low threshold (qmin), a packet is chosen at random from the queue. + If both the new and chosen packet are from the same flow, both + are dropped. Unlike RED, CHOKe is not really a "classful" qdisc because it + needs to access packets in queue randomly. It has a minimal class + interface to allow overriding the builtin flow classifier with + filters. + + Source: + R. Pan, B. Prabhakar, and K. Psounis, "CHOKe, A Stateless + Active Queue Management Scheme for Approximating Fair Bandwidth Allocation", + IEEE INFOCOM, 2000. + + A. Tang, J. Wang, S. Low, "Understanding CHOKe: Throughput and Spatial + Characteristics", IEEE/ACM Transactions on Networking, 2004 + + */ + +/* Upper bound on size of sk_buff table (packets) */ +#define CHOKE_MAX_QUEUE (128*1024 - 1) + +struct choke_sched_data { +/* Parameters */ + u32 limit; + unsigned char flags; + + struct red_parms parms; + +/* Variables */ + struct tcf_proto *filter_list; + struct { + u32 prob_drop; /* Early probability drops */ + u32 prob_mark; /* Early probability marks */ + u32 forced_drop; /* Forced drops, qavg > max_thresh */ + u32 forced_mark; /* Forced marks, qavg > max_thresh */ + u32 pdrop; /* Drops due to queue limits */ + u32 other; /* Drops due to drop() calls */ + u32 matched; /* Drops to flow match */ + } stats; + + unsigned int head; + unsigned int tail; + + unsigned int tab_mask; /* size - 1 */ + + struct sk_buff **tab; +}; + +/* deliver a random number between 0 and N - 1 */ +static u32 random_N(unsigned int N) +{ + return reciprocal_divide(random32(), N); +} + +/* number of elements in queue including holes */ +static unsigned int choke_len(const struct choke_sched_data *q) +{ + return (q->tail - q->head) & q->tab_mask; +} + +/* Is ECN parameter configured */ +static int use_ecn(const struct choke_sched_data *q) +{ + return q->flags & TC_RED_ECN; +} + +/* Should packets over max just be dropped (versus marked) */ +static int use_harddrop(const struct choke_sched_data *q) +{ + return q->flags & TC_RED_HARDDROP; +} + +/* Move head pointer forward to skip over holes */ +static void choke_zap_head_holes(struct choke_sched_data *q) +{ + do { + q->head = (q->head + 1) & q->tab_mask; + if (q->head == q->tail) + break; + } while (q->tab[q->head] == NULL); +} + +/* Move tail pointer backwards to reuse holes */ +static void choke_zap_tail_holes(struct choke_sched_data *q) +{ + do { + q->tail = (q->tail - 1) & q->tab_mask; + if (q->head == q->tail) + break; + } while (q->tab[q->tail] == NULL); +} + +/* Drop packet from queue array by creating a "hole" */ +static void choke_drop_by_idx(struct Qdisc *sch, unsigned int idx) +{ + struct choke_sched_data *q = qdisc_priv(sch); + struct sk_buff *skb = q->tab[idx]; + + q->tab[idx] = NULL; + + if (idx == q->head) + choke_zap_head_holes(q); + if (idx == q->tail) + choke_zap_tail_holes(q); + + sch->qstats.backlog -= qdisc_pkt_len(skb); + qdisc_drop(skb, sch); + qdisc_tree_decrease_qlen(sch, 1); + --sch->q.qlen; +} + +/* + * Compare flow of two packets + * Returns true only if source and destination address and port match. + * false for special cases + */ +static bool choke_match_flow(struct sk_buff *skb1, + struct sk_buff *skb2) +{ + int off1, off2, poff; + const u32 *ports1, *ports2; + u8 ip_proto; + __u32 hash1; + + if (skb1->protocol != skb2->protocol) + return false; + + /* Use hash value as quick check + * Assumes that __skb_get_rxhash makes IP header and ports linear + */ + hash1 = skb_get_rxhash(skb1); + if (!hash1 || hash1 != skb_get_rxhash(skb2)) + return false; + + /* Probably match, but be sure to avoid hash collisions */ + off1 = skb_network_offset(skb1); + off2 = skb_network_offset(skb2); + + switch (skb1->protocol) { + case __constant_htons(ETH_P_IP): { + const struct iphdr *ip1, *ip2; + + ip1 = (const struct iphdr *) (skb1->data + off1); + ip2 = (const struct iphdr *) (skb2->data + off2); + + ip_proto = ip1->protocol; + if (ip_proto != ip2->protocol || + ip1->saddr != ip2->saddr || ip1->daddr != ip2->daddr) + return false; + + if ((ip1->frag_off | ip2->frag_off) & htons(IP_MF | IP_OFFSET)) + ip_proto = 0; + off1 += ip1->ihl * 4; + off2 += ip2->ihl * 4; + break; + } + + case __constant_htons(ETH_P_IPV6): { + const struct ipv6hdr *ip1, *ip2; + + ip1 = (const struct ipv6hdr *) (skb1->data + off1); + ip2 = (const struct ipv6hdr *) (skb2->data + off2); + + ip_proto = ip1->nexthdr; + if (ip_proto != ip2->nexthdr || + ipv6_addr_cmp(&ip1->saddr, &ip2->saddr) || + ipv6_addr_cmp(&ip1->daddr, &ip2->daddr)) + return false; + off1 += 40; + off2 += 40; + } + + default: /* Maybe compare MAC header here? */ + return false; + } + + poff = proto_ports_offset(ip_proto); + if (poff < 0) + return true; + + off1 += poff; + off2 += poff; + + ports1 = (__force u32 *)(skb1->data + off1); + ports2 = (__force u32 *)(skb2->data + off2); + return *ports1 == *ports2; +} + +static inline void choke_set_classid(struct sk_buff *skb, u16 classid) +{ + *(unsigned int *)(qdisc_skb_cb(skb)->data) = classid; +} + +static u16 choke_get_classid(const struct sk_buff *skb) +{ + return *(unsigned int *)(qdisc_skb_cb(skb)->data); +} + +/* + * Classify flow using either: + * 1. pre-existing classification result in skb + * 2. fast internal classification + * 3. use TC filter based classification + */ +static bool choke_classify(struct sk_buff *skb, + struct Qdisc *sch, int *qerr) + +{ + struct choke_sched_data *q = qdisc_priv(sch); + struct tcf_result res; + int result; + + result = tc_classify(skb, q->filter_list, &res); + if (result >= 0) { +#ifdef CONFIG_NET_CLS_ACT + switch (result) { + case TC_ACT_STOLEN: + case TC_ACT_QUEUED: + *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; + case TC_ACT_SHOT: + return false; + } +#endif + choke_set_classid(skb, TC_H_MIN(res.classid)); + return true; + } + + return false; +} + +/* + * Select a packet at random from queue + * HACK: since queue can have holes from previous deletion; retry several + * times to find a random skb but then just give up and return the head + * Will return NULL if queue is empty (q->head == q->tail) + */ +static struct sk_buff *choke_peek_random(const struct choke_sched_data *q, + unsigned int *pidx) +{ + struct sk_buff *skb; + int retrys = 3; + + do { + *pidx = (q->head + random_N(choke_len(q))) & q->tab_mask; + skb = q->tab[*pidx]; + if (skb) + return skb; + } while (--retrys > 0); + + return q->tab[*pidx = q->head]; +} + +/* + * Compare new packet with random packet in queue + * returns true if matched and sets *pidx + */ +static bool choke_match_random(const struct choke_sched_data *q, + struct sk_buff *nskb, + unsigned int *pidx) +{ + struct sk_buff *oskb; + + if (q->head == q->tail) + return false; + + oskb = choke_peek_random(q, pidx); + if (q->filter_list) + return choke_get_classid(nskb) == choke_get_classid(oskb); + + return choke_match_flow(oskb, nskb); +} + +static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch) +{ + struct choke_sched_data *q = qdisc_priv(sch); + struct red_parms *p = &q->parms; + int ret = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; + + if (q->filter_list) { + /* If using external classifiers, get result and record it. */ + if (!choke_classify(skb, sch, &ret)) + goto other_drop; /* Packet was eaten by filter */ + } + + /* Compute average queue usage (see RED) */ + p->qavg = red_calc_qavg(p, sch->q.qlen); + if (red_is_idling(p)) + red_end_of_idle_period(p); + + /* Is queue small? */ + if (p->qavg <= p->qth_min) + p->qcount = -1; + else { + unsigned int idx; + + /* Draw a packet at random from queue and compare flow */ + if (choke_match_random(q, skb, &idx)) { + q->stats.matched++; + choke_drop_by_idx(sch, idx); + goto congestion_drop; + } + + /* Queue is large, always mark/drop */ + if (p->qavg > p->qth_max) { + p->qcount = -1; + + sch->qstats.overlimits++; + if (use_harddrop(q) || !use_ecn(q) || + !INET_ECN_set_ce(skb)) { + q->stats.forced_drop++; + goto congestion_drop; + } + + q->stats.forced_mark++; + } else if (++p->qcount) { + if (red_mark_probability(p, p->qavg)) { + p->qcount = 0; + p->qR = red_random(p); + + sch->qstats.overlimits++; + if (!use_ecn(q) || !INET_ECN_set_ce(skb)) { + q->stats.prob_drop++; + goto congestion_drop; + } + + q->stats.prob_mark++; + } + } else + p->qR = red_random(p); + } + + /* Admit new packet */ + if (sch->q.qlen < q->limit) { + q->tab[q->tail] = skb; + q->tail = (q->tail + 1) & q->tab_mask; + ++sch->q.qlen; + sch->qstats.backlog += qdisc_pkt_len(skb); + return NET_XMIT_SUCCESS; + } + + q->stats.pdrop++; + sch->qstats.drops++; + kfree_skb(skb); + return NET_XMIT_DROP; + + congestion_drop: + qdisc_drop(skb, sch); + return NET_XMIT_CN; + + other_drop: + if (ret & __NET_XMIT_BYPASS) + sch->qstats.drops++; + kfree_skb(skb); + return ret; +} + +static struct sk_buff *choke_dequeue(struct Qdisc *sch) +{ + struct choke_sched_data *q = qdisc_priv(sch); + struct sk_buff *skb; + + if (q->head == q->tail) { + if (!red_is_idling(&q->parms)) + red_start_of_idle_period(&q->parms); + return NULL; + } + + skb = q->tab[q->head]; + q->tab[q->head] = NULL; + choke_zap_head_holes(q); + --sch->q.qlen; + sch->qstats.backlog -= qdisc_pkt_len(skb); + qdisc_bstats_update(sch, skb); + + return skb; +} + +static unsigned int choke_drop(struct Qdisc *sch) +{ + struct choke_sched_data *q = qdisc_priv(sch); + unsigned int len; + + len = qdisc_queue_drop(sch); + if (len > 0) + q->stats.other++; + else { + if (!red_is_idling(&q->parms)) + red_start_of_idle_period(&q->parms); + } + + return len; +} + +static void choke_reset(struct Qdisc *sch) +{ + struct choke_sched_data *q = qdisc_priv(sch); + + red_restart(&q->parms); +} + +static const struct nla_policy choke_policy[TCA_CHOKE_MAX + 1] = { + [TCA_CHOKE_PARMS] = { .len = sizeof(struct tc_red_qopt) }, + [TCA_CHOKE_STAB] = { .len = RED_STAB_SIZE }, +}; + + +static void choke_free(void *addr) +{ + if (addr) { + if (is_vmalloc_addr(addr)) + vfree(addr); + else + kfree(addr); + } +} + +static int choke_change(struct Qdisc *sch, struct nlattr *opt) +{ + struct choke_sched_data *q = qdisc_priv(sch); + struct nlattr *tb[TCA_CHOKE_MAX + 1]; + const struct tc_red_qopt *ctl; + int err; + struct sk_buff **old = NULL; + unsigned int mask; + + if (opt == NULL) + return -EINVAL; + + err = nla_parse_nested(tb, TCA_CHOKE_MAX, opt, choke_policy); + if (err < 0) + return err; + + if (tb[TCA_CHOKE_PARMS] == NULL || + tb[TCA_CHOKE_STAB] == NULL) + return -EINVAL; + + ctl = nla_data(tb[TCA_CHOKE_PARMS]); + + if (ctl->limit > CHOKE_MAX_QUEUE) + return -EINVAL; + + mask = roundup_pow_of_two(ctl->limit + 1) - 1; + if (mask != q->tab_mask) { + struct sk_buff **ntab; + + ntab = kcalloc(mask + 1, sizeof(struct sk_buff *), GFP_KERNEL); + if (!ntab) + ntab = vzalloc((mask + 1) * sizeof(struct sk_buff *)); + if (!ntab) + return -ENOMEM; + + sch_tree_lock(sch); + old = q->tab; + if (old) { + unsigned int oqlen = sch->q.qlen, tail = 0; + + while (q->head != q->tail) { + struct sk_buff *skb = q->tab[q->head]; + + q->head = (q->head + 1) & q->tab_mask; + if (!skb) + continue; + if (tail < mask) { + ntab[tail++] = skb; + continue; + } + sch->qstats.backlog -= qdisc_pkt_len(skb); + --sch->q.qlen; + qdisc_drop(skb, sch); + } + qdisc_tree_decrease_qlen(sch, oqlen - sch->q.qlen); + q->head = 0; + q->tail = tail; + } + + q->tab_mask = mask; + q->tab = ntab; + } else + sch_tree_lock(sch); + + q->flags = ctl->flags; + q->limit = ctl->limit; + + red_set_parms(&q->parms, ctl->qth_min, ctl->qth_max, ctl->Wlog, + ctl->Plog, ctl->Scell_log, + nla_data(tb[TCA_CHOKE_STAB])); + + if (q->head == q->tail) + red_end_of_idle_period(&q->parms); + + sch_tree_unlock(sch); + choke_free(old); + return 0; +} + +static int choke_init(struct Qdisc *sch, struct nlattr *opt) +{ + return choke_change(sch, opt); +} + +static int choke_dump(struct Qdisc *sch, struct sk_buff *skb) +{ + struct choke_sched_data *q = qdisc_priv(sch); + struct nlattr *opts = NULL; + struct tc_red_qopt opt = { + .limit = q->limit, + .flags = q->flags, + .qth_min = q->parms.qth_min >> q->parms.Wlog, + .qth_max = q->parms.qth_max >> q->parms.Wlog, + .Wlog = q->parms.Wlog, + .Plog = q->parms.Plog, + .Scell_log = q->parms.Scell_log, + }; + + opts = nla_nest_start(skb, TCA_OPTIONS); + if (opts == NULL) + goto nla_put_failure; + + NLA_PUT(skb, TCA_CHOKE_PARMS, sizeof(opt), &opt); + return nla_nest_end(skb, opts); + +nla_put_failure: + nla_nest_cancel(skb, opts); + return -EMSGSIZE; +} + +static int choke_dump_stats(struct Qdisc *sch, struct gnet_dump *d) +{ + struct choke_sched_data *q = qdisc_priv(sch); + struct tc_choke_xstats st = { + .early = q->stats.prob_drop + q->stats.forced_drop, + .marked = q->stats.prob_mark + q->stats.forced_mark, + .pdrop = q->stats.pdrop, + .other = q->stats.other, + .matched = q->stats.matched, + }; + + return gnet_stats_copy_app(d, &st, sizeof(st)); +} + +static void choke_destroy(struct Qdisc *sch) +{ + struct choke_sched_data *q = qdisc_priv(sch); + + tcf_destroy_chain(&q->filter_list); + choke_free(q->tab); +} + +static struct Qdisc *choke_leaf(struct Qdisc *sch, unsigned long arg) +{ + return NULL; +} + +static unsigned long choke_get(struct Qdisc *sch, u32 classid) +{ + return 0; +} + +static void choke_put(struct Qdisc *q, unsigned long cl) +{ +} + +static unsigned long choke_bind(struct Qdisc *sch, unsigned long parent, + u32 classid) +{ + return 0; +} + +static struct tcf_proto **choke_find_tcf(struct Qdisc *sch, unsigned long cl) +{ + struct choke_sched_data *q = qdisc_priv(sch); + + if (cl) + return NULL; + return &q->filter_list; +} + +static int choke_dump_class(struct Qdisc *sch, unsigned long cl, + struct sk_buff *skb, struct tcmsg *tcm) +{ + tcm->tcm_handle |= TC_H_MIN(cl); + return 0; +} + +static void choke_walk(struct Qdisc *sch, struct qdisc_walker *arg) +{ + if (!arg->stop) { + if (arg->fn(sch, 1, arg) < 0) { + arg->stop = 1; + return; + } + arg->count++; + } +} + +static const struct Qdisc_class_ops choke_class_ops = { + .leaf = choke_leaf, + .get = choke_get, + .put = choke_put, + .tcf_chain = choke_find_tcf, + .bind_tcf = choke_bind, + .unbind_tcf = choke_put, + .dump = choke_dump_class, + .walk = choke_walk, +}; + +static struct sk_buff *choke_peek_head(struct Qdisc *sch) +{ + struct choke_sched_data *q = qdisc_priv(sch); + + return (q->head != q->tail) ? q->tab[q->head] : NULL; +} + +static struct Qdisc_ops choke_qdisc_ops __read_mostly = { + .id = "choke", + .priv_size = sizeof(struct choke_sched_data), + + .enqueue = choke_enqueue, + .dequeue = choke_dequeue, + .peek = choke_peek_head, + .drop = choke_drop, + .init = choke_init, + .destroy = choke_destroy, + .reset = choke_reset, + .change = choke_change, + .dump = choke_dump, + .dump_stats = choke_dump_stats, + .owner = THIS_MODULE, +}; + +static int __init choke_module_init(void) +{ + return register_qdisc(&choke_qdisc_ops); +} + +static void __exit choke_module_exit(void) +{ + unregister_qdisc(&choke_qdisc_ops); +} + +module_init(choke_module_init) +module_exit(choke_module_exit) + +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From fe4b04fa31a6dcf4358aa84cf81e5a7fd079469b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Feb 2011 13:19:09 +0100 Subject: perf: Cure task_oncpu_function_call() races Oleg reported that on architectures with __ARCH_WANT_INTERRUPTS_ON_CTXSW the IPI from task_oncpu_function_call() can land before perf_event_task_sched_in() and cause interesting situations for eg. perf_install_in_context(). This patch reworks the task_oncpu_function_call() interface to give a more usable primitive as well as rework all its users to hopefully be more obvious as well as remove the races. While looking at the code I also found a number of races against perf_event_task_sched_out() which can flip contexts between tasks so plug those too. Reported-and-reviewed-by: Oleg Nesterov Signed-off-by: Peter Zijlstra LKML-Reference: Signed-off-by: Ingo Molnar --- include/linux/sched.h | 7 -- kernel/perf_event.c | 260 ++++++++++++++++++++++++++++++++------------------ kernel/sched.c | 29 +----- 3 files changed, 172 insertions(+), 124 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index d747f948b34e..0b40ee3f6d7a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2578,13 +2578,6 @@ static inline void inc_syscw(struct task_struct *tsk) #define TASK_SIZE_OF(tsk) TASK_SIZE #endif -/* - * Call the function if the target task is executing on a CPU right now: - */ -extern void task_oncpu_function_call(struct task_struct *p, - void (*func) (void *info), void *info); - - #ifdef CONFIG_MM_OWNER extern void mm_update_next_owner(struct mm_struct *mm); extern void mm_init_owner(struct mm_struct *mm, struct task_struct *p); diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 126a302c481c..7d3faa25e136 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -38,6 +38,79 @@ #include +struct remote_function_call { + struct task_struct *p; + int (*func)(void *info); + void *info; + int ret; +}; + +static void remote_function(void *data) +{ + struct remote_function_call *tfc = data; + struct task_struct *p = tfc->p; + + if (p) { + tfc->ret = -EAGAIN; + if (task_cpu(p) != smp_processor_id() || !task_curr(p)) + return; + } + + tfc->ret = tfc->func(tfc->info); +} + +/** + * task_function_call - call a function on the cpu on which a task runs + * @p: the task to evaluate + * @func: the function to be called + * @info: the function call argument + * + * Calls the function @func when the task is currently running. This might + * be on the current CPU, which just calls the function directly + * + * returns: @func return value, or + * -ESRCH - when the process isn't running + * -EAGAIN - when the process moved away + */ +static int +task_function_call(struct task_struct *p, int (*func) (void *info), void *info) +{ + struct remote_function_call data = { + .p = p, + .func = func, + .info = info, + .ret = -ESRCH, /* No such (running) process */ + }; + + if (task_curr(p)) + smp_call_function_single(task_cpu(p), remote_function, &data, 1); + + return data.ret; +} + +/** + * cpu_function_call - call a function on the cpu + * @func: the function to be called + * @info: the function call argument + * + * Calls the function @func on the remote cpu. + * + * returns: @func return value or -ENXIO when the cpu is offline + */ +static int cpu_function_call(int cpu, int (*func) (void *info), void *info) +{ + struct remote_function_call data = { + .p = NULL, + .func = func, + .info = info, + .ret = -ENXIO, /* No such CPU */ + }; + + smp_call_function_single(cpu, remote_function, &data, 1); + + return data.ret; +} + enum event_type_t { EVENT_FLEXIBLE = 0x1, EVENT_PINNED = 0x2, @@ -254,7 +327,6 @@ static void perf_unpin_context(struct perf_event_context *ctx) raw_spin_lock_irqsave(&ctx->lock, flags); --ctx->pin_count; raw_spin_unlock_irqrestore(&ctx->lock, flags); - put_ctx(ctx); } /* @@ -618,35 +690,24 @@ __get_cpu_context(struct perf_event_context *ctx) * We disable the event on the hardware level first. After that we * remove it from the context list. */ -static void __perf_event_remove_from_context(void *info) +static int __perf_remove_from_context(void *info) { struct perf_event *event = info; struct perf_event_context *ctx = event->ctx; struct perf_cpu_context *cpuctx = __get_cpu_context(ctx); - /* - * If this is a task context, we need to check whether it is - * the current task context of this cpu. If not it has been - * scheduled out before the smp call arrived. - */ - if (ctx->task && cpuctx->task_ctx != ctx) - return; - raw_spin_lock(&ctx->lock); - event_sched_out(event, cpuctx, ctx); - list_del_event(event, ctx); - raw_spin_unlock(&ctx->lock); + + return 0; } /* * Remove the event from a task's (or a CPU's) list of events. * - * Must be called with ctx->mutex held. - * * CPU events are removed with a smp call. For task events we only * call when the task is on a CPU. * @@ -657,49 +718,48 @@ static void __perf_event_remove_from_context(void *info) * When called from perf_event_exit_task, it's OK because the * context has been detached from its task. */ -static void perf_event_remove_from_context(struct perf_event *event) +static void perf_remove_from_context(struct perf_event *event) { struct perf_event_context *ctx = event->ctx; struct task_struct *task = ctx->task; + lockdep_assert_held(&ctx->mutex); + if (!task) { /* * Per cpu events are removed via an smp call and * the removal is always successful. */ - smp_call_function_single(event->cpu, - __perf_event_remove_from_context, - event, 1); + cpu_function_call(event->cpu, __perf_remove_from_context, event); return; } retry: - task_oncpu_function_call(task, __perf_event_remove_from_context, - event); + if (!task_function_call(task, __perf_remove_from_context, event)) + return; raw_spin_lock_irq(&ctx->lock); /* - * If the context is active we need to retry the smp call. + * If we failed to find a running task, but find the context active now + * that we've acquired the ctx->lock, retry. */ - if (ctx->nr_active && !list_empty(&event->group_entry)) { + if (ctx->is_active) { raw_spin_unlock_irq(&ctx->lock); goto retry; } /* - * The lock prevents that this context is scheduled in so we - * can remove the event safely, if the call above did not - * succeed. + * Since the task isn't running, its safe to remove the event, us + * holding the ctx->lock ensures the task won't get scheduled in. */ - if (!list_empty(&event->group_entry)) - list_del_event(event, ctx); + list_del_event(event, ctx); raw_spin_unlock_irq(&ctx->lock); } /* * Cross CPU call to disable a performance event */ -static void __perf_event_disable(void *info) +static int __perf_event_disable(void *info) { struct perf_event *event = info; struct perf_event_context *ctx = event->ctx; @@ -708,9 +768,12 @@ static void __perf_event_disable(void *info) /* * If this is a per-task event, need to check whether this * event's task is the current task on this cpu. + * + * Can trigger due to concurrent perf_event_context_sched_out() + * flipping contexts around. */ if (ctx->task && cpuctx->task_ctx != ctx) - return; + return -EINVAL; raw_spin_lock(&ctx->lock); @@ -729,6 +792,8 @@ static void __perf_event_disable(void *info) } raw_spin_unlock(&ctx->lock); + + return 0; } /* @@ -753,13 +818,13 @@ void perf_event_disable(struct perf_event *event) /* * Disable the event on the cpu that it's on */ - smp_call_function_single(event->cpu, __perf_event_disable, - event, 1); + cpu_function_call(event->cpu, __perf_event_disable, event); return; } retry: - task_oncpu_function_call(task, __perf_event_disable, event); + if (!task_function_call(task, __perf_event_disable, event)) + return; raw_spin_lock_irq(&ctx->lock); /* @@ -767,6 +832,11 @@ retry: */ if (event->state == PERF_EVENT_STATE_ACTIVE) { raw_spin_unlock_irq(&ctx->lock); + /* + * Reload the task pointer, it might have been changed by + * a concurrent perf_event_context_sched_out(). + */ + task = ctx->task; goto retry; } @@ -778,7 +848,6 @@ retry: update_group_times(event); event->state = PERF_EVENT_STATE_OFF; } - raw_spin_unlock_irq(&ctx->lock); } @@ -928,12 +997,14 @@ static void add_event_to_ctx(struct perf_event *event, event->tstamp_stopped = tstamp; } +static void perf_event_context_sched_in(struct perf_event_context *ctx); + /* * Cross CPU call to install and enable a performance event * * Must be called with ctx->mutex held */ -static void __perf_install_in_context(void *info) +static int __perf_install_in_context(void *info) { struct perf_event *event = info; struct perf_event_context *ctx = event->ctx; @@ -942,17 +1013,12 @@ static void __perf_install_in_context(void *info) int err; /* - * If this is a task context, we need to check whether it is - * the current task context of this cpu. If not it has been - * scheduled out before the smp call arrived. - * Or possibly this is the right context but it isn't - * on this cpu because it had no events. + * In case we're installing a new context to an already running task, + * could also happen before perf_event_task_sched_in() on architectures + * which do context switches with IRQs enabled. */ - if (ctx->task && cpuctx->task_ctx != ctx) { - if (cpuctx->task_ctx || ctx->task != current) - return; - cpuctx->task_ctx = ctx; - } + if (ctx->task && !cpuctx->task_ctx) + perf_event_context_sched_in(ctx); raw_spin_lock(&ctx->lock); ctx->is_active = 1; @@ -997,6 +1063,8 @@ static void __perf_install_in_context(void *info) unlock: raw_spin_unlock(&ctx->lock); + + return 0; } /* @@ -1008,8 +1076,6 @@ unlock: * If the event is attached to a task which is on a CPU we use a smp * call to enable it in the task context. The task might have been * scheduled away, but we check this in the smp call again. - * - * Must be called with ctx->mutex held. */ static void perf_install_in_context(struct perf_event_context *ctx, @@ -1018,6 +1084,8 @@ perf_install_in_context(struct perf_event_context *ctx, { struct task_struct *task = ctx->task; + lockdep_assert_held(&ctx->mutex); + event->ctx = ctx; if (!task) { @@ -1025,31 +1093,29 @@ perf_install_in_context(struct perf_event_context *ctx, * Per cpu events are installed via an smp call and * the install is always successful. */ - smp_call_function_single(cpu, __perf_install_in_context, - event, 1); + cpu_function_call(cpu, __perf_install_in_context, event); return; } retry: - task_oncpu_function_call(task, __perf_install_in_context, - event); + if (!task_function_call(task, __perf_install_in_context, event)) + return; raw_spin_lock_irq(&ctx->lock); /* - * we need to retry the smp call. + * If we failed to find a running task, but find the context active now + * that we've acquired the ctx->lock, retry. */ - if (ctx->is_active && list_empty(&event->group_entry)) { + if (ctx->is_active) { raw_spin_unlock_irq(&ctx->lock); goto retry; } /* - * The lock prevents that this context is scheduled in so we - * can add the event safely, if it the call above did not - * succeed. + * Since the task isn't running, its safe to add the event, us holding + * the ctx->lock ensures the task won't get scheduled in. */ - if (list_empty(&event->group_entry)) - add_event_to_ctx(event, ctx); + add_event_to_ctx(event, ctx); raw_spin_unlock_irq(&ctx->lock); } @@ -1078,7 +1144,7 @@ static void __perf_event_mark_enabled(struct perf_event *event, /* * Cross CPU call to enable a performance event */ -static void __perf_event_enable(void *info) +static int __perf_event_enable(void *info) { struct perf_event *event = info; struct perf_event_context *ctx = event->ctx; @@ -1086,18 +1152,10 @@ static void __perf_event_enable(void *info) struct perf_cpu_context *cpuctx = __get_cpu_context(ctx); int err; - /* - * If this is a per-task event, need to check whether this - * event's task is the current task on this cpu. - */ - if (ctx->task && cpuctx->task_ctx != ctx) { - if (cpuctx->task_ctx || ctx->task != current) - return; - cpuctx->task_ctx = ctx; - } + if (WARN_ON_ONCE(!ctx->is_active)) + return -EINVAL; raw_spin_lock(&ctx->lock); - ctx->is_active = 1; update_context_time(ctx); if (event->state >= PERF_EVENT_STATE_INACTIVE) @@ -1138,6 +1196,8 @@ static void __perf_event_enable(void *info) unlock: raw_spin_unlock(&ctx->lock); + + return 0; } /* @@ -1158,8 +1218,7 @@ void perf_event_enable(struct perf_event *event) /* * Enable the event on the cpu that it's on */ - smp_call_function_single(event->cpu, __perf_event_enable, - event, 1); + cpu_function_call(event->cpu, __perf_event_enable, event); return; } @@ -1178,8 +1237,15 @@ void perf_event_enable(struct perf_event *event) event->state = PERF_EVENT_STATE_OFF; retry: + if (!ctx->is_active) { + __perf_event_mark_enabled(event, ctx); + goto out; + } + raw_spin_unlock_irq(&ctx->lock); - task_oncpu_function_call(task, __perf_event_enable, event); + + if (!task_function_call(task, __perf_event_enable, event)) + return; raw_spin_lock_irq(&ctx->lock); @@ -1187,15 +1253,14 @@ retry: * If the context is active and the event is still off, * we need to retry the cross-call. */ - if (ctx->is_active && event->state == PERF_EVENT_STATE_OFF) + if (ctx->is_active && event->state == PERF_EVENT_STATE_OFF) { + /* + * task could have been flipped by a concurrent + * perf_event_context_sched_out() + */ + task = ctx->task; goto retry; - - /* - * Since we have the lock this context can't be scheduled - * in, so we can change the state safely. - */ - if (event->state == PERF_EVENT_STATE_OFF) - __perf_event_mark_enabled(event, ctx); + } out: raw_spin_unlock_irq(&ctx->lock); @@ -1339,8 +1404,8 @@ static void perf_event_sync_stat(struct perf_event_context *ctx, } } -void perf_event_context_sched_out(struct task_struct *task, int ctxn, - struct task_struct *next) +static void perf_event_context_sched_out(struct task_struct *task, int ctxn, + struct task_struct *next) { struct perf_event_context *ctx = task->perf_event_ctxp[ctxn]; struct perf_event_context *next_ctx; @@ -1533,7 +1598,7 @@ static void task_ctx_sched_in(struct perf_event_context *ctx, { struct perf_cpu_context *cpuctx; - cpuctx = __get_cpu_context(ctx); + cpuctx = __get_cpu_context(ctx); if (cpuctx->task_ctx == ctx) return; @@ -1541,7 +1606,7 @@ static void task_ctx_sched_in(struct perf_event_context *ctx, cpuctx->task_ctx = ctx; } -void perf_event_context_sched_in(struct perf_event_context *ctx) +static void perf_event_context_sched_in(struct perf_event_context *ctx) { struct perf_cpu_context *cpuctx; @@ -1627,7 +1692,7 @@ static u64 perf_calculate_period(struct perf_event *event, u64 nsec, u64 count) * Reduce accuracy by one bit such that @a and @b converge * to a similar magnitude. */ -#define REDUCE_FLS(a, b) \ +#define REDUCE_FLS(a, b) \ do { \ if (a##_fls > b##_fls) { \ a >>= 1; \ @@ -2213,6 +2278,9 @@ errout: } +/* + * Returns a matching context with refcount and pincount. + */ static struct perf_event_context * find_get_context(struct pmu *pmu, struct task_struct *task, int cpu) { @@ -2237,6 +2305,7 @@ find_get_context(struct pmu *pmu, struct task_struct *task, int cpu) cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu); ctx = &cpuctx->ctx; get_ctx(ctx); + ++ctx->pin_count; return ctx; } @@ -2250,6 +2319,7 @@ retry: ctx = perf_lock_task_context(task, ctxn, &flags); if (ctx) { unclone_ctx(ctx); + ++ctx->pin_count; raw_spin_unlock_irqrestore(&ctx->lock, flags); } @@ -2271,8 +2341,10 @@ retry: err = -ESRCH; else if (task->perf_event_ctxp[ctxn]) err = -EAGAIN; - else + else { + ++ctx->pin_count; rcu_assign_pointer(task->perf_event_ctxp[ctxn], ctx); + } mutex_unlock(&task->perf_event_mutex); if (unlikely(err)) { @@ -5950,10 +6022,10 @@ SYSCALL_DEFINE5(perf_event_open, struct perf_event_context *gctx = group_leader->ctx; mutex_lock(&gctx->mutex); - perf_event_remove_from_context(group_leader); + perf_remove_from_context(group_leader); list_for_each_entry(sibling, &group_leader->sibling_list, group_entry) { - perf_event_remove_from_context(sibling); + perf_remove_from_context(sibling); put_ctx(gctx); } mutex_unlock(&gctx->mutex); @@ -5976,6 +6048,7 @@ SYSCALL_DEFINE5(perf_event_open, perf_install_in_context(ctx, event, cpu); ++ctx->generation; + perf_unpin_context(ctx); mutex_unlock(&ctx->mutex); event->owner = current; @@ -6001,6 +6074,7 @@ SYSCALL_DEFINE5(perf_event_open, return event_fd; err_context: + perf_unpin_context(ctx); put_ctx(ctx); err_alloc: free_event(event); @@ -6051,6 +6125,7 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, mutex_lock(&ctx->mutex); perf_install_in_context(ctx, event, cpu); ++ctx->generation; + perf_unpin_context(ctx); mutex_unlock(&ctx->mutex); return event; @@ -6104,7 +6179,7 @@ __perf_event_exit_task(struct perf_event *child_event, { struct perf_event *parent_event; - perf_event_remove_from_context(child_event); + perf_remove_from_context(child_event); parent_event = child_event->parent; /* @@ -6411,7 +6486,7 @@ inherit_task_group(struct perf_event *event, struct task_struct *parent, return 0; } - child_ctx = child->perf_event_ctxp[ctxn]; + child_ctx = child->perf_event_ctxp[ctxn]; if (!child_ctx) { /* * This is executed from the parent task context, so @@ -6526,6 +6601,7 @@ int perf_event_init_context(struct task_struct *child, int ctxn) mutex_unlock(&parent_ctx->mutex); perf_unpin_context(parent_ctx); + put_ctx(parent_ctx); return ret; } @@ -6595,9 +6671,9 @@ static void __perf_event_exit_context(void *__info) perf_pmu_rotate_stop(ctx->pmu); list_for_each_entry_safe(event, tmp, &ctx->pinned_groups, group_entry) - __perf_event_remove_from_context(event); + __perf_remove_from_context(event); list_for_each_entry_safe(event, tmp, &ctx->flexible_groups, group_entry) - __perf_event_remove_from_context(event); + __perf_remove_from_context(event); } static void perf_event_exit_cpu_context(int cpu) diff --git a/kernel/sched.c b/kernel/sched.c index 18d38e4ec7ba..31cb5d5e1aac 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2265,27 +2265,6 @@ void kick_process(struct task_struct *p) EXPORT_SYMBOL_GPL(kick_process); #endif /* CONFIG_SMP */ -/** - * task_oncpu_function_call - call a function on the cpu on which a task runs - * @p: the task to evaluate - * @func: the function to be called - * @info: the function call argument - * - * Calls the function @func when the task is currently running. This might - * be on the current CPU, which just calls the function directly - */ -void task_oncpu_function_call(struct task_struct *p, - void (*func) (void *info), void *info) -{ - int cpu; - - preempt_disable(); - cpu = task_cpu(p); - if (task_curr(p)) - smp_call_function_single(cpu, func, info, 1); - preempt_enable(); -} - #ifdef CONFIG_SMP /* * ->cpus_allowed is protected by either TASK_WAKING or rq->lock held. @@ -2776,9 +2755,12 @@ static inline void prepare_task_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next) { + sched_info_switch(prev, next); + perf_event_task_sched_out(prev, next); fire_sched_out_preempt_notifiers(prev, next); prepare_lock_switch(rq, next); prepare_arch_switch(next); + trace_sched_switch(prev, next); } /** @@ -2911,7 +2893,7 @@ context_switch(struct rq *rq, struct task_struct *prev, struct mm_struct *mm, *oldmm; prepare_task_switch(rq, prev, next); - trace_sched_switch(prev, next); + mm = next->mm; oldmm = prev->active_mm; /* @@ -3989,9 +3971,6 @@ need_resched_nonpreemptible: rq->skip_clock_update = 0; if (likely(prev != next)) { - sched_info_switch(prev, next); - perf_event_task_sched_out(prev, next); - rq->nr_switches++; rq->curr = next; ++*switch_count; -- cgit v1.2.3 From ac53db596cc08ecb8040cfb6f71ae40c6f2041c4 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Tue, 1 Feb 2011 09:51:03 -0500 Subject: sched: Use a buddy to implement yield_task_fair() Use the buddy mechanism to implement yield_task_fair. This allows us to skip onto the next highest priority se at every level in the CFS tree, unless doing so would introduce gross unfairness in CPU time distribution. We order the buddy selection in pick_next_entity to check yield first, then last, then next. We need next to be able to override yield, because it is possible for the "next" and "yield" task to be different processen in the same sub-tree of the CFS tree. When they are, we need to go into that sub-tree regardless of the "yield" hint, and pick the correct entity once we get to the right level. Signed-off-by: Rik van Riel Signed-off-by: Peter Zijlstra LKML-Reference: <20110201095103.3a79e92a@annuminas.surriel.com> Signed-off-by: Ingo Molnar --- include/linux/sched.h | 2 - kernel/sched.c | 2 +- kernel/sched_debug.c | 2 +- kernel/sched_fair.c | 148 ++++++++++++++++++++++++++++++-------------------- kernel/sysctl.c | 7 --- 5 files changed, 90 insertions(+), 71 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 0542774914d4..4e9fad271c30 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1942,8 +1942,6 @@ int sched_rt_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); -extern unsigned int sysctl_sched_compat_yield; - #ifdef CONFIG_SCHED_AUTOGROUP extern unsigned int sysctl_sched_autogroup_enabled; diff --git a/kernel/sched.c b/kernel/sched.c index 477e1bcc63f9..ae5e1a19b9d6 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -324,7 +324,7 @@ struct cfs_rq { * 'curr' points to currently running entity on this cfs_rq. * It is set to NULL otherwise (i.e when none are currently running). */ - struct sched_entity *curr, *next, *last; + struct sched_entity *curr, *next, *last, *skip; unsigned int nr_spread_over; diff --git a/kernel/sched_debug.c b/kernel/sched_debug.c index eb6cb8edd075..7bacd83a4158 100644 --- a/kernel/sched_debug.c +++ b/kernel/sched_debug.c @@ -179,7 +179,7 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) raw_spin_lock_irqsave(&rq->lock, flags); if (cfs_rq->rb_leftmost) - MIN_vruntime = (__pick_next_entity(cfs_rq))->vruntime; + MIN_vruntime = (__pick_first_entity(cfs_rq))->vruntime; last = __pick_last_entity(cfs_rq); if (last) max_vruntime = last->vruntime; diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index a785e08202cf..c0fbeb992833 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -68,14 +68,6 @@ static unsigned int sched_nr_latency = 8; */ unsigned int sysctl_sched_child_runs_first __read_mostly; -/* - * sys_sched_yield() compat mode - * - * This option switches the agressive yield implementation of the - * old scheduler back on. - */ -unsigned int __read_mostly sysctl_sched_compat_yield; - /* * SCHED_OTHER wake-up granularity. * (default: 1 msec * (1 + ilog(ncpus)), units: nanoseconds) @@ -419,7 +411,7 @@ static void __dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) rb_erase(&se->run_node, &cfs_rq->tasks_timeline); } -static struct sched_entity *__pick_next_entity(struct cfs_rq *cfs_rq) +static struct sched_entity *__pick_first_entity(struct cfs_rq *cfs_rq) { struct rb_node *left = cfs_rq->rb_leftmost; @@ -429,6 +421,17 @@ static struct sched_entity *__pick_next_entity(struct cfs_rq *cfs_rq) return rb_entry(left, struct sched_entity, run_node); } +static struct sched_entity *__pick_next_entity(struct sched_entity *se) +{ + struct rb_node *next = rb_next(&se->run_node); + + if (!next) + return NULL; + + return rb_entry(next, struct sched_entity, run_node); +} + +#ifdef CONFIG_SCHED_DEBUG static struct sched_entity *__pick_last_entity(struct cfs_rq *cfs_rq) { struct rb_node *last = rb_last(&cfs_rq->tasks_timeline); @@ -443,7 +446,6 @@ static struct sched_entity *__pick_last_entity(struct cfs_rq *cfs_rq) * Scheduling class statistics methods: */ -#ifdef CONFIG_SCHED_DEBUG int sched_proc_update_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) @@ -1017,6 +1019,17 @@ static void __clear_buddies_next(struct sched_entity *se) } } +static void __clear_buddies_skip(struct sched_entity *se) +{ + for_each_sched_entity(se) { + struct cfs_rq *cfs_rq = cfs_rq_of(se); + if (cfs_rq->skip == se) + cfs_rq->skip = NULL; + else + break; + } +} + static void clear_buddies(struct cfs_rq *cfs_rq, struct sched_entity *se) { if (cfs_rq->last == se) @@ -1024,6 +1037,9 @@ static void clear_buddies(struct cfs_rq *cfs_rq, struct sched_entity *se) if (cfs_rq->next == se) __clear_buddies_next(se); + + if (cfs_rq->skip == se) + __clear_buddies_skip(se); } static void @@ -1099,7 +1115,7 @@ check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr) return; if (cfs_rq->nr_running > 1) { - struct sched_entity *se = __pick_next_entity(cfs_rq); + struct sched_entity *se = __pick_first_entity(cfs_rq); s64 delta = curr->vruntime - se->vruntime; if (delta < 0) @@ -1143,13 +1159,27 @@ set_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) static int wakeup_preempt_entity(struct sched_entity *curr, struct sched_entity *se); +/* + * Pick the next process, keeping these things in mind, in this order: + * 1) keep things fair between processes/task groups + * 2) pick the "next" process, since someone really wants that to run + * 3) pick the "last" process, for cache locality + * 4) do not run the "skip" process, if something else is available + */ static struct sched_entity *pick_next_entity(struct cfs_rq *cfs_rq) { - struct sched_entity *se = __pick_next_entity(cfs_rq); + struct sched_entity *se = __pick_first_entity(cfs_rq); struct sched_entity *left = se; - if (cfs_rq->next && wakeup_preempt_entity(cfs_rq->next, left) < 1) - se = cfs_rq->next; + /* + * Avoid running the skip buddy, if running something else can + * be done without getting too unfair. + */ + if (cfs_rq->skip == se) { + struct sched_entity *second = __pick_next_entity(se); + if (second && wakeup_preempt_entity(second, left) < 1) + se = second; + } /* * Prefer last buddy, try to return the CPU to a preempted task. @@ -1157,6 +1187,12 @@ static struct sched_entity *pick_next_entity(struct cfs_rq *cfs_rq) if (cfs_rq->last && wakeup_preempt_entity(cfs_rq->last, left) < 1) se = cfs_rq->last; + /* + * Someone really wants this to run. If it's not unfair, run it. + */ + if (cfs_rq->next && wakeup_preempt_entity(cfs_rq->next, left) < 1) + se = cfs_rq->next; + clear_buddies(cfs_rq, se); return se; @@ -1333,52 +1369,6 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) hrtick_update(rq); } -/* - * sched_yield() support is very simple - we dequeue and enqueue. - * - * If compat_yield is turned on then we requeue to the end of the tree. - */ -static void yield_task_fair(struct rq *rq) -{ - struct task_struct *curr = rq->curr; - struct cfs_rq *cfs_rq = task_cfs_rq(curr); - struct sched_entity *rightmost, *se = &curr->se; - - /* - * Are we the only task in the tree? - */ - if (unlikely(rq->nr_running == 1)) - return; - - clear_buddies(cfs_rq, se); - - if (likely(!sysctl_sched_compat_yield) && curr->policy != SCHED_BATCH) { - update_rq_clock(rq); - /* - * Update run-time statistics of the 'current'. - */ - update_curr(cfs_rq); - - return; - } - /* - * Find the rightmost entry in the rbtree: - */ - rightmost = __pick_last_entity(cfs_rq); - /* - * Already in the rightmost position? - */ - if (unlikely(!rightmost || entity_before(rightmost, se))) - return; - - /* - * Minimally necessary key value to be last in the tree: - * Upon rescheduling, sched_class::put_prev_task() will place - * 'current' within the tree based on its new key value. - */ - se->vruntime = rightmost->vruntime + 1; -} - #ifdef CONFIG_SMP static void task_waking_fair(struct rq *rq, struct task_struct *p) @@ -1849,6 +1839,14 @@ static void set_next_buddy(struct sched_entity *se) } } +static void set_skip_buddy(struct sched_entity *se) +{ + if (likely(task_of(se)->policy != SCHED_IDLE)) { + for_each_sched_entity(se) + cfs_rq_of(se)->skip = se; + } +} + /* * Preempt the current task with a newly woken task if needed: */ @@ -1947,6 +1945,36 @@ static void put_prev_task_fair(struct rq *rq, struct task_struct *prev) } } +/* + * sched_yield() is very simple + * + * The magic of dealing with the ->skip buddy is in pick_next_entity. + */ +static void yield_task_fair(struct rq *rq) +{ + struct task_struct *curr = rq->curr; + struct cfs_rq *cfs_rq = task_cfs_rq(curr); + struct sched_entity *se = &curr->se; + + /* + * Are we the only task in the tree? + */ + if (unlikely(rq->nr_running == 1)) + return; + + clear_buddies(cfs_rq, se); + + if (curr->policy != SCHED_BATCH) { + update_rq_clock(rq); + /* + * Update run-time statistics of the 'current'. + */ + update_curr(cfs_rq); + } + + set_skip_buddy(se); +} + #ifdef CONFIG_SMP /************************************************** * Fair scheduling class load-balancing methods: diff --git a/kernel/sysctl.c b/kernel/sysctl.c index bc86bb32e126..cbfda7e2416e 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -360,13 +360,6 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = sched_rt_handler, }, - { - .procname = "sched_compat_yield", - .data = &sysctl_sched_compat_yield, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, #ifdef CONFIG_SCHED_AUTOGROUP { .procname = "sched_autogroup_enabled", -- cgit v1.2.3 From d95f412200652694e63e64bfd49f0ae274a54479 Mon Sep 17 00:00:00 2001 From: Mike Galbraith Date: Tue, 1 Feb 2011 09:50:51 -0500 Subject: sched: Add yield_to(task, preempt) functionality Currently only implemented for fair class tasks. Add a yield_to_task method() to the fair scheduling class. allowing the caller of yield_to() to accelerate another thread in it's thread group, task group. Implemented via a scheduler hint, using cfs_rq->next to encourage the target being selected. We can rely on pick_next_entity to keep things fair, so noone can accelerate a thread that has already used its fair share of CPU time. This also means callers should only call yield_to when they really mean it. Calling it too often can result in the scheduler just ignoring the hint. Signed-off-by: Rik van Riel Signed-off-by: Marcelo Tosatti Signed-off-by: Mike Galbraith Signed-off-by: Peter Zijlstra LKML-Reference: <20110201095051.4ddb7738@annuminas.surriel.com> Signed-off-by: Ingo Molnar --- include/linux/sched.h | 2 ++ kernel/sched.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++ kernel/sched_fair.c | 20 ++++++++++++ 3 files changed, 107 insertions(+) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 4e9fad271c30..c88b3bfbd09e 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1058,6 +1058,7 @@ struct sched_class { void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags); void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags); void (*yield_task) (struct rq *rq); + bool (*yield_to_task) (struct rq *rq, struct task_struct *p, bool preempt); void (*check_preempt_curr) (struct rq *rq, struct task_struct *p, int flags); @@ -1972,6 +1973,7 @@ static inline int rt_mutex_getprio(struct task_struct *p) # define rt_mutex_adjust_pi(p) do { } while (0) #endif +extern bool yield_to(struct task_struct *p, bool preempt); extern void set_user_nice(struct task_struct *p, long nice); extern int task_prio(const struct task_struct *p); extern int task_nice(const struct task_struct *p); diff --git a/kernel/sched.c b/kernel/sched.c index ae5e1a19b9d6..2effcb71a478 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1686,6 +1686,39 @@ static void double_rq_unlock(struct rq *rq1, struct rq *rq2) __release(rq2->lock); } +#else /* CONFIG_SMP */ + +/* + * double_rq_lock - safely lock two runqueues + * + * Note this does not disable interrupts like task_rq_lock, + * you need to do so manually before calling. + */ +static void double_rq_lock(struct rq *rq1, struct rq *rq2) + __acquires(rq1->lock) + __acquires(rq2->lock) +{ + BUG_ON(!irqs_disabled()); + BUG_ON(rq1 != rq2); + raw_spin_lock(&rq1->lock); + __acquire(rq2->lock); /* Fake it out ;) */ +} + +/* + * double_rq_unlock - safely unlock two runqueues + * + * Note this does not restore interrupts like task_rq_unlock, + * you need to do so manually after calling. + */ +static void double_rq_unlock(struct rq *rq1, struct rq *rq2) + __releases(rq1->lock) + __releases(rq2->lock) +{ + BUG_ON(rq1 != rq2); + raw_spin_unlock(&rq1->lock); + __release(rq2->lock); +} + #endif static void calc_load_account_idle(struct rq *this_rq); @@ -5448,6 +5481,58 @@ void __sched yield(void) } EXPORT_SYMBOL(yield); +/** + * yield_to - yield the current processor to another thread in + * your thread group, or accelerate that thread toward the + * processor it's on. + * + * It's the caller's job to ensure that the target task struct + * can't go away on us before we can do any checks. + * + * Returns true if we indeed boosted the target task. + */ +bool __sched yield_to(struct task_struct *p, bool preempt) +{ + struct task_struct *curr = current; + struct rq *rq, *p_rq; + unsigned long flags; + bool yielded = 0; + + local_irq_save(flags); + rq = this_rq(); + +again: + p_rq = task_rq(p); + double_rq_lock(rq, p_rq); + while (task_rq(p) != p_rq) { + double_rq_unlock(rq, p_rq); + goto again; + } + + if (!curr->sched_class->yield_to_task) + goto out; + + if (curr->sched_class != p->sched_class) + goto out; + + if (task_running(p_rq, p) || p->state) + goto out; + + yielded = curr->sched_class->yield_to_task(rq, p, preempt); + if (yielded) + schedstat_inc(rq, yld_count); + +out: + double_rq_unlock(rq, p_rq); + local_irq_restore(flags); + + if (yielded) + schedule(); + + return yielded; +} +EXPORT_SYMBOL_GPL(yield_to); + /* * This task is about to go to sleep on IO. Increment rq->nr_iowait so * that process accounting knows that this is a task in IO wait state. diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index c0fbeb992833..027024694043 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -1975,6 +1975,25 @@ static void yield_task_fair(struct rq *rq) set_skip_buddy(se); } +static bool yield_to_task_fair(struct rq *rq, struct task_struct *p, bool preempt) +{ + struct sched_entity *se = &p->se; + + if (!se->on_rq) + return false; + + /* Tell the scheduler that we'd really like pse to run next. */ + set_next_buddy(se); + + /* Make p's CPU reschedule; pick_next_entity takes care of fairness. */ + if (preempt) + resched_task(rq->curr); + + yield_task_fair(rq); + + return true; +} + #ifdef CONFIG_SMP /************************************************** * Fair scheduling class load-balancing methods: @@ -4243,6 +4262,7 @@ static const struct sched_class fair_sched_class = { .enqueue_task = enqueue_task_fair, .dequeue_task = dequeue_task_fair, .yield_task = yield_task_fair, + .yield_to_task = yield_to_task_fair, .check_preempt_curr = check_preempt_wakeup, -- cgit v1.2.3 From 8211e46004518c977f70f2661da961d5ba617399 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 30 Jan 2011 13:38:25 +0100 Subject: HID: roccat: Add ioctl command to retreive report size from chardev Roccat chardev was reworked to support only a defined report size per device and this can be retreived by an ioctl now to enable future changes in report definitions. Header was moved/renamed from drivers/hid to include/linux for accessibility. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- Documentation/ioctl/ioctl-number.txt | 1 + drivers/hid/hid-roccat-arvo.c | 9 ++++--- drivers/hid/hid-roccat-kone.c | 11 ++++----- drivers/hid/hid-roccat-koneplus.c | 8 +++--- drivers/hid/hid-roccat-kovaplus.c | 8 +++--- drivers/hid/hid-roccat-pyra.c | 11 ++++----- drivers/hid/hid-roccat.c | 48 ++++++++++++++++++++++++++++-------- drivers/hid/hid-roccat.h | 22 ----------------- include/linux/roccat.h | 29 ++++++++++++++++++++++ 9 files changed, 91 insertions(+), 56 deletions(-) delete mode 100644 drivers/hid/hid-roccat.h create mode 100644 include/linux/roccat.h (limited to 'include') diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index ac293e955308..b0f1fa0770d1 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -133,6 +133,7 @@ Code Seq#(hex) Include File Comments 'H' C0-DF net/bluetooth/hidp/hidp.h conflict! 'H' C0-DF net/bluetooth/cmtp/cmtp.h conflict! 'H' C0-DF net/bluetooth/bnep/bnep.h conflict! +'H' F1 linux/roccat.h 'I' all linux/isdn.h conflict! 'I' 00-0F drivers/isdn/divert/isdn_divert.h conflict! 'I' 40-4F linux/mISDNif.h conflict! diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c index d72ee4186d11..7b9a992611bc 100644 --- a/drivers/hid/hid-roccat-arvo.c +++ b/drivers/hid/hid-roccat-arvo.c @@ -21,8 +21,8 @@ #include #include #include +#include #include "hid-ids.h" -#include "hid-roccat.h" #include "hid-roccat-common.h" #include "hid-roccat-arvo.h" @@ -303,7 +303,8 @@ static int arvo_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(arvo_class, hdev); + retval = roccat_connect(arvo_class, hdev, + sizeof(struct arvo_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); } else { @@ -386,8 +387,8 @@ static void arvo_report_to_chrdev(struct arvo_device const *arvo, else roccat_report.action = ARVO_ROCCAT_REPORT_ACTION_RELEASE; - roccat_report_event(arvo->chrdev_minor, (uint8_t const *)&roccat_report, - sizeof(struct arvo_roccat_report)); + roccat_report_event(arvo->chrdev_minor, + (uint8_t const *)&roccat_report); } static int arvo_raw_event(struct hid_device *hdev, diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index 551665359eba..5cdb282dad11 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -30,8 +30,8 @@ #include #include #include +#include #include "hid-ids.h" -#include "hid-roccat.h" #include "hid-roccat-common.h" #include "hid-roccat-kone.h" @@ -660,7 +660,8 @@ static int kone_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(kone_class, hdev); + retval = roccat_connect(kone_class, hdev, + sizeof(struct kone_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); /* be tolerant about not getting chrdev */ @@ -760,8 +761,7 @@ static void kone_report_to_chrdev(struct kone_device const *kone, roccat_report.value = event->value; roccat_report.key = 0; roccat_report_event(kone->chrdev_minor, - (uint8_t *)&roccat_report, - sizeof(struct kone_roccat_report)); + (uint8_t *)&roccat_report); break; case kone_mouse_event_call_overlong_macro: if (event->value == kone_keystroke_action_press) { @@ -769,8 +769,7 @@ static void kone_report_to_chrdev(struct kone_device const *kone, roccat_report.value = kone->actual_profile; roccat_report.key = event->macro_key; roccat_report_event(kone->chrdev_minor, - (uint8_t *)&roccat_report, - sizeof(struct kone_roccat_report)); + (uint8_t *)&roccat_report); } break; } diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index df85ed8a3397..7367e4edfa6c 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -21,8 +21,8 @@ #include #include #include +#include #include "hid-ids.h" -#include "hid-roccat.h" #include "hid-roccat-common.h" #include "hid-roccat-koneplus.h" @@ -612,7 +612,8 @@ static int koneplus_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(koneplus_class, hdev); + retval = roccat_connect(koneplus_class, hdev, + sizeof(struct koneplus_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); } else { @@ -718,8 +719,7 @@ static void koneplus_report_to_chrdev(struct koneplus_device const *koneplus, roccat_report.data2 = button_report->data2; roccat_report.profile = koneplus->actual_profile + 1; roccat_report_event(koneplus->chrdev_minor, - (uint8_t const *)&roccat_report, - sizeof(struct koneplus_roccat_report)); + (uint8_t const *)&roccat_report); } static int koneplus_raw_event(struct hid_device *hdev, diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index 1f547a2b39de..7664e2ce2886 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -20,8 +20,8 @@ #include #include #include +#include #include "hid-ids.h" -#include "hid-roccat.h" #include "hid-roccat-common.h" #include "hid-roccat-kovaplus.h" @@ -524,7 +524,8 @@ static int kovaplus_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(kovaplus_class, hdev); + retval = roccat_connect(kovaplus_class, hdev, + sizeof(struct kovaplus_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); } else { @@ -648,8 +649,7 @@ static void kovaplus_report_to_chrdev(struct kovaplus_device const *kovaplus, roccat_report.data2 = button_report->data2; roccat_report_event(kovaplus->chrdev_minor, - (uint8_t const *)&roccat_report, - sizeof(struct kovaplus_roccat_report)); + (uint8_t const *)&roccat_report); } static int kovaplus_raw_event(struct hid_device *hdev, diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index abe77d3e6d8b..be4daa96f7c2 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -22,8 +22,8 @@ #include #include #include +#include #include "hid-ids.h" -#include "hid-roccat.h" #include "hid-roccat-common.h" #include "hid-roccat-pyra.h" @@ -506,7 +506,8 @@ static int pyra_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(pyra_class, hdev); + retval = roccat_connect(pyra_class, hdev, + sizeof(struct pyra_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); } else { @@ -610,8 +611,7 @@ static void pyra_report_to_chrdev(struct pyra_device const *pyra, roccat_report.value = button_event->data1; roccat_report.key = 0; roccat_report_event(pyra->chrdev_minor, - (uint8_t const *)&roccat_report, - sizeof(struct pyra_roccat_report)); + (uint8_t const *)&roccat_report); break; case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO: case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT: @@ -625,8 +625,7 @@ static void pyra_report_to_chrdev(struct pyra_device const *pyra, */ roccat_report.value = pyra->actual_profile + 1; roccat_report_event(pyra->chrdev_minor, - (uint8_t const *)&roccat_report, - sizeof(struct pyra_roccat_report)); + (uint8_t const *)&roccat_report); } break; } diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index a14c579ea781..0fa23dead5e1 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -26,8 +26,7 @@ #include #include #include - -#include "hid-roccat.h" +#include #define ROCCAT_FIRST_MINOR 0 #define ROCCAT_MAX_DEVICES 8 @@ -37,11 +36,11 @@ struct roccat_report { uint8_t *value; - int len; }; struct roccat_device { unsigned int minor; + int report_size; int open; int exist; wait_queue_head_t wait; @@ -123,7 +122,7 @@ static ssize_t roccat_read(struct file *file, char __user *buffer, * If report is larger than requested amount of data, rest of report * is lost! */ - len = report->len > count ? count : report->len; + len = device->report_size > count ? count : device->report_size; if (copy_to_user(buffer, report->value, len)) { retval = -EFAULT; @@ -248,26 +247,25 @@ static int roccat_release(struct inode *inode, struct file *file) * * This is called from interrupt handler. */ -int roccat_report_event(int minor, u8 const *data, int len) +int roccat_report_event(int minor, u8 const *data) { struct roccat_device *device; struct roccat_reader *reader; struct roccat_report *report; uint8_t *new_value; - new_value = kmemdup(data, len, GFP_ATOMIC); + device = devices[minor]; + + new_value = kmemdup(data, device->report_size, GFP_ATOMIC); if (!new_value) return -ENOMEM; - device = devices[minor]; - report = &device->cbuf[device->cbuf_end]; /* passing NULL is safe */ kfree(report->value); report->value = new_value; - report->len = len; device->cbuf_end = (device->cbuf_end + 1) % ROCCAT_CBUF_SIZE; list_for_each_entry(reader, &device->readers, node) { @@ -295,7 +293,7 @@ EXPORT_SYMBOL_GPL(roccat_report_event); * Return value is minor device number in Range [0, ROCCAT_MAX_DEVICES] on * success, a negative error code on failure. */ -int roccat_connect(struct class *klass, struct hid_device *hid) +int roccat_connect(struct class *klass, struct hid_device *hid, int report_size) { unsigned int minor; struct roccat_device *device; @@ -343,6 +341,7 @@ int roccat_connect(struct class *klass, struct hid_device *hid) device->hid = hid; device->exist = 1; device->cbuf_end = 0; + device->report_size = report_size; return minor; } @@ -373,6 +372,34 @@ void roccat_disconnect(int minor) } EXPORT_SYMBOL_GPL(roccat_disconnect); +static long roccat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct roccat_device *device; + unsigned int minor = iminor(inode); + long retval = 0; + + mutex_lock(&devices_lock); + + device = devices[minor]; + if (!device) { + retval = -ENODEV; + goto out; + } + + switch (cmd) { + case ROCCATIOCGREPSIZE: + if (put_user(device->report_size, (int __user *)arg)) + retval = -EFAULT; + break; + default: + retval = -ENOTTY; + } +out: + mutex_unlock(&devices_lock); + return retval; +} + static const struct file_operations roccat_ops = { .owner = THIS_MODULE, .read = roccat_read, @@ -380,6 +407,7 @@ static const struct file_operations roccat_ops = { .open = roccat_open, .release = roccat_release, .llseek = noop_llseek, + .unlocked_ioctl = roccat_ioctl, }; static int __init roccat_init(void) diff --git a/drivers/hid/hid-roccat.h b/drivers/hid/hid-roccat.h deleted file mode 100644 index c7393372df7c..000000000000 --- a/drivers/hid/hid-roccat.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef __HID_ROCCAT_H -#define __HID_ROCCAT_H - -/* - * Copyright (c) 2010 Stefan Achatz - */ - -/* - * 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 - -int roccat_connect(struct class *klass, struct hid_device *hid); -void roccat_disconnect(int minor); -int roccat_report_event(int minor, u8 const *data, int len); - -#endif diff --git a/include/linux/roccat.h b/include/linux/roccat.h new file mode 100644 index 000000000000..24e1ca01f9a0 --- /dev/null +++ b/include/linux/roccat.h @@ -0,0 +1,29 @@ +#ifndef __HID_ROCCAT_H +#define __HID_ROCCAT_H + +/* + * Copyright (c) 2010 Stefan Achatz + */ + +/* + * 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 + +#define ROCCATIOCGREPSIZE _IOR('H', 0xf1, int) + +#ifdef __KERNEL__ + +int roccat_connect(struct class *klass, struct hid_device *hid, + int report_size); +void roccat_disconnect(int minor); +int roccat_report_event(int minor, u8 const *data); + +#endif + +#endif -- cgit v1.2.3 From 5dc0c9835fb96c75c8dbf657393764bd0abbac04 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Thu, 3 Feb 2011 16:14:43 +0100 Subject: HID: roccat: Rename header roccat.h -> hid-roccat.h It was desired that the header roccat.h should be named hid-roccat.h Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- Documentation/ioctl/ioctl-number.txt | 2 +- drivers/hid/hid-roccat-arvo.c | 2 +- drivers/hid/hid-roccat-kone.c | 2 +- drivers/hid/hid-roccat-koneplus.c | 2 +- drivers/hid/hid-roccat-kovaplus.c | 2 +- drivers/hid/hid-roccat-pyra.c | 2 +- drivers/hid/hid-roccat.c | 2 +- include/linux/hid-roccat.h | 29 +++++++++++++++++++++++++++++ include/linux/roccat.h | 29 ----------------------------- 9 files changed, 36 insertions(+), 36 deletions(-) create mode 100644 include/linux/hid-roccat.h delete mode 100644 include/linux/roccat.h (limited to 'include') diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index b0f1fa0770d1..e68543f767d5 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -133,7 +133,7 @@ Code Seq#(hex) Include File Comments 'H' C0-DF net/bluetooth/hidp/hidp.h conflict! 'H' C0-DF net/bluetooth/cmtp/cmtp.h conflict! 'H' C0-DF net/bluetooth/bnep/bnep.h conflict! -'H' F1 linux/roccat.h +'H' F1 linux/hid-roccat.h 'I' all linux/isdn.h conflict! 'I' 00-0F drivers/isdn/divert/isdn_divert.h conflict! 'I' 40-4F linux/mISDNif.h conflict! diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c index 75f532f4d4cc..2307471d96dc 100644 --- a/drivers/hid/hid-roccat-arvo.c +++ b/drivers/hid/hid-roccat-arvo.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include "hid-ids.h" #include "hid-roccat-common.h" #include "hid-roccat-arvo.h" diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index d7497964a5b3..a57838d15267 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include "hid-ids.h" #include "hid-roccat-common.h" #include "hid-roccat-kone.h" diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index ac2010732495..a8e2117756ac 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include "hid-ids.h" #include "hid-roccat-common.h" #include "hid-roccat-koneplus.h" diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index 4eeb62fb4e75..984be2f8967e 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include "hid-ids.h" #include "hid-roccat-common.h" #include "hid-roccat-kovaplus.h" diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 02a72433a712..160f481344f6 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include "hid-ids.h" #include "hid-roccat-common.h" #include "hid-roccat-pyra.h" diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index bbe294c0dd9b..5666e7587b18 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #define ROCCAT_FIRST_MINOR 0 #define ROCCAT_MAX_DEVICES 8 diff --git a/include/linux/hid-roccat.h b/include/linux/hid-roccat.h new file mode 100644 index 000000000000..24e1ca01f9a0 --- /dev/null +++ b/include/linux/hid-roccat.h @@ -0,0 +1,29 @@ +#ifndef __HID_ROCCAT_H +#define __HID_ROCCAT_H + +/* + * Copyright (c) 2010 Stefan Achatz + */ + +/* + * 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 + +#define ROCCATIOCGREPSIZE _IOR('H', 0xf1, int) + +#ifdef __KERNEL__ + +int roccat_connect(struct class *klass, struct hid_device *hid, + int report_size); +void roccat_disconnect(int minor); +int roccat_report_event(int minor, u8 const *data); + +#endif + +#endif diff --git a/include/linux/roccat.h b/include/linux/roccat.h deleted file mode 100644 index 24e1ca01f9a0..000000000000 --- a/include/linux/roccat.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __HID_ROCCAT_H -#define __HID_ROCCAT_H - -/* - * Copyright (c) 2010 Stefan Achatz - */ - -/* - * 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 - -#define ROCCATIOCGREPSIZE _IOR('H', 0xf1, int) - -#ifdef __KERNEL__ - -int roccat_connect(struct class *klass, struct hid_device *hid, - int report_size); -void roccat_disconnect(int minor); -int roccat_report_event(int minor, u8 const *data); - -#endif - -#endif -- cgit v1.2.3 From 2f1522eccb09188f0008168f75420bc2fedc9cae Mon Sep 17 00:00:00 2001 From: Russ Gorby Date: Wed, 2 Feb 2011 12:56:58 -0800 Subject: serial: ifx6x60: expanded info available from platform data Some platform attributes (e.g. max_hz, use_dma) were being intuited from the modem type. These things should be specified by the platform data. Added max_hz, use_dma to ifx_modem_platform_data definition, replaced is_6160 w/ modem_type, and changed clients accordingly Signed-off-by: Russ Gorby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ifx6x60.c | 33 +++++++++++++++++---------------- drivers/tty/serial/ifx6x60.h | 4 +++- include/linux/spi/ifx_modem.h | 19 ++++++++++++------- 3 files changed, 32 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index ab93763862d5..c42de7152eea 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -8,7 +8,7 @@ * Jan Dumon * * Copyright (C) 2009, 2010 Intel Corp - * Russ Gorby + * Russ Gorby * * 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 @@ -732,7 +732,7 @@ static void ifx_spi_io(unsigned long data) /* * setup dma pointers */ - if (ifx_dev->is_6160) { + if (ifx_dev->use_dma) { ifx_dev->spi_msg.is_dma_mapped = 1; ifx_dev->tx_dma = ifx_dev->tx_bus; ifx_dev->rx_dma = ifx_dev->rx_bus; @@ -960,7 +960,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi) { int ret; int srdy; - struct ifx_modem_platform_data *pl_data = NULL; + struct ifx_modem_platform_data *pl_data; struct ifx_spi_device *ifx_dev; if (saved_ifx_dev) { @@ -968,6 +968,12 @@ static int ifx_spi_spi_probe(struct spi_device *spi) return -ENODEV; } + pl_data = (struct ifx_modem_platform_data *)spi->dev.platform_data; + if (!pl_data) { + dev_err(&spi->dev, "missing platform data!"); + return -ENODEV; + } + /* initialize structure to hold our device variables */ ifx_dev = kzalloc(sizeof(struct ifx_spi_device), GFP_KERNEL); if (!ifx_dev) { @@ -983,7 +989,9 @@ static int ifx_spi_spi_probe(struct spi_device *spi) init_timer(&ifx_dev->spi_timer); ifx_dev->spi_timer.function = ifx_spi_timeout; ifx_dev->spi_timer.data = (unsigned long)ifx_dev; - ifx_dev->is_6160 = pl_data->is_6160; + ifx_dev->modem = pl_data->modem_type; + ifx_dev->use_dma = pl_data->use_dma; + ifx_dev->max_hz = pl_data->max_hz; /* ensure SPI protocol flags are initialized to enable transfer */ ifx_dev->spi_more = 0; @@ -1025,18 +1033,11 @@ static int ifx_spi_spi_probe(struct spi_device *spi) goto error_ret; } - pl_data = (struct ifx_modem_platform_data *)spi->dev.platform_data; - if (pl_data) { - ifx_dev->gpio.reset = pl_data->rst_pmu; - ifx_dev->gpio.po = pl_data->pwr_on; - ifx_dev->gpio.mrdy = pl_data->mrdy; - ifx_dev->gpio.srdy = pl_data->srdy; - ifx_dev->gpio.reset_out = pl_data->rst_out; - } else { - dev_err(&spi->dev, "missing platform data!"); - ret = -ENODEV; - goto error_ret; - } + ifx_dev->gpio.reset = pl_data->rst_pmu; + ifx_dev->gpio.po = pl_data->pwr_on; + ifx_dev->gpio.mrdy = pl_data->mrdy; + ifx_dev->gpio.srdy = pl_data->srdy; + ifx_dev->gpio.reset_out = pl_data->rst_out; dev_info(&spi->dev, "gpios %d, %d, %d, %d, %d", ifx_dev->gpio.reset, ifx_dev->gpio.po, ifx_dev->gpio.mrdy, diff --git a/drivers/tty/serial/ifx6x60.h b/drivers/tty/serial/ifx6x60.h index deb7b8d977dc..0ec39b58ccc4 100644 --- a/drivers/tty/serial/ifx6x60.h +++ b/drivers/tty/serial/ifx6x60.h @@ -88,7 +88,9 @@ struct ifx_spi_device { dma_addr_t rx_dma; dma_addr_t tx_dma; - int is_6160; /* Modem type */ + int modem; /* Modem type */ + int use_dma; /* provide dma-able addrs in SPI msg */ + long max_hz; /* max SPI frequency */ spinlock_t write_lock; int write_pending; diff --git a/include/linux/spi/ifx_modem.h b/include/linux/spi/ifx_modem.h index a68f3b19d112..394fec9e7722 100644 --- a/include/linux/spi/ifx_modem.h +++ b/include/linux/spi/ifx_modem.h @@ -2,13 +2,18 @@ #define LINUX_IFX_MODEM_H struct ifx_modem_platform_data { - unsigned short rst_out; /* modem reset out */ - unsigned short pwr_on; /* power on */ - unsigned short rst_pmu; /* reset modem */ - unsigned short tx_pwr; /* modem power threshold */ - unsigned short srdy; /* SRDY */ - unsigned short mrdy; /* MRDY */ - unsigned short is_6160; /* Modem type */ + unsigned short rst_out; /* modem reset out */ + unsigned short pwr_on; /* power on */ + unsigned short rst_pmu; /* reset modem */ + unsigned short tx_pwr; /* modem power threshold */ + unsigned short srdy; /* SRDY */ + unsigned short mrdy; /* MRDY */ + unsigned char modem_type; /* Modem type */ + unsigned long max_hz; /* max SPI frequency */ + unsigned short use_dma:1; /* spi protocol driver supplies + dma-able addrs */ }; +#define IFX_MODEM_6160 1 +#define IFX_MODEM_6260 2 #endif -- cgit v1.2.3 From d057e5a381cbaec5632117bf62ba49438ab16214 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 31 Jan 2011 22:29:13 +0200 Subject: mac80211: add HW flag for disabling auto link-PS in AP mode When operating in AP mode the wl1271 hardware filters out null-data packets as well as management packets. This makes it impossible for mac80211 to monitor the PS mode by using the PM bit of incoming frames. Implement a HW flag to indicate that mac80211 should ignore the PM bit. In addition, expose ieee80211_sta_ps_transition() to make low-level drivers capable of controlling PS-mode. Signed-off-by: Arik Nemtsov Signed-off-by: John W. Linville --- include/net/mac80211.h | 54 ++++++++++++++++++++++++++++++++++++++++++++++++- net/mac80211/rx.c | 27 +++++++++++++++++++++++-- net/mac80211/sta_info.c | 3 ++- net/mac80211/status.c | 4 ++++ 4 files changed, 84 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d6b0045788ce..0396cecd1d62 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1069,6 +1069,13 @@ enum ieee80211_tkip_key_type { * to decrypt group addressed frames, then IBSS RSN support is still * possible but software crypto will be used. Advertise the wiphy flag * only in that case. + * + * @IEEE80211_HW_AP_LINK_PS: When operating in AP mode the device + * autonomously manages the PS status of connected stations. When + * this flag is set mac80211 will not trigger PS mode for connected + * stations based on the PM bit of incoming frames. + * Use ieee80211_start_ps()/ieee8021_end_ps() to manually configure + * the PS mode of connected stations. */ enum ieee80211_hw_flags { IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, @@ -1093,6 +1100,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_CONNECTION_MONITOR = 1<<19, IEEE80211_HW_SUPPORTS_CQM_RSSI = 1<<20, IEEE80211_HW_SUPPORTS_PER_STA_GTK = 1<<21, + IEEE80211_HW_AP_LINK_PS = 1<<22, }; /** @@ -1701,7 +1709,9 @@ enum ieee80211_ampdu_mlme_action { * station, AP, IBSS/WDS/mesh peer etc. This callback can sleep. * * @sta_notify: Notifies low level driver about power state transition of an - * associated station, AP, IBSS/WDS/mesh peer etc. Must be atomic. + * associated station, AP, IBSS/WDS/mesh peer etc. For a VIF operating + * in AP mode, this callback will not be called when the flag + * %IEEE80211_HW_AP_LINK_PS is set. Must be atomic. * * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max), * bursting) for a hardware TX queue. @@ -2131,6 +2141,48 @@ static inline void ieee80211_rx_ni(struct ieee80211_hw *hw, local_bh_enable(); } +/** + * ieee80211_sta_ps_transition - PS transition for connected sta + * + * When operating in AP mode with the %IEEE80211_HW_AP_LINK_PS + * flag set, use this function to inform mac80211 about a connected station + * entering/leaving PS mode. + * + * This function may not be called in IRQ context or with softirqs enabled. + * + * Calls to this function for a single hardware must be synchronized against + * each other. + * + * The function returns -EINVAL when the requested PS mode is already set. + * + * @sta: currently connected sta + * @start: start or stop PS + */ +int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start); + +/** + * ieee80211_sta_ps_transition_ni - PS transition for connected sta + * (in process context) + * + * Like ieee80211_sta_ps_transition() but can be called in process context + * (internally disables bottom halves). Concurrent call restriction still + * applies. + * + * @sta: currently connected sta + * @start: start or stop PS + */ +static inline int ieee80211_sta_ps_transition_ni(struct ieee80211_sta *sta, + bool start) +{ + int ret; + + local_bh_disable(); + ret = ieee80211_sta_ps_transition(sta, start); + local_bh_enable(); + + return ret; +} + /* * The TX headroom reserved by mac80211 for its own tx_status functions. * This is enough for the radiotap header. diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 7185c9316be2..d78d6fc333d2 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1105,7 +1105,8 @@ static void ap_sta_ps_start(struct sta_info *sta) atomic_inc(&sdata->bss->num_sta_ps); set_sta_flags(sta, WLAN_STA_PS_STA); - drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta); + if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) + drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta); #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "%s: STA %pM aid %d enters power save mode\n", sdata->name, sta->sta.addr, sta->sta.aid); @@ -1134,6 +1135,27 @@ static void ap_sta_ps_end(struct sta_info *sta) ieee80211_sta_ps_deliver_wakeup(sta); } +int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start) +{ + struct sta_info *sta_inf = container_of(sta, struct sta_info, sta); + bool in_ps; + + WARN_ON(!(sta_inf->local->hw.flags & IEEE80211_HW_AP_LINK_PS)); + + /* Don't let the same PS state be set twice */ + in_ps = test_sta_flags(sta_inf, WLAN_STA_PS_STA); + if ((start && in_ps) || (!start && !in_ps)) + return -EINVAL; + + if (start) + ap_sta_ps_start(sta_inf); + else + ap_sta_ps_end(sta_inf); + + return 0; +} +EXPORT_SYMBOL(ieee80211_sta_ps_transition); + static ieee80211_rx_result debug_noinline ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) { @@ -1178,7 +1200,8 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) * Change STA power saving mode only at the end of a frame * exchange sequence. */ - if (!ieee80211_has_morefrags(hdr->frame_control) && + if (!(sta->local->hw.flags & IEEE80211_HW_AP_LINK_PS) && + !ieee80211_has_morefrags(hdr->frame_control) && !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) && (rx->sdata->vif.type == NL80211_IFTYPE_AP || rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) { diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index c426504ed1cf..5a11078827ab 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -899,7 +899,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) struct ieee80211_local *local = sdata->local; int sent, buffered; - drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); + if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) + drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); if (!skb_queue_empty(&sta->ps_tx_buf)) sta_info_clear_tim_bit(sta); diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 38a797217a91..ffb0de9bc2fa 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -98,6 +98,10 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, * (b) always process RX events before TX status events if ordering * can be unknown, for example with different interrupt status * bits. + * (c) if PS mode transitions are manual (i.e. the flag + * %IEEE80211_HW_AP_LINK_PS is set), always process PS state + * changes before calling TX status events if ordering can be + * unknown. */ if (test_sta_flags(sta, WLAN_STA_PS_STA) && skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) { -- cgit v1.2.3 From 681d119047761cc59a15c0bb86891f3a878997cf Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 3 Feb 2011 18:35:19 +0200 Subject: mac80211: Add testing functionality for TKIP TKIP countermeasures depend on devices being able to detect Michael MIC failures on received frames and for stations to report errors to the AP. In order to test that behavior, it is useful to be able to send out TKIP frames with incorrect Michael MIC. This testing behavior has minimal effect on the TX path, so it can be added to mac80211 for convenient use. The interface for using this functionality is a file in mac80211 netdev debugfs (tkip_mic_test). Writing a MAC address to the file makes mac80211 generate a dummy data frame that will be sent out using invalid Michael MIC value. In AP mode, the address needs to be for one of the associated stations or ff:ff:ff:ff:ff:ff to use a broadcast frame. In station mode, the address can be anything, e.g., the current BSSID. It should be noted that this functionality works correctly only when associated and using TKIP. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 4 ++ net/mac80211/debugfs_netdev.c | 102 +++++++++++++++++++++++++++++++++++++++++- net/mac80211/wpa.c | 7 +++ 3 files changed, 112 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 0396cecd1d62..8fcd1691cfb7 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -341,6 +341,9 @@ struct ieee80211_bss_conf { * the off-channel channel when a remain-on-channel offload is done * in hardware -- normal packets still flow and are expected to be * handled properly by the device. + * @IEEE80211_TX_INTFL_TKIP_MIC_FAILURE: Marks this packet to be used for TKIP + * testing. It will be sent out with incorrect Michael MIC key to allow + * TKIP countermeasures to be tested. * * Note: If you have to add new flags to the enumeration, then don't * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary. @@ -370,6 +373,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_LDPC = BIT(22), IEEE80211_TX_CTL_STBC = BIT(23) | BIT(24), IEEE80211_TX_CTL_TX_OFFCHAN = BIT(25), + IEEE80211_TX_INTFL_TKIP_MIC_FAILURE = BIT(26), }; #define IEEE80211_TX_CTL_STBC_SHIFT 23 diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 4cffbf65cccd..dacace6b1393 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -36,7 +36,7 @@ static ssize_t ieee80211_if_read( ret = (*format)(sdata, buf, sizeof(buf)); read_unlock(&dev_base_lock); - if (ret != -EINVAL) + if (ret >= 0) ret = simple_read_from_buffer(userbuf, count, ppos, buf, ret); return ret; @@ -221,6 +221,104 @@ static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, __IEEE80211_IF_FILE_W(smps); +static ssize_t ieee80211_if_fmt_tkip_mic_test( + const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ + return -EOPNOTSUPP; +} + +static int hwaddr_aton(const char *txt, u8 *addr) +{ + int i; + + for (i = 0; i < ETH_ALEN; i++) { + int a, b; + + a = hex_to_bin(*txt++); + if (a < 0) + return -1; + b = hex_to_bin(*txt++); + if (b < 0) + return -1; + *addr++ = (a << 4) | b; + if (i < 5 && *txt++ != ':') + return -1; + } + + return 0; +} + +static ssize_t ieee80211_if_parse_tkip_mic_test( + struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) +{ + struct ieee80211_local *local = sdata->local; + u8 addr[ETH_ALEN]; + struct sk_buff *skb; + struct ieee80211_hdr *hdr; + __le16 fc; + + /* + * Assume colon-delimited MAC address with possible white space + * following. + */ + if (buflen < 3 * ETH_ALEN - 1) + return -EINVAL; + if (hwaddr_aton(buf, addr) < 0) + return -EINVAL; + + if (!ieee80211_sdata_running(sdata)) + return -ENOTCONN; + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 100); + if (!skb) + return -ENOMEM; + skb_reserve(skb, local->hw.extra_tx_headroom); + + hdr = (struct ieee80211_hdr *) skb_put(skb, 24); + memset(hdr, 0, 24); + fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); + + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP: + fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); + /* DA BSSID SA */ + memcpy(hdr->addr1, addr, ETH_ALEN); + memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); + memcpy(hdr->addr3, sdata->vif.addr, ETH_ALEN); + break; + case NL80211_IFTYPE_STATION: + fc |= cpu_to_le16(IEEE80211_FCTL_TODS); + /* BSSID SA DA */ + if (sdata->vif.bss_conf.bssid == NULL) { + dev_kfree_skb(skb); + return -ENOTCONN; + } + memcpy(hdr->addr1, sdata->vif.bss_conf.bssid, ETH_ALEN); + memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); + memcpy(hdr->addr3, addr, ETH_ALEN); + break; + default: + dev_kfree_skb(skb); + return -EOPNOTSUPP; + } + hdr->frame_control = fc; + + /* + * Add some length to the test frame to make it look bit more valid. + * The exact contents does not matter since the recipient is required + * to drop this because of the Michael MIC failure. + */ + memset(skb_put(skb, 50), 0, 50); + + IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_TKIP_MIC_FAILURE; + + ieee80211_tx_skb(sdata, skb); + + return buflen; +} + +__IEEE80211_IF_FILE_W(tkip_mic_test); + /* AP attributes */ IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC); @@ -299,6 +397,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(last_beacon); DEBUGFS_ADD(ave_beacon); DEBUGFS_ADD_MODE(smps, 0600); + DEBUGFS_ADD_MODE(tkip_mic_test, 0200); } static void add_ap_files(struct ieee80211_sub_if_data *sdata) @@ -313,6 +412,7 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(num_sta_ps); DEBUGFS_ADD(dtim_count); DEBUGFS_ADD(num_buffered_multicast); + DEBUGFS_ADD_MODE(tkip_mic_test, 0200); } static void add_wds_files(struct ieee80211_sub_if_data *sdata) diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index cd5e730873a8..f1765de2f4bf 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -46,6 +46,11 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) data = skb->data + hdrlen; data_len = skb->len - hdrlen; + if (unlikely(info->flags & IEEE80211_TX_INTFL_TKIP_MIC_FAILURE)) { + /* Need to use software crypto for the test */ + info->control.hw_key = NULL; + } + if (info->control.hw_key && !(tx->flags & IEEE80211_TX_FRAGMENTED) && !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) { @@ -64,6 +69,8 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) key = &tx->key->conf.key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY]; mic = skb_put(skb, MICHAEL_MIC_LEN); michael_mic(key, hdr, data, data_len, mic); + if (unlikely(info->flags & IEEE80211_TX_INTFL_TKIP_MIC_FAILURE)) + mic[0]++; return TX_CONTINUE; } -- cgit v1.2.3 From 9b99b7f84ea520d2ecaf816bde247a1ad07e454e Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Sat, 1 Jan 2011 13:51:52 +0100 Subject: kobject: Add missing format attribute specifications Several functions in accept printf-style arguments. Some of these functions have been annotated with a format attribute declaration while others have not been annotated. Add a format attribute specification where it is missing. Signed-off-by: Bart Van Assche Signed-off-by: Greg Kroah-Hartman --- include/linux/kobject.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 8f6d12151048..15e82c132f78 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -85,11 +85,13 @@ static inline const char *kobject_name(const struct kobject *kobj) extern void kobject_init(struct kobject *kobj, struct kobj_type *ktype); extern int __must_check kobject_add(struct kobject *kobj, struct kobject *parent, - const char *fmt, ...); + const char *fmt, ...) + __attribute__((format(printf, 3, 4))); extern int __must_check kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, - const char *fmt, ...); + const char *fmt, ...) + __attribute__((format(printf, 4, 5))); extern void kobject_del(struct kobject *kobj); @@ -226,6 +228,7 @@ static inline int kobject_uevent_env(struct kobject *kobj, static inline int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...) + __attribute__((format(printf, 2, 3))) { return 0; } static inline int kobject_action_type(const char *buf, size_t count, -- cgit v1.2.3 From 8ba6ebf583f12da32036fc0f003ab4043e54692e Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Sun, 23 Jan 2011 17:17:24 +0100 Subject: Dynamic debug: Add more flags Add flags that allow the user to specify via debugfs whether or not the module name, function name, line number and/or thread ID have to be included in the printed message. Signed-off-by: Bart Van Assche Cc: Greg Banks Cc: Konrad Rzeszutek Wilk Signed-off-by: Greg Kroah-Hartman --- Documentation/dynamic-debug-howto.txt | 12 +++++-- include/linux/dynamic_debug.h | 8 ++++- lib/dynamic_debug.c | 60 +++++++++++++++++++++++++++++------ 3 files changed, 68 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/Documentation/dynamic-debug-howto.txt b/Documentation/dynamic-debug-howto.txt index 58ea64a96165..e6c4b757025b 100644 --- a/Documentation/dynamic-debug-howto.txt +++ b/Documentation/dynamic-debug-howto.txt @@ -205,12 +205,20 @@ of the characters: The flags are: +f + Include the function name in the printed message +l + Include line number in the printed message +m + Include module name in the printed message p Causes a printk() message to be emitted to dmesg +t + Include thread ID in messages not generated from interrupt context -Note the regexp ^[-+=][scp]+$ matches a flags specification. +Note the regexp ^[-+=][flmpt]+$ matches a flags specification. Note also that there is no convenient syntax to remove all -the flags at once, you need to use "-psc". +the flags at once, you need to use "-flmpt". Debug messages during boot process diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index 1c70028f81f9..0c9653f11c18 100644 --- a/include/linux/dynamic_debug.h +++ b/include/linux/dynamic_debug.h @@ -31,6 +31,10 @@ struct _ddebug { * writes commands to /dynamic_debug/control */ #define _DPRINTK_FLAGS_PRINT (1<<0) /* printk() a message using the format */ +#define _DPRINTK_FLAGS_INCL_MODNAME (1<<1) +#define _DPRINTK_FLAGS_INCL_FUNCNAME (1<<2) +#define _DPRINTK_FLAGS_INCL_LINENO (1<<3) +#define _DPRINTK_FLAGS_INCL_TID (1<<4) #define _DPRINTK_FLAGS_DEFAULT 0 unsigned int flags:8; char enabled; @@ -42,6 +46,8 @@ int ddebug_add_module(struct _ddebug *tab, unsigned int n, #if defined(CONFIG_DYNAMIC_DEBUG) extern int ddebug_remove_module(const char *mod_name); +extern int __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...) + __attribute__ ((format (printf, 2, 3))); #define dynamic_pr_debug(fmt, ...) do { \ static struct _ddebug descriptor \ @@ -50,7 +56,7 @@ extern int ddebug_remove_module(const char *mod_name); { KBUILD_MODNAME, __func__, __FILE__, fmt, __LINE__, \ _DPRINTK_FLAGS_DEFAULT }; \ if (unlikely(descriptor.enabled)) \ - printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \ + __dynamic_pr_debug(&descriptor, pr_fmt(fmt), ##__VA_ARGS__); \ } while (0) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index b335acb43be2..863c83430964 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -7,6 +7,7 @@ * Copyright (C) 2008 Jason Baron * By Greg Banks * Copyright (c) 2008 Silicon Graphics Inc. All Rights Reserved. + * Copyright (C) 2011 Bart Van Assche. All Rights Reserved. */ #include @@ -27,6 +28,7 @@ #include #include #include +#include extern struct _ddebug __start___verbose[]; extern struct _ddebug __stop___verbose[]; @@ -63,15 +65,25 @@ static inline const char *basename(const char *path) return tail ? tail+1 : path; } +static struct { unsigned flag:8; char opt_char; } opt_array[] = { + { _DPRINTK_FLAGS_PRINT, 'p' }, + { _DPRINTK_FLAGS_INCL_MODNAME, 'm' }, + { _DPRINTK_FLAGS_INCL_FUNCNAME, 'f' }, + { _DPRINTK_FLAGS_INCL_LINENO, 'l' }, + { _DPRINTK_FLAGS_INCL_TID, 't' }, +}; + /* format a string into buf[] which describes the _ddebug's flags */ static char *ddebug_describe_flags(struct _ddebug *dp, char *buf, size_t maxlen) { char *p = buf; + int i; BUG_ON(maxlen < 4); - if (dp->flags & _DPRINTK_FLAGS_PRINT) - *p++ = 'p'; + for (i = 0; i < ARRAY_SIZE(opt_array); ++i) + if (dp->flags & opt_array[i].flag) + *p++ = opt_array[i].opt_char; if (p == buf) *p++ = '-'; *p = '\0'; @@ -343,7 +355,7 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp, unsigned int *maskp) { unsigned flags = 0; - int op = '='; + int op = '=', i; switch (*str) { case '+': @@ -358,13 +370,14 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp, printk(KERN_INFO "%s: op='%c'\n", __func__, op); for ( ; *str ; ++str) { - switch (*str) { - case 'p': - flags |= _DPRINTK_FLAGS_PRINT; - break; - default: - return -EINVAL; + for (i = ARRAY_SIZE(opt_array) - 1; i >= 0; i--) { + if (*str == opt_array[i].opt_char) { + flags |= opt_array[i].flag; + break; + } } + if (i < 0) + return -EINVAL; } if (flags == 0) return -EINVAL; @@ -413,6 +426,35 @@ static int ddebug_exec_query(char *query_string) return 0; } +int __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...) +{ + va_list args; + int res; + + BUG_ON(!descriptor); + BUG_ON(!fmt); + + va_start(args, fmt); + res = printk(KERN_DEBUG); + if (descriptor->flags & _DPRINTK_FLAGS_INCL_TID) { + if (in_interrupt()) + res += printk(KERN_CONT " "); + else + res += printk(KERN_CONT "[%d] ", task_pid_vnr(current)); + } + if (descriptor->flags & _DPRINTK_FLAGS_INCL_MODNAME) + res += printk(KERN_CONT "%s:", descriptor->modname); + if (descriptor->flags & _DPRINTK_FLAGS_INCL_FUNCNAME) + res += printk(KERN_CONT "%s:", descriptor->function); + if (descriptor->flags & _DPRINTK_FLAGS_INCL_LINENO) + res += printk(KERN_CONT "%d ", descriptor->lineno); + res += vprintk(fmt, args); + va_end(args); + + return res; +} +EXPORT_SYMBOL(__dynamic_pr_debug); + static __initdata char ddebug_setup_string[1024]; static __init int ddebug_setup_query(char *str) { -- cgit v1.2.3 From 072fc8f0a8df9bf36392f15b729044cb2ad27332 Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Wed, 26 Jan 2011 18:33:32 +0800 Subject: firmware_classs: change val uevent's type to bool Some place in firmware_class.c using "int uevent" define, but others use "bool uevent". This patch replace all int uevent define to bool. Signed-off-by: Bob Liu Acked-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 7 +++---- include/linux/firmware.h | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 40af43ebd92d..8c798ef7f13f 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -593,8 +593,7 @@ int request_firmware(const struct firmware **firmware_p, const char *name, struct device *device) { - int uevent = 1; - return _request_firmware(firmware_p, name, device, uevent, false); + return _request_firmware(firmware_p, name, device, true, false); } /** @@ -618,7 +617,7 @@ struct firmware_work { struct device *device; void *context; void (*cont)(const struct firmware *fw, void *context); - int uevent; + bool uevent; }; static int request_firmware_work_func(void *arg) @@ -661,7 +660,7 @@ static int request_firmware_work_func(void *arg) **/ int request_firmware_nowait( - struct module *module, int uevent, + struct module *module, bool uevent, const char *name, struct device *device, gfp_t gfp, void *context, void (*cont)(const struct firmware *fw, void *context)) { diff --git a/include/linux/firmware.h b/include/linux/firmware.h index 53d1e6c4f848..21b3e7588abd 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -39,7 +39,7 @@ struct builtin_fw { int request_firmware(const struct firmware **fw, const char *name, struct device *device); int request_firmware_nowait( - struct module *module, int uevent, + struct module *module, bool uevent, const char *name, struct device *device, gfp_t gfp, void *context, void (*cont)(const struct firmware *fw, void *context)); @@ -52,7 +52,7 @@ static inline int request_firmware(const struct firmware **fw, return -EINVAL; } static inline int request_firmware_nowait( - struct module *module, int uevent, + struct module *module, bool uevent, const char *name, struct device *device, gfp_t gfp, void *context, void (*cont)(const struct firmware *fw, void *context)) { -- cgit v1.2.3 From d33601644cd3b09afb2edd9474517edc441c8fad Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Thu, 20 Jan 2011 10:44:29 -0600 Subject: memory hotplug: Update phys_index to [start|end]_section_nr Update the 'phys_index' property of a the memory_block struct to be called start_section_nr, and add a end_section_nr property. The data tracked here is the same but the updated naming is more in line with what is stored here, namely the first and last section number that the memory block spans. The names presented to userspace remain the same, phys_index for start_section_nr and end_phys_index for end_section_nr, to avoid breaking anything in userspace. This also updates the node sysfs code to be aware of the new capability for a memory block to contain multiple memory sections and be aware of the memory block structure name changes (start_section_nr). This requires an additional parameter to unregister_mem_sect_under_nodes so that we know which memory section of the memory block to unregister. Signed-off-by: Nathan Fontenot Reviewed-by: Robin Holt Reviewed-by: KAMEZAWA Hiroyuki Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 41 +++++++++++++++++++++++++++++++---------- drivers/base/node.c | 12 ++++++++---- include/linux/memory.h | 3 ++- include/linux/node.h | 6 ++++-- 4 files changed, 45 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 0b7040042587..71b4a32b1710 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -97,7 +97,7 @@ int register_memory(struct memory_block *memory) int error; memory->sysdev.cls = &memory_sysdev_class; - memory->sysdev.id = memory->phys_index / sections_per_block; + memory->sysdev.id = memory->start_section_nr / sections_per_block; error = sysdev_register(&memory->sysdev); return error; @@ -138,12 +138,26 @@ static unsigned long get_memory_block_size(void) * uses. */ -static ssize_t show_mem_phys_index(struct sys_device *dev, +static ssize_t show_mem_start_phys_index(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) { struct memory_block *mem = container_of(dev, struct memory_block, sysdev); - return sprintf(buf, "%08lx\n", mem->phys_index / sections_per_block); + unsigned long phys_index; + + phys_index = mem->start_section_nr / sections_per_block; + return sprintf(buf, "%08lx\n", phys_index); +} + +static ssize_t show_mem_end_phys_index(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) +{ + struct memory_block *mem = + container_of(dev, struct memory_block, sysdev); + unsigned long phys_index; + + phys_index = mem->end_section_nr / sections_per_block; + return sprintf(buf, "%08lx\n", phys_index); } /* @@ -158,7 +172,7 @@ static ssize_t show_mem_removable(struct sys_device *dev, container_of(dev, struct memory_block, sysdev); for (i = 0; i < sections_per_block; i++) { - pfn = section_nr_to_pfn(mem->phys_index + i); + pfn = section_nr_to_pfn(mem->start_section_nr + i); ret &= is_mem_section_removable(pfn, PAGES_PER_SECTION); } @@ -275,14 +289,15 @@ static int memory_block_change_state(struct memory_block *mem, mem->state = MEM_GOING_OFFLINE; for (i = 0; i < sections_per_block; i++) { - ret = memory_section_action(mem->phys_index + i, to_state); + ret = memory_section_action(mem->start_section_nr + i, + to_state); if (ret) break; } if (ret) { for (i = 0; i < sections_per_block; i++) - memory_section_action(mem->phys_index + i, + memory_section_action(mem->start_section_nr + i, from_state_req); mem->state = from_state_req; @@ -330,7 +345,8 @@ static ssize_t show_phys_device(struct sys_device *dev, return sprintf(buf, "%d\n", mem->phys_device); } -static SYSDEV_ATTR(phys_index, 0444, show_mem_phys_index, NULL); +static SYSDEV_ATTR(phys_index, 0444, show_mem_start_phys_index, NULL); +static SYSDEV_ATTR(end_phys_index, 0444, show_mem_end_phys_index, NULL); static SYSDEV_ATTR(state, 0644, show_mem_state, store_mem_state); static SYSDEV_ATTR(phys_device, 0444, show_phys_device, NULL); static SYSDEV_ATTR(removable, 0444, show_mem_removable, NULL); @@ -522,16 +538,20 @@ static int init_memory_block(struct memory_block **memory, return -ENOMEM; scn_nr = __section_nr(section); - mem->phys_index = base_memory_block_id(scn_nr) * sections_per_block; + mem->start_section_nr = + base_memory_block_id(scn_nr) * sections_per_block; + mem->end_section_nr = mem->start_section_nr + sections_per_block - 1; mem->state = state; mem->section_count++; mutex_init(&mem->state_mutex); - start_pfn = section_nr_to_pfn(mem->phys_index); + start_pfn = section_nr_to_pfn(mem->start_section_nr); mem->phys_device = arch_get_memory_phys_device(start_pfn); ret = register_memory(mem); if (!ret) ret = mem_create_simple_file(mem, phys_index); + if (!ret) + ret = mem_create_simple_file(mem, end_phys_index); if (!ret) ret = mem_create_simple_file(mem, state); if (!ret) @@ -575,11 +595,12 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, mutex_lock(&mem_sysfs_mutex); mem = find_memory_block(section); + unregister_mem_sect_under_nodes(mem, __section_nr(section)); mem->section_count--; if (mem->section_count == 0) { - unregister_mem_sect_under_nodes(mem); mem_remove_simple_file(mem, phys_index); + mem_remove_simple_file(mem, end_phys_index); mem_remove_simple_file(mem, state); mem_remove_simple_file(mem, phys_device); mem_remove_simple_file(mem, removable); diff --git a/drivers/base/node.c b/drivers/base/node.c index 36b43052001d..b3b72d64e805 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -375,8 +375,10 @@ int register_mem_sect_under_node(struct memory_block *mem_blk, int nid) return -EFAULT; if (!node_online(nid)) return 0; - sect_start_pfn = section_nr_to_pfn(mem_blk->phys_index); - sect_end_pfn = sect_start_pfn + PAGES_PER_SECTION - 1; + + sect_start_pfn = section_nr_to_pfn(mem_blk->start_section_nr); + sect_end_pfn = section_nr_to_pfn(mem_blk->end_section_nr); + sect_end_pfn += PAGES_PER_SECTION - 1; for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) { int page_nid; @@ -400,7 +402,8 @@ int register_mem_sect_under_node(struct memory_block *mem_blk, int nid) } /* unregister memory section under all nodes that it spans */ -int unregister_mem_sect_under_nodes(struct memory_block *mem_blk) +int unregister_mem_sect_under_nodes(struct memory_block *mem_blk, + unsigned long phys_index) { NODEMASK_ALLOC(nodemask_t, unlinked_nodes, GFP_KERNEL); unsigned long pfn, sect_start_pfn, sect_end_pfn; @@ -412,7 +415,8 @@ int unregister_mem_sect_under_nodes(struct memory_block *mem_blk) if (!unlinked_nodes) return -ENOMEM; nodes_clear(*unlinked_nodes); - sect_start_pfn = section_nr_to_pfn(mem_blk->phys_index); + + sect_start_pfn = section_nr_to_pfn(phys_index); sect_end_pfn = sect_start_pfn + PAGES_PER_SECTION - 1; for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) { int nid; diff --git a/include/linux/memory.h b/include/linux/memory.h index 06c1fa0a5c7b..e1e3b2b84f85 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h @@ -21,7 +21,8 @@ #include struct memory_block { - unsigned long phys_index; + unsigned long start_section_nr; + unsigned long end_section_nr; unsigned long state; int section_count; diff --git a/include/linux/node.h b/include/linux/node.h index 1466945cc9ef..92370e22343c 100644 --- a/include/linux/node.h +++ b/include/linux/node.h @@ -39,7 +39,8 @@ extern int register_cpu_under_node(unsigned int cpu, unsigned int nid); extern int unregister_cpu_under_node(unsigned int cpu, unsigned int nid); extern int register_mem_sect_under_node(struct memory_block *mem_blk, int nid); -extern int unregister_mem_sect_under_nodes(struct memory_block *mem_blk); +extern int unregister_mem_sect_under_nodes(struct memory_block *mem_blk, + unsigned long phys_index); #ifdef CONFIG_HUGETLBFS extern void register_hugetlbfs_with_node(node_registration_func_t doregister, @@ -67,7 +68,8 @@ static inline int register_mem_sect_under_node(struct memory_block *mem_blk, { return 0; } -static inline int unregister_mem_sect_under_nodes(struct memory_block *mem_blk) +static inline int unregister_mem_sect_under_nodes(struct memory_block *mem_blk, + unsigned long phys_index) { return 0; } -- cgit v1.2.3 From 25a64ec1e7d0cfe172832d06a31215d458dfea7f Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Thu, 3 Feb 2011 22:43:48 -0700 Subject: fix comment spelling becausse => because Signed-off-by: Pete Zaitcev Signed-off-by: Jiri Kosina --- include/linux/mmzone.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 39c24ebe9cfd..52f96d78c0e8 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -477,7 +477,7 @@ unsigned long zone_nr_free_pages(struct zone *zone); #ifdef CONFIG_NUMA /* - * The NUMA zonelists are doubled becausse we need zonelists that restrict the + * The NUMA zonelists are doubled because we need zonelists that restrict the * allocations to a single node for GFP_THISNODE. * * [0] : Zonelist with fallback -- cgit v1.2.3 From c8ebae37034c0ead62eb4df8ef88e999ddb8d5cf Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 11 Jan 2011 19:35:53 +0000 Subject: ARM: mmci: add dmaengine-based DMA support Based on a patch from Linus Walleij. Add dmaengine based support for DMA to the MMCI driver, using the Primecell DMA engine interface. The changes over Linus' driver are: - rename txsize_threshold to dmasize_threshold, as this reflects the purpose more. - use 'mmci_dma_' as the function prefix rather than 'dma_mmci_'. - clean up requesting of dma channels. - don't release a single channel twice when it's shared between tx and rx. - get rid of 'dma_enable' bool - instead check whether the channel is NULL. - detect incomplete DMA at the end of a transfer. Some DMA controllers (eg, PL08x) are unable to be configured for scatter DMA and also listen to all four DMA request signals [BREQ,SREQ,LBREQ,LSREQ] from the MMCI. They can do one or other but not both. As MMCI uses LBREQ/LSREQ for the final burst/words, PL08x does not transfer the last few words. - map and unmap DMA buffers using the DMA engine struct device, not the MMCI struct device - the DMA engine is doing the DMA transfer, not us. - avoid double-unmapping of the DMA buffers on MMCI data errors. - don't check for negative values from the dmaengine tx submission function - Dan says this must never fail. - use new dmaengine helper functions rather than using the ugly function pointers directly. - allow DMA code to be fully optimized away using dma_inprogress() which is defined to constant 0 if DMA engine support is disabled. - request maximum segment size from the DMA engine struct device and set this appropriately. - removed checking of buffer alignment - the DMA engine should deal with its own restrictions on buffer alignment, not the individual DMA engine users. - removed setting DMAREQCTL - this confuses some DMA controllers as it causes LBREQ to be asserted for the last seven transfers, rather than six SREQ and one LSREQ. - removed burst setting - the DMA controller should not burst past the transfer size required to complete the DMA operation. Tested-by: Linus Walleij Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 282 ++++++++++++++++++++++++++++++++++++++++++++-- drivers/mmc/host/mmci.h | 13 +++ include/linux/amba/mmci.h | 17 +++ 3 files changed, 304 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index db2a358143d6..8a29c9f4a812 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -2,7 +2,7 @@ * linux/drivers/mmc/host/mmci.c - ARM PrimeCell MMCI PL180/1 driver * * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. - * Copyright (C) 2010 ST-Ericsson AB. + * Copyright (C) 2010 ST-Ericsson SA * * 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 @@ -25,8 +25,10 @@ #include #include #include -#include #include +#include +#include +#include #include #include @@ -186,6 +188,248 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); } +/* + * All the DMA operation mode stuff goes inside this ifdef. + * This assumes that you have a generic DMA device interface, + * no custom DMA interfaces are supported. + */ +#ifdef CONFIG_DMA_ENGINE +static void __devinit mmci_dma_setup(struct mmci_host *host) +{ + struct mmci_platform_data *plat = host->plat; + const char *rxname, *txname; + dma_cap_mask_t mask; + + if (!plat || !plat->dma_filter) { + dev_info(mmc_dev(host->mmc), "no DMA platform data\n"); + return; + } + + /* Try to acquire a generic DMA engine slave channel */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + /* + * If only an RX channel is specified, the driver will + * attempt to use it bidirectionally, however if it is + * is specified but cannot be located, DMA will be disabled. + */ + if (plat->dma_rx_param) { + host->dma_rx_channel = dma_request_channel(mask, + plat->dma_filter, + plat->dma_rx_param); + /* E.g if no DMA hardware is present */ + if (!host->dma_rx_channel) + dev_err(mmc_dev(host->mmc), "no RX DMA channel\n"); + } + + if (plat->dma_tx_param) { + host->dma_tx_channel = dma_request_channel(mask, + plat->dma_filter, + plat->dma_tx_param); + if (!host->dma_tx_channel) + dev_warn(mmc_dev(host->mmc), "no TX DMA channel\n"); + } else { + host->dma_tx_channel = host->dma_rx_channel; + } + + if (host->dma_rx_channel) + rxname = dma_chan_name(host->dma_rx_channel); + else + rxname = "none"; + + if (host->dma_tx_channel) + txname = dma_chan_name(host->dma_tx_channel); + else + txname = "none"; + + dev_info(mmc_dev(host->mmc), "DMA channels RX %s, TX %s\n", + rxname, txname); + + /* + * Limit the maximum segment size in any SG entry according to + * the parameters of the DMA engine device. + */ + if (host->dma_tx_channel) { + struct device *dev = host->dma_tx_channel->device->dev; + unsigned int max_seg_size = dma_get_max_seg_size(dev); + + if (max_seg_size < host->mmc->max_seg_size) + host->mmc->max_seg_size = max_seg_size; + } + if (host->dma_rx_channel) { + struct device *dev = host->dma_rx_channel->device->dev; + unsigned int max_seg_size = dma_get_max_seg_size(dev); + + if (max_seg_size < host->mmc->max_seg_size) + host->mmc->max_seg_size = max_seg_size; + } +} + +/* + * This is used in __devinit or __devexit so inline it + * so it can be discarded. + */ +static inline void mmci_dma_release(struct mmci_host *host) +{ + struct mmci_platform_data *plat = host->plat; + + if (host->dma_rx_channel) + dma_release_channel(host->dma_rx_channel); + if (host->dma_tx_channel && plat->dma_tx_param) + dma_release_channel(host->dma_tx_channel); + host->dma_rx_channel = host->dma_tx_channel = NULL; +} + +static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data) +{ + struct dma_chan *chan = host->dma_current; + enum dma_data_direction dir; + u32 status; + int i; + + /* Wait up to 1ms for the DMA to complete */ + for (i = 0; ; i++) { + status = readl(host->base + MMCISTATUS); + if (!(status & MCI_RXDATAAVLBLMASK) || i >= 100) + break; + udelay(10); + } + + /* + * Check to see whether we still have some data left in the FIFO - + * this catches DMA controllers which are unable to monitor the + * DMALBREQ and DMALSREQ signals while allowing us to DMA to non- + * contiguous buffers. On TX, we'll get a FIFO underrun error. + */ + if (status & MCI_RXDATAAVLBLMASK) { + dmaengine_terminate_all(chan); + if (!data->error) + data->error = -EIO; + } + + if (data->flags & MMC_DATA_WRITE) { + dir = DMA_TO_DEVICE; + } else { + dir = DMA_FROM_DEVICE; + } + + dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir); + + /* + * Use of DMA with scatter-gather is impossible. + * Give up with DMA and switch back to PIO mode. + */ + if (status & MCI_RXDATAAVLBLMASK) { + dev_err(mmc_dev(host->mmc), "buggy DMA detected. Taking evasive action.\n"); + mmci_dma_release(host); + } +} + +static void mmci_dma_data_error(struct mmci_host *host) +{ + dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n"); + dmaengine_terminate_all(host->dma_current); +} + +static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) +{ + struct variant_data *variant = host->variant; + struct dma_slave_config conf = { + .src_addr = host->phybase + MMCIFIFO, + .dst_addr = host->phybase + MMCIFIFO, + .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .src_maxburst = variant->fifohalfsize >> 2, /* # of words */ + .dst_maxburst = variant->fifohalfsize >> 2, /* # of words */ + }; + struct mmc_data *data = host->data; + struct dma_chan *chan; + struct dma_device *device; + struct dma_async_tx_descriptor *desc; + int nr_sg; + + host->dma_current = NULL; + + if (data->flags & MMC_DATA_READ) { + conf.direction = DMA_FROM_DEVICE; + chan = host->dma_rx_channel; + } else { + conf.direction = DMA_TO_DEVICE; + chan = host->dma_tx_channel; + } + + /* If there's no DMA channel, fall back to PIO */ + if (!chan) + return -EINVAL; + + /* If less than or equal to the fifo size, don't bother with DMA */ + if (host->size <= variant->fifosize) + return -EINVAL; + + device = chan->device; + nr_sg = dma_map_sg(device->dev, data->sg, data->sg_len, conf.direction); + if (nr_sg == 0) + return -EINVAL; + + dmaengine_slave_config(chan, &conf); + desc = device->device_prep_slave_sg(chan, data->sg, nr_sg, + conf.direction, DMA_CTRL_ACK); + if (!desc) + goto unmap_exit; + + /* Okay, go for it. */ + host->dma_current = chan; + + dev_vdbg(mmc_dev(host->mmc), + "Submit MMCI DMA job, sglen %d blksz %04x blks %04x flags %08x\n", + data->sg_len, data->blksz, data->blocks, data->flags); + dmaengine_submit(desc); + dma_async_issue_pending(chan); + + datactrl |= MCI_DPSM_DMAENABLE; + + /* Trigger the DMA transfer */ + writel(datactrl, host->base + MMCIDATACTRL); + + /* + * Let the MMCI say when the data is ended and it's time + * to fire next DMA request. When that happens, MMCI will + * call mmci_data_end() + */ + writel(readl(host->base + MMCIMASK0) | MCI_DATAENDMASK, + host->base + MMCIMASK0); + return 0; + +unmap_exit: + dmaengine_terminate_all(chan); + dma_unmap_sg(device->dev, data->sg, data->sg_len, conf.direction); + return -ENOMEM; +} +#else +/* Blank functions if the DMA engine is not available */ +static inline void mmci_dma_setup(struct mmci_host *host) +{ +} + +static inline void mmci_dma_release(struct mmci_host *host) +{ +} + +static inline void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data) +{ +} + +static inline void mmci_dma_data_error(struct mmci_host *host) +{ +} + +static inline int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) +{ + return -ENOSYS; +} +#endif + static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) { struct variant_data *variant = host->variant; @@ -201,8 +445,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) host->size = data->blksz * data->blocks; data->bytes_xfered = 0; - mmci_init_sg(host, data); - clks = (unsigned long long)data->timeout_ns * host->cclk; do_div(clks, 1000000000UL); @@ -216,8 +458,21 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) BUG_ON(1 << blksz_bits != data->blksz); datactrl = MCI_DPSM_ENABLE | blksz_bits << 4; - if (data->flags & MMC_DATA_READ) { + + if (data->flags & MMC_DATA_READ) datactrl |= MCI_DPSM_DIRECTION; + + /* + * Attempt to use DMA operation mode, if this + * should fail, fall back to PIO mode + */ + if (!mmci_dma_start_data(host, datactrl)) + return; + + /* IRQ mode, map the SG list for CPU reading/writing */ + mmci_init_sg(host, data); + + if (data->flags & MMC_DATA_READ) { irqmask = MCI_RXFIFOHALFFULLMASK; /* @@ -281,6 +536,10 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { u32 remain, success; + /* Terminate the DMA transfer */ + if (dma_inprogress(host)) + mmci_dma_data_error(host); + /* * Calculate how far we are into the transfer. Note that * the data counter gives the number of bytes transferred @@ -315,6 +574,8 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, dev_err(mmc_dev(host->mmc), "stray MCI_DATABLOCKEND interrupt\n"); if (status & MCI_DATAEND || data->error) { + if (dma_inprogress(host)) + mmci_dma_unmap(host, data); mmci_stop_data(host); if (!data->error) @@ -767,6 +1028,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) dev_dbg(mmc_dev(mmc), "eventual mclk rate: %u Hz\n", host->mclk); } + host->phybase = dev->res.start; host->base = ioremap(dev->res.start, resource_size(&dev->res)); if (!host->base) { ret = -ENOMEM; @@ -894,9 +1156,12 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) amba_set_drvdata(dev, mmc); - dev_info(&dev->dev, "%s: PL%03x rev%u at 0x%08llx irq %d,%d\n", - mmc_hostname(mmc), amba_part(dev), amba_rev(dev), - (unsigned long long)dev->res.start, dev->irq[0], dev->irq[1]); + dev_info(&dev->dev, "%s: PL%03x manf %x rev%u at 0x%08llx irq %d,%d (pio)\n", + mmc_hostname(mmc), amba_part(dev), amba_manf(dev), + amba_rev(dev), (unsigned long long)dev->res.start, + dev->irq[0], dev->irq[1]); + + mmci_dma_setup(host); mmc_add_host(mmc); @@ -943,6 +1208,7 @@ static int __devexit mmci_remove(struct amba_device *dev) writel(0, host->base + MMCICOMMAND); writel(0, host->base + MMCIDATACTRL); + mmci_dma_release(host); free_irq(dev->irq[0], host); if (!host->singleirq) free_irq(dev->irq[1], host); diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 164ce060fc1f..ec9a7bc6d0df 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -148,8 +148,10 @@ struct clk; struct variant_data; +struct dma_chan; struct mmci_host { + phys_addr_t phybase; void __iomem *base; struct mmc_request *mrq; struct mmc_command *cmd; @@ -179,5 +181,16 @@ struct mmci_host { struct sg_mapping_iter sg_miter; unsigned int size; struct regulator *vcc; + +#ifdef CONFIG_DMA_ENGINE + /* DMA stuff */ + struct dma_chan *dma_current; + struct dma_chan *dma_rx_channel; + struct dma_chan *dma_tx_channel; + +#define dma_inprogress(host) ((host)->dma_current) +#else +#define dma_inprogress(host) (0) +#endif }; diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h index f4ee9acc9721..f60227088b7b 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -6,6 +6,9 @@ #include +/* Just some dummy forwarding */ +struct dma_chan; + /** * struct mmci_platform_data - platform configuration for the MMCI * (also known as PL180) block. @@ -27,6 +30,17 @@ * @cd_invert: true if the gpio_cd pin value is active low * @capabilities: the capabilities of the block as implemented in * this platform, signify anything MMC_CAP_* from mmc/host.h + * @dma_filter: function used to select an apropriate RX and TX + * DMA channel to be used for DMA, if and only if you're deploying the + * generic DMA engine + * @dma_rx_param: parameter passed to the DMA allocation + * filter in order to select an apropriate RX channel. If + * there is a bidirectional RX+TX channel, then just specify + * this and leave dma_tx_param set to NULL + * @dma_tx_param: parameter passed to the DMA allocation + * filter in order to select an apropriate TX channel. If this + * is NULL the driver will attempt to use the RX channel as a + * bidirectional channel */ struct mmci_platform_data { unsigned int f_max; @@ -38,6 +52,9 @@ struct mmci_platform_data { int gpio_cd; bool cd_invert; unsigned long capabilities; + bool (*dma_filter)(struct dma_chan *chan, void *filter_param); + void *dma_rx_param; + void *dma_tx_param; }; #endif -- cgit v1.2.3 From 04bea68b2f0eeebb089ecc67b618795925268b4a Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 24 Jan 2011 09:58:55 +0530 Subject: of/pci: move of_irq_map_pci() into generic code There is a tiny difference between PPC32 and PPC64. Microblaze uses the PPC32 variant. Signed-off-by: Sebastian Andrzej Siewior [grant.likely@secretlab.ca: Added comment to #endif, moved documentation block to function implementation, fixed for non ppc and microblaze compiles] Signed-off-by: Grant Likely --- arch/microblaze/include/asm/pci-bridge.h | 12 +++++ arch/microblaze/include/asm/prom.h | 15 ------ arch/microblaze/kernel/prom_parse.c | 77 --------------------------- arch/microblaze/pci/pci-common.c | 1 + arch/powerpc/include/asm/pci-bridge.h | 10 ++++ arch/powerpc/include/asm/prom.h | 15 ------ arch/powerpc/kernel/pci-common.c | 1 + arch/powerpc/kernel/prom_parse.c | 84 ----------------------------- drivers/of/Kconfig | 6 +++ drivers/of/Makefile | 1 + drivers/of/of_pci.c | 91 ++++++++++++++++++++++++++++++++ include/linux/of_pci.h | 9 ++++ 12 files changed, 131 insertions(+), 191 deletions(-) create mode 100644 drivers/of/of_pci.c create mode 100644 include/linux/of_pci.h (limited to 'include') diff --git a/arch/microblaze/include/asm/pci-bridge.h b/arch/microblaze/include/asm/pci-bridge.h index 0c68764ab547..10717669e0c2 100644 --- a/arch/microblaze/include/asm/pci-bridge.h +++ b/arch/microblaze/include/asm/pci-bridge.h @@ -104,11 +104,22 @@ struct pci_controller { int global_number; /* PCI domain number */ }; +#ifdef CONFIG_PCI static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus) { return bus->sysdata; } +static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus) +{ + struct pci_controller *host; + + if (bus->self) + return pci_device_to_OF_node(bus->self); + host = pci_bus_to_host(bus); + return host ? host->dn : NULL; +} + static inline int isa_vaddr_is_ioport(void __iomem *address) { /* No specific ISA handling on ppc32 at this stage, it @@ -116,6 +127,7 @@ static inline int isa_vaddr_is_ioport(void __iomem *address) */ return 0; } +#endif /* CONFIG_PCI */ /* These are used for config access before all the PCI probing has been done. */ diff --git a/arch/microblaze/include/asm/prom.h b/arch/microblaze/include/asm/prom.h index 2e72af078b05..d0890d36ef61 100644 --- a/arch/microblaze/include/asm/prom.h +++ b/arch/microblaze/include/asm/prom.h @@ -64,21 +64,6 @@ extern void kdump_move_device_tree(void); /* CPU OF node matching */ struct device_node *of_get_cpu_node(int cpu, unsigned int *thread); -/** - * of_irq_map_pci - Resolve the interrupt for a PCI device - * @pdev: the device whose interrupt is to be resolved - * @out_irq: structure of_irq filled by this function - * - * This function resolves the PCI interrupt for a given PCI device. If a - * device-node exists for a given pci_dev, it will use normal OF tree - * walking. If not, it will implement standard swizzling and walk up the - * PCI tree until an device-node is found, at which point it will finish - * resolving using the OF tree walking. - */ -struct pci_dev; -struct of_irq; -extern int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq); - #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/arch/microblaze/kernel/prom_parse.c b/arch/microblaze/kernel/prom_parse.c index 9ae24f4b882b..47187cc2cf00 100644 --- a/arch/microblaze/kernel/prom_parse.c +++ b/arch/microblaze/kernel/prom_parse.c @@ -2,88 +2,11 @@ #include #include -#include #include #include #include #include #include -#include - -#ifdef CONFIG_PCI -int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq) -{ - struct device_node *dn, *ppnode; - struct pci_dev *ppdev; - u32 lspec; - u32 laddr[3]; - u8 pin; - int rc; - - /* Check if we have a device node, if yes, fallback to standard OF - * parsing - */ - dn = pci_device_to_OF_node(pdev); - if (dn) - return of_irq_map_one(dn, 0, out_irq); - - /* Ok, we don't, time to have fun. Let's start by building up an - * interrupt spec. we assume #interrupt-cells is 1, which is standard - * for PCI. If you do different, then don't use that routine. - */ - rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin); - if (rc != 0) - return rc; - /* No pin, exit */ - if (pin == 0) - return -ENODEV; - - /* Now we walk up the PCI tree */ - lspec = pin; - for (;;) { - /* Get the pci_dev of our parent */ - ppdev = pdev->bus->self; - - /* Ouch, it's a host bridge... */ - if (ppdev == NULL) { - struct pci_controller *host; - host = pci_bus_to_host(pdev->bus); - ppnode = host ? host->dn : NULL; - /* No node for host bridge ? give up */ - if (ppnode == NULL) - return -EINVAL; - } else - /* We found a P2P bridge, check if it has a node */ - ppnode = pci_device_to_OF_node(ppdev); - - /* Ok, we have found a parent with a device-node, hand over to - * the OF parsing code. - * We build a unit address from the linux device to be used for - * resolution. Note that we use the linux bus number which may - * not match your firmware bus numbering. - * Fortunately, in most cases, interrupt-map-mask doesn't - * include the bus number as part of the matching. - * You should still be careful about that though if you intend - * to rely on this function (you ship a firmware that doesn't - * create device nodes for all PCI devices). - */ - if (ppnode) - break; - - /* We can only get here if we hit a P2P bridge with no node, - * let's do standard swizzling and try again - */ - lspec = pci_swizzle_interrupt_pin(pdev, lspec); - pdev = ppdev; - } - - laddr[0] = (pdev->bus->number << 16) - | (pdev->devfn << 8); - laddr[1] = laddr[2] = 0; - return of_irq_map_raw(ppnode, &lspec, 1, laddr, out_irq); -} -EXPORT_SYMBOL_GPL(of_irq_map_pci); -#endif /* CONFIG_PCI */ void of_parse_dma_window(struct device_node *dn, const void *dma_window_prop, unsigned long *busno, unsigned long *phys, unsigned long *size) diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index e363615d6798..1e01a1253631 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 51e9e6f90d12..edeb80fdd2c3 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -171,6 +171,16 @@ static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus) return bus->sysdata; } +static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus) +{ + struct pci_controller *host; + + if (bus->self) + return pci_device_to_OF_node(bus->self); + host = pci_bus_to_host(bus); + return host ? host->dn : NULL; +} + static inline int isa_vaddr_is_ioport(void __iomem *address) { /* No specific ISA handling on ppc32 at this stage, it diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h index d72757585595..c189aa5fe1f4 100644 --- a/arch/powerpc/include/asm/prom.h +++ b/arch/powerpc/include/asm/prom.h @@ -70,21 +70,6 @@ static inline int of_node_to_nid(struct device_node *device) { return 0; } #endif #define of_node_to_nid of_node_to_nid -/** - * of_irq_map_pci - Resolve the interrupt for a PCI device - * @pdev: the device whose interrupt is to be resolved - * @out_irq: structure of_irq filled by this function - * - * This function resolves the PCI interrupt for a given PCI device. If a - * device-node exists for a given pci_dev, it will use normal OF tree - * walking. If not, it will implement standard swizzling and walk up the - * PCI tree until an device-node is found, at which point it will finish - * resolving using the OF tree walking. - */ -struct pci_dev; -struct of_irq; -extern int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq); - extern void of_instantiate_rtc(void); /* These includes are put at the bottom because they may contain things diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 10a44e68ef11..eb341be9a4d9 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/powerpc/kernel/prom_parse.c b/arch/powerpc/kernel/prom_parse.c index c2b7a07cc3d3..47187cc2cf00 100644 --- a/arch/powerpc/kernel/prom_parse.c +++ b/arch/powerpc/kernel/prom_parse.c @@ -2,95 +2,11 @@ #include #include -#include #include #include #include #include #include -#include - -#ifdef CONFIG_PCI -int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq) -{ - struct device_node *dn, *ppnode; - struct pci_dev *ppdev; - u32 lspec; - u32 laddr[3]; - u8 pin; - int rc; - - /* Check if we have a device node, if yes, fallback to standard OF - * parsing - */ - dn = pci_device_to_OF_node(pdev); - if (dn) { - rc = of_irq_map_one(dn, 0, out_irq); - if (!rc) - return rc; - } - - /* Ok, we don't, time to have fun. Let's start by building up an - * interrupt spec. we assume #interrupt-cells is 1, which is standard - * for PCI. If you do different, then don't use that routine. - */ - rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin); - if (rc != 0) - return rc; - /* No pin, exit */ - if (pin == 0) - return -ENODEV; - - /* Now we walk up the PCI tree */ - lspec = pin; - for (;;) { - /* Get the pci_dev of our parent */ - ppdev = pdev->bus->self; - - /* Ouch, it's a host bridge... */ - if (ppdev == NULL) { -#ifdef CONFIG_PPC64 - ppnode = pci_bus_to_OF_node(pdev->bus); -#else - struct pci_controller *host; - host = pci_bus_to_host(pdev->bus); - ppnode = host ? host->dn : NULL; -#endif - /* No node for host bridge ? give up */ - if (ppnode == NULL) - return -EINVAL; - } else - /* We found a P2P bridge, check if it has a node */ - ppnode = pci_device_to_OF_node(ppdev); - - /* Ok, we have found a parent with a device-node, hand over to - * the OF parsing code. - * We build a unit address from the linux device to be used for - * resolution. Note that we use the linux bus number which may - * not match your firmware bus numbering. - * Fortunately, in most cases, interrupt-map-mask doesn't include - * the bus number as part of the matching. - * You should still be careful about that though if you intend - * to rely on this function (you ship a firmware that doesn't - * create device nodes for all PCI devices). - */ - if (ppnode) - break; - - /* We can only get here if we hit a P2P bridge with no node, - * let's do standard swizzling and try again - */ - lspec = pci_swizzle_interrupt_pin(pdev, lspec); - pdev = ppdev; - } - - laddr[0] = (pdev->bus->number << 16) - | (pdev->devfn << 8); - laddr[1] = laddr[2] = 0; - return of_irq_map_raw(ppnode, &lspec, 1, laddr, out_irq); -} -EXPORT_SYMBOL_GPL(of_irq_map_pci); -#endif /* CONFIG_PCI */ void of_parse_dma_window(struct device_node *dn, const void *dma_window_prop, unsigned long *busno, unsigned long *phys, unsigned long *size) diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 3c6e100a3ad0..efabbf9dd607 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -69,4 +69,10 @@ config OF_MDIO help OpenFirmware MDIO bus (Ethernet PHY) accessors +config OF_PCI + def_tristate PCI + depends on PCI && (PPC || MICROBLAZE) + help + OpenFirmware PCI bus accessors + endmenu # OF diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 3ab21a0a4907..f7861ed2f287 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_OF_I2C) += of_i2c.o obj-$(CONFIG_OF_NET) += of_net.o obj-$(CONFIG_OF_SPI) += of_spi.o obj-$(CONFIG_OF_MDIO) += of_mdio.o +obj-$(CONFIG_OF_PCI) += of_pci.o diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c new file mode 100644 index 000000000000..314535fa32c1 --- /dev/null +++ b/drivers/of/of_pci.c @@ -0,0 +1,91 @@ +#include +#include +#include + +/** + * of_irq_map_pci - Resolve the interrupt for a PCI device + * @pdev: the device whose interrupt is to be resolved + * @out_irq: structure of_irq filled by this function + * + * This function resolves the PCI interrupt for a given PCI device. If a + * device-node exists for a given pci_dev, it will use normal OF tree + * walking. If not, it will implement standard swizzling and walk up the + * PCI tree until an device-node is found, at which point it will finish + * resolving using the OF tree walking. + */ +int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq) +{ + struct device_node *dn, *ppnode; + struct pci_dev *ppdev; + u32 lspec; + __be32 lspec_be; + __be32 laddr[3]; + u8 pin; + int rc; + + /* Check if we have a device node, if yes, fallback to standard + * device tree parsing + */ + dn = pci_device_to_OF_node(pdev); + if (dn) { + rc = of_irq_map_one(dn, 0, out_irq); + if (!rc) + return rc; + } + + /* Ok, we don't, time to have fun. Let's start by building up an + * interrupt spec. we assume #interrupt-cells is 1, which is standard + * for PCI. If you do different, then don't use that routine. + */ + rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin); + if (rc != 0) + return rc; + /* No pin, exit */ + if (pin == 0) + return -ENODEV; + + /* Now we walk up the PCI tree */ + lspec = pin; + for (;;) { + /* Get the pci_dev of our parent */ + ppdev = pdev->bus->self; + + /* Ouch, it's a host bridge... */ + if (ppdev == NULL) { + ppnode = pci_bus_to_OF_node(pdev->bus); + + /* No node for host bridge ? give up */ + if (ppnode == NULL) + return -EINVAL; + } else { + /* We found a P2P bridge, check if it has a node */ + ppnode = pci_device_to_OF_node(ppdev); + } + + /* Ok, we have found a parent with a device-node, hand over to + * the OF parsing code. + * We build a unit address from the linux device to be used for + * resolution. Note that we use the linux bus number which may + * not match your firmware bus numbering. + * Fortunately, in most cases, interrupt-map-mask doesn't + * include the bus number as part of the matching. + * You should still be careful about that though if you intend + * to rely on this function (you ship a firmware that doesn't + * create device nodes for all PCI devices). + */ + if (ppnode) + break; + + /* We can only get here if we hit a P2P bridge with no node, + * let's do standard swizzling and try again + */ + lspec = pci_swizzle_interrupt_pin(pdev, lspec); + pdev = ppdev; + } + + lspec_be = cpu_to_be32(lspec); + laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8)); + laddr[1] = laddr[2] = cpu_to_be32(0); + return of_irq_map_raw(ppnode, &lspec_be, 1, laddr, out_irq); +} +EXPORT_SYMBOL_GPL(of_irq_map_pci); diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h new file mode 100644 index 000000000000..85a27b650d76 --- /dev/null +++ b/include/linux/of_pci.h @@ -0,0 +1,9 @@ +#ifndef __OF_PCI_H +#define __OF_PCI_H + +#include + +struct pci_dev; +struct of_irq; +int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq); +#endif -- cgit v1.2.3 From c8cf203a1d228fa001b95534f639ffb7a23d5386 Mon Sep 17 00:00:00 2001 From: Robert Morell Date: Wed, 26 Jan 2011 19:06:47 -0800 Subject: USB: HCD: Add usb_hcd prefix to exported functions The convention is to prefix symbols exported from the USB HCD core with "usb_hcd". This change makes unmap_urb_setup_for_dma() and unmap_urb_for_dma() consistent with that. Signed-off-by: Robert Morell Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 16 ++++++++-------- drivers/usb/host/imx21-hcd.c | 5 +++-- drivers/usb/musb/musb_host.c | 4 ++-- include/linux/usb/hcd.h | 4 ++-- 4 files changed, 15 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 6a95017fa62b..335c1ddb260d 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1262,7 +1262,7 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle, *dma_handle = 0; } -void unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb) +void usb_hcd_unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb) { if (urb->transfer_flags & URB_SETUP_MAP_SINGLE) dma_unmap_single(hcd->self.controller, @@ -1279,13 +1279,13 @@ void unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb) /* Make it safe to call this routine more than once */ urb->transfer_flags &= ~(URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL); } -EXPORT_SYMBOL_GPL(unmap_urb_setup_for_dma); +EXPORT_SYMBOL_GPL(usb_hcd_unmap_urb_setup_for_dma); -void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) { enum dma_data_direction dir; - unmap_urb_setup_for_dma(hcd, urb); + usb_hcd_unmap_urb_setup_for_dma(hcd, urb); dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; if (urb->transfer_flags & URB_DMA_MAP_SG) @@ -1314,7 +1314,7 @@ void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) urb->transfer_flags &= ~(URB_DMA_MAP_SG | URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE | URB_MAP_LOCAL); } -EXPORT_SYMBOL_GPL(unmap_urb_for_dma); +EXPORT_SYMBOL_GPL(usb_hcd_unmap_urb_for_dma); static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) @@ -1410,7 +1410,7 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, } if (ret && (urb->transfer_flags & (URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL))) - unmap_urb_for_dma(hcd, urb); + usb_hcd_unmap_urb_for_dma(hcd, urb); } return ret; } @@ -1451,7 +1451,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) if (likely(status == 0)) { status = hcd->driver->urb_enqueue(hcd, urb, mem_flags); if (unlikely(status)) - unmap_urb_for_dma(hcd, urb); + usb_hcd_unmap_urb_for_dma(hcd, urb); } } @@ -1557,7 +1557,7 @@ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status) !status)) status = -EREMOTEIO; - unmap_urb_for_dma(hcd, urb); + usb_hcd_unmap_urb_for_dma(hcd, urb); usbmon_urb_complete(&hcd->self, urb, status); usb_unanchor_urb(urb); diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index f90d003f2302..b7dfda8a1d51 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -927,7 +927,8 @@ static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb) if (state == US_CTRL_SETUP) { dir = TD_DIR_SETUP; if (unsuitable_for_dma(urb->setup_dma)) - unmap_urb_setup_for_dma(imx21->hcd, urb); + usb_hcd_unmap_urb_setup_for_dma(imx21->hcd, + urb); etd->dma_handle = urb->setup_dma; etd->cpu_buffer = urb->setup_packet; bufround = 0; @@ -943,7 +944,7 @@ static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb) dir = usb_pipeout(pipe) ? TD_DIR_OUT : TD_DIR_IN; bufround = (dir == TD_DIR_IN) ? 1 : 0; if (unsuitable_for_dma(urb->transfer_dma)) - unmap_urb_for_dma(imx21->hcd, urb); + usb_hcd_unmap_urb_for_dma(imx21->hcd, urb); etd->dma_handle = urb->transfer_dma; etd->cpu_buffer = urb->transfer_buffer; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 4d5bcb4e14d2..4d2f3f79c425 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1336,7 +1336,7 @@ void musb_host_tx(struct musb *musb, u8 epnum) if (length > qh->maxpacket) length = qh->maxpacket; /* Unmap the buffer so that CPU can use it */ - unmap_urb_for_dma(musb_to_hcd(musb), urb); + usb_hcd_unmap_urb_for_dma(musb_to_hcd(musb), urb); musb_write_fifo(hw_ep, length, urb->transfer_buffer + offset); qh->segsize = length; @@ -1758,7 +1758,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) if (!dma) { /* Unmap the buffer so that CPU can use it */ - unmap_urb_for_dma(musb_to_hcd(musb), urb); + usb_hcd_unmap_urb_for_dma(musb_to_hcd(musb), urb); done = musb_host_packet_rx(musb, urb, epnum, iso_err); DBG(6, "read %spacket\n", done ? "last " : ""); diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index dd6ee49a0844..395704bdf5cc 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -329,8 +329,8 @@ extern int usb_hcd_submit_urb(struct urb *urb, gfp_t mem_flags); extern int usb_hcd_unlink_urb(struct urb *urb, int status); extern void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status); -extern void unmap_urb_setup_for_dma(struct usb_hcd *, struct urb *); -extern void unmap_urb_for_dma(struct usb_hcd *, struct urb *); +extern void usb_hcd_unmap_urb_setup_for_dma(struct usb_hcd *, struct urb *); +extern void usb_hcd_unmap_urb_for_dma(struct usb_hcd *, struct urb *); extern void usb_hcd_flush_endpoint(struct usb_device *udev, struct usb_host_endpoint *ep); extern void usb_hcd_disable_endpoint(struct usb_device *udev, -- cgit v1.2.3 From 2694a48d9007a8bdf1731c1b97d4942c9cc49296 Mon Sep 17 00:00:00 2001 From: Robert Morell Date: Wed, 26 Jan 2011 19:06:48 -0800 Subject: USB: HCD: Add driver hooks for (un)?map_urb_for_dma Provide optional hooks for the host controller driver to override the default DMA mapping and unmapping routines. In general, these shouldn't be necessary unless the host controller has special DMA requirements, such as alignment contraints. If these are not specified, the general usb_hcd_(un)?map_urb_for_dma functions will be used instead. Also, pass the status to unmap_urb_for_dma so it can know whether the DMA buffer has been overwritten. Finally, add a flag to be used by these implementations if they allocated a temporary buffer so it can be freed properly when unmapping. Signed-off-by: Robert Morell Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 22 ++++++++++++++++++++-- include/linux/usb.h | 1 + include/linux/usb/hcd.h | 15 +++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 335c1ddb260d..d0b782c4523a 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1281,6 +1281,14 @@ void usb_hcd_unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb) } EXPORT_SYMBOL_GPL(usb_hcd_unmap_urb_setup_for_dma); +static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +{ + if (hcd->driver->unmap_urb_for_dma) + hcd->driver->unmap_urb_for_dma(hcd, urb); + else + usb_hcd_unmap_urb_for_dma(hcd, urb); +} + void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) { enum dma_data_direction dir; @@ -1318,6 +1326,15 @@ EXPORT_SYMBOL_GPL(usb_hcd_unmap_urb_for_dma); static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) +{ + if (hcd->driver->map_urb_for_dma) + return hcd->driver->map_urb_for_dma(hcd, urb, mem_flags); + else + return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); +} + +int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) { enum dma_data_direction dir; int ret = 0; @@ -1414,6 +1431,7 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, } return ret; } +EXPORT_SYMBOL_GPL(usb_hcd_map_urb_for_dma); /*-------------------------------------------------------------------------*/ @@ -1451,7 +1469,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) if (likely(status == 0)) { status = hcd->driver->urb_enqueue(hcd, urb, mem_flags); if (unlikely(status)) - usb_hcd_unmap_urb_for_dma(hcd, urb); + unmap_urb_for_dma(hcd, urb); } } @@ -1557,7 +1575,7 @@ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status) !status)) status = -EREMOTEIO; - usb_hcd_unmap_urb_for_dma(hcd, urb); + unmap_urb_for_dma(hcd, urb); usbmon_urb_complete(&hcd->self, urb, status); usb_unanchor_urb(urb); diff --git a/include/linux/usb.h b/include/linux/usb.h index bd69b65f3356..e63efeb378e3 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -976,6 +976,7 @@ extern int usb_disabled(void); #define URB_SETUP_MAP_SINGLE 0x00100000 /* Setup packet DMA mapped */ #define URB_SETUP_MAP_LOCAL 0x00200000 /* HCD-local setup packet */ #define URB_DMA_SG_COMBINED 0x00400000 /* S-G entries were combined */ +#define URB_ALIGNED_TEMP_BUFFER 0x00800000 /* Temp buffer was alloc'd */ struct usb_iso_packet_descriptor { unsigned int offset; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 395704bdf5cc..92b96fe39307 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -233,6 +233,19 @@ struct hc_driver { int (*urb_dequeue)(struct usb_hcd *hcd, struct urb *urb, int status); + /* + * (optional) these hooks allow an HCD to override the default DMA + * mapping and unmapping routines. In general, they shouldn't be + * necessary unless the host controller has special DMA requirements, + * such as alignment contraints. If these are not specified, the + * general usb_hcd_(un)?map_urb_for_dma functions will be used instead + * (and it may be a good idea to call these functions in your HCD + * implementation) + */ + int (*map_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags); + void (*unmap_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb); + /* hw synch, freeing endpoint resources that urb_dequeue can't */ void (*endpoint_disable)(struct usb_hcd *hcd, struct usb_host_endpoint *ep); @@ -329,6 +342,8 @@ extern int usb_hcd_submit_urb(struct urb *urb, gfp_t mem_flags); extern int usb_hcd_unlink_urb(struct urb *urb, int status); extern void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status); +extern int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags); extern void usb_hcd_unmap_urb_setup_for_dma(struct usb_hcd *, struct urb *); extern void usb_hcd_unmap_urb_for_dma(struct usb_hcd *, struct urb *); extern void usb_hcd_flush_endpoint(struct usb_device *udev, -- cgit v1.2.3 From 9a1b2e64020d41c577881952734fecd114af75f1 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 28 Jan 2011 10:23:57 +0100 Subject: USB: gadget: export functionfs.h to the includes available for userspace To compile functionfs userspace driver one needs definitions from include/linux/usb/functionfs.h. This patch add this file to the list of includes exported to userspace. Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/Kbuild | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/usb/Kbuild b/include/linux/usb/Kbuild index 51410e0200cf..ed91fb62674b 100644 --- a/include/linux/usb/Kbuild +++ b/include/linux/usb/Kbuild @@ -2,6 +2,7 @@ header-y += audio.h header-y += cdc.h header-y += ch9.h header-y += ch11.h +header-y += functionfs.h header-y += gadgetfs.h header-y += midi.h header-y += g_printer.h -- cgit v1.2.3 From 5c88b02196a99332dacf305c8757674dd7a303ff Mon Sep 17 00:00:00 2001 From: Pavan Savoy Date: Fri, 4 Feb 2011 02:23:09 -0600 Subject: drivers:misc: ti-st: register with channel IDs The architecture of shared transport had begun with individual protocols like bluetooth, fm and gps telling the shared transport what sort of protocol they are and then expecting the ST driver to parse the incoming data from chip and forward data only relevant to the protocol drivers. This change would mean each protocol drivers would also send information to ST driver as to how to intrepret their protocol data coming out of the chip. Signed-off-by: Pavan Savoy Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ti-st/st_core.c | 355 ++++++++++++++----------------------------- drivers/misc/ti-st/st_kim.c | 56 ++++--- include/linux/ti_wilink_st.h | 40 +++-- 3 files changed, 167 insertions(+), 284 deletions(-) (limited to 'include') diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index f9aad06d1ae5..84d73c5cb74d 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -25,10 +25,9 @@ #include #include -/* understand BT, FM and GPS for now */ -#include -#include -#include +#include +#include + #include /* function pointer pointing to either, @@ -38,21 +37,20 @@ void (*st_recv) (void*, const unsigned char*, long); /********************************************************************/ -#if 0 -/* internal misc functions */ -bool is_protocol_list_empty(void) +static void add_channel_to_table(struct st_data_s *st_gdata, + struct st_proto_s *new_proto) { - unsigned char i = 0; - pr_debug(" %s ", __func__); - for (i = 0; i < ST_MAX; i++) { - if (st_gdata->list[i] != NULL) - return ST_NOTEMPTY; - /* not empty */ - } - /* list empty */ - return ST_EMPTY; + pr_info("%s: id %d\n", __func__, new_proto->chnl_id); + /* list now has the channel id as index itself */ + st_gdata->list[new_proto->chnl_id] = new_proto; +} + +static void remove_channel_from_table(struct st_data_s *st_gdata, + struct st_proto_s *proto) +{ + pr_info("%s: id %d\n", __func__, proto->chnl_id); + st_gdata->list[proto->chnl_id] = NULL; } -#endif /* can be called in from * -- KIM (during fw download) @@ -82,15 +80,15 @@ int st_int_write(struct st_data_s *st_gdata, * push the skb received to relevant * protocol stacks */ -void st_send_frame(enum proto_type protoid, struct st_data_s *st_gdata) +void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata) { - pr_info(" %s(prot:%d) ", __func__, protoid); + pr_info(" %s(prot:%d) ", __func__, chnl_id); if (unlikely (st_gdata == NULL || st_gdata->rx_skb == NULL - || st_gdata->list[protoid] == NULL)) { - pr_err("protocol %d not registered, no data to send?", - protoid); + || st_gdata->list[chnl_id] == NULL)) { + pr_err("chnl_id %d not registered, no data to send?", + chnl_id); kfree_skb(st_gdata->rx_skb); return; } @@ -99,17 +97,17 @@ void st_send_frame(enum proto_type protoid, struct st_data_s *st_gdata) * - should be just skb_queue_tail for the * protocol stack driver */ - if (likely(st_gdata->list[protoid]->recv != NULL)) { + if (likely(st_gdata->list[chnl_id]->recv != NULL)) { if (unlikely - (st_gdata->list[protoid]->recv - (st_gdata->list[protoid]->priv_data, st_gdata->rx_skb) + (st_gdata->list[chnl_id]->recv + (st_gdata->list[chnl_id]->priv_data, st_gdata->rx_skb) != 0)) { - pr_err(" proto stack %d's ->recv failed", protoid); + pr_err(" proto stack %d's ->recv failed", chnl_id); kfree_skb(st_gdata->rx_skb); return; } } else { - pr_err(" proto stack %d's ->recv null", protoid); + pr_err(" proto stack %d's ->recv null", chnl_id); kfree_skb(st_gdata->rx_skb); } return; @@ -124,7 +122,7 @@ void st_reg_complete(struct st_data_s *st_gdata, char err) { unsigned char i = 0; pr_info(" %s ", __func__); - for (i = 0; i < ST_MAX; i++) { + for (i = 0; i < ST_MAX_CHANNELS; i++) { if (likely(st_gdata != NULL && st_gdata->list[i] != NULL && st_gdata->list[i]->reg_complete_cb != NULL)) st_gdata->list[i]->reg_complete_cb @@ -133,7 +131,7 @@ void st_reg_complete(struct st_data_s *st_gdata, char err) } static inline int st_check_data_len(struct st_data_s *st_gdata, - int protoid, int len) + unsigned char chnl_id, int len) { int room = skb_tailroom(st_gdata->rx_skb); @@ -144,7 +142,7 @@ static inline int st_check_data_len(struct st_data_s *st_gdata, * has zero length payload. So, ask ST CORE to * forward the packet to protocol driver (BT/FM/GPS) */ - st_send_frame(protoid, st_gdata); + st_send_frame(chnl_id, st_gdata); } else if (len > room) { /* Received packet's payload length is larger. @@ -157,7 +155,7 @@ static inline int st_check_data_len(struct st_data_s *st_gdata, /* Packet header has non-zero payload length and * we have enough space in created skb. Lets read * payload data */ - st_gdata->rx_state = ST_BT_W4_DATA; + st_gdata->rx_state = ST_W4_DATA; st_gdata->rx_count = len; return len; } @@ -167,6 +165,7 @@ static inline int st_check_data_len(struct st_data_s *st_gdata, st_gdata->rx_state = ST_W4_PACKET_TYPE; st_gdata->rx_skb = NULL; st_gdata->rx_count = 0; + st_gdata->rx_chnl = 0; return 0; } @@ -208,13 +207,10 @@ void st_int_recv(void *disc_data, const unsigned char *data, long count) { char *ptr; - struct hci_event_hdr *eh; - struct hci_acl_hdr *ah; - struct hci_sco_hdr *sh; - struct fm_event_hdr *fm; - struct gps_event_hdr *gps; - int len = 0, type = 0, dlen = 0; - static enum proto_type protoid = ST_MAX; + struct st_proto_s *proto; + unsigned short payload_len = 0; + int len = 0, type = 0; + unsigned char *plen; struct st_data_s *st_gdata = (struct st_data_s *)disc_data; ptr = (char *)data; @@ -242,64 +238,36 @@ void st_int_recv(void *disc_data, /* Check ST RX state machine , where are we? */ switch (st_gdata->rx_state) { - - /* Waiting for complete packet ? */ - case ST_BT_W4_DATA: + /* Waiting for complete packet ? */ + case ST_W4_DATA: pr_debug("Complete pkt received"); - /* Ask ST CORE to forward * the packet to protocol driver */ - st_send_frame(protoid, st_gdata); + st_send_frame(st_gdata->rx_chnl, st_gdata); st_gdata->rx_state = ST_W4_PACKET_TYPE; st_gdata->rx_skb = NULL; - protoid = ST_MAX; /* is this required ? */ - continue; - - /* Waiting for Bluetooth event header ? */ - case ST_BT_W4_EVENT_HDR: - eh = (struct hci_event_hdr *)st_gdata->rx_skb-> - data; - - pr_debug("Event header: evt 0x%2.2x" - "plen %d", eh->evt, eh->plen); - - st_check_data_len(st_gdata, protoid, eh->plen); - continue; - - /* Waiting for Bluetooth acl header ? */ - case ST_BT_W4_ACL_HDR: - ah = (struct hci_acl_hdr *)st_gdata->rx_skb-> - data; - dlen = __le16_to_cpu(ah->dlen); - - pr_info("ACL header: dlen %d", dlen); - - st_check_data_len(st_gdata, protoid, dlen); - continue; - - /* Waiting for Bluetooth sco header ? */ - case ST_BT_W4_SCO_HDR: - sh = (struct hci_sco_hdr *)st_gdata->rx_skb-> - data; - - pr_info("SCO header: dlen %d", sh->dlen); - - st_check_data_len(st_gdata, protoid, sh->dlen); - continue; - case ST_FM_W4_EVENT_HDR: - fm = (struct fm_event_hdr *)st_gdata->rx_skb-> - data; - pr_info("FM Header: "); - st_check_data_len(st_gdata, ST_FM, fm->plen); continue; - /* TODO : Add GPS packet machine logic here */ - case ST_GPS_W4_EVENT_HDR: - /* [0x09 pkt hdr][R/W byte][2 byte len] */ - gps = (struct gps_event_hdr *)st_gdata->rx_skb-> - data; - pr_info("GPS Header: "); - st_check_data_len(st_gdata, ST_GPS, gps->plen); + /* parse the header to know details */ + case ST_W4_HEADER: + proto = st_gdata->list[st_gdata->rx_chnl]; + plen = + &st_gdata->rx_skb->data + [proto->offset_len_in_hdr]; + pr_info("plen pointing to %x\n", *plen); + if (proto->len_size == 1)/* 1 byte len field */ + payload_len = *(unsigned char *)plen; + else if (proto->len_size == 2) + payload_len = + __le16_to_cpu(*(unsigned short *)plen); + else + pr_info("%s: invalid length " + "for id %d\n", + __func__, proto->chnl_id); + st_check_data_len(st_gdata, proto->chnl_id, + payload_len); + pr_info("off %d, pay len %d\n", + proto->offset_len_in_hdr, payload_len); continue; } /* end of switch rx_state */ } @@ -308,51 +276,6 @@ void st_int_recv(void *disc_data, /* Check first byte of packet and identify module * owner (BT/FM/GPS) */ switch (*ptr) { - - /* Bluetooth event packet? */ - case HCI_EVENT_PKT: - pr_info("Event packet"); - st_gdata->rx_state = ST_BT_W4_EVENT_HDR; - st_gdata->rx_count = HCI_EVENT_HDR_SIZE; - type = HCI_EVENT_PKT; - protoid = ST_BT; - break; - - /* Bluetooth acl packet? */ - case HCI_ACLDATA_PKT: - pr_info("ACL packet"); - st_gdata->rx_state = ST_BT_W4_ACL_HDR; - st_gdata->rx_count = HCI_ACL_HDR_SIZE; - type = HCI_ACLDATA_PKT; - protoid = ST_BT; - break; - - /* Bluetooth sco packet? */ - case HCI_SCODATA_PKT: - pr_info("SCO packet"); - st_gdata->rx_state = ST_BT_W4_SCO_HDR; - st_gdata->rx_count = HCI_SCO_HDR_SIZE; - type = HCI_SCODATA_PKT; - protoid = ST_BT; - break; - - /* Channel 8(FM) packet? */ - case ST_FM_CH8_PKT: - pr_info("FM CH8 packet"); - type = ST_FM_CH8_PKT; - st_gdata->rx_state = ST_FM_W4_EVENT_HDR; - st_gdata->rx_count = FM_EVENT_HDR_SIZE; - protoid = ST_FM; - break; - - /* Channel 9(GPS) packet? */ - case 0x9: /*ST_LL_GPS_CH9_PKT */ - pr_info("GPS CH9 packet"); - type = 0x9; /* ST_LL_GPS_CH9_PKT; */ - protoid = ST_GPS; - st_gdata->rx_state = ST_GPS_W4_EVENT_HDR; - st_gdata->rx_count = 3; /* GPS_EVENT_HDR_SIZE -1*/ - break; case LL_SLEEP_IND: case LL_SLEEP_ACK: case LL_WAKE_UP_IND: @@ -373,57 +296,22 @@ void st_int_recv(void *disc_data, continue; /* Unknow packet? */ default: - pr_err("Unknown packet type %2.2x", (__u8) *ptr); - ptr++; - count--; - continue; + type = *ptr; + st_gdata->rx_skb = alloc_skb( + st_gdata->list[type]->max_frame_size, + GFP_ATOMIC); + skb_reserve(st_gdata->rx_skb, + st_gdata->list[type]->reserve); + /* next 2 required for BT only */ + st_gdata->rx_skb->cb[0] = type; /*pkt_type*/ + st_gdata->rx_skb->cb[1] = 0; /*incoming*/ + st_gdata->rx_chnl = *ptr; + st_gdata->rx_state = ST_W4_HEADER; + st_gdata->rx_count = st_gdata->list[type]->hdr_len; + pr_info("rx_count %ld\n", st_gdata->rx_count); }; ptr++; count--; - - switch (protoid) { - case ST_BT: - /* Allocate new packet to hold received data */ - st_gdata->rx_skb = - bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); - if (!st_gdata->rx_skb) { - pr_err("Can't allocate mem for new packet"); - st_gdata->rx_state = ST_W4_PACKET_TYPE; - st_gdata->rx_count = 0; - return; - } - bt_cb(st_gdata->rx_skb)->pkt_type = type; - break; - case ST_FM: /* for FM */ - st_gdata->rx_skb = - alloc_skb(FM_MAX_FRAME_SIZE, GFP_ATOMIC); - if (!st_gdata->rx_skb) { - pr_err("Can't allocate mem for new packet"); - st_gdata->rx_state = ST_W4_PACKET_TYPE; - st_gdata->rx_count = 0; - return; - } - /* place holder 0x08 */ - skb_reserve(st_gdata->rx_skb, 1); - st_gdata->rx_skb->cb[0] = ST_FM_CH8_PKT; - break; - case ST_GPS: - /* for GPS */ - st_gdata->rx_skb = - alloc_skb(100 /*GPS_MAX_FRAME_SIZE */ , GFP_ATOMIC); - if (!st_gdata->rx_skb) { - pr_err("Can't allocate mem for new packet"); - st_gdata->rx_state = ST_W4_PACKET_TYPE; - st_gdata->rx_count = 0; - return; - } - /* place holder 0x09 */ - skb_reserve(st_gdata->rx_skb, 1); - st_gdata->rx_skb->cb[0] = 0x09; /*ST_GPS_CH9_PKT; */ - break; - case ST_MAX: - break; - } } pr_debug("done %s", __func__); return; @@ -565,20 +453,28 @@ long st_register(struct st_proto_s *new_proto) unsigned long flags = 0; st_kim_ref(&st_gdata, 0); - pr_info("%s(%d) ", __func__, new_proto->type); + pr_info("%s(%d) ", __func__, new_proto->chnl_id); if (st_gdata == NULL || new_proto == NULL || new_proto->recv == NULL || new_proto->reg_complete_cb == NULL) { pr_err("gdata/new_proto/recv or reg_complete_cb not ready"); + if (st_gdata == NULL) + pr_err("error 1\n"); + if (new_proto == NULL) + pr_err("error 2\n"); + if (new_proto->recv == NULL) + pr_err("error 3\n"); + if (new_proto->reg_complete_cb == NULL) + pr_err("erro 4\n"); return -1; } - if (new_proto->type < ST_BT || new_proto->type >= ST_MAX) { - pr_err("protocol %d not supported", new_proto->type); + if (new_proto->chnl_id >= ST_MAX_CHANNELS) { + pr_err("chnl_id %d not supported", new_proto->chnl_id); return -EPROTONOSUPPORT; } - if (st_gdata->list[new_proto->type] != NULL) { - pr_err("protocol %d already registered", new_proto->type); + if (st_gdata->list[new_proto->chnl_id] != NULL) { + pr_err("chnl_id %d already registered", new_proto->chnl_id); return -EALREADY; } @@ -586,11 +482,11 @@ long st_register(struct st_proto_s *new_proto) spin_lock_irqsave(&st_gdata->lock, flags); if (test_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state)) { - pr_info(" ST_REG_IN_PROGRESS:%d ", new_proto->type); + pr_info(" ST_REG_IN_PROGRESS:%d ", new_proto->chnl_id); /* fw download in progress */ - st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE); + st_kim_chip_toggle(new_proto->chnl_id, KIM_GPIO_ACTIVE); - st_gdata->list[new_proto->type] = new_proto; + add_channel_to_table(st_gdata, new_proto); st_gdata->protos_registered++; new_proto->write = st_write; @@ -598,7 +494,7 @@ long st_register(struct st_proto_s *new_proto) spin_unlock_irqrestore(&st_gdata->lock, flags); return -EINPROGRESS; } else if (st_gdata->protos_registered == ST_EMPTY) { - pr_info(" protocol list empty :%d ", new_proto->type); + pr_info(" chnl_id list empty :%d ", new_proto->chnl_id); set_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); st_recv = st_kim_recv; @@ -622,9 +518,9 @@ long st_register(struct st_proto_s *new_proto) return -1; } - /* the protocol might require other gpios to be toggled + /* the chnl_id might require other gpios to be toggled */ - st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE); + st_kim_chip_toggle(new_proto->chnl_id, KIM_GPIO_ACTIVE); clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); st_recv = st_int_recv; @@ -642,14 +538,14 @@ long st_register(struct st_proto_s *new_proto) /* check for already registered once more, * since the above check is old */ - if (st_gdata->list[new_proto->type] != NULL) { + if (st_gdata->list[new_proto->chnl_id] != NULL) { pr_err(" proto %d already registered ", - new_proto->type); + new_proto->chnl_id); return -EALREADY; } spin_lock_irqsave(&st_gdata->lock, flags); - st_gdata->list[new_proto->type] = new_proto; + add_channel_to_table(st_gdata, new_proto); st_gdata->protos_registered++; new_proto->write = st_write; spin_unlock_irqrestore(&st_gdata->lock, flags); @@ -657,22 +553,7 @@ long st_register(struct st_proto_s *new_proto) } /* if fw is already downloaded & new stack registers protocol */ else { - switch (new_proto->type) { - case ST_BT: - /* do nothing */ - break; - case ST_FM: - case ST_GPS: - st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE); - break; - case ST_MAX: - default: - pr_err("%d protocol not supported", - new_proto->type); - spin_unlock_irqrestore(&st_gdata->lock, flags); - return -EPROTONOSUPPORT; - } - st_gdata->list[new_proto->type] = new_proto; + add_channel_to_table(st_gdata, new_proto); st_gdata->protos_registered++; new_proto->write = st_write; @@ -680,48 +561,48 @@ long st_register(struct st_proto_s *new_proto) spin_unlock_irqrestore(&st_gdata->lock, flags); return err; } - pr_debug("done %s(%d) ", __func__, new_proto->type); + pr_debug("done %s(%d) ", __func__, new_proto->chnl_id); } EXPORT_SYMBOL_GPL(st_register); /* to unregister a protocol - * to be called from protocol stack driver */ -long st_unregister(enum proto_type type) +long st_unregister(struct st_proto_s *proto) { long err = 0; unsigned long flags = 0; struct st_data_s *st_gdata; - pr_debug("%s: %d ", __func__, type); + pr_debug("%s: %d ", __func__, proto->chnl_id); st_kim_ref(&st_gdata, 0); - if (type < ST_BT || type >= ST_MAX) { - pr_err(" protocol %d not supported", type); + if (proto->chnl_id >= ST_MAX_CHANNELS) { + pr_err(" chnl_id %d not supported", proto->chnl_id); return -EPROTONOSUPPORT; } spin_lock_irqsave(&st_gdata->lock, flags); - if (st_gdata->list[type] == NULL) { - pr_err(" protocol %d not registered", type); + if (st_gdata->list[proto->chnl_id] == NULL) { + pr_err(" chnl_id %d not registered", proto->chnl_id); spin_unlock_irqrestore(&st_gdata->lock, flags); return -EPROTONOSUPPORT; } st_gdata->protos_registered--; - st_gdata->list[type] = NULL; + remove_channel_from_table(st_gdata, proto); /* kim ignores BT in the below function * and handles the rest, BT is toggled * only in kim_start and kim_stop */ - st_kim_chip_toggle(type, KIM_GPIO_INACTIVE); + st_kim_chip_toggle(proto->chnl_id, KIM_GPIO_INACTIVE); spin_unlock_irqrestore(&st_gdata->lock, flags); if ((st_gdata->protos_registered == ST_EMPTY) && (!test_bit(ST_REG_PENDING, &st_gdata->st_state))) { - pr_info(" all protocols unregistered "); + pr_info(" all chnl_ids unregistered "); /* stop traffic on tty */ if (st_gdata->tty) { @@ -729,7 +610,7 @@ long st_unregister(enum proto_type type) stop_tty(st_gdata->tty); } - /* all protocols now unregistered */ + /* all chnl_ids now unregistered */ st_kim_stop(st_gdata->kim_data); /* disable ST LL */ st_ll_disable(st_gdata); @@ -745,7 +626,7 @@ long st_write(struct sk_buff *skb) { struct st_data_s *st_gdata; #ifdef DEBUG - enum proto_type protoid = ST_MAX; + unsigned char chnl_id = ST_MAX_CHANNELS; #endif long len; @@ -756,22 +637,10 @@ long st_write(struct sk_buff *skb) return -1; } #ifdef DEBUG /* open-up skb to read the 1st byte */ - switch (skb->data[0]) { - case HCI_COMMAND_PKT: - case HCI_ACLDATA_PKT: - case HCI_SCODATA_PKT: - protoid = ST_BT; - break; - case ST_FM_CH8_PKT: - protoid = ST_FM; - break; - case 0x09: - protoid = ST_GPS; - break; - } - if (unlikely(st_gdata->list[protoid] == NULL)) { - pr_err(" protocol %d not registered, and writing? ", - protoid); + chnl_id = skb->data[0]; + if (unlikely(st_gdata->list[chnl_id] == NULL)) { + pr_err(" chnl_id %d not registered, and writing? ", + chnl_id); return -1; } #endif @@ -824,7 +693,7 @@ static int st_tty_open(struct tty_struct *tty) static void st_tty_close(struct tty_struct *tty) { - unsigned char i = ST_MAX; + unsigned char i = ST_MAX_CHANNELS; unsigned long flags = 0; struct st_data_s *st_gdata = tty->disc_data; @@ -835,7 +704,7 @@ static void st_tty_close(struct tty_struct *tty) * un-installed for some reason - what should be done ? */ spin_lock_irqsave(&st_gdata->lock, flags); - for (i = ST_BT; i < ST_MAX; i++) { + for (i = ST_BT; i < ST_MAX_CHANNELS; i++) { if (st_gdata->list[i] != NULL) pr_err("%d not un-registered", i); st_gdata->list[i] = NULL; @@ -869,7 +738,7 @@ static void st_tty_close(struct tty_struct *tty) static void st_tty_receive(struct tty_struct *tty, const unsigned char *data, char *tty_flags, int count) { - +#define VERBOSE #ifdef VERBOSE print_hex_dump(KERN_DEBUG, ">in>", DUMP_PREFIX_NONE, 16, 1, data, count, 0); diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c index 73b6c8b0e869..707c85826417 100644 --- a/drivers/misc/ti-st/st_kim.c +++ b/drivers/misc/ti-st/st_kim.c @@ -32,11 +32,7 @@ #include #include -/* understand BT events for fw response */ -#include -#include -#include - +#include #include @@ -134,7 +130,7 @@ static inline int kim_check_data_len(struct kim_data_s *kim_gdata, int len) /* Packet header has non-zero payload length and * we have enough space in created skb. Lets read * payload data */ - kim_gdata->rx_state = ST_BT_W4_DATA; + kim_gdata->rx_state = ST_W4_DATA; kim_gdata->rx_count = len; return len; } @@ -158,8 +154,8 @@ void kim_int_recv(struct kim_data_s *kim_gdata, const unsigned char *data, long count) { const unsigned char *ptr; - struct hci_event_hdr *eh; int len = 0, type = 0; + unsigned char *plen; pr_debug("%s", __func__); /* Decode received bytes here */ @@ -183,29 +179,27 @@ void kim_int_recv(struct kim_data_s *kim_gdata, /* Check ST RX state machine , where are we? */ switch (kim_gdata->rx_state) { /* Waiting for complete packet ? */ - case ST_BT_W4_DATA: + case ST_W4_DATA: pr_debug("Complete pkt received"); validate_firmware_response(kim_gdata); kim_gdata->rx_state = ST_W4_PACKET_TYPE; kim_gdata->rx_skb = NULL; continue; /* Waiting for Bluetooth event header ? */ - case ST_BT_W4_EVENT_HDR: - eh = (struct hci_event_hdr *)kim_gdata-> - rx_skb->data; - pr_debug("Event header: evt 0x%2.2x" - "plen %d", eh->evt, eh->plen); - kim_check_data_len(kim_gdata, eh->plen); + case ST_W4_HEADER: + plen = + (unsigned char *)&kim_gdata->rx_skb->data[1]; + pr_debug("event hdr: plen 0x%02x\n", *plen); + kim_check_data_len(kim_gdata, *plen); continue; } /* end of switch */ } /* end of if rx_state */ switch (*ptr) { /* Bluetooth event packet? */ - case HCI_EVENT_PKT: - pr_info("Event packet"); - kim_gdata->rx_state = ST_BT_W4_EVENT_HDR; - kim_gdata->rx_count = HCI_EVENT_HDR_SIZE; - type = HCI_EVENT_PKT; + case 0x04: + kim_gdata->rx_state = ST_W4_HEADER; + kim_gdata->rx_count = 2; + type = *ptr; break; default: pr_info("unknown packet"); @@ -216,16 +210,18 @@ void kim_int_recv(struct kim_data_s *kim_gdata, ptr++; count--; kim_gdata->rx_skb = - bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + alloc_skb(1024+8, GFP_ATOMIC); if (!kim_gdata->rx_skb) { pr_err("can't allocate mem for new packet"); kim_gdata->rx_state = ST_W4_PACKET_TYPE; kim_gdata->rx_count = 0; return; } - bt_cb(kim_gdata->rx_skb)->pkt_type = type; + skb_reserve(kim_gdata->rx_skb, 8); + kim_gdata->rx_skb->cb[0] = 4; + kim_gdata->rx_skb->cb[1] = 0; + } - pr_info("done %s", __func__); return; } @@ -398,7 +394,7 @@ void st_kim_chip_toggle(enum proto_type type, enum kim_gpio_state state) gpio_set_value(kim_gdata->gpios[ST_GPS], GPIO_LOW); break; - case ST_MAX: + case ST_MAX_CHANNELS: default: break; } @@ -416,7 +412,6 @@ void st_kim_recv(void *disc_data, const unsigned char *data, long count) struct st_data_s *st_gdata = (struct st_data_s *)disc_data; struct kim_data_s *kim_gdata = st_gdata->kim_data; - pr_info(" %s ", __func__); /* copy to local buffer */ if (unlikely(data[4] == 0x01 && data[5] == 0x10 && data[0] == 0x04)) { /* must be the read_ver_cmd */ @@ -578,7 +573,7 @@ static int kim_toggle_radio(void *data, bool blocked) else st_kim_chip_toggle(type, KIM_GPIO_ACTIVE); break; - case ST_MAX: + case ST_MAX_CHANNELS: pr_err(" wrong proto type "); break; } @@ -664,12 +659,13 @@ static int kim_probe(struct platform_device *pdev) /* refer to itself */ kim_gdata->core_data->kim_data = kim_gdata; - for (proto = 0; proto < ST_MAX; proto++) { + for (proto = 0; proto < ST_MAX_CHANNELS; proto++) { kim_gdata->gpios[proto] = gpios[proto]; pr_info(" %ld gpio to be requested", gpios[proto]); } - for (proto = 0; (proto < ST_MAX) && (gpios[proto] != -1); proto++) { + for (proto = 0; (proto < ST_MAX_CHANNELS) + && (gpios[proto] != -1); proto++) { /* Claim the Bluetooth/FM/GPIO * nShutdown gpio from the system */ @@ -704,7 +700,8 @@ static int kim_probe(struct platform_device *pdev) init_completion(&kim_gdata->kim_rcvd); init_completion(&kim_gdata->ldisc_installed); - for (proto = 0; (proto < ST_MAX) && (gpios[proto] != -1); proto++) { + for (proto = 0; (proto < ST_MAX_CHANNELS) + && (gpios[proto] != -1); proto++) { /* TODO: should all types be rfkill_type_bt ? */ kim_gdata->rf_protos[proto] = proto; kim_gdata->rfkill[proto] = rfkill_alloc(protocol_names[proto], @@ -752,7 +749,8 @@ static int kim_remove(struct platform_device *pdev) kim_gdata = dev_get_drvdata(&pdev->dev); - for (proto = 0; (proto < ST_MAX) && (gpios[proto] != -1); proto++) { + for (proto = 0; (proto < ST_MAX_CHANNELS) + && (gpios[proto] != -1); proto++) { /* Claim the Bluetooth/FM/GPIO * nShutdown gpio from the system */ diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h index 4c7be2263011..1674ca7ab86d 100644 --- a/include/linux/ti_wilink_st.h +++ b/include/linux/ti_wilink_st.h @@ -42,7 +42,7 @@ enum proto_type { ST_BT, ST_FM, ST_GPS, - ST_MAX, + ST_MAX_CHANNELS = 16, }; /** @@ -62,6 +62,17 @@ enum proto_type { * @priv_data: privdate data holder for the protocol drivers, sent * from the protocol drivers during registration, and sent back on * reg_complete_cb and recv. + * @chnl_id: channel id the protocol driver is interested in, the channel + * id is nothing but the 1st byte of the packet in UART frame. + * @max_frame_size: size of the largest frame the protocol can receive. + * @hdr_len: length of the header structure of the protocol. + * @offset_len_in_hdr: this provides the offset of the length field in the + * header structure of the protocol header, to assist ST to know + * how much to receive, if the data is split across UART frames. + * @len_size: whether the length field inside the header is 2 bytes + * or 1 byte. + * @reserve: the number of bytes ST needs to reserve in the skb being + * prepared for the protocol driver. */ struct st_proto_s { enum proto_type type; @@ -70,10 +81,17 @@ struct st_proto_s { void (*reg_complete_cb) (void *, char data); long (*write) (struct sk_buff *skb); void *priv_data; + + unsigned char chnl_id; + unsigned short max_frame_size; + unsigned char hdr_len; + unsigned char offset_len_in_hdr; + unsigned char len_size; + unsigned char reserve; }; extern long st_register(struct st_proto_s *); -extern long st_unregister(enum proto_type); +extern long st_unregister(struct st_proto_s *); /* @@ -114,6 +132,7 @@ extern long st_unregister(enum proto_type); * @rx_skb: the skb where all data for a protocol gets accumulated, * since tty might not call receive when a complete event packet * is received, the states, count and the skb needs to be maintained. + * @rx_chnl: the channel ID for which the data is getting accumalated for. * @txq: the list of skbs which needs to be sent onto the TTY. * @tx_waitq: if the chip is not in AWAKE state, the skbs needs to be queued * up in here, PM(WAKEUP_IND) data needs to be sent and then the skbs @@ -135,10 +154,11 @@ struct st_data_s { #define ST_TX_SENDING 1 #define ST_TX_WAKEUP 2 unsigned long tx_state; - struct st_proto_s *list[ST_MAX]; + struct st_proto_s *list[ST_MAX_CHANNELS]; unsigned long rx_state; unsigned long rx_count; struct sk_buff *rx_skb; + unsigned char rx_chnl; struct sk_buff_head txq, tx_waitq; spinlock_t lock; unsigned char protos_registered; @@ -243,12 +263,12 @@ struct kim_data_s { struct completion kim_rcvd, ldisc_installed; char resp_buffer[30]; const struct firmware *fw_entry; - long gpios[ST_MAX]; + long gpios[ST_MAX_CHANNELS]; unsigned long rx_state; unsigned long rx_count; struct sk_buff *rx_skb; - struct rfkill *rfkill[ST_MAX]; - enum proto_type rf_protos[ST_MAX]; + struct rfkill *rfkill[ST_MAX_CHANNELS]; + enum proto_type rf_protos[ST_MAX_CHANNELS]; struct st_data_s *core_data; struct chip_version version; }; @@ -338,12 +358,8 @@ struct hci_command { /* ST LL receiver states */ #define ST_W4_PACKET_TYPE 0 -#define ST_BT_W4_EVENT_HDR 1 -#define ST_BT_W4_ACL_HDR 2 -#define ST_BT_W4_SCO_HDR 3 -#define ST_BT_W4_DATA 4 -#define ST_FM_W4_EVENT_HDR 5 -#define ST_GPS_W4_EVENT_HDR 6 +#define ST_W4_HEADER 1 +#define ST_W4_DATA 2 /* ST LL state machines */ #define ST_LL_ASLEEP 0 -- cgit v1.2.3 From ec60d0ad20ff8796dc41b30a9dce485478ccd263 Mon Sep 17 00:00:00 2001 From: Pavan Savoy Date: Fri, 4 Feb 2011 02:23:10 -0600 Subject: drivers:misc: ti-st: move from rfkill to sysfs The communication between ST KIM and UIM was interfaced over the /dev/rfkill device node. Move the interface to a simpler less abusive sysfs entry mechanism and document it in Documentation/ABI/testing/ under sysfs-platform-kim. Shared transport driver would now read the UART details originally received by bootloader or firmware as platform data. The data read will be shared over sysfs entries for the user-space UIM or other n/w manager/plugins to be read, and assist the driver by opening up the UART, setting the baud-rate and installing the line discipline. Signed-off-by: Pavan Savoy Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-platform-kim | 48 ++++++ drivers/misc/ti-st/st_kim.c | 244 +++++++++++++-------------- include/linux/ti_wilink_st.h | 19 ++- 3 files changed, 186 insertions(+), 125 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-platform-kim (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-platform-kim b/Documentation/ABI/testing/sysfs-platform-kim new file mode 100644 index 000000000000..c1653271872a --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-kim @@ -0,0 +1,48 @@ +What: /sys/devices/platform/kim/dev_name +Date: January 2010 +KernelVersion: 2.6.38 +Contact: "Pavan Savoy" +Description: + Name of the UART device at which the WL128x chip + is connected. example: "/dev/ttyS0". + The device name flows down to architecture specific board + initialization file from the SFI/ATAGS bootloader + firmware. The name exposed is read from the user-space + dameon and opens the device when install is requested. + +What: /sys/devices/platform/kim/baud_rate +Date: January 2010 +KernelVersion: 2.6.38 +Contact: "Pavan Savoy" +Description: + The maximum reliable baud-rate the host can support. + Different platforms tend to have different high-speed + UART configurations, so the baud-rate needs to be set + locally and also sent across to the WL128x via a HCI-VS + command. The entry is read and made use by the user-space + daemon when the ldisc install is requested. + +What: /sys/devices/platform/kim/flow_cntrl +Date: January 2010 +KernelVersion: 2.6.38 +Contact: "Pavan Savoy" +Description: + The WL128x makes use of flow control mechanism, and this + entry most often should be 1, the host's UART is required + to have the capability of flow-control, or else this + entry can be made use of for exceptions. + +What: /sys/devices/platform/kim/install +Date: January 2010 +KernelVersion: 2.6.38 +Contact: "Pavan Savoy" +Description: + When one of the protocols Bluetooth, FM or GPS wants to make + use of the shared UART transport, it registers to the shared + transport driver, which will signal the user-space for opening, + configuring baud and install line discipline via this sysfs + entry. This entry would be polled upon by the user-space + daemon managing the UART, and is notified about the change + by the sysfs_notify. The value would be '1' when UART needs + to be opened/ldisc installed, and would be '0' when UART + is no more required and needs to be closed. diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c index 707c85826417..a7fda8141758 100644 --- a/drivers/misc/ti-st/st_kim.c +++ b/drivers/misc/ti-st/st_kim.c @@ -30,46 +30,12 @@ #include #include #include -#include +#include #include #include -static int kim_probe(struct platform_device *pdev); -static int kim_remove(struct platform_device *pdev); - -/* KIM platform device driver structure */ -static struct platform_driver kim_platform_driver = { - .probe = kim_probe, - .remove = kim_remove, - /* TODO: ST driver power management during suspend/resume ? - */ -#if 0 - .suspend = kim_suspend, - .resume = kim_resume, -#endif - .driver = { - .name = "kim", - .owner = THIS_MODULE, - }, -}; - -static int kim_toggle_radio(void*, bool); -static const struct rfkill_ops kim_rfkill_ops = { - .set_block = kim_toggle_radio, -}; - -/* strings to be used for rfkill entries and by - * ST Core to be used for sysfs debug entry - */ -#define PROTO_ENTRY(type, name) name -const unsigned char *protocol_names[] = { - PROTO_ENTRY(ST_BT, "Bluetooth"), - PROTO_ENTRY(ST_FM, "FM"), - PROTO_ENTRY(ST_GPS, "GPS"), -}; - #define MAX_ST_DEVICES 3 /* Imagine 1 on each UART for now */ static struct platform_device *st_kim_devices[MAX_ST_DEVICES]; @@ -371,8 +337,7 @@ void st_kim_chip_toggle(enum proto_type type, enum kim_gpio_state state) kim_gdata = dev_get_drvdata(&kim_pdev->dev); if (kim_gdata->gpios[type] == -1) { - pr_info(" gpio not requested for protocol %s", - protocol_names[type]); + pr_info("gpio not requested for protocol %d", type); return; } switch (type) { @@ -450,11 +415,6 @@ long st_kim_start(void *kim_data) pr_info(" %s", __func__); do { - /* TODO: this is only because rfkill sub-system - * doesn't send events to user-space if the state - * isn't changed - */ - rfkill_set_hw_state(kim_gdata->rfkill[ST_BT], 1); /* Configure BT nShutdown to HIGH state */ gpio_set_value(kim_gdata->gpios[ST_BT], GPIO_LOW); mdelay(5); /* FIXME: a proper toggle */ @@ -462,22 +422,20 @@ long st_kim_start(void *kim_data) mdelay(100); /* re-initialize the completion */ INIT_COMPLETION(kim_gdata->ldisc_installed); -#if 0 /* older way of signalling user-space UIM */ - /* send signal to UIM */ - err = kill_pid(find_get_pid(kim_gdata->uim_pid), SIGUSR2, 0); - if (err != 0) { - pr_info(" sending SIGUSR2 to uim failed %ld", err); - err = -1; - continue; - } -#endif - /* unblock and send event to UIM via /dev/rfkill */ - rfkill_set_hw_state(kim_gdata->rfkill[ST_BT], 0); + /* send notification to UIM */ + kim_gdata->ldisc_install = 1; + pr_info("ldisc_install = 1"); + sysfs_notify(&kim_gdata->kim_pdev->dev.kobj, + NULL, "install"); /* wait for ldisc to be installed */ err = wait_for_completion_timeout(&kim_gdata->ldisc_installed, msecs_to_jiffies(LDISC_TIME)); if (!err) { /* timeout */ pr_err("line disc installation timed out "); + kim_gdata->ldisc_install = 0; + pr_info("ldisc_install = 0"); + sysfs_notify(&kim_gdata->kim_pdev->dev.kobj, + NULL, "install"); err = -1; continue; } else { @@ -486,6 +444,10 @@ long st_kim_start(void *kim_data) err = download_firmware(kim_gdata); if (err != 0) { pr_err("download firmware failed"); + kim_gdata->ldisc_install = 0; + pr_info("ldisc_install = 0"); + sysfs_notify(&kim_gdata->kim_pdev->dev.kobj, + NULL, "install"); continue; } else { /* on success don't retry */ break; @@ -505,16 +467,15 @@ long st_kim_stop(void *kim_data) struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data; INIT_COMPLETION(kim_gdata->ldisc_installed); -#if 0 /* older way of signalling user-space UIM */ - /* send signal to UIM */ - err = kill_pid(find_get_pid(kim_gdata->uim_pid), SIGUSR2, 1); - if (err != 0) { - pr_err("sending SIGUSR2 to uim failed %ld", err); - return -1; - } -#endif - /* set BT rfkill to be blocked */ - err = rfkill_set_hw_state(kim_gdata->rfkill[ST_BT], 1); + + /* Flush any pending characters in the driver and discipline. */ + tty_ldisc_flush(kim_gdata->core_data->tty); + tty_driver_flush_buffer(kim_gdata->core_data->tty); + + /* send uninstall notification to UIM */ + pr_info("ldisc_install = 0"); + kim_gdata->ldisc_install = 0; + sysfs_notify(&kim_gdata->kim_pdev->dev.kobj, NULL, "install"); /* wait for ldisc to be un-installed */ err = wait_for_completion_timeout(&kim_gdata->ldisc_installed, @@ -553,33 +514,59 @@ static int show_list(struct seq_file *s, void *unused) return 0; } -/* function called from rfkill subsystem, when someone from - * user space would write 0/1 on the sysfs entry - * /sys/class/rfkill/rfkill0,1,3/state - */ -static int kim_toggle_radio(void *data, bool blocked) +static ssize_t show_install(struct device *dev, + struct device_attribute *attr, char *buf) { - enum proto_type type = *((enum proto_type *)data); - pr_debug(" %s: %d ", __func__, type); + struct kim_data_s *kim_data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", kim_data->ldisc_install); +} - switch (type) { - case ST_BT: - /* do nothing */ - break; - case ST_FM: - case ST_GPS: - if (blocked) - st_kim_chip_toggle(type, KIM_GPIO_INACTIVE); - else - st_kim_chip_toggle(type, KIM_GPIO_ACTIVE); - break; - case ST_MAX_CHANNELS: - pr_err(" wrong proto type "); - break; - } - return 0; +static ssize_t show_dev_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kim_data_s *kim_data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", kim_data->dev_name); +} + +static ssize_t show_baud_rate(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kim_data_s *kim_data = dev_get_drvdata(dev); + return sprintf(buf, "%ld\n", kim_data->baud_rate); +} + +static ssize_t show_flow_cntrl(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kim_data_s *kim_data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", kim_data->flow_cntrl); } +/* structures specific for sysfs entries */ +static struct kobj_attribute ldisc_install = +__ATTR(install, 0444, (void *)show_install, NULL); + +static struct kobj_attribute uart_dev_name = +__ATTR(dev_name, 0444, (void *)show_dev_name, NULL); + +static struct kobj_attribute uart_baud_rate = +__ATTR(baud_rate, 0444, (void *)show_baud_rate, NULL); + +static struct kobj_attribute uart_flow_cntrl = +__ATTR(flow_cntrl, 0444, (void *)show_flow_cntrl, NULL); + +static struct attribute *uim_attrs[] = { + &ldisc_install.attr, + &uart_dev_name.attr, + &uart_baud_rate.attr, + &uart_flow_cntrl.attr, + NULL, +}; + +static struct attribute_group uim_attr_grp = { + .attrs = uim_attrs, +}; + /** * st_kim_ref - reference the core's data * This references the per-ST platform device in the arch/xx/ @@ -633,8 +620,9 @@ static int kim_probe(struct platform_device *pdev) { long status; long proto; - long *gpios = pdev->dev.platform_data; struct kim_data_s *kim_gdata; + struct ti_st_plat_data *pdata = pdev->dev.platform_data; + long *gpios = pdata->gpios; if ((pdev->id != -1) && (pdev->id < MAX_ST_DEVICES)) { /* multiple devices could exist */ @@ -700,30 +688,18 @@ static int kim_probe(struct platform_device *pdev) init_completion(&kim_gdata->kim_rcvd); init_completion(&kim_gdata->ldisc_installed); - for (proto = 0; (proto < ST_MAX_CHANNELS) - && (gpios[proto] != -1); proto++) { - /* TODO: should all types be rfkill_type_bt ? */ - kim_gdata->rf_protos[proto] = proto; - kim_gdata->rfkill[proto] = rfkill_alloc(protocol_names[proto], - &pdev->dev, RFKILL_TYPE_BLUETOOTH, - &kim_rfkill_ops, &kim_gdata->rf_protos[proto]); - if (kim_gdata->rfkill[proto] == NULL) { - pr_err("cannot create rfkill entry for gpio %ld", - gpios[proto]); - continue; - } - /* block upon creation */ - rfkill_init_sw_state(kim_gdata->rfkill[proto], 1); - status = rfkill_register(kim_gdata->rfkill[proto]); - if (unlikely(status)) { - pr_err("rfkill registration failed for gpio %ld", - gpios[proto]); - rfkill_unregister(kim_gdata->rfkill[proto]); - continue; - } - pr_info("rfkill entry created for %ld", gpios[proto]); + status = sysfs_create_group(&pdev->dev.kobj, &uim_attr_grp); + if (status) { + pr_err("failed to create sysfs entries"); + return status; } + /* copying platform data */ + strncpy(kim_gdata->dev_name, pdata->dev_name, UART_DEV_NAME_LEN); + kim_gdata->flow_cntrl = pdata->flow_cntrl; + kim_gdata->baud_rate = pdata->baud_rate; + pr_info("sysfs entries created\n"); + kim_debugfs_dir = debugfs_create_dir("ti-st", NULL); if (IS_ERR(kim_debugfs_dir)) { pr_err(" debugfs entries creation failed "); @@ -741,9 +717,9 @@ static int kim_probe(struct platform_device *pdev) static int kim_remove(struct platform_device *pdev) { - /* free the GPIOs requested - */ - long *gpios = pdev->dev.platform_data; + /* free the GPIOs requested */ + struct ti_st_plat_data *pdata = pdev->dev.platform_data; + long *gpios = pdata->gpios; long proto; struct kim_data_s *kim_gdata; @@ -755,12 +731,11 @@ static int kim_remove(struct platform_device *pdev) * nShutdown gpio from the system */ gpio_free(gpios[proto]); - rfkill_unregister(kim_gdata->rfkill[proto]); - rfkill_destroy(kim_gdata->rfkill[proto]); - kim_gdata->rfkill[proto] = NULL; } pr_info("kim: GPIO Freed"); debugfs_remove_recursive(kim_debugfs_dir); + + sysfs_remove_group(&pdev->dev.kobj, &uim_attr_grp); kim_gdata->kim_pdev = NULL; st_core_exit(kim_gdata->core_data); @@ -769,23 +744,46 @@ static int kim_remove(struct platform_device *pdev) return 0; } +int kim_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct ti_st_plat_data *pdata = pdev->dev.platform_data; + + if (pdata->suspend) + return pdata->suspend(pdev, state); + + return -EOPNOTSUPP; +} + +int kim_resume(struct platform_device *pdev) +{ + struct ti_st_plat_data *pdata = pdev->dev.platform_data; + + if (pdata->resume) + return pdata->resume(pdev); + + return -EOPNOTSUPP; +} + /**********************************************************************/ /* entry point for ST KIM module, called in from ST Core */ +static struct platform_driver kim_platform_driver = { + .probe = kim_probe, + .remove = kim_remove, + .suspend = kim_suspend, + .resume = kim_resume, + .driver = { + .name = "kim", + .owner = THIS_MODULE, + }, +}; static int __init st_kim_init(void) { - long ret = 0; - ret = platform_driver_register(&kim_platform_driver); - if (ret != 0) { - pr_err("platform drv registration failed"); - return -1; - } - return 0; + return platform_driver_register(&kim_platform_driver); } static void __exit st_kim_deinit(void) { - /* the following returns void */ platform_driver_unregister(&kim_platform_driver); } diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h index 1674ca7ab86d..010cda7287a0 100644 --- a/include/linux/ti_wilink_st.h +++ b/include/linux/ti_wilink_st.h @@ -206,8 +206,8 @@ void gps_chrdrv_stub_init(void); /* time in msec to wait for * line discipline to be installed */ -#define LDISC_TIME 500 -#define CMD_RESP_TIME 500 +#define LDISC_TIME 1000 +#define CMD_RESP_TIME 800 #define MAKEWORD(a, b) ((unsigned short)(((unsigned char)(a)) \ | ((unsigned short)((unsigned char)(b))) << 8)) @@ -230,6 +230,7 @@ struct chip_version { unsigned short maj_ver; }; +#define UART_DEV_NAME_LEN 32 /** * struct kim_data_s - the KIM internal data, embedded as the * platform's drv data. One for each ST device in the system. @@ -271,6 +272,10 @@ struct kim_data_s { enum proto_type rf_protos[ST_MAX_CHANNELS]; struct st_data_s *core_data; struct chip_version version; + unsigned char ldisc_install; + unsigned char dev_name[UART_DEV_NAME_LEN]; + unsigned char flow_cntrl; + unsigned long baud_rate; }; /** @@ -413,4 +418,14 @@ struct gps_event_hdr { u16 plen; } __attribute__ ((packed)); +/* platform data */ +struct ti_st_plat_data { + long gpios[ST_MAX_CHANNELS]; /* BT, FM and GPS */ + unsigned char dev_name[UART_DEV_NAME_LEN]; /* uart name */ + unsigned char flow_cntrl; /* flow control flag */ + unsigned long baud_rate; + int (*suspend)(struct platform_device *, pm_message_t); + int (*resume)(struct platform_device *); +}; + #endif /* TI_WILINK_ST_H */ -- cgit v1.2.3 From ef04d121f030329aae0c2d3ec22beea0c5cbcfd3 Mon Sep 17 00:00:00 2001 From: Pavan Savoy Date: Fri, 4 Feb 2011 02:23:13 -0600 Subject: drivers:misc: ti-st: firmware download optimization To fasten the process of firmware download, the chip allows disabling of the command complete event generation from host. In these cases, only few very essential commands would have the command complete events and hence the wait associated with them. So now the driver would wait for a command complete event, only when it comes across a wait event during firmware parsing. This would also mean we need to skip not just the change baud rate command but also the wait for it. Signed-off-by: Pavan Savoy Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ti-st/st_core.c | 18 ++++++++++ drivers/misc/ti-st/st_kim.c | 80 ++++++++++++++++++++++++++++++++++++++++---- include/linux/ti_wilink_st.h | 6 ++++ 3 files changed, 97 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index f7bb96f3a424..dd2c879faff6 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -52,6 +52,24 @@ static void remove_channel_from_table(struct st_data_s *st_gdata, st_gdata->list[proto->chnl_id] = NULL; } +/* + * called from KIM during firmware download. + * + * This is a wrapper function to tty->ops->write_room. + * It returns number of free space available in + * uart tx buffer. + */ +int st_get_uart_wr_room(struct st_data_s *st_gdata) +{ + struct tty_struct *tty; + if (unlikely(st_gdata == NULL || st_gdata->tty == NULL)) { + pr_err("tty unavailable to perform write"); + return -1; + } + tty = st_gdata->tty; + return tty->ops->write_room(tty); +} + /* can be called in from * -- KIM (during fw download) * -- ST Core (during st_write) diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c index ccc46a7b0abb..2c096ccd53b0 100644 --- a/drivers/misc/ti-st/st_kim.c +++ b/drivers/misc/ti-st/st_kim.c @@ -232,6 +232,26 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name) return 0; } +void skip_change_remote_baud(unsigned char **ptr, long *len) +{ + unsigned char *nxt_action, *cur_action; + cur_action = *ptr; + + nxt_action = cur_action + sizeof(struct bts_action) + + ((struct bts_action *) cur_action)->size; + + if (((struct bts_action *) nxt_action)->type != ACTION_WAIT_EVENT) { + pr_err("invalid action after change remote baud command"); + } else { + *ptr = *ptr + sizeof(struct bts_action) + + ((struct bts_action *)nxt_action)->size; + *len = *len - (sizeof(struct bts_action) + + ((struct bts_action *)nxt_action)->size); + /* warn user on not commenting these in firmware */ + pr_warn("skipping the wait event of change remote baud"); + } +} + /** * download_firmware - * internal function which parses through the .bts firmware @@ -244,6 +264,9 @@ static long download_firmware(struct kim_data_s *kim_gdata) unsigned char *ptr = NULL; unsigned char *action_ptr = NULL; unsigned char bts_scr_name[30] = { 0 }; /* 30 char long bts scr name? */ + int wr_room_space; + int cmd_size; + unsigned long timeout; err = read_local_version(kim_gdata, bts_scr_name); if (err != 0) { @@ -280,13 +303,43 @@ static long download_firmware(struct kim_data_s *kim_gdata) 0xFF36)) { /* ignore remote change * baud rate HCI VS command */ - pr_err - (" change remote baud" + pr_warn("change remote baud" " rate command in firmware"); + skip_change_remote_baud(&ptr, &len); break; } + /* + * Make sure we have enough free space in uart + * tx buffer to write current firmware command + */ + cmd_size = ((struct bts_action *)ptr)->size; + timeout = jiffies + msecs_to_jiffies(CMD_WR_TIME); + do { + wr_room_space = + st_get_uart_wr_room(kim_gdata->core_data); + if (wr_room_space < 0) { + pr_err("Unable to get free " + "space info from uart tx buffer"); + release_firmware(kim_gdata->fw_entry); + return wr_room_space; + } + mdelay(1); /* wait 1ms before checking room */ + } while ((wr_room_space < cmd_size) && + time_before(jiffies, timeout)); + + /* Timeout happened ? */ + if (time_after_eq(jiffies, timeout)) { + pr_err("Timeout while waiting for free " + "free space in uart tx buffer"); + release_firmware(kim_gdata->fw_entry); + return -ETIMEDOUT; + } - INIT_COMPLETION(kim_gdata->kim_rcvd); + /* + * Free space found in uart buffer, call st_int_write + * to send current firmware command to the uart tx + * buffer. + */ err = st_int_write(kim_gdata->core_data, ((struct bts_action_send *)action_ptr)->data, ((struct bts_action *)ptr)->size); @@ -294,15 +347,28 @@ static long download_firmware(struct kim_data_s *kim_gdata) release_firmware(kim_gdata->fw_entry); return err; } + /* + * Check number of bytes written to the uart tx buffer + * and requested command write size + */ + if (err != cmd_size) { + pr_err("Number of bytes written to uart " + "tx buffer are not matching with " + "requested cmd write size"); + release_firmware(kim_gdata->fw_entry); + return -EIO; + } + break; + case ACTION_WAIT_EVENT: /* wait */ if (!wait_for_completion_timeout - (&kim_gdata->kim_rcvd, - msecs_to_jiffies(CMD_RESP_TIME))) { - pr_err - (" response timeout during fw download "); + (&kim_gdata->kim_rcvd, + msecs_to_jiffies(CMD_RESP_TIME))) { + pr_err("response timeout during fw download "); /* timed out */ release_firmware(kim_gdata->fw_entry); return -ETIMEDOUT; } + INIT_COMPLETION(kim_gdata->kim_rcvd); break; case ACTION_DELAY: /* sleep */ pr_info("sleep command in scr"); diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h index 010cda7287a0..7885a779c588 100644 --- a/include/linux/ti_wilink_st.h +++ b/include/linux/ti_wilink_st.h @@ -166,6 +166,11 @@ struct st_data_s { void *kim_data; }; +/* + * wrapper around tty->ops->write_room to check + * availability during firmware download + */ +int st_get_uart_wr_room(struct st_data_s *st_gdata); /** * st_int_write - * point this to tty->driver->write or tty->ops->write @@ -208,6 +213,7 @@ void gps_chrdrv_stub_init(void); */ #define LDISC_TIME 1000 #define CMD_RESP_TIME 800 +#define CMD_WR_TIME 5000 #define MAKEWORD(a, b) ((unsigned short)(((unsigned char)(a)) \ | ((unsigned short)((unsigned char)(b))) << 8)) -- cgit v1.2.3 From 781a7395d239dbdb59738ca7fe08e71641bf583c Mon Sep 17 00:00:00 2001 From: Pavan Savoy Date: Fri, 4 Feb 2011 02:23:15 -0600 Subject: drivers:misc: ti-st: remove multiple gpio handling TI shared transport driver previously intended to expose rfkill entries for each of the protocol gpio that the chip would have. However now in case such gpios exist, which requires to be enabled for a specific protocol, the responsibility lay on protocol driver. This patch removes the request/free of multiple gpios, rfkill struct references and also removes the chip_toggle function. Signed-off-by: Pavan Savoy Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ti-st/st_core.c | 11 ---- drivers/misc/ti-st/st_kim.c | 117 +++++++++---------------------------------- include/linux/ti_wilink_st.h | 19 +------ 3 files changed, 26 insertions(+), 121 deletions(-) (limited to 'include') diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index f0d24d852078..1847c477c0c0 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -515,7 +515,6 @@ long st_register(struct st_proto_s *new_proto) if (test_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state)) { pr_info(" ST_REG_IN_PROGRESS:%d ", new_proto->chnl_id); /* fw download in progress */ - st_kim_chip_toggle(new_proto->chnl_id, KIM_GPIO_ACTIVE); add_channel_to_table(st_gdata, new_proto); st_gdata->protos_registered++; @@ -548,10 +547,6 @@ long st_register(struct st_proto_s *new_proto) return -EINVAL; } - /* the chnl_id might require other gpios to be toggled - */ - st_kim_chip_toggle(new_proto->chnl_id, KIM_GPIO_ACTIVE); - clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); st_recv = st_int_recv; @@ -622,12 +617,6 @@ long st_unregister(struct st_proto_s *proto) st_gdata->protos_registered--; remove_channel_from_table(st_gdata, proto); - - /* kim ignores BT in the below function - * and handles the rest, BT is toggled - * only in kim_start and kim_stop - */ - st_kim_chip_toggle(proto->chnl_id, KIM_GPIO_INACTIVE); spin_unlock_irqrestore(&st_gdata->lock, flags); if ((st_gdata->protos_registered == ST_EMPTY) && diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c index 2c096ccd53b0..9ee4c788aa69 100644 --- a/drivers/misc/ti-st/st_kim.c +++ b/drivers/misc/ti-st/st_kim.c @@ -390,49 +390,6 @@ static long download_firmware(struct kim_data_s *kim_gdata) /**********************************************************************/ /* functions called from ST core */ -/* function to toggle the GPIO - * needs to know whether the GPIO is active high or active low - */ -void st_kim_chip_toggle(enum proto_type type, enum kim_gpio_state state) -{ - struct platform_device *kim_pdev; - struct kim_data_s *kim_gdata; - pr_info(" %s ", __func__); - - kim_pdev = st_get_plat_device(0); - kim_gdata = dev_get_drvdata(&kim_pdev->dev); - - if (kim_gdata->gpios[type] == -1) { - pr_info("gpio not requested for protocol %d", type); - return; - } - switch (type) { - case ST_BT: - /*Do Nothing */ - break; - - case ST_FM: - if (state == KIM_GPIO_ACTIVE) - gpio_set_value(kim_gdata->gpios[ST_FM], GPIO_LOW); - else - gpio_set_value(kim_gdata->gpios[ST_FM], GPIO_HIGH); - break; - - case ST_GPS: - if (state == KIM_GPIO_ACTIVE) - gpio_set_value(kim_gdata->gpios[ST_GPS], GPIO_HIGH); - else - gpio_set_value(kim_gdata->gpios[ST_GPS], GPIO_LOW); - break; - - case ST_MAX_CHANNELS: - default: - break; - } - - return; -} - /* called from ST Core, when REG_IN_PROGRESS (registration in progress) * can be because of * 1. response to read local version @@ -482,9 +439,9 @@ long st_kim_start(void *kim_data) do { /* Configure BT nShutdown to HIGH state */ - gpio_set_value(kim_gdata->gpios[ST_BT], GPIO_LOW); + gpio_set_value(kim_gdata->nshutdown, GPIO_LOW); mdelay(5); /* FIXME: a proper toggle */ - gpio_set_value(kim_gdata->gpios[ST_BT], GPIO_HIGH); + gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH); mdelay(100); /* re-initialize the completion */ INIT_COMPLETION(kim_gdata->ldisc_installed); @@ -552,11 +509,11 @@ long st_kim_stop(void *kim_data) } /* By default configure BT nShutdown to LOW state */ - gpio_set_value(kim_gdata->gpios[ST_BT], GPIO_LOW); + gpio_set_value(kim_gdata->nshutdown, GPIO_LOW); mdelay(1); - gpio_set_value(kim_gdata->gpios[ST_BT], GPIO_HIGH); + gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH); mdelay(1); - gpio_set_value(kim_gdata->gpios[ST_BT], GPIO_LOW); + gpio_set_value(kim_gdata->nshutdown, GPIO_LOW); return err; } @@ -685,10 +642,8 @@ struct dentry *kim_debugfs_dir; static int kim_probe(struct platform_device *pdev) { long status; - long proto; struct kim_data_s *kim_gdata; struct ti_st_plat_data *pdata = pdev->dev.platform_data; - long *gpios = pdata->gpios; if ((pdev->id != -1) && (pdev->id < MAX_ST_DEVICES)) { /* multiple devices could exist */ @@ -713,40 +668,19 @@ static int kim_probe(struct platform_device *pdev) /* refer to itself */ kim_gdata->core_data->kim_data = kim_gdata; - for (proto = 0; proto < ST_MAX_CHANNELS; proto++) { - kim_gdata->gpios[proto] = gpios[proto]; - pr_info(" %ld gpio to be requested", gpios[proto]); + /* Claim the chip enable nShutdown gpio from the system */ + kim_gdata->nshutdown = pdata->nshutdown_gpio; + status = gpio_request(kim_gdata->nshutdown, "kim"); + if (unlikely(status)) { + pr_err(" gpio %ld request failed ", kim_gdata->nshutdown); + return status; } - for (proto = 0; (proto < ST_MAX_CHANNELS) - && (gpios[proto] != -1); proto++) { - /* Claim the Bluetooth/FM/GPIO - * nShutdown gpio from the system - */ - status = gpio_request(gpios[proto], "kim"); - if (unlikely(status)) { - pr_err(" gpio %ld request failed ", gpios[proto]); - proto -= 1; - while (proto >= 0) { - if (gpios[proto] != -1) - gpio_free(gpios[proto]); - } - return status; - } - - /* Configure nShutdown GPIO as output=0 */ - status = - gpio_direction_output(gpios[proto], 0); - if (unlikely(status)) { - pr_err(" unable to configure gpio %ld", - gpios[proto]); - proto -= 1; - while (proto >= 0) { - if (gpios[proto] != -1) - gpio_free(gpios[proto]); - } - return status; - } + /* Configure nShutdown GPIO as output=0 */ + status = gpio_direction_output(kim_gdata->nshutdown, 0); + if (unlikely(status)) { + pr_err(" unable to configure gpio %ld", kim_gdata->nshutdown); + return status; } /* get reference of pdev for request_firmware */ @@ -785,23 +719,20 @@ static int kim_remove(struct platform_device *pdev) { /* free the GPIOs requested */ struct ti_st_plat_data *pdata = pdev->dev.platform_data; - long *gpios = pdata->gpios; - long proto; struct kim_data_s *kim_gdata; kim_gdata = dev_get_drvdata(&pdev->dev); - for (proto = 0; (proto < ST_MAX_CHANNELS) - && (gpios[proto] != -1); proto++) { - /* Claim the Bluetooth/FM/GPIO - * nShutdown gpio from the system - */ - gpio_free(gpios[proto]); - } - pr_info("kim: GPIO Freed"); - debugfs_remove_recursive(kim_debugfs_dir); + /* Free the Bluetooth/FM/GPIO + * nShutdown gpio from the system + */ + gpio_free(pdata->nshutdown_gpio); + pr_info("nshutdown GPIO Freed"); + debugfs_remove_recursive(kim_debugfs_dir); sysfs_remove_group(&pdev->dev.kobj, &uim_attr_grp); + pr_info("sysfs entries removed"); + kim_gdata->kim_pdev = NULL; st_core_exit(kim_gdata->core_data); diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h index 7885a779c588..7071ec5d0118 100644 --- a/include/linux/ti_wilink_st.h +++ b/include/linux/ti_wilink_st.h @@ -25,15 +25,6 @@ #ifndef TI_WILINK_ST_H #define TI_WILINK_ST_H -/** - * enum kim_gpio_state - Few protocols such as FM have ACTIVE LOW - * gpio states for their chip/core enable gpios - */ -enum kim_gpio_state { - KIM_GPIO_INACTIVE, - KIM_GPIO_ACTIVE, -}; - /** * enum proto-type - The protocol on WiLink chips which share a * common physical interface like UART. @@ -252,14 +243,11 @@ struct chip_version { * the ldisc was properly installed. * @resp_buffer: data buffer for the .bts fw file name. * @fw_entry: firmware class struct to request/release the fw. - * @gpios: the list of core/chip enable gpios for BT, FM and GPS cores. * @rx_state: the rx state for kim's receive func during fw download. * @rx_count: the rx count for the kim's receive func during fw download. * @rx_skb: all of fw data might not come at once, and hence data storage for * whole of the fw response, only HCI_EVENTs and hence diff from ST's * response. - * @rfkill: rfkill data for each of the cores to be registered with rfkill. - * @rf_protos: proto types of the data registered with rfkill sub-system. * @core_data: ST core's data, which mainly is the tty's disc_data * @version: chip version available via a sysfs entry. * @@ -270,12 +258,10 @@ struct kim_data_s { struct completion kim_rcvd, ldisc_installed; char resp_buffer[30]; const struct firmware *fw_entry; - long gpios[ST_MAX_CHANNELS]; + long nshutdown; unsigned long rx_state; unsigned long rx_count; struct sk_buff *rx_skb; - struct rfkill *rfkill[ST_MAX_CHANNELS]; - enum proto_type rf_protos[ST_MAX_CHANNELS]; struct st_data_s *core_data; struct chip_version version; unsigned char ldisc_install; @@ -293,7 +279,6 @@ long st_kim_start(void *); long st_kim_stop(void *); void st_kim_recv(void *, const unsigned char *, long count); -void st_kim_chip_toggle(enum proto_type, enum kim_gpio_state); void st_kim_complete(void *); void kim_st_list_protocols(struct st_data_s *, void *); @@ -426,7 +411,7 @@ struct gps_event_hdr { /* platform data */ struct ti_st_plat_data { - long gpios[ST_MAX_CHANNELS]; /* BT, FM and GPS */ + long nshutdown_gpio; unsigned char dev_name[UART_DEV_NAME_LEN]; /* uart name */ unsigned char flow_cntrl; /* flow control flag */ unsigned long baud_rate; -- cgit v1.2.3 From 92d8682926342d2b6aa5b2ecc02221e00e1573a0 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 4 Feb 2011 15:55:25 -0800 Subject: inetpeer: Move ICMP rate limiting state into inet_peer entries. Like metrics, the ICMP rate limiting bits are cached state about a destination. So move it into the inet_peer entries. If an inet_peer cannot be bound (the reason is memory allocation failure or similar), the policy is to allow. Signed-off-by: David S. Miller --- include/net/dst.h | 2 -- include/net/icmp.h | 3 --- include/net/inetpeer.h | 3 +++ net/ipv4/icmp.c | 49 ++++++++----------------------------------- net/ipv4/inetpeer.c | 43 ++++++++++++++++++++++++++++++++++++++ net/ipv4/route.c | 56 ++++++++++++++++++++++++++++++++++---------------- net/ipv6/icmp.c | 16 ++++++++------- net/ipv6/ip6_output.c | 5 ++++- net/ipv6/ndisc.c | 4 +++- 9 files changed, 108 insertions(+), 73 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index 484f80b69ada..e550195d4f86 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -78,8 +78,6 @@ 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 diff --git a/include/net/icmp.h b/include/net/icmp.h index 6e991e0d0d6f..f0698b955b73 100644 --- a/include/net/icmp.h +++ b/include/net/icmp.h @@ -45,7 +45,4 @@ extern int icmp_ioctl(struct sock *sk, int cmd, unsigned long arg); extern int icmp_init(void); extern void icmp_out_count(struct net *net, unsigned char type); -/* Move into dst.h ? */ -extern int xrlim_allow(struct dst_entry *dst, int timeout); - #endif /* _ICMP_H */ diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index 61f2c66edb2a..ead2cb2de18c 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -44,6 +44,8 @@ struct inet_peer { __u32 tcp_ts; __u32 tcp_ts_stamp; u32 metrics[RTAX_MAX]; + u32 rate_tokens; /* rate limiting for ICMP */ + unsigned long rate_last; }; struct rcu_head rcu; }; @@ -81,6 +83,7 @@ static inline struct inet_peer *inet_getpeer_v6(struct in6_addr *v6daddr, int cr /* can be called from BH context or outside */ extern void inet_putpeer(struct inet_peer *p); +extern bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout); /* * temporary check to make sure we dont access rid, ip_id_count, tcp_ts, diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 4aa1b7f01ea0..ad2bcf1b69ae 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -233,48 +233,11 @@ static inline void icmp_xmit_unlock(struct sock *sk) * Send an ICMP frame. */ -/* - * Check transmit rate limitation for given message. - * The rate information is held in the destination cache now. - * This function is generic and could be used for other purposes - * too. It uses a Token bucket filter as suggested by Alexey Kuznetsov. - * - * Note that the same dst_entry fields are modified by functions in - * route.c too, but these work for packet destinations while xrlim_allow - * works for icmp destinations. This means the rate limiting information - * for one "ip object" is shared - and these ICMPs are twice limited: - * by source and by destination. - * - * RFC 1812: 4.3.2.8 SHOULD be able to limit error message rate - * SHOULD allow setting of rate limits - * - * Shared between ICMPv4 and ICMPv6. - */ -#define XRLIM_BURST_FACTOR 6 -int xrlim_allow(struct dst_entry *dst, int timeout) -{ - unsigned long now, token = dst->rate_tokens; - int rc = 0; - - now = jiffies; - token += now - dst->rate_last; - dst->rate_last = now; - if (token > XRLIM_BURST_FACTOR * timeout) - token = XRLIM_BURST_FACTOR * timeout; - if (token >= timeout) { - token -= timeout; - rc = 1; - } - dst->rate_tokens = token; - return rc; -} -EXPORT_SYMBOL(xrlim_allow); - -static inline int icmpv4_xrlim_allow(struct net *net, struct rtable *rt, +static inline bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt, int type, int code) { struct dst_entry *dst = &rt->dst; - int rc = 1; + bool rc = true; if (type > NR_ICMP_TYPES) goto out; @@ -288,8 +251,12 @@ static inline int icmpv4_xrlim_allow(struct net *net, struct rtable *rt, goto out; /* Limit if icmp type is enabled in ratemask. */ - if ((1 << type) & net->ipv4.sysctl_icmp_ratemask) - rc = xrlim_allow(dst, net->ipv4.sysctl_icmp_ratelimit); + if ((1 << type) & net->ipv4.sysctl_icmp_ratemask) { + if (!rt->peer) + rt_bind_peer(rt, 1); + rc = inet_peer_xrlim_allow(rt->peer, + net->ipv4.sysctl_icmp_ratelimit); + } out: return rc; } diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index b6513b13d729..709fbb4132d7 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -513,6 +513,8 @@ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) atomic_set(&p->ip_id_count, secure_ip_id(daddr->a4)); p->tcp_ts_stamp = 0; p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW; + p->rate_tokens = 0; + p->rate_last = 0; INIT_LIST_HEAD(&p->unused); @@ -580,3 +582,44 @@ void inet_putpeer(struct inet_peer *p) local_bh_enable(); } EXPORT_SYMBOL_GPL(inet_putpeer); + +/* + * Check transmit rate limitation for given message. + * The rate information is held in the inet_peer entries now. + * This function is generic and could be used for other purposes + * too. It uses a Token bucket filter as suggested by Alexey Kuznetsov. + * + * Note that the same inet_peer fields are modified by functions in + * route.c too, but these work for packet destinations while xrlim_allow + * works for icmp destinations. This means the rate limiting information + * for one "ip object" is shared - and these ICMPs are twice limited: + * by source and by destination. + * + * RFC 1812: 4.3.2.8 SHOULD be able to limit error message rate + * SHOULD allow setting of rate limits + * + * Shared between ICMPv4 and ICMPv6. + */ +#define XRLIM_BURST_FACTOR 6 +bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout) +{ + unsigned long now, token; + bool rc = false; + + if (!peer) + return true; + + token = peer->rate_tokens; + now = jiffies; + token += now - peer->rate_last; + peer->rate_last = now; + if (token > XRLIM_BURST_FACTOR * timeout) + token = XRLIM_BURST_FACTOR * timeout; + if (token >= timeout) { + token -= timeout; + rc = true; + } + peer->rate_tokens = token; + return rc; +} +EXPORT_SYMBOL(inet_peer_xrlim_allow); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 0ba6a382b2b4..2e225dafc4f8 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1563,6 +1563,7 @@ void ip_rt_send_redirect(struct sk_buff *skb) { struct rtable *rt = skb_rtable(skb); struct in_device *in_dev; + struct inet_peer *peer; int log_martians; rcu_read_lock(); @@ -1574,33 +1575,41 @@ void ip_rt_send_redirect(struct sk_buff *skb) log_martians = IN_DEV_LOG_MARTIANS(in_dev); rcu_read_unlock(); + if (!rt->peer) + rt_bind_peer(rt, 1); + peer = rt->peer; + if (!peer) { + icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, rt->rt_gateway); + return; + } + /* No redirected packets during ip_rt_redirect_silence; * reset the algorithm. */ - if (time_after(jiffies, rt->dst.rate_last + ip_rt_redirect_silence)) - rt->dst.rate_tokens = 0; + if (time_after(jiffies, peer->rate_last + ip_rt_redirect_silence)) + peer->rate_tokens = 0; /* Too many ignored redirects; do not send anything * set dst.rate_last to the last seen redirected packet. */ - if (rt->dst.rate_tokens >= ip_rt_redirect_number) { - rt->dst.rate_last = jiffies; + if (peer->rate_tokens >= ip_rt_redirect_number) { + peer->rate_last = jiffies; return; } /* Check for load limit; set rate_last to the latest sent * redirect. */ - if (rt->dst.rate_tokens == 0 || + if (peer->rate_tokens == 0 || time_after(jiffies, - (rt->dst.rate_last + - (ip_rt_redirect_load << rt->dst.rate_tokens)))) { + (peer->rate_last + + (ip_rt_redirect_load << peer->rate_tokens)))) { icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, rt->rt_gateway); - rt->dst.rate_last = jiffies; - ++rt->dst.rate_tokens; + peer->rate_last = jiffies; + ++peer->rate_tokens; #ifdef CONFIG_IP_ROUTE_VERBOSE if (log_martians && - rt->dst.rate_tokens == ip_rt_redirect_number && + peer->rate_tokens == ip_rt_redirect_number && net_ratelimit()) printk(KERN_WARNING "host %pI4/if%d ignores redirects for %pI4 to %pI4.\n", &rt->rt_src, rt->rt_iif, @@ -1612,7 +1621,9 @@ void ip_rt_send_redirect(struct sk_buff *skb) static int ip_error(struct sk_buff *skb) { struct rtable *rt = skb_rtable(skb); + struct inet_peer *peer; unsigned long now; + bool send; int code; switch (rt->dst.error) { @@ -1632,15 +1643,24 @@ static int ip_error(struct sk_buff *skb) break; } - now = jiffies; - rt->dst.rate_tokens += now - rt->dst.rate_last; - if (rt->dst.rate_tokens > ip_rt_error_burst) - rt->dst.rate_tokens = ip_rt_error_burst; - rt->dst.rate_last = now; - if (rt->dst.rate_tokens >= ip_rt_error_cost) { - rt->dst.rate_tokens -= ip_rt_error_cost; - icmp_send(skb, ICMP_DEST_UNREACH, code, 0); + if (!rt->peer) + rt_bind_peer(rt, 1); + peer = rt->peer; + + send = true; + if (peer) { + now = jiffies; + peer->rate_tokens += now - peer->rate_last; + if (peer->rate_tokens > ip_rt_error_burst) + peer->rate_tokens = ip_rt_error_burst; + peer->rate_last = now; + if (peer->rate_tokens >= ip_rt_error_cost) + peer->rate_tokens -= ip_rt_error_cost; + else + send = false; } + if (send) + icmp_send(skb, ICMP_DEST_UNREACH, code, 0); out: kfree_skb(skb); return 0; diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 03e62f94ff8e..a31d91b04c87 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -157,20 +157,20 @@ static int is_ineligible(struct sk_buff *skb) /* * Check the ICMP output rate limit */ -static inline int icmpv6_xrlim_allow(struct sock *sk, u8 type, - struct flowi *fl) +static inline bool icmpv6_xrlim_allow(struct sock *sk, u8 type, + struct flowi *fl) { struct dst_entry *dst; struct net *net = sock_net(sk); - int res = 0; + bool res = false; /* Informational messages are not limited. */ if (type & ICMPV6_INFOMSG_MASK) - return 1; + return true; /* Do not limit pmtu discovery, it would break it. */ if (type == ICMPV6_PKT_TOOBIG) - return 1; + return true; /* * Look up the output route. @@ -182,7 +182,7 @@ static inline int icmpv6_xrlim_allow(struct sock *sk, u8 type, IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); } else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) { - res = 1; + res = true; } else { struct rt6_info *rt = (struct rt6_info *)dst; int tmo = net->ipv6.sysctl.icmpv6_time; @@ -191,7 +191,9 @@ static inline int icmpv6_xrlim_allow(struct sock *sk, u8 type, if (rt->rt6i_dst.plen < 128) tmo >>= ((128 - rt->rt6i_dst.plen)>>5); - res = xrlim_allow(dst, tmo); + if (!rt->rt6i_peer) + rt6_bind_peer(rt, 1); + res = inet_peer_xrlim_allow(rt->rt6i_peer, tmo); } dst_release(dst); return res; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 5f8d242be3f3..2600e2288724 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -479,10 +479,13 @@ int ip6_forward(struct sk_buff *skb) else target = &hdr->daddr; + if (!rt->rt6i_peer) + rt6_bind_peer(rt, 1); + /* Limit redirects both by destination (here) and by source (inside ndisc_send_redirect) */ - if (xrlim_allow(dst, 1*HZ)) + if (inet_peer_xrlim_allow(rt->rt6i_peer, 1*HZ)) ndisc_send_redirect(skb, n, target); } else { int addrtype = ipv6_addr_type(&hdr->saddr); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 2342545a5ee9..7254ce364006 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1553,7 +1553,9 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, "ICMPv6 Redirect: destination is not a neighbour.\n"); goto release; } - if (!xrlim_allow(dst, 1*HZ)) + if (!rt->rt6i_peer) + rt6_bind_peer(rt, 1); + if (inet_peer_xrlim_allow(rt->rt6i_peer, 1*HZ)) goto release; if (dev->addr_len) { -- cgit v1.2.3 From 7eb38527c4e485923fa3f87d11ce11b4e6ebf807 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 5 Feb 2011 18:13:45 -0800 Subject: tcp: Add reference to initial CWND ietf draft. Suggested by Alexander Zimmermann Signed-off-by: David S. Miller --- include/net/tcp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 7118668ad534..adfe6dbe9053 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -196,7 +196,7 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo); /* TCP thin-stream limits */ #define TCP_THIN_LINEAR_RETRIES 6 /* After 6 linear retries, do exp. backoff */ -/* TCP initial congestion window */ +/* TCP initial congestion window as per draft-hkchu-tcpm-initcwnd-01 */ #define TCP_INIT_CWND 10 extern struct inet_timewait_death_row tcp_death_row; -- cgit v1.2.3 From 7f50684717511d30bba180902105c4cd4efca732 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 25 Jan 2011 23:17:16 +0100 Subject: drm: remove i830 driver This driver is one of the last users of the big kernel lock, which is going away. All the hardware supported by this driver also works with the newer i915 driver, and recent X.org releases only work with that driver anyway. Signed-off-by: Arnd Bergmann Cc: Chris Wilson Cc: dri-devel@lists.freedesktop.org Signed-off-by: Dave Airlie --- drivers/gpu/drm/Kconfig | 43 +- drivers/gpu/drm/Makefile | 1 - drivers/gpu/drm/i830/Makefile | 8 - drivers/gpu/drm/i830/i830_dma.c | 1560 --------------------------------------- drivers/gpu/drm/i830/i830_drv.c | 107 --- drivers/gpu/drm/i830/i830_drv.h | 295 -------- drivers/gpu/drm/i830/i830_irq.c | 186 ----- include/drm/Kbuild | 1 - include/drm/i830_drm.h | 342 --------- 9 files changed, 17 insertions(+), 2526 deletions(-) delete mode 100644 drivers/gpu/drm/i830/Makefile delete mode 100644 drivers/gpu/drm/i830/i830_dma.c delete mode 100644 drivers/gpu/drm/i830/i830_drv.c delete mode 100644 drivers/gpu/drm/i830/i830_drv.h delete mode 100644 drivers/gpu/drm/i830/i830_irq.c delete mode 100644 include/drm/i830_drm.h (limited to 'include') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 0902d4460039..44588764a524 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -80,25 +80,10 @@ config DRM_I810 selected, the module will be called i810. AGP support is required for this driver to work. -choice - prompt "Intel 830M, 845G, 852GM, 855GM, 865G" - depends on DRM && AGP && AGP_INTEL - optional - -config DRM_I830 - tristate "i830 driver" - # BKL usage in order to avoid AB-BA deadlocks, i830 may get removed - depends on BKL - help - Choose this option if you have a system that has Intel 830M, 845G, - 852GM, 855GM or 865G integrated graphics. If M is selected, the - module will be called i830. AGP support is required for this driver - to work. This driver is used by the older X releases X.org 6.7 and - XFree86 4.3. If unsure, build this and i915 as modules and the X server - will load the correct one. - config DRM_I915 - tristate "i915 driver" + tristate "Intel 8xx/9xx/G3x/G4x/HD Graphics" + depends on DRM + depends on AGP depends on AGP_INTEL # we need shmfs for the swappable backing store, and in particular # the shmem_readpage() which depends upon tmpfs @@ -115,12 +100,20 @@ config DRM_I915 select ACPI_VIDEO if ACPI select ACPI_BUTTON if ACPI help - Choose this option if you have a system that has Intel 830M, 845G, - 852GM, 855GM 865G or 915G integrated graphics. If M is selected, the - module will be called i915. AGP support is required for this driver - to work. This driver is used by the Intel driver in X.org 6.8 and - XFree86 4.4 and above. If unsure, build this and i830 as modules and - the X server will load the correct one. + Choose this option if you have a system that has "Intel Graphics + Media Accelerator" or "HD Graphics" integrated graphics, + including 830M, 845G, 852GM, 855GM, 865G, 915G, 945G, 965G, + G35, G41, G43, G45 chipsets and Celeron, Pentium, Core i3, + Core i5, Core i7 as well as Atom CPUs with integrated graphics. + If M is selected, the module will be called i915. AGP support + is required for this driver to work. This driver is used by + the Intel driver in X.org 6.8 and XFree86 4.4 and above. It + replaces the older i830 module that supported a subset of the + hardware in older X.org releases. + + Note that the older i810/i815 chipsets require the use of the + i810 driver instead, and the Atom z5xx series has an entirely + different implementation. config DRM_I915_KMS bool "Enable modesetting on intel by default" @@ -132,8 +125,6 @@ config DRM_I915_KMS the driver to bind to PCI devices, which precludes loading things like intelfb. -endchoice - config DRM_MGA tristate "Matrox g200/g400" depends on DRM && PCI diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 997c43d04909..d9cb3e314321 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -29,7 +29,6 @@ obj-$(CONFIG_DRM_R128) += r128/ obj-$(CONFIG_DRM_RADEON)+= radeon/ obj-$(CONFIG_DRM_MGA) += mga/ obj-$(CONFIG_DRM_I810) += i810/ -obj-$(CONFIG_DRM_I830) += i830/ obj-$(CONFIG_DRM_I915) += i915/ obj-$(CONFIG_DRM_SIS) += sis/ obj-$(CONFIG_DRM_SAVAGE)+= savage/ diff --git a/drivers/gpu/drm/i830/Makefile b/drivers/gpu/drm/i830/Makefile deleted file mode 100644 index c642ee0b238c..000000000000 --- a/drivers/gpu/drm/i830/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# Makefile for the drm device driver. This driver provides support for the -# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. - -ccflags-y := -Iinclude/drm -i830-y := i830_drv.o i830_dma.o i830_irq.o - -obj-$(CONFIG_DRM_I830) += i830.o diff --git a/drivers/gpu/drm/i830/i830_dma.c b/drivers/gpu/drm/i830/i830_dma.c deleted file mode 100644 index ca6f31ff0eec..000000000000 --- a/drivers/gpu/drm/i830/i830_dma.c +++ /dev/null @@ -1,1560 +0,0 @@ -/* i830_dma.c -- DMA support for the I830 -*- linux-c -*- - * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com - * - * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: Rickard E. (Rik) Faith - * Jeff Hartmann - * Keith Whitwell - * Abraham vd Merwe - * - */ - -#include "drmP.h" -#include "drm.h" -#include "i830_drm.h" -#include "i830_drv.h" -#include /* For task queue support */ -#include -#include -#include -#include -#include - -#define I830_BUF_FREE 2 -#define I830_BUF_CLIENT 1 -#define I830_BUF_HARDWARE 0 - -#define I830_BUF_UNMAPPED 0 -#define I830_BUF_MAPPED 1 - -static struct drm_buf *i830_freelist_get(struct drm_device * dev) -{ - struct drm_device_dma *dma = dev->dma; - int i; - int used; - - /* Linear search might not be the best solution */ - - for (i = 0; i < dma->buf_count; i++) { - struct drm_buf *buf = dma->buflist[i]; - drm_i830_buf_priv_t *buf_priv = buf->dev_private; - /* In use is already a pointer */ - used = cmpxchg(buf_priv->in_use, I830_BUF_FREE, - I830_BUF_CLIENT); - if (used == I830_BUF_FREE) - return buf; - } - return NULL; -} - -/* This should only be called if the buffer is not sent to the hardware - * yet, the hardware updates in use for us once its on the ring buffer. - */ - -static int i830_freelist_put(struct drm_device *dev, struct drm_buf *buf) -{ - drm_i830_buf_priv_t *buf_priv = buf->dev_private; - int used; - - /* In use is already a pointer */ - used = cmpxchg(buf_priv->in_use, I830_BUF_CLIENT, I830_BUF_FREE); - if (used != I830_BUF_CLIENT) { - DRM_ERROR("Freeing buffer thats not in use : %d\n", buf->idx); - return -EINVAL; - } - - return 0; -} - -static int i830_mmap_buffers(struct file *filp, struct vm_area_struct *vma) -{ - struct drm_file *priv = filp->private_data; - struct drm_device *dev; - drm_i830_private_t *dev_priv; - struct drm_buf *buf; - drm_i830_buf_priv_t *buf_priv; - - lock_kernel(); - dev = priv->minor->dev; - dev_priv = dev->dev_private; - buf = dev_priv->mmap_buffer; - buf_priv = buf->dev_private; - - vma->vm_flags |= (VM_IO | VM_DONTCOPY); - vma->vm_file = filp; - - buf_priv->currently_mapped = I830_BUF_MAPPED; - unlock_kernel(); - - if (io_remap_pfn_range(vma, vma->vm_start, - vma->vm_pgoff, - vma->vm_end - vma->vm_start, vma->vm_page_prot)) - return -EAGAIN; - return 0; -} - -static const struct file_operations i830_buffer_fops = { - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = i830_ioctl, - .mmap = i830_mmap_buffers, - .fasync = drm_fasync, - .llseek = noop_llseek, -}; - -static int i830_map_buffer(struct drm_buf *buf, struct drm_file *file_priv) -{ - struct drm_device *dev = file_priv->minor->dev; - drm_i830_buf_priv_t *buf_priv = buf->dev_private; - drm_i830_private_t *dev_priv = dev->dev_private; - const struct file_operations *old_fops; - unsigned long virtual; - int retcode = 0; - - if (buf_priv->currently_mapped == I830_BUF_MAPPED) - return -EINVAL; - - down_write(¤t->mm->mmap_sem); - old_fops = file_priv->filp->f_op; - file_priv->filp->f_op = &i830_buffer_fops; - dev_priv->mmap_buffer = buf; - virtual = do_mmap(file_priv->filp, 0, buf->total, PROT_READ | PROT_WRITE, - MAP_SHARED, buf->bus_address); - dev_priv->mmap_buffer = NULL; - file_priv->filp->f_op = old_fops; - if (IS_ERR((void *)virtual)) { /* ugh */ - /* Real error */ - DRM_ERROR("mmap error\n"); - retcode = PTR_ERR((void *)virtual); - buf_priv->virtual = NULL; - } else { - buf_priv->virtual = (void __user *)virtual; - } - up_write(¤t->mm->mmap_sem); - - return retcode; -} - -static int i830_unmap_buffer(struct drm_buf *buf) -{ - drm_i830_buf_priv_t *buf_priv = buf->dev_private; - int retcode = 0; - - if (buf_priv->currently_mapped != I830_BUF_MAPPED) - return -EINVAL; - - down_write(¤t->mm->mmap_sem); - retcode = do_munmap(current->mm, - (unsigned long)buf_priv->virtual, - (size_t) buf->total); - up_write(¤t->mm->mmap_sem); - - buf_priv->currently_mapped = I830_BUF_UNMAPPED; - buf_priv->virtual = NULL; - - return retcode; -} - -static int i830_dma_get_buffer(struct drm_device *dev, drm_i830_dma_t *d, - struct drm_file *file_priv) -{ - struct drm_buf *buf; - drm_i830_buf_priv_t *buf_priv; - int retcode = 0; - - buf = i830_freelist_get(dev); - if (!buf) { - retcode = -ENOMEM; - DRM_DEBUG("retcode=%d\n", retcode); - return retcode; - } - - retcode = i830_map_buffer(buf, file_priv); - if (retcode) { - i830_freelist_put(dev, buf); - DRM_ERROR("mapbuf failed, retcode %d\n", retcode); - return retcode; - } - buf->file_priv = file_priv; - buf_priv = buf->dev_private; - d->granted = 1; - d->request_idx = buf->idx; - d->request_size = buf->total; - d->virtual = buf_priv->virtual; - - return retcode; -} - -static int i830_dma_cleanup(struct drm_device *dev) -{ - struct drm_device_dma *dma = dev->dma; - - /* Make sure interrupts are disabled here because the uninstall ioctl - * may not have been called from userspace and after dev_private - * is freed, it's too late. - */ - if (dev->irq_enabled) - drm_irq_uninstall(dev); - - if (dev->dev_private) { - int i; - drm_i830_private_t *dev_priv = - (drm_i830_private_t *) dev->dev_private; - - if (dev_priv->ring.virtual_start) - drm_core_ioremapfree(&dev_priv->ring.map, dev); - if (dev_priv->hw_status_page) { - pci_free_consistent(dev->pdev, PAGE_SIZE, - dev_priv->hw_status_page, - dev_priv->dma_status_page); - /* Need to rewrite hardware status page */ - I830_WRITE(0x02080, 0x1ffff000); - } - - kfree(dev->dev_private); - dev->dev_private = NULL; - - for (i = 0; i < dma->buf_count; i++) { - struct drm_buf *buf = dma->buflist[i]; - drm_i830_buf_priv_t *buf_priv = buf->dev_private; - if (buf_priv->kernel_virtual && buf->total) - drm_core_ioremapfree(&buf_priv->map, dev); - } - } - return 0; -} - -int i830_wait_ring(struct drm_device *dev, int n, const char *caller) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - drm_i830_ring_buffer_t *ring = &(dev_priv->ring); - int iters = 0; - unsigned long end; - unsigned int last_head = I830_READ(LP_RING + RING_HEAD) & HEAD_ADDR; - - end = jiffies + (HZ * 3); - while (ring->space < n) { - ring->head = I830_READ(LP_RING + RING_HEAD) & HEAD_ADDR; - ring->space = ring->head - (ring->tail + 8); - if (ring->space < 0) - ring->space += ring->Size; - - if (ring->head != last_head) { - end = jiffies + (HZ * 3); - last_head = ring->head; - } - - iters++; - if (time_before(end, jiffies)) { - DRM_ERROR("space: %d wanted %d\n", ring->space, n); - DRM_ERROR("lockup\n"); - goto out_wait_ring; - } - udelay(1); - dev_priv->sarea_priv->perf_boxes |= I830_BOX_WAIT; - } - -out_wait_ring: - return iters; -} - -static void i830_kernel_lost_context(struct drm_device *dev) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - drm_i830_ring_buffer_t *ring = &(dev_priv->ring); - - ring->head = I830_READ(LP_RING + RING_HEAD) & HEAD_ADDR; - ring->tail = I830_READ(LP_RING + RING_TAIL) & TAIL_ADDR; - ring->space = ring->head - (ring->tail + 8); - if (ring->space < 0) - ring->space += ring->Size; - - if (ring->head == ring->tail) - dev_priv->sarea_priv->perf_boxes |= I830_BOX_RING_EMPTY; -} - -static int i830_freelist_init(struct drm_device *dev, drm_i830_private_t *dev_priv) -{ - struct drm_device_dma *dma = dev->dma; - int my_idx = 36; - u32 *hw_status = (u32 *) (dev_priv->hw_status_page + my_idx); - int i; - - if (dma->buf_count > 1019) { - /* Not enough space in the status page for the freelist */ - return -EINVAL; - } - - for (i = 0; i < dma->buf_count; i++) { - struct drm_buf *buf = dma->buflist[i]; - drm_i830_buf_priv_t *buf_priv = buf->dev_private; - - buf_priv->in_use = hw_status++; - buf_priv->my_use_idx = my_idx; - my_idx += 4; - - *buf_priv->in_use = I830_BUF_FREE; - - buf_priv->map.offset = buf->bus_address; - buf_priv->map.size = buf->total; - buf_priv->map.type = _DRM_AGP; - buf_priv->map.flags = 0; - buf_priv->map.mtrr = 0; - - drm_core_ioremap(&buf_priv->map, dev); - buf_priv->kernel_virtual = buf_priv->map.handle; - } - return 0; -} - -static int i830_dma_initialize(struct drm_device *dev, - drm_i830_private_t *dev_priv, - drm_i830_init_t *init) -{ - struct drm_map_list *r_list; - - memset(dev_priv, 0, sizeof(drm_i830_private_t)); - - list_for_each_entry(r_list, &dev->maplist, head) { - if (r_list->map && - r_list->map->type == _DRM_SHM && - r_list->map->flags & _DRM_CONTAINS_LOCK) { - dev_priv->sarea_map = r_list->map; - break; - } - } - - if (!dev_priv->sarea_map) { - dev->dev_private = (void *)dev_priv; - i830_dma_cleanup(dev); - DRM_ERROR("can not find sarea!\n"); - return -EINVAL; - } - dev_priv->mmio_map = drm_core_findmap(dev, init->mmio_offset); - if (!dev_priv->mmio_map) { - dev->dev_private = (void *)dev_priv; - i830_dma_cleanup(dev); - DRM_ERROR("can not find mmio map!\n"); - return -EINVAL; - } - dev->agp_buffer_token = init->buffers_offset; - dev->agp_buffer_map = drm_core_findmap(dev, init->buffers_offset); - if (!dev->agp_buffer_map) { - dev->dev_private = (void *)dev_priv; - i830_dma_cleanup(dev); - DRM_ERROR("can not find dma buffer map!\n"); - return -EINVAL; - } - - dev_priv->sarea_priv = (drm_i830_sarea_t *) - ((u8 *) dev_priv->sarea_map->handle + init->sarea_priv_offset); - - dev_priv->ring.Start = init->ring_start; - dev_priv->ring.End = init->ring_end; - dev_priv->ring.Size = init->ring_size; - - dev_priv->ring.map.offset = dev->agp->base + init->ring_start; - dev_priv->ring.map.size = init->ring_size; - dev_priv->ring.map.type = _DRM_AGP; - dev_priv->ring.map.flags = 0; - dev_priv->ring.map.mtrr = 0; - - drm_core_ioremap(&dev_priv->ring.map, dev); - - if (dev_priv->ring.map.handle == NULL) { - dev->dev_private = (void *)dev_priv; - i830_dma_cleanup(dev); - DRM_ERROR("can not ioremap virtual address for" - " ring buffer\n"); - return -ENOMEM; - } - - dev_priv->ring.virtual_start = dev_priv->ring.map.handle; - - dev_priv->ring.tail_mask = dev_priv->ring.Size - 1; - - dev_priv->w = init->w; - dev_priv->h = init->h; - dev_priv->pitch = init->pitch; - dev_priv->back_offset = init->back_offset; - dev_priv->depth_offset = init->depth_offset; - dev_priv->front_offset = init->front_offset; - - dev_priv->front_di1 = init->front_offset | init->pitch_bits; - dev_priv->back_di1 = init->back_offset | init->pitch_bits; - dev_priv->zi1 = init->depth_offset | init->pitch_bits; - - DRM_DEBUG("front_di1 %x\n", dev_priv->front_di1); - DRM_DEBUG("back_offset %x\n", dev_priv->back_offset); - DRM_DEBUG("back_di1 %x\n", dev_priv->back_di1); - DRM_DEBUG("pitch_bits %x\n", init->pitch_bits); - - dev_priv->cpp = init->cpp; - /* We are using separate values as placeholders for mechanisms for - * private backbuffer/depthbuffer usage. - */ - - dev_priv->back_pitch = init->back_pitch; - dev_priv->depth_pitch = init->depth_pitch; - dev_priv->do_boxes = 0; - dev_priv->use_mi_batchbuffer_start = 0; - - /* Program Hardware Status Page */ - dev_priv->hw_status_page = - pci_alloc_consistent(dev->pdev, PAGE_SIZE, - &dev_priv->dma_status_page); - if (!dev_priv->hw_status_page) { - dev->dev_private = (void *)dev_priv; - i830_dma_cleanup(dev); - DRM_ERROR("Can not allocate hardware status page\n"); - return -ENOMEM; - } - memset(dev_priv->hw_status_page, 0, PAGE_SIZE); - DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page); - - I830_WRITE(0x02080, dev_priv->dma_status_page); - DRM_DEBUG("Enabled hardware status page\n"); - - /* Now we need to init our freelist */ - if (i830_freelist_init(dev, dev_priv) != 0) { - dev->dev_private = (void *)dev_priv; - i830_dma_cleanup(dev); - DRM_ERROR("Not enough space in the status page for" - " the freelist\n"); - return -ENOMEM; - } - dev->dev_private = (void *)dev_priv; - - return 0; -} - -static int i830_dma_init(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i830_private_t *dev_priv; - drm_i830_init_t *init = data; - int retcode = 0; - - switch (init->func) { - case I830_INIT_DMA: - dev_priv = kmalloc(sizeof(drm_i830_private_t), GFP_KERNEL); - if (dev_priv == NULL) - return -ENOMEM; - retcode = i830_dma_initialize(dev, dev_priv, init); - break; - case I830_CLEANUP_DMA: - retcode = i830_dma_cleanup(dev); - break; - default: - retcode = -EINVAL; - break; - } - - return retcode; -} - -#define GFX_OP_STIPPLE ((0x3<<29)|(0x1d<<24)|(0x83<<16)) -#define ST1_ENABLE (1<<16) -#define ST1_MASK (0xffff) - -/* Most efficient way to verify state for the i830 is as it is - * emitted. Non-conformant state is silently dropped. - */ -static void i830EmitContextVerified(struct drm_device *dev, unsigned int *code) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - int i, j = 0; - unsigned int tmp; - RING_LOCALS; - - BEGIN_LP_RING(I830_CTX_SETUP_SIZE + 4); - - for (i = 0; i < I830_CTXREG_BLENDCOLR0; i++) { - tmp = code[i]; - if ((tmp & (7 << 29)) == CMD_3D && - (tmp & (0x1f << 24)) < (0x1d << 24)) { - OUT_RING(tmp); - j++; - } else { - DRM_ERROR("Skipping %d\n", i); - } - } - - OUT_RING(STATE3D_CONST_BLEND_COLOR_CMD); - OUT_RING(code[I830_CTXREG_BLENDCOLR]); - j += 2; - - for (i = I830_CTXREG_VF; i < I830_CTXREG_MCSB0; i++) { - tmp = code[i]; - if ((tmp & (7 << 29)) == CMD_3D && - (tmp & (0x1f << 24)) < (0x1d << 24)) { - OUT_RING(tmp); - j++; - } else { - DRM_ERROR("Skipping %d\n", i); - } - } - - OUT_RING(STATE3D_MAP_COORD_SETBIND_CMD); - OUT_RING(code[I830_CTXREG_MCSB1]); - j += 2; - - if (j & 1) - OUT_RING(0); - - ADVANCE_LP_RING(); -} - -static void i830EmitTexVerified(struct drm_device *dev, unsigned int *code) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - int i, j = 0; - unsigned int tmp; - RING_LOCALS; - - if (code[I830_TEXREG_MI0] == GFX_OP_MAP_INFO || - (code[I830_TEXREG_MI0] & ~(0xf * LOAD_TEXTURE_MAP0)) == - (STATE3D_LOAD_STATE_IMMEDIATE_2 | 4)) { - - BEGIN_LP_RING(I830_TEX_SETUP_SIZE); - - OUT_RING(code[I830_TEXREG_MI0]); /* TM0LI */ - OUT_RING(code[I830_TEXREG_MI1]); /* TM0S0 */ - OUT_RING(code[I830_TEXREG_MI2]); /* TM0S1 */ - OUT_RING(code[I830_TEXREG_MI3]); /* TM0S2 */ - OUT_RING(code[I830_TEXREG_MI4]); /* TM0S3 */ - OUT_RING(code[I830_TEXREG_MI5]); /* TM0S4 */ - - for (i = 6; i < I830_TEX_SETUP_SIZE; i++) { - tmp = code[i]; - OUT_RING(tmp); - j++; - } - - if (j & 1) - OUT_RING(0); - - ADVANCE_LP_RING(); - } else - printk("rejected packet %x\n", code[0]); -} - -static void i830EmitTexBlendVerified(struct drm_device *dev, - unsigned int *code, unsigned int num) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - int i, j = 0; - unsigned int tmp; - RING_LOCALS; - - if (!num) - return; - - BEGIN_LP_RING(num + 1); - - for (i = 0; i < num; i++) { - tmp = code[i]; - OUT_RING(tmp); - j++; - } - - if (j & 1) - OUT_RING(0); - - ADVANCE_LP_RING(); -} - -static void i830EmitTexPalette(struct drm_device *dev, - unsigned int *palette, int number, int is_shared) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - int i; - RING_LOCALS; - - return; - - BEGIN_LP_RING(258); - - if (is_shared == 1) { - OUT_RING(CMD_OP_MAP_PALETTE_LOAD | - MAP_PALETTE_NUM(0) | MAP_PALETTE_BOTH); - } else { - OUT_RING(CMD_OP_MAP_PALETTE_LOAD | MAP_PALETTE_NUM(number)); - } - for (i = 0; i < 256; i++) - OUT_RING(palette[i]); - OUT_RING(0); - /* KW: WHERE IS THE ADVANCE_LP_RING? This is effectively a noop! - */ -} - -/* Need to do some additional checking when setting the dest buffer. - */ -static void i830EmitDestVerified(struct drm_device *dev, unsigned int *code) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - unsigned int tmp; - RING_LOCALS; - - BEGIN_LP_RING(I830_DEST_SETUP_SIZE + 10); - - tmp = code[I830_DESTREG_CBUFADDR]; - if (tmp == dev_priv->front_di1 || tmp == dev_priv->back_di1) { - if (((int)outring) & 8) { - OUT_RING(0); - OUT_RING(0); - } - - OUT_RING(CMD_OP_DESTBUFFER_INFO); - OUT_RING(BUF_3D_ID_COLOR_BACK | - BUF_3D_PITCH(dev_priv->back_pitch * dev_priv->cpp) | - BUF_3D_USE_FENCE); - OUT_RING(tmp); - OUT_RING(0); - - OUT_RING(CMD_OP_DESTBUFFER_INFO); - OUT_RING(BUF_3D_ID_DEPTH | BUF_3D_USE_FENCE | - BUF_3D_PITCH(dev_priv->depth_pitch * dev_priv->cpp)); - OUT_RING(dev_priv->zi1); - OUT_RING(0); - } else { - DRM_ERROR("bad di1 %x (allow %x or %x)\n", - tmp, dev_priv->front_di1, dev_priv->back_di1); - } - - /* invarient: - */ - - OUT_RING(GFX_OP_DESTBUFFER_VARS); - OUT_RING(code[I830_DESTREG_DV1]); - - OUT_RING(GFX_OP_DRAWRECT_INFO); - OUT_RING(code[I830_DESTREG_DR1]); - OUT_RING(code[I830_DESTREG_DR2]); - OUT_RING(code[I830_DESTREG_DR3]); - OUT_RING(code[I830_DESTREG_DR4]); - - /* Need to verify this */ - tmp = code[I830_DESTREG_SENABLE]; - if ((tmp & ~0x3) == GFX_OP_SCISSOR_ENABLE) { - OUT_RING(tmp); - } else { - DRM_ERROR("bad scissor enable\n"); - OUT_RING(0); - } - - OUT_RING(GFX_OP_SCISSOR_RECT); - OUT_RING(code[I830_DESTREG_SR1]); - OUT_RING(code[I830_DESTREG_SR2]); - OUT_RING(0); - - ADVANCE_LP_RING(); -} - -static void i830EmitStippleVerified(struct drm_device *dev, unsigned int *code) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - RING_LOCALS; - - BEGIN_LP_RING(2); - OUT_RING(GFX_OP_STIPPLE); - OUT_RING(code[1]); - ADVANCE_LP_RING(); -} - -static void i830EmitState(struct drm_device *dev) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - drm_i830_sarea_t *sarea_priv = dev_priv->sarea_priv; - unsigned int dirty = sarea_priv->dirty; - - DRM_DEBUG("%s %x\n", __func__, dirty); - - if (dirty & I830_UPLOAD_BUFFERS) { - i830EmitDestVerified(dev, sarea_priv->BufferState); - sarea_priv->dirty &= ~I830_UPLOAD_BUFFERS; - } - - if (dirty & I830_UPLOAD_CTX) { - i830EmitContextVerified(dev, sarea_priv->ContextState); - sarea_priv->dirty &= ~I830_UPLOAD_CTX; - } - - if (dirty & I830_UPLOAD_TEX0) { - i830EmitTexVerified(dev, sarea_priv->TexState[0]); - sarea_priv->dirty &= ~I830_UPLOAD_TEX0; - } - - if (dirty & I830_UPLOAD_TEX1) { - i830EmitTexVerified(dev, sarea_priv->TexState[1]); - sarea_priv->dirty &= ~I830_UPLOAD_TEX1; - } - - if (dirty & I830_UPLOAD_TEXBLEND0) { - i830EmitTexBlendVerified(dev, sarea_priv->TexBlendState[0], - sarea_priv->TexBlendStateWordsUsed[0]); - sarea_priv->dirty &= ~I830_UPLOAD_TEXBLEND0; - } - - if (dirty & I830_UPLOAD_TEXBLEND1) { - i830EmitTexBlendVerified(dev, sarea_priv->TexBlendState[1], - sarea_priv->TexBlendStateWordsUsed[1]); - sarea_priv->dirty &= ~I830_UPLOAD_TEXBLEND1; - } - - if (dirty & I830_UPLOAD_TEX_PALETTE_SHARED) { - i830EmitTexPalette(dev, sarea_priv->Palette[0], 0, 1); - } else { - if (dirty & I830_UPLOAD_TEX_PALETTE_N(0)) { - i830EmitTexPalette(dev, sarea_priv->Palette[0], 0, 0); - sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(0); - } - if (dirty & I830_UPLOAD_TEX_PALETTE_N(1)) { - i830EmitTexPalette(dev, sarea_priv->Palette[1], 1, 0); - sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(1); - } - - /* 1.3: - */ -#if 0 - if (dirty & I830_UPLOAD_TEX_PALETTE_N(2)) { - i830EmitTexPalette(dev, sarea_priv->Palette2[0], 0, 0); - sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(2); - } - if (dirty & I830_UPLOAD_TEX_PALETTE_N(3)) { - i830EmitTexPalette(dev, sarea_priv->Palette2[1], 1, 0); - sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(2); - } -#endif - } - - /* 1.3: - */ - if (dirty & I830_UPLOAD_STIPPLE) { - i830EmitStippleVerified(dev, sarea_priv->StippleState); - sarea_priv->dirty &= ~I830_UPLOAD_STIPPLE; - } - - if (dirty & I830_UPLOAD_TEX2) { - i830EmitTexVerified(dev, sarea_priv->TexState2); - sarea_priv->dirty &= ~I830_UPLOAD_TEX2; - } - - if (dirty & I830_UPLOAD_TEX3) { - i830EmitTexVerified(dev, sarea_priv->TexState3); - sarea_priv->dirty &= ~I830_UPLOAD_TEX3; - } - - if (dirty & I830_UPLOAD_TEXBLEND2) { - i830EmitTexBlendVerified(dev, - sarea_priv->TexBlendState2, - sarea_priv->TexBlendStateWordsUsed2); - - sarea_priv->dirty &= ~I830_UPLOAD_TEXBLEND2; - } - - if (dirty & I830_UPLOAD_TEXBLEND3) { - i830EmitTexBlendVerified(dev, - sarea_priv->TexBlendState3, - sarea_priv->TexBlendStateWordsUsed3); - sarea_priv->dirty &= ~I830_UPLOAD_TEXBLEND3; - } -} - -/* ================================================================ - * Performance monitoring functions - */ - -static void i830_fill_box(struct drm_device *dev, - int x, int y, int w, int h, int r, int g, int b) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - u32 color; - unsigned int BR13, CMD; - RING_LOCALS; - - BR13 = (0xF0 << 16) | (dev_priv->pitch * dev_priv->cpp) | (1 << 24); - CMD = XY_COLOR_BLT_CMD; - x += dev_priv->sarea_priv->boxes[0].x1; - y += dev_priv->sarea_priv->boxes[0].y1; - - if (dev_priv->cpp == 4) { - BR13 |= (1 << 25); - CMD |= (XY_COLOR_BLT_WRITE_ALPHA | XY_COLOR_BLT_WRITE_RGB); - color = (((0xff) << 24) | (r << 16) | (g << 8) | b); - } else { - color = (((r & 0xf8) << 8) | - ((g & 0xfc) << 3) | ((b & 0xf8) >> 3)); - } - - BEGIN_LP_RING(6); - OUT_RING(CMD); - OUT_RING(BR13); - OUT_RING((y << 16) | x); - OUT_RING(((y + h) << 16) | (x + w)); - - if (dev_priv->current_page == 1) - OUT_RING(dev_priv->front_offset); - else - OUT_RING(dev_priv->back_offset); - - OUT_RING(color); - ADVANCE_LP_RING(); -} - -static void i830_cp_performance_boxes(struct drm_device *dev) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - - /* Purple box for page flipping - */ - if (dev_priv->sarea_priv->perf_boxes & I830_BOX_FLIP) - i830_fill_box(dev, 4, 4, 8, 8, 255, 0, 255); - - /* Red box if we have to wait for idle at any point - */ - if (dev_priv->sarea_priv->perf_boxes & I830_BOX_WAIT) - i830_fill_box(dev, 16, 4, 8, 8, 255, 0, 0); - - /* Blue box: lost context? - */ - if (dev_priv->sarea_priv->perf_boxes & I830_BOX_LOST_CONTEXT) - i830_fill_box(dev, 28, 4, 8, 8, 0, 0, 255); - - /* Yellow box for texture swaps - */ - if (dev_priv->sarea_priv->perf_boxes & I830_BOX_TEXTURE_LOAD) - i830_fill_box(dev, 40, 4, 8, 8, 255, 255, 0); - - /* Green box if hardware never idles (as far as we can tell) - */ - if (!(dev_priv->sarea_priv->perf_boxes & I830_BOX_RING_EMPTY)) - i830_fill_box(dev, 64, 4, 8, 8, 0, 255, 0); - - /* Draw bars indicating number of buffers allocated - * (not a great measure, easily confused) - */ - if (dev_priv->dma_used) { - int bar = dev_priv->dma_used / 10240; - if (bar > 100) - bar = 100; - if (bar < 1) - bar = 1; - i830_fill_box(dev, 4, 16, bar, 4, 196, 128, 128); - dev_priv->dma_used = 0; - } - - dev_priv->sarea_priv->perf_boxes = 0; -} - -static void i830_dma_dispatch_clear(struct drm_device *dev, int flags, - unsigned int clear_color, - unsigned int clear_zval, - unsigned int clear_depthmask) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - drm_i830_sarea_t *sarea_priv = dev_priv->sarea_priv; - int nbox = sarea_priv->nbox; - struct drm_clip_rect *pbox = sarea_priv->boxes; - int pitch = dev_priv->pitch; - int cpp = dev_priv->cpp; - int i; - unsigned int BR13, CMD, D_CMD; - RING_LOCALS; - - if (dev_priv->current_page == 1) { - unsigned int tmp = flags; - - flags &= ~(I830_FRONT | I830_BACK); - if (tmp & I830_FRONT) - flags |= I830_BACK; - if (tmp & I830_BACK) - flags |= I830_FRONT; - } - - i830_kernel_lost_context(dev); - - switch (cpp) { - case 2: - BR13 = (0xF0 << 16) | (pitch * cpp) | (1 << 24); - D_CMD = CMD = XY_COLOR_BLT_CMD; - break; - case 4: - BR13 = (0xF0 << 16) | (pitch * cpp) | (1 << 24) | (1 << 25); - CMD = (XY_COLOR_BLT_CMD | XY_COLOR_BLT_WRITE_ALPHA | - XY_COLOR_BLT_WRITE_RGB); - D_CMD = XY_COLOR_BLT_CMD; - if (clear_depthmask & 0x00ffffff) - D_CMD |= XY_COLOR_BLT_WRITE_RGB; - if (clear_depthmask & 0xff000000) - D_CMD |= XY_COLOR_BLT_WRITE_ALPHA; - break; - default: - BR13 = (0xF0 << 16) | (pitch * cpp) | (1 << 24); - D_CMD = CMD = XY_COLOR_BLT_CMD; - break; - } - - if (nbox > I830_NR_SAREA_CLIPRECTS) - nbox = I830_NR_SAREA_CLIPRECTS; - - for (i = 0; i < nbox; i++, pbox++) { - if (pbox->x1 > pbox->x2 || - pbox->y1 > pbox->y2 || - pbox->x2 > dev_priv->w || pbox->y2 > dev_priv->h) - continue; - - if (flags & I830_FRONT) { - DRM_DEBUG("clear front\n"); - BEGIN_LP_RING(6); - OUT_RING(CMD); - OUT_RING(BR13); - OUT_RING((pbox->y1 << 16) | pbox->x1); - OUT_RING((pbox->y2 << 16) | pbox->x2); - OUT_RING(dev_priv->front_offset); - OUT_RING(clear_color); - ADVANCE_LP_RING(); - } - - if (flags & I830_BACK) { - DRM_DEBUG("clear back\n"); - BEGIN_LP_RING(6); - OUT_RING(CMD); - OUT_RING(BR13); - OUT_RING((pbox->y1 << 16) | pbox->x1); - OUT_RING((pbox->y2 << 16) | pbox->x2); - OUT_RING(dev_priv->back_offset); - OUT_RING(clear_color); - ADVANCE_LP_RING(); - } - - if (flags & I830_DEPTH) { - DRM_DEBUG("clear depth\n"); - BEGIN_LP_RING(6); - OUT_RING(D_CMD); - OUT_RING(BR13); - OUT_RING((pbox->y1 << 16) | pbox->x1); - OUT_RING((pbox->y2 << 16) | pbox->x2); - OUT_RING(dev_priv->depth_offset); - OUT_RING(clear_zval); - ADVANCE_LP_RING(); - } - } -} - -static void i830_dma_dispatch_swap(struct drm_device *dev) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - drm_i830_sarea_t *sarea_priv = dev_priv->sarea_priv; - int nbox = sarea_priv->nbox; - struct drm_clip_rect *pbox = sarea_priv->boxes; - int pitch = dev_priv->pitch; - int cpp = dev_priv->cpp; - int i; - unsigned int CMD, BR13; - RING_LOCALS; - - DRM_DEBUG("swapbuffers\n"); - - i830_kernel_lost_context(dev); - - if (dev_priv->do_boxes) - i830_cp_performance_boxes(dev); - - switch (cpp) { - case 2: - BR13 = (pitch * cpp) | (0xCC << 16) | (1 << 24); - CMD = XY_SRC_COPY_BLT_CMD; - break; - case 4: - BR13 = (pitch * cpp) | (0xCC << 16) | (1 << 24) | (1 << 25); - CMD = (XY_SRC_COPY_BLT_CMD | XY_SRC_COPY_BLT_WRITE_ALPHA | - XY_SRC_COPY_BLT_WRITE_RGB); - break; - default: - BR13 = (pitch * cpp) | (0xCC << 16) | (1 << 24); - CMD = XY_SRC_COPY_BLT_CMD; - break; - } - - if (nbox > I830_NR_SAREA_CLIPRECTS) - nbox = I830_NR_SAREA_CLIPRECTS; - - for (i = 0; i < nbox; i++, pbox++) { - if (pbox->x1 > pbox->x2 || - pbox->y1 > pbox->y2 || - pbox->x2 > dev_priv->w || pbox->y2 > dev_priv->h) - continue; - - DRM_DEBUG("dispatch swap %d,%d-%d,%d!\n", - pbox->x1, pbox->y1, pbox->x2, pbox->y2); - - BEGIN_LP_RING(8); - OUT_RING(CMD); - OUT_RING(BR13); - OUT_RING((pbox->y1 << 16) | pbox->x1); - OUT_RING((pbox->y2 << 16) | pbox->x2); - - if (dev_priv->current_page == 0) - OUT_RING(dev_priv->front_offset); - else - OUT_RING(dev_priv->back_offset); - - OUT_RING((pbox->y1 << 16) | pbox->x1); - OUT_RING(BR13 & 0xffff); - - if (dev_priv->current_page == 0) - OUT_RING(dev_priv->back_offset); - else - OUT_RING(dev_priv->front_offset); - - ADVANCE_LP_RING(); - } -} - -static void i830_dma_dispatch_flip(struct drm_device *dev) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - RING_LOCALS; - - DRM_DEBUG("%s: page=%d pfCurrentPage=%d\n", - __func__, - dev_priv->current_page, - dev_priv->sarea_priv->pf_current_page); - - i830_kernel_lost_context(dev); - - if (dev_priv->do_boxes) { - dev_priv->sarea_priv->perf_boxes |= I830_BOX_FLIP; - i830_cp_performance_boxes(dev); - } - - BEGIN_LP_RING(2); - OUT_RING(INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE); - OUT_RING(0); - ADVANCE_LP_RING(); - - BEGIN_LP_RING(6); - OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | ASYNC_FLIP); - OUT_RING(0); - if (dev_priv->current_page == 0) { - OUT_RING(dev_priv->back_offset); - dev_priv->current_page = 1; - } else { - OUT_RING(dev_priv->front_offset); - dev_priv->current_page = 0; - } - OUT_RING(0); - ADVANCE_LP_RING(); - - BEGIN_LP_RING(2); - OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_PLANE_A_FLIP); - OUT_RING(0); - ADVANCE_LP_RING(); - - dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; -} - -static void i830_dma_dispatch_vertex(struct drm_device *dev, - struct drm_buf *buf, int discard, int used) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - drm_i830_buf_priv_t *buf_priv = buf->dev_private; - drm_i830_sarea_t *sarea_priv = dev_priv->sarea_priv; - struct drm_clip_rect *box = sarea_priv->boxes; - int nbox = sarea_priv->nbox; - unsigned long address = (unsigned long)buf->bus_address; - unsigned long start = address - dev->agp->base; - int i = 0, u; - RING_LOCALS; - - i830_kernel_lost_context(dev); - - if (nbox > I830_NR_SAREA_CLIPRECTS) - nbox = I830_NR_SAREA_CLIPRECTS; - - if (discard) { - u = cmpxchg(buf_priv->in_use, I830_BUF_CLIENT, - I830_BUF_HARDWARE); - if (u != I830_BUF_CLIENT) - DRM_DEBUG("xxxx 2\n"); - } - - if (used > 4 * 1023) - used = 0; - - if (sarea_priv->dirty) - i830EmitState(dev); - - DRM_DEBUG("dispatch vertex addr 0x%lx, used 0x%x nbox %d\n", - address, used, nbox); - - dev_priv->counter++; - DRM_DEBUG("dispatch counter : %ld\n", dev_priv->counter); - DRM_DEBUG("i830_dma_dispatch\n"); - DRM_DEBUG("start : %lx\n", start); - DRM_DEBUG("used : %d\n", used); - DRM_DEBUG("start + used - 4 : %ld\n", start + used - 4); - - if (buf_priv->currently_mapped == I830_BUF_MAPPED) { - u32 *vp = buf_priv->kernel_virtual; - - vp[0] = (GFX_OP_PRIMITIVE | - sarea_priv->vertex_prim | ((used / 4) - 2)); - - if (dev_priv->use_mi_batchbuffer_start) { - vp[used / 4] = MI_BATCH_BUFFER_END; - used += 4; - } - - if (used & 4) { - vp[used / 4] = 0; - used += 4; - } - - i830_unmap_buffer(buf); - } - - if (used) { - do { - if (i < nbox) { - BEGIN_LP_RING(6); - OUT_RING(GFX_OP_DRAWRECT_INFO); - OUT_RING(sarea_priv-> - BufferState[I830_DESTREG_DR1]); - OUT_RING(box[i].x1 | (box[i].y1 << 16)); - OUT_RING(box[i].x2 | (box[i].y2 << 16)); - OUT_RING(sarea_priv-> - BufferState[I830_DESTREG_DR4]); - OUT_RING(0); - ADVANCE_LP_RING(); - } - - if (dev_priv->use_mi_batchbuffer_start) { - BEGIN_LP_RING(2); - OUT_RING(MI_BATCH_BUFFER_START | (2 << 6)); - OUT_RING(start | MI_BATCH_NON_SECURE); - ADVANCE_LP_RING(); - } else { - BEGIN_LP_RING(4); - OUT_RING(MI_BATCH_BUFFER); - OUT_RING(start | MI_BATCH_NON_SECURE); - OUT_RING(start + used - 4); - OUT_RING(0); - ADVANCE_LP_RING(); - } - - } while (++i < nbox); - } - - if (discard) { - dev_priv->counter++; - - (void)cmpxchg(buf_priv->in_use, I830_BUF_CLIENT, - I830_BUF_HARDWARE); - - BEGIN_LP_RING(8); - OUT_RING(CMD_STORE_DWORD_IDX); - OUT_RING(20); - OUT_RING(dev_priv->counter); - OUT_RING(CMD_STORE_DWORD_IDX); - OUT_RING(buf_priv->my_use_idx); - OUT_RING(I830_BUF_FREE); - OUT_RING(CMD_REPORT_HEAD); - OUT_RING(0); - ADVANCE_LP_RING(); - } -} - -static void i830_dma_quiescent(struct drm_device *dev) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - RING_LOCALS; - - i830_kernel_lost_context(dev); - - BEGIN_LP_RING(4); - OUT_RING(INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE); - OUT_RING(CMD_REPORT_HEAD); - OUT_RING(0); - OUT_RING(0); - ADVANCE_LP_RING(); - - i830_wait_ring(dev, dev_priv->ring.Size - 8, __func__); -} - -static int i830_flush_queue(struct drm_device *dev) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - struct drm_device_dma *dma = dev->dma; - int i, ret = 0; - RING_LOCALS; - - i830_kernel_lost_context(dev); - - BEGIN_LP_RING(2); - OUT_RING(CMD_REPORT_HEAD); - OUT_RING(0); - ADVANCE_LP_RING(); - - i830_wait_ring(dev, dev_priv->ring.Size - 8, __func__); - - for (i = 0; i < dma->buf_count; i++) { - struct drm_buf *buf = dma->buflist[i]; - drm_i830_buf_priv_t *buf_priv = buf->dev_private; - - int used = cmpxchg(buf_priv->in_use, I830_BUF_HARDWARE, - I830_BUF_FREE); - - if (used == I830_BUF_HARDWARE) - DRM_DEBUG("reclaimed from HARDWARE\n"); - if (used == I830_BUF_CLIENT) - DRM_DEBUG("still on client\n"); - } - - return ret; -} - -/* Must be called with the lock held */ -static void i830_reclaim_buffers(struct drm_device *dev, struct drm_file *file_priv) -{ - struct drm_device_dma *dma = dev->dma; - int i; - - if (!dma) - return; - if (!dev->dev_private) - return; - if (!dma->buflist) - return; - - i830_flush_queue(dev); - - for (i = 0; i < dma->buf_count; i++) { - struct drm_buf *buf = dma->buflist[i]; - drm_i830_buf_priv_t *buf_priv = buf->dev_private; - - if (buf->file_priv == file_priv && buf_priv) { - int used = cmpxchg(buf_priv->in_use, I830_BUF_CLIENT, - I830_BUF_FREE); - - if (used == I830_BUF_CLIENT) - DRM_DEBUG("reclaimed from client\n"); - if (buf_priv->currently_mapped == I830_BUF_MAPPED) - buf_priv->currently_mapped = I830_BUF_UNMAPPED; - } - } -} - -static int i830_flush_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - LOCK_TEST_WITH_RETURN(dev, file_priv); - - i830_flush_queue(dev); - return 0; -} - -static int i830_dma_vertex(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_device_dma *dma = dev->dma; - drm_i830_private_t *dev_priv = (drm_i830_private_t *) dev->dev_private; - u32 *hw_status = dev_priv->hw_status_page; - drm_i830_sarea_t *sarea_priv = (drm_i830_sarea_t *) - dev_priv->sarea_priv; - drm_i830_vertex_t *vertex = data; - - LOCK_TEST_WITH_RETURN(dev, file_priv); - - DRM_DEBUG("i830 dma vertex, idx %d used %d discard %d\n", - vertex->idx, vertex->used, vertex->discard); - - if (vertex->idx < 0 || vertex->idx > dma->buf_count) - return -EINVAL; - - i830_dma_dispatch_vertex(dev, - dma->buflist[vertex->idx], - vertex->discard, vertex->used); - - sarea_priv->last_enqueue = dev_priv->counter - 1; - sarea_priv->last_dispatch = (int)hw_status[5]; - - return 0; -} - -static int i830_clear_bufs(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i830_clear_t *clear = data; - - LOCK_TEST_WITH_RETURN(dev, file_priv); - - /* GH: Someone's doing nasty things... */ - if (!dev->dev_private) - return -EINVAL; - - i830_dma_dispatch_clear(dev, clear->flags, - clear->clear_color, - clear->clear_depth, clear->clear_depthmask); - return 0; -} - -static int i830_swap_bufs(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - DRM_DEBUG("i830_swap_bufs\n"); - - LOCK_TEST_WITH_RETURN(dev, file_priv); - - i830_dma_dispatch_swap(dev); - return 0; -} - -/* Not sure why this isn't set all the time: - */ -static void i830_do_init_pageflip(struct drm_device *dev) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - - DRM_DEBUG("%s\n", __func__); - dev_priv->page_flipping = 1; - dev_priv->current_page = 0; - dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; -} - -static int i830_do_cleanup_pageflip(struct drm_device *dev) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - - DRM_DEBUG("%s\n", __func__); - if (dev_priv->current_page != 0) - i830_dma_dispatch_flip(dev); - - dev_priv->page_flipping = 0; - return 0; -} - -static int i830_flip_bufs(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - - DRM_DEBUG("%s\n", __func__); - - LOCK_TEST_WITH_RETURN(dev, file_priv); - - if (!dev_priv->page_flipping) - i830_do_init_pageflip(dev); - - i830_dma_dispatch_flip(dev); - return 0; -} - -static int i830_getage(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i830_private_t *dev_priv = (drm_i830_private_t *) dev->dev_private; - u32 *hw_status = dev_priv->hw_status_page; - drm_i830_sarea_t *sarea_priv = (drm_i830_sarea_t *) - dev_priv->sarea_priv; - - sarea_priv->last_dispatch = (int)hw_status[5]; - return 0; -} - -static int i830_getbuf(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - int retcode = 0; - drm_i830_dma_t *d = data; - drm_i830_private_t *dev_priv = (drm_i830_private_t *) dev->dev_private; - u32 *hw_status = dev_priv->hw_status_page; - drm_i830_sarea_t *sarea_priv = (drm_i830_sarea_t *) - dev_priv->sarea_priv; - - DRM_DEBUG("getbuf\n"); - - LOCK_TEST_WITH_RETURN(dev, file_priv); - - d->granted = 0; - - retcode = i830_dma_get_buffer(dev, d, file_priv); - - DRM_DEBUG("i830_dma: %d returning %d, granted = %d\n", - task_pid_nr(current), retcode, d->granted); - - sarea_priv->last_dispatch = (int)hw_status[5]; - - return retcode; -} - -static int i830_copybuf(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - /* Never copy - 2.4.x doesn't need it */ - return 0; -} - -static int i830_docopy(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - return 0; -} - -static int i830_getparam(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - drm_i830_getparam_t *param = data; - int value; - - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __func__); - return -EINVAL; - } - - switch (param->param) { - case I830_PARAM_IRQ_ACTIVE: - value = dev->irq_enabled; - break; - default: - return -EINVAL; - } - - if (copy_to_user(param->value, &value, sizeof(int))) { - DRM_ERROR("copy_to_user\n"); - return -EFAULT; - } - - return 0; -} - -static int i830_setparam(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - drm_i830_setparam_t *param = data; - - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __func__); - return -EINVAL; - } - - switch (param->param) { - case I830_SETPARAM_USE_MI_BATCHBUFFER_START: - dev_priv->use_mi_batchbuffer_start = param->value; - break; - default: - return -EINVAL; - } - - return 0; -} - -int i830_driver_load(struct drm_device *dev, unsigned long flags) -{ - /* i830 has 4 more counters */ - dev->counters += 4; - dev->types[6] = _DRM_STAT_IRQ; - dev->types[7] = _DRM_STAT_PRIMARY; - dev->types[8] = _DRM_STAT_SECONDARY; - dev->types[9] = _DRM_STAT_DMA; - - return 0; -} - -void i830_driver_lastclose(struct drm_device *dev) -{ - i830_dma_cleanup(dev); -} - -void i830_driver_preclose(struct drm_device *dev, struct drm_file *file_priv) -{ - if (dev->dev_private) { - drm_i830_private_t *dev_priv = dev->dev_private; - if (dev_priv->page_flipping) - i830_do_cleanup_pageflip(dev); - } -} - -void i830_driver_reclaim_buffers_locked(struct drm_device *dev, struct drm_file *file_priv) -{ - i830_reclaim_buffers(dev, file_priv); -} - -int i830_driver_dma_quiescent(struct drm_device *dev) -{ - i830_dma_quiescent(dev); - return 0; -} - -/* - * call the drm_ioctl under the big kernel lock because - * to lock against the i830_mmap_buffers function. - */ -long i830_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int ret; - lock_kernel(); - ret = drm_ioctl(file, cmd, arg); - unlock_kernel(); - return ret; -} - -struct drm_ioctl_desc i830_ioctls[] = { - DRM_IOCTL_DEF_DRV(I830_INIT, i830_dma_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(I830_VERTEX, i830_dma_vertex, DRM_AUTH|DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(I830_CLEAR, i830_clear_bufs, DRM_AUTH|DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(I830_FLUSH, i830_flush_ioctl, DRM_AUTH|DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(I830_GETAGE, i830_getage, DRM_AUTH|DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(I830_GETBUF, i830_getbuf, DRM_AUTH|DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(I830_SWAP, i830_swap_bufs, DRM_AUTH|DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(I830_COPY, i830_copybuf, DRM_AUTH|DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(I830_DOCOPY, i830_docopy, DRM_AUTH|DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(I830_FLIP, i830_flip_bufs, DRM_AUTH|DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(I830_IRQ_EMIT, i830_irq_emit, DRM_AUTH|DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(I830_IRQ_WAIT, i830_irq_wait, DRM_AUTH|DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(I830_GETPARAM, i830_getparam, DRM_AUTH|DRM_UNLOCKED), - DRM_IOCTL_DEF_DRV(I830_SETPARAM, i830_setparam, DRM_AUTH|DRM_UNLOCKED), -}; - -int i830_max_ioctl = DRM_ARRAY_SIZE(i830_ioctls); - -/** - * Determine if the device really is AGP or not. - * - * All Intel graphics chipsets are treated as AGP, even if they are really - * PCI-e. - * - * \param dev The device to be tested. - * - * \returns - * A value of 1 is always retured to indictate every i8xx is AGP. - */ -int i830_driver_device_is_agp(struct drm_device *dev) -{ - return 1; -} diff --git a/drivers/gpu/drm/i830/i830_drv.c b/drivers/gpu/drm/i830/i830_drv.c deleted file mode 100644 index f655ab7977da..000000000000 --- a/drivers/gpu/drm/i830/i830_drv.c +++ /dev/null @@ -1,107 +0,0 @@ -/* i830_drv.c -- I810 driver -*- linux-c -*- - * Created: Mon Dec 13 01:56:22 1999 by jhartmann@precisioninsight.com - * - * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Rickard E. (Rik) Faith - * Jeff Hartmann - * Gareth Hughes - * Abraham vd Merwe - * Keith Whitwell - */ - -#include "drmP.h" -#include "drm.h" -#include "i830_drm.h" -#include "i830_drv.h" - -#include "drm_pciids.h" - -static struct pci_device_id pciidlist[] = { - i830_PCI_IDS -}; - -static struct drm_driver driver = { - .driver_features = - DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | DRIVER_USE_MTRR | - DRIVER_HAVE_DMA | DRIVER_DMA_QUEUE, -#if USE_IRQS - .driver_features |= DRIVER_HAVE_IRQ | DRIVER_SHARED_IRQ, -#endif - .dev_priv_size = sizeof(drm_i830_buf_priv_t), - .load = i830_driver_load, - .lastclose = i830_driver_lastclose, - .preclose = i830_driver_preclose, - .device_is_agp = i830_driver_device_is_agp, - .reclaim_buffers_locked = i830_driver_reclaim_buffers_locked, - .dma_quiescent = i830_driver_dma_quiescent, -#if USE_IRQS - .irq_preinstall = i830_driver_irq_preinstall, - .irq_postinstall = i830_driver_irq_postinstall, - .irq_uninstall = i830_driver_irq_uninstall, - .irq_handler = i830_driver_irq_handler, -#endif - .ioctls = i830_ioctls, - .fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = i830_ioctl, - .mmap = drm_mmap, - .poll = drm_poll, - .fasync = drm_fasync, - .llseek = noop_llseek, - }, - - .pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, - }, - - .name = DRIVER_NAME, - .desc = DRIVER_DESC, - .date = DRIVER_DATE, - .major = DRIVER_MAJOR, - .minor = DRIVER_MINOR, - .patchlevel = DRIVER_PATCHLEVEL, -}; - -static int __init i830_init(void) -{ - driver.num_ioctls = i830_max_ioctl; - return drm_init(&driver); -} - -static void __exit i830_exit(void) -{ - drm_exit(&driver); -} - -module_init(i830_init); -module_exit(i830_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/i830/i830_drv.h b/drivers/gpu/drm/i830/i830_drv.h deleted file mode 100644 index 0df1c720560b..000000000000 --- a/drivers/gpu/drm/i830/i830_drv.h +++ /dev/null @@ -1,295 +0,0 @@ -/* i830_drv.h -- Private header for the I830 driver -*- linux-c -*- - * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com - * - * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: Rickard E. (Rik) Faith - * Jeff Hartmann - * - */ - -#ifndef _I830_DRV_H_ -#define _I830_DRV_H_ - -/* General customization: - */ - -#define DRIVER_AUTHOR "VA Linux Systems Inc." - -#define DRIVER_NAME "i830" -#define DRIVER_DESC "Intel 830M" -#define DRIVER_DATE "20021108" - -/* Interface history: - * - * 1.1: Original. - * 1.2: ? - * 1.3: New irq emit/wait ioctls. - * New pageflip ioctl. - * New getparam ioctl. - * State for texunits 3&4 in sarea. - * New (alternative) layout for texture state. - */ -#define DRIVER_MAJOR 1 -#define DRIVER_MINOR 3 -#define DRIVER_PATCHLEVEL 2 - -/* Driver will work either way: IRQ's save cpu time when waiting for - * the card, but are subject to subtle interactions between bios, - * hardware and the driver. - */ -/* XXX: Add vblank support? */ -#define USE_IRQS 0 - -typedef struct drm_i830_buf_priv { - u32 *in_use; - int my_use_idx; - int currently_mapped; - void __user *virtual; - void *kernel_virtual; - drm_local_map_t map; -} drm_i830_buf_priv_t; - -typedef struct _drm_i830_ring_buffer { - int tail_mask; - unsigned long Start; - unsigned long End; - unsigned long Size; - u8 *virtual_start; - int head; - int tail; - int space; - drm_local_map_t map; -} drm_i830_ring_buffer_t; - -typedef struct drm_i830_private { - struct drm_local_map *sarea_map; - struct drm_local_map *mmio_map; - - drm_i830_sarea_t *sarea_priv; - drm_i830_ring_buffer_t ring; - - void *hw_status_page; - unsigned long counter; - - dma_addr_t dma_status_page; - - struct drm_buf *mmap_buffer; - - u32 front_di1, back_di1, zi1; - - int back_offset; - int depth_offset; - int front_offset; - int w, h; - int pitch; - int back_pitch; - int depth_pitch; - unsigned int cpp; - - int do_boxes; - int dma_used; - - int current_page; - int page_flipping; - - wait_queue_head_t irq_queue; - atomic_t irq_received; - atomic_t irq_emitted; - - int use_mi_batchbuffer_start; - -} drm_i830_private_t; - -long i830_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -extern struct drm_ioctl_desc i830_ioctls[]; -extern int i830_max_ioctl; - -/* i830_irq.c */ -extern int i830_irq_emit(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int i830_irq_wait(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -extern irqreturn_t i830_driver_irq_handler(DRM_IRQ_ARGS); -extern void i830_driver_irq_preinstall(struct drm_device *dev); -extern void i830_driver_irq_postinstall(struct drm_device *dev); -extern void i830_driver_irq_uninstall(struct drm_device *dev); -extern int i830_driver_load(struct drm_device *, unsigned long flags); -extern void i830_driver_preclose(struct drm_device *dev, - struct drm_file *file_priv); -extern void i830_driver_lastclose(struct drm_device *dev); -extern void i830_driver_reclaim_buffers_locked(struct drm_device *dev, - struct drm_file *file_priv); -extern int i830_driver_dma_quiescent(struct drm_device *dev); -extern int i830_driver_device_is_agp(struct drm_device *dev); - -#define I830_READ(reg) DRM_READ32(dev_priv->mmio_map, reg) -#define I830_WRITE(reg, val) DRM_WRITE32(dev_priv->mmio_map, reg, val) -#define I830_READ16(reg) DRM_READ16(dev_priv->mmio_map, reg) -#define I830_WRITE16(reg, val) DRM_WRITE16(dev_priv->mmio_map, reg, val) - -#define I830_VERBOSE 0 - -#define RING_LOCALS unsigned int outring, ringmask, outcount; \ - volatile char *virt; - -#define BEGIN_LP_RING(n) do { \ - if (I830_VERBOSE) \ - printk("BEGIN_LP_RING(%d)\n", (n)); \ - if (dev_priv->ring.space < n*4) \ - i830_wait_ring(dev, n*4, __func__); \ - outcount = 0; \ - outring = dev_priv->ring.tail; \ - ringmask = dev_priv->ring.tail_mask; \ - virt = dev_priv->ring.virtual_start; \ -} while (0) - -#define OUT_RING(n) do { \ - if (I830_VERBOSE) \ - printk(" OUT_RING %x\n", (int)(n)); \ - *(volatile unsigned int *)(virt + outring) = n; \ - outcount++; \ - outring += 4; \ - outring &= ringmask; \ -} while (0) - -#define ADVANCE_LP_RING() do { \ - if (I830_VERBOSE) \ - printk("ADVANCE_LP_RING %x\n", outring); \ - dev_priv->ring.tail = outring; \ - dev_priv->ring.space -= outcount * 4; \ - I830_WRITE(LP_RING + RING_TAIL, outring); \ -} while (0) - -extern int i830_wait_ring(struct drm_device *dev, int n, const char *caller); - -#define GFX_OP_USER_INTERRUPT ((0<<29)|(2<<23)) -#define GFX_OP_BREAKPOINT_INTERRUPT ((0<<29)|(1<<23)) -#define CMD_REPORT_HEAD (7<<23) -#define CMD_STORE_DWORD_IDX ((0x21<<23) | 0x1) -#define CMD_OP_BATCH_BUFFER ((0x0<<29)|(0x30<<23)|0x1) - -#define STATE3D_LOAD_STATE_IMMEDIATE_2 ((0x3<<29)|(0x1d<<24)|(0x03<<16)) -#define LOAD_TEXTURE_MAP0 (1<<11) - -#define INST_PARSER_CLIENT 0x00000000 -#define INST_OP_FLUSH 0x02000000 -#define INST_FLUSH_MAP_CACHE 0x00000001 - -#define BB1_START_ADDR_MASK (~0x7) -#define BB1_PROTECTED (1<<0) -#define BB1_UNPROTECTED (0<<0) -#define BB2_END_ADDR_MASK (~0x7) - -#define I830REG_HWSTAM 0x02098 -#define I830REG_INT_IDENTITY_R 0x020a4 -#define I830REG_INT_MASK_R 0x020a8 -#define I830REG_INT_ENABLE_R 0x020a0 - -#define I830_IRQ_RESERVED ((1<<13)|(3<<2)) - -#define LP_RING 0x2030 -#define HP_RING 0x2040 -#define RING_TAIL 0x00 -#define TAIL_ADDR 0x001FFFF8 -#define RING_HEAD 0x04 -#define HEAD_WRAP_COUNT 0xFFE00000 -#define HEAD_WRAP_ONE 0x00200000 -#define HEAD_ADDR 0x001FFFFC -#define RING_START 0x08 -#define START_ADDR 0x0xFFFFF000 -#define RING_LEN 0x0C -#define RING_NR_PAGES 0x001FF000 -#define RING_REPORT_MASK 0x00000006 -#define RING_REPORT_64K 0x00000002 -#define RING_REPORT_128K 0x00000004 -#define RING_NO_REPORT 0x00000000 -#define RING_VALID_MASK 0x00000001 -#define RING_VALID 0x00000001 -#define RING_INVALID 0x00000000 - -#define GFX_OP_SCISSOR ((0x3<<29)|(0x1c<<24)|(0x10<<19)) -#define SC_UPDATE_SCISSOR (0x1<<1) -#define SC_ENABLE_MASK (0x1<<0) -#define SC_ENABLE (0x1<<0) - -#define GFX_OP_SCISSOR_INFO ((0x3<<29)|(0x1d<<24)|(0x81<<16)|(0x1)) -#define SCI_YMIN_MASK (0xffff<<16) -#define SCI_XMIN_MASK (0xffff<<0) -#define SCI_YMAX_MASK (0xffff<<16) -#define SCI_XMAX_MASK (0xffff<<0) - -#define GFX_OP_SCISSOR_ENABLE ((0x3<<29)|(0x1c<<24)|(0x10<<19)) -#define GFX_OP_SCISSOR_RECT ((0x3<<29)|(0x1d<<24)|(0x81<<16)|1) -#define GFX_OP_COLOR_FACTOR ((0x3<<29)|(0x1d<<24)|(0x1<<16)|0x0) -#define GFX_OP_STIPPLE ((0x3<<29)|(0x1d<<24)|(0x83<<16)) -#define GFX_OP_MAP_INFO ((0x3<<29)|(0x1d<<24)|0x4) -#define GFX_OP_DESTBUFFER_VARS ((0x3<<29)|(0x1d<<24)|(0x85<<16)|0x0) -#define GFX_OP_DRAWRECT_INFO ((0x3<<29)|(0x1d<<24)|(0x80<<16)|(0x3)) -#define GFX_OP_PRIMITIVE ((0x3<<29)|(0x1f<<24)) - -#define CMD_OP_DESTBUFFER_INFO ((0x3<<29)|(0x1d<<24)|(0x8e<<16)|1) - -#define CMD_OP_DISPLAYBUFFER_INFO ((0x0<<29)|(0x14<<23)|2) -#define ASYNC_FLIP (1<<22) - -#define CMD_3D (0x3<<29) -#define STATE3D_CONST_BLEND_COLOR_CMD (CMD_3D|(0x1d<<24)|(0x88<<16)) -#define STATE3D_MAP_COORD_SETBIND_CMD (CMD_3D|(0x1d<<24)|(0x02<<16)) - -#define BR00_BITBLT_CLIENT 0x40000000 -#define BR00_OP_COLOR_BLT 0x10000000 -#define BR00_OP_SRC_COPY_BLT 0x10C00000 -#define BR13_SOLID_PATTERN 0x80000000 - -#define BUF_3D_ID_COLOR_BACK (0x3<<24) -#define BUF_3D_ID_DEPTH (0x7<<24) -#define BUF_3D_USE_FENCE (1<<23) -#define BUF_3D_PITCH(x) (((x)/4)<<2) - -#define CMD_OP_MAP_PALETTE_LOAD ((3<<29)|(0x1d<<24)|(0x82<<16)|255) -#define MAP_PALETTE_NUM(x) ((x<<8) & (1<<8)) -#define MAP_PALETTE_BOTH (1<<11) - -#define XY_COLOR_BLT_CMD ((2<<29)|(0x50<<22)|0x4) -#define XY_COLOR_BLT_WRITE_ALPHA (1<<21) -#define XY_COLOR_BLT_WRITE_RGB (1<<20) - -#define XY_SRC_COPY_BLT_CMD ((2<<29)|(0x53<<22)|6) -#define XY_SRC_COPY_BLT_WRITE_ALPHA (1<<21) -#define XY_SRC_COPY_BLT_WRITE_RGB (1<<20) - -#define MI_BATCH_BUFFER ((0x30<<23)|1) -#define MI_BATCH_BUFFER_START (0x31<<23) -#define MI_BATCH_BUFFER_END (0xA<<23) -#define MI_BATCH_NON_SECURE (1) - -#define MI_WAIT_FOR_EVENT ((0x3<<23)) -#define MI_WAIT_FOR_PLANE_A_FLIP (1<<2) -#define MI_WAIT_FOR_PLANE_A_SCANLINES (1<<1) - -#define MI_LOAD_SCAN_LINES_INCL ((0x12<<23)) - -#endif diff --git a/drivers/gpu/drm/i830/i830_irq.c b/drivers/gpu/drm/i830/i830_irq.c deleted file mode 100644 index d1a6b95d631d..000000000000 --- a/drivers/gpu/drm/i830/i830_irq.c +++ /dev/null @@ -1,186 +0,0 @@ -/* i830_dma.c -- DMA support for the I830 -*- linux-c -*- - * - * Copyright 2002 Tungsten Graphics, Inc. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: Keith Whitwell - * - */ - -#include "drmP.h" -#include "drm.h" -#include "i830_drm.h" -#include "i830_drv.h" -#include /* For task queue support */ -#include - -irqreturn_t i830_driver_irq_handler(DRM_IRQ_ARGS) -{ - struct drm_device *dev = (struct drm_device *) arg; - drm_i830_private_t *dev_priv = (drm_i830_private_t *) dev->dev_private; - u16 temp; - - temp = I830_READ16(I830REG_INT_IDENTITY_R); - DRM_DEBUG("%x\n", temp); - - if (!(temp & 2)) - return IRQ_NONE; - - I830_WRITE16(I830REG_INT_IDENTITY_R, temp); - - atomic_inc(&dev_priv->irq_received); - wake_up_interruptible(&dev_priv->irq_queue); - - return IRQ_HANDLED; -} - -static int i830_emit_irq(struct drm_device *dev) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - RING_LOCALS; - - DRM_DEBUG("%s\n", __func__); - - atomic_inc(&dev_priv->irq_emitted); - - BEGIN_LP_RING(2); - OUT_RING(0); - OUT_RING(GFX_OP_USER_INTERRUPT); - ADVANCE_LP_RING(); - - return atomic_read(&dev_priv->irq_emitted); -} - -static int i830_wait_irq(struct drm_device *dev, int irq_nr) -{ - drm_i830_private_t *dev_priv = (drm_i830_private_t *) dev->dev_private; - DECLARE_WAITQUEUE(entry, current); - unsigned long end = jiffies + HZ * 3; - int ret = 0; - - DRM_DEBUG("%s\n", __func__); - - if (atomic_read(&dev_priv->irq_received) >= irq_nr) - return 0; - - dev_priv->sarea_priv->perf_boxes |= I830_BOX_WAIT; - - add_wait_queue(&dev_priv->irq_queue, &entry); - - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - if (atomic_read(&dev_priv->irq_received) >= irq_nr) - break; - if ((signed)(end - jiffies) <= 0) { - DRM_ERROR("timeout iir %x imr %x ier %x hwstam %x\n", - I830_READ16(I830REG_INT_IDENTITY_R), - I830_READ16(I830REG_INT_MASK_R), - I830_READ16(I830REG_INT_ENABLE_R), - I830_READ16(I830REG_HWSTAM)); - - ret = -EBUSY; /* Lockup? Missed irq? */ - break; - } - schedule_timeout(HZ * 3); - if (signal_pending(current)) { - ret = -EINTR; - break; - } - } - - __set_current_state(TASK_RUNNING); - remove_wait_queue(&dev_priv->irq_queue, &entry); - return ret; -} - -/* Needs the lock as it touches the ring. - */ -int i830_irq_emit(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - drm_i830_irq_emit_t *emit = data; - int result; - - LOCK_TEST_WITH_RETURN(dev, file_priv); - - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __func__); - return -EINVAL; - } - - result = i830_emit_irq(dev); - - if (copy_to_user(emit->irq_seq, &result, sizeof(int))) { - DRM_ERROR("copy_to_user\n"); - return -EFAULT; - } - - return 0; -} - -/* Doesn't need the hardware lock. - */ -int i830_irq_wait(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - drm_i830_irq_wait_t *irqwait = data; - - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __func__); - return -EINVAL; - } - - return i830_wait_irq(dev, irqwait->irq_seq); -} - -/* drm_dma.h hooks -*/ -void i830_driver_irq_preinstall(struct drm_device *dev) -{ - drm_i830_private_t *dev_priv = (drm_i830_private_t *) dev->dev_private; - - I830_WRITE16(I830REG_HWSTAM, 0xffff); - I830_WRITE16(I830REG_INT_MASK_R, 0x0); - I830_WRITE16(I830REG_INT_ENABLE_R, 0x0); - atomic_set(&dev_priv->irq_received, 0); - atomic_set(&dev_priv->irq_emitted, 0); - init_waitqueue_head(&dev_priv->irq_queue); -} - -void i830_driver_irq_postinstall(struct drm_device *dev) -{ - drm_i830_private_t *dev_priv = (drm_i830_private_t *) dev->dev_private; - - I830_WRITE16(I830REG_INT_ENABLE_R, 0x2); -} - -void i830_driver_irq_uninstall(struct drm_device *dev) -{ - drm_i830_private_t *dev_priv = (drm_i830_private_t *) dev->dev_private; - if (!dev_priv) - return; - - I830_WRITE16(I830REG_INT_MASK_R, 0xffff); - I830_WRITE16(I830REG_INT_ENABLE_R, 0x0); -} diff --git a/include/drm/Kbuild b/include/drm/Kbuild index ffec177f3481..3a60ac889520 100644 --- a/include/drm/Kbuild +++ b/include/drm/Kbuild @@ -2,7 +2,6 @@ header-y += drm.h header-y += drm_mode.h header-y += drm_sarea.h header-y += i810_drm.h -header-y += i830_drm.h header-y += i915_drm.h header-y += mga_drm.h header-y += nouveau_drm.h diff --git a/include/drm/i830_drm.h b/include/drm/i830_drm.h deleted file mode 100644 index 61315c29b8f3..000000000000 --- a/include/drm/i830_drm.h +++ /dev/null @@ -1,342 +0,0 @@ -#ifndef _I830_DRM_H_ -#define _I830_DRM_H_ - -/* WARNING: These defines must be the same as what the Xserver uses. - * if you change them, you must change the defines in the Xserver. - * - * KW: Actually, you can't ever change them because doing so would - * break backwards compatibility. - */ - -#ifndef _I830_DEFINES_ -#define _I830_DEFINES_ - -#define I830_DMA_BUF_ORDER 12 -#define I830_DMA_BUF_SZ (1< Date: Mon, 7 Feb 2011 12:16:14 +1000 Subject: drm: dumb scanout create/mmap for intel/radeon (v3) This is just an idea that might or might not be a good idea, it basically adds two ioctls to create a dumb and map a dumb buffer suitable for scanout. The handle can be passed to the KMS ioctls to create a framebuffer. It looks to me like it would be useful in the following cases: a) in development drivers - we can always provide a shadowfb fallback. b) libkms users - we can clean up libkms a lot and avoid linking to libdrm_*. c) plymouth via libkms is a lot easier. Userspace bits would be just calls + mmaps. We could probably mark these handles somehow as not being suitable for acceleartion so as top stop people who are dumber than dumb. Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 33 +++++++++++ drivers/gpu/drm/drm_drv.c | 5 +- drivers/gpu/drm/drm_gem.c | 3 +- drivers/gpu/drm/i915/i915_drv.c | 3 + drivers/gpu/drm/i915/i915_drv.h | 7 +++ drivers/gpu/drm/i915/i915_gem.c | 103 +++++++++++++++++++++++++---------- drivers/gpu/drm/radeon/radeon.h | 9 +++ drivers/gpu/drm/radeon/radeon_drv.c | 13 +++++ drivers/gpu/drm/radeon/radeon_fb.c | 2 +- drivers/gpu/drm/radeon/radeon_gem.c | 53 ++++++++++++++++-- drivers/gpu/drm/radeon/radeon_mode.h | 1 + include/drm/drm.h | 4 ++ include/drm/drmP.h | 12 ++++ include/drm/drm_crtc.h | 7 +++ include/drm/drm_mode.h | 29 ++++++++++ 15 files changed, 246 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 654faa803dcb..4c95b5fd9df3 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2694,3 +2694,36 @@ void drm_mode_config_reset(struct drm_device *dev) connector->funcs->reset(connector); } EXPORT_SYMBOL(drm_mode_config_reset); + +int drm_mode_create_dumb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_create_dumb *args = data; + + if (!dev->driver->dumb_create) + return -ENOSYS; + return dev->driver->dumb_create(file_priv, dev, args); +} + +int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_map_dumb *args = data; + + /* call driver ioctl to get mmap offset */ + if (!dev->driver->dumb_map_offset) + return -ENOSYS; + + return dev->driver->dumb_map_offset(file_priv, dev, args->handle, &args->offset); +} + +int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_destroy_dumb *args = data; + + if (!dev->driver->dumb_destroy) + return -ENOSYS; + + return dev->driver->dumb_destroy(file_priv, dev, args->handle); +} diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 271835a71570..3e991980cee2 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -150,7 +150,10 @@ static struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED) + DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED) }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index ea1c4b019ebf..aa8df2534819 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -181,7 +181,7 @@ EXPORT_SYMBOL(drm_gem_object_alloc); /** * Removes the mapping from handle to filp for this object. */ -static int +int drm_gem_handle_delete(struct drm_file *filp, u32 handle) { struct drm_device *dev; @@ -214,6 +214,7 @@ drm_gem_handle_delete(struct drm_file *filp, u32 handle) return 0; } +EXPORT_SYMBOL(drm_gem_handle_delete); /** * Create a handle for this object. This adds a handle reference diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index cfb56d0ff367..17fde2f27418 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -700,6 +700,9 @@ static struct drm_driver driver = { .gem_init_object = i915_gem_init_object, .gem_free_object = i915_gem_free_object, .gem_vm_ops = &i915_gem_vm_ops, + .dumb_create = i915_gem_dumb_create, + .dumb_map_offset = i915_gem_mmap_gtt, + .dumb_destroy = i915_gem_dumb_destroy, .ioctls = i915_ioctls, .fops = { .owner = THIS_MODULE, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a0149c619cdd..a78197d43ce6 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1114,6 +1114,13 @@ void i915_gem_object_move_to_active(struct drm_i915_gem_object *obj, struct intel_ring_buffer *ring, u32 seqno); +int i915_gem_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args); +int i915_gem_mmap_gtt(struct drm_file *file_priv, struct drm_device *dev, + uint32_t handle, uint64_t *offset); +int i915_gem_dumb_destroy(struct drm_file *file_priv, struct drm_device *dev, + uint32_t handle); /** * Returns true if seq1 is later than seq2. */ diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index cf4f74c7c6fb..bc7f06b8fbca 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -193,22 +193,20 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, return 0; } -/** - * Creates a new mm object and returns a handle to it. - */ -int -i915_gem_create_ioctl(struct drm_device *dev, void *data, - struct drm_file *file) +static int +i915_gem_create(struct drm_file *file, + struct drm_device *dev, + uint64_t size, + uint32_t *handle_p) { - struct drm_i915_gem_create *args = data; struct drm_i915_gem_object *obj; int ret; u32 handle; - args->size = roundup(args->size, PAGE_SIZE); + size = roundup(size, PAGE_SIZE); /* Allocate the new object */ - obj = i915_gem_alloc_object(dev, args->size); + obj = i915_gem_alloc_object(dev, size); if (obj == NULL) return -ENOMEM; @@ -224,10 +222,41 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data, drm_gem_object_unreference(&obj->base); trace_i915_gem_object_create(obj); - args->handle = handle; + *handle_p = handle; return 0; } +int +i915_gem_dumb_create(struct drm_file *file, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + /* have to work out size/pitch and return them */ + args->pitch = ALIGN(args->width & ((args->bpp + 1) / 8), 64); + args->size = args->pitch * args->height; + return i915_gem_create(file, dev, + args->size, &args->handle); +} + +int i915_gem_dumb_destroy(struct drm_file *file, + struct drm_device *dev, + uint32_t handle) +{ + return drm_gem_handle_delete(file, handle); +} + +/** + * Creates a new mm object and returns a handle to it. + */ +int +i915_gem_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_i915_gem_create *args = data; + return i915_gem_create(file, dev, + args->size, &args->handle); +} + static int i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_object *obj) { drm_i915_private_t *dev_priv = obj->base.dev->dev_private; @@ -1425,27 +1454,13 @@ i915_gem_get_unfenced_gtt_alignment(struct drm_i915_gem_object *obj) return tile_height * obj->stride * 2; } -/** - * i915_gem_mmap_gtt_ioctl - prepare an object for GTT mmap'ing - * @dev: DRM device - * @data: GTT mapping ioctl data - * @file: GEM object info - * - * Simply returns the fake offset to userspace so it can mmap it. - * The mmap call will end up in drm_gem_mmap(), which will set things - * up so we can get faults in the handler above. - * - * The fault handler will take care of binding the object into the GTT - * (since it may have been evicted to make room for something), allocating - * a fence register, and mapping the appropriate aperture address into - * userspace. - */ int -i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, - struct drm_file *file) +i915_gem_mmap_gtt(struct drm_file *file, + struct drm_device *dev, + uint32_t handle, + uint64_t *offset) { struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_i915_gem_mmap_gtt *args = data; struct drm_i915_gem_object *obj; int ret; @@ -1456,7 +1471,7 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, if (ret) return ret; - obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle)); + obj = to_intel_bo(drm_gem_object_lookup(dev, file, handle)); if (obj == NULL) { ret = -ENOENT; goto unlock; @@ -1479,7 +1494,7 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, goto out; } - args->offset = (u64)obj->base.map_list.hash.key << PAGE_SHIFT; + *offset = (u64)obj->base.map_list.hash.key << PAGE_SHIFT; out: drm_gem_object_unreference(&obj->base); @@ -1488,6 +1503,34 @@ unlock: return ret; } +/** + * i915_gem_mmap_gtt_ioctl - prepare an object for GTT mmap'ing + * @dev: DRM device + * @data: GTT mapping ioctl data + * @file: GEM object info + * + * Simply returns the fake offset to userspace so it can mmap it. + * The mmap call will end up in drm_gem_mmap(), which will set things + * up so we can get faults in the handler above. + * + * The fault handler will take care of binding the object into the GTT + * (since it may have been evicted to make room for something), allocating + * a fence register, and mapping the appropriate aperture address into + * userspace. + */ +int +i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_i915_gem_mmap_gtt *args = data; + + if (!(dev->driver->driver_features & DRIVER_GEM)) + return -ENODEV; + + return i915_gem_mmap_gtt(file, dev, args->handle, &args->offset); +} + + static int i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj, gfp_t gfpmask) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 56c48b67ef3d..a4605362c528 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -288,6 +288,15 @@ int radeon_gem_object_pin(struct drm_gem_object *obj, uint32_t pin_domain, uint64_t *gpu_addr); void radeon_gem_object_unpin(struct drm_gem_object *obj); +int radeon_mode_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args); +int radeon_mode_dumb_mmap(struct drm_file *filp, + struct drm_device *dev, + uint32_t handle, uint64_t *offset_p); +int radeon_mode_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle); /* * GART structures, functions & helpers diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 275b26a708d6..ca1b7d4c1d83 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -84,6 +84,16 @@ extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, extern struct drm_ioctl_desc radeon_ioctls_kms[]; extern int radeon_max_kms_ioctl; int radeon_mmap(struct file *filp, struct vm_area_struct *vma); +int radeon_mode_dumb_mmap(struct drm_file *filp, + struct drm_device *dev, + uint32_t handle, uint64_t *offset_p); +int radeon_mode_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args); +int radeon_mode_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle); + #if defined(CONFIG_DEBUG_FS) int radeon_debugfs_init(struct drm_minor *minor); void radeon_debugfs_cleanup(struct drm_minor *minor); @@ -322,6 +332,9 @@ static struct drm_driver kms_driver = { .gem_init_object = radeon_gem_object_init, .gem_free_object = radeon_gem_object_free, .dma_ioctl = radeon_dma_ioctl_kms, + .dumb_create = radeon_mode_dumb_create, + .dumb_map_offset = radeon_mode_dumb_mmap, + .dumb_destroy = radeon_mode_dumb_destroy, .fops = { .owner = THIS_MODULE, .open = drm_open, diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 66324b5bb5ba..cb968f997ce7 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -64,7 +64,7 @@ static struct fb_ops radeonfb_ops = { }; -static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled) +int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled) { int aligned = width; int align_large = (ASIC_IS_AVIVO(rdev)) || tiled; diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index df95eb83dac6..ede5dccdf79f 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -236,23 +236,31 @@ int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data, return r; } -int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data, - struct drm_file *filp) +int radeon_mode_dumb_mmap(struct drm_file *filp, + struct drm_device *dev, + uint32_t handle, uint64_t *offset_p) { - struct drm_radeon_gem_mmap *args = data; struct drm_gem_object *gobj; struct radeon_bo *robj; - gobj = drm_gem_object_lookup(dev, filp, args->handle); + gobj = drm_gem_object_lookup(dev, filp, handle); if (gobj == NULL) { return -ENOENT; } robj = gobj->driver_private; - args->addr_ptr = radeon_bo_mmap_offset(robj); + *offset_p = radeon_bo_mmap_offset(robj); drm_gem_object_unreference_unlocked(gobj); return 0; } +int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct drm_radeon_gem_mmap *args = data; + + return radeon_mode_dumb_mmap(filp, dev, args->handle, &args->addr_ptr); +} + int radeon_gem_busy_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { @@ -345,3 +353,38 @@ out: drm_gem_object_unreference_unlocked(gobj); return r; } + +int radeon_mode_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct radeon_device *rdev = dev->dev_private; + struct drm_gem_object *gobj; + int r; + + args->pitch = radeon_align_pitch(rdev, args->width, args->bpp, 0) * ((args->bpp + 1) / 8); + args->size = args->pitch * args->height; + args->size = ALIGN(args->size, PAGE_SIZE); + + r = radeon_gem_object_create(rdev, args->size, 0, + RADEON_GEM_DOMAIN_VRAM, + false, ttm_bo_type_device, + &gobj); + if (r) + return -ENOMEM; + + r = drm_gem_handle_create(file_priv, gobj, &args->handle); + if (r) { + drm_gem_object_unreference_unlocked(gobj); + return r; + } + drm_gem_object_handle_unreference_unlocked(gobj); + return 0; +} + +int radeon_mode_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle) +{ + return drm_gem_handle_delete(file_priv, handle); +} diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 6794cdf91f28..c3f23f6ff60e 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -675,4 +675,5 @@ void radeon_fb_output_poll_changed(struct radeon_device *rdev); void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id); +int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled); #endif diff --git a/include/drm/drm.h b/include/drm/drm.h index e5f70617dec5..8598cc94e169 100644 --- a/include/drm/drm.h +++ b/include/drm/drm.h @@ -701,6 +701,10 @@ struct drm_gem_open { #define DRM_IOCTL_MODE_PAGE_FLIP DRM_IOWR(0xB0, struct drm_mode_crtc_page_flip) #define DRM_IOCTL_MODE_DIRTYFB DRM_IOWR(0xB1, struct drm_mode_fb_dirty_cmd) +#define DRM_IOCTL_MODE_CREATE_DUMB DRM_IOWR(0xB2, struct drm_mode_create_dumb) +#define DRM_IOCTL_MODE_MAP_DUMB DRM_IOWR(0xB3, struct drm_mode_map_dumb) +#define DRM_IOCTL_MODE_DESTROY_DUMB DRM_IOWR(0xB4, struct drm_mode_destroy_dumb) + /** * Device specific ioctls should only be in their respective headers * The device specific ioctl range is from 0x40 to 0x99. diff --git a/include/drm/drmP.h b/include/drm/drmP.h index fe29aadb129d..3cbe7a02d2aa 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -880,6 +880,17 @@ struct drm_driver { /* vga arb irq handler */ void (*vgaarb_irq)(struct drm_device *dev, bool state); + /* dumb alloc support */ + int (*dumb_create)(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args); + int (*dumb_map_offset)(struct drm_file *file_priv, + struct drm_device *dev, uint32_t handle, + uint64_t *offset); + int (*dumb_destroy)(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle); + /* Driver private ops for this object */ struct vm_operations_struct *gem_vm_ops; @@ -1544,6 +1555,7 @@ drm_gem_object_unreference_unlocked(struct drm_gem_object *obj) int drm_gem_handle_create(struct drm_file *file_priv, struct drm_gem_object *obj, u32 *handlep); +int drm_gem_handle_delete(struct drm_file *filp, u32 handle); static inline void drm_gem_object_handle_reference(struct drm_gem_object *obj) diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 801be59f4f15..080a6e33470e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -798,4 +798,11 @@ extern int drm_add_modes_noedid(struct drm_connector *connector, extern bool drm_edid_is_valid(struct edid *edid); struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, int hsize, int vsize, int fresh); + +extern int drm_mode_create_dumb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +extern int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); #endif /* __DRM_CRTC_H__ */ diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h index 0fc7397c8f1f..ae6b7a3dbec7 100644 --- a/include/drm/drm_mode.h +++ b/include/drm/drm_mode.h @@ -344,4 +344,33 @@ struct drm_mode_crtc_page_flip { __u64 user_data; }; +/* create a dumb scanout buffer */ +struct drm_mode_create_dumb { + uint32_t height; + uint32_t width; + uint32_t bpp; + uint32_t flags; + /* handle, pitch, size will be returned */ + uint32_t handle; + uint32_t pitch; + uint64_t size; +}; + +/* set up for mmap of a dumb scanout buffer */ +struct drm_mode_map_dumb { + /** Handle for the object being mapped. */ + __u32 handle; + __u32 pad; + /** + * Fake offset to use for subsequent mmap call + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 offset; +}; + +struct drm_mode_destroy_dumb { + uint32_t handle; +}; + #endif -- cgit v1.2.3 From 8410ea3b95d105a5be5db501656f44bbb91197c1 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 15 Dec 2010 03:16:38 +1000 Subject: drm: rework PCI/platform driver interface. This abstracts the pci/platform interface out a step further, we can go further but this is far enough for now to allow USB to be plugged in. The drivers now just call the init code directly for their device type. Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_drv.c | 43 ------- drivers/gpu/drm/drm_info.c | 27 ++--- drivers/gpu/drm/drm_ioctl.c | 115 ++---------------- drivers/gpu/drm/drm_irq.c | 14 +-- drivers/gpu/drm/drm_pci.c | 205 +++++++++++++++++++++++++++++++- drivers/gpu/drm/drm_platform.c | 75 +++++++++++- drivers/gpu/drm/drm_stub.c | 20 +--- drivers/gpu/drm/i810/i810_drv.c | 14 +-- drivers/gpu/drm/i915/i915_drv.c | 20 ++-- drivers/gpu/drm/mga/mga_dma.c | 2 +- drivers/gpu/drm/mga/mga_drv.c | 13 +- drivers/gpu/drm/nouveau/nouveau_drv.c | 21 ++-- drivers/gpu/drm/nouveau/nouveau_mem.c | 2 +- drivers/gpu/drm/nouveau/nouveau_state.c | 4 +- drivers/gpu/drm/r128/r128_drv.c | 14 ++- drivers/gpu/drm/radeon/radeon_cp.c | 4 +- drivers/gpu/drm/radeon/radeon_drv.c | 36 +++--- drivers/gpu/drm/radeon/radeon_kms.c | 4 +- drivers/gpu/drm/savage/savage_drv.c | 14 +-- drivers/gpu/drm/sis/sis_drv.c | 13 +- drivers/gpu/drm/tdfx/tdfx_drv.c | 13 +- drivers/gpu/drm/via/via_drv.c | 13 +- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 23 ++-- include/drm/drmP.h | 106 ++++++++--------- 24 files changed, 456 insertions(+), 359 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 3e991980cee2..0d04914eb058 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -237,49 +237,6 @@ int drm_lastclose(struct drm_device * dev) return 0; } -/** - * Module initialization. Called via init_module at module load time, or via - * linux/init/main.c (this is not currently supported). - * - * \return zero on success or a negative number on failure. - * - * Initializes an array of drm_device structures, and attempts to - * initialize all available devices, using consecutive minors, registering the - * stubs and initializing the device. - * - * Expands the \c DRIVER_PREINIT and \c DRIVER_POST_INIT macros before and - * after the initialization for driver customization. - */ -int drm_init(struct drm_driver *driver) -{ - DRM_DEBUG("\n"); - INIT_LIST_HEAD(&driver->device_list); - - if (driver->driver_features & DRIVER_USE_PLATFORM_DEVICE) - return drm_platform_init(driver); - else - return drm_pci_init(driver); -} - -EXPORT_SYMBOL(drm_init); - -void drm_exit(struct drm_driver *driver) -{ - struct drm_device *dev, *tmp; - DRM_DEBUG("\n"); - - if (driver->driver_features & DRIVER_MODESET) { - pci_unregister_driver(&driver->pci_driver); - } else { - list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item) - drm_put_dev(dev); - } - - DRM_INFO("Module unloaded\n"); -} - -EXPORT_SYMBOL(drm_exit); - /** File operations structure */ static const struct file_operations drm_stub_fops = { .owner = THIS_MODULE, diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c index 3cdbaf379bb5..812aaac4438a 100644 --- a/drivers/gpu/drm/drm_info.c +++ b/drivers/gpu/drm/drm_info.c @@ -47,30 +47,19 @@ int drm_name_info(struct seq_file *m, void *data) struct drm_minor *minor = node->minor; struct drm_device *dev = minor->dev; struct drm_master *master = minor->master; - + const char *bus_name; if (!master) return 0; - if (drm_core_check_feature(dev, DRIVER_USE_PLATFORM_DEVICE)) { - if (master->unique) { - seq_printf(m, "%s %s %s\n", - dev->driver->platform_device->name, - dev_name(dev->dev), master->unique); - } else { - seq_printf(m, "%s\n", - dev->driver->platform_device->name); - } + bus_name = dev->driver->bus->get_name(dev); + if (master->unique) { + seq_printf(m, "%s %s %s\n", + bus_name, + dev_name(dev->dev), master->unique); } else { - if (master->unique) { - seq_printf(m, "%s %s %s\n", - dev->driver->pci_driver.name, - dev_name(dev->dev), master->unique); - } else { - seq_printf(m, "%s %s\n", dev->driver->pci_driver.name, - dev_name(dev->dev)); - } + seq_printf(m, "%s %s\n", + bus_name, dev_name(dev->dev)); } - return 0; } diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 47db4df37a69..117490590f56 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -96,7 +96,7 @@ int drm_setunique(struct drm_device *dev, void *data, { struct drm_unique *u = data; struct drm_master *master = file_priv->master; - int domain, bus, slot, func, ret; + int ret; if (master->unique_len || master->unique) return -EBUSY; @@ -104,50 +104,12 @@ int drm_setunique(struct drm_device *dev, void *data, if (!u->unique_len || u->unique_len > 1024) return -EINVAL; - master->unique_len = u->unique_len; - master->unique_size = u->unique_len + 1; - master->unique = kmalloc(master->unique_size, GFP_KERNEL); - if (!master->unique) { - ret = -ENOMEM; - goto err; - } - - if (copy_from_user(master->unique, u->unique, master->unique_len)) { - ret = -EFAULT; - goto err; - } - - master->unique[master->unique_len] = '\0'; - - dev->devname = kmalloc(strlen(dev->driver->pci_driver.name) + - strlen(master->unique) + 2, GFP_KERNEL); - if (!dev->devname) { - ret = -ENOMEM; - goto err; - } - - sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name, - master->unique); - - /* Return error if the busid submitted doesn't match the device's actual - * busid. - */ - ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func); - if (ret != 3) { - ret = -EINVAL; - goto err; - } - - domain = bus >> 8; - bus &= 0xff; + if (!dev->driver->bus->set_unique) + return -EINVAL; - if ((domain != drm_get_pci_domain(dev)) || - (bus != dev->pdev->bus->number) || - (slot != PCI_SLOT(dev->pdev->devfn)) || - (func != PCI_FUNC(dev->pdev->devfn))) { - ret = -EINVAL; + ret = dev->driver->bus->set_unique(dev, master, u); + if (ret) goto err; - } return 0; @@ -159,74 +121,15 @@ err: static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv) { struct drm_master *master = file_priv->master; - int len, ret; + int ret; if (master->unique != NULL) drm_unset_busid(dev, master); - if (drm_core_check_feature(dev, DRIVER_USE_PLATFORM_DEVICE)) { - master->unique_len = 10 + strlen(dev->platformdev->name); - master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL); - - if (master->unique == NULL) - return -ENOMEM; - - len = snprintf(master->unique, master->unique_len, - "platform:%s", dev->platformdev->name); - - if (len > master->unique_len) { - DRM_ERROR("Unique buffer overflowed\n"); - ret = -EINVAL; - goto err; - } - - dev->devname = - kmalloc(strlen(dev->platformdev->name) + - master->unique_len + 2, GFP_KERNEL); - - if (dev->devname == NULL) { - ret = -ENOMEM; - goto err; - } - - sprintf(dev->devname, "%s@%s", dev->platformdev->name, - master->unique); - - } else { - master->unique_len = 40; - master->unique_size = master->unique_len; - master->unique = kmalloc(master->unique_size, GFP_KERNEL); - if (master->unique == NULL) - return -ENOMEM; - - len = snprintf(master->unique, master->unique_len, - "pci:%04x:%02x:%02x.%d", - drm_get_pci_domain(dev), - dev->pdev->bus->number, - PCI_SLOT(dev->pdev->devfn), - PCI_FUNC(dev->pdev->devfn)); - if (len >= master->unique_len) { - DRM_ERROR("buffer overflow"); - ret = -EINVAL; - goto err; - } else - master->unique_len = len; - - dev->devname = - kmalloc(strlen(dev->driver->pci_driver.name) + - master->unique_len + 2, GFP_KERNEL); - - if (dev->devname == NULL) { - ret = -ENOMEM; - goto err; - } - - sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name, - master->unique); - } - + ret = dev->driver->bus->set_busid(dev, master); + if (ret) + goto err; return 0; - err: drm_unset_busid(dev, master); return ret; diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 3dadfa2a8528..cb49685bde01 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -74,23 +74,13 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, { struct drm_irq_busid *p = data; - if (drm_core_check_feature(dev, DRIVER_USE_PLATFORM_DEVICE)) + if (!dev->driver->bus->irq_by_busid) return -EINVAL; if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) return -EINVAL; - if ((p->busnum >> 8) != drm_get_pci_domain(dev) || - (p->busnum & 0xff) != dev->pdev->bus->number || - p->devnum != PCI_SLOT(dev->pdev->devfn) || p->funcnum != PCI_FUNC(dev->pdev->devfn)) - return -EINVAL; - - p->irq = dev->pdev->irq; - - DRM_DEBUG("%d:%d:%d => IRQ %d\n", p->busnum, p->devnum, p->funcnum, - p->irq); - - return 0; + return dev->driver->bus->irq_by_busid(dev, p); } /* diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index f5bd9e590c80..e1aee4f6a7c6 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -125,6 +125,176 @@ void drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) EXPORT_SYMBOL(drm_pci_free); #ifdef CONFIG_PCI + +static int drm_get_pci_domain(struct drm_device *dev) +{ +#ifndef __alpha__ + /* For historical reasons, drm_get_pci_domain() is busticated + * on most archs and has to remain so for userspace interface + * < 1.4, except on alpha which was right from the beginning + */ + if (dev->if_version < 0x10004) + return 0; +#endif /* __alpha__ */ + + return pci_domain_nr(dev->pdev->bus); +} + +static int drm_pci_get_irq(struct drm_device *dev) +{ + return dev->pdev->irq; +} + +static const char *drm_pci_get_name(struct drm_device *dev) +{ + struct pci_driver *pdriver = dev->driver->kdriver.pci; + return pdriver->name; +} + +int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master) +{ + int len, ret; + struct pci_driver *pdriver = dev->driver->kdriver.pci; + master->unique_len = 40; + master->unique_size = master->unique_len; + master->unique = kmalloc(master->unique_size, GFP_KERNEL); + if (master->unique == NULL) + return -ENOMEM; + + + len = snprintf(master->unique, master->unique_len, + "pci:%04x:%02x:%02x.%d", + drm_get_pci_domain(dev), + dev->pdev->bus->number, + PCI_SLOT(dev->pdev->devfn), + PCI_FUNC(dev->pdev->devfn)); + + if (len >= master->unique_len) { + DRM_ERROR("buffer overflow"); + ret = -EINVAL; + goto err; + } else + master->unique_len = len; + + dev->devname = + kmalloc(strlen(pdriver->name) + + master->unique_len + 2, GFP_KERNEL); + + if (dev->devname == NULL) { + ret = -ENOMEM; + goto err; + } + + sprintf(dev->devname, "%s@%s", pdriver->name, + master->unique); + + return 0; +err: + return ret; +} + +int drm_pci_set_unique(struct drm_device *dev, + struct drm_master *master, + struct drm_unique *u) +{ + int domain, bus, slot, func, ret; + const char *bus_name; + + master->unique_len = u->unique_len; + master->unique_size = u->unique_len + 1; + master->unique = kmalloc(master->unique_size, GFP_KERNEL); + if (!master->unique) { + ret = -ENOMEM; + goto err; + } + + if (copy_from_user(master->unique, u->unique, master->unique_len)) { + ret = -EFAULT; + goto err; + } + + master->unique[master->unique_len] = '\0'; + + bus_name = dev->driver->bus->get_name(dev); + dev->devname = kmalloc(strlen(bus_name) + + strlen(master->unique) + 2, GFP_KERNEL); + if (!dev->devname) { + ret = -ENOMEM; + goto err; + } + + sprintf(dev->devname, "%s@%s", bus_name, + master->unique); + + /* Return error if the busid submitted doesn't match the device's actual + * busid. + */ + ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func); + if (ret != 3) { + ret = -EINVAL; + goto err; + } + + domain = bus >> 8; + bus &= 0xff; + + if ((domain != drm_get_pci_domain(dev)) || + (bus != dev->pdev->bus->number) || + (slot != PCI_SLOT(dev->pdev->devfn)) || + (func != PCI_FUNC(dev->pdev->devfn))) { + ret = -EINVAL; + goto err; + } + return 0; +err: + return ret; +} + + +int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p) +{ + if ((p->busnum >> 8) != drm_get_pci_domain(dev) || + (p->busnum & 0xff) != dev->pdev->bus->number || + p->devnum != PCI_SLOT(dev->pdev->devfn) || p->funcnum != PCI_FUNC(dev->pdev->devfn)) + return -EINVAL; + + p->irq = dev->pdev->irq; + + DRM_DEBUG("%d:%d:%d => IRQ %d\n", p->busnum, p->devnum, p->funcnum, + p->irq); + return 0; +} + +int drm_pci_agp_init(struct drm_device *dev) +{ + if (drm_core_has_AGP(dev)) { + if (drm_pci_device_is_agp(dev)) + dev->agp = drm_agp_init(dev); + if (drm_core_check_feature(dev, DRIVER_REQUIRE_AGP) + && (dev->agp == NULL)) { + DRM_ERROR("Cannot initialize the agpgart module.\n"); + return -EINVAL; + } + if (drm_core_has_MTRR(dev)) { + if (dev->agp) + dev->agp->agp_mtrr = + mtrr_add(dev->agp->agp_info.aper_base, + dev->agp->agp_info.aper_size * + 1024 * 1024, MTRR_TYPE_WRCOMB, 1); + } + } + return 0; +} + +static struct drm_bus drm_pci_bus = { + .bus_type = DRIVER_BUS_PCI, + .get_irq = drm_pci_get_irq, + .get_name = drm_pci_get_name, + .set_busid = drm_pci_set_busid, + .set_unique = drm_pci_set_unique, + .agp_init = drm_pci_agp_init, +}; + /** * Register. * @@ -219,7 +389,7 @@ err_g1: EXPORT_SYMBOL(drm_get_pci_dev); /** - * PCI device initialization. Called via drm_init at module load time, + * PCI device initialization. Called direct from modules at load time. * * \return zero on success or a negative number on failure. * @@ -229,18 +399,24 @@ EXPORT_SYMBOL(drm_get_pci_dev); * Expands the \c DRIVER_PREINIT and \c DRIVER_POST_INIT macros before and * after the initialization for driver customization. */ -int drm_pci_init(struct drm_driver *driver) +int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) { struct pci_dev *pdev = NULL; const struct pci_device_id *pid; int i; + DRM_DEBUG("\n"); + + INIT_LIST_HEAD(&driver->device_list); + driver->kdriver.pci = pdriver; + driver->bus = &drm_pci_bus; + if (driver->driver_features & DRIVER_MODESET) - return pci_register_driver(&driver->pci_driver); + return pci_register_driver(pdriver); /* If not using KMS, fall back to stealth mode manual scanning. */ - for (i = 0; driver->pci_driver.id_table[i].vendor != 0; i++) { - pid = &driver->pci_driver.id_table[i]; + for (i = 0; pdriver->id_table[i].vendor != 0; i++) { + pid = &pdriver->id_table[i]; /* Loop around setting up a DRM device for each PCI device * matching our ID and device class. If we had the internal @@ -265,10 +441,27 @@ int drm_pci_init(struct drm_driver *driver) #else -int drm_pci_init(struct drm_driver *driver) +int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) { return -1; } #endif + +EXPORT_SYMBOL(drm_pci_init); + /*@}*/ +void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver) +{ + struct drm_device *dev, *tmp; + DRM_DEBUG("\n"); + + if (driver->driver_features & DRIVER_MODESET) { + pci_unregister_driver(pdriver); + } else { + list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item) + drm_put_dev(dev); + } + DRM_INFO("Module unloaded\n"); +} +EXPORT_SYMBOL(drm_pci_exit); diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c index 92d1d0fb7b75..7223f06d8e58 100644 --- a/drivers/gpu/drm/drm_platform.c +++ b/drivers/gpu/drm/drm_platform.c @@ -109,8 +109,60 @@ err_g1: } EXPORT_SYMBOL(drm_get_platform_dev); +static int drm_platform_get_irq(struct drm_device *dev) +{ + return platform_get_irq(dev->platformdev, 0); +} + +static const char *drm_platform_get_name(struct drm_device *dev) +{ + return dev->platformdev->name; +} + +static int drm_platform_set_busid(struct drm_device *dev, struct drm_master *master) +{ + int len, ret; + + master->unique_len = 10 + strlen(dev->platformdev->name); + master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL); + + if (master->unique == NULL) + return -ENOMEM; + + len = snprintf(master->unique, master->unique_len, + "platform:%s", dev->platformdev->name); + + if (len > master->unique_len) { + DRM_ERROR("Unique buffer overflowed\n"); + ret = -EINVAL; + goto err; + } + + dev->devname = + kmalloc(strlen(dev->platformdev->name) + + master->unique_len + 2, GFP_KERNEL); + + if (dev->devname == NULL) { + ret = -ENOMEM; + goto err; + } + + sprintf(dev->devname, "%s@%s", dev->platformdev->name, + master->unique); + return 0; +err: + return ret; +} + +static struct drm_bus drm_platform_bus = { + .bus_type = DRIVER_BUS_PLATFORM, + .get_irq = drm_platform_get_irq, + .get_name = drm_platform_get_name, + .set_busid = drm_platform_set_busid, +}; + /** - * Platform device initialization. Called via drm_init at module load time, + * Platform device initialization. Called direct from modules. * * \return zero on success or a negative number on failure. * @@ -121,7 +173,24 @@ EXPORT_SYMBOL(drm_get_platform_dev); * after the initialization for driver customization. */ -int drm_platform_init(struct drm_driver *driver) +int drm_platform_init(struct drm_driver *driver, struct platform_device *platform_device) { - return drm_get_platform_dev(driver->platform_device, driver); + DRM_DEBUG("\n"); + + driver->kdriver.platform_device = platform_device; + driver->bus = &drm_platform_bus; + INIT_LIST_HEAD(&driver->device_list); + return drm_get_platform_dev(platform_device, driver); +} +EXPORT_SYMBOL(drm_platform_init); + +void drm_platform_exit(struct drm_driver *driver, struct platform_device *platform_device) +{ + struct drm_device *dev, *tmp; + DRM_DEBUG("\n"); + + list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item) + drm_put_dev(dev); + DRM_INFO("Module unloaded\n"); } +EXPORT_SYMBOL(drm_platform_exit); diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index d59edc18301f..0bf2c773c6c5 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -269,25 +269,14 @@ int drm_fill_in_dev(struct drm_device *dev, dev->driver = driver; - if (drm_core_has_AGP(dev)) { - if (drm_device_is_agp(dev)) - dev->agp = drm_agp_init(dev); - if (drm_core_check_feature(dev, DRIVER_REQUIRE_AGP) - && (dev->agp == NULL)) { - DRM_ERROR("Cannot initialize the agpgart module.\n"); - retcode = -EINVAL; + if (dev->driver->bus->agp_init) { + retcode = dev->driver->bus->agp_init(dev); + if (retcode) goto error_out_unreg; - } - if (drm_core_has_MTRR(dev)) { - if (dev->agp) - dev->agp->agp_mtrr = - mtrr_add(dev->agp->agp_info.aper_base, - dev->agp->agp_info.aper_size * - 1024 * 1024, MTRR_TYPE_WRCOMB, 1); - } } + retcode = drm_ctxbitmap_init(dev); if (retcode) { DRM_ERROR("Cannot allocate memory for context bitmap.\n"); @@ -425,7 +414,6 @@ int drm_put_minor(struct drm_minor **minor_p) * * Cleans up all DRM device, calling drm_lastclose(). * - * \sa drm_init */ void drm_put_dev(struct drm_device *dev) { diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c index 0152fa27e616..6f98d059f68a 100644 --- a/drivers/gpu/drm/i810/i810_drv.c +++ b/drivers/gpu/drm/i810/i810_drv.c @@ -64,11 +64,6 @@ static struct drm_driver driver = { .llseek = noop_llseek, }, - .pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, - }, - .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, @@ -77,6 +72,11 @@ static struct drm_driver driver = { .patchlevel = DRIVER_PATCHLEVEL, }; +static struct pci_driver i810_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, +}; + static int __init i810_init(void) { if (num_possible_cpus() > 1) { @@ -84,12 +84,12 @@ static int __init i810_init(void) return -EINVAL; } driver.num_ioctls = i810_max_ioctl; - return drm_init(&driver); + return drm_pci_init(&driver, &i810_pci_driver); } static void __exit i810_exit(void) { - drm_exit(&driver); + drm_pci_exit(&driver, &i810_pci_driver); } module_init(i810_init); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 17fde2f27418..9ad42d583493 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -719,14 +719,6 @@ static struct drm_driver driver = { .llseek = noop_llseek, }, - .pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, - .probe = i915_pci_probe, - .remove = i915_pci_remove, - .driver.pm = &i915_pm_ops, - }, - .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, @@ -735,6 +727,14 @@ static struct drm_driver driver = { .patchlevel = DRIVER_PATCHLEVEL, }; +static struct pci_driver i915_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, + .probe = i915_pci_probe, + .remove = i915_pci_remove, + .driver.pm = &i915_pm_ops, +}; + static int __init i915_init(void) { if (!intel_agp_enabled) { @@ -768,12 +768,12 @@ static int __init i915_init(void) if (!(driver.driver_features & DRIVER_MODESET)) driver.get_vblank_timestamp = NULL; - return drm_init(&driver); + return drm_pci_init(&driver, &i915_pci_driver); } static void __exit i915_exit(void) { - drm_exit(&driver); + drm_pci_exit(&driver, &i915_pci_driver); } module_init(i915_init); diff --git a/drivers/gpu/drm/mga/mga_dma.c b/drivers/gpu/drm/mga/mga_dma.c index 08868ac3048a..1e1eb1d7e971 100644 --- a/drivers/gpu/drm/mga/mga_dma.c +++ b/drivers/gpu/drm/mga/mga_dma.c @@ -703,7 +703,7 @@ static int mga_do_pci_dma_bootstrap(struct drm_device *dev, static int mga_do_dma_bootstrap(struct drm_device *dev, drm_mga_dma_bootstrap_t *dma_bs) { - const int is_agp = (dma_bs->agp_mode != 0) && drm_device_is_agp(dev); + const int is_agp = (dma_bs->agp_mode != 0) && drm_pci_device_is_agp(dev); int err; drm_mga_private_t *const dev_priv = (drm_mga_private_t *) dev->dev_private; diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c index 0aaf5f67a436..42d31874edf2 100644 --- a/drivers/gpu/drm/mga/mga_drv.c +++ b/drivers/gpu/drm/mga/mga_drv.c @@ -75,10 +75,6 @@ static struct drm_driver driver = { #endif .llseek = noop_llseek, }, - .pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, - }, .name = DRIVER_NAME, .desc = DRIVER_DESC, @@ -88,15 +84,20 @@ static struct drm_driver driver = { .patchlevel = DRIVER_PATCHLEVEL, }; +static struct pci_driver mga_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, +}; + static int __init mga_init(void) { driver.num_ioctls = mga_max_ioctl; - return drm_init(&driver); + return drm_pci_init(&driver, &mga_pci_driver); } static void __exit mga_exit(void) { - drm_exit(&driver); + drm_pci_exit(&driver, &mga_pci_driver); } module_init(mga_init); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index f658a04eecf9..155ebdcbf06f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -408,14 +408,6 @@ static struct drm_driver driver = { #endif .llseek = noop_llseek, }, - .pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, - .probe = nouveau_pci_probe, - .remove = nouveau_pci_remove, - .suspend = nouveau_pci_suspend, - .resume = nouveau_pci_resume - }, .gem_init_object = nouveau_gem_object_new, .gem_free_object = nouveau_gem_object_del, @@ -432,6 +424,15 @@ static struct drm_driver driver = { .patchlevel = DRIVER_PATCHLEVEL, }; +static struct pci_driver nouveau_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, + .probe = nouveau_pci_probe, + .remove = nouveau_pci_remove, + .suspend = nouveau_pci_suspend, + .resume = nouveau_pci_resume +}; + static int __init nouveau_init(void) { driver.num_ioctls = nouveau_max_ioctl; @@ -449,7 +450,7 @@ static int __init nouveau_init(void) return 0; nouveau_register_dsm_handler(); - return drm_init(&driver); + return drm_pci_init(&driver, &nouveau_pci_driver); } static void __exit nouveau_exit(void) @@ -457,7 +458,7 @@ static void __exit nouveau_exit(void) if (!nouveau_modeset) return; - drm_exit(&driver); + drm_pci_exit(&driver, &nouveau_pci_driver); nouveau_unregister_dsm_handler(); } diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 26347b7cd872..123969dd4f56 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -480,7 +480,7 @@ nouveau_mem_gart_init(struct drm_device *dev) dev_priv->gart_info.type = NOUVEAU_GART_NONE; #if !defined(__powerpc__) && !defined(__ia64__) - if (drm_device_is_agp(dev) && dev->agp && nouveau_agpmode) { + if (drm_pci_device_is_agp(dev) && dev->agp && nouveau_agpmode) { ret = nouveau_mem_init_agp(dev); if (ret) NV_ERROR(dev, "Error initialising AGP: %d\n", ret); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index a54fc431fe98..2148d01354da 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -1103,9 +1103,9 @@ int nouveau_ioctl_getparam(struct drm_device *dev, void *data, getparam->value = dev->pci_device; break; case NOUVEAU_GETPARAM_BUS_TYPE: - if (drm_device_is_agp(dev)) + if (drm_pci_device_is_agp(dev)) getparam->value = NV_AGP; - else if (drm_device_is_pcie(dev)) + else if (drm_pci_device_is_pcie(dev)) getparam->value = NV_PCIE; else getparam->value = NV_PCI; diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c index 18c3c71e41b1..b9e8efd2b754 100644 --- a/drivers/gpu/drm/r128/r128_drv.c +++ b/drivers/gpu/drm/r128/r128_drv.c @@ -71,10 +71,7 @@ static struct drm_driver driver = { #endif .llseek = noop_llseek, }, - .pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, - }, + .name = DRIVER_NAME, .desc = DRIVER_DESC, @@ -89,16 +86,21 @@ int r128_driver_load(struct drm_device *dev, unsigned long flags) return drm_vblank_init(dev, 1); } +static struct pci_driver r128_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, +}; + static int __init r128_init(void) { driver.num_ioctls = r128_max_ioctl; - return drm_init(&driver); + return drm_pci_init(&driver, &r128_pci_driver); } static void __exit r128_exit(void) { - drm_exit(&driver); + drm_pci_exit(&driver, &r128_pci_driver); } module_init(r128_init); diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c index eb6b9eed7349..3d599e33b9cc 100644 --- a/drivers/gpu/drm/radeon/radeon_cp.c +++ b/drivers/gpu/drm/radeon/radeon_cp.c @@ -2113,9 +2113,9 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags) break; } - if (drm_device_is_agp(dev)) + if (drm_pci_device_is_agp(dev)) dev_priv->flags |= RADEON_IS_AGP; - else if (drm_device_is_pcie(dev)) + else if (drm_pci_device_is_pcie(dev)) dev_priv->flags |= RADEON_IS_PCIE; else dev_priv->flags |= RADEON_IS_PCI; diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index ca1b7d4c1d83..8a0df3d2a4c3 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -238,11 +238,6 @@ static struct drm_driver driver_old = { .llseek = noop_llseek, }, - .pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, - }, - .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, @@ -349,15 +344,6 @@ static struct drm_driver kms_driver = { #endif }, - .pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, - .probe = radeon_pci_probe, - .remove = radeon_pci_remove, - .suspend = radeon_pci_suspend, - .resume = radeon_pci_resume, - }, - .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, @@ -367,15 +353,32 @@ static struct drm_driver kms_driver = { }; static struct drm_driver *driver; +static struct pci_driver *pdriver; + +static struct pci_driver radeon_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, +}; + +static struct pci_driver radeon_kms_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, + .probe = radeon_pci_probe, + .remove = radeon_pci_remove, + .suspend = radeon_pci_suspend, + .resume = radeon_pci_resume, +}; static int __init radeon_init(void) { driver = &driver_old; + pdriver = &radeon_pci_driver; driver->num_ioctls = radeon_max_ioctl; #ifdef CONFIG_VGA_CONSOLE if (vgacon_text_force() && radeon_modeset == -1) { DRM_INFO("VGACON disable radeon kernel modesetting.\n"); driver = &driver_old; + pdriver = &radeon_pci_driver; driver->driver_features &= ~DRIVER_MODESET; radeon_modeset = 0; } @@ -393,18 +396,19 @@ static int __init radeon_init(void) if (radeon_modeset == 1) { DRM_INFO("radeon kernel modesetting enabled.\n"); driver = &kms_driver; + pdriver = &radeon_kms_pci_driver; driver->driver_features |= DRIVER_MODESET; driver->num_ioctls = radeon_max_kms_ioctl; radeon_register_atpx_handler(); } /* if the vga console setting is enabled still * let modprobe override it */ - return drm_init(driver); + return drm_pci_init(driver, pdriver); } static void __exit radeon_exit(void) { - drm_exit(driver); + drm_pci_exit(driver, pdriver); radeon_unregister_atpx_handler(); } diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 8387d32caaa7..4057ebf5195d 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -58,9 +58,9 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags) dev->dev_private = (void *)rdev; /* update BUS flag */ - if (drm_device_is_agp(dev)) { + if (drm_pci_device_is_agp(dev)) { flags |= RADEON_IS_AGP; - } else if (drm_device_is_pcie(dev)) { + } else if (drm_pci_device_is_pcie(dev)) { flags |= RADEON_IS_PCIE; } else { flags |= RADEON_IS_PCI; diff --git a/drivers/gpu/drm/savage/savage_drv.c b/drivers/gpu/drm/savage/savage_drv.c index fa64d25d4248..6464490b240b 100644 --- a/drivers/gpu/drm/savage/savage_drv.c +++ b/drivers/gpu/drm/savage/savage_drv.c @@ -55,11 +55,6 @@ static struct drm_driver driver = { .llseek = noop_llseek, }, - .pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, - }, - .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, @@ -68,15 +63,20 @@ static struct drm_driver driver = { .patchlevel = DRIVER_PATCHLEVEL, }; +static struct pci_driver savage_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, +}; + static int __init savage_init(void) { driver.num_ioctls = savage_max_ioctl; - return drm_init(&driver); + return drm_pci_init(&driver, &savage_pci_driver); } static void __exit savage_exit(void) { - drm_exit(&driver); + drm_pci_exit(&driver, &savage_pci_driver); } module_init(savage_init); diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c index 4caf5d01cfd3..46d5be6e97e5 100644 --- a/drivers/gpu/drm/sis/sis_drv.c +++ b/drivers/gpu/drm/sis/sis_drv.c @@ -82,10 +82,6 @@ static struct drm_driver driver = { .fasync = drm_fasync, .llseek = noop_llseek, }, - .pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, - }, .name = DRIVER_NAME, .desc = DRIVER_DESC, @@ -95,15 +91,20 @@ static struct drm_driver driver = { .patchlevel = DRIVER_PATCHLEVEL, }; +static struct pci_driver sis_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, +}; + static int __init sis_init(void) { driver.num_ioctls = sis_max_ioctl; - return drm_init(&driver); + return drm_pci_init(&driver, &sis_pci_driver); } static void __exit sis_exit(void) { - drm_exit(&driver); + drm_pci_exit(&driver, &sis_pci_driver); } module_init(sis_init); diff --git a/drivers/gpu/drm/tdfx/tdfx_drv.c b/drivers/gpu/drm/tdfx/tdfx_drv.c index b70fa91d761a..8bf98810a8d6 100644 --- a/drivers/gpu/drm/tdfx/tdfx_drv.c +++ b/drivers/gpu/drm/tdfx/tdfx_drv.c @@ -52,10 +52,6 @@ static struct drm_driver driver = { .fasync = drm_fasync, .llseek = noop_llseek, }, - .pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, - }, .name = DRIVER_NAME, .desc = DRIVER_DESC, @@ -65,14 +61,19 @@ static struct drm_driver driver = { .patchlevel = DRIVER_PATCHLEVEL, }; +static struct pci_driver tdfx_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, +}; + static int __init tdfx_init(void) { - return drm_init(&driver); + return drm_pci_init(&driver, &tdfx_pci_driver); } static void __exit tdfx_exit(void) { - drm_exit(&driver); + drm_pci_exit(&driver, &tdfx_pci_driver); } module_init(tdfx_init); diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c index e1ff4e7a6eb0..920a55214bcf 100644 --- a/drivers/gpu/drm/via/via_drv.c +++ b/drivers/gpu/drm/via/via_drv.c @@ -62,10 +62,6 @@ static struct drm_driver driver = { .fasync = drm_fasync, .llseek = noop_llseek, }, - .pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, - }, .name = DRIVER_NAME, .desc = DRIVER_DESC, @@ -75,16 +71,21 @@ static struct drm_driver driver = { .patchlevel = DRIVER_PATCHLEVEL, }; +static struct pci_driver via_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, +}; + static int __init via_init(void) { driver.num_ioctls = via_max_ioctl; via_init_command_verifier(); - return drm_init(&driver); + return drm_pci_init(&driver, &via_pci_driver); } static void __exit via_exit(void) { - drm_exit(&driver); + drm_pci_exit(&driver, &via_pci_driver); } module_init(via_init); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 10ca97ee0206..96949b93d920 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -909,15 +909,6 @@ static struct drm_driver driver = { #endif .llseek = noop_llseek, }, - .pci_driver = { - .name = VMWGFX_DRIVER_NAME, - .id_table = vmw_pci_id_list, - .probe = vmw_probe, - .remove = vmw_remove, - .driver = { - .pm = &vmw_pm_ops - } - }, .name = VMWGFX_DRIVER_NAME, .desc = VMWGFX_DRIVER_DESC, .date = VMWGFX_DRIVER_DATE, @@ -926,6 +917,16 @@ static struct drm_driver driver = { .patchlevel = VMWGFX_DRIVER_PATCHLEVEL }; +static struct pci_driver vmw_pci_driver = { + .name = VMWGFX_DRIVER_NAME, + .id_table = vmw_pci_id_list, + .probe = vmw_probe, + .remove = vmw_remove, + .driver = { + .pm = &vmw_pm_ops + } +}; + static int vmw_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { return drm_get_pci_dev(pdev, ent, &driver); @@ -934,7 +935,7 @@ static int vmw_probe(struct pci_dev *pdev, const struct pci_device_id *ent) static int __init vmwgfx_init(void) { int ret; - ret = drm_init(&driver); + ret = drm_pci_init(&driver, &vmw_pci_driver); if (ret) DRM_ERROR("Failed initializing DRM.\n"); return ret; @@ -942,7 +943,7 @@ static int __init vmwgfx_init(void) static void __exit vmwgfx_exit(void) { - drm_exit(&driver); + drm_pci_exit(&driver, &vmw_pci_driver); } module_init(vmwgfx_init); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 3cbe7a02d2aa..a99aefb9537c 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -145,7 +145,10 @@ extern void drm_ut_debug_printk(unsigned int request_level, #define DRIVER_IRQ_VBL2 0x800 #define DRIVER_GEM 0x1000 #define DRIVER_MODESET 0x2000 -#define DRIVER_USE_PLATFORM_DEVICE 0x4000 + +#define DRIVER_BUS_PCI 0x1 +#define DRIVER_BUS_PLATFORM 0x2 +#define DRIVER_BUS_USB 0x3 /***********************************************************************/ /** \name Begin the DRM... */ @@ -698,6 +701,19 @@ struct drm_master { #define DRM_SCANOUTPOS_INVBL (1 << 1) #define DRM_SCANOUTPOS_ACCURATE (1 << 2) +struct drm_bus { + int bus_type; + int (*get_irq)(struct drm_device *dev); + const char *(*get_name)(struct drm_device *dev); + int (*set_busid)(struct drm_device *dev, struct drm_master *master); + int (*set_unique)(struct drm_device *dev, struct drm_master *master, + struct drm_unique *unique); + int (*irq_by_busid)(struct drm_device *dev, struct drm_irq_busid *p); + /* hooks that are for PCI */ + int (*agp_init)(struct drm_device *dev); + +}; + /** * DRM driver structure. This structure represent the common code for * a family of cards. There will one drm_device for each card present @@ -906,8 +922,12 @@ struct drm_driver { struct drm_ioctl_desc *ioctls; int num_ioctls; struct file_operations fops; - struct pci_driver pci_driver; - struct platform_device *platform_device; + union { + struct pci_driver *pci; + struct platform_device *platform_device; + } kdriver; + struct drm_bus *bus; + /* List of devices hanging off this driver */ struct list_head device_list; }; @@ -1147,28 +1167,9 @@ static __inline__ int drm_core_check_feature(struct drm_device *dev, static inline int drm_dev_to_irq(struct drm_device *dev) { - if (drm_core_check_feature(dev, DRIVER_USE_PLATFORM_DEVICE)) - return platform_get_irq(dev->platformdev, 0); - else - return dev->pdev->irq; + return dev->driver->bus->get_irq(dev); } -static inline int drm_get_pci_domain(struct drm_device *dev) -{ - if (drm_core_check_feature(dev, DRIVER_USE_PLATFORM_DEVICE)) - return 0; - -#ifndef __alpha__ - /* For historical reasons, drm_get_pci_domain() is busticated - * on most archs and has to remain so for userspace interface - * < 1.4, except on alpha which was right from the beginning - */ - if (dev->if_version < 0x10004) - return 0; -#endif /* __alpha__ */ - - return pci_domain_nr(dev->pdev->bus); -} #if __OS_HAS_AGP static inline int drm_core_has_AGP(struct drm_device *dev) @@ -1222,8 +1223,6 @@ static inline int drm_mtrr_del(int handle, unsigned long offset, /*@{*/ /* Driver support (drm_drv.h) */ -extern int drm_init(struct drm_driver *driver); -extern void drm_exit(struct drm_driver *driver); extern long drm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); extern long drm_compat_ioctl(struct file *filp, @@ -1433,11 +1432,7 @@ extern int drm_dropmaster_ioctl(struct drm_device *dev, void *data, struct drm_master *drm_master_create(struct drm_minor *minor); extern struct drm_master *drm_master_get(struct drm_master *master); extern void drm_master_put(struct drm_master **master); -extern int drm_get_pci_dev(struct pci_dev *pdev, - const struct pci_device_id *ent, - struct drm_driver *driver); -extern int drm_get_platform_dev(struct platform_device *pdev, - struct drm_driver *driver); + extern void drm_put_dev(struct drm_device *dev); extern int drm_put_minor(struct drm_minor **minor); extern unsigned int drm_debug; @@ -1628,11 +1623,21 @@ static __inline__ struct drm_local_map *drm_core_findmap(struct drm_device *dev, return NULL; } -static __inline__ int drm_device_is_agp(struct drm_device *dev) +static __inline__ void drm_core_dropmap(struct drm_local_map *map) { - if (drm_core_check_feature(dev, DRIVER_USE_PLATFORM_DEVICE)) - return 0; +} + +#include "drm_mem_util.h" +extern int drm_fill_in_dev(struct drm_device *dev, + const struct pci_device_id *ent, + struct drm_driver *driver); +int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type); +/*@}*/ + +/* PCI section */ +static __inline__ int drm_pci_device_is_agp(struct drm_device *dev) +{ if (dev->driver->device_is_agp != NULL) { int err = (*dev->driver->device_is_agp) (dev); @@ -1644,35 +1649,26 @@ static __inline__ int drm_device_is_agp(struct drm_device *dev) return pci_find_capability(dev->pdev, PCI_CAP_ID_AGP); } -static __inline__ int drm_device_is_pcie(struct drm_device *dev) -{ - if (drm_core_check_feature(dev, DRIVER_USE_PLATFORM_DEVICE)) - return 0; - else - return pci_find_capability(dev->pdev, PCI_CAP_ID_EXP); -} -static __inline__ void drm_core_dropmap(struct drm_local_map *map) +static __inline__ int drm_pci_device_is_pcie(struct drm_device *dev) { + return pci_find_capability(dev->pdev, PCI_CAP_ID_EXP); } -#include "drm_mem_util.h" -static inline void *drm_get_device(struct drm_device *dev) -{ - if (drm_core_check_feature(dev, DRIVER_USE_PLATFORM_DEVICE)) - return dev->platformdev; - else - return dev->pdev; -} - -extern int drm_platform_init(struct drm_driver *driver); -extern int drm_pci_init(struct drm_driver *driver); -extern int drm_fill_in_dev(struct drm_device *dev, +extern int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver); +extern void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver); +extern int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, struct drm_driver *driver); -int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type); -/*@}*/ + + +/* platform section */ +extern int drm_platform_init(struct drm_driver *driver, struct platform_device *platform_device); +extern void drm_platform_exit(struct drm_driver *driver, struct platform_device *platform_device); + +extern int drm_get_platform_dev(struct platform_device *pdev, + struct drm_driver *driver); #endif /* __KERNEL__ */ #endif -- cgit v1.2.3 From a250b9fdc53a286d32e22f21170382a46b3a3ef5 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 15 Dec 2010 07:13:55 +1000 Subject: drm: add usb framework This adds an initial framework to plug USB graphics devices into the drm/kms subsystem. I've started writing a displaylink driver using this interface. Signed-off-by: Dave Airlie --- drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_stub.c | 1 + drivers/gpu/drm/drm_usb.c | 117 +++++++++++++++++++++++++++++++++++++++++++++ include/drm/drmP.h | 2 + include/drm/drm_usb.h | 15 ++++++ 5 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/drm_usb.c create mode 100644 include/drm/drm_usb.h (limited to 'include') diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index d9cb3e314321..89cf05a72d1c 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -12,7 +12,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm_platform.o drm_sysfs.o drm_hashtab.o drm_sman.o drm_mm.o \ drm_crtc.o drm_modes.o drm_edid.o \ drm_info.o drm_debugfs.o drm_encoder_slave.o \ - drm_trace_points.o drm_global.o + drm_trace_points.o drm_global.o drm_usb.o drm-$(CONFIG_COMPAT) += drm_ioc32.o diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 0bf2c773c6c5..001273d57f2d 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -463,6 +463,7 @@ void drm_put_dev(struct drm_device *dev) drm_put_minor(&dev->primary); + list_del(&dev->driver_item); if (dev->devname) { kfree(dev->devname); dev->devname = NULL; diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c new file mode 100644 index 000000000000..206d2300d873 --- /dev/null +++ b/drivers/gpu/drm/drm_usb.c @@ -0,0 +1,117 @@ +#include "drmP.h" +#include + +#ifdef CONFIG_USB +int drm_get_usb_dev(struct usb_interface *interface, + const struct usb_device_id *id, + struct drm_driver *driver) +{ + struct drm_device *dev; + struct usb_device *usbdev; + int ret; + + DRM_DEBUG("\n"); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + usbdev = interface_to_usbdev(interface); + dev->usbdev = usbdev; + dev->dev = &usbdev->dev; + + mutex_lock(&drm_global_mutex); + + ret = drm_fill_in_dev(dev, NULL, driver); + if (ret) { + printk(KERN_ERR "DRM: Fill_in_dev failed.\n"); + goto err_g1; + } + + usb_set_intfdata(interface, dev); + ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL); + if (ret) + goto err_g1; + + ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY); + if (ret) + goto err_g2; + + if (dev->driver->load) { + ret = dev->driver->load(dev, 0); + if (ret) + goto err_g3; + } + + /* setup the grouping for the legacy output */ + ret = drm_mode_group_init_legacy_group(dev, + &dev->primary->mode_group); + if (ret) + goto err_g3; + + list_add_tail(&dev->driver_item, &driver->device_list); + + mutex_unlock(&drm_global_mutex); + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", + driver->name, driver->major, driver->minor, driver->patchlevel, + driver->date, dev->primary->index); + + return 0; + +err_g3: + drm_put_minor(&dev->primary); +err_g2: + drm_put_minor(&dev->control); +err_g1: + kfree(dev); + mutex_unlock(&drm_global_mutex); + return ret; + +} +EXPORT_SYMBOL(drm_get_usb_dev); + +static int drm_usb_get_irq(struct drm_device *dev) +{ + return 0; +} + +static const char *drm_usb_get_name(struct drm_device *dev) +{ + return "USB"; +} + +static int drm_usb_set_busid(struct drm_device *dev, + struct drm_master *master) +{ + return 0; +} + +static struct drm_bus drm_usb_bus = { + .bus_type = DRIVER_BUS_USB, + .get_irq = drm_usb_get_irq, + .get_name = drm_usb_get_name, + .set_busid = drm_usb_set_busid, +}; + +int drm_usb_init(struct drm_driver *driver, struct usb_driver *udriver) +{ + int res; + DRM_DEBUG("\n"); + + INIT_LIST_HEAD(&driver->device_list); + driver->kdriver.usb = udriver; + driver->bus = &drm_usb_bus; + + res = usb_register(udriver); + return res; +} +EXPORT_SYMBOL(drm_usb_init); + +void drm_usb_exit(struct drm_driver *driver, + struct usb_driver *udriver) +{ + usb_deregister(udriver); +} +EXPORT_SYMBOL(drm_usb_exit); +#endif diff --git a/include/drm/drmP.h b/include/drm/drmP.h index a99aefb9537c..52a2fd2f7789 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -925,6 +925,7 @@ struct drm_driver { union { struct pci_driver *pci; struct platform_device *platform_device; + struct usb_driver *usb; } kdriver; struct drm_bus *bus; @@ -1130,6 +1131,7 @@ struct drm_device { #endif struct platform_device *platformdev; /**< Platform device struture */ + struct usb_device *usbdev; struct drm_sg_mem *sg; /**< Scatter gather memory */ int num_crtcs; /**< Number of CRTCs on this device */ diff --git a/include/drm/drm_usb.h b/include/drm/drm_usb.h new file mode 100644 index 000000000000..33506c11da8b --- /dev/null +++ b/include/drm/drm_usb.h @@ -0,0 +1,15 @@ +#ifndef DRM_USB_H +#define DRM_USB_H + +#include + +#include + +extern int drm_usb_init(struct drm_driver *driver, struct usb_driver *udriver); +extern void drm_usb_exit(struct drm_driver *driver, struct usb_driver *udriver); + +int drm_get_usb_dev(struct usb_interface *interface, + const struct usb_device_id *id, + struct drm_driver *driver); + +#endif -- cgit v1.2.3 From 4a3d27e98a7f2682e96d6f863752e0424b00d691 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 27 Jan 2011 23:19:49 -0500 Subject: tracing/filter: Move MAX_FILTER_PRED to local tracing directory The MAX_FILTER_PRED is only needed by the kernel/trace/*.c files. Move it to kernel/trace/trace.h. Cc: Tom Zanussi Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 1 - kernel/trace/trace.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 47e3997f7b5c..1a99e7939c2b 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -208,7 +208,6 @@ struct ftrace_event_call { #define PERF_MAX_TRACE_SIZE 2048 -#define MAX_FILTER_PRED 32 #define MAX_FILTER_STR_VAL 256 /* Should handle KSYM_SYMBOL_LEN */ extern void destroy_preds(struct ftrace_event_call *call); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index d754330934bb..fbff872f8db1 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -680,6 +680,8 @@ struct event_subsystem { #define FILTER_PRED_IS_RIGHT (1 << 15) #define FILTER_PRED_FOLD (1 << 15) +#define MAX_FILTER_PRED 32 + struct filter_pred; struct regex; -- cgit v1.2.3 From ba976970c79fd2fbfe1a4b3b6766a318f4eb9d4c Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Thu, 3 Feb 2011 14:27:20 +1100 Subject: tracing/syscalls: Don't add events for unmapped syscalls FTRACE_SYSCALLS would create events for each and every system call, even if it had failed to map the system call's name with it's number. This resulted in a number of events being created that would not behave as expected. This could happen, for example, on architectures who's symbol names are unusual and will not match the system call name. It could also happen with system calls which were mapped to sys_ni_syscall. This patch changes the default system call number in the metadata to -1. If the system call name from the metadata is not successfully mapped to a system call number during boot, than the event initialisation routine will now return an error, preventing the event from being created. Signed-off-by: Ian Munsie LKML-Reference: <1296703645-18718-2-git-send-email-imunsie@au1.ibm.com> Signed-off-by: Steven Rostedt --- include/linux/syscalls.h | 2 ++ kernel/trace/trace_syscalls.c | 8 ++++++++ 2 files changed, 10 insertions(+) (limited to 'include') diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 98664db1be47..8e8968e74544 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -158,6 +158,7 @@ extern struct trace_event_functions exit_syscall_print_funcs; static struct syscall_metadata __used \ __syscall_meta_##sname = { \ .name = "sys"#sname, \ + .syscall_nr = -1, /* Filled in at boot */ \ .nb_args = nb, \ .types = types_##sname, \ .args = args_##sname, \ @@ -175,6 +176,7 @@ extern struct trace_event_functions exit_syscall_print_funcs; static struct syscall_metadata __used \ __syscall_meta__##sname = { \ .name = "sys_"#sname, \ + .syscall_nr = -1, /* Filled in at boot */ \ .nb_args = 0, \ .enter_event = &event_enter__##sname, \ .exit_event = &event_exit__##sname, \ diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 5c9fe08d2093..a9ceabd52247 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -424,6 +424,14 @@ void unreg_event_syscall_exit(struct ftrace_event_call *call) int init_syscall_trace(struct ftrace_event_call *call) { int id; + int num; + + num = ((struct syscall_metadata *)call->data)->syscall_nr; + if (num < 0 || num >= NR_syscalls) { + pr_debug("syscall %s metadata not mapped, disabling ftrace event\n", + ((struct syscall_metadata *)call->data)->name); + return -ENOSYS; + } if (set_syscall_print_fmt(call) < 0) return -ENOMEM; -- cgit v1.2.3 From e702112ff68a554bcac16bb03ddc2b8e5425bcbf Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 3 Jan 2011 11:14:36 +0200 Subject: Bluetooth: Use non-flushable by default L2CAP data packets Modification of Nick Pelly patch. With Bluetooth 2.1 ACL packets can be flushable or non-flushable. This commit makes ACL data packets non-flushable by default on compatible chipsets, and adds the BT_FLUSHABLE socket option to explicitly request flushable ACL data packets for a given L2CAP socket. This is useful for A2DP data which can be safely discarded if it can not be delivered within a short time (while other ACL data should not be discarded). Note that making ACL data flushable has no effect unless the automatic flush timeout for that ACL link is changed from its default of 0 (infinite). Default packet types (for compatible chipsets): Frame 34: 13 bytes on wire (104 bits), 13 bytes captured (104 bits) Bluetooth HCI H4 Bluetooth HCI ACL Packet .... 0000 0000 0010 = Connection Handle: 0x0002 ..00 .... .... .... = PB Flag: First Non-automatically Flushable Packet (0) 00.. .... .... .... = BC Flag: Point-To-Point (0) Data Total Length: 8 Bluetooth L2CAP Packet After setting BT_FLUSHABLE (sock.setsockopt(274 /*SOL_BLUETOOTH*/, 8 /* BT_FLUSHABLE */, 1 /* flush */)) Frame 34: 13 bytes on wire (104 bits), 13 bytes captured (104 bits) Bluetooth HCI H4 Bluetooth HCI ACL Packet .... 0000 0000 0010 = Connection Handle: 0x0002 ..10 .... .... .... = PB Flag: First Automatically Flushable Packet (2) 00.. .... .... .... = BC Flag: Point-To-Point (0) Data Total Length: 8 Bluetooth L2CAP Packet Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/bluetooth.h | 5 ++++ include/net/bluetooth/hci.h | 2 ++ include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/l2cap.h | 1 + net/bluetooth/hci_core.c | 7 +++-- net/bluetooth/l2cap.c | 59 ++++++++++++++++++++++++++++++++++++--- 6 files changed, 69 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 0c5e72503b77..ed7d775337e0 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -64,6 +64,11 @@ struct bt_security { #define BT_DEFER_SETUP 7 +#define BT_FLUSHABLE 8 + +#define BT_FLUSHABLE_OFF 0 +#define BT_FLUSHABLE_ON 1 + #define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg) #define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg) #define BT_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 29a7a8ca0438..5d033dc9d43b 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -150,6 +150,7 @@ enum { #define EDR_ESCO_MASK (ESCO_2EV3 | ESCO_3EV3 | ESCO_2EV5 | ESCO_3EV5) /* ACL flags */ +#define ACL_START_NO_FLUSH 0x00 #define ACL_CONT 0x01 #define ACL_START 0x02 #define ACL_ACTIVE_BCAST 0x04 @@ -194,6 +195,7 @@ enum { #define LMP_EDR_3S_ESCO 0x80 #define LMP_SIMPLE_PAIR 0x08 +#define LMP_NO_FLUSH 0x40 /* Connection modes */ #define HCI_CM_ACTIVE 0x0000 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d2cf88407690..4e14610baece 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -458,6 +458,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR) #define lmp_esco_capable(dev) ((dev)->features[3] & LMP_ESCO) #define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR) +#define lmp_no_flush_capable(dev) ((dev)->features[6] & LMP_NO_FLUSH) /* ----- HCI protocols ----- */ struct hci_proto { diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 7ad25ca60ec0..7f88a87d7a46 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -327,6 +327,7 @@ struct l2cap_pinfo { __u8 sec_level; __u8 role_switch; __u8 force_reliable; + __u8 flushable; __u8 conf_req[64]; __u8 conf_len; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9c4541bc488a..9ba92adaa9ad 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1395,7 +1395,7 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags) skb->dev = (void *) hdev; bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; - hci_add_acl_hdr(skb, conn->handle, flags | ACL_START); + hci_add_acl_hdr(skb, conn->handle, flags); list = skb_shinfo(skb)->frag_list; if (!list) { @@ -1413,12 +1413,15 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags) spin_lock_bh(&conn->data_q.lock); __skb_queue_tail(&conn->data_q, skb); + + flags &= ~ACL_START; + flags |= ACL_CONT; do { skb = list; list = list->next; skb->dev = (void *) hdev; bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; - hci_add_acl_hdr(skb, conn->handle, flags | ACL_CONT); + hci_add_acl_hdr(skb, conn->handle, flags); BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 675614e38e14..4bf98dfd24bc 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -373,13 +373,19 @@ static inline u8 l2cap_get_ident(struct l2cap_conn *conn) static inline void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) { struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data); + u8 flags; BT_DBG("code 0x%2.2x", code); if (!skb) return; - hci_send_acl(conn->hcon, skb, 0); + if (lmp_no_flush_capable(conn->hcon->hdev)) + flags = ACL_START_NO_FLUSH; + else + flags = ACL_START; + + hci_send_acl(conn->hcon, skb, flags); } static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) @@ -389,6 +395,7 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) struct l2cap_conn *conn = pi->conn; struct sock *sk = (struct sock *)pi; int count, hlen = L2CAP_HDR_SIZE + 2; + u8 flags; if (sk->sk_state != BT_CONNECTED) return; @@ -425,7 +432,12 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) put_unaligned_le16(fcs, skb_put(skb, 2)); } - hci_send_acl(pi->conn->hcon, skb, 0); + if (lmp_no_flush_capable(conn->hcon->hdev)) + flags = ACL_START_NO_FLUSH; + else + flags = ACL_START; + + hci_send_acl(pi->conn->hcon, skb, flags); } static inline void l2cap_send_rr_or_rnr(struct l2cap_pinfo *pi, u16 control) @@ -912,6 +924,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) pi->sec_level = l2cap_pi(parent)->sec_level; pi->role_switch = l2cap_pi(parent)->role_switch; pi->force_reliable = l2cap_pi(parent)->force_reliable; + pi->flushable = l2cap_pi(parent)->flushable; } else { pi->imtu = L2CAP_DEFAULT_MTU; pi->omtu = 0; @@ -927,6 +940,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) pi->sec_level = BT_SECURITY_LOW; pi->role_switch = 0; pi->force_reliable = 0; + pi->flushable = BT_FLUSHABLE_OFF; } /* Default config options */ @@ -1431,10 +1445,17 @@ static void l2cap_drop_acked_frames(struct sock *sk) static inline void l2cap_do_send(struct sock *sk, struct sk_buff *skb) { struct l2cap_pinfo *pi = l2cap_pi(sk); + struct hci_conn *hcon = pi->conn->hcon; + u16 flags; BT_DBG("sk %p, skb %p len %d", sk, skb, skb->len); - hci_send_acl(pi->conn->hcon, skb, 0); + if (!pi->flushable && lmp_no_flush_capable(hcon->hdev)) + flags = ACL_START_NO_FLUSH; + else + flags = ACL_START; + + hci_send_acl(hcon, skb, flags); } static void l2cap_streaming_send(struct sock *sk) @@ -2079,6 +2100,30 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch bt_sk(sk)->defer_setup = opt; break; + case BT_FLUSHABLE: + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + + if (opt > BT_FLUSHABLE_ON) { + err = -EINVAL; + break; + } + + if (opt == BT_FLUSHABLE_OFF) { + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + /* proceed futher only when we have l2cap_conn and + No Flush support in the LM */ + if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) { + err = -EINVAL; + break; + } + } + + l2cap_pi(sk)->flushable = opt; + break; + default: err = -ENOPROTOOPT; break; @@ -2218,6 +2263,12 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch break; + case BT_FLUSHABLE: + if (put_user(l2cap_pi(sk)->flushable, (u32 __user *) optval)) + err = -EFAULT; + + break; + default: err = -ENOPROTOOPT; break; @@ -4678,7 +4729,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags); - if (flags & ACL_START) { + if (!(flags & ACL_CONT)) { struct l2cap_hdr *hdr; struct sock *sk; u16 cid; -- cgit v1.2.3 From ab81cbf99c881ca2b9a83682a8722fc84b2483d2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Dec 2010 13:53:18 +0200 Subject: Bluetooth: Implement automatic setup procedure for local adapters This patch implements automatic initialization of basic information about newly registered Bluetooth adapters. E.g. the address and features are always needed so it makes sense for the kernel to automatically power on adapters and read this information. A new HCI_SETUP flag is added to track this state. In order to not consume unnecessary amounts of power if there isn't a user space available that could switch the adapter back off, a timer is added to do this automatically as long as no Bluetooth user space seems to be present. A new HCI_AUTO_OFF flag is added that user space needs to clear to avoid the automatic power off. Additionally, the management interface index_added event is moved to the end of the HCI_SETUP stage so a user space supporting the managment inteface has all the necessary information available for fetching when it gets notified of a new adapter. The HCI_DEV_REG event is kept in the same place as before since existing HCI raw socket based user space versions depend on seeing the kernels initialization sequence (hci_init_req) to determine when the adapter is ready for use. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 3 ++ include/net/bluetooth/hci_core.h | 6 ++++ net/bluetooth/hci_core.c | 64 ++++++++++++++++++++++++++++++++++++++-- net/bluetooth/mgmt.c | 8 +++++ 4 files changed, 79 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 5d033dc9d43b..51c9df16e764 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -76,6 +76,9 @@ enum { HCI_INQUIRY, HCI_RAW, + + HCI_SETUP, + HCI_AUTO_OFF, }; /* HCI ioctl defines */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4e14610baece..75c4f201c1c6 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -114,6 +114,10 @@ struct hci_dev { struct workqueue_struct *workqueue; + struct work_struct power_on; + struct work_struct power_off; + struct timer_list off_timer; + struct tasklet_struct cmd_task; struct tasklet_struct rx_task; struct tasklet_struct tx_task; @@ -437,6 +441,8 @@ int hci_inquiry(void __user *arg); struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_blacklist_clear(struct hci_dev *hdev); +void hci_del_off_timer(struct hci_dev *hdev); + void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); int hci_recv_frame(struct sk_buff *skb); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9ba92adaa9ad..b22ce9f8bf91 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -50,6 +50,8 @@ #include #include +#define AUTO_OFF_TIMEOUT 2000 + static void hci_cmd_task(unsigned long arg); static void hci_rx_task(unsigned long arg); static void hci_tx_task(unsigned long arg); @@ -794,6 +796,7 @@ int hci_get_dev_list(void __user *arg) list_for_each(p, &hci_dev_list) { struct hci_dev *hdev; hdev = list_entry(p, struct hci_dev, list); + hci_del_off_timer(hdev); (dr + n)->dev_id = hdev->id; (dr + n)->dev_opt = hdev->flags; if (++n >= dev_num) @@ -823,6 +826,8 @@ int hci_get_dev_info(void __user *arg) if (!hdev) return -ENODEV; + hci_del_off_timer(hdev); + strcpy(di.name, hdev->name); di.bdaddr = hdev->bdaddr; di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4); @@ -891,6 +896,51 @@ void hci_free_dev(struct hci_dev *hdev) } EXPORT_SYMBOL(hci_free_dev); +static void hci_power_on(struct work_struct *work) +{ + struct hci_dev *hdev = container_of(work, struct hci_dev, power_on); + + BT_DBG("%s", hdev->name); + + if (hci_dev_open(hdev->id) < 0) + return; + + if (test_bit(HCI_AUTO_OFF, &hdev->flags)) + mod_timer(&hdev->off_timer, + jiffies + msecs_to_jiffies(AUTO_OFF_TIMEOUT)); + + if (test_and_clear_bit(HCI_SETUP, &hdev->flags)) + mgmt_index_added(hdev->id); +} + +static void hci_power_off(struct work_struct *work) +{ + struct hci_dev *hdev = container_of(work, struct hci_dev, power_off); + + BT_DBG("%s", hdev->name); + + hci_dev_close(hdev->id); +} + +static void hci_auto_off(unsigned long data) +{ + struct hci_dev *hdev = (struct hci_dev *) data; + + BT_DBG("%s", hdev->name); + + clear_bit(HCI_AUTO_OFF, &hdev->flags); + + queue_work(hdev->workqueue, &hdev->power_off); +} + +void hci_del_off_timer(struct hci_dev *hdev) +{ + BT_DBG("%s", hdev->name); + + clear_bit(HCI_AUTO_OFF, &hdev->flags); + del_timer(&hdev->off_timer); +} + /* Register HCI device */ int hci_register_dev(struct hci_dev *hdev) { @@ -948,6 +998,10 @@ int hci_register_dev(struct hci_dev *hdev) INIT_LIST_HEAD(&hdev->blacklist); + INIT_WORK(&hdev->power_on, hci_power_on); + INIT_WORK(&hdev->power_off, hci_power_off); + setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev); + memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); atomic_set(&hdev->promisc, 0); @@ -969,7 +1023,10 @@ int hci_register_dev(struct hci_dev *hdev) } } - mgmt_index_added(hdev->id); + set_bit(HCI_AUTO_OFF, &hdev->flags); + set_bit(HCI_SETUP, &hdev->flags); + queue_work(hdev->workqueue, &hdev->power_on); + hci_notify(hdev, HCI_DEV_REG); return id; @@ -999,7 +1056,10 @@ int hci_unregister_dev(struct hci_dev *hdev) for (i = 0; i < NUM_REASSEMBLY; i++) kfree_skb(hdev->reassembly[i]); - mgmt_index_removed(hdev->id); + if (!test_bit(HCI_INIT, &hdev->flags) && + !test_bit(HCI_SETUP, &hdev->flags)) + mgmt_index_removed(hdev->id); + hci_notify(hdev, HCI_DEV_UNREG); if (hdev->rfkill) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ace872615c06..d479e241a9de 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -129,6 +129,12 @@ static int read_index_list(struct sock *sk) i = 0; list_for_each(p, &hci_dev_list) { struct hci_dev *d = list_entry(p, struct hci_dev, list); + + hci_del_off_timer(d); + + if (test_bit(HCI_SETUP, &d->flags)) + continue; + put_unaligned_le16(d->id, &rp->index[i++]); BT_DBG("Added hci%u", d->id); } @@ -180,6 +186,8 @@ static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV); } + hci_del_off_timer(hdev); + hci_dev_lock_bh(hdev); put_unaligned_le16(hdev->id, &rp->index); -- cgit v1.2.3 From 5add6af8fcbce269cac2457584c0ebfda055474a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Dec 2010 10:00:37 +0200 Subject: Bluetooth: Add support for management powered event This patch adds support for the powered event that's used to indicate to userspace when the powered state of a local adapter changes. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/mgmt.h | 6 ++++++ net/bluetooth/hci_core.c | 4 ++++ net/bluetooth/mgmt.c | 10 ++++++++++ 4 files changed, 21 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 75c4f201c1c6..32e11b37ef28 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -673,6 +673,7 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); int mgmt_index_added(u16 index); int mgmt_index_removed(u16 index); +int mgmt_powered(u16 index, u8 powered); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index ca29c1367ffd..0ac1520573ed 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -85,3 +85,9 @@ struct mgmt_ev_index_added { struct mgmt_ev_index_removed { __le16 index; } __packed; + +#define MGMT_EV_POWERED 0x0006 +struct mgmt_ev_powered { + __le16 index; + __u8 powered; +} __packed; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b22ce9f8bf91..c5a78e797bc2 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -535,6 +535,8 @@ int hci_dev_open(__u16 dev) hci_dev_hold(hdev); set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); + if (!test_bit(HCI_SETUP, &hdev->flags)) + mgmt_powered(hdev->id, 1); } else { /* Init failed, cleanup */ tasklet_kill(&hdev->rx_task); @@ -616,6 +618,8 @@ static int hci_dev_do_close(struct hci_dev *hdev) * and no tasks are scheduled. */ hdev->close(hdev); + mgmt_powered(hdev->id, 0); + /* Clear flags */ hdev->flags = 0; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d479e241a9de..f746e19ebec0 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -316,3 +316,13 @@ int mgmt_index_removed(u16 index) return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev)); } + +int mgmt_powered(u16 index, u8 powered) +{ + struct mgmt_ev_powered ev; + + put_unaligned_le16(index, &ev.index); + ev.powered = powered; + + return mgmt_event(MGMT_EV_POWERED, &ev, sizeof(ev)); +} -- cgit v1.2.3 From eec8d2bcc841ae44edcde9660ff21144a2016053 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Dec 2010 10:17:38 +0200 Subject: Bluetooth: Add support for set_powered management command This patch adds a set_powered command to the management interface through which the powered state of local adapters can be controlled. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 3 +- include/net/bluetooth/mgmt.h | 10 ++ net/bluetooth/hci_core.c | 4 +- net/bluetooth/hci_event.c | 2 +- net/bluetooth/hci_sock.c | 6 +- net/bluetooth/mgmt.c | 200 ++++++++++++++++++++++++++++++++++++++- 6 files changed, 215 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 32e11b37ef28..2d046e07a586 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -667,7 +667,8 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data); /* ----- HCI Sockets ----- */ -void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); +void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb, + struct sock *skip_sk); /* Management interface */ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 0ac1520573ed..81ef78918b66 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -58,6 +58,16 @@ struct mgmt_rp_read_info { __u16 hci_rev; } __packed; +#define MGMT_OP_SET_POWERED 0x0005 +struct mgmt_cp_set_powered { + __le16 index; + __u8 powered; +} __packed; +struct mgmt_rp_set_powered { + __le16 index; + __u8 powered; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index c5a78e797bc2..dfc4ef90deca 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1377,7 +1377,7 @@ static int hci_send_frame(struct sk_buff *skb) /* Time stamp */ __net_timestamp(skb); - hci_send_to_sock(hdev, skb); + hci_send_to_sock(hdev, skb, NULL); } /* Get rid of skb owner, prior to sending to the driver. */ @@ -1767,7 +1767,7 @@ static void hci_rx_task(unsigned long arg) while ((skb = skb_dequeue(&hdev->rx_q))) { if (atomic_read(&hdev->promisc)) { /* Send copy to the sockets */ - hci_send_to_sock(hdev, skb); + hci_send_to_sock(hdev, skb, NULL); } if (test_bit(HCI_RAW, &hdev->flags)) { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a290854fdaa6..d42fb35309b5 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2083,6 +2083,6 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) bt_cb(skb)->pkt_type = HCI_EVENT_PKT; skb->dev = (void *) hdev; - hci_send_to_sock(hdev, skb); + hci_send_to_sock(hdev, skb, NULL); kfree_skb(skb); } diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 29827c77f6ce..d50e96136608 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -85,7 +85,8 @@ static struct bt_sock_list hci_sk_list = { }; /* Send frame to RAW socket */ -void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) +void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb, + struct sock *skip_sk) { struct sock *sk; struct hlist_node *node; @@ -97,6 +98,9 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) struct hci_filter *flt; struct sk_buff *nskb; + if (sk == skip_sk) + continue; + if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev) continue; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f746e19ebec0..b65b6ca08463 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -32,6 +32,16 @@ #define MGMT_VERSION 0 #define MGMT_REVISION 1 +struct pending_cmd { + struct list_head list; + __u16 opcode; + int index; + void *cmd; + struct sock *sk; +}; + +LIST_HEAD(cmd_list); + static int cmd_status(struct sock *sk, u16 cmd, u8 status) { struct sk_buff *skb; @@ -220,6 +230,129 @@ static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) return 0; } +static void mgmt_pending_free(struct pending_cmd *cmd) +{ + sock_put(cmd->sk); + kfree(cmd->cmd); + kfree(cmd); +} + +static int mgmt_pending_add(struct sock *sk, u16 opcode, int index, + void *data, u16 len) +{ + struct pending_cmd *cmd; + + cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC); + if (!cmd) + return -ENOMEM; + + cmd->opcode = opcode; + cmd->index = index; + + cmd->cmd = kmalloc(len, GFP_ATOMIC); + if (!cmd->cmd) { + kfree(cmd); + return -ENOMEM; + } + + memcpy(cmd->cmd, data, len); + + cmd->sk = sk; + sock_hold(sk); + + list_add(&cmd->list, &cmd_list); + + return 0; +} + +static void mgmt_pending_foreach(u16 opcode, int index, + void (*cb)(struct pending_cmd *cmd, void *data), + void *data) +{ + struct list_head *p, *n; + + list_for_each_safe(p, n, &cmd_list) { + struct pending_cmd *cmd; + + cmd = list_entry(p, struct pending_cmd, list); + + if (cmd->opcode != opcode) + continue; + + if (index >= 0 && cmd->index != index) + continue; + + cb(cmd, data); + } +} + +static struct pending_cmd *mgmt_pending_find(u16 opcode, int index) +{ + struct list_head *p; + + list_for_each(p, &cmd_list) { + struct pending_cmd *cmd; + + cmd = list_entry(p, struct pending_cmd, list); + + if (cmd->opcode != opcode) + continue; + + if (index >= 0 && cmd->index != index) + continue; + + return cmd; + } + + return NULL; +} + +static int set_powered(struct sock *sk, unsigned char *data, u16 len) +{ + struct mgmt_cp_set_powered *cp; + struct hci_dev *hdev; + u16 dev_id; + int ret, up; + + cp = (void *) data; + dev_id = get_unaligned_le16(&cp->index); + + BT_DBG("request for hci%u", dev_id); + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, MGMT_OP_SET_POWERED, ENODEV); + + hci_dev_lock_bh(hdev); + + up = test_bit(HCI_UP, &hdev->flags); + if ((cp->powered && up) || (!cp->powered && !up)) { + ret = cmd_status(sk, MGMT_OP_SET_POWERED, EALREADY); + goto failed; + } + + if (mgmt_pending_find(MGMT_OP_SET_POWERED, dev_id)) { + ret = cmd_status(sk, MGMT_OP_SET_POWERED, EBUSY); + goto failed; + } + + ret = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, dev_id, data, len); + if (ret < 0) + goto failed; + + if (cp->powered) + queue_work(hdev->workqueue, &hdev->power_on); + else + queue_work(hdev->workqueue, &hdev->power_off); + + ret = 0; + +failed: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + return ret; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -260,6 +393,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_READ_INFO: err = read_controller_info(sk, buf + sizeof(*hdr), len); break; + case MGMT_OP_SET_POWERED: + err = set_powered(sk, buf + sizeof(*hdr), len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, opcode, 0x01); @@ -276,7 +412,7 @@ done: return err; } -static int mgmt_event(u16 event, void *data, u16 data_len) +static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk) { struct sk_buff *skb; struct mgmt_hdr *hdr; @@ -293,7 +429,7 @@ static int mgmt_event(u16 event, void *data, u16 data_len) memcpy(skb_put(skb, data_len), data, data_len); - hci_send_to_sock(NULL, skb); + hci_send_to_sock(NULL, skb, skip_sk); kfree_skb(skb); return 0; @@ -305,7 +441,7 @@ int mgmt_index_added(u16 index) put_unaligned_le16(index, &ev.index); - return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev)); + return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev), NULL); } int mgmt_index_removed(u16 index) @@ -314,15 +450,69 @@ int mgmt_index_removed(u16 index) put_unaligned_le16(index, &ev.index); - return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev)); + return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev), NULL); +} + +struct powered_lookup { + u8 powered; + struct sock *sk; +}; + +static void power_rsp(struct pending_cmd *cmd, void *data) +{ + struct mgmt_hdr *hdr; + struct mgmt_ev_cmd_complete *ev; + struct mgmt_rp_set_powered *rp; + struct mgmt_cp_set_powered *cp = cmd->cmd; + struct sk_buff *skb; + struct powered_lookup *match = data; + + if (cp->powered != match->powered) + return; + + skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); + if (!skb) + return; + + hdr = (void *) skb_put(skb, sizeof(*hdr)); + hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); + hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); + + ev = (void *) skb_put(skb, sizeof(*ev)); + put_unaligned_le16(cmd->opcode, &ev->opcode); + + rp = (void *) skb_put(skb, sizeof(*rp)); + put_unaligned_le16(cmd->index, &rp->index); + rp->powered = cp->powered; + + if (sock_queue_rcv_skb(cmd->sk, skb) < 0) + kfree_skb(skb); + + list_del(&cmd->list); + + if (match->sk == NULL) { + match->sk = cmd->sk; + sock_hold(match->sk); + } + + mgmt_pending_free(cmd); } int mgmt_powered(u16 index, u8 powered) { struct mgmt_ev_powered ev; + struct powered_lookup match = { powered, NULL }; + int ret; put_unaligned_le16(index, &ev.index); ev.powered = powered; - return mgmt_event(MGMT_EV_POWERED, &ev, sizeof(ev)); + mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, power_rsp, &match); + + ret = mgmt_event(MGMT_EV_POWERED, &ev, sizeof(ev), match.sk); + + if (match.sk) + sock_put(match.sk); + + return ret; } -- cgit v1.2.3 From 73f22f62388795c0f6b4f3f97bda7a64f9681aac Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 29 Dec 2010 16:00:25 +0200 Subject: Bluetooth: Add support for set_discoverable management command This patch adds a set_discoverable command to the management interface as well as the corresponding event. The command is used to control the discoverable state of adapters. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/mgmt.h | 16 +++++ net/bluetooth/hci_event.c | 5 +- net/bluetooth/mgmt.c | 142 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 158 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 2d046e07a586..ee5ec4f17a15 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -675,6 +675,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); int mgmt_index_added(u16 index); int mgmt_index_removed(u16 index); int mgmt_powered(u16 index, u8 powered); +int mgmt_discoverable(u16 index, u8 discoverable); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 81ef78918b66..434dbcf28b6e 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -68,6 +68,16 @@ struct mgmt_rp_set_powered { __u8 powered; } __packed; +#define MGMT_OP_SET_DISCOVERABLE 0x0006 +struct mgmt_cp_set_discoverable { + __le16 index; + __u8 discoverable; +} __packed; +struct mgmt_rp_set_discoverable { + __le16 index; + __u8 discoverable; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; @@ -101,3 +111,9 @@ struct mgmt_ev_powered { __le16 index; __u8 powered; } __packed; + +#define MGMT_EV_DISCOVERABLE 0x0007 +struct mgmt_ev_discoverable { + __le16 index; + __u8 discoverable; +} __packed; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d42fb35309b5..f55004af0558 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -278,8 +278,11 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_PSCAN, &hdev->flags); clear_bit(HCI_ISCAN, &hdev->flags); - if (param & SCAN_INQUIRY) + if (param & SCAN_INQUIRY) { set_bit(HCI_ISCAN, &hdev->flags); + mgmt_discoverable(hdev->id, 1); + } else + mgmt_discoverable(hdev->id, 0); if (param & SCAN_PAGE) set_bit(HCI_PSCAN, &hdev->flags); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b65b6ca08463..5fa3034fe79f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -307,6 +307,18 @@ static struct pending_cmd *mgmt_pending_find(u16 opcode, int index) return NULL; } +static void mgmt_pending_remove(u16 opcode, int index) +{ + struct pending_cmd *cmd; + + cmd = mgmt_pending_find(opcode, index); + if (cmd == NULL) + return; + + list_del(&cmd->list); + mgmt_pending_free(cmd); +} + static int set_powered(struct sock *sk, unsigned char *data, u16 len) { struct mgmt_cp_set_powered *cp; @@ -353,6 +365,63 @@ failed: return ret; } +static int set_discoverable(struct sock *sk, unsigned char *data, u16 len) +{ + struct mgmt_cp_set_discoverable *cp; + struct hci_dev *hdev; + u16 dev_id; + u8 scan; + int err; + + cp = (void *) data; + dev_id = get_unaligned_le16(&cp->index); + + BT_DBG("request for hci%u", dev_id); + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, ENODEV); + + hci_dev_lock_bh(hdev); + + if (!test_bit(HCI_UP, &hdev->flags)) { + err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, ENETDOWN); + goto failed; + } + + if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) || + mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id) || + hci_sent_cmd_data(hdev, HCI_OP_WRITE_SCAN_ENABLE)) { + err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EBUSY); + goto failed; + } + + if (cp->discoverable == test_bit(HCI_ISCAN, &hdev->flags) && + test_bit(HCI_PSCAN, &hdev->flags)) { + err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EALREADY); + goto failed; + } + + err = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, dev_id, data, len); + if (err < 0) + goto failed; + + scan = SCAN_PAGE; + + if (cp->discoverable) + scan |= SCAN_INQUIRY; + + err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); + if (err < 0) + mgmt_pending_remove(MGMT_OP_SET_DISCOVERABLE, dev_id); + +failed: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -396,6 +465,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_SET_POWERED: err = set_powered(sk, buf + sizeof(*hdr), len); break; + case MGMT_OP_SET_DISCOVERABLE: + err = set_discoverable(sk, buf + sizeof(*hdr), len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, opcode, 0x01); @@ -453,8 +525,8 @@ int mgmt_index_removed(u16 index) return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev), NULL); } -struct powered_lookup { - u8 powered; +struct cmd_lookup { + u8 value; struct sock *sk; }; @@ -465,9 +537,9 @@ static void power_rsp(struct pending_cmd *cmd, void *data) struct mgmt_rp_set_powered *rp; struct mgmt_cp_set_powered *cp = cmd->cmd; struct sk_buff *skb; - struct powered_lookup *match = data; + struct cmd_lookup *match = data; - if (cp->powered != match->powered) + if (cp->powered != match->value) return; skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); @@ -501,7 +573,7 @@ static void power_rsp(struct pending_cmd *cmd, void *data) int mgmt_powered(u16 index, u8 powered) { struct mgmt_ev_powered ev; - struct powered_lookup match = { powered, NULL }; + struct cmd_lookup match = { powered, NULL }; int ret; put_unaligned_le16(index, &ev.index); @@ -516,3 +588,63 @@ int mgmt_powered(u16 index, u8 powered) return ret; } + +static void discoverable_rsp(struct pending_cmd *cmd, void *data) +{ + struct mgmt_cp_set_discoverable *cp = cmd->cmd; + struct cmd_lookup *match = data; + struct sk_buff *skb; + struct mgmt_hdr *hdr; + struct mgmt_ev_cmd_complete *ev; + struct mgmt_rp_set_discoverable *rp; + + if (cp->discoverable != match->value) + return; + + skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); + if (!skb) + return; + + hdr = (void *) skb_put(skb, sizeof(*hdr)); + hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); + hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); + + ev = (void *) skb_put(skb, sizeof(*ev)); + put_unaligned_le16(MGMT_OP_SET_DISCOVERABLE, &ev->opcode); + + rp = (void *) skb_put(skb, sizeof(*rp)); + put_unaligned_le16(cmd->index, &rp->index); + rp->discoverable = cp->discoverable; + + if (sock_queue_rcv_skb(cmd->sk, skb) < 0) + kfree_skb(skb); + + list_del(&cmd->list); + + if (match->sk == NULL) { + match->sk = cmd->sk; + sock_hold(match->sk); + } + + mgmt_pending_free(cmd); +} + +int mgmt_discoverable(u16 index, u8 discoverable) +{ + struct mgmt_ev_discoverable ev; + struct cmd_lookup match = { discoverable, NULL }; + int ret; + + put_unaligned_le16(index, &ev.index); + ev.discoverable = discoverable; + + mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index, + discoverable_rsp, &match); + + ret = mgmt_event(MGMT_EV_DISCOVERABLE, &ev, sizeof(ev), match.sk); + + if (match.sk) + sock_put(match.sk); + + return ret; +} -- cgit v1.2.3 From 9fbcbb455dd01abfad4f314b618ac51d566114cb Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 30 Dec 2010 00:18:33 +0200 Subject: Bluetooth: Add set_connectable management command This patch adds a set_connectable command as well as a corresponding event to the management interface. It's mainly useful for setting an adapter as connectable from a non-initialized state as well as setting an already initialized adapter as non-connectable (mostly useful for qualification purposes). Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/mgmt.h | 17 ++++++ net/bluetooth/hci_event.c | 16 +++-- net/bluetooth/mgmt.c | 122 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 149 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ee5ec4f17a15..ba3dbe3188ed 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -676,6 +676,7 @@ int mgmt_index_added(u16 index); int mgmt_index_removed(u16 index); int mgmt_powered(u16 index, u8 powered); int mgmt_discoverable(u16 index, u8 discoverable); +int mgmt_connectable(u16 index, u8 connectable); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 434dbcf28b6e..008acf54147a 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -47,6 +47,7 @@ struct mgmt_rp_read_info { __le16 index; __u8 type; __u8 powered; + __u8 connectable; __u8 discoverable; __u8 pairable; __u8 sec_mode; @@ -78,6 +79,16 @@ struct mgmt_rp_set_discoverable { __u8 discoverable; } __packed; +#define MGMT_OP_SET_CONNECTABLE 0x0007 +struct mgmt_cp_set_connectable { + __le16 index; + __u8 connectable; +} __packed; +struct mgmt_rp_set_connectable { + __le16 index; + __u8 connectable; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; @@ -117,3 +128,9 @@ struct mgmt_ev_discoverable { __le16 index; __u8 discoverable; } __packed; + +#define MGMT_EV_CONNECTABLE 0x0008 +struct mgmt_ev_connectable { + __le16 index; + __u8 connectable; +} __packed; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f55004af0558..a8a38f17ef78 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -274,18 +274,24 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) if (!status) { __u8 param = *((__u8 *) sent); + int old_pscan, old_iscan; - clear_bit(HCI_PSCAN, &hdev->flags); - clear_bit(HCI_ISCAN, &hdev->flags); + old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags); + old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags); if (param & SCAN_INQUIRY) { set_bit(HCI_ISCAN, &hdev->flags); - mgmt_discoverable(hdev->id, 1); - } else + if (!old_iscan) + mgmt_discoverable(hdev->id, 1); + } else if (old_iscan) mgmt_discoverable(hdev->id, 0); - if (param & SCAN_PAGE) + if (param & SCAN_PAGE) { set_bit(HCI_PSCAN, &hdev->flags); + if (!old_pscan) + mgmt_connectable(hdev->id, 1); + } else if (old_pscan) + mgmt_connectable(hdev->id, 0); } hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 5fa3034fe79f..fc41cfc3f162 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -204,6 +204,7 @@ static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) rp->type = hdev->dev_type; rp->powered = test_bit(HCI_UP, &hdev->flags); + rp->connectable = test_bit(HCI_PSCAN, &hdev->flags); rp->discoverable = test_bit(HCI_ISCAN, &hdev->flags); rp->pairable = test_bit(HCI_PSCAN, &hdev->flags); @@ -390,8 +391,7 @@ static int set_discoverable(struct sock *sk, unsigned char *data, u16 len) } if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id) || - hci_sent_cmd_data(hdev, HCI_OP_WRITE_SCAN_ENABLE)) { + mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id)) { err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EBUSY); goto failed; } @@ -422,6 +422,61 @@ failed: return err; } +static int set_connectable(struct sock *sk, unsigned char *data, u16 len) +{ + struct mgmt_cp_set_connectable *cp; + struct hci_dev *hdev; + u16 dev_id; + u8 scan; + int err; + + cp = (void *) data; + dev_id = get_unaligned_le16(&cp->index); + + BT_DBG("request for hci%u", dev_id); + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, MGMT_OP_SET_CONNECTABLE, ENODEV); + + hci_dev_lock_bh(hdev); + + if (!test_bit(HCI_UP, &hdev->flags)) { + err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, ENETDOWN); + goto failed; + } + + if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) || + mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id)) { + err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, EBUSY); + goto failed; + } + + if (cp->connectable == test_bit(HCI_PSCAN, &hdev->flags)) { + err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, EALREADY); + goto failed; + } + + err = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, dev_id, data, len); + if (err < 0) + goto failed; + + if (cp->connectable) + scan = SCAN_PAGE; + else + scan = 0; + + err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); + if (err < 0) + mgmt_pending_remove(MGMT_OP_SET_CONNECTABLE, dev_id); + +failed: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -468,6 +523,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_SET_DISCOVERABLE: err = set_discoverable(sk, buf + sizeof(*hdr), len); break; + case MGMT_OP_SET_CONNECTABLE: + err = set_connectable(sk, buf + sizeof(*hdr), len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, opcode, 0x01); @@ -648,3 +706,63 @@ int mgmt_discoverable(u16 index, u8 discoverable) return ret; } + +static void connectable_rsp(struct pending_cmd *cmd, void *data) +{ + struct mgmt_cp_set_connectable *cp = cmd->cmd; + struct cmd_lookup *match = data; + struct sk_buff *skb; + struct mgmt_hdr *hdr; + struct mgmt_ev_cmd_complete *ev; + struct mgmt_rp_set_connectable *rp; + + if (cp->connectable != match->value) + return; + + skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); + if (!skb) + return; + + hdr = (void *) skb_put(skb, sizeof(*hdr)); + hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); + hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); + + ev = (void *) skb_put(skb, sizeof(*ev)); + put_unaligned_le16(MGMT_OP_SET_CONNECTABLE, &ev->opcode); + + rp = (void *) skb_put(skb, sizeof(*rp)); + put_unaligned_le16(cmd->index, &rp->index); + rp->connectable = cp->connectable; + + if (sock_queue_rcv_skb(cmd->sk, skb) < 0) + kfree_skb(skb); + + list_del(&cmd->list); + + if (match->sk == NULL) { + match->sk = cmd->sk; + sock_hold(match->sk); + } + + mgmt_pending_free(cmd); +} + +int mgmt_connectable(u16 index, u8 connectable) +{ + struct mgmt_ev_connectable ev; + struct cmd_lookup match = { connectable, NULL }; + int ret; + + put_unaligned_le16(index, &ev.index); + ev.connectable = connectable; + + mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, + connectable_rsp, &match); + + ret = mgmt_event(MGMT_EV_CONNECTABLE, &ev, sizeof(ev), match.sk); + + if (match.sk) + sock_put(match.sk); + + return ret; +} -- cgit v1.2.3 From 72a734ec1aca8cd2ef3fc85428c11bde662e149e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 30 Dec 2010 00:38:22 +0200 Subject: Bluetooth: Unify mode related management messages to a single struct The powered, connectable and discoverable messages all have the same format. By using a single struct for all of them a lot of code can be simplified and reused. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/mgmt.h | 39 ++---------- net/bluetooth/mgmt.c | 137 +++++++++---------------------------------- 2 files changed, 32 insertions(+), 144 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 008acf54147a..f61fd6779ee5 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -59,35 +59,16 @@ struct mgmt_rp_read_info { __u16 hci_rev; } __packed; -#define MGMT_OP_SET_POWERED 0x0005 -struct mgmt_cp_set_powered { +struct mgmt_mode { __le16 index; - __u8 powered; -} __packed; -struct mgmt_rp_set_powered { - __le16 index; - __u8 powered; + __u8 val; } __packed; +#define MGMT_OP_SET_POWERED 0x0005 + #define MGMT_OP_SET_DISCOVERABLE 0x0006 -struct mgmt_cp_set_discoverable { - __le16 index; - __u8 discoverable; -} __packed; -struct mgmt_rp_set_discoverable { - __le16 index; - __u8 discoverable; -} __packed; #define MGMT_OP_SET_CONNECTABLE 0x0007 -struct mgmt_cp_set_connectable { - __le16 index; - __u8 connectable; -} __packed; -struct mgmt_rp_set_connectable { - __le16 index; - __u8 connectable; -} __packed; #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { @@ -118,19 +99,7 @@ struct mgmt_ev_index_removed { } __packed; #define MGMT_EV_POWERED 0x0006 -struct mgmt_ev_powered { - __le16 index; - __u8 powered; -} __packed; #define MGMT_EV_DISCOVERABLE 0x0007 -struct mgmt_ev_discoverable { - __le16 index; - __u8 discoverable; -} __packed; #define MGMT_EV_CONNECTABLE 0x0008 -struct mgmt_ev_connectable { - __le16 index; - __u8 connectable; -} __packed; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index fc41cfc3f162..dbb1e5776644 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -322,7 +322,7 @@ static void mgmt_pending_remove(u16 opcode, int index) static int set_powered(struct sock *sk, unsigned char *data, u16 len) { - struct mgmt_cp_set_powered *cp; + struct mgmt_mode *cp; struct hci_dev *hdev; u16 dev_id; int ret, up; @@ -339,7 +339,7 @@ static int set_powered(struct sock *sk, unsigned char *data, u16 len) hci_dev_lock_bh(hdev); up = test_bit(HCI_UP, &hdev->flags); - if ((cp->powered && up) || (!cp->powered && !up)) { + if ((cp->val && up) || (!cp->val && !up)) { ret = cmd_status(sk, MGMT_OP_SET_POWERED, EALREADY); goto failed; } @@ -353,7 +353,7 @@ static int set_powered(struct sock *sk, unsigned char *data, u16 len) if (ret < 0) goto failed; - if (cp->powered) + if (cp->val) queue_work(hdev->workqueue, &hdev->power_on); else queue_work(hdev->workqueue, &hdev->power_off); @@ -368,7 +368,7 @@ failed: static int set_discoverable(struct sock *sk, unsigned char *data, u16 len) { - struct mgmt_cp_set_discoverable *cp; + struct mgmt_mode *cp; struct hci_dev *hdev; u16 dev_id; u8 scan; @@ -396,7 +396,7 @@ static int set_discoverable(struct sock *sk, unsigned char *data, u16 len) goto failed; } - if (cp->discoverable == test_bit(HCI_ISCAN, &hdev->flags) && + if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) && test_bit(HCI_PSCAN, &hdev->flags)) { err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EALREADY); goto failed; @@ -408,7 +408,7 @@ static int set_discoverable(struct sock *sk, unsigned char *data, u16 len) scan = SCAN_PAGE; - if (cp->discoverable) + if (cp->val) scan |= SCAN_INQUIRY; err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); @@ -424,7 +424,7 @@ failed: static int set_connectable(struct sock *sk, unsigned char *data, u16 len) { - struct mgmt_cp_set_connectable *cp; + struct mgmt_mode *cp; struct hci_dev *hdev; u16 dev_id; u8 scan; @@ -452,7 +452,7 @@ static int set_connectable(struct sock *sk, unsigned char *data, u16 len) goto failed; } - if (cp->connectable == test_bit(HCI_PSCAN, &hdev->flags)) { + if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, EALREADY); goto failed; } @@ -461,7 +461,7 @@ static int set_connectable(struct sock *sk, unsigned char *data, u16 len) if (err < 0) goto failed; - if (cp->connectable) + if (cp->val) scan = SCAN_PAGE; else scan = 0; @@ -584,20 +584,20 @@ int mgmt_index_removed(u16 index) } struct cmd_lookup { - u8 value; + u8 val; struct sock *sk; }; -static void power_rsp(struct pending_cmd *cmd, void *data) +static void mode_rsp(struct pending_cmd *cmd, void *data) { struct mgmt_hdr *hdr; struct mgmt_ev_cmd_complete *ev; - struct mgmt_rp_set_powered *rp; - struct mgmt_cp_set_powered *cp = cmd->cmd; + struct mgmt_mode *rp; + struct mgmt_mode *cp = cmd->cmd; struct sk_buff *skb; struct cmd_lookup *match = data; - if (cp->powered != match->value) + if (cp->val != match->val) return; skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); @@ -613,7 +613,7 @@ static void power_rsp(struct pending_cmd *cmd, void *data) rp = (void *) skb_put(skb, sizeof(*rp)); put_unaligned_le16(cmd->index, &rp->index); - rp->powered = cp->powered; + rp->val = cp->val; if (sock_queue_rcv_skb(cmd->sk, skb) < 0) kfree_skb(skb); @@ -630,14 +630,14 @@ static void power_rsp(struct pending_cmd *cmd, void *data) int mgmt_powered(u16 index, u8 powered) { - struct mgmt_ev_powered ev; + struct mgmt_mode ev; struct cmd_lookup match = { powered, NULL }; int ret; - put_unaligned_le16(index, &ev.index); - ev.powered = powered; + mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match); - mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, power_rsp, &match); + put_unaligned_le16(index, &ev.index); + ev.val = powered; ret = mgmt_event(MGMT_EV_POWERED, &ev, sizeof(ev), match.sk); @@ -647,57 +647,17 @@ int mgmt_powered(u16 index, u8 powered) return ret; } -static void discoverable_rsp(struct pending_cmd *cmd, void *data) -{ - struct mgmt_cp_set_discoverable *cp = cmd->cmd; - struct cmd_lookup *match = data; - struct sk_buff *skb; - struct mgmt_hdr *hdr; - struct mgmt_ev_cmd_complete *ev; - struct mgmt_rp_set_discoverable *rp; - - if (cp->discoverable != match->value) - return; - - skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); - if (!skb) - return; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); - hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); - - ev = (void *) skb_put(skb, sizeof(*ev)); - put_unaligned_le16(MGMT_OP_SET_DISCOVERABLE, &ev->opcode); - - rp = (void *) skb_put(skb, sizeof(*rp)); - put_unaligned_le16(cmd->index, &rp->index); - rp->discoverable = cp->discoverable; - - if (sock_queue_rcv_skb(cmd->sk, skb) < 0) - kfree_skb(skb); - - list_del(&cmd->list); - - if (match->sk == NULL) { - match->sk = cmd->sk; - sock_hold(match->sk); - } - - mgmt_pending_free(cmd); -} - int mgmt_discoverable(u16 index, u8 discoverable) { - struct mgmt_ev_discoverable ev; + struct mgmt_mode ev; struct cmd_lookup match = { discoverable, NULL }; int ret; - put_unaligned_le16(index, &ev.index); - ev.discoverable = discoverable; - mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index, - discoverable_rsp, &match); + mode_rsp, &match); + + put_unaligned_le16(index, &ev.index); + ev.val = discoverable; ret = mgmt_event(MGMT_EV_DISCOVERABLE, &ev, sizeof(ev), match.sk); @@ -707,57 +667,16 @@ int mgmt_discoverable(u16 index, u8 discoverable) return ret; } -static void connectable_rsp(struct pending_cmd *cmd, void *data) -{ - struct mgmt_cp_set_connectable *cp = cmd->cmd; - struct cmd_lookup *match = data; - struct sk_buff *skb; - struct mgmt_hdr *hdr; - struct mgmt_ev_cmd_complete *ev; - struct mgmt_rp_set_connectable *rp; - - if (cp->connectable != match->value) - return; - - skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); - if (!skb) - return; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); - hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); - - ev = (void *) skb_put(skb, sizeof(*ev)); - put_unaligned_le16(MGMT_OP_SET_CONNECTABLE, &ev->opcode); - - rp = (void *) skb_put(skb, sizeof(*rp)); - put_unaligned_le16(cmd->index, &rp->index); - rp->connectable = cp->connectable; - - if (sock_queue_rcv_skb(cmd->sk, skb) < 0) - kfree_skb(skb); - - list_del(&cmd->list); - - if (match->sk == NULL) { - match->sk = cmd->sk; - sock_hold(match->sk); - } - - mgmt_pending_free(cmd); -} - int mgmt_connectable(u16 index, u8 connectable) { - struct mgmt_ev_connectable ev; + struct mgmt_mode ev; struct cmd_lookup match = { connectable, NULL }; int ret; - put_unaligned_le16(index, &ev.index); - ev.connectable = connectable; + mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, mode_rsp, &match); - mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, - connectable_rsp, &match); + put_unaligned_le16(index, &ev.index); + ev.val = connectable; ret = mgmt_event(MGMT_EV_CONNECTABLE, &ev, sizeof(ev), match.sk); -- cgit v1.2.3 From ebc99feba7378349e2bfae7018af062767382f6c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 4 Jan 2011 11:54:26 +0200 Subject: Bluetooth: Add flag to track managment controlled adapters This patch adds a HCI_MGMT flag to track adapters which are under the control of the management interface. This is needed to make sure that new kernels will work with old user space versions. I.e. behaviour which could break old user space versions (but is needed by the management interface) should not be exhibited when the HCI_MGMT flag is not set. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 1 + net/bluetooth/mgmt.c | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 51c9df16e764..469f8fdb2f5d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -79,6 +79,7 @@ enum { HCI_SETUP, HCI_AUTO_OFF, + HCI_MGMT, }; /* HCI ioctl defines */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index dbb1e5776644..5f871b385a27 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -142,6 +142,8 @@ static int read_index_list(struct sock *sk) hci_del_off_timer(d); + set_bit(HCI_MGMT, &d->flags); + if (test_bit(HCI_SETUP, &d->flags)) continue; @@ -200,6 +202,8 @@ static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) hci_dev_lock_bh(hdev); + set_bit(HCI_MGMT, &hdev->flags); + put_unaligned_le16(hdev->id, &rp->index); rp->type = hdev->dev_type; -- cgit v1.2.3 From c542a06c29acbf4ea0024884a198065a10613147 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 26 Jan 2011 13:11:03 +0200 Subject: Bluetooth: Implement set_pairable managment command This patch implements a new set_pairable management command to control the pairable state of local adapters. The state is represented using a new HCI_PAIRABLE flag in the hci_dev struct. For backwards compatibility with older user space versions the HCI_PAIRABLE flag gets automatically set when the existence of an adapter is reported to user space through legacy methods and the HCI_MGMT flag is not set. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/mgmt.h | 4 ++ net/bluetooth/hci_core.c | 10 +++++ net/bluetooth/mgmt.c | 88 ++++++++++++++++++++++++++++++++------------ 4 files changed, 80 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 469f8fdb2f5d..f0c25b5ba4b2 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -80,6 +80,7 @@ enum { HCI_SETUP, HCI_AUTO_OFF, HCI_MGMT, + HCI_PAIRABLE, }; /* HCI ioctl defines */ diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index f61fd6779ee5..a554802291ed 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -70,6 +70,8 @@ struct mgmt_mode { #define MGMT_OP_SET_CONNECTABLE 0x0007 +#define MGMT_OP_SET_PAIRABLE 0x0008 + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; @@ -103,3 +105,5 @@ struct mgmt_ev_index_removed { #define MGMT_EV_DISCOVERABLE 0x0007 #define MGMT_EV_CONNECTABLE 0x0008 + +#define MGMT_EV_PAIRABLE 0x0009 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index dfc4ef90deca..13eb5a8beb84 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -799,10 +799,17 @@ int hci_get_dev_list(void __user *arg) read_lock_bh(&hci_dev_list_lock); list_for_each(p, &hci_dev_list) { struct hci_dev *hdev; + hdev = list_entry(p, struct hci_dev, list); + hci_del_off_timer(hdev); + + if (!test_bit(HCI_MGMT, &hdev->flags)) + set_bit(HCI_PAIRABLE, &hdev->flags); + (dr + n)->dev_id = hdev->id; (dr + n)->dev_opt = hdev->flags; + if (++n >= dev_num) break; } @@ -832,6 +839,9 @@ int hci_get_dev_info(void __user *arg) hci_del_off_timer(hdev); + if (!test_bit(HCI_MGMT, &hdev->flags)) + set_bit(HCI_PAIRABLE, &hdev->flags); + strcpy(di.name, hdev->name); di.bdaddr = hdev->bdaddr; di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 13872ae219c9..d10735076a25 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -481,6 +481,29 @@ failed: return err; } +static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk) +{ + struct sk_buff *skb; + struct mgmt_hdr *hdr; + + skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + bt_cb(skb)->channel = HCI_CHANNEL_CONTROL; + + hdr = (void *) skb_put(skb, sizeof(*hdr)); + hdr->opcode = cpu_to_le16(event); + hdr->len = cpu_to_le16(data_len); + + memcpy(skb_put(skb, data_len), data, data_len); + + hci_send_to_sock(NULL, skb, skip_sk); + kfree_skb(skb); + + return 0; +} + static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val) { struct mgmt_hdr *hdr; @@ -509,6 +532,45 @@ static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val) return 0; } +static int set_pairable(struct sock *sk, unsigned char *data, u16 len) +{ + struct mgmt_mode *cp, ev; + struct hci_dev *hdev; + u16 dev_id; + int err; + + cp = (void *) data; + dev_id = get_unaligned_le16(&cp->index); + + BT_DBG("request for hci%u", dev_id); + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, MGMT_OP_SET_PAIRABLE, ENODEV); + + hci_dev_lock_bh(hdev); + + if (cp->val) + set_bit(HCI_PAIRABLE, &hdev->flags); + else + clear_bit(HCI_PAIRABLE, &hdev->flags); + + err = send_mode_rsp(sk, MGMT_OP_SET_PAIRABLE, dev_id, cp->val); + if (err < 0) + goto failed; + + put_unaligned_le16(dev_id, &ev.index); + ev.val = cp->val; + + err = mgmt_event(MGMT_EV_PAIRABLE, &ev, sizeof(ev), sk); + +failed: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -558,6 +620,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_SET_CONNECTABLE: err = set_connectable(sk, buf + sizeof(*hdr), len); break; + case MGMT_OP_SET_PAIRABLE: + err = set_pairable(sk, buf + sizeof(*hdr), len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, opcode, 0x01); @@ -574,29 +639,6 @@ done: return err; } -static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk) -{ - struct sk_buff *skb; - struct mgmt_hdr *hdr; - - skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - bt_cb(skb)->channel = HCI_CHANNEL_CONTROL; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(event); - hdr->len = cpu_to_le16(data_len); - - memcpy(skb_put(skb, data_len), data, data_len); - - hci_send_to_sock(NULL, skb, skip_sk); - kfree_skb(skb); - - return 0; -} - int mgmt_index_added(u16 index) { struct mgmt_ev_index_added ev; -- cgit v1.2.3 From 2aeb9a1ae0e34fb46cb78b82f827a6a54ab65111 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 4 Jan 2011 12:08:51 +0200 Subject: Bluetooth: Implement UUID handling through the management interface This patch adds methods to the management interface for userspace to notify the kernel of which services have been registered for specific adapters. This information is needed for setting the appropriate Class of Device value as well as the Extended Inquiry Response value. This patch doesn't actually implement setting of these values but just provides the storage of the UUIDs so the needed functionality can be built on top of it. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 10 ++++ include/net/bluetooth/mgmt.h | 12 ++++ net/bluetooth/hci_core.c | 19 +++++++ net/bluetooth/mgmt.c | 120 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ba3dbe3188ed..8ee0b8bac77c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -66,6 +66,12 @@ struct bdaddr_list { struct list_head list; bdaddr_t bdaddr; }; + +struct bt_uuid { + struct list_head list; + u8 uuid[16]; +}; + #define NUM_REASSEMBLY 4 struct hci_dev { struct list_head list; @@ -139,6 +145,8 @@ struct hci_dev { struct hci_conn_hash conn_hash; struct list_head blacklist; + struct list_head uuids; + struct hci_dev_stats stat; struct sk_buff_head driver_init; @@ -441,6 +449,8 @@ int hci_inquiry(void __user *arg); struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_blacklist_clear(struct hci_dev *hdev); +int hci_uuids_clear(struct hci_dev *hdev); + void hci_del_off_timer(struct hci_dev *hdev); void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index a554802291ed..c118ad3af332 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -72,6 +72,18 @@ struct mgmt_mode { #define MGMT_OP_SET_PAIRABLE 0x0008 +#define MGMT_OP_ADD_UUID 0x0009 +struct mgmt_cp_add_uuid { + __le16 index; + __u8 uuid[16]; +} __packed; + +#define MGMT_OP_REMOVE_UUID 0x000A +struct mgmt_cp_remove_uuid { + __le16 index; + __u8 uuid[16]; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 13eb5a8beb84..b99248d4a5b2 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -955,6 +955,22 @@ void hci_del_off_timer(struct hci_dev *hdev) del_timer(&hdev->off_timer); } +int hci_uuids_clear(struct hci_dev *hdev) +{ + struct list_head *p, *n; + + list_for_each_safe(p, n, &hdev->uuids) { + struct bt_uuid *uuid; + + uuid = list_entry(p, struct bt_uuid, list); + + list_del(p); + kfree(uuid); + } + + return 0; +} + /* Register HCI device */ int hci_register_dev(struct hci_dev *hdev) { @@ -1012,6 +1028,8 @@ int hci_register_dev(struct hci_dev *hdev) INIT_LIST_HEAD(&hdev->blacklist); + INIT_LIST_HEAD(&hdev->uuids); + INIT_WORK(&hdev->power_on, hci_power_on); INIT_WORK(&hdev->power_off, hci_power_off); setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev); @@ -1087,6 +1105,7 @@ int hci_unregister_dev(struct hci_dev *hdev) hci_dev_lock_bh(hdev); hci_blacklist_clear(hdev); + hci_uuids_clear(hdev); hci_dev_unlock_bh(hdev); __hci_dev_put(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d10735076a25..0854c2f1073c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -571,6 +571,120 @@ failed: return err; } +static int uuid_rsp(struct sock *sk, u16 opcode, u16 index) +{ + struct mgmt_hdr *hdr; + struct mgmt_ev_cmd_complete *ev; + struct sk_buff *skb; + + skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(index), GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + hdr = (void *) skb_put(skb, sizeof(*hdr)); + hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); + hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(index)); + + ev = (void *) skb_put(skb, sizeof(*ev)); + put_unaligned_le16(opcode, &ev->opcode); + + put_unaligned_le16(index, skb_put(skb, sizeof(index))); + + if (sock_queue_rcv_skb(sk, skb) < 0) + kfree_skb(skb); + + return 0; +} + +static int add_uuid(struct sock *sk, unsigned char *data, u16 len) +{ + struct mgmt_cp_add_uuid *cp; + struct hci_dev *hdev; + struct bt_uuid *uuid; + u16 dev_id; + int err; + + cp = (void *) data; + dev_id = get_unaligned_le16(&cp->index); + + BT_DBG("request for hci%u", dev_id); + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, MGMT_OP_ADD_UUID, ENODEV); + + hci_dev_lock_bh(hdev); + + uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC); + if (!uuid) { + err = -ENOMEM; + goto failed; + } + + memcpy(uuid->uuid, cp->uuid, 16); + + list_add(&uuid->list, &hdev->uuids); + + err = uuid_rsp(sk, MGMT_OP_ADD_UUID, dev_id); + +failed: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + +static int remove_uuid(struct sock *sk, unsigned char *data, u16 len) +{ + struct list_head *p, *n; + struct mgmt_cp_add_uuid *cp; + struct hci_dev *hdev; + u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + u16 dev_id; + int err, found; + + cp = (void *) data; + dev_id = get_unaligned_le16(&cp->index); + + BT_DBG("request for hci%u", dev_id); + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, MGMT_OP_REMOVE_UUID, ENODEV); + + hci_dev_lock_bh(hdev); + + if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) { + err = hci_uuids_clear(hdev); + goto unlock; + } + + found = 0; + + list_for_each_safe(p, n, &hdev->uuids) { + struct bt_uuid *match = list_entry(p, struct bt_uuid, list); + + if (memcmp(match->uuid, cp->uuid, 16) != 0) + continue; + + list_del(&match->list); + found++; + } + + if (found == 0) { + err = cmd_status(sk, MGMT_OP_REMOVE_UUID, ENOENT); + goto unlock; + } + + err = uuid_rsp(sk, MGMT_OP_REMOVE_UUID, dev_id); + +unlock: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -623,6 +737,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_SET_PAIRABLE: err = set_pairable(sk, buf + sizeof(*hdr), len); break; + case MGMT_OP_ADD_UUID: + err = add_uuid(sk, buf + sizeof(*hdr), len); + break; + case MGMT_OP_REMOVE_UUID: + err = remove_uuid(sk, buf + sizeof(*hdr), len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, opcode, 0x01); -- cgit v1.2.3 From 03b555e119de8288a16e086e1fbd223d9b429d3d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 4 Jan 2011 15:40:05 +0200 Subject: Bluetooth: Reject pairing requests when in non-pairable mode This patch adds the necessary logic to act accordingly when the HCI_PAIRABLE flag is not set. In that case PIN code replies as well as Secure Simple Pairing requests without a NoBonding requirement need to be rejected. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 14 ++++++++++ include/net/bluetooth/hci_core.h | 4 +++ net/bluetooth/hci_event.c | 55 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 71 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index f0c25b5ba4b2..65cab137e19f 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -384,6 +384,12 @@ struct hci_cp_reject_sync_conn_req { __u8 reason; } __packed; +#define HCI_OP_IO_CAPABILITY_NEG_REPLY 0x0434 +struct hci_cp_io_capability_neg_reply { + bdaddr_t bdaddr; + __u8 reason; +} __packed; + #define HCI_OP_SNIFF_MODE 0x0803 struct hci_cp_sniff_mode { __le16 handle; @@ -840,6 +846,14 @@ struct hci_ev_io_capa_request { bdaddr_t bdaddr; } __packed; +#define HCI_EV_IO_CAPA_REPLY 0x32 +struct hci_ev_io_capa_reply { + bdaddr_t bdaddr; + __u8 capability; + __u8 oob_data; + __u8 authentication; +} __packed; + #define HCI_EV_SIMPLE_PAIR_COMPLETE 0x36 struct hci_ev_simple_pair_complete { __u8 status; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 8ee0b8bac77c..dc8084a139ed 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -201,6 +201,10 @@ struct hci_conn { __u16 disc_timeout; unsigned long pend; + __u8 remote_cap; + __u8 remote_oob; + __u8 remote_auth; + unsigned int sent; struct sk_buff_head data_q; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a8a38f17ef78..cf3014ae00e4 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1595,6 +1595,10 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff hci_conn_put(conn); } + if (!test_bit(HCI_PAIRABLE, &hdev->flags)) + hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, + sizeof(ev->bdaddr), &ev->bdaddr); + hci_dev_unlock(hdev); } @@ -1885,9 +1889,52 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (conn) - hci_conn_hold(conn); + if (!conn) + goto unlock; + + hci_conn_hold(conn); + + if (!test_bit(HCI_MGMT, &hdev->flags)) + goto unlock; + + if (test_bit(HCI_PAIRABLE, &hdev->flags) || + (conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) { + /* FIXME: Do IO capa response based on information + * provided through the management interface */ + } else { + struct hci_cp_io_capability_neg_reply cp; + + bacpy(&cp.bdaddr, &ev->bdaddr); + cp.reason = 0x16; /* Pairing not allowed */ + hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_NEG_REPLY, + sizeof(cp), &cp); + } + +unlock: + hci_dev_unlock(hdev); +} + +static inline void hci_io_capa_reply_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_io_capa_reply *ev = (void *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); + if (!conn) + goto unlock; + + hci_conn_hold(conn); + + conn->remote_cap = ev->capability; + conn->remote_oob = ev->oob_data; + conn->remote_auth = ev->authentication; + +unlock: hci_dev_unlock(hdev); } @@ -2051,6 +2098,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_io_capa_request_evt(hdev, skb); break; + case HCI_EV_IO_CAPA_REPLY: + hci_io_capa_reply_evt(hdev, skb); + break; + case HCI_EV_SIMPLE_PAIR_COMPLETE: hci_simple_pair_complete_evt(hdev, skb); break; -- cgit v1.2.3 From a5040efa2017f3e4f1b4d5f40fd989567f3994c1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 10 Jan 2011 13:28:59 +0200 Subject: Bluetooth: Add special handling with __hci_request and HCI_INIT To support a more dynamic HCI initialization sequence the __hci_request behavior requires some more changes. Particularly, the init sequence should be able to have conditionals in it (sending some HCI commands depending on the outcome of a previous command) instead of being a fixed list as it is right now. The reasons for these additional requirements are the moving all previously user space driven initialization commands to the kernel side as well as the support the Low Energy controllers. To fulfull these requirements the init sequence is made the only special case for multi-command requests and req_last_cmd is renamed to init_last_cmd. The hci_send_cmd function is changed to update init_last_cmd as long as the HCI_INIT flag is set. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 3 ++- net/bluetooth/hci_core.c | 17 +++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index dc8084a139ed..0dbdcc5f44e4 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -139,7 +139,8 @@ struct hci_dev { wait_queue_head_t req_wait_q; __u32 req_status; __u32 req_result; - __u16 req_last_cmd; + + __u16 init_last_cmd; struct inquiry_cache inq_cache; struct hci_conn_hash conn_hash; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b99248d4a5b2..183ce81f7a5c 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -97,11 +97,10 @@ void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result) { BT_DBG("%s command 0x%04x result 0x%2.2x", hdev->name, cmd, result); - /* If the request has set req_last_cmd (typical for multi-HCI - * command requests) check if the completed command matches - * this, and if not just return. Single HCI command requests - * typically leave req_last_cmd as 0 */ - if (hdev->req_last_cmd && cmd != hdev->req_last_cmd) + /* If this is the init phase check if the completed command matches + * the last init command, and if not just return. + */ + if (test_bit(HCI_INIT, &hdev->flags) && hdev->init_last_cmd != cmd) return; if (hdev->req_status == HCI_REQ_PEND) { @@ -158,7 +157,7 @@ static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, break; } - hdev->req_last_cmd = hdev->req_status = hdev->req_result = 0; + hdev->req_status = hdev->req_result = 0; BT_DBG("%s end: err %d", hdev->name, err); @@ -261,8 +260,6 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) /* Connection accept timeout ~20 secs */ param = cpu_to_le16(0x7d00); hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); - - hdev->req_last_cmd = HCI_OP_WRITE_CA_TIMEOUT; } static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) @@ -523,6 +520,7 @@ int hci_dev_open(__u16 dev) if (!test_bit(HCI_RAW, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); set_bit(HCI_INIT, &hdev->flags); + hdev->init_last_cmd = 0; //__hci_request(hdev, hci_reset_req, 0, HZ); ret = __hci_request(hdev, hci_init_req, 0, @@ -1442,6 +1440,9 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; skb->dev = (void *) hdev; + if (test_bit(HCI_INIT, &hdev->flags)) + hdev->init_last_cmd = opcode; + skb_queue_tail(&hdev->cmd_q, skb); tasklet_schedule(&hdev->cmd_task); -- cgit v1.2.3 From b0916ea0d9e6ea3ed46bb7a61c13a2b357b0248b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 10 Jan 2011 13:44:55 +0200 Subject: Bluetooth: Add controller side link key clearing to hci_init_req The controller may have link keys in its own memory and these keys could be used for secure connections. However, since the interface to access these keys doesn't provide information about the key types (which would be needed to infer the level of security each key provides) using these keys is rather useless. Therefore, simply clear the controller side list in the initialization procedure. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 6 ++++++ net/bluetooth/hci_core.c | 5 +++++ net/bluetooth/hci_event.c | 14 ++++++++++++++ 3 files changed, 25 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 65cab137e19f..4e2f008d32e1 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -487,6 +487,12 @@ struct hci_cp_set_event_flt { #define HCI_CONN_SETUP_AUTO_OFF 0x01 #define HCI_CONN_SETUP_AUTO_ON 0x02 +#define HCI_OP_DELETE_STORED_LINK_KEY 0x0c12 +struct hci_cp_delete_stored_link_key { + bdaddr_t bdaddr; + __u8 delete_all; +} __packed; + #define HCI_OP_WRITE_LOCAL_NAME 0x0c13 struct hci_cp_write_local_name { __u8 name[248]; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 183ce81f7a5c..cedb8a966df6 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -190,6 +190,7 @@ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt) static void hci_init_req(struct hci_dev *hdev, unsigned long opt) { + struct hci_cp_delete_stored_link_key cp; struct sk_buff *skb; __le16 param; __u8 flt_type; @@ -260,6 +261,10 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) /* Connection accept timeout ~20 secs */ param = cpu_to_le16(0x7d00); hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); + + bacpy(&cp.bdaddr, BDADDR_ANY); + cp.delete_all = 1; + hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); } static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index cf3014ae00e4..49b387cdcc38 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -557,6 +557,16 @@ static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb) hci_req_complete(hdev, HCI_OP_WRITE_CA_TIMEOUT, status); } +static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, + struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%x", hdev->name, status); + + hci_req_complete(hdev, HCI_OP_DELETE_STORED_LINK_KEY, status); +} + static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%x", hdev->name, status); @@ -1402,6 +1412,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_write_ca_timeout(hdev, skb); break; + case HCI_OP_DELETE_STORED_LINK_KEY: + hci_cc_delete_stored_link_key(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; -- cgit v1.2.3 From d5859e22cd40b73164b3e5d8d5d796f96edcc6af Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 25 Jan 2011 01:19:58 +0200 Subject: Bluetooth: Implement a more complete adapter initialization sequence Using the managment interface means that user space doesn't need to do any HCI command sending at all. This patch moves the remaining initialization commands from user space to the kernel side. The patch makes use of the new feature of __hci_request which allows the request to be dynamically modified while it is ongoing (something that is needed to react appropriately to the local features and the version of the adapter). Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 11 +++ include/net/bluetooth/hci_core.h | 2 + net/bluetooth/hci_event.c | 194 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 206 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 4e2f008d32e1..99ac3516fe9d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -189,19 +189,26 @@ enum { #define LMP_PSCHEME 0x02 #define LMP_PCONTROL 0x04 +#define LMP_RSSI_INQ 0x40 #define LMP_ESCO 0x80 #define LMP_EV4 0x01 #define LMP_EV5 0x02 +#define LMP_LE 0x40 #define LMP_SNIFF_SUBR 0x02 +#define LMP_PAUSE_ENC 0x04 #define LMP_EDR_ESCO_2M 0x20 #define LMP_EDR_ESCO_3M 0x40 #define LMP_EDR_3S_ESCO 0x80 +#define LMP_EXT_INQ 0x01 #define LMP_SIMPLE_PAIR 0x08 #define LMP_NO_FLUSH 0x40 +#define LMP_LSTO 0x01 +#define LMP_INQ_TX_PWR 0x02 + /* Connection modes */ #define HCI_CM_ACTIVE 0x0000 #define HCI_CM_HOLD 0x0001 @@ -556,6 +563,8 @@ struct hci_cp_host_buffer_size { __le16 sco_max_pkt; } __packed; +#define HCI_OP_WRITE_INQUIRY_MODE 0x0c45 + #define HCI_OP_READ_SSP_MODE 0x0c55 struct hci_rp_read_ssp_mode { __u8 status; @@ -567,6 +576,8 @@ struct hci_cp_write_ssp_mode { __u8 mode; } __packed; +#define HCI_OP_READ_INQ_RSP_TX_POWER 0x0c58 + #define HCI_OP_READ_LOCAL_VERSION 0x1001 struct hci_rp_read_local_version { __u8 status; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 0dbdcc5f44e4..71a3fbf1e785 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -91,7 +91,9 @@ struct hci_dev { __u8 ssp_mode; __u8 hci_ver; __u16 hci_rev; + __u8 lmp_ver; __u16 manufacturer; + __le16 lmp_subver; __u16 voice_setting; __u16 pkt_type; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 49b387cdcc38..c69ee44d5bd7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -424,6 +424,115 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) hdev->ssp_mode = *((__u8 *) sent); } +static u8 hci_get_inquiry_mode(struct hci_dev *hdev) +{ + if (hdev->features[6] & LMP_EXT_INQ) + return 2; + + if (hdev->features[3] & LMP_RSSI_INQ) + return 1; + + if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 && + hdev->lmp_subver == 0x0757) + return 1; + + if (hdev->manufacturer == 15) { + if (hdev->hci_rev == 0x03 && hdev->lmp_subver == 0x6963) + return 1; + if (hdev->hci_rev == 0x09 && hdev->lmp_subver == 0x6963) + return 1; + if (hdev->hci_rev == 0x00 && hdev->lmp_subver == 0x6965) + return 1; + } + + if (hdev->manufacturer == 31 && hdev->hci_rev == 0x2005 && + hdev->lmp_subver == 0x1805) + return 1; + + return 0; +} + +static void hci_setup_inquiry_mode(struct hci_dev *hdev) +{ + u8 mode; + + mode = hci_get_inquiry_mode(hdev); + + hci_send_cmd(hdev, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode); +} + +static void hci_setup_event_mask(struct hci_dev *hdev) +{ + /* The second byte is 0xff instead of 0x9f (two reserved bits + * disabled) since a Broadcom 1.2 dongle doesn't respond to the + * command otherwise */ + u8 events[8] = { 0xff, 0xff, 0xfb, 0xff, 0x00, 0x00, 0x00, 0x00 }; + + /* Events for 1.2 and newer controllers */ + if (hdev->lmp_ver > 1) { + events[4] |= 0x01; /* Flow Specification Complete */ + events[4] |= 0x02; /* Inquiry Result with RSSI */ + events[4] |= 0x04; /* Read Remote Extended Features Complete */ + events[5] |= 0x08; /* Synchronous Connection Complete */ + events[5] |= 0x10; /* Synchronous Connection Changed */ + } + + if (hdev->features[3] & LMP_RSSI_INQ) + events[4] |= 0x04; /* Inquiry Result with RSSI */ + + if (hdev->features[5] & LMP_SNIFF_SUBR) + events[5] |= 0x20; /* Sniff Subrating */ + + if (hdev->features[5] & LMP_PAUSE_ENC) + events[5] |= 0x80; /* Encryption Key Refresh Complete */ + + if (hdev->features[6] & LMP_EXT_INQ) + events[5] |= 0x40; /* Extended Inquiry Result */ + + if (hdev->features[6] & LMP_NO_FLUSH) + events[7] |= 0x01; /* Enhanced Flush Complete */ + + if (hdev->features[7] & LMP_LSTO) + events[6] |= 0x80; /* Link Supervision Timeout Changed */ + + if (hdev->features[6] & LMP_SIMPLE_PAIR) { + events[6] |= 0x01; /* IO Capability Request */ + events[6] |= 0x02; /* IO Capability Response */ + events[6] |= 0x04; /* User Confirmation Request */ + events[6] |= 0x08; /* User Passkey Request */ + events[6] |= 0x10; /* Remote OOB Data Request */ + events[6] |= 0x20; /* Simple Pairing Complete */ + events[7] |= 0x04; /* User Passkey Notification */ + events[7] |= 0x08; /* Keypress Notification */ + events[7] |= 0x10; /* Remote Host Supported + * Features Notification */ + } + + if (hdev->features[4] & LMP_LE) + events[7] |= 0x20; /* LE Meta-Event */ + + hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events); +} + +static void hci_setup(struct hci_dev *hdev) +{ + hci_setup_event_mask(hdev); + + if (hdev->lmp_ver > 1) + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL); + + if (hdev->features[6] & LMP_SIMPLE_PAIR) { + u8 mode = 0x01; + hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode); + } + + if (hdev->features[3] & LMP_RSSI_INQ) + hci_setup_inquiry_mode(hdev); + + if (hdev->features[7] & LMP_INQ_TX_PWR) + hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); +} + static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_local_version *rp = (void *) skb->data; @@ -435,11 +544,34 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) hdev->hci_ver = rp->hci_ver; hdev->hci_rev = __le16_to_cpu(rp->hci_rev); + hdev->lmp_ver = rp->lmp_ver; hdev->manufacturer = __le16_to_cpu(rp->manufacturer); + hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver); BT_DBG("%s manufacturer %d hci ver %d:%d", hdev->name, hdev->manufacturer, hdev->hci_ver, hdev->hci_rev); + + if (test_bit(HCI_INIT, &hdev->flags)) + hci_setup(hdev); +} + +static void hci_setup_link_policy(struct hci_dev *hdev) +{ + u16 link_policy = 0; + + if (hdev->features[0] & LMP_RSWITCH) + link_policy |= HCI_LP_RSWITCH; + if (hdev->features[0] & LMP_HOLD) + link_policy |= HCI_LP_HOLD; + if (hdev->features[0] & LMP_SNIFF) + link_policy |= HCI_LP_SNIFF; + if (hdev->features[1] & LMP_PARK) + link_policy |= HCI_LP_PARK; + + link_policy = cpu_to_le16(link_policy); + hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, + sizeof(link_policy), &link_policy); } static void hci_cc_read_local_commands(struct hci_dev *hdev, struct sk_buff *skb) @@ -449,9 +581,15 @@ static void hci_cc_read_local_commands(struct hci_dev *hdev, struct sk_buff *skb BT_DBG("%s status 0x%x", hdev->name, rp->status); if (rp->status) - return; + goto done; memcpy(hdev->commands, rp->commands, sizeof(hdev->commands)); + + if (test_bit(HCI_INIT, &hdev->flags) && (hdev->commands[5] & 0x10)) + hci_setup_link_policy(hdev); + +done: + hci_req_complete(hdev, HCI_OP_READ_LOCAL_COMMANDS, rp->status); } static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb) @@ -567,6 +705,44 @@ static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, hci_req_complete(hdev, HCI_OP_DELETE_STORED_LINK_KEY, status); } +static void hci_cc_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%x", hdev->name, status); + + hci_req_complete(hdev, HCI_OP_SET_EVENT_MASK, status); +} + +static void hci_cc_write_inquiry_mode(struct hci_dev *hdev, + struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%x", hdev->name, status); + + hci_req_complete(hdev, HCI_OP_WRITE_INQUIRY_MODE, status); +} + +static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev, + struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%x", hdev->name, status); + + hci_req_complete(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, status); +} + +static void hci_cc_set_event_flt(struct hci_dev *hdev, struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%x", hdev->name, status); + + hci_req_complete(hdev, HCI_OP_SET_EVENT_FLT, status); +} + static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%x", hdev->name, status); @@ -1416,6 +1592,22 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_delete_stored_link_key(hdev, skb); break; + case HCI_OP_SET_EVENT_MASK: + hci_cc_set_event_mask(hdev, skb); + break; + + case HCI_OP_WRITE_INQUIRY_MODE: + hci_cc_write_inquiry_mode(hdev, skb); + break; + + case HCI_OP_READ_INQ_RSP_TX_POWER: + hci_cc_read_inq_rsp_tx_power(hdev, skb); + break; + + case HCI_OP_SET_EVENT_FLT: + hci_cc_set_event_flt(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; -- cgit v1.2.3 From 1aff6f09491f454d4cd9f405c783fa5e9d3168a0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 13 Jan 2011 21:56:52 +0200 Subject: Bluetooth: Add class of device control to the management interface This patch adds the possibility for user space to fully control the Class of Device value of local adapters. To control the service class bits each UUID that's added comes with a service class "hint" which acts as a mask of bits that the UUID needs to have enabled. The set_service_cache management command is used to make sure we queue up all UUID changes as user space initializes its drivers and then send a single HCI_Write_Class_of_Device command when initialization is complete. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/hci_core.h | 3 + include/net/bluetooth/mgmt.h | 14 +++++ net/bluetooth/mgmt.c | 121 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 136 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 99ac3516fe9d..9ce46cd00ba2 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -81,6 +81,7 @@ enum { HCI_AUTO_OFF, HCI_MGMT, HCI_PAIRABLE, + HCI_SERVICE_CACHE, }; /* HCI ioctl defines */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 71a3fbf1e785..e62da084e01d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -70,6 +70,7 @@ struct bdaddr_list { struct bt_uuid { struct list_head list; u8 uuid[16]; + u8 svc_hint; }; #define NUM_REASSEMBLY 4 @@ -86,6 +87,8 @@ struct hci_dev { bdaddr_t bdaddr; __u8 dev_name[248]; __u8 dev_class[3]; + __u8 major_class; + __u8 minor_class; __u8 features[8]; __u8 commands[64]; __u8 ssp_mode; diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index c118ad3af332..b092c4c014eb 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -76,6 +76,7 @@ struct mgmt_mode { struct mgmt_cp_add_uuid { __le16 index; __u8 uuid[16]; + __u8 svc_hint; } __packed; #define MGMT_OP_REMOVE_UUID 0x000A @@ -84,6 +85,19 @@ struct mgmt_cp_remove_uuid { __u8 uuid[16]; } __packed; +#define MGMT_OP_SET_DEV_CLASS 0x000B +struct mgmt_cp_set_dev_class { + __le16 index; + __u8 major; + __u8 minor; +} __packed; + +#define MGMT_OP_SET_SERVICE_CACHE 0x000C +struct mgmt_cp_set_service_cache { + __le16 index; + __u8 enable; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0854c2f1073c..a08f4ce03182 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -571,7 +571,7 @@ failed: return err; } -static int uuid_rsp(struct sock *sk, u16 opcode, u16 index) +static int index_rsp(struct sock *sk, u16 opcode, u16 index) { struct mgmt_hdr *hdr; struct mgmt_ev_cmd_complete *ev; @@ -596,6 +596,39 @@ static int uuid_rsp(struct sock *sk, u16 opcode, u16 index) return 0; } +static u8 get_service_classes(struct hci_dev *hdev) +{ + struct list_head *p; + u8 val = 0; + + list_for_each(p, &hdev->uuids) { + struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list); + + val |= uuid->svc_hint; + } + + return val; +} + +static int update_class(struct hci_dev *hdev) +{ + u8 cod[3]; + + BT_DBG("%s", hdev->name); + + if (test_bit(HCI_SERVICE_CACHE, &hdev->flags)) + return 0; + + cod[0] = hdev->minor_class; + cod[1] = hdev->major_class; + cod[2] = get_service_classes(hdev); + + if (memcmp(cod, hdev->dev_class, 3) == 0) + return 0; + + return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); +} + static int add_uuid(struct sock *sk, unsigned char *data, u16 len) { struct mgmt_cp_add_uuid *cp; @@ -622,10 +655,15 @@ static int add_uuid(struct sock *sk, unsigned char *data, u16 len) } memcpy(uuid->uuid, cp->uuid, 16); + uuid->svc_hint = cp->svc_hint; list_add(&uuid->list, &hdev->uuids); - err = uuid_rsp(sk, MGMT_OP_ADD_UUID, dev_id); + err = update_class(hdev); + if (err < 0) + goto failed; + + err = index_rsp(sk, MGMT_OP_ADD_UUID, dev_id); failed: hci_dev_unlock_bh(hdev); @@ -676,7 +714,11 @@ static int remove_uuid(struct sock *sk, unsigned char *data, u16 len) goto unlock; } - err = uuid_rsp(sk, MGMT_OP_REMOVE_UUID, dev_id); + err = update_class(hdev); + if (err < 0) + goto unlock; + + err = index_rsp(sk, MGMT_OP_REMOVE_UUID, dev_id); unlock: hci_dev_unlock_bh(hdev); @@ -685,6 +727,73 @@ unlock: return err; } +static int set_dev_class(struct sock *sk, unsigned char *data, u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_set_dev_class *cp; + u16 dev_id; + int err; + + cp = (void *) data; + dev_id = get_unaligned_le16(&cp->index); + + BT_DBG("request for hci%u", dev_id); + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, MGMT_OP_SET_DEV_CLASS, ENODEV); + + hci_dev_lock_bh(hdev); + + hdev->major_class = cp->major; + hdev->minor_class = cp->minor; + + err = update_class(hdev); + + if (err == 0) + err = index_rsp(sk, MGMT_OP_SET_DEV_CLASS, dev_id); + + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + +static int set_service_cache(struct sock *sk, unsigned char *data, u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_set_service_cache *cp; + u16 dev_id; + int err; + + cp = (void *) data; + dev_id = get_unaligned_le16(&cp->index); + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, MGMT_OP_SET_SERVICE_CACHE, ENODEV); + + hci_dev_lock_bh(hdev); + + BT_DBG("hci%u enable %d", dev_id, cp->enable); + + if (cp->enable) { + set_bit(HCI_SERVICE_CACHE, &hdev->flags); + err = 0; + } else { + clear_bit(HCI_SERVICE_CACHE, &hdev->flags); + err = update_class(hdev); + } + + if (err == 0) + err = index_rsp(sk, MGMT_OP_SET_SERVICE_CACHE, dev_id); + + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -743,6 +852,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_REMOVE_UUID: err = remove_uuid(sk, buf + sizeof(*hdr), len); break; + case MGMT_OP_SET_DEV_CLASS: + err = set_dev_class(sk, buf + sizeof(*hdr), len); + break; + case MGMT_OP_SET_SERVICE_CACHE: + err = set_service_cache(sk, buf + sizeof(*hdr), len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, opcode, 0x01); -- cgit v1.2.3 From 55ed8ca10f3530de8edbbf138acb50992bf5005b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 17 Jan 2011 14:41:05 +0200 Subject: Bluetooth: Implement link key handling for the management interface This patch adds a management commands to feed the kernel with all stored link keys as well as remove specific ones or all of them. Once the load_keys command has been called the kernel takes over link key replies. A new_key event is also added to inform userspace of newly created link keys that should be stored permanently. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 2 + include/net/bluetooth/hci_core.h | 17 ++++++ include/net/bluetooth/mgmt.h | 29 ++++++++++ net/bluetooth/hci_core.c | 85 ++++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 51 +++++++++++++++++ net/bluetooth/mgmt.c | 116 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 300 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 9ce46cd00ba2..08fbf1253b83 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -82,6 +82,8 @@ enum { HCI_MGMT, HCI_PAIRABLE, HCI_SERVICE_CACHE, + HCI_LINK_KEYS, + HCI_DEBUG_KEYS, }; /* HCI ioctl defines */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e62da084e01d..009fa63a9048 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -73,6 +73,14 @@ struct bt_uuid { u8 svc_hint; }; +struct link_key { + struct list_head list; + bdaddr_t bdaddr; + u8 type; + u8 val[16]; + u8 pin_len; +}; + #define NUM_REASSEMBLY 4 struct hci_dev { struct list_head list; @@ -153,6 +161,8 @@ struct hci_dev { struct list_head uuids; + struct list_head link_keys; + struct hci_dev_stats stat; struct sk_buff_head driver_init; @@ -461,6 +471,12 @@ int hci_blacklist_clear(struct hci_dev *hdev); int hci_uuids_clear(struct hci_dev *hdev); +int hci_link_keys_clear(struct hci_dev *hdev); +struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); +int hci_add_link_key(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, + u8 *key, u8 type, u8 pin_len); +int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); + void hci_del_off_timer(struct hci_dev *hdev); void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); @@ -697,6 +713,7 @@ int mgmt_index_removed(u16 index); int mgmt_powered(u16 index, u8 powered); int mgmt_discoverable(u16 index, u8 discoverable); int mgmt_connectable(u16 index, u8 connectable); +int mgmt_new_key(u16 index, struct link_key *key, u8 old_key_type); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index b092c4c014eb..56b500a2f68c 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -98,6 +98,28 @@ struct mgmt_cp_set_service_cache { __u8 enable; } __packed; +struct mgmt_key_info { + bdaddr_t bdaddr; + u8 type; + u8 val[16]; + u8 pin_len; +} __packed; + +#define MGMT_OP_LOAD_KEYS 0x000D +struct mgmt_cp_load_keys { + __le16 index; + __u8 debug_keys; + __le16 key_count; + struct mgmt_key_info keys[0]; +} __packed; + +#define MGMT_OP_REMOVE_KEY 0x000E +struct mgmt_cp_remove_key { + __le16 index; + bdaddr_t bdaddr; + __u8 disconnect; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; @@ -133,3 +155,10 @@ struct mgmt_ev_index_removed { #define MGMT_EV_CONNECTABLE 0x0008 #define MGMT_EV_PAIRABLE 0x0009 + +#define MGMT_EV_NEW_KEY 0x000A +struct mgmt_ev_new_key { + __le16 index; + struct mgmt_key_info key; + __u8 old_key_type; +} __packed; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 748f5a65caf4..8ca8cf147058 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -970,6 +970,88 @@ int hci_uuids_clear(struct hci_dev *hdev) return 0; } +int hci_link_keys_clear(struct hci_dev *hdev) +{ + struct list_head *p, *n; + + list_for_each_safe(p, n, &hdev->link_keys) { + struct link_key *key; + + key = list_entry(p, struct link_key, list); + + list_del(p); + kfree(key); + } + + return 0; +} + +struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) +{ + struct list_head *p; + + list_for_each(p, &hdev->link_keys) { + struct link_key *k; + + k = list_entry(p, struct link_key, list); + + if (bacmp(bdaddr, &k->bdaddr) == 0) + return k; + } + + return NULL; +} + +int hci_add_link_key(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, + u8 *val, u8 type, u8 pin_len) +{ + struct link_key *key, *old_key; + u8 old_key_type; + + old_key = hci_find_link_key(hdev, bdaddr); + if (old_key) { + old_key_type = old_key->type; + key = old_key; + } else { + old_key_type = 0xff; + key = kzalloc(sizeof(*key), GFP_ATOMIC); + if (!key) + return -ENOMEM; + list_add(&key->list, &hdev->link_keys); + } + + BT_DBG("%s key for %s type %u", hdev->name, batostr(bdaddr), type); + + bacpy(&key->bdaddr, bdaddr); + memcpy(key->val, val, 16); + key->type = type; + key->pin_len = pin_len; + + if (new_key) + mgmt_new_key(hdev->id, key, old_key_type); + + if (type == 0x06) + key->type = old_key_type; + + return 0; +} + +int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) +{ + struct link_key *key; + + key = hci_find_link_key(hdev, bdaddr); + if (!key) + return -ENOENT; + + BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); + + list_del(&key->list); + kfree(key); + + return 0; +} + /* Register HCI device */ int hci_register_dev(struct hci_dev *hdev) { @@ -1029,6 +1111,8 @@ int hci_register_dev(struct hci_dev *hdev) INIT_LIST_HEAD(&hdev->uuids); + INIT_LIST_HEAD(&hdev->link_keys); + INIT_WORK(&hdev->power_on, hci_power_on); INIT_WORK(&hdev->power_off, hci_power_off); setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev); @@ -1105,6 +1189,7 @@ int hci_unregister_dev(struct hci_dev *hdev) hci_dev_lock_bh(hdev); hci_blacklist_clear(hdev); hci_uuids_clear(hdev); + hci_link_keys_clear(hdev); hci_dev_unlock_bh(hdev); __hci_dev_put(hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index c69ee44d5bd7..80ffd3a901fc 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1810,13 +1810,60 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { + struct hci_ev_link_key_req *ev = (void *) skb->data; + struct hci_cp_link_key_reply cp; + struct hci_conn *conn; + struct link_key *key; + BT_DBG("%s", hdev->name); + + if (!test_bit(HCI_LINK_KEYS, &hdev->flags)) + return; + + hci_dev_lock(hdev); + + key = hci_find_link_key(hdev, &ev->bdaddr); + if (!key) { + BT_DBG("%s link key not found for %s", hdev->name, + batostr(&ev->bdaddr)); + goto not_found; + } + + BT_DBG("%s found key type %u for %s", hdev->name, key->type, + batostr(&ev->bdaddr)); + + if (!test_bit(HCI_DEBUG_KEYS, &hdev->flags) && key->type == 0x03) { + BT_DBG("%s ignoring debug key", hdev->name); + goto not_found; + } + + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); + + if (key->type == 0x04 && conn && conn->auth_type != 0xff && + (conn->auth_type & 0x01)) { + BT_DBG("%s ignoring unauthenticated key", hdev->name); + goto not_found; + } + + bacpy(&cp.bdaddr, &ev->bdaddr); + memcpy(cp.link_key, key->val, 16); + + hci_send_cmd(hdev, HCI_OP_LINK_KEY_REPLY, sizeof(cp), &cp); + + hci_dev_unlock(hdev); + + return; + +not_found: + hci_send_cmd(hdev, HCI_OP_LINK_KEY_NEG_REPLY, 6, &ev->bdaddr); + hci_dev_unlock(hdev); } static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_link_key_notify *ev = (void *) skb->data; struct hci_conn *conn; + u8 pin_len = 0; BT_DBG("%s", hdev->name); @@ -1829,6 +1876,10 @@ static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff hci_conn_put(conn); } + if (test_bit(HCI_LINK_KEYS, &hdev->flags)) + hci_add_link_key(hdev, 1, &ev->bdaddr, ev->link_key, + ev->key_type, pin_len); + hci_dev_unlock(hdev); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a08f4ce03182..bdb0e85f182e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -794,6 +794,99 @@ static int set_service_cache(struct sock *sk, unsigned char *data, u16 len) return err; } +static int load_keys(struct sock *sk, unsigned char *data, u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_load_keys *cp; + u16 dev_id, key_count, expected_len; + int i; + + cp = (void *) data; + dev_id = get_unaligned_le16(&cp->index); + key_count = get_unaligned_le16(&cp->key_count); + + expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info); + if (expected_len != len) { + BT_ERR("load_keys: expected %u bytes, got %u bytes", + len, expected_len); + return -EINVAL; + } + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, MGMT_OP_LOAD_KEYS, ENODEV); + + BT_DBG("hci%u debug_keys %u key_count %u", dev_id, cp->debug_keys, + key_count); + + hci_dev_lock_bh(hdev); + + hci_link_keys_clear(hdev); + + set_bit(HCI_LINK_KEYS, &hdev->flags); + + if (cp->debug_keys) + set_bit(HCI_DEBUG_KEYS, &hdev->flags); + else + clear_bit(HCI_DEBUG_KEYS, &hdev->flags); + + for (i = 0; i < key_count; i++) { + struct mgmt_key_info *key = &cp->keys[i]; + + hci_add_link_key(hdev, 0, &key->bdaddr, key->val, key->type, + key->pin_len); + } + + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return 0; +} + +static int remove_key(struct sock *sk, unsigned char *data, u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_remove_key *cp; + struct hci_conn *conn; + u16 dev_id; + int err; + + cp = (void *) data; + dev_id = get_unaligned_le16(&cp->index); + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, MGMT_OP_REMOVE_KEY, ENODEV); + + hci_dev_lock_bh(hdev); + + err = hci_remove_link_key(hdev, &cp->bdaddr); + if (err < 0) { + err = cmd_status(sk, MGMT_OP_REMOVE_KEY, -err); + goto unlock; + } + + err = 0; + + if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) + goto unlock; + + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); + if (conn) { + struct hci_cp_disconnect dc; + + put_unaligned_le16(conn->handle, &dc.handle); + dc.reason = 0x13; /* Remote User Terminated Connection */ + err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, 0, NULL); + } + +unlock: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -858,6 +951,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_SET_SERVICE_CACHE: err = set_service_cache(sk, buf + sizeof(*hdr), len); break; + case MGMT_OP_LOAD_KEYS: + err = load_keys(sk, buf + sizeof(*hdr), len); + break; + case MGMT_OP_REMOVE_KEY: + err = remove_key(sk, buf + sizeof(*hdr), len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, opcode, 0x01); @@ -974,3 +1073,20 @@ int mgmt_connectable(u16 index, u8 connectable) return ret; } + +int mgmt_new_key(u16 index, struct link_key *key, u8 old_key_type) +{ + struct mgmt_ev_new_key ev; + + memset(&ev, 0, sizeof(ev)); + + put_unaligned_le16(index, &ev.index); + + bacpy(&ev.key.bdaddr, &key->bdaddr); + ev.key.type = key->type; + memcpy(ev.key.val, key->val, 16); + ev.key.pin_len = key->pin_len; + ev.old_key_type = old_key_type; + + return mgmt_event(MGMT_EV_NEW_KEY, &ev, sizeof(ev), NULL); +} -- cgit v1.2.3 From f7520543ab40341edbc2aeee7fef68218be19a0a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 20 Jan 2011 12:34:39 +0200 Subject: Bluetooth: Add connected/disconnected management events This patch adds connected and disconnected managment events to track the connection status to remote devices. The events map directly to successful connection complete and disconnection complete HCI events for ACL links. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 2 ++ include/net/bluetooth/mgmt.h | 12 ++++++++++++ net/bluetooth/hci_event.c | 16 +++++++++++----- net/bluetooth/mgmt.c | 20 ++++++++++++++++++++ 4 files changed, 45 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 009fa63a9048..746f8dc8aad1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -714,6 +714,8 @@ int mgmt_powered(u16 index, u8 powered); int mgmt_discoverable(u16 index, u8 discoverable); int mgmt_connectable(u16 index, u8 connectable); int mgmt_new_key(u16 index, struct link_key *key, u8 old_key_type); +int mgmt_connected(u16 index, bdaddr_t *bdaddr); +int mgmt_disconnected(u16 index, bdaddr_t *bdaddr); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 56b500a2f68c..6719e9a36613 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -162,3 +162,15 @@ struct mgmt_ev_new_key { struct mgmt_key_info key; __u8 old_key_type; } __packed; + +#define MGMT_EV_CONNECTED 0x000B +struct mgmt_ev_connected { + __le16 index; + bdaddr_t bdaddr; +} __packed; + +#define MGMT_EV_DISCONNECTED 0x000C +struct mgmt_ev_disconnected { + __le16 index; + bdaddr_t bdaddr; +} __packed; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 80ffd3a901fc..46ddb029912b 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1137,6 +1137,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s conn->state = BT_CONFIG; hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; + mgmt_connected(hdev->id, &ev->bdaddr); } else conn->state = BT_CONNECTED; @@ -1269,13 +1270,18 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn) { - conn->state = BT_CLOSED; + if (!conn) + goto unlock; - hci_proto_disconn_cfm(conn, ev->reason); - hci_conn_del(conn); - } + conn->state = BT_CLOSED; + + if (conn->type == ACL_LINK) + mgmt_disconnected(hdev->id, &conn->dst); + hci_proto_disconn_cfm(conn, ev->reason); + hci_conn_del(conn); + +unlock: hci_dev_unlock(hdev); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bdb0e85f182e..7cf1968157d8 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1090,3 +1090,23 @@ int mgmt_new_key(u16 index, struct link_key *key, u8 old_key_type) return mgmt_event(MGMT_EV_NEW_KEY, &ev, sizeof(ev), NULL); } + +int mgmt_connected(u16 index, bdaddr_t *bdaddr) +{ + struct mgmt_ev_connected ev; + + put_unaligned_le16(index, &ev.index); + bacpy(&ev.bdaddr, bdaddr); + + return mgmt_event(MGMT_EV_CONNECTED, &ev, sizeof(ev), NULL); +} + +int mgmt_disconnected(u16 index, bdaddr_t *bdaddr) +{ + struct mgmt_ev_disconnected ev; + + put_unaligned_le16(index, &ev.index); + bacpy(&ev.bdaddr, bdaddr); + + return mgmt_event(MGMT_EV_DISCONNECTED, &ev, sizeof(ev), NULL); +} -- cgit v1.2.3 From 8962ee74be48df16027100f657b2b12e8ef3d34d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 20 Jan 2011 12:40:27 +0200 Subject: Bluetooth: Add disconnect managment command This patch adds a disconnect command to the managment interface. Using this command user space is able to force the disconnection of connected devices. The command maps directly to the Disconnect HCI command. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/mgmt.h | 10 ++++ net/bluetooth/hci_event.c | 9 ++- net/bluetooth/mgmt.c | 119 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 137 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 746f8dc8aad1..2197a099a2b7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -716,6 +716,7 @@ int mgmt_connectable(u16 index, u8 connectable); int mgmt_new_key(u16 index, struct link_key *key, u8 old_key_type); int mgmt_connected(u16 index, bdaddr_t *bdaddr); int mgmt_disconnected(u16 index, bdaddr_t *bdaddr); +int mgmt_disconnect_failed(u16 index); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 6719e9a36613..2c47601b6e63 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -120,6 +120,16 @@ struct mgmt_cp_remove_key { __u8 disconnect; } __packed; +#define MGMT_OP_DISCONNECT 0x000F +struct mgmt_cp_disconnect { + __le16 index; + bdaddr_t bdaddr; +} __packed; +struct mgmt_rp_disconnect { + __le16 index; + bdaddr_t bdaddr; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 46ddb029912b..335c60bad96c 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1264,8 +1264,10 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff BT_DBG("%s status %d", hdev->name, ev->status); - if (ev->status) + if (ev->status) { + mgmt_disconnect_failed(hdev->id); return; + } hci_dev_lock(hdev); @@ -1680,6 +1682,11 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_exit_sniff_mode(hdev, ev->status); break; + case HCI_OP_DISCONNECT: + if (ev->status != 0) + mgmt_disconnect_failed(hdev->id); + break; + default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7cf1968157d8..48f266a64caf 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -887,6 +887,60 @@ unlock: return err; } +static int disconnect(struct sock *sk, unsigned char *data, u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_disconnect *cp; + struct hci_cp_disconnect dc; + struct hci_conn *conn; + u16 dev_id; + int err; + + BT_DBG(""); + + cp = (void *) data; + dev_id = get_unaligned_le16(&cp->index); + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, MGMT_OP_DISCONNECT, ENODEV); + + hci_dev_lock_bh(hdev); + + if (!test_bit(HCI_UP, &hdev->flags)) { + err = cmd_status(sk, MGMT_OP_DISCONNECT, ENETDOWN); + goto failed; + } + + if (mgmt_pending_find(MGMT_OP_DISCONNECT, dev_id)) { + err = cmd_status(sk, MGMT_OP_DISCONNECT, EBUSY); + goto failed; + } + + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); + if (!conn) { + err = cmd_status(sk, MGMT_OP_DISCONNECT, ENOTCONN); + goto failed; + } + + err = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, dev_id, data, len); + if (err < 0) + goto failed; + + put_unaligned_le16(conn->handle, &dc.handle); + dc.reason = 0x13; /* Remote User Terminated Connection */ + + err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); + if (err < 0) + mgmt_pending_remove(MGMT_OP_DISCONNECT, dev_id); + +failed: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -957,6 +1011,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_REMOVE_KEY: err = remove_key(sk, buf + sizeof(*hdr), len); break; + case MGMT_OP_DISCONNECT: + err = disconnect(sk, buf + sizeof(*hdr), len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, opcode, 0x01); @@ -1101,12 +1158,72 @@ int mgmt_connected(u16 index, bdaddr_t *bdaddr) return mgmt_event(MGMT_EV_CONNECTED, &ev, sizeof(ev), NULL); } +static void disconnect_rsp(struct pending_cmd *cmd, void *data) +{ + struct mgmt_cp_disconnect *cp = cmd->cmd; + struct sock **sk = data; + struct sk_buff *skb; + struct mgmt_hdr *hdr; + struct mgmt_ev_cmd_complete *ev; + struct mgmt_rp_disconnect *rp; + + skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); + if (!skb) + return; + + hdr = (void *) skb_put(skb, sizeof(*hdr)); + hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); + hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); + + ev = (void *) skb_put(skb, sizeof(*ev)); + put_unaligned_le16(MGMT_OP_DISCONNECT, &ev->opcode); + + rp = (void *) skb_put(skb, sizeof(*rp)); + put_unaligned_le16(cmd->index, &rp->index); + bacpy(&rp->bdaddr, &cp->bdaddr); + + if (sock_queue_rcv_skb(cmd->sk, skb) < 0) + kfree_skb(skb); + + *sk = cmd->sk; + sock_hold(*sk); + + list_del(&cmd->list); + mgmt_pending_free(cmd); +} + int mgmt_disconnected(u16 index, bdaddr_t *bdaddr) { struct mgmt_ev_disconnected ev; + struct sock *sk = NULL; + int err; + + mgmt_pending_foreach(MGMT_OP_DISCONNECT, index, disconnect_rsp, &sk); put_unaligned_le16(index, &ev.index); bacpy(&ev.bdaddr, bdaddr); - return mgmt_event(MGMT_EV_DISCONNECTED, &ev, sizeof(ev), NULL); + err = mgmt_event(MGMT_EV_DISCONNECTED, &ev, sizeof(ev), sk); + + if (sk) + sock_put(sk); + + return err; +} + +int mgmt_disconnect_failed(u16 index) +{ + struct pending_cmd *cmd; + int err; + + cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, index); + if (!cmd) + return -ENOENT; + + err = cmd_status(cmd->sk, MGMT_OP_DISCONNECT, EIO); + + list_del(&cmd->list); + mgmt_pending_free(cmd); + + return err; } -- cgit v1.2.3 From 17d5c04cb597418a177c3ca18dfde679636dd51c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 22 Jan 2011 06:09:08 +0200 Subject: Bluetooth: Add support for connect failed management event This patch add a new connect failed management event to track failures in connecting to remote devices. It is particularly useful for security mode 3 scenarios when we don't have a connected state while pairing but still need to detect when the connect attempt failed. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/mgmt.h | 7 +++++++ net/bluetooth/hci_event.c | 5 ++++- net/bluetooth/mgmt.c | 11 +++++++++++ 4 files changed, 23 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 2197a099a2b7..45caae62cb8e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -717,6 +717,7 @@ int mgmt_new_key(u16 index, struct link_key *key, u8 old_key_type); int mgmt_connected(u16 index, bdaddr_t *bdaddr); int mgmt_disconnected(u16 index, bdaddr_t *bdaddr); int mgmt_disconnect_failed(u16 index); +int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 2c47601b6e63..1d822f2c0f1a 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -184,3 +184,10 @@ struct mgmt_ev_disconnected { __le16 index; bdaddr_t bdaddr; } __packed; + +#define MGMT_EV_CONNECT_FAILED 0x000D +struct mgmt_ev_connect_failed { + __le16 index; + bdaddr_t bdaddr; + __u8 status; +} __packed; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 335c60bad96c..995ae6c17f11 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1166,8 +1166,11 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE, sizeof(cp), &cp); } - } else + } else { conn->state = BT_CLOSED; + if (conn->type == ACL_LINK) + mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status); + } if (conn->type == ACL_LINK) hci_sco_setup(conn, ev->status); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 48f266a64caf..9fb989f4216e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1227,3 +1227,14 @@ int mgmt_disconnect_failed(u16 index) return err; } + +int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status) +{ + struct mgmt_ev_connect_failed ev; + + put_unaligned_le16(index, &ev.index); + bacpy(&ev.bdaddr, bdaddr); + ev.status = status; + + return mgmt_event(MGMT_EV_CONNECT_FAILED, &ev, sizeof(ev), NULL); +} -- cgit v1.2.3 From 2784eb41b1fbb3ff80f4921fe9dbb4c4acb6dc24 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 21 Jan 2011 13:56:35 +0200 Subject: Bluetooth: Add get_connections managment interface command This patch adds a get_connections command to the management interface. With this command userspace can get the current list of connected devices. Typically this command would only be used once when enumerating existing adapters. After that the connected and disconnected events are used to track connections. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/mgmt.h | 10 ++++++ net/bluetooth/mgmt.c | 72 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 1d822f2c0f1a..3d8d589fa559 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -130,6 +130,16 @@ struct mgmt_rp_disconnect { bdaddr_t bdaddr; } __packed; +#define MGMT_OP_GET_CONNECTIONS 0x0010 +struct mgmt_cp_get_connections { + __le16 index; +} __packed; +struct mgmt_rp_get_connections { + __le16 index; + __le16 conn_count; + bdaddr_t conn[0]; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 9fb989f4216e..8f4f47e9d5c9 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -941,6 +941,75 @@ failed: return err; } +static int get_connections(struct sock *sk, unsigned char *data, u16 len) +{ + struct sk_buff *skb; + struct mgmt_hdr *hdr; + struct mgmt_cp_get_connections *cp; + struct mgmt_ev_cmd_complete *ev; + struct mgmt_rp_get_connections *rp; + struct hci_dev *hdev; + struct list_head *p; + size_t body_len; + u16 dev_id, count; + int i, err; + + BT_DBG(""); + + cp = (void *) data; + dev_id = get_unaligned_le16(&cp->index); + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, MGMT_OP_GET_CONNECTIONS, ENODEV); + + hci_dev_lock_bh(hdev); + + count = 0; + list_for_each(p, &hdev->conn_hash.list) { + count++; + } + + body_len = sizeof(*ev) + sizeof(*rp) + (count * sizeof(bdaddr_t)); + skb = alloc_skb(sizeof(*hdr) + body_len, GFP_ATOMIC); + if (!skb) { + err = -ENOMEM; + goto unlock; + } + + hdr = (void *) skb_put(skb, sizeof(*hdr)); + hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); + hdr->len = cpu_to_le16(body_len); + + ev = (void *) skb_put(skb, sizeof(*ev)); + put_unaligned_le16(MGMT_OP_GET_CONNECTIONS, &ev->opcode); + + rp = (void *) skb_put(skb, sizeof(*rp) + (count * sizeof(bdaddr_t))); + put_unaligned_le16(dev_id, &rp->index); + put_unaligned_le16(count, &rp->conn_count); + + read_lock(&hci_dev_list_lock); + + i = 0; + list_for_each(p, &hdev->conn_hash.list) { + struct hci_conn *c = list_entry(p, struct hci_conn, list); + + bacpy(&rp->conn[i++], &c->dst); + } + + read_unlock(&hci_dev_list_lock); + + if (sock_queue_rcv_skb(sk, skb) < 0) + kfree_skb(skb); + + err = 0; + +unlock: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + return err; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -1014,6 +1083,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_DISCONNECT: err = disconnect(sk, buf + sizeof(*hdr), len); break; + case MGMT_OP_GET_CONNECTIONS: + err = get_connections(sk, buf + sizeof(*hdr), len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, opcode, 0x01); -- cgit v1.2.3 From 980e1a537fed7dfa53e9a4b6e586b43341f8c2d5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 22 Jan 2011 06:10:07 +0200 Subject: Bluetooth: Add support for PIN code handling in the management interface This patch adds the necessary commands and events needed to communicate PIN code related actions between the kernel and userspace. This includes a pin_code_request event as well as pin_code_reply and pin_code_negative_reply commands. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 8 +++ include/net/bluetooth/hci_core.h | 4 ++ include/net/bluetooth/mgmt.h | 20 ++++++ net/bluetooth/hci_event.c | 46 +++++++++++++ net/bluetooth/mgmt.c | 141 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 219 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 08fbf1253b83..e8e52da2b26b 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -309,11 +309,19 @@ struct hci_cp_pin_code_reply { __u8 pin_len; __u8 pin_code[16]; } __packed; +struct hci_rp_pin_code_reply { + __u8 status; + bdaddr_t bdaddr; +} __packed; #define HCI_OP_PIN_CODE_NEG_REPLY 0x040e struct hci_cp_pin_code_neg_reply { bdaddr_t bdaddr; } __packed; +struct hci_rp_pin_code_neg_reply { + __u8 status; + bdaddr_t bdaddr; +} __packed; #define HCI_OP_CHANGE_CONN_PTYPE 0x040f struct hci_cp_change_conn_ptype { diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 45caae62cb8e..9ac3da6e4a9a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -213,6 +213,7 @@ struct hci_conn { __u8 auth_type; __u8 sec_level; __u8 pending_sec_level; + __u8 pin_length; __u8 power_save; __u16 disc_timeout; unsigned long pend; @@ -718,6 +719,9 @@ int mgmt_connected(u16 index, bdaddr_t *bdaddr); int mgmt_disconnected(u16 index, bdaddr_t *bdaddr); int mgmt_disconnect_failed(u16 index); int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status); +int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr); +int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); +int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 3d8d589fa559..46fb56d21b59 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -140,6 +140,20 @@ struct mgmt_rp_get_connections { bdaddr_t conn[0]; } __packed; +#define MGMT_OP_PIN_CODE_REPLY 0x0011 +struct mgmt_cp_pin_code_reply { + __le16 index; + bdaddr_t bdaddr; + __u8 pin_len; + __u8 pin_code[16]; +} __packed; + +#define MGMT_OP_PIN_CODE_NEG_REPLY 0x0012 +struct mgmt_cp_pin_code_neg_reply { + __le16 index; + bdaddr_t bdaddr; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; @@ -201,3 +215,9 @@ struct mgmt_ev_connect_failed { bdaddr_t bdaddr; __u8 status; } __packed; + +#define MGMT_EV_PIN_CODE_REQUEST 0x000E +struct mgmt_ev_pin_code_request { + __le16 index; + bdaddr_t bdaddr; +} __packed; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 995ae6c17f11..98bcf78f2021 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -743,6 +743,40 @@ static void hci_cc_set_event_flt(struct hci_dev *hdev, struct sk_buff *skb) hci_req_complete(hdev, HCI_OP_SET_EVENT_FLT, status); } +static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_pin_code_reply *rp = (void *) skb->data; + struct hci_cp_pin_code_reply *cp; + struct hci_conn *conn; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (test_bit(HCI_MGMT, &hdev->flags)) + mgmt_pin_code_reply_complete(hdev->id, &rp->bdaddr, rp->status); + + if (rp->status != 0) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY); + if (!cp) + return; + + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); + if (conn) + conn->pin_length = cp->pin_len; +} + +static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_pin_code_neg_reply *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (test_bit(HCI_MGMT, &hdev->flags)) + mgmt_pin_code_neg_reply_complete(hdev->id, &rp->bdaddr, + rp->status); +} + static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%x", hdev->name, status); @@ -1619,6 +1653,14 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_set_event_flt(hdev, skb); break; + case HCI_OP_PIN_CODE_REPLY: + hci_cc_pin_code_reply(hdev, skb); + break; + + case HCI_OP_PIN_CODE_NEG_REPLY: + hci_cc_pin_code_neg_reply(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; @@ -1821,6 +1863,9 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(ev->bdaddr), &ev->bdaddr); + if (test_bit(HCI_MGMT, &hdev->flags)) + mgmt_pin_code_request(hdev->id, &ev->bdaddr); + hci_dev_unlock(hdev); } @@ -1889,6 +1934,7 @@ static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff if (conn) { hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; + pin_len = conn->pin_length; hci_conn_put(conn); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 005288b2a58e..3800aaf5792d 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -933,6 +933,89 @@ unlock: return err; } +static int pin_code_reply(struct sock *sk, unsigned char *data, u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_pin_code_reply *cp; + struct hci_cp_pin_code_reply reply; + u16 dev_id; + int err; + + BT_DBG(""); + + cp = (void *) data; + dev_id = get_unaligned_le16(&cp->index); + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, MGMT_OP_DISCONNECT, ENODEV); + + hci_dev_lock_bh(hdev); + + if (!test_bit(HCI_UP, &hdev->flags)) { + err = cmd_status(sk, MGMT_OP_PIN_CODE_REPLY, ENETDOWN); + goto failed; + } + + err = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, dev_id, data, len); + if (err < 0) + goto failed; + + bacpy(&reply.bdaddr, &cp->bdaddr); + reply.pin_len = cp->pin_len; + memcpy(reply.pin_code, cp->pin_code, 16); + + err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply); + if (err < 0) + mgmt_pending_remove(MGMT_OP_PIN_CODE_REPLY, dev_id); + +failed: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + +static int pin_code_neg_reply(struct sock *sk, unsigned char *data, u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_pin_code_neg_reply *cp; + u16 dev_id; + int err; + + BT_DBG(""); + + cp = (void *) data; + dev_id = get_unaligned_le16(&cp->index); + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, MGMT_OP_PIN_CODE_NEG_REPLY, ENODEV); + + hci_dev_lock_bh(hdev); + + if (!test_bit(HCI_UP, &hdev->flags)) { + err = cmd_status(sk, MGMT_OP_PIN_CODE_NEG_REPLY, ENETDOWN); + goto failed; + } + + err = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, dev_id, + data, len); + if (err < 0) + goto failed; + + err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(bdaddr_t), + &cp->bdaddr); + if (err < 0) + mgmt_pending_remove(MGMT_OP_PIN_CODE_NEG_REPLY, dev_id); + +failed: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -1009,6 +1092,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_GET_CONNECTIONS: err = get_connections(sk, buf + sizeof(*hdr), len); break; + case MGMT_OP_PIN_CODE_REPLY: + err = pin_code_reply(sk, buf + sizeof(*hdr), len); + break; + case MGMT_OP_PIN_CODE_NEG_REPLY: + err = pin_code_neg_reply(sk, buf + sizeof(*hdr), len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, opcode, 0x01); @@ -1217,3 +1306,55 @@ int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status) return mgmt_event(MGMT_EV_CONNECT_FAILED, &ev, sizeof(ev), NULL); } + +int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr) +{ + struct mgmt_ev_pin_code_request ev; + + put_unaligned_le16(index, &ev.index); + bacpy(&ev.bdaddr, bdaddr); + + return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, &ev, sizeof(ev), NULL); +} + +int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +{ + struct pending_cmd *cmd; + int err; + + cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, index); + if (!cmd) + return -ENOENT; + + if (status != 0) + err = cmd_status(cmd->sk, MGMT_OP_PIN_CODE_REPLY, status); + else + err = cmd_complete(cmd->sk, MGMT_OP_PIN_CODE_REPLY, + bdaddr, sizeof(*bdaddr)); + + list_del(&cmd->list); + mgmt_pending_free(cmd); + + return err; +} + +int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +{ + struct pending_cmd *cmd; + int err; + + cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, index); + if (!cmd) + return -ENOENT; + + if (status != 0) + err = cmd_status(cmd->sk, MGMT_OP_PIN_CODE_NEG_REPLY, status); + else + err = cmd_complete(cmd->sk, MGMT_OP_PIN_CODE_NEG_REPLY, + bdaddr, sizeof(*bdaddr)); + + list_del(&cmd->list); + mgmt_pending_free(cmd); + + return err; +} -- cgit v1.2.3 From 17fa4b9dff72fb3a1a68cc80caf98fc941d2b8b3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 25 Jan 2011 13:28:33 +0200 Subject: Bluetooth: Add set_io_capability management command This patch adds a new set_io_capability management command which is used to set the IO capability for Secure Simple Pairing (SSP) as well as the Security Manager Protocol (SMP). The value is per hci_dev and each hci_conn object inherits it upon creation. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 8 ++++++++ include/net/bluetooth/hci_core.h | 2 ++ include/net/bluetooth/mgmt.h | 6 ++++++ net/bluetooth/hci_conn.c | 1 + net/bluetooth/hci_core.c | 1 + net/bluetooth/hci_event.c | 30 ++++++++++++++++++++++++++++-- net/bluetooth/mgmt.c | 32 ++++++++++++++++++++++++++++++++ 7 files changed, 78 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index e8e52da2b26b..4bee030e4b52 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -402,6 +402,14 @@ struct hci_cp_reject_sync_conn_req { __u8 reason; } __packed; +#define HCI_OP_IO_CAPABILITY_REPLY 0x042b +struct hci_cp_io_capability_reply { + bdaddr_t bdaddr; + __u8 capability; + __u8 oob_data; + __u8 authentication; +} __packed; + #define HCI_OP_IO_CAPABILITY_NEG_REPLY 0x0434 struct hci_cp_io_capability_neg_reply { bdaddr_t bdaddr; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9ac3da6e4a9a..6163bff6fa91 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -106,6 +106,7 @@ struct hci_dev { __u16 manufacturer; __le16 lmp_subver; __u16 voice_setting; + __u8 io_capability; __u16 pkt_type; __u16 esco_type; @@ -214,6 +215,7 @@ struct hci_conn { __u8 sec_level; __u8 pending_sec_level; __u8 pin_length; + __u8 io_capability; __u8 power_save; __u16 disc_timeout; unsigned long pend; diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 46fb56d21b59..44ac55c85079 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -154,6 +154,12 @@ struct mgmt_cp_pin_code_neg_reply { bdaddr_t bdaddr; } __packed; +#define MGMT_OP_SET_IO_CAPABILITY 0x0013 +struct mgmt_cp_set_io_capability { + __le16 index; + __u8 io_capability; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 99cd8d9d891b..42dc39f25b72 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -234,6 +234,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) conn->mode = HCI_CM_ACTIVE; conn->state = BT_OPEN; conn->auth_type = HCI_AT_GENERAL_BONDING; + conn->io_capability = hdev->io_capability; conn->power_save = 1; conn->disc_timeout = HCI_DISCONN_TIMEOUT; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8ca8cf147058..bf6729a53378 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1084,6 +1084,7 @@ int hci_register_dev(struct hci_dev *hdev) hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1); hdev->esco_type = (ESCO_HV1); hdev->link_mode = (HCI_LM_ACCEPT); + hdev->io_capability = 0x03; /* No Input No Output */ hdev->idle_timeout = 0; hdev->sniff_max_interval = 800; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 98bcf78f2021..617f58363dbc 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2198,6 +2198,25 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct hci_dev_unlock(hdev); } +static inline u8 hci_get_auth_req(struct hci_conn *conn) +{ + /* If remote requests dedicated bonding follow that lead */ + if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) { + /* If both remote and local IO capabilities allow MITM + * protection then require it, otherwise don't */ + if (conn->remote_cap == 0x03 || conn->io_capability == 0x03) + return 0x02; + else + return 0x03; + } + + /* If remote requests no-bonding follow that lead */ + if (conn->remote_auth == 0x00 || conn->remote_auth == 0x01) + return 0x00; + + return conn->auth_type; +} + static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_io_capa_request *ev = (void *) skb->data; @@ -2218,8 +2237,15 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff if (test_bit(HCI_PAIRABLE, &hdev->flags) || (conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) { - /* FIXME: Do IO capa response based on information - * provided through the management interface */ + struct hci_cp_io_capability_reply cp; + + bacpy(&cp.bdaddr, &ev->bdaddr); + cp.capability = conn->io_capability; + cp.oob_data = 0; + cp.authentication = hci_get_auth_req(conn); + + hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_REPLY, + sizeof(cp), &cp); } else { struct hci_cp_io_capability_neg_reply cp; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 3800aaf5792d..b2bda83050a4 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1016,6 +1016,35 @@ failed: return err; } +static int set_io_capability(struct sock *sk, unsigned char *data, u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_set_io_capability *cp; + u16 dev_id; + + BT_DBG(""); + + cp = (void *) data; + dev_id = get_unaligned_le16(&cp->index); + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, MGMT_OP_SET_IO_CAPABILITY, ENODEV); + + hci_dev_lock_bh(hdev); + + hdev->io_capability = cp->io_capability; + + BT_DBG("%s IO capability set to 0x%02x", hdev->name, + hdev->io_capability); + + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return cmd_complete(sk, MGMT_OP_SET_IO_CAPABILITY, + &dev_id, sizeof(dev_id)); +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -1098,6 +1127,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_PIN_CODE_NEG_REPLY: err = pin_code_neg_reply(sk, buf + sizeof(*hdr), len); break; + case MGMT_OP_SET_IO_CAPABILITY: + err = set_io_capability(sk, buf + sizeof(*hdr), len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, opcode, 0x01); -- cgit v1.2.3 From bb58f747e519aba07a6f05a78d58cf8a0788e2d5 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 3 Feb 2011 20:50:35 -0200 Subject: Bluetooth: Initial work for L2CAP split. This patch tries to do the minimal to move l2cap_sock_create() and its dependencies to l2cap_sock.c. It create a API to initialize and cleanup the L2CAP sockets from l2cap_core.c through l2cap_init_sockets() and l2cap_cleanup_sockets(). Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 15 +++ net/bluetooth/Makefile | 2 +- net/bluetooth/l2cap_core.c | 187 +++--------------------------------- net/bluetooth/l2cap_sock.c | 213 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 240 insertions(+), 177 deletions(-) create mode 100644 net/bluetooth/l2cap_sock.c (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 7f88a87d7a46..fce5274a4f7b 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -424,6 +424,21 @@ static inline int l2cap_tx_window_full(struct sock *sk) #define __is_sframe(ctrl) ((ctrl) & L2CAP_CTRL_FRAME_TYPE) #define __is_sar_start(ctrl) (((ctrl) & L2CAP_CTRL_SAR) == L2CAP_SDU_START) +extern int disable_ertm; +extern const struct proto_ops l2cap_sock_ops; +extern struct bt_sock_list l2cap_sk_list; + +int l2cap_init_sockets(void); +void l2cap_cleanup_sockets(void); + +void l2cap_sock_set_timer(struct sock *sk, long timeout); +void __l2cap_sock_close(struct sock *sk, int reason); +void l2cap_sock_kill(struct sock *sk); +void l2cap_sock_init(struct sock *sk, struct sock *parent); +struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, + int proto, gfp_t prio); + + void l2cap_load(void); #endif /* __L2CAP_H */ diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index bf2945e1d9ef..339b42932b33 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -11,4 +11,4 @@ obj-$(CONFIG_BT_CMTP) += cmtp/ obj-$(CONFIG_BT_HIDP) += hidp/ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o hci_sock.o hci_sysfs.o lib.o -l2cap-y := l2cap_core.o +l2cap-y := l2cap_core.o l2cap_sock.o diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 28d2954f94a4..af678efec15f 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -24,7 +24,7 @@ SOFTWARE IS DISCLAIMED. */ -/* Bluetooth L2CAP core and sockets. */ +/* Bluetooth L2CAP core. */ #include @@ -57,24 +57,20 @@ #define VERSION "2.15" -static int disable_ertm; +int disable_ertm; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; static u8 l2cap_fixed_chan[8] = { 0x02, }; -static const struct proto_ops l2cap_sock_ops; - static struct workqueue_struct *_busy_wq; -static struct bt_sock_list l2cap_sk_list = { +struct bt_sock_list l2cap_sk_list = { .lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock) }; static void l2cap_busy_work(struct work_struct *work); -static void __l2cap_sock_close(struct sock *sk, int reason); static void l2cap_sock_close(struct sock *sk); -static void l2cap_sock_kill(struct sock *sk); static int l2cap_build_conf_req(struct sock *sk, void *data); static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, @@ -83,7 +79,7 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb); /* ---- L2CAP timers ---- */ -static void l2cap_sock_set_timer(struct sock *sk, long timeout) +void l2cap_sock_set_timer(struct sock *sk, long timeout) { BT_DBG("sk %p state %d timeout %ld", sk, sk->sk_state, timeout); sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout); @@ -95,39 +91,6 @@ static void l2cap_sock_clear_timer(struct sock *sk) sk_stop_timer(sk, &sk->sk_timer); } -static void l2cap_sock_timeout(unsigned long arg) -{ - struct sock *sk = (struct sock *) arg; - int reason; - - BT_DBG("sock %p state %d", sk, sk->sk_state); - - bh_lock_sock(sk); - - if (sock_owned_by_user(sk)) { - /* sk is owned by user. Try again later */ - l2cap_sock_set_timer(sk, HZ / 5); - bh_unlock_sock(sk); - sock_put(sk); - return; - } - - if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG) - reason = ECONNREFUSED; - else if (sk->sk_state == BT_CONNECT && - l2cap_pi(sk)->sec_level != BT_SECURITY_SDP) - reason = ECONNREFUSED; - else - reason = ETIMEDOUT; - - __l2cap_sock_close(sk, reason); - - bh_unlock_sock(sk); - - l2cap_sock_kill(sk); - sock_put(sk); -} - /* ---- L2CAP channels ---- */ static struct sock *__l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, u16 cid) { @@ -801,14 +764,6 @@ static struct sock *l2cap_get_sock_by_psm(int state, __le16 psm, bdaddr_t *src) return node ? sk : sk1; } -static void l2cap_sock_destruct(struct sock *sk) -{ - BT_DBG("sk %p", sk); - - skb_queue_purge(&sk->sk_receive_queue); - skb_queue_purge(&sk->sk_write_queue); -} - static void l2cap_sock_cleanup_listen(struct sock *parent) { struct sock *sk; @@ -826,7 +781,7 @@ static void l2cap_sock_cleanup_listen(struct sock *parent) /* Kill socket (only if zapped and orphan) * Must be called on unlocked socket. */ -static void l2cap_sock_kill(struct sock *sk) +void l2cap_sock_kill(struct sock *sk) { if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket) return; @@ -839,7 +794,7 @@ static void l2cap_sock_kill(struct sock *sk) sock_put(sk); } -static void __l2cap_sock_close(struct sock *sk, int reason) +void __l2cap_sock_close(struct sock *sk, int reason) { BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); @@ -904,111 +859,6 @@ static void l2cap_sock_close(struct sock *sk) l2cap_sock_kill(sk); } -static void l2cap_sock_init(struct sock *sk, struct sock *parent) -{ - struct l2cap_pinfo *pi = l2cap_pi(sk); - - BT_DBG("sk %p", sk); - - if (parent) { - sk->sk_type = parent->sk_type; - bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup; - - pi->imtu = l2cap_pi(parent)->imtu; - pi->omtu = l2cap_pi(parent)->omtu; - pi->conf_state = l2cap_pi(parent)->conf_state; - pi->mode = l2cap_pi(parent)->mode; - pi->fcs = l2cap_pi(parent)->fcs; - pi->max_tx = l2cap_pi(parent)->max_tx; - pi->tx_win = l2cap_pi(parent)->tx_win; - pi->sec_level = l2cap_pi(parent)->sec_level; - pi->role_switch = l2cap_pi(parent)->role_switch; - pi->force_reliable = l2cap_pi(parent)->force_reliable; - pi->flushable = l2cap_pi(parent)->flushable; - } else { - pi->imtu = L2CAP_DEFAULT_MTU; - pi->omtu = 0; - if (!disable_ertm && sk->sk_type == SOCK_STREAM) { - pi->mode = L2CAP_MODE_ERTM; - pi->conf_state |= L2CAP_CONF_STATE2_DEVICE; - } else { - pi->mode = L2CAP_MODE_BASIC; - } - pi->max_tx = L2CAP_DEFAULT_MAX_TX; - pi->fcs = L2CAP_FCS_CRC16; - pi->tx_win = L2CAP_DEFAULT_TX_WINDOW; - pi->sec_level = BT_SECURITY_LOW; - pi->role_switch = 0; - pi->force_reliable = 0; - pi->flushable = BT_FLUSHABLE_OFF; - } - - /* Default config options */ - pi->conf_len = 0; - pi->flush_to = L2CAP_DEFAULT_FLUSH_TO; - skb_queue_head_init(TX_QUEUE(sk)); - skb_queue_head_init(SREJ_QUEUE(sk)); - skb_queue_head_init(BUSY_QUEUE(sk)); - INIT_LIST_HEAD(SREJ_LIST(sk)); -} - -static struct proto l2cap_proto = { - .name = "L2CAP", - .owner = THIS_MODULE, - .obj_size = sizeof(struct l2cap_pinfo) -}; - -static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) -{ - struct sock *sk; - - sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto); - if (!sk) - return NULL; - - sock_init_data(sock, sk); - INIT_LIST_HEAD(&bt_sk(sk)->accept_q); - - sk->sk_destruct = l2cap_sock_destruct; - sk->sk_sndtimeo = msecs_to_jiffies(L2CAP_CONN_TIMEOUT); - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = proto; - sk->sk_state = BT_OPEN; - - setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk); - - bt_sock_link(&l2cap_sk_list, sk); - return sk; -} - -static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - sock->state = SS_UNCONNECTED; - - if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM && - sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - - if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) - return -EPERM; - - sock->ops = &l2cap_sock_ops; - - sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC); - if (!sk) - return -ENOMEM; - - l2cap_sock_init(sk, NULL); - return 0; -} - static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) { struct sock *sk = sock->sk; @@ -4865,7 +4715,7 @@ static const struct file_operations l2cap_debugfs_fops = { static struct dentry *l2cap_debugfs; -static const struct proto_ops l2cap_sock_ops = { +const struct proto_ops l2cap_sock_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .release = l2cap_sock_release, @@ -4885,12 +4735,6 @@ static const struct proto_ops l2cap_sock_ops = { .getsockopt = l2cap_sock_getsockopt }; -static const struct net_proto_family l2cap_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = l2cap_sock_create, -}; - static struct hci_proto l2cap_hci_proto = { .name = "L2CAP", .id = HCI_PROTO_L2CAP, @@ -4906,19 +4750,13 @@ static int __init l2cap_init(void) { int err; - err = proto_register(&l2cap_proto, 0); + err = l2cap_init_sockets(); if (err < 0) return err; _busy_wq = create_singlethread_workqueue("l2cap"); if (!_busy_wq) { - proto_unregister(&l2cap_proto); - return -ENOMEM; - } - - err = bt_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops); - if (err < 0) { - BT_ERR("L2CAP socket registration failed"); + err = -ENOMEM; goto error; } @@ -4943,7 +4781,7 @@ static int __init l2cap_init(void) error: destroy_workqueue(_busy_wq); - proto_unregister(&l2cap_proto); + l2cap_cleanup_sockets(); return err; } @@ -4954,13 +4792,10 @@ static void __exit l2cap_exit(void) flush_workqueue(_busy_wq); destroy_workqueue(_busy_wq); - if (bt_sock_unregister(BTPROTO_L2CAP) < 0) - BT_ERR("L2CAP socket unregistration failed"); - if (hci_unregister_proto(&l2cap_hci_proto) < 0) BT_ERR("L2CAP protocol unregistration failed"); - proto_unregister(&l2cap_proto); + l2cap_cleanup_sockets(); } void l2cap_load(void) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c new file mode 100644 index 000000000000..6ea1894cecb7 --- /dev/null +++ b/net/bluetooth/l2cap_sock.c @@ -0,0 +1,213 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + Copyright (C) 2000-2001 Qualcomm Incorporated + Copyright (C) 2009-2010 Gustavo F. Padovan + Copyright (C) 2010 Google Inc. + + Written 2000,2001 by Maxim Krasnyansky + + 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 SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +/* Bluetooth L2CAP sockets. */ + +#include +#include + +static void l2cap_sock_timeout(unsigned long arg) +{ + struct sock *sk = (struct sock *) arg; + int reason; + + BT_DBG("sock %p state %d", sk, sk->sk_state); + + bh_lock_sock(sk); + + if (sock_owned_by_user(sk)) { + /* sk is owned by user. Try again later */ + l2cap_sock_set_timer(sk, HZ / 5); + bh_unlock_sock(sk); + sock_put(sk); + return; + } + + if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG) + reason = ECONNREFUSED; + else if (sk->sk_state == BT_CONNECT && + l2cap_pi(sk)->sec_level != BT_SECURITY_SDP) + reason = ECONNREFUSED; + else + reason = ETIMEDOUT; + + __l2cap_sock_close(sk, reason); + + bh_unlock_sock(sk); + + l2cap_sock_kill(sk); + sock_put(sk); +} + + +static void l2cap_sock_destruct(struct sock *sk) +{ + BT_DBG("sk %p", sk); + + skb_queue_purge(&sk->sk_receive_queue); + skb_queue_purge(&sk->sk_write_queue); +} + +void l2cap_sock_init(struct sock *sk, struct sock *parent) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + + BT_DBG("sk %p", sk); + + if (parent) { + sk->sk_type = parent->sk_type; + bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup; + + pi->imtu = l2cap_pi(parent)->imtu; + pi->omtu = l2cap_pi(parent)->omtu; + pi->conf_state = l2cap_pi(parent)->conf_state; + pi->mode = l2cap_pi(parent)->mode; + pi->fcs = l2cap_pi(parent)->fcs; + pi->max_tx = l2cap_pi(parent)->max_tx; + pi->tx_win = l2cap_pi(parent)->tx_win; + pi->sec_level = l2cap_pi(parent)->sec_level; + pi->role_switch = l2cap_pi(parent)->role_switch; + pi->force_reliable = l2cap_pi(parent)->force_reliable; + pi->flushable = l2cap_pi(parent)->flushable; + } else { + pi->imtu = L2CAP_DEFAULT_MTU; + pi->omtu = 0; + if (!disable_ertm && sk->sk_type == SOCK_STREAM) { + pi->mode = L2CAP_MODE_ERTM; + pi->conf_state |= L2CAP_CONF_STATE2_DEVICE; + } else { + pi->mode = L2CAP_MODE_BASIC; + } + pi->max_tx = L2CAP_DEFAULT_MAX_TX; + pi->fcs = L2CAP_FCS_CRC16; + pi->tx_win = L2CAP_DEFAULT_TX_WINDOW; + pi->sec_level = BT_SECURITY_LOW; + pi->role_switch = 0; + pi->force_reliable = 0; + pi->flushable = BT_FLUSHABLE_OFF; + } + + /* Default config options */ + pi->conf_len = 0; + pi->flush_to = L2CAP_DEFAULT_FLUSH_TO; + skb_queue_head_init(TX_QUEUE(sk)); + skb_queue_head_init(SREJ_QUEUE(sk)); + skb_queue_head_init(BUSY_QUEUE(sk)); + INIT_LIST_HEAD(SREJ_LIST(sk)); +} + +static struct proto l2cap_proto = { + .name = "L2CAP", + .owner = THIS_MODULE, + .obj_size = sizeof(struct l2cap_pinfo) +}; + +struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) +{ + struct sock *sk; + + sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto); + if (!sk) + return NULL; + + sock_init_data(sock, sk); + INIT_LIST_HEAD(&bt_sk(sk)->accept_q); + + sk->sk_destruct = l2cap_sock_destruct; + sk->sk_sndtimeo = msecs_to_jiffies(L2CAP_CONN_TIMEOUT); + + sock_reset_flag(sk, SOCK_ZAPPED); + + sk->sk_protocol = proto; + sk->sk_state = BT_OPEN; + + setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk); + + bt_sock_link(&l2cap_sk_list, sk); + return sk; +} + +static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, + int kern) +{ + struct sock *sk; + + BT_DBG("sock %p", sock); + + sock->state = SS_UNCONNECTED; + + if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM && + sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) + return -ESOCKTNOSUPPORT; + + if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) + return -EPERM; + + sock->ops = &l2cap_sock_ops; + + sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC); + if (!sk) + return -ENOMEM; + + l2cap_sock_init(sk, NULL); + return 0; +} + +static const struct net_proto_family l2cap_sock_family_ops = { + .family = PF_BLUETOOTH, + .owner = THIS_MODULE, + .create = l2cap_sock_create, +}; + +int __init l2cap_init_sockets(void) +{ + int err; + + err = proto_register(&l2cap_proto, 0); + if (err < 0) + return err; + + err = bt_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops); + if (err < 0) + goto error; + + BT_INFO("L2CAP socket layer initialized"); + + return 0; + +error: + BT_ERR("L2CAP socket registration failed"); + proto_unregister(&l2cap_proto); + return err; +} + +void l2cap_cleanup_sockets(void) +{ + if (bt_sock_unregister(BTPROTO_L2CAP) < 0) + BT_ERR("L2CAP socket unregistration failed"); + + proto_unregister(&l2cap_proto); +} -- cgit v1.2.3 From 65390587c7bcf8bb60b48387db766d8d7dfea982 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 4 Feb 2011 02:33:56 -0200 Subject: Bluetooth: move l2cap_sock_ops to l2cap_sock.c First step to move all l2cap_sock_ops function to l2cap_sock.c Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 12 ++++++++++++ net/bluetooth/l2cap_core.c | 42 +++++++++++------------------------------- net/bluetooth/l2cap_sock.c | 21 ++++++++++++++++++++- 3 files changed, 43 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index fce5274a4f7b..533bef5f6341 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -438,6 +438,18 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent); struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); +int l2cap_sock_release(struct socket *sock); +int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen); +int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags); +int l2cap_sock_listen(struct socket *sock, int backlog); +int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags); +int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer); +int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len); +int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags); +int l2cap_sock_shutdown(struct socket *sock, int how); +int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen); +int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen); + void l2cap_load(void); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index af678efec15f..74a3ea3625d6 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -859,7 +859,7 @@ static void l2cap_sock_close(struct sock *sk) l2cap_sock_kill(sk); } -static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) +int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) { struct sock *sk = sock->sk; struct sockaddr_l2 la; @@ -983,7 +983,7 @@ done: return err; } -static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) +int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) { struct sock *sk = sock->sk; struct sockaddr_l2 la; @@ -1068,7 +1068,7 @@ done: return err; } -static int l2cap_sock_listen(struct socket *sock, int backlog) +int l2cap_sock_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; int err = 0; @@ -1127,7 +1127,7 @@ done: return err; } -static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) +int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) { DECLARE_WAITQUEUE(wait, current); struct sock *sk = sock->sk, *nsk; @@ -1183,7 +1183,7 @@ done: return err; } -static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) +int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) { struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; struct sock *sk = sock->sk; @@ -1665,7 +1665,7 @@ static inline int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, siz return size; } -static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) +int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct l2cap_pinfo *pi = l2cap_pi(sk); @@ -1767,7 +1767,7 @@ done: return err; } -static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) +int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) { struct sock *sk = sock->sk; @@ -1894,7 +1894,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us return err; } -static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) +int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) { struct sock *sk = sock->sk; struct bt_security sec; @@ -2067,7 +2067,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us return err; } -static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) +int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; struct bt_security sec; @@ -2128,7 +2128,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch return err; } -static int l2cap_sock_shutdown(struct socket *sock, int how) +int l2cap_sock_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; int err = 0; @@ -2159,7 +2159,7 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) return err; } -static int l2cap_sock_release(struct socket *sock) +int l2cap_sock_release(struct socket *sock) { struct sock *sk = sock->sk; int err; @@ -4715,26 +4715,6 @@ static const struct file_operations l2cap_debugfs_fops = { static struct dentry *l2cap_debugfs; -const struct proto_ops l2cap_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = l2cap_sock_release, - .bind = l2cap_sock_bind, - .connect = l2cap_sock_connect, - .listen = l2cap_sock_listen, - .accept = l2cap_sock_accept, - .getname = l2cap_sock_getname, - .sendmsg = l2cap_sock_sendmsg, - .recvmsg = l2cap_sock_recvmsg, - .poll = bt_sock_poll, - .ioctl = bt_sock_ioctl, - .mmap = sock_no_mmap, - .socketpair = sock_no_socketpair, - .shutdown = l2cap_sock_shutdown, - .setsockopt = l2cap_sock_setsockopt, - .getsockopt = l2cap_sock_getsockopt -}; - static struct hci_proto l2cap_hci_proto = { .name = "L2CAP", .id = HCI_PROTO_L2CAP, diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 6ea1894cecb7..c1455f72bf03 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -62,7 +62,6 @@ static void l2cap_sock_timeout(unsigned long arg) sock_put(sk); } - static void l2cap_sock_destruct(struct sock *sk) { BT_DBG("sk %p", sk); @@ -176,6 +175,26 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, return 0; } +const struct proto_ops l2cap_sock_ops = { + .family = PF_BLUETOOTH, + .owner = THIS_MODULE, + .release = l2cap_sock_release, + .bind = l2cap_sock_bind, + .connect = l2cap_sock_connect, + .listen = l2cap_sock_listen, + .accept = l2cap_sock_accept, + .getname = l2cap_sock_getname, + .sendmsg = l2cap_sock_sendmsg, + .recvmsg = l2cap_sock_recvmsg, + .poll = bt_sock_poll, + .ioctl = bt_sock_ioctl, + .mmap = sock_no_mmap, + .socketpair = sock_no_socketpair, + .shutdown = l2cap_sock_shutdown, + .setsockopt = l2cap_sock_setsockopt, + .getsockopt = l2cap_sock_getsockopt +}; + static const struct net_proto_family l2cap_sock_family_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, -- cgit v1.2.3 From 554f05bb8a0707dcc0ba4ea1dba1fb9970846ab5 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 4 Feb 2011 02:36:42 -0200 Subject: Bluetooth: move l2cap_sock_release() to l2cap_sock.c Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 1 - net/bluetooth/l2cap_core.c | 17 ----------------- net/bluetooth/l2cap_sock.c | 17 +++++++++++++++++ 3 files changed, 17 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 533bef5f6341..d0baf4163261 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -438,7 +438,6 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent); struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); -int l2cap_sock_release(struct socket *sock); int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen); int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags); int l2cap_sock_listen(struct socket *sock, int backlog); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 74a3ea3625d6..5765a82cf380 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2159,23 +2159,6 @@ int l2cap_sock_shutdown(struct socket *sock, int how) return err; } -int l2cap_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - int err; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (!sk) - return 0; - - err = l2cap_sock_shutdown(sock, 2); - - sock_orphan(sk); - l2cap_sock_kill(sk); - return err; -} - static void l2cap_chan_ready(struct sock *sk) { struct sock *parent = bt_sk(sk)->parent; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index c1455f72bf03..20efd240a786 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -62,6 +62,23 @@ static void l2cap_sock_timeout(unsigned long arg) sock_put(sk); } +static int l2cap_sock_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + int err; + + BT_DBG("sock %p, sk %p", sock, sk); + + if (!sk) + return 0; + + err = l2cap_sock_shutdown(sock, 2); + + sock_orphan(sk); + l2cap_sock_kill(sk); + return err; +} + static void l2cap_sock_destruct(struct sock *sk) { BT_DBG("sk %p", sk); -- cgit v1.2.3 From af6bcd8205ac06fa1de98b2b28303157fb9c3dfc Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 4 Feb 2011 02:40:28 -0200 Subject: Bluetooth: move l2cap_sock_bind()/listen() to l2cap_sock.c Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 2 - net/bluetooth/l2cap_core.c | 134 ----------------------------------------- net/bluetooth/l2cap_sock.c | 135 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 136 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index d0baf4163261..3ca4fe30d75e 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -438,9 +438,7 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent); struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); -int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen); int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags); -int l2cap_sock_listen(struct socket *sock, int backlog); int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags); int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer); int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 5765a82cf380..6af38722d5cb 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -722,17 +722,6 @@ static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, stru } /* ---- Socket interface ---- */ -static struct sock *__l2cap_get_sock_by_addr(__le16 psm, bdaddr_t *src) -{ - struct sock *sk; - struct hlist_node *node; - sk_for_each(sk, node, &l2cap_sk_list.head) - if (l2cap_pi(sk)->sport == psm && !bacmp(&bt_sk(sk)->src, src)) - goto found; - sk = NULL; -found: - return sk; -} /* Find socket with psm and source bdaddr. * Returns closest match. @@ -859,70 +848,6 @@ static void l2cap_sock_close(struct sock *sk) l2cap_sock_kill(sk); } -int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) -{ - struct sock *sk = sock->sk; - struct sockaddr_l2 la; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (!addr || addr->sa_family != AF_BLUETOOTH) - return -EINVAL; - - memset(&la, 0, sizeof(la)); - len = min_t(unsigned int, sizeof(la), alen); - memcpy(&la, addr, len); - - if (la.l2_cid) - return -EINVAL; - - lock_sock(sk); - - if (sk->sk_state != BT_OPEN) { - err = -EBADFD; - goto done; - } - - if (la.l2_psm) { - __u16 psm = __le16_to_cpu(la.l2_psm); - - /* PSM must be odd and lsb of upper byte must be 0 */ - if ((psm & 0x0101) != 0x0001) { - err = -EINVAL; - goto done; - } - - /* Restrict usage of well-known PSMs */ - if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) { - err = -EACCES; - goto done; - } - } - - write_lock_bh(&l2cap_sk_list.lock); - - if (la.l2_psm && __l2cap_get_sock_by_addr(la.l2_psm, &la.l2_bdaddr)) { - err = -EADDRINUSE; - } else { - /* Save source address */ - bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); - l2cap_pi(sk)->psm = la.l2_psm; - l2cap_pi(sk)->sport = la.l2_psm; - sk->sk_state = BT_BOUND; - - if (__le16_to_cpu(la.l2_psm) == 0x0001 || - __le16_to_cpu(la.l2_psm) == 0x0003) - l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; - } - - write_unlock_bh(&l2cap_sk_list.lock); - -done: - release_sock(sk); - return err; -} - static int l2cap_do_connect(struct sock *sk) { bdaddr_t *src = &bt_sk(sk)->src; @@ -1068,65 +993,6 @@ done: return err; } -int l2cap_sock_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sk %p backlog %d", sk, backlog); - - lock_sock(sk); - - if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM) - || sk->sk_state != BT_BOUND) { - err = -EBADFD; - goto done; - } - - switch (l2cap_pi(sk)->mode) { - case L2CAP_MODE_BASIC: - break; - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - if (!disable_ertm) - break; - /* fall through */ - default: - err = -ENOTSUPP; - goto done; - } - - if (!l2cap_pi(sk)->psm) { - bdaddr_t *src = &bt_sk(sk)->src; - u16 psm; - - err = -EINVAL; - - write_lock_bh(&l2cap_sk_list.lock); - - for (psm = 0x1001; psm < 0x1100; psm += 2) - if (!__l2cap_get_sock_by_addr(cpu_to_le16(psm), src)) { - l2cap_pi(sk)->psm = cpu_to_le16(psm); - l2cap_pi(sk)->sport = cpu_to_le16(psm); - err = 0; - break; - } - - write_unlock_bh(&l2cap_sk_list.lock); - - if (err < 0) - goto done; - } - - sk->sk_max_ack_backlog = backlog; - sk->sk_ack_backlog = 0; - sk->sk_state = BT_LISTEN; - -done: - release_sock(sk); - return err; -} - int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) { DECLARE_WAITQUEUE(wait, current); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 20efd240a786..ef9a60fda495 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -62,6 +62,141 @@ static void l2cap_sock_timeout(unsigned long arg) sock_put(sk); } +static struct sock *__l2cap_get_sock_by_addr(__le16 psm, bdaddr_t *src) +{ + struct sock *sk; + struct hlist_node *node; + sk_for_each(sk, node, &l2cap_sk_list.head) + if (l2cap_pi(sk)->sport == psm && !bacmp(&bt_sk(sk)->src, src)) + goto found; + sk = NULL; +found: + return sk; +} + +static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) +{ + struct sock *sk = sock->sk; + struct sockaddr_l2 la; + int len, err = 0; + + BT_DBG("sk %p", sk); + + if (!addr || addr->sa_family != AF_BLUETOOTH) + return -EINVAL; + + memset(&la, 0, sizeof(la)); + len = min_t(unsigned int, sizeof(la), alen); + memcpy(&la, addr, len); + + if (la.l2_cid) + return -EINVAL; + + lock_sock(sk); + + if (sk->sk_state != BT_OPEN) { + err = -EBADFD; + goto done; + } + + if (la.l2_psm) { + __u16 psm = __le16_to_cpu(la.l2_psm); + + /* PSM must be odd and lsb of upper byte must be 0 */ + if ((psm & 0x0101) != 0x0001) { + err = -EINVAL; + goto done; + } + + /* Restrict usage of well-known PSMs */ + if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) { + err = -EACCES; + goto done; + } + } + + write_lock_bh(&l2cap_sk_list.lock); + + if (la.l2_psm && __l2cap_get_sock_by_addr(la.l2_psm, &la.l2_bdaddr)) { + err = -EADDRINUSE; + } else { + /* Save source address */ + bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); + l2cap_pi(sk)->psm = la.l2_psm; + l2cap_pi(sk)->sport = la.l2_psm; + sk->sk_state = BT_BOUND; + + if (__le16_to_cpu(la.l2_psm) == 0x0001 || + __le16_to_cpu(la.l2_psm) == 0x0003) + l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; + } + + write_unlock_bh(&l2cap_sk_list.lock); + +done: + release_sock(sk); + return err; +} + +static int l2cap_sock_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + int err = 0; + + BT_DBG("sk %p backlog %d", sk, backlog); + + lock_sock(sk); + + if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM) + || sk->sk_state != BT_BOUND) { + err = -EBADFD; + goto done; + } + + switch (l2cap_pi(sk)->mode) { + case L2CAP_MODE_BASIC: + break; + case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: + if (!disable_ertm) + break; + /* fall through */ + default: + err = -ENOTSUPP; + goto done; + } + + if (!l2cap_pi(sk)->psm) { + bdaddr_t *src = &bt_sk(sk)->src; + u16 psm; + + err = -EINVAL; + + write_lock_bh(&l2cap_sk_list.lock); + + for (psm = 0x1001; psm < 0x1100; psm += 2) + if (!__l2cap_get_sock_by_addr(cpu_to_le16(psm), src)) { + l2cap_pi(sk)->psm = cpu_to_le16(psm); + l2cap_pi(sk)->sport = cpu_to_le16(psm); + err = 0; + break; + } + + write_unlock_bh(&l2cap_sk_list.lock); + + if (err < 0) + goto done; + } + + sk->sk_max_ack_backlog = backlog; + sk->sk_ack_backlog = 0; + sk->sk_state = BT_LISTEN; + +done: + release_sock(sk); + return err; +} + static int l2cap_sock_release(struct socket *sock) { struct sock *sk = sock->sk; -- cgit v1.2.3 From c47b7c724bc7106acf602b2ce99922a2d14ea62b Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 4 Feb 2011 02:42:23 -0200 Subject: Bluetooth: move l2cap_sock_accept() to l2cap_sock.c Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 1 - net/bluetooth/l2cap_core.c | 56 ------------------------------------------- net/bluetooth/l2cap_sock.c | 56 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 57 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 3ca4fe30d75e..7921b6b980cb 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -439,7 +439,6 @@ struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags); -int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags); int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer); int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len); int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 6af38722d5cb..ff6a54ffed89 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -993,62 +993,6 @@ done: return err; } -int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) -{ - DECLARE_WAITQUEUE(wait, current); - struct sock *sk = sock->sk, *nsk; - long timeo; - int err = 0; - - lock_sock_nested(sk, SINGLE_DEPTH_NESTING); - - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; - goto done; - } - - timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); - - BT_DBG("sk %p timeo %ld", sk, timeo); - - /* Wait for an incoming connection. (wake-one). */ - add_wait_queue_exclusive(sk_sleep(sk), &wait); - while (!(nsk = bt_accept_dequeue(sk, newsock))) { - set_current_state(TASK_INTERRUPTIBLE); - if (!timeo) { - err = -EAGAIN; - break; - } - - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock_nested(sk, SINGLE_DEPTH_NESTING); - - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; - break; - } - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - break; - } - } - set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - - if (err) - goto done; - - newsock->state = SS_CONNECTED; - - BT_DBG("new socket %p", nsk); - -done: - release_sock(sk); - return err; -} - int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) { struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index ef9a60fda495..b19a386332fc 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -197,6 +197,62 @@ done: return err; } +static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) +{ + DECLARE_WAITQUEUE(wait, current); + struct sock *sk = sock->sk, *nsk; + long timeo; + int err = 0; + + lock_sock_nested(sk, SINGLE_DEPTH_NESTING); + + if (sk->sk_state != BT_LISTEN) { + err = -EBADFD; + goto done; + } + + timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); + + BT_DBG("sk %p timeo %ld", sk, timeo); + + /* Wait for an incoming connection. (wake-one). */ + add_wait_queue_exclusive(sk_sleep(sk), &wait); + while (!(nsk = bt_accept_dequeue(sk, newsock))) { + set_current_state(TASK_INTERRUPTIBLE); + if (!timeo) { + err = -EAGAIN; + break; + } + + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock_nested(sk, SINGLE_DEPTH_NESTING); + + if (sk->sk_state != BT_LISTEN) { + err = -EBADFD; + break; + } + + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(sk_sleep(sk), &wait); + + if (err) + goto done; + + newsock->state = SS_CONNECTED; + + BT_DBG("new socket %p", nsk); + +done: + release_sock(sk); + return err; +} + static int l2cap_sock_release(struct socket *sock) { struct sock *sk = sock->sk; -- cgit v1.2.3 From d7175d55255cb0a576844bc6e986000e0d7f8e9d Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 4 Feb 2011 02:43:46 -0200 Subject: Bluetooth: move l2cap_sock_getname() to l2cap_sock.c Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 1 - net/bluetooth/l2cap_core.c | 23 ----------------------- net/bluetooth/l2cap_sock.c | 23 +++++++++++++++++++++++ 3 files changed, 23 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 7921b6b980cb..0d0c18014a54 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -439,7 +439,6 @@ struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags); -int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer); int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len); int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags); int l2cap_sock_shutdown(struct socket *sock, int how); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ff6a54ffed89..bd46cacc165a 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -993,29 +993,6 @@ done: return err; } -int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) -{ - struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; - struct sock *sk = sock->sk; - - BT_DBG("sock %p, sk %p", sock, sk); - - addr->sa_family = AF_BLUETOOTH; - *len = sizeof(struct sockaddr_l2); - - if (peer) { - la->l2_psm = l2cap_pi(sk)->psm; - bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); - la->l2_cid = cpu_to_le16(l2cap_pi(sk)->dcid); - } else { - la->l2_psm = l2cap_pi(sk)->sport; - bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); - la->l2_cid = cpu_to_le16(l2cap_pi(sk)->scid); - } - - return 0; -} - static int __l2cap_wait_ack(struct sock *sk) { DECLARE_WAITQUEUE(wait, current); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index b19a386332fc..4c13f8bc1b18 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -253,6 +253,29 @@ done: return err; } +static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) +{ + struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; + struct sock *sk = sock->sk; + + BT_DBG("sock %p, sk %p", sock, sk); + + addr->sa_family = AF_BLUETOOTH; + *len = sizeof(struct sockaddr_l2); + + if (peer) { + la->l2_psm = l2cap_pi(sk)->psm; + bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); + la->l2_cid = cpu_to_le16(l2cap_pi(sk)->dcid); + } else { + la->l2_psm = l2cap_pi(sk)->sport; + bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); + la->l2_cid = cpu_to_le16(l2cap_pi(sk)->scid); + } + + return 0; +} + static int l2cap_sock_release(struct socket *sock) { struct sock *sk = sock->sk; -- cgit v1.2.3 From 33575df7be6748292f88453f29319af6d639c5c8 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 4 Feb 2011 02:48:48 -0200 Subject: Bluetooth: move l2cap_sock_setsockopt() to l2cap_sock.c Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 1 - net/bluetooth/l2cap_core.c | 174 ----------------------------------------- net/bluetooth/l2cap_sock.c | 175 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 175 insertions(+), 175 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 0d0c18014a54..901ecbe573a3 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -442,7 +442,6 @@ int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len); int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags); int l2cap_sock_shutdown(struct socket *sock, int how); -int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen); int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bd46cacc165a..9d35cafe18aa 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1596,180 +1596,6 @@ int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m return bt_sock_recvmsg(iocb, sock, msg, len, flags); } -static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - struct l2cap_options opts; - int len, err = 0; - u32 opt; - - BT_DBG("sk %p", sk); - - lock_sock(sk); - - switch (optname) { - case L2CAP_OPTIONS: - if (sk->sk_state == BT_CONNECTED) { - err = -EINVAL; - break; - } - - opts.imtu = l2cap_pi(sk)->imtu; - opts.omtu = l2cap_pi(sk)->omtu; - opts.flush_to = l2cap_pi(sk)->flush_to; - opts.mode = l2cap_pi(sk)->mode; - opts.fcs = l2cap_pi(sk)->fcs; - opts.max_tx = l2cap_pi(sk)->max_tx; - opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win; - - len = min_t(unsigned int, sizeof(opts), optlen); - if (copy_from_user((char *) &opts, optval, len)) { - err = -EFAULT; - break; - } - - if (opts.txwin_size > L2CAP_DEFAULT_TX_WINDOW) { - err = -EINVAL; - break; - } - - l2cap_pi(sk)->mode = opts.mode; - switch (l2cap_pi(sk)->mode) { - case L2CAP_MODE_BASIC: - l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_STATE2_DEVICE; - break; - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - if (!disable_ertm) - break; - /* fall through */ - default: - err = -EINVAL; - break; - } - - l2cap_pi(sk)->imtu = opts.imtu; - l2cap_pi(sk)->omtu = opts.omtu; - l2cap_pi(sk)->fcs = opts.fcs; - l2cap_pi(sk)->max_tx = opts.max_tx; - l2cap_pi(sk)->tx_win = (__u8)opts.txwin_size; - break; - - case L2CAP_LM: - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - if (opt & L2CAP_LM_AUTH) - l2cap_pi(sk)->sec_level = BT_SECURITY_LOW; - if (opt & L2CAP_LM_ENCRYPT) - l2cap_pi(sk)->sec_level = BT_SECURITY_MEDIUM; - if (opt & L2CAP_LM_SECURE) - l2cap_pi(sk)->sec_level = BT_SECURITY_HIGH; - - l2cap_pi(sk)->role_switch = (opt & L2CAP_LM_MASTER); - l2cap_pi(sk)->force_reliable = (opt & L2CAP_LM_RELIABLE); - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - struct bt_security sec; - int len, err = 0; - u32 opt; - - BT_DBG("sk %p", sk); - - if (level == SOL_L2CAP) - return l2cap_sock_setsockopt_old(sock, optname, optval, optlen); - - if (level != SOL_BLUETOOTH) - return -ENOPROTOOPT; - - lock_sock(sk); - - switch (optname) { - case BT_SECURITY: - if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM - && sk->sk_type != SOCK_RAW) { - err = -EINVAL; - break; - } - - sec.level = BT_SECURITY_LOW; - - len = min_t(unsigned int, sizeof(sec), optlen); - if (copy_from_user((char *) &sec, optval, len)) { - err = -EFAULT; - break; - } - - if (sec.level < BT_SECURITY_LOW || - sec.level > BT_SECURITY_HIGH) { - err = -EINVAL; - break; - } - - l2cap_pi(sk)->sec_level = sec.level; - break; - - case BT_DEFER_SETUP: - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { - err = -EINVAL; - break; - } - - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - bt_sk(sk)->defer_setup = opt; - break; - - case BT_FLUSHABLE: - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - if (opt > BT_FLUSHABLE_ON) { - err = -EINVAL; - break; - } - - if (opt == BT_FLUSHABLE_OFF) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - /* proceed futher only when we have l2cap_conn and - No Flush support in the LM */ - if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) { - err = -EINVAL; - break; - } - } - - l2cap_pi(sk)->flushable = opt; - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 4c13f8bc1b18..1bbe8a06189b 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -27,6 +27,7 @@ /* Bluetooth L2CAP sockets. */ #include +#include #include static void l2cap_sock_timeout(unsigned long arg) @@ -276,6 +277,180 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l return 0; } +static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen) +{ + struct sock *sk = sock->sk; + struct l2cap_options opts; + int len, err = 0; + u32 opt; + + BT_DBG("sk %p", sk); + + lock_sock(sk); + + switch (optname) { + case L2CAP_OPTIONS: + if (sk->sk_state == BT_CONNECTED) { + err = -EINVAL; + break; + } + + opts.imtu = l2cap_pi(sk)->imtu; + opts.omtu = l2cap_pi(sk)->omtu; + opts.flush_to = l2cap_pi(sk)->flush_to; + opts.mode = l2cap_pi(sk)->mode; + opts.fcs = l2cap_pi(sk)->fcs; + opts.max_tx = l2cap_pi(sk)->max_tx; + opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win; + + len = min_t(unsigned int, sizeof(opts), optlen); + if (copy_from_user((char *) &opts, optval, len)) { + err = -EFAULT; + break; + } + + if (opts.txwin_size > L2CAP_DEFAULT_TX_WINDOW) { + err = -EINVAL; + break; + } + + l2cap_pi(sk)->mode = opts.mode; + switch (l2cap_pi(sk)->mode) { + case L2CAP_MODE_BASIC: + l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_STATE2_DEVICE; + break; + case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: + if (!disable_ertm) + break; + /* fall through */ + default: + err = -EINVAL; + break; + } + + l2cap_pi(sk)->imtu = opts.imtu; + l2cap_pi(sk)->omtu = opts.omtu; + l2cap_pi(sk)->fcs = opts.fcs; + l2cap_pi(sk)->max_tx = opts.max_tx; + l2cap_pi(sk)->tx_win = (__u8)opts.txwin_size; + break; + + case L2CAP_LM: + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + + if (opt & L2CAP_LM_AUTH) + l2cap_pi(sk)->sec_level = BT_SECURITY_LOW; + if (opt & L2CAP_LM_ENCRYPT) + l2cap_pi(sk)->sec_level = BT_SECURITY_MEDIUM; + if (opt & L2CAP_LM_SECURE) + l2cap_pi(sk)->sec_level = BT_SECURITY_HIGH; + + l2cap_pi(sk)->role_switch = (opt & L2CAP_LM_MASTER); + l2cap_pi(sk)->force_reliable = (opt & L2CAP_LM_RELIABLE); + break; + + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + +static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) +{ + struct sock *sk = sock->sk; + struct bt_security sec; + int len, err = 0; + u32 opt; + + BT_DBG("sk %p", sk); + + if (level == SOL_L2CAP) + return l2cap_sock_setsockopt_old(sock, optname, optval, optlen); + + if (level != SOL_BLUETOOTH) + return -ENOPROTOOPT; + + lock_sock(sk); + + switch (optname) { + case BT_SECURITY: + if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM + && sk->sk_type != SOCK_RAW) { + err = -EINVAL; + break; + } + + sec.level = BT_SECURITY_LOW; + + len = min_t(unsigned int, sizeof(sec), optlen); + if (copy_from_user((char *) &sec, optval, len)) { + err = -EFAULT; + break; + } + + if (sec.level < BT_SECURITY_LOW || + sec.level > BT_SECURITY_HIGH) { + err = -EINVAL; + break; + } + + l2cap_pi(sk)->sec_level = sec.level; + break; + + case BT_DEFER_SETUP: + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + err = -EINVAL; + break; + } + + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + + bt_sk(sk)->defer_setup = opt; + break; + + case BT_FLUSHABLE: + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + + if (opt > BT_FLUSHABLE_ON) { + err = -EINVAL; + break; + } + + if (opt == BT_FLUSHABLE_OFF) { + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + /* proceed futher only when we have l2cap_conn and + No Flush support in the LM */ + if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) { + err = -EINVAL; + break; + } + } + + l2cap_pi(sk)->flushable = opt; + break; + + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + static int l2cap_sock_release(struct socket *sock) { struct sock *sk = sock->sk; -- cgit v1.2.3 From 99f4808db0c052f3c92a689ec2841618bf2ce14a Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 4 Feb 2011 02:52:55 -0200 Subject: Bluetooth: move l2cap_sock_getsockopt() to l2cap_sock.c Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 1 - net/bluetooth/l2cap_core.c | 145 ------------------------------------------ net/bluetooth/l2cap_sock.c | 145 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 146 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 901ecbe573a3..1905aad4ba0a 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -442,7 +442,6 @@ int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len); int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags); int l2cap_sock_shutdown(struct socket *sock, int how); -int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen); void l2cap_load(void); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 9d35cafe18aa..8e015d971267 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1596,151 +1596,6 @@ int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m return bt_sock_recvmsg(iocb, sock, msg, len, flags); } -static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct l2cap_options opts; - struct l2cap_conninfo cinfo; - int len, err = 0; - u32 opt; - - BT_DBG("sk %p", sk); - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - case L2CAP_OPTIONS: - opts.imtu = l2cap_pi(sk)->imtu; - opts.omtu = l2cap_pi(sk)->omtu; - opts.flush_to = l2cap_pi(sk)->flush_to; - opts.mode = l2cap_pi(sk)->mode; - opts.fcs = l2cap_pi(sk)->fcs; - opts.max_tx = l2cap_pi(sk)->max_tx; - opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win; - - len = min_t(unsigned int, len, sizeof(opts)); - if (copy_to_user(optval, (char *) &opts, len)) - err = -EFAULT; - - break; - - case L2CAP_LM: - switch (l2cap_pi(sk)->sec_level) { - case BT_SECURITY_LOW: - opt = L2CAP_LM_AUTH; - break; - case BT_SECURITY_MEDIUM: - opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT; - break; - case BT_SECURITY_HIGH: - opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | - L2CAP_LM_SECURE; - break; - default: - opt = 0; - break; - } - - if (l2cap_pi(sk)->role_switch) - opt |= L2CAP_LM_MASTER; - - if (l2cap_pi(sk)->force_reliable) - opt |= L2CAP_LM_RELIABLE; - - if (put_user(opt, (u32 __user *) optval)) - err = -EFAULT; - break; - - case L2CAP_CONNINFO: - if (sk->sk_state != BT_CONNECTED && - !(sk->sk_state == BT_CONNECT2 && - bt_sk(sk)->defer_setup)) { - err = -ENOTCONN; - break; - } - - cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle; - memcpy(cinfo.dev_class, l2cap_pi(sk)->conn->hcon->dev_class, 3); - - len = min_t(unsigned int, len, sizeof(cinfo)); - if (copy_to_user(optval, (char *) &cinfo, len)) - err = -EFAULT; - - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct bt_security sec; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (level == SOL_L2CAP) - return l2cap_sock_getsockopt_old(sock, optname, optval, optlen); - - if (level != SOL_BLUETOOTH) - return -ENOPROTOOPT; - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - case BT_SECURITY: - if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM - && sk->sk_type != SOCK_RAW) { - err = -EINVAL; - break; - } - - sec.level = l2cap_pi(sk)->sec_level; - - len = min_t(unsigned int, len, sizeof(sec)); - if (copy_to_user(optval, (char *) &sec, len)) - err = -EFAULT; - - break; - - case BT_DEFER_SETUP: - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { - err = -EINVAL; - break; - } - - if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval)) - err = -EFAULT; - - break; - - case BT_FLUSHABLE: - if (put_user(l2cap_pi(sk)->flushable, (u32 __user *) optval)) - err = -EFAULT; - - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - int l2cap_sock_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 1bbe8a06189b..b7d5ae9c6bdf 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -277,6 +277,151 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l return 0; } +static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) +{ + struct sock *sk = sock->sk; + struct l2cap_options opts; + struct l2cap_conninfo cinfo; + int len, err = 0; + u32 opt; + + BT_DBG("sk %p", sk); + + if (get_user(len, optlen)) + return -EFAULT; + + lock_sock(sk); + + switch (optname) { + case L2CAP_OPTIONS: + opts.imtu = l2cap_pi(sk)->imtu; + opts.omtu = l2cap_pi(sk)->omtu; + opts.flush_to = l2cap_pi(sk)->flush_to; + opts.mode = l2cap_pi(sk)->mode; + opts.fcs = l2cap_pi(sk)->fcs; + opts.max_tx = l2cap_pi(sk)->max_tx; + opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win; + + len = min_t(unsigned int, len, sizeof(opts)); + if (copy_to_user(optval, (char *) &opts, len)) + err = -EFAULT; + + break; + + case L2CAP_LM: + switch (l2cap_pi(sk)->sec_level) { + case BT_SECURITY_LOW: + opt = L2CAP_LM_AUTH; + break; + case BT_SECURITY_MEDIUM: + opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT; + break; + case BT_SECURITY_HIGH: + opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | + L2CAP_LM_SECURE; + break; + default: + opt = 0; + break; + } + + if (l2cap_pi(sk)->role_switch) + opt |= L2CAP_LM_MASTER; + + if (l2cap_pi(sk)->force_reliable) + opt |= L2CAP_LM_RELIABLE; + + if (put_user(opt, (u32 __user *) optval)) + err = -EFAULT; + break; + + case L2CAP_CONNINFO: + if (sk->sk_state != BT_CONNECTED && + !(sk->sk_state == BT_CONNECT2 && + bt_sk(sk)->defer_setup)) { + err = -ENOTCONN; + break; + } + + cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle; + memcpy(cinfo.dev_class, l2cap_pi(sk)->conn->hcon->dev_class, 3); + + len = min_t(unsigned int, len, sizeof(cinfo)); + if (copy_to_user(optval, (char *) &cinfo, len)) + err = -EFAULT; + + break; + + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + +static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) +{ + struct sock *sk = sock->sk; + struct bt_security sec; + int len, err = 0; + + BT_DBG("sk %p", sk); + + if (level == SOL_L2CAP) + return l2cap_sock_getsockopt_old(sock, optname, optval, optlen); + + if (level != SOL_BLUETOOTH) + return -ENOPROTOOPT; + + if (get_user(len, optlen)) + return -EFAULT; + + lock_sock(sk); + + switch (optname) { + case BT_SECURITY: + if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM + && sk->sk_type != SOCK_RAW) { + err = -EINVAL; + break; + } + + sec.level = l2cap_pi(sk)->sec_level; + + len = min_t(unsigned int, len, sizeof(sec)); + if (copy_to_user(optval, (char *) &sec, len)) + err = -EFAULT; + + break; + + case BT_DEFER_SETUP: + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + err = -EINVAL; + break; + } + + if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval)) + err = -EFAULT; + + break; + + case BT_FLUSHABLE: + if (put_user(l2cap_pi(sk)->flushable, (u32 __user *) optval)) + err = -EFAULT; + + break; + + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen) { struct sock *sk = sock->sk; -- cgit v1.2.3 From 4e34c50bfe5ba87da1622cc7c6ed10712da255ad Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 4 Feb 2011 02:56:13 -0200 Subject: Bluetooth: move l2cap_sock_connect() to l2cap_sock.c Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 2 +- net/bluetooth/l2cap_core.c | 87 +------------------------------------------ net/bluetooth/l2cap_sock.c | 85 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 87 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 1905aad4ba0a..b5ebf878ca22 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -437,8 +437,8 @@ void l2cap_sock_kill(struct sock *sk); void l2cap_sock_init(struct sock *sk, struct sock *parent); struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); +int l2cap_do_connect(struct sock *sk); -int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags); int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len); int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags); int l2cap_sock_shutdown(struct socket *sock, int how); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8e015d971267..97327457b52d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -848,7 +848,7 @@ static void l2cap_sock_close(struct sock *sk) l2cap_sock_kill(sk); } -static int l2cap_do_connect(struct sock *sk) +int l2cap_do_connect(struct sock *sk) { bdaddr_t *src = &bt_sk(sk)->src; bdaddr_t *dst = &bt_sk(sk)->dst; @@ -908,91 +908,6 @@ done: return err; } -int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) -{ - struct sock *sk = sock->sk; - struct sockaddr_l2 la; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (!addr || alen < sizeof(addr->sa_family) || - addr->sa_family != AF_BLUETOOTH) - return -EINVAL; - - memset(&la, 0, sizeof(la)); - len = min_t(unsigned int, sizeof(la), alen); - memcpy(&la, addr, len); - - if (la.l2_cid) - return -EINVAL; - - lock_sock(sk); - - if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) - && !la.l2_psm) { - err = -EINVAL; - goto done; - } - - switch (l2cap_pi(sk)->mode) { - case L2CAP_MODE_BASIC: - break; - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - if (!disable_ertm) - break; - /* fall through */ - default: - err = -ENOTSUPP; - goto done; - } - - switch (sk->sk_state) { - case BT_CONNECT: - case BT_CONNECT2: - case BT_CONFIG: - /* Already connecting */ - goto wait; - - case BT_CONNECTED: - /* Already connected */ - err = -EISCONN; - goto done; - - case BT_OPEN: - case BT_BOUND: - /* Can connect */ - break; - - default: - err = -EBADFD; - goto done; - } - - /* PSM must be odd and lsb of upper byte must be 0 */ - if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 && - sk->sk_type != SOCK_RAW) { - err = -EINVAL; - goto done; - } - - /* Set destination address and psm */ - bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr); - l2cap_pi(sk)->psm = la.l2_psm; - - err = l2cap_do_connect(sk); - if (err) - goto done; - -wait: - err = bt_sock_wait_state(sk, BT_CONNECTED, - sock_sndtimeo(sk, flags & O_NONBLOCK)); -done: - release_sock(sk); - return err; -} - static int __l2cap_wait_ack(struct sock *sk) { DECLARE_WAITQUEUE(wait, current); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index b7d5ae9c6bdf..e2f14f1783f6 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -139,6 +139,91 @@ done: return err; } +static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) +{ + struct sock *sk = sock->sk; + struct sockaddr_l2 la; + int len, err = 0; + + BT_DBG("sk %p", sk); + + if (!addr || alen < sizeof(addr->sa_family) || + addr->sa_family != AF_BLUETOOTH) + return -EINVAL; + + memset(&la, 0, sizeof(la)); + len = min_t(unsigned int, sizeof(la), alen); + memcpy(&la, addr, len); + + if (la.l2_cid) + return -EINVAL; + + lock_sock(sk); + + if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) + && !la.l2_psm) { + err = -EINVAL; + goto done; + } + + switch (l2cap_pi(sk)->mode) { + case L2CAP_MODE_BASIC: + break; + case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: + if (!disable_ertm) + break; + /* fall through */ + default: + err = -ENOTSUPP; + goto done; + } + + switch (sk->sk_state) { + case BT_CONNECT: + case BT_CONNECT2: + case BT_CONFIG: + /* Already connecting */ + goto wait; + + case BT_CONNECTED: + /* Already connected */ + err = -EISCONN; + goto done; + + case BT_OPEN: + case BT_BOUND: + /* Can connect */ + break; + + default: + err = -EBADFD; + goto done; + } + + /* PSM must be odd and lsb of upper byte must be 0 */ + if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 && + sk->sk_type != SOCK_RAW) { + err = -EINVAL; + goto done; + } + + /* Set destination address and psm */ + bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr); + l2cap_pi(sk)->psm = la.l2_psm; + + err = l2cap_do_connect(sk); + if (err) + goto done; + +wait: + err = bt_sock_wait_state(sk, BT_CONNECTED, + sock_sndtimeo(sk, flags & O_NONBLOCK)); +done: + release_sock(sk); + return err; +} + static int l2cap_sock_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; -- cgit v1.2.3 From 6898325923f9571fbede3372dc490faa43b3258a Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 4 Feb 2011 03:02:31 -0200 Subject: Bluetooth: move l2cap_sock_recvmsg() to l2cap_sock.c It causes the move of the declaration of 3 functions to l2cap.h: l2cap_get_ident(), l2cap_send_cmd(), l2cap_build_conf_req() Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 5 ++++- net/bluetooth/l2cap_core.c | 49 +++---------------------------------------- net/bluetooth/l2cap_sock.c | 42 +++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index b5ebf878ca22..336b2af758b3 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -431,6 +431,10 @@ extern struct bt_sock_list l2cap_sk_list; int l2cap_init_sockets(void); void l2cap_cleanup_sockets(void); +u8 l2cap_get_ident(struct l2cap_conn *conn); +void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data); +int l2cap_build_conf_req(struct sock *sk, void *data); + void l2cap_sock_set_timer(struct sock *sk, long timeout); void __l2cap_sock_close(struct sock *sk, int reason); void l2cap_sock_kill(struct sock *sk); @@ -440,7 +444,6 @@ struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int l2cap_do_connect(struct sock *sk); int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len); -int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags); int l2cap_sock_shutdown(struct socket *sock, int how); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 97327457b52d..3a0e42be89ea 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -72,7 +72,6 @@ static void l2cap_busy_work(struct work_struct *work); static void l2cap_sock_close(struct sock *sk); -static int l2cap_build_conf_req(struct sock *sk, void *data); static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, u8 ident, u16 dlen, void *data); @@ -311,7 +310,7 @@ static inline int l2cap_check_security(struct sock *sk) auth_type); } -static inline u8 l2cap_get_ident(struct l2cap_conn *conn) +u8 l2cap_get_ident(struct l2cap_conn *conn) { u8 id; @@ -333,7 +332,7 @@ static inline u8 l2cap_get_ident(struct l2cap_conn *conn) return id; } -static inline void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) +void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) { struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data); u8 flags; @@ -1469,48 +1468,6 @@ done: return err; } -int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) -{ - struct sock *sk = sock->sk; - - lock_sock(sk); - - if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) { - struct l2cap_conn_rsp rsp; - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - u8 buf[128]; - - sk->sk_state = BT_CONFIG; - - rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); - rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); - rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(l2cap_pi(sk)->conn, l2cap_pi(sk)->ident, - L2CAP_CONN_RSP, sizeof(rsp), &rsp); - - if (l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT) { - release_sock(sk); - return 0; - } - - l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; - l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(sk, buf), buf); - l2cap_pi(sk)->num_conf_req++; - - release_sock(sk); - return 0; - } - - release_sock(sk); - - if (sock->type == SOCK_STREAM) - return bt_sock_stream_recvmsg(iocb, sock, msg, len, flags); - - return bt_sock_recvmsg(iocb, sock, msg, len, flags); -} - int l2cap_sock_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; @@ -1760,7 +1717,7 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) } } -static int l2cap_build_conf_req(struct sock *sk, void *data) +int l2cap_build_conf_req(struct sock *sk, void *data) { struct l2cap_pinfo *pi = l2cap_pi(sk); struct l2cap_conf_req *req = data; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index e2f14f1783f6..fa2bc5d85560 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -681,6 +681,48 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch return err; } +static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) +{ + struct sock *sk = sock->sk; + + lock_sock(sk); + + if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) { + struct l2cap_conn_rsp rsp; + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + u8 buf[128]; + + sk->sk_state = BT_CONFIG; + + rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); + rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); + rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + l2cap_send_cmd(l2cap_pi(sk)->conn, l2cap_pi(sk)->ident, + L2CAP_CONN_RSP, sizeof(rsp), &rsp); + + if (l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT) { + release_sock(sk); + return 0; + } + + l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; + l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, + l2cap_build_conf_req(sk, buf), buf); + l2cap_pi(sk)->num_conf_req++; + + release_sock(sk); + return 0; + } + + release_sock(sk); + + if (sock->type == SOCK_STREAM) + return bt_sock_stream_recvmsg(iocb, sock, msg, len, flags); + + return bt_sock_recvmsg(iocb, sock, msg, len, flags); +} + static int l2cap_sock_release(struct socket *sock) { struct sock *sk = sock->sk; -- cgit v1.2.3 From dcba0dba54b566a08376f93cab35cdabd6abda20 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 4 Feb 2011 03:08:36 -0200 Subject: Bluetooth: move l2cap_sock_shutdown() to l2cap_sock.c Declare __l2cap_wait_ack() and l2cap_sock_clear_timer() in l2cap.h Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 3 ++- net/bluetooth/l2cap_core.c | 35 ++--------------------------------- net/bluetooth/l2cap_sock.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 336b2af758b3..c9df0ef5b6f5 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -434,8 +434,10 @@ void l2cap_cleanup_sockets(void); u8 l2cap_get_ident(struct l2cap_conn *conn); void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data); int l2cap_build_conf_req(struct sock *sk, void *data); +int __l2cap_wait_ack(struct sock *sk); void l2cap_sock_set_timer(struct sock *sk, long timeout); +void l2cap_sock_clear_timer(struct sock *sk); void __l2cap_sock_close(struct sock *sk, int reason); void l2cap_sock_kill(struct sock *sk); void l2cap_sock_init(struct sock *sk, struct sock *parent); @@ -444,7 +446,6 @@ struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int l2cap_do_connect(struct sock *sk); int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len); -int l2cap_sock_shutdown(struct socket *sock, int how); void l2cap_load(void); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 3a0e42be89ea..6e48e580555e 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -84,7 +84,7 @@ void l2cap_sock_set_timer(struct sock *sk, long timeout) sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout); } -static void l2cap_sock_clear_timer(struct sock *sk) +void l2cap_sock_clear_timer(struct sock *sk) { BT_DBG("sock %p state %d", sk, sk->sk_state); sk_stop_timer(sk, &sk->sk_timer); @@ -907,7 +907,7 @@ done: return err; } -static int __l2cap_wait_ack(struct sock *sk) +int __l2cap_wait_ack(struct sock *sk) { DECLARE_WAITQUEUE(wait, current); int err = 0; @@ -1468,37 +1468,6 @@ done: return err; } -int l2cap_sock_shutdown(struct socket *sock, int how) -{ - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (!sk) - return 0; - - lock_sock(sk); - if (!sk->sk_shutdown) { - if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) - err = __l2cap_wait_ack(sk); - - sk->sk_shutdown = SHUTDOWN_MASK; - l2cap_sock_clear_timer(sk); - __l2cap_sock_close(sk, 0); - - if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) - err = bt_sock_wait_state(sk, BT_CLOSED, - sk->sk_lingertime); - } - - if (!err && sk->sk_err) - err = -sk->sk_err; - - release_sock(sk); - return err; -} - static void l2cap_chan_ready(struct sock *sk) { struct sock *parent = bt_sk(sk)->parent; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index fa2bc5d85560..93af233bb167 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -723,6 +723,37 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms return bt_sock_recvmsg(iocb, sock, msg, len, flags); } +static int l2cap_sock_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + int err = 0; + + BT_DBG("sock %p, sk %p", sock, sk); + + if (!sk) + return 0; + + lock_sock(sk); + if (!sk->sk_shutdown) { + if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) + err = __l2cap_wait_ack(sk); + + sk->sk_shutdown = SHUTDOWN_MASK; + l2cap_sock_clear_timer(sk); + __l2cap_sock_close(sk, 0); + + if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) + err = bt_sock_wait_state(sk, BT_CLOSED, + sk->sk_lingertime); + } + + if (!err && sk->sk_err) + err = -sk->sk_err; + + release_sock(sk); + return err; +} + static int l2cap_sock_release(struct socket *sock) { struct sock *sk = sock->sk; -- cgit v1.2.3 From fd83ccdb393e3190633e0240dd73faac8998164b Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 4 Feb 2011 03:20:52 -0200 Subject: Bluetooth: move l2cap_sock_sendmsg() to l2cap_sock.c Also moves some L2CAP sending functions declaration to l2cap.h Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 11 ++-- net/bluetooth/l2cap_core.c | 116 +++--------------------------------------- net/bluetooth/l2cap_sock.c | 102 +++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 112 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index c9df0ef5b6f5..d4c93eded727 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -436,6 +436,14 @@ void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *d int l2cap_build_conf_req(struct sock *sk, void *data); int __l2cap_wait_ack(struct sock *sk); +struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, size_t len); +struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *msg, size_t len); +struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, size_t len, u16 control, u16 sdulen); +int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len); +void l2cap_do_send(struct sock *sk, struct sk_buff *skb); +void l2cap_streaming_send(struct sock *sk); +int l2cap_ertm_send(struct sock *sk); + void l2cap_sock_set_timer(struct sock *sk, long timeout); void l2cap_sock_clear_timer(struct sock *sk); void __l2cap_sock_close(struct sock *sk, int reason); @@ -445,9 +453,6 @@ struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); int l2cap_do_connect(struct sock *sk); -int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len); - - void l2cap_load(void); #endif /* __L2CAP_H */ diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 6e48e580555e..da9b3a44b0f0 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -993,7 +993,7 @@ static void l2cap_drop_acked_frames(struct sock *sk) del_timer(&l2cap_pi(sk)->retrans_timer); } -static inline void l2cap_do_send(struct sock *sk, struct sk_buff *skb) +void l2cap_do_send(struct sock *sk, struct sk_buff *skb) { struct l2cap_pinfo *pi = l2cap_pi(sk); struct hci_conn *hcon = pi->conn->hcon; @@ -1009,7 +1009,7 @@ static inline void l2cap_do_send(struct sock *sk, struct sk_buff *skb) hci_send_acl(hcon, skb, flags); } -static void l2cap_streaming_send(struct sock *sk) +void l2cap_streaming_send(struct sock *sk) { struct sk_buff *skb; struct l2cap_pinfo *pi = l2cap_pi(sk); @@ -1078,7 +1078,7 @@ static void l2cap_retransmit_one_frame(struct sock *sk, u8 tx_seq) l2cap_do_send(sk, tx_skb); } -static int l2cap_ertm_send(struct sock *sk) +int l2cap_ertm_send(struct sock *sk) { struct sk_buff *skb, *tx_skb; struct l2cap_pinfo *pi = l2cap_pi(sk); @@ -1218,7 +1218,7 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in return sent; } -static struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, size_t len) +struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, size_t len) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct sk_buff *skb; @@ -1247,7 +1247,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr return skb; } -static struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *msg, size_t len) +struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *msg, size_t len) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct sk_buff *skb; @@ -1275,7 +1275,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *ms return skb; } -static struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, size_t len, u16 control, u16 sdulen) +struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, size_t len, u16 control, u16 sdulen) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct sk_buff *skb; @@ -1320,7 +1320,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *m return skb; } -static inline int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len) +int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len) { struct l2cap_pinfo *pi = l2cap_pi(sk); struct sk_buff *skb; @@ -1366,108 +1366,6 @@ static inline int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, siz return size; } -int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct l2cap_pinfo *pi = l2cap_pi(sk); - struct sk_buff *skb; - u16 control; - int err; - - BT_DBG("sock %p, sk %p", sock, sk); - - err = sock_error(sk); - if (err) - return err; - - if (msg->msg_flags & MSG_OOB) - return -EOPNOTSUPP; - - lock_sock(sk); - - if (sk->sk_state != BT_CONNECTED) { - err = -ENOTCONN; - goto done; - } - - /* Connectionless channel */ - if (sk->sk_type == SOCK_DGRAM) { - skb = l2cap_create_connless_pdu(sk, msg, len); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - } else { - l2cap_do_send(sk, skb); - err = len; - } - goto done; - } - - switch (pi->mode) { - case L2CAP_MODE_BASIC: - /* Check outgoing MTU */ - if (len > pi->omtu) { - err = -EMSGSIZE; - goto done; - } - - /* Create a basic PDU */ - skb = l2cap_create_basic_pdu(sk, msg, len); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - goto done; - } - - l2cap_do_send(sk, skb); - err = len; - break; - - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - /* Entire SDU fits into one PDU */ - if (len <= pi->remote_mps) { - control = L2CAP_SDU_UNSEGMENTED; - skb = l2cap_create_iframe_pdu(sk, msg, len, control, 0); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - goto done; - } - __skb_queue_tail(TX_QUEUE(sk), skb); - - if (sk->sk_send_head == NULL) - sk->sk_send_head = skb; - - } else { - /* Segment SDU into multiples PDUs */ - err = l2cap_sar_segment_sdu(sk, msg, len); - if (err < 0) - goto done; - } - - if (pi->mode == L2CAP_MODE_STREAMING) { - l2cap_streaming_send(sk); - } else { - if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) && - (pi->conn_state & L2CAP_CONN_WAIT_F)) { - err = len; - break; - } - err = l2cap_ertm_send(sk); - } - - if (err >= 0) - err = len; - break; - - default: - BT_DBG("bad state %1.1x", pi->mode); - err = -EBADFD; - } - -done: - release_sock(sk); - return err; -} - static void l2cap_chan_ready(struct sock *sk) { struct sock *parent = bt_sk(sk)->parent; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 93af233bb167..fe4f834f03dd 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -681,6 +681,108 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch return err; } +static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) +{ + struct sock *sk = sock->sk; + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct sk_buff *skb; + u16 control; + int err; + + BT_DBG("sock %p, sk %p", sock, sk); + + err = sock_error(sk); + if (err) + return err; + + if (msg->msg_flags & MSG_OOB) + return -EOPNOTSUPP; + + lock_sock(sk); + + if (sk->sk_state != BT_CONNECTED) { + err = -ENOTCONN; + goto done; + } + + /* Connectionless channel */ + if (sk->sk_type == SOCK_DGRAM) { + skb = l2cap_create_connless_pdu(sk, msg, len); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + } else { + l2cap_do_send(sk, skb); + err = len; + } + goto done; + } + + switch (pi->mode) { + case L2CAP_MODE_BASIC: + /* Check outgoing MTU */ + if (len > pi->omtu) { + err = -EMSGSIZE; + goto done; + } + + /* Create a basic PDU */ + skb = l2cap_create_basic_pdu(sk, msg, len); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + goto done; + } + + l2cap_do_send(sk, skb); + err = len; + break; + + case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: + /* Entire SDU fits into one PDU */ + if (len <= pi->remote_mps) { + control = L2CAP_SDU_UNSEGMENTED; + skb = l2cap_create_iframe_pdu(sk, msg, len, control, 0); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + goto done; + } + __skb_queue_tail(TX_QUEUE(sk), skb); + + if (sk->sk_send_head == NULL) + sk->sk_send_head = skb; + + } else { + /* Segment SDU into multiples PDUs */ + err = l2cap_sar_segment_sdu(sk, msg, len); + if (err < 0) + goto done; + } + + if (pi->mode == L2CAP_MODE_STREAMING) { + l2cap_streaming_send(sk); + } else { + if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) && + (pi->conn_state & L2CAP_CONN_WAIT_F)) { + err = len; + break; + } + err = l2cap_ertm_send(sk); + } + + if (err >= 0) + err = len; + break; + + default: + BT_DBG("bad state %1.1x", pi->mode); + err = -EBADFD; + } + +done: + release_sock(sk); + return err; +} + static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) { struct sock *sk = sock->sk; -- cgit v1.2.3 From 6de0702b5b93da0ef097aa092b4597fbc024ebba Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 4 Feb 2011 03:35:20 -0200 Subject: Bluetooth: move __l2cap_sock_close() to l2cap_sock.c Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 2 + net/bluetooth/l2cap_core.c | 85 +------------------------------------------ net/bluetooth/l2cap_sock.c | 78 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index d4c93eded727..75ef0b2948f9 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -451,6 +451,8 @@ void l2cap_sock_kill(struct sock *sk); void l2cap_sock_init(struct sock *sk, struct sock *parent); struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); +void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int err); +void l2cap_chan_del(struct sock *sk, int err); int l2cap_do_connect(struct sock *sk); void l2cap_load(void); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 9d51af300d9c..ba7f9da68998 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -70,8 +70,6 @@ struct bt_sock_list l2cap_sk_list = { static void l2cap_busy_work(struct work_struct *work); -static void l2cap_sock_close(struct sock *sk); - static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, u8 ident, u16 dlen, void *data); @@ -207,7 +205,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so /* Delete channel. * Must be called on the locked socket. */ -static void l2cap_chan_del(struct sock *sk, int err) +void l2cap_chan_del(struct sock *sk, int err) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct sock *parent = bt_sk(sk)->parent; @@ -457,7 +455,7 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask) } } -static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int err) +void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int err) { struct l2cap_disconn_req req; @@ -739,85 +737,6 @@ static struct sock *l2cap_get_sock_by_psm(int state, __le16 psm, bdaddr_t *src) return node ? sk : sk1; } -static void l2cap_sock_cleanup_listen(struct sock *parent) -{ - struct sock *sk; - - BT_DBG("parent %p", parent); - - /* Close not yet accepted channels */ - while ((sk = bt_accept_dequeue(parent, NULL))) - l2cap_sock_close(sk); - - parent->sk_state = BT_CLOSED; - sock_set_flag(parent, SOCK_ZAPPED); -} - -void __l2cap_sock_close(struct sock *sk, int reason) -{ - BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); - - switch (sk->sk_state) { - case BT_LISTEN: - l2cap_sock_cleanup_listen(sk); - break; - - case BT_CONNECTED: - case BT_CONFIG: - if (sk->sk_type == SOCK_SEQPACKET || - sk->sk_type == SOCK_STREAM) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - - l2cap_sock_set_timer(sk, sk->sk_sndtimeo); - l2cap_send_disconn_req(conn, sk, reason); - } else - l2cap_chan_del(sk, reason); - break; - - case BT_CONNECT2: - if (sk->sk_type == SOCK_SEQPACKET || - sk->sk_type == SOCK_STREAM) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - struct l2cap_conn_rsp rsp; - __u16 result; - - if (bt_sk(sk)->defer_setup) - result = L2CAP_CR_SEC_BLOCK; - else - result = L2CAP_CR_BAD_PSM; - sk->sk_state = BT_DISCONN; - - rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); - rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); - rsp.result = cpu_to_le16(result); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(conn, l2cap_pi(sk)->ident, - L2CAP_CONN_RSP, sizeof(rsp), &rsp); - } else - l2cap_chan_del(sk, reason); - break; - - case BT_CONNECT: - case BT_DISCONN: - l2cap_chan_del(sk, reason); - break; - - default: - sock_set_flag(sk, SOCK_ZAPPED); - break; - } -} - -/* Must be called on unlocked socket. */ -static void l2cap_sock_close(struct sock *sk) -{ - l2cap_sock_clear_timer(sk); - lock_sock(sk); - __l2cap_sock_close(sk, ECONNRESET); - release_sock(sk); - l2cap_sock_kill(sk); -} - int l2cap_do_connect(struct sock *sk) { bdaddr_t *src = &bt_sk(sk)->src; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 4b4e0201ebbe..adf41692daf3 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -854,6 +854,84 @@ void l2cap_sock_kill(struct sock *sk) sock_put(sk); } +/* Must be called on unlocked socket. */ +static void l2cap_sock_close(struct sock *sk) +{ + l2cap_sock_clear_timer(sk); + lock_sock(sk); + __l2cap_sock_close(sk, ECONNRESET); + release_sock(sk); + l2cap_sock_kill(sk); +} + +static void l2cap_sock_cleanup_listen(struct sock *parent) +{ + struct sock *sk; + + BT_DBG("parent %p", parent); + + /* Close not yet accepted channels */ + while ((sk = bt_accept_dequeue(parent, NULL))) + l2cap_sock_close(sk); + + parent->sk_state = BT_CLOSED; + sock_set_flag(parent, SOCK_ZAPPED); +} + +void __l2cap_sock_close(struct sock *sk, int reason) +{ + BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); + + switch (sk->sk_state) { + case BT_LISTEN: + l2cap_sock_cleanup_listen(sk); + break; + + case BT_CONNECTED: + case BT_CONFIG: + if (sk->sk_type == SOCK_SEQPACKET || + sk->sk_type == SOCK_STREAM) { + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + + l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + l2cap_send_disconn_req(conn, sk, reason); + } else + l2cap_chan_del(sk, reason); + break; + + case BT_CONNECT2: + if (sk->sk_type == SOCK_SEQPACKET || + sk->sk_type == SOCK_STREAM) { + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct l2cap_conn_rsp rsp; + __u16 result; + + if (bt_sk(sk)->defer_setup) + result = L2CAP_CR_SEC_BLOCK; + else + result = L2CAP_CR_BAD_PSM; + + rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); + rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + rsp.result = cpu_to_le16(result); + rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + l2cap_send_cmd(conn, l2cap_pi(sk)->ident, + L2CAP_CONN_RSP, sizeof(rsp), &rsp); + } else + l2cap_chan_del(sk, reason); + break; + + case BT_CONNECT: + case BT_DISCONN: + l2cap_chan_del(sk, reason); + break; + + default: + sock_set_flag(sk, SOCK_ZAPPED); + break; + } +} + static int l2cap_sock_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; -- cgit v1.2.3 From a98a0bc6c92eacd181417a9c0ccd2e8028066622 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Thu, 3 Feb 2011 03:11:45 +0300 Subject: ASoC: CS4271: Move Chip Select control out of the CODEC code. Move Chip Select control out of the CODEC code for CS4271. Signed-off-by: Alexander Sverdlin Reviewed-by: H Hartley Sweeten Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/cs4271.h | 1 - sound/soc/codecs/cs4271.c | 22 +++------------------- 2 files changed, 3 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/sound/cs4271.h b/include/sound/cs4271.h index 16f8d325d3dc..50a059e7d116 100644 --- a/include/sound/cs4271.h +++ b/include/sound/cs4271.h @@ -19,7 +19,6 @@ 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/cs4271.c b/sound/soc/codecs/cs4271.c index 9c5b7db0ce6a..1791796216c8 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -441,22 +441,11 @@ static int cs4271_probe(struct snd_soc_codec *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 (cs4271plat && gpio_is_valid(cs4271plat->gpio_nreset)) + gpio_nreset = cs4271plat->gpio_nreset; if (gpio_nreset >= 0) if (gpio_request(gpio_nreset, "CS4271 Reset")) @@ -471,7 +460,6 @@ static int cs4271_probe(struct snd_soc_codec *codec) } cs4271->gpio_nreset = gpio_nreset; - cs4271->gpio_disable = gpio_disable; /* * In case of I2C, chip address specified in board data. @@ -509,10 +497,9 @@ static int cs4271_probe(struct snd_soc_codec *codec) static int cs4271_remove(struct snd_soc_codec *codec) { struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); - int gpio_nreset, gpio_disable; + int gpio_nreset; gpio_nreset = cs4271->gpio_nreset; - gpio_disable = cs4271->gpio_disable; if (gpio_is_valid(gpio_nreset)) { /* Set codec to the reset state */ @@ -520,9 +507,6 @@ static int cs4271_remove(struct snd_soc_codec *codec) gpio_free(gpio_nreset); } - if (gpio_is_valid(gpio_disable)) - gpio_free(gpio_disable); - return 0; }; -- cgit v1.2.3 From dc5f219e88294b93009eef946251251ffffb6d60 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 4 Feb 2011 13:19:20 +0100 Subject: genirq: Add IRQF_FORCE_RESUME Xen needs to reenable interrupts which are marked IRQF_NO_SUSPEND in the resume path. Add a flag to force the reenabling in the resume code. Tested-and-acked-by: Ian Campbell Signed-off-by: Thomas Gleixner --- include/linux/interrupt.h | 3 ++- kernel/irq/manage.c | 11 ++++++++++- kernel/irq/pm.c | 3 --- 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 55e0d4253e49..d746da19c6a2 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -55,7 +55,7 @@ * Used by threaded interrupts which need to keep the * irq line disabled until the threaded handler has been run. * IRQF_NO_SUSPEND - Do not disable this IRQ during suspend - * + * IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set */ #define IRQF_DISABLED 0x00000020 #define IRQF_SAMPLE_RANDOM 0x00000040 @@ -67,6 +67,7 @@ #define IRQF_IRQPOLL 0x00001000 #define IRQF_ONESHOT 0x00002000 #define IRQF_NO_SUSPEND 0x00004000 +#define IRQF_FORCE_RESUME 0x00008000 #define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND) diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 0caa59f747dd..b4198ee8cfdf 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -282,8 +282,17 @@ EXPORT_SYMBOL(disable_irq); void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume) { - if (resume) + if (resume) { + if (!(desc->status & IRQ_SUSPENDED)) { + if (!desc->action) + return; + if (!(desc->action->flags & IRQF_FORCE_RESUME)) + return; + /* Pretend that it got disabled ! */ + desc->depth++; + } desc->status &= ~IRQ_SUSPENDED; + } switch (desc->depth) { case 0: diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c index 0d4005d85b03..d6bfb89cce91 100644 --- a/kernel/irq/pm.c +++ b/kernel/irq/pm.c @@ -53,9 +53,6 @@ void resume_device_irqs(void) for_each_irq_desc(irq, desc) { unsigned long flags; - if (!(desc->status & IRQ_SUSPENDED)) - continue; - raw_spin_lock_irqsave(&desc->lock, flags); __enable_irq(desc, irq, true); raw_spin_unlock_irqrestore(&desc->lock, flags); -- cgit v1.2.3 From c13ff2ff3ad1479f222e18f9caba3db5af68d549 Mon Sep 17 00:00:00 2001 From: Seth Heasley Date: Mon, 10 Jan 2011 13:08:37 -0800 Subject: PCI/lpc: irq and pci_ids patch for Intel DH89xxCC DeviceIDs This patch adds the LPC Controller DeviceIDs for the Intel DH89xxCC PCH. The code for capturing ranges of LPC Controller DeviceIDs has also been updated. Acked-by: Jean Delvare Signed-off-by: Seth Heasley Signed-off-by: Jesse Barnes --- arch/x86/pci/irq.c | 15 ++++++--------- include/linux/pci_ids.h | 3 +++ 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c index 87e6c8323117..8201165bae28 100644 --- a/arch/x86/pci/irq.c +++ b/arch/x86/pci/irq.c @@ -597,21 +597,18 @@ static __init int intel_router_probe(struct irq_router *r, struct pci_dev *route return 1; } - if ((device >= PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MIN) && - (device <= PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MAX)) { + if ((device >= PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MIN && + device <= PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MAX) + || (device >= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN && + device <= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MAX) + || (device >= PCI_DEVICE_ID_INTEL_DH89XXCC_LPC_MIN && + device <= PCI_DEVICE_ID_INTEL_DH89XXCC_LPC_MAX)) { r->name = "PIIX/ICH"; r->get = pirq_piix_get; r->set = pirq_piix_set; return 1; } - if ((device >= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN) && - (device <= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MAX)) { - r->name = "PIIX/ICH"; - r->get = pirq_piix_get; - r->set = pirq_piix_set; - return 1; - } return 0; } diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 3adb06ebf841..46f23999458d 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2480,6 +2480,9 @@ #define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22 #define PCI_DEVICE_ID_INTEL_PATSBURG_LPC_0 0x1d40 #define PCI_DEVICE_ID_INTEL_PATSBURG_LPC_1 0x1d41 +#define PCI_DEVICE_ID_INTEL_DH89XXCC_LPC_MIN 0x2310 +#define PCI_DEVICE_ID_INTEL_DH89XXCC_LPC_MAX 0x231f +#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330 #define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410 #define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411 #define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413 -- cgit v1.2.3 From fa9921e46fd52b78070dc67ce0d27ec301a90410 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Wed, 2 Feb 2011 06:29:02 +0000 Subject: ipsec: allow to align IPv4 AH on 32 bits The Linux IPv4 AH stack aligns the AH header on a 64 bit boundary (like in IPv6). This is not RFC compliant (see RFC4302, Section 3.3.3.2.1), it should be aligned on 32 bits. For most of the authentication algorithms, the ICV size is 96 bits. The AH header alignment on 32 or 64 bits gives the same results. However for SHA-256-128 for instance, the wrong 64 bit alignment results in adding useless padding in IPv4 AH, which is forbidden by the RFC. To avoid breaking backward compatibility, we use a new flag (XFRM_STATE_ALIGN4) do change original behavior. Initial patch from Dang Hongwu and Christophe Gouault . Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- include/linux/xfrm.h | 1 + include/net/xfrm.h | 1 + net/ipv4/ah4.c | 25 +++++++++++++++++++------ 3 files changed, 21 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h index 930fdd2de79c..b93d6f598085 100644 --- a/include/linux/xfrm.h +++ b/include/linux/xfrm.h @@ -350,6 +350,7 @@ struct xfrm_usersa_info { #define XFRM_STATE_WILDRECV 8 #define XFRM_STATE_ICMP 16 #define XFRM_STATE_AF_UNSPEC 32 +#define XFRM_STATE_ALIGN4 64 }; struct xfrm_usersa_id { diff --git a/include/net/xfrm.h b/include/net/xfrm.h index b9f385da758e..1f6e8a0eb544 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -36,6 +36,7 @@ #define XFRM_PROTO_ROUTING IPPROTO_ROUTING #define XFRM_PROTO_DSTOPTS IPPROTO_DSTOPTS +#define XFRM_ALIGN4(len) (((len) + 3) & ~3) #define XFRM_ALIGN8(len) (((len) + 7) & ~7) #define MODULE_ALIAS_XFRM_MODE(family, encap) \ MODULE_ALIAS("xfrm-mode-" __stringify(family) "-" __stringify(encap)) diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index 86961bec70ab..325053df6e70 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c @@ -201,7 +201,10 @@ static int ah_output(struct xfrm_state *x, struct sk_buff *skb) top_iph->ttl = 0; top_iph->check = 0; - ah->hdrlen = (XFRM_ALIGN8(sizeof(*ah) + ahp->icv_trunc_len) >> 2) - 2; + if (x->props.flags & XFRM_STATE_ALIGN4) + ah->hdrlen = (XFRM_ALIGN4(sizeof(*ah) + ahp->icv_trunc_len) >> 2) - 2; + else + ah->hdrlen = (XFRM_ALIGN8(sizeof(*ah) + ahp->icv_trunc_len) >> 2) - 2; ah->reserved = 0; ah->spi = x->id.spi; @@ -299,9 +302,15 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb) nexthdr = ah->nexthdr; ah_hlen = (ah->hdrlen + 2) << 2; - if (ah_hlen != XFRM_ALIGN8(sizeof(*ah) + ahp->icv_full_len) && - ah_hlen != XFRM_ALIGN8(sizeof(*ah) + ahp->icv_trunc_len)) - goto out; + if (x->props.flags & XFRM_STATE_ALIGN4) { + if (ah_hlen != XFRM_ALIGN4(sizeof(*ah) + ahp->icv_full_len) && + ah_hlen != XFRM_ALIGN4(sizeof(*ah) + ahp->icv_trunc_len)) + goto out; + } else { + if (ah_hlen != XFRM_ALIGN8(sizeof(*ah) + ahp->icv_full_len) && + ah_hlen != XFRM_ALIGN8(sizeof(*ah) + ahp->icv_trunc_len)) + goto out; + } if (!pskb_may_pull(skb, ah_hlen)) goto out; @@ -450,8 +459,12 @@ static int ah_init_state(struct xfrm_state *x) BUG_ON(ahp->icv_trunc_len > MAX_AH_AUTH_LEN); - x->props.header_len = XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + - ahp->icv_trunc_len); + if (x->props.flags & XFRM_STATE_ALIGN4) + x->props.header_len = XFRM_ALIGN4(sizeof(struct ip_auth_hdr) + + ahp->icv_trunc_len); + else + x->props.header_len = XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + + ahp->icv_trunc_len); if (x->props.mode == XFRM_MODE_TUNNEL) x->props.header_len += sizeof(struct iphdr); x->data = ahp; -- cgit v1.2.3 From f4d5c029bd6731baac0937324cef0f746e7d5ea7 Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Wed, 26 Jan 2011 16:49:00 +0800 Subject: tracing: Compile time initialization for event flags value Compile time initialization is better than runtime initialization. Remove many early_initcall()s and many trace_init_flags_##name()s. Acked-by: Frederic Weisbecker Signed-off-by: Lai Jiangshan LKML-Reference: <4D3FDFFC.6030304@cn.fujitsu.com> Signed-off-by: Steven Rostedt --- include/linux/syscalls.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 8e8968e74544..a17fcea2ca58 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -132,11 +132,11 @@ extern struct trace_event_functions exit_syscall_print_funcs; .class = &event_class_syscall_enter, \ .event.funcs = &enter_syscall_print_funcs, \ .data = (void *)&__syscall_meta_##sname,\ + .flags = TRACE_EVENT_FL_CAP_ANY, \ }; \ 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) + *__event_enter_##sname = &event_enter_##sname; #define SYSCALL_TRACE_EXIT_EVENT(sname) \ static struct syscall_metadata __syscall_meta_##sname; \ @@ -146,11 +146,11 @@ extern struct trace_event_functions exit_syscall_print_funcs; .class = &event_class_syscall_exit, \ .event.funcs = &exit_syscall_print_funcs, \ .data = (void *)&__syscall_meta_##sname,\ + .flags = TRACE_EVENT_FL_CAP_ANY, \ }; \ 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) + *__event_exit_##sname = &event_exit_##sname; #define SYSCALL_METADATA(sname, nb) \ SYSCALL_TRACE_ENTER_EVENT(sname); \ -- cgit v1.2.3 From e7b66bdc02592f5573ade667e4d68ac6e7b0f9e1 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Feb 2011 15:33:22 -0800 Subject: net: Remove bogus barrier() in dst_allfrag(). I simply missed this one when modifying the other dst metric interfaces earlier. Signed-off-by: David S. Miller --- include/net/dst.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index e550195d4f86..e01855de21e8 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -220,8 +220,6 @@ static inline u32 dst_allfrag(const struct dst_entry *dst) { int ret = dst_feature(dst, RTAX_FEATURE_ALLFRAG); - /* Yes, _exactly_. This is paranoia. */ - barrier(); return ret; } -- cgit v1.2.3 From 8d13a2a9fb3e5e3f68e9d3ec0de3c8fcfa56a224 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Feb 2011 16:17:55 -0800 Subject: net: Kill NETEVENT_PMTU_UPDATE. Nobody actually does anything in response to the event, so just kill it off. Signed-off-by: David S. Miller --- drivers/net/cxgb3/cxgb3_offload.c | 2 -- drivers/net/cxgb4/cxgb4_main.c | 1 - include/net/netevent.h | 1 - net/ipv4/route.c | 1 - net/ipv6/route.c | 1 - 5 files changed, 6 deletions(-) (limited to 'include') diff --git a/drivers/net/cxgb3/cxgb3_offload.c b/drivers/net/cxgb3/cxgb3_offload.c index ef02aa68c926..7ea94b5205f8 100644 --- a/drivers/net/cxgb3/cxgb3_offload.c +++ b/drivers/net/cxgb3/cxgb3_offload.c @@ -967,8 +967,6 @@ static int nb_callback(struct notifier_block *self, unsigned long event, cxgb_neigh_update((struct neighbour *)ctx); break; } - case (NETEVENT_PMTU_UPDATE): - break; case (NETEVENT_REDIRECT):{ struct netevent_redirect *nr = ctx; cxgb_redirect(nr->old, nr->new); diff --git a/drivers/net/cxgb4/cxgb4_main.c b/drivers/net/cxgb4/cxgb4_main.c index ec35d458102c..5352c8a23f4d 100644 --- a/drivers/net/cxgb4/cxgb4_main.c +++ b/drivers/net/cxgb4/cxgb4_main.c @@ -2471,7 +2471,6 @@ static int netevent_cb(struct notifier_block *nb, unsigned long event, case NETEVENT_NEIGH_UPDATE: check_neigh_update(data); break; - case NETEVENT_PMTU_UPDATE: case NETEVENT_REDIRECT: default: break; diff --git a/include/net/netevent.h b/include/net/netevent.h index e82b7bab3ff3..22b239c17eaa 100644 --- a/include/net/netevent.h +++ b/include/net/netevent.h @@ -21,7 +21,6 @@ struct netevent_redirect { enum netevent_notif_type { NETEVENT_NEIGH_UPDATE = 1, /* arg is struct neighbour ptr */ - NETEVENT_PMTU_UPDATE, /* arg is struct dst_entry ptr */ NETEVENT_REDIRECT, /* arg is struct netevent_redirect ptr */ }; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 2e225dafc4f8..0455af851751 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1762,7 +1762,6 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu) } dst_metric_set(dst, RTAX_MTU, mtu); dst_set_expires(dst, ip_rt_mtu_expires); - call_netevent_notifiers(NETEVENT_PMTU_UPDATE, dst); } } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 0a63d44e6f48..12ec83d48806 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -965,7 +965,6 @@ static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu) dst_metric_set(dst, RTAX_FEATURES, features); } dst_metric_set(dst, RTAX_MTU, mtu); - call_netevent_notifiers(NETEVENT_PMTU_UPDATE, dst); } } -- cgit v1.2.3 From 4149efb22da66e326fc48baf80d628834509f7f0 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 8 Feb 2011 10:39:03 +0100 Subject: workqueue: add system_freezeable_wq Add system wide freezeable workqueue. Signed-off-by: Tejun Heo Acked-by: Dmitry Torokhov Cc: "Rafael J. Wysocki" --- include/linux/workqueue.h | 4 ++++ kernel/workqueue.c | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 1ac11586a2f5..de6a755befac 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -286,11 +286,15 @@ enum { * any specific CPU, not concurrency managed, and all queued works are * executed immediately as long as max_active limit is not reached and * resources are available. + * + * system_freezeable_wq is equivalent to system_wq except that it's + * freezeable. */ extern struct workqueue_struct *system_wq; extern struct workqueue_struct *system_long_wq; extern struct workqueue_struct *system_nrt_wq; extern struct workqueue_struct *system_unbound_wq; +extern struct workqueue_struct *system_freezeable_wq; extern struct workqueue_struct * __alloc_workqueue_key(const char *name, unsigned int flags, int max_active, diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 11869faa6819..28f8bd08f0e7 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -249,10 +249,12 @@ struct workqueue_struct *system_wq __read_mostly; struct workqueue_struct *system_long_wq __read_mostly; struct workqueue_struct *system_nrt_wq __read_mostly; struct workqueue_struct *system_unbound_wq __read_mostly; +struct workqueue_struct *system_freezeable_wq __read_mostly; EXPORT_SYMBOL_GPL(system_wq); EXPORT_SYMBOL_GPL(system_long_wq); EXPORT_SYMBOL_GPL(system_nrt_wq); EXPORT_SYMBOL_GPL(system_unbound_wq); +EXPORT_SYMBOL_GPL(system_freezeable_wq); #define CREATE_TRACE_POINTS #include @@ -3764,8 +3766,10 @@ static int __init init_workqueues(void) system_nrt_wq = alloc_workqueue("events_nrt", WQ_NON_REENTRANT, 0); system_unbound_wq = alloc_workqueue("events_unbound", WQ_UNBOUND, WQ_UNBOUND_MAX_ACTIVE); + system_freezeable_wq = alloc_workqueue("events_freezeable", + WQ_FREEZEABLE, 0); BUG_ON(!system_wq || !system_long_wq || !system_nrt_wq || - !system_unbound_wq); + !system_unbound_wq || !system_freezeable_wq); return 0; } early_initcall(init_workqueues); -- cgit v1.2.3 From 6d1d4ea4a82f8c17a3ff7c2f677bc3d41ea7484b Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Tue, 8 Feb 2011 23:32:17 +0100 Subject: ssb: extract boardflags2 for SPROMs rev 4 and 5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/ssb/pci.c | 4 ++++ include/linux/ssb/ssb_regs.h | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'include') diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index 158449e55044..5b33b3b06f7f 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -468,10 +468,14 @@ static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in) SPEX(country_code, SSB_SPROM4_CCODE, 0xFFFF, 0); SPEX(boardflags_lo, SSB_SPROM4_BFLLO, 0xFFFF, 0); SPEX(boardflags_hi, SSB_SPROM4_BFLHI, 0xFFFF, 0); + SPEX(boardflags2_lo, SSB_SPROM4_BFL2LO, 0xFFFF, 0); + SPEX(boardflags2_hi, SSB_SPROM4_BFL2HI, 0xFFFF, 0); } else { SPEX(country_code, SSB_SPROM5_CCODE, 0xFFFF, 0); SPEX(boardflags_lo, SSB_SPROM5_BFLLO, 0xFFFF, 0); SPEX(boardflags_hi, SSB_SPROM5_BFLHI, 0xFFFF, 0); + SPEX(boardflags2_lo, SSB_SPROM5_BFL2LO, 0xFFFF, 0); + SPEX(boardflags2_hi, SSB_SPROM5_BFL2HI, 0xFFFF, 0); } SPEX(ant_available_a, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_A, SSB_SPROM4_ANTAVAIL_A_SHIFT); diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h index 489f7b6d61c5..df9211a84634 100644 --- a/include/linux/ssb/ssb_regs.h +++ b/include/linux/ssb/ssb_regs.h @@ -268,6 +268,8 @@ /* SPROM Revision 4 */ #define SSB_SPROM4_BFLLO 0x0044 /* Boardflags (low 16 bits) */ #define SSB_SPROM4_BFLHI 0x0046 /* Board Flags Hi */ +#define SSB_SPROM4_BFL2LO 0x0048 /* Board flags 2 (low 16 bits) */ +#define SSB_SPROM4_BFL2HI 0x004A /* Board flags 2 Hi */ #define SSB_SPROM4_IL0MAC 0x004C /* 6 byte MAC address for a/b/g/n */ #define SSB_SPROM4_CCODE 0x0052 /* Country Code (2 bytes) */ #define SSB_SPROM4_GPIOA 0x0056 /* Gen. Purpose IO # 0 and 1 */ @@ -358,6 +360,8 @@ #define SSB_SPROM5_CCODE 0x0044 /* Country Code (2 bytes) */ #define SSB_SPROM5_BFLLO 0x004A /* Boardflags (low 16 bits) */ #define SSB_SPROM5_BFLHI 0x004C /* Board Flags Hi */ +#define SSB_SPROM5_BFL2LO 0x004E /* Board flags 2 (low 16 bits) */ +#define SSB_SPROM5_BFL2HI 0x0050 /* Board flags 2 Hi */ #define SSB_SPROM5_IL0MAC 0x0052 /* 6 byte MAC address for a/b/g/n */ #define SSB_SPROM5_GPIOA 0x0076 /* Gen. Purpose IO # 0 and 1 */ #define SSB_SPROM5_GPIOA_P0 0x00FF /* Pin 0 */ -- cgit v1.2.3 From fa9879edebdaad4cfcd2dbe3eaa2ba0dc4f0a262 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 9 Feb 2011 14:44:17 +0530 Subject: ASoC: add support for multiple jack types This patch adds soc-jack support for adding voltage zones and for detecting jack type Signed-off-by: Vinod Koul Signed-off-by: Harsha Priya Signed-off-by: Mark Brown --- include/sound/soc.h | 23 +++++++++++++++++++++++ sound/soc/soc-jack.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 4b6c0a8c332f..4ccf1e4e0dd0 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -234,6 +234,7 @@ struct snd_soc_codec; struct snd_soc_codec_driver; struct soc_enum; struct snd_soc_jack; +struct snd_soc_jack_zone; struct snd_soc_jack_pin; struct snd_soc_cache_ops; #include @@ -307,6 +308,9 @@ void snd_soc_jack_notifier_register(struct snd_soc_jack *jack, struct notifier_block *nb); void snd_soc_jack_notifier_unregister(struct snd_soc_jack *jack, struct notifier_block *nb); +int snd_soc_jack_add_zones(struct snd_soc_jack *jack, int count, + struct snd_soc_jack_zone *zones); +int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage); #ifdef CONFIG_GPIOLIB int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, struct snd_soc_jack_gpio *gpios); @@ -406,6 +410,24 @@ struct snd_soc_jack_pin { bool invert; }; +/** + * struct snd_soc_jack_zone - Describes voltage zones of jack detection + * + * @min_mv: start voltage in mv + * @max_mv: end voltage in mv + * @jack_type: type of jack that is expected for this voltage + * @debounce_time: debounce_time for jack, codec driver should wait for this + * duration before reading the adc for voltages + * @:list: list container + */ +struct snd_soc_jack_zone { + unsigned int min_mv; + unsigned int max_mv; + unsigned int jack_type; + unsigned int debounce_time; + struct list_head list; +}; + /** * struct snd_soc_jack_gpio - Describes a gpio pin for jack detection * @@ -435,6 +457,7 @@ struct snd_soc_jack { struct list_head pins; int status; struct blocking_notifier_head notifier; + struct list_head jack_zones; }; /* SoC PCM stream information */ diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index ac5a5bc7375a..99dbaf756b44 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -37,6 +37,7 @@ int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, { jack->codec = codec; INIT_LIST_HEAD(&jack->pins); + INIT_LIST_HEAD(&jack->jack_zones); BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); return snd_jack_new(codec->card->snd_card, id, type, &jack->jack); @@ -111,6 +112,51 @@ out: } EXPORT_SYMBOL_GPL(snd_soc_jack_report); +/** + * snd_soc_jack_add_zones - Associate voltage zones with jack + * + * @jack: ASoC jack + * @count: Number of zones + * @zone: Array of zones + * + * After this function has been called the zones specified in the + * array will be associated with the jack. + */ +int snd_soc_jack_add_zones(struct snd_soc_jack *jack, int count, + struct snd_soc_jack_zone *zones) +{ + int i; + + for (i = 0; i < count; i++) { + INIT_LIST_HEAD(&zones[i].list); + list_add(&(zones[i].list), &jack->jack_zones); + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_jack_add_zones); + +/** + * snd_soc_jack_get_type - Based on the mic bias value, this function returns + * the type of jack from the zones delcared in the jack type + * + * @micbias_voltage: mic bias voltage at adc channel when jack is plugged in + * + * Based on the mic bias value passed, this function helps identify + * the type of jack from the already delcared jack zones + */ +int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage) +{ + struct snd_soc_jack_zone *zone; + + list_for_each_entry(zone, &jack->jack_zones, list) { + if (micbias_voltage >= zone->min_mv && + micbias_voltage < zone->max_mv) + return zone->jack_type; + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_jack_get_type); + /** * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack * -- cgit v1.2.3 From a68a27b6f2354273bacc39c3dd06456edb202230 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 2 Nov 2010 10:10:56 -0400 Subject: IMA: convert i_readcount to atomic Convert the inode's i_readcount from an unsigned int to atomic. Signed-off-by: Mimi Zohar Acked-by: Eric Paris --- include/linux/fs.h | 3 +-- security/integrity/ima/ima_iint.c | 7 ++++--- security/integrity/ima/ima_main.c | 11 ++++++----- 3 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/fs.h b/include/linux/fs.h index baf3e556ff0e..ef85322863b9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -794,8 +794,7 @@ struct inode { #endif #ifdef CONFIG_IMA - /* protected by i_lock */ - unsigned int i_readcount; /* struct files open RO */ + atomic_t i_readcount; /* struct files open RO */ #endif atomic_t i_writecount; #ifdef CONFIG_SECURITY diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c index c442e47b6785..f0053552fd58 100644 --- a/security/integrity/ima/ima_iint.c +++ b/security/integrity/ima/ima_iint.c @@ -137,10 +137,11 @@ void ima_inode_free(struct inode *inode) { struct ima_iint_cache *iint; - if (inode->i_readcount) - printk(KERN_INFO "%s: readcount: %u\n", __func__, inode->i_readcount); + if (atomic_read(&inode->i_readcount)) + printk(KERN_INFO "%s: readcount: %u\n", __func__, + atomic_read(&inode->i_readcount)); - inode->i_readcount = 0; + atomic_set(&inode->i_readcount, 0); if (!IS_IMA(inode)) return; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 203de979d305..6e8cb931b8f1 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -113,7 +113,7 @@ void ima_counts_get(struct file *file) goto out; if (mode & FMODE_WRITE) { - if (inode->i_readcount && IS_IMA(inode)) + if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) send_tomtou = true; goto out; } @@ -127,7 +127,7 @@ void ima_counts_get(struct file *file) out: /* remember the vfs deals with i_writecount */ if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) - inode->i_readcount++; + atomic_inc(&inode->i_readcount); spin_unlock(&inode->i_lock); @@ -149,15 +149,16 @@ static void ima_dec_counts(struct inode *inode, struct file *file) assert_spin_locked(&inode->i_lock); if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) { - if (unlikely(inode->i_readcount == 0)) { + if (unlikely(atomic_read(&inode->i_readcount) == 0)) { if (!ima_limit_imbalance(file)) { printk(KERN_INFO "%s: open/free imbalance (r:%u)\n", - __func__, inode->i_readcount); + __func__, + atomic_read(&inode->i_readcount)); dump_stack(); } return; } - inode->i_readcount--; + atomic_dec(&inode->i_readcount); } } -- cgit v1.2.3 From a5c96ebf1d71df0c5fb77ab58c9aeb307cf02372 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 2 Nov 2010 10:11:37 -0400 Subject: IMA: define readcount functions Define i_readcount_inc/dec() functions to be called from the VFS layer. Changelog: - renamed iget/iput_readcount to i_readcount_inc/dec (Dave Chinner's suggestion) - removed i_lock in iput_readcount() (based on comments:Dave Chinner,Eric Paris) Signed-off-by: Mimi Zohar Acked-by: Eric Paris --- include/linux/fs.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'include') diff --git a/include/linux/fs.h b/include/linux/fs.h index ef85322863b9..a3e8f02b727d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2186,6 +2186,26 @@ static inline void allow_write_access(struct file *file) if (file) atomic_inc(&file->f_path.dentry->d_inode->i_writecount); } +#ifdef CONFIG_IMA +static inline void i_readcount_dec(struct inode *inode) +{ + BUG_ON(!atomic_read(&inode->i_readcount)); + atomic_dec(&inode->i_readcount); +} +static inline void i_readcount_inc(struct inode *inode) +{ + atomic_inc(&inode->i_readcount); +} +#else +static inline void i_readcount_dec(struct inode *inode) +{ + return; +} +static inline void i_readcount_inc(struct inode *inode) +{ + return; +} +#endif extern int do_pipe_flags(int *, int); extern struct file *create_read_pipe(struct file *f, int flags); extern struct file *create_write_pipe(int flags); -- cgit v1.2.3 From 890275b5eb79e9933d12290473eab9ac38da0051 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 2 Nov 2010 10:13:07 -0400 Subject: IMA: maintain i_readcount in the VFS layer ima_counts_get() updated the readcount and invalidated the PCR, as necessary. Only update the i_readcount in the VFS layer. Move the PCR invalidation checks to ima_file_check(), where it belongs. Maintaining the i_readcount in the VFS layer, will allow other subsystems to use i_readcount. Signed-off-by: Mimi Zohar Acked-by: Eric Paris --- fs/file_table.c | 5 ++++- fs/open.c | 3 ++- include/linux/ima.h | 6 ------ security/integrity/ima/ima_iint.c | 2 -- security/integrity/ima/ima_main.c | 25 ++++++++----------------- 5 files changed, 14 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/fs/file_table.c b/fs/file_table.c index c3dee381f1b4..0c724deb46f9 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -190,7 +190,8 @@ struct file *alloc_file(struct path *path, fmode_t mode, file_take_write(file); WARN_ON(mnt_clone_write(path->mnt)); } - ima_counts_get(file); + if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) + i_readcount_inc(path->dentry->d_inode); return file; } EXPORT_SYMBOL(alloc_file); @@ -251,6 +252,8 @@ static void __fput(struct file *file) fops_put(file->f_op); put_pid(file->f_owner.pid); file_sb_list_del(file); + if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) + i_readcount_dec(inode); if (file->f_mode & FMODE_WRITE) drop_file_write_access(file); file->f_path.dentry = NULL; diff --git a/fs/open.c b/fs/open.c index 4197b9ed023d..0d485c50bb95 100644 --- a/fs/open.c +++ b/fs/open.c @@ -688,7 +688,8 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, if (error) goto cleanup_all; } - ima_counts_get(f); + if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) + i_readcount_inc(inode); f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); diff --git a/include/linux/ima.h b/include/linux/ima.h index 975837e7d6c0..09e6e62f9953 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -20,7 +20,6 @@ extern void ima_inode_free(struct inode *inode); extern int ima_file_check(struct file *file, int mask); extern void ima_file_free(struct file *file); extern int ima_file_mmap(struct file *file, unsigned long prot); -extern void ima_counts_get(struct file *file); #else static inline int ima_bprm_check(struct linux_binprm *bprm) @@ -53,10 +52,5 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot) return 0; } -static inline void ima_counts_get(struct file *file) -{ - return; -} - #endif /* CONFIG_IMA_H */ #endif /* _LINUX_IMA_H */ diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c index f0053552fd58..68efe3b8d993 100644 --- a/security/integrity/ima/ima_iint.c +++ b/security/integrity/ima/ima_iint.c @@ -141,8 +141,6 @@ void ima_inode_free(struct inode *inode) printk(KERN_INFO "%s: readcount: %u\n", __func__, atomic_read(&inode->i_readcount)); - atomic_set(&inode->i_readcount, 0); - if (!IS_IMA(inode)) return; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 6e8cb931b8f1..69b4856af4da 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -86,17 +86,16 @@ out: } /* - * ima_counts_get - increment file counts + * ima_rdwr_violation_check * - * Maintain read/write counters for all files, but only - * invalidate the PCR for measured files: + * Only invalidate the PCR for measured files: * - Opening a file for write when already open for read, * results in a time of measure, time of use (ToMToU) error. * - Opening a file for read when already open for write, * could result in a file measurement error. * */ -void ima_counts_get(struct file *file) +static void ima_rdwr_violation_check(struct file *file) { struct dentry *dentry = file->f_path.dentry; struct inode *inode = dentry->d_inode; @@ -104,13 +103,10 @@ void ima_counts_get(struct file *file) int rc; bool send_tomtou = false, send_writers = false; - if (!S_ISREG(inode->i_mode)) + if (!S_ISREG(inode->i_mode) || !ima_initialized) return; - spin_lock(&inode->i_lock); - - if (!ima_initialized) - goto out; + mutex_lock(&inode->i_mutex); /* file metadata: permissions, xattr */ if (mode & FMODE_WRITE) { if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) @@ -125,11 +121,7 @@ void ima_counts_get(struct file *file) if (atomic_read(&inode->i_writecount) > 0) send_writers = true; out: - /* remember the vfs deals with i_writecount */ - if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) - atomic_inc(&inode->i_readcount); - - spin_unlock(&inode->i_lock); + mutex_unlock(&inode->i_mutex); if (send_tomtou) ima_add_violation(inode, dentry->d_name.name, "invalid_pcr", @@ -158,7 +150,6 @@ static void ima_dec_counts(struct inode *inode, struct file *file) } return; } - atomic_dec(&inode->i_readcount); } } @@ -203,8 +194,7 @@ static void ima_file_free_noiint(struct inode *inode, struct file *file) * ima_file_free - called on __fput() * @file: pointer to file structure being freed * - * Flag files that changed, based on i_version; - * and decrement the i_readcount. + * Flag files that changed, based on i_version */ void ima_file_free(struct file *file) { @@ -318,6 +308,7 @@ int ima_file_check(struct file *file, int mask) { int rc; + ima_rdwr_violation_check(file); rc = process_measurement(file, file->f_dentry->d_name.name, mask & (MAY_READ | MAY_WRITE | MAY_EXEC), FILE_CHECK); -- cgit v1.2.3 From 7a71ed899e77cc822abb863e24a422dcf7e9fa33 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 9 Feb 2011 14:30:26 -0800 Subject: inetpeer: Abstract address representation further. Future changes will add caching information, and some of these new elements will be addresses. Since the family is implicit via the ->daddr.family member, replicating the family in ever address we store is entirely redundant. Signed-off-by: David S. Miller --- include/net/inetpeer.h | 16 ++++++++++------ net/ipv4/inetpeer.c | 6 +++--- net/ipv4/tcp_ipv4.c | 2 +- net/ipv6/tcp_ipv6.c | 2 +- 4 files changed, 15 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index ead2cb2de18c..60e2cd8d1319 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -15,12 +15,16 @@ #include #include -struct inetpeer_addr { +struct inetpeer_addr_base { union { - __be32 a4; - __be32 a6[4]; + __be32 a4; + __be32 a6[4]; }; - __u16 family; +}; + +struct inetpeer_addr { + struct inetpeer_addr_base addr; + __u16 family; }; struct inet_peer { @@ -67,7 +71,7 @@ static inline struct inet_peer *inet_getpeer_v4(__be32 v4daddr, int create) { struct inetpeer_addr daddr; - daddr.a4 = v4daddr; + daddr.addr.a4 = v4daddr; daddr.family = AF_INET; return inet_getpeer(&daddr, create); } @@ -76,7 +80,7 @@ static inline struct inet_peer *inet_getpeer_v6(struct in6_addr *v6daddr, int cr { struct inetpeer_addr daddr; - ipv6_addr_copy((struct in6_addr *)daddr.a6, v6daddr); + ipv6_addr_copy((struct in6_addr *)daddr.addr.a6, v6daddr); daddr.family = AF_INET6; return inet_getpeer(&daddr, create); } diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index 709fbb4132d7..4346c38763ae 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -167,9 +167,9 @@ static int addr_compare(const struct inetpeer_addr *a, int i, n = (a->family == AF_INET ? 1 : 4); for (i = 0; i < n; i++) { - if (a->a6[i] == b->a6[i]) + if (a->addr.a6[i] == b->addr.a6[i]) continue; - if (a->a6[i] < b->a6[i]) + if (a->addr.a6[i] < b->addr.a6[i]) return -1; return 1; } @@ -510,7 +510,7 @@ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) p->daddr = *daddr; atomic_set(&p->refcnt, 1); atomic_set(&p->rid, 0); - atomic_set(&p->ip_id_count, secure_ip_id(daddr->a4)); + atomic_set(&p->ip_id_count, secure_ip_id(daddr->addr.a4)); p->tcp_ts_stamp = 0; p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW; p->rate_tokens = 0; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 02f583b3744a..e2b9be27f226 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1341,7 +1341,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) tcp_death_row.sysctl_tw_recycle && (dst = inet_csk_route_req(sk, req)) != NULL && (peer = rt_get_peer((struct rtable *)dst)) != NULL && - peer->daddr.a4 == saddr) { + peer->daddr.addr.a4 == saddr) { inet_peer_refcheck(peer); if ((u32)get_seconds() - peer->tcp_ts_stamp < TCP_PAWS_MSL && (s32)(peer->tcp_ts - req->ts_recent) > diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 20aa95e37359..d6954e318324 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1323,7 +1323,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) tcp_death_row.sysctl_tw_recycle && (dst = inet6_csk_route_req(sk, req)) != NULL && (peer = rt6_get_peer((struct rt6_info *)dst)) != NULL && - ipv6_addr_equal((struct in6_addr *)peer->daddr.a6, + ipv6_addr_equal((struct in6_addr *)peer->daddr.addr.a6, &treq->rmt_addr)) { inet_peer_refcheck(peer); if ((u32)get_seconds() - peer->tcp_ts_stamp < TCP_PAWS_MSL && -- cgit v1.2.3 From ddd4aa424b866a08ceba7ddf38e61542c91b93a0 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 9 Feb 2011 15:36:47 -0800 Subject: inetpeer: Add redirect and PMTU discovery cached info. Validity of the cached PMTU information is indicated by it's expiration value being non-zero, just as per dst->expires. The scheme we will use is that we will remember the pre-ICMP value held in the metrics or route entry, and then at expiration time we will restore that value. In this way PMTU expiration does not kill off the cached route as is done currently. Redirect information is permanent, or at least until another redirect is received. Signed-off-by: David S. Miller --- include/net/inetpeer.h | 18 +++++++++++------- net/ipv4/inetpeer.c | 2 ++ 2 files changed, 13 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index 60e2cd8d1319..e6dd8da6b2ad 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -43,13 +43,17 @@ struct inet_peer { */ union { struct { - atomic_t rid; /* Frag reception counter */ - atomic_t ip_id_count; /* IP ID for the next packet */ - __u32 tcp_ts; - __u32 tcp_ts_stamp; - u32 metrics[RTAX_MAX]; - u32 rate_tokens; /* rate limiting for ICMP */ - unsigned long rate_last; + atomic_t rid; /* Frag reception counter */ + atomic_t ip_id_count; /* IP ID for the next packet */ + __u32 tcp_ts; + __u32 tcp_ts_stamp; + u32 metrics[RTAX_MAX]; + u32 rate_tokens; /* rate limiting for ICMP */ + unsigned long rate_last; + unsigned long pmtu_expires; + u32 pmtu_orig; + u32 pmtu_learned; + struct inetpeer_addr_base redirect_learned; }; struct rcu_head rcu; }; diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index 4346c38763ae..48f8d4592ccd 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -515,6 +515,8 @@ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW; p->rate_tokens = 0; p->rate_last = 0; + p->pmtu_expires = 0; + memset(&p->redirect_learned, 0, sizeof(p->redirect_learned)); INIT_LIST_HEAD(&p->unused); -- cgit v1.2.3 From 6431cbc25fa21635ee04eb0516ba6c51389fbfac Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 7 Feb 2011 20:38:06 -0800 Subject: inet: Create a mechanism for upward inetpeer propagation into routes. If we didn't have a routing cache, we would not be able to properly propagate certain kinds of dynamic path attributes, for example PMTU information and redirects. The reason is that if we didn't have a routing cache, then there would be no way to lookup all of the active cached routes hanging off of sockets, tunnels, IPSEC bundles, etc. Consider the case where we created a cached route, but no inetpeer entry existed and also we were not asked to pre-COW the route metrics and therefore did not force the creation a new inetpeer entry. If we later get a PMTU message, or a redirect, and store this information in a new inetpeer entry, there is no way to teach that cached route about the newly existing inetpeer entry. The facilities implemented here handle this problem. First we create a generation ID. When we create a cached route of any kind, we remember the generation ID at the time of attachment. Any time we force-create an inetpeer entry in response to new path information, we bump that generation ID. The dst_ops->check() callback is where the knowledge of this event is propagated. If the global generation ID does not equal the one stored in the cached route, and the cached route has not attached to an inetpeer yet, we look it up and attach if one is found. Now that we've updated the cached route's information, we update the route's generation ID too. This clears the way for implementing PMTU and redirects directly in the inetpeer cache. There is absolutely no need to consult cached route information in order to maintain this information. At this point nothing bumps the inetpeer genids, that comes in the later changes which handle PMTUs and redirects using inetpeers. Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 1 + include/net/route.h | 1 + net/ipv4/route.c | 19 ++++++++++++++++++- net/ipv6/route.c | 18 ++++++++++++++++-- 4 files changed, 36 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 708ff7cb8806..46a6e8ae232c 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -108,6 +108,7 @@ struct rt6_info { u32 rt6i_flags; struct rt6key rt6i_src; u32 rt6i_metric; + u32 rt6i_peer_genid; struct inet6_dev *rt6i_idev; struct inet_peer *rt6i_peer; diff --git a/include/net/route.h b/include/net/route.h index e5864658dc76..bf790c1c6ac8 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -69,6 +69,7 @@ struct rtable { /* Miscellaneous cached information */ __be32 rt_spec_dst; /* RFC1122 specific destination */ + u32 rt_peer_genid; struct inet_peer *peer; /* long-living peer info */ struct fib_info *fi; /* for client ref to shared metrics */ }; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 0455af851751..0979e039104a 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1308,6 +1308,13 @@ skip_hashing: return 0; } +static atomic_t __rt_peer_genid = ATOMIC_INIT(0); + +static u32 rt_peer_genid(void) +{ + return atomic_read(&__rt_peer_genid); +} + void rt_bind_peer(struct rtable *rt, int create) { struct inet_peer *peer; @@ -1316,6 +1323,8 @@ void rt_bind_peer(struct rtable *rt, int create) if (peer && cmpxchg(&rt->peer, NULL, peer) != NULL) inet_putpeer(peer); + else + rt->rt_peer_genid = rt_peer_genid(); } /* @@ -1767,8 +1776,16 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu) static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie) { - if (rt_is_expired((struct rtable *)dst)) + struct rtable *rt = (struct rtable *) dst; + + if (rt_is_expired(rt)) return NULL; + if (rt->rt_peer_genid != rt_peer_genid()) { + if (!rt->peer) + rt_bind_peer(rt, 0); + + rt->rt_peer_genid = rt_peer_genid(); + } return dst; } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 12ec83d48806..ad8556e6fd41 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -240,6 +240,13 @@ static void ip6_dst_destroy(struct dst_entry *dst) } } +static atomic_t __rt6_peer_genid = ATOMIC_INIT(0); + +static u32 rt6_peer_genid(void) +{ + return atomic_read(&__rt6_peer_genid); +} + void rt6_bind_peer(struct rt6_info *rt, int create) { struct inet_peer *peer; @@ -247,6 +254,8 @@ void rt6_bind_peer(struct rt6_info *rt, int create) peer = inet_getpeer_v6(&rt->rt6i_dst.addr, create); if (peer && cmpxchg(&rt->rt6i_peer, NULL, peer) != NULL) inet_putpeer(peer); + else + rt->rt6i_peer_genid = rt6_peer_genid(); } static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, @@ -912,9 +921,14 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) rt = (struct rt6_info *) dst; - if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) + if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) { + if (rt->rt6i_peer_genid != rt6_peer_genid()) { + if (!rt->rt6i_peer) + rt6_bind_peer(rt, 0); + rt->rt6i_peer_genid = rt6_peer_genid(); + } return dst; - + } return NULL; } -- cgit v1.2.3 From c186794dbb466b45cf40f942f2d09d6d5b4b0e42 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Fri, 11 Feb 2011 11:08:00 +0100 Subject: block: share request flush fields with elevator_private Flush requests are never put on the IO scheduler. Convert request structure's elevator_private* into an array and have the flush fields share a union with it. Reclaim the space lost in 'struct request' by moving 'completion_data' back in the union with 'rb_node'. Signed-off-by: Mike Snitzer Acked-by: Vivek Goyal Acked-by: Tejun Heo Signed-off-by: Jens Axboe --- block/cfq-iosched.c | 18 +++++++++--------- block/elevator.c | 2 +- include/linux/blkdev.h | 23 ++++++++++++----------- 3 files changed, 22 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 4cd59b0d7c15..968455c57e1a 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -54,9 +54,9 @@ static const int cfq_hist_divisor = 4; #define CFQQ_SEEKY(cfqq) (hweight32(cfqq->seek_history) > 32/8) #define RQ_CIC(rq) \ - ((struct cfq_io_context *) (rq)->elevator_private) -#define RQ_CFQQ(rq) (struct cfq_queue *) ((rq)->elevator_private2) -#define RQ_CFQG(rq) (struct cfq_group *) ((rq)->elevator_private3) + ((struct cfq_io_context *) (rq)->elevator_private[0]) +#define RQ_CFQQ(rq) (struct cfq_queue *) ((rq)->elevator_private[1]) +#define RQ_CFQG(rq) (struct cfq_group *) ((rq)->elevator_private[2]) static struct kmem_cache *cfq_pool; static struct kmem_cache *cfq_ioc_pool; @@ -3589,12 +3589,12 @@ static void cfq_put_request(struct request *rq) put_io_context(RQ_CIC(rq)->ioc); - rq->elevator_private = NULL; - rq->elevator_private2 = NULL; + rq->elevator_private[0] = NULL; + rq->elevator_private[1] = NULL; /* Put down rq reference on cfqg */ cfq_put_cfqg(RQ_CFQG(rq)); - rq->elevator_private3 = NULL; + rq->elevator_private[2] = NULL; cfq_put_queue(cfqq); } @@ -3685,9 +3685,9 @@ new_queue: spin_unlock_irqrestore(q->queue_lock, flags); - rq->elevator_private = cic; - rq->elevator_private2 = cfqq; - rq->elevator_private3 = cfq_ref_get_cfqg(cfqq->cfqg); + rq->elevator_private[0] = cic; + rq->elevator_private[1] = cfqq; + rq->elevator_private[2] = cfq_ref_get_cfqg(cfqq->cfqg); return 0; queue_fail: diff --git a/block/elevator.c b/block/elevator.c index 270e0972eb9f..f98e92edc937 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -764,7 +764,7 @@ int elv_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask) if (e->ops->elevator_set_req_fn) return e->ops->elevator_set_req_fn(q, rq, gfp_mask); - rq->elevator_private = NULL; + rq->elevator_private[0] = NULL; return 0; } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 6d7e9afd08c3..12bb426949e9 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -99,25 +99,26 @@ struct request { /* * The rb_node is only used inside the io scheduler, requests * are pruned when moved to the dispatch queue. So let the - * flush fields share space with the rb_node. + * completion_data share space with the rb_node. */ union { struct rb_node rb_node; /* sort/lookup */ - struct { - unsigned int seq; - struct list_head list; - } flush; + void *completion_data; }; - void *completion_data; - /* * Three pointers are available for the IO schedulers, if they need - * more they have to dynamically allocate it. + * more they have to dynamically allocate it. Flush requests are + * never put on the IO scheduler. So let the flush fields share + * space with the three elevator_private pointers. */ - void *elevator_private; - void *elevator_private2; - void *elevator_private3; + union { + void *elevator_private[3]; + struct { + unsigned int seq; + struct list_head list; + } flush; + }; struct gendisk *rq_disk; unsigned long start_time; -- cgit v1.2.3 From b4dbde9da8ece42bbe4c70c26bac3b28dd6a3ddb Mon Sep 17 00:00:00 2001 From: Alan Ott Date: Tue, 18 Jan 2011 03:04:39 -0500 Subject: HID: Add Support for Setting and Getting Feature Reports from hidraw Per the HID Specification, Feature reports must be sent and received on the Configuration endpoint (EP 0) through the Set_Report/Get_Report interfaces. This patch adds two ioctls to hidraw to set and get feature reports to and from the device. Modifications were made to hidraw and usbhid. New hidraw ioctls: HIDIOCSFEATURE - Perform a Set_Report transfer of a Feature report. HIDIOCGFEATURE - Perform a Get_Report transfer of a Feature report. Signed-off-by: Alan Ott Signed-off-by: Antonio Ospite Signed-off-by: Jiri Kosina --- drivers/hid/hidraw.c | 106 +++++++++++++++++++++++++++++++++++++++--- drivers/hid/usbhid/hid-core.c | 35 ++++++++++++++ include/linux/hid.h | 3 ++ include/linux/hidraw.h | 3 ++ 4 files changed, 141 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 468e87b53ed2..8f06044a3e3e 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -102,15 +102,14 @@ out: } /* the first byte is expected to be a report number */ -static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +/* This function is to be called with the minors_lock mutex held */ +static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type) { unsigned int minor = iminor(file->f_path.dentry->d_inode); struct hid_device *dev; __u8 *buf; int ret = 0; - mutex_lock(&minors_lock); - if (!hidraw_table[minor]) { ret = -ENODEV; goto out; @@ -148,14 +147,92 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t goto out_free; } - ret = dev->hid_output_raw_report(dev, buf, count, HID_OUTPUT_REPORT); + ret = dev->hid_output_raw_report(dev, buf, count, report_type); out_free: kfree(buf); out: + return ret; +} + +/* the first byte is expected to be a report number */ +static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + ssize_t ret; + mutex_lock(&minors_lock); + ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT); mutex_unlock(&minors_lock); return ret; } + +/* This function performs a Get_Report transfer over the control endpoint + per section 7.2.1 of the HID specification, version 1.1. The first byte + of buffer is the report number to request, or 0x0 if the defice does not + use numbered reports. The report_type parameter can be HID_FEATURE_REPORT + or HID_INPUT_REPORT. This function is to be called with the minors_lock + mutex held. */ +static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type) +{ + unsigned int minor = iminor(file->f_path.dentry->d_inode); + struct hid_device *dev; + __u8 *buf; + int ret = 0, len; + unsigned char report_number; + + dev = hidraw_table[minor]->hid; + + if (!dev->hid_get_raw_report) { + ret = -ENODEV; + goto out; + } + + if (count > HID_MAX_BUFFER_SIZE) { + printk(KERN_WARNING "hidraw: pid %d passed too large report\n", + task_pid_nr(current)); + ret = -EINVAL; + goto out; + } + + if (count < 2) { + printk(KERN_WARNING "hidraw: pid %d passed too short report\n", + task_pid_nr(current)); + ret = -EINVAL; + goto out; + } + + buf = kmalloc(count * sizeof(__u8), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto out; + } + + /* Read the first byte from the user. This is the report number, + which is passed to dev->hid_get_raw_report(). */ + if (copy_from_user(&report_number, buffer, 1)) { + ret = -EFAULT; + goto out_free; + } + + ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type); + + if (ret < 0) + goto out_free; + + len = (ret < count) ? ret : count; + + if (copy_to_user(buffer, buf, len)) { + ret = -EFAULT; + goto out_free; + } + + ret = len; + +out_free: + kfree(buf); +out: + return ret; +} + static unsigned int hidraw_poll(struct file *file, poll_table *wait) { struct hidraw_list *list = file->private_data; @@ -295,7 +372,24 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, default: { struct hid_device *hid = dev->hid; - if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) { + if (_IOC_TYPE(cmd) != 'H') { + ret = -EINVAL; + break; + } + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE(0))) { + int len = _IOC_SIZE(cmd); + ret = hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT); + break; + } + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE(0))) { + int len = _IOC_SIZE(cmd); + ret = hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT); + break; + } + + /* Begin Read-only ioctls. */ + if (_IOC_DIR(cmd) != _IOC_READ) { ret = -EINVAL; break; } @@ -327,7 +421,7 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, -EFAULT : len; break; } - } + } ret = -ENOTTY; } diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index b336dd84036f..38c261a40c74 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -799,6 +799,40 @@ static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid) return 0; } +static int usbhid_get_raw_report(struct hid_device *hid, + unsigned char report_number, __u8 *buf, size_t count, + unsigned char report_type) +{ + struct usbhid_device *usbhid = hid->driver_data; + struct usb_device *dev = hid_to_usb_dev(hid); + struct usb_interface *intf = usbhid->intf; + struct usb_host_interface *interface = intf->cur_altsetting; + int skipped_report_id = 0; + int ret; + + /* Byte 0 is the report number. Report data starts at byte 1.*/ + buf[0] = report_number; + if (report_number == 0x0) { + /* Offset the return buffer by 1, so that the report ID + will remain in byte 0. */ + buf++; + count--; + skipped_report_id = 1; + } + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + HID_REQ_GET_REPORT, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + ((report_type + 1) << 8) | report_number, + interface->desc.bInterfaceNumber, buf, count, + USB_CTRL_SET_TIMEOUT); + + /* count also the report id */ + if (ret > 0 && skipped_report_id) + ret++; + + return ret; +} + static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count, unsigned char report_type) { @@ -1139,6 +1173,7 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * usb_set_intfdata(intf, hid); hid->ll_driver = &usb_hid_driver; + hid->hid_get_raw_report = usbhid_get_raw_report; hid->hid_output_raw_report = usbhid_output_raw_report; hid->ff_init = hid_pidff_init; #ifdef CONFIG_USB_HIDDEV diff --git a/include/linux/hid.h b/include/linux/hid.h index d91c25e253c8..e8ee0a93bda3 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -504,6 +504,9 @@ struct hid_device { /* device report descriptor */ struct hid_usage *, __s32); void (*hiddev_report_event) (struct hid_device *, struct hid_report *); + /* handler for raw input (Get_Report) data, used by hidraw */ + int (*hid_get_raw_report) (struct hid_device *, unsigned char, __u8 *, size_t, unsigned char); + /* handler for raw output data, used by hidraw */ int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t, unsigned char); diff --git a/include/linux/hidraw.h b/include/linux/hidraw.h index dd8d69269176..4b88e697c4e9 100644 --- a/include/linux/hidraw.h +++ b/include/linux/hidraw.h @@ -35,6 +35,9 @@ struct hidraw_devinfo { #define HIDIOCGRAWINFO _IOR('H', 0x03, struct hidraw_devinfo) #define HIDIOCGRAWNAME(len) _IOC(_IOC_READ, 'H', 0x04, len) #define HIDIOCGRAWPHYS(len) _IOC(_IOC_READ, 'H', 0x05, len) +/* The first byte of SFEATURE and GFEATURE is the report number */ +#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) +#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) #define HIDRAW_FIRST_MINOR 0 #define HIDRAW_MAX_DEVICES 64 -- cgit v1.2.3 From d033d526a465c4bb8a499a0b5df65b3e7cf4da6f Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Thu, 10 Feb 2011 14:40:01 +0000 Subject: ixgbe: DCB, implement 802.1Qaz routines Implements 802.1Qaz support for ixgbe driver. Additionally, this adds IEEE_8021QAZ_TSA_{} defines to dcbnl.h this is to avoid having to use cryptic numeric codes for the TSA type. Signed-off-by: John Fastabend Tested-by: Ross Brattain Signed-off-by: Jeff Kirsher --- drivers/net/ixgbe/ixgbe.h | 4 ++ drivers/net/ixgbe/ixgbe_dcb.c | 103 ++++++++++++++++++++++++++++++++++++ drivers/net/ixgbe/ixgbe_dcb.h | 4 ++ drivers/net/ixgbe/ixgbe_dcb_82598.c | 2 +- drivers/net/ixgbe/ixgbe_dcb_nl.c | 91 +++++++++++++++++++++++++++++++ drivers/net/ixgbe/ixgbe_main.c | 4 ++ include/linux/dcbnl.h | 5 ++ 7 files changed, 212 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ixgbe/ixgbe.h index 3b8c92463617..d04afdeee035 100644 --- a/drivers/net/ixgbe/ixgbe.h +++ b/drivers/net/ixgbe/ixgbe.h @@ -334,6 +334,10 @@ struct ixgbe_adapter { u16 bd_number; struct work_struct reset_task; struct ixgbe_q_vector *q_vector[MAX_MSIX_Q_VECTORS]; + + /* DCB parameters */ + struct ieee_pfc *ixgbe_ieee_pfc; + struct ieee_ets *ixgbe_ieee_ets; struct ixgbe_dcb_config dcb_cfg; struct ixgbe_dcb_config temp_dcb_cfg; u8 dcb_set_bitmap; diff --git a/drivers/net/ixgbe/ixgbe_dcb.c b/drivers/net/ixgbe/ixgbe_dcb.c index d9bb670ae258..13c962efbfc9 100644 --- a/drivers/net/ixgbe/ixgbe_dcb.c +++ b/drivers/net/ixgbe/ixgbe_dcb.c @@ -33,6 +33,42 @@ #include "ixgbe_dcb_82598.h" #include "ixgbe_dcb_82599.h" +/** + * ixgbe_ieee_credits - This calculates the ieee traffic class + * credits from the configured bandwidth percentages. Credits + * are the smallest unit programable into the underlying + * hardware. The IEEE 802.1Qaz specification do not use bandwidth + * groups so this is much simplified from the CEE case. + */ +s32 ixgbe_ieee_credits(__u8 *bw, __u16 *refill, __u16 *max, int max_frame) +{ + int min_percent = 100; + int min_credit, multiplier; + int i; + + min_credit = ((max_frame / 2) + DCB_CREDIT_QUANTUM - 1) / + DCB_CREDIT_QUANTUM; + + for (i = 0; i < MAX_TRAFFIC_CLASS; i++) { + if (bw[i] < min_percent && bw[i]) + min_percent = bw[i]; + } + + multiplier = (min_credit / min_percent) + 1; + + /* Find out the hw credits for each TC */ + for (i = 0; i < MAX_TRAFFIC_CLASS; i++) { + int val = min(bw[i] * multiplier, MAX_CREDIT_REFILL); + + if (val < min_credit) + val = min_credit; + refill[i] = val; + + max[i] = (bw[i] * MAX_CREDIT)/100; + } + return 0; +} + /** * ixgbe_dcb_calculate_tc_credits - Calculates traffic class credits * @ixgbe_dcb_config: Struct containing DCB settings. @@ -236,3 +272,70 @@ s32 ixgbe_dcb_hw_config(struct ixgbe_hw *hw, return ret; } +/* Helper routines to abstract HW specifics from DCB netlink ops */ +s32 ixgbe_dcb_hw_pfc_config(struct ixgbe_hw *hw, u8 pfc_en) +{ + int ret = -EINVAL; + + switch (hw->mac.type) { + case ixgbe_mac_82598EB: + ret = ixgbe_dcb_config_pfc_82598(hw, pfc_en); + break; + case ixgbe_mac_82599EB: + case ixgbe_mac_X540: + ret = ixgbe_dcb_config_pfc_82599(hw, pfc_en); + break; + default: + break; + } + return ret; +} + +s32 ixgbe_dcb_hw_ets_config(struct ixgbe_hw *hw, + u16 *refill, u16 *max, u8 *bwg_id, u8 *tsa) +{ + int i; + u8 prio_type[IEEE_8021QAZ_MAX_TCS]; + + /* Map TSA onto CEE prio type */ + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + switch (tsa[i]) { + case IEEE_8021QAZ_TSA_STRICT: + prio_type[i] = 2; + break; + case IEEE_8021QAZ_TSA_ETS: + prio_type[i] = 0; + break; + default: + /* Hardware only supports priority strict or + * ETS transmission selection algorithms if + * we receive some other value from dcbnl + * throw an error + */ + return -EINVAL; + } + } + + switch (hw->mac.type) { + case ixgbe_mac_82598EB: + ixgbe_dcb_config_rx_arbiter_82598(hw, refill, max, + prio_type); + ixgbe_dcb_config_tx_desc_arbiter_82598(hw, refill, max, + bwg_id, prio_type); + ixgbe_dcb_config_tx_data_arbiter_82598(hw, refill, max, + bwg_id, prio_type); + break; + case ixgbe_mac_82599EB: + case ixgbe_mac_X540: + ixgbe_dcb_config_rx_arbiter_82599(hw, refill, max, + bwg_id, prio_type); + ixgbe_dcb_config_tx_desc_arbiter_82599(hw, refill, max, + bwg_id, prio_type); + ixgbe_dcb_config_tx_data_arbiter_82599(hw, refill, max, + bwg_id, prio_type); + break; + default: + break; + } + return 0; +} diff --git a/drivers/net/ixgbe/ixgbe_dcb.h b/drivers/net/ixgbe/ixgbe_dcb.h index aa6cb5f9ebf4..4e4a641f3d53 100644 --- a/drivers/net/ixgbe/ixgbe_dcb.h +++ b/drivers/net/ixgbe/ixgbe_dcb.h @@ -150,10 +150,14 @@ struct ixgbe_dcb_config { void ixgbe_dcb_unpack_pfc(struct ixgbe_dcb_config *cfg, u8 *pfc_en); /* DCB credits calculation */ +s32 ixgbe_ieee_credits(__u8 *bw, __u16 *refill, __u16 *max, int max_frame); s32 ixgbe_dcb_calculate_tc_credits(struct ixgbe_hw *, struct ixgbe_dcb_config *, int, u8); /* DCB hw initialization */ +s32 ixgbe_dcb_hw_ets_config(struct ixgbe_hw *hw, + u16 *refill, u16 *max, u8 *bwg_id, u8 *prio_type); +s32 ixgbe_dcb_hw_pfc_config(struct ixgbe_hw *hw, u8 pfc_en); s32 ixgbe_dcb_hw_config(struct ixgbe_hw *, struct ixgbe_dcb_config *); /* DCB definitions for credit calculation */ diff --git a/drivers/net/ixgbe/ixgbe_dcb_82598.c b/drivers/net/ixgbe/ixgbe_dcb_82598.c index d1288060cbd0..2965edcdac7b 100644 --- a/drivers/net/ixgbe/ixgbe_dcb_82598.c +++ b/drivers/net/ixgbe/ixgbe_dcb_82598.c @@ -291,7 +291,7 @@ out: * Configure queue statistics registers, all queues belonging to same traffic * class uses a single set of queue statistics counters. */ -static s32 ixgbe_dcb_config_tc_stats_82598(struct ixgbe_hw *hw) +s32 ixgbe_dcb_config_tc_stats_82598(struct ixgbe_hw *hw) { u32 reg = 0; u8 i = 0; diff --git a/drivers/net/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ixgbe/ixgbe_dcb_nl.c index 6ab1f1abaa01..e75a3c91198d 100644 --- a/drivers/net/ixgbe/ixgbe_dcb_nl.c +++ b/drivers/net/ixgbe/ixgbe_dcb_nl.c @@ -606,7 +606,98 @@ static u8 ixgbe_dcbnl_setapp(struct net_device *netdev, return rval; } +static int ixgbe_dcbnl_ieee_getets(struct net_device *dev, + struct ieee_ets *ets) +{ + struct ixgbe_adapter *adapter = netdev_priv(dev); + struct ieee_ets *my_ets = adapter->ixgbe_ieee_ets; + + /* No IEEE PFC settings available */ + if (!my_ets) + return -EINVAL; + + ets->ets_cap = MAX_TRAFFIC_CLASS; + ets->cbs = my_ets->cbs; + memcpy(ets->tc_tx_bw, my_ets->tc_tx_bw, sizeof(ets->tc_tx_bw)); + memcpy(ets->tc_rx_bw, my_ets->tc_rx_bw, sizeof(ets->tc_rx_bw)); + memcpy(ets->tc_tsa, my_ets->tc_tsa, sizeof(ets->tc_tsa)); + memcpy(ets->prio_tc, my_ets->prio_tc, sizeof(ets->prio_tc)); + return 0; +} + +static int ixgbe_dcbnl_ieee_setets(struct net_device *dev, + struct ieee_ets *ets) +{ + struct ixgbe_adapter *adapter = netdev_priv(dev); + __u16 refill[IEEE_8021QAZ_MAX_TCS], max[IEEE_8021QAZ_MAX_TCS]; + int max_frame = dev->mtu + ETH_HLEN + ETH_FCS_LEN; + int err; + /* naively give each TC a bwg to map onto CEE hardware */ + __u8 bwg_id[IEEE_8021QAZ_MAX_TCS] = {0, 1, 2, 3, 4, 5, 6, 7}; + + if (!adapter->ixgbe_ieee_ets) { + adapter->ixgbe_ieee_ets = kmalloc(sizeof(struct ieee_ets), + GFP_KERNEL); + if (!adapter->ixgbe_ieee_ets) + return -ENOMEM; + } + + + memcpy(adapter->ixgbe_ieee_ets, ets, sizeof(*adapter->ixgbe_ieee_ets)); + + ixgbe_ieee_credits(ets->tc_tx_bw, refill, max, max_frame); + err = ixgbe_dcb_hw_ets_config(&adapter->hw, refill, max, + bwg_id, ets->tc_tsa); + return err; +} + +static int ixgbe_dcbnl_ieee_getpfc(struct net_device *dev, + struct ieee_pfc *pfc) +{ + struct ixgbe_adapter *adapter = netdev_priv(dev); + struct ieee_pfc *my_pfc = adapter->ixgbe_ieee_pfc; + int i; + + /* No IEEE PFC settings available */ + if (!my_pfc) + return -EINVAL; + + pfc->pfc_cap = MAX_TRAFFIC_CLASS; + pfc->pfc_en = my_pfc->pfc_en; + pfc->mbc = my_pfc->mbc; + pfc->delay = my_pfc->delay; + + for (i = 0; i < MAX_TRAFFIC_CLASS; i++) { + pfc->requests[i] = adapter->stats.pxoffrxc[i]; + pfc->indications[i] = adapter->stats.pxofftxc[i]; + } + + return 0; +} + +static int ixgbe_dcbnl_ieee_setpfc(struct net_device *dev, + struct ieee_pfc *pfc) +{ + struct ixgbe_adapter *adapter = netdev_priv(dev); + int err; + + if (!adapter->ixgbe_ieee_pfc) { + adapter->ixgbe_ieee_pfc = kmalloc(sizeof(struct ieee_pfc), + GFP_KERNEL); + if (!adapter->ixgbe_ieee_pfc) + return -ENOMEM; + } + + memcpy(adapter->ixgbe_ieee_pfc, pfc, sizeof(*adapter->ixgbe_ieee_pfc)); + err = ixgbe_dcb_hw_pfc_config(&adapter->hw, pfc->pfc_en); + return err; +} + const struct dcbnl_rtnl_ops dcbnl_ops = { + .ieee_getets = ixgbe_dcbnl_ieee_getets, + .ieee_setets = ixgbe_dcbnl_ieee_setets, + .ieee_getpfc = ixgbe_dcbnl_ieee_getpfc, + .ieee_setpfc = ixgbe_dcbnl_ieee_setpfc, .getstate = ixgbe_dcbnl_get_state, .setstate = ixgbe_dcbnl_set_state, .getpermhwaddr = ixgbe_dcbnl_get_perm_hw_addr, diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index 1e4814875945..c2e09b9cff46 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -5609,6 +5609,10 @@ static int __ixgbe_shutdown(struct pci_dev *pdev, bool *enable_wake) } ixgbe_clear_interrupt_scheme(adapter); +#ifdef CONFIG_DCB + kfree(adapter->ixgbe_ieee_pfc); + kfree(adapter->ixgbe_ieee_ets); +#endif #ifdef CONFIG_PM retval = pci_save_state(pdev); diff --git a/include/linux/dcbnl.h b/include/linux/dcbnl.h index 68cd248f6d3e..cd8d518efa3b 100644 --- a/include/linux/dcbnl.h +++ b/include/linux/dcbnl.h @@ -25,6 +25,11 @@ /* IEEE 802.1Qaz std supported values */ #define IEEE_8021QAZ_MAX_TCS 8 +#define IEEE_8021QAZ_TSA_STRICT 0 +#define IEEE_8021QAZ_TSA_CB_SHABER 1 +#define IEEE_8021QAZ_TSA_ETS 2 +#define IEEE_8021QAZ_TSA_VENDOR 255 + /* This structure contains the IEEE 802.1Qaz ETS managed object * * @willing: willing bit in ETS configuratin TLV -- cgit v1.2.3 From 868baf07b1a259f5f3803c1dc2777b6c358f83cf Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 10 Feb 2011 21:26:13 -0500 Subject: ftrace: Fix memory leak with function graph and cpu hotplug When the fuction graph tracer starts, it needs to make a special stack for each task to save the real return values of the tasks. All running tasks have this stack created, as well as any new tasks. On CPU hot plug, the new idle task will allocate a stack as well when init_idle() is called. The problem is that cpu hotplug does not create a new idle_task. Instead it uses the idle task that existed when the cpu went down. ftrace_graph_init_task() will add a new ret_stack to the task that is given to it. Because a clone will make the task have a stack of its parent it does not check if the task's ret_stack is already NULL or not. When the CPU hotplug code starts a CPU up again, it will allocate a new stack even though one already existed for it. The solution is to treat the idle_task specially. In fact, the function_graph code already does, just not at init_idle(). Instead of using the ftrace_graph_init_task() for the idle task, which that function expects the task to be a clone, have a separate ftrace_graph_init_idle_task(). Also, we will create a per_cpu ret_stack that is used by the idle task. When we call ftrace_graph_init_idle_task() it will check if the idle task's ret_stack is NULL, if it is, then it will assign it the per_cpu ret_stack. Reported-by: Benjamin Herrenschmidt Suggested-by: Peter Zijlstra Cc: Stable Tree Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 2 ++ kernel/sched.c | 2 +- kernel/trace/ftrace.c | 52 +++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 48 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index dcd6a7c3a435..ca29e03c1fac 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -428,6 +428,7 @@ extern void unregister_ftrace_graph(void); extern void ftrace_graph_init_task(struct task_struct *t); extern void ftrace_graph_exit_task(struct task_struct *t); +extern void ftrace_graph_init_idle_task(struct task_struct *t, int cpu); static inline int task_curr_ret_stack(struct task_struct *t) { @@ -451,6 +452,7 @@ static inline void unpause_graph_tracing(void) static inline void ftrace_graph_init_task(struct task_struct *t) { } static inline void ftrace_graph_exit_task(struct task_struct *t) { } +static inline void ftrace_graph_init_idle_task(struct task_struct *t, int cpu) { } static inline int register_ftrace_graph(trace_func_graph_ret_t retfunc, trace_func_graph_ent_t entryfunc) diff --git a/kernel/sched.c b/kernel/sched.c index 18d38e4ec7ba..fbe86cb04b61 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -5571,7 +5571,7 @@ void __cpuinit init_idle(struct task_struct *idle, int cpu) * The idle tasks have their own, simple scheduling class: */ idle->sched_class = &idle_sched_class; - ftrace_graph_init_task(idle); + ftrace_graph_init_idle_task(idle, cpu); } /* diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f3dadae83883..888b611897d3 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -3328,7 +3328,7 @@ static int start_graph_tracing(void) /* The cpu_boot init_task->ret_stack will never be freed */ for_each_online_cpu(cpu) { if (!idle_task(cpu)->ret_stack) - ftrace_graph_init_task(idle_task(cpu)); + ftrace_graph_init_idle_task(idle_task(cpu), cpu); } do { @@ -3418,6 +3418,49 @@ void unregister_ftrace_graph(void) mutex_unlock(&ftrace_lock); } +static DEFINE_PER_CPU(struct ftrace_ret_stack *, idle_ret_stack); + +static void +graph_init_task(struct task_struct *t, struct ftrace_ret_stack *ret_stack) +{ + atomic_set(&t->tracing_graph_pause, 0); + atomic_set(&t->trace_overrun, 0); + t->ftrace_timestamp = 0; + /* make curr_ret_stack visable before we add the ret_stack */ + smp_wmb(); + t->ret_stack = ret_stack; +} + +/* + * Allocate a return stack for the idle task. May be the first + * time through, or it may be done by CPU hotplug online. + */ +void ftrace_graph_init_idle_task(struct task_struct *t, int cpu) +{ + t->curr_ret_stack = -1; + /* + * The idle task has no parent, it either has its own + * stack or no stack at all. + */ + if (t->ret_stack) + WARN_ON(t->ret_stack != per_cpu(idle_ret_stack, cpu)); + + if (ftrace_graph_active) { + struct ftrace_ret_stack *ret_stack; + + ret_stack = per_cpu(idle_ret_stack, cpu); + if (!ret_stack) { + ret_stack = kmalloc(FTRACE_RETFUNC_DEPTH + * sizeof(struct ftrace_ret_stack), + GFP_KERNEL); + if (!ret_stack) + return; + per_cpu(idle_ret_stack, cpu) = ret_stack; + } + graph_init_task(t, ret_stack); + } +} + /* Allocate a return stack for newly created task */ void ftrace_graph_init_task(struct task_struct *t) { @@ -3433,12 +3476,7 @@ void ftrace_graph_init_task(struct task_struct *t) GFP_KERNEL); if (!ret_stack) return; - atomic_set(&t->tracing_graph_pause, 0); - atomic_set(&t->trace_overrun, 0); - t->ftrace_timestamp = 0; - /* make curr_ret_stack visable before we add the ret_stack */ - smp_wmb(); - t->ret_stack = ret_stack; + graph_init_task(t, ret_stack); } } -- cgit v1.2.3 From 63583cca745f440167bf27877182dc13e19d4bcf Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Tue, 18 Jan 2011 10:13:11 +0100 Subject: [SCSI] Add detailed SCSI I/O errors Instead of just passing 'EIO' for any I/O error we should be notifying the upper layers with more details about the cause of this error. Update the possible I/O errors to: - ENOLINK: Link failure between host and target - EIO: Retryable I/O error - EREMOTEIO: Non-retryable I/O error - EBADE: I/O error restricted to the I_T_L nexus 'Retryable' in this context means that an I/O error _might_ be restricted to the I_T_L nexus (vulgo: path), so retrying on another nexus / path might succeed. 'Non-retryable' in general refers to a target failure, so this error will always be generated regardless of the I_T_L nexus it was send on. I/O errors restricted to the I_T_L nexus might be retried on another nexus / path, but they should _not_ be queued if no paths are available. Signed-off-by: Hannes Reinecke Signed-off-by: Mike Snitzer Signed-off-by: James Bottomley --- drivers/scsi/scsi_error.c | 24 +++++++++++++++++------- drivers/scsi/scsi_lib.c | 28 ++++++++++++++++++++++++++-- include/scsi/scsi.h | 5 +++++ 3 files changed, 48 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 45c75649b9e0..991de3c15cfc 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -223,7 +223,7 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost, * @scmd: Cmd to have sense checked. * * Return value: - * SUCCESS or FAILED or NEEDS_RETRY + * SUCCESS or FAILED or NEEDS_RETRY or TARGET_ERROR * * Notes: * When a deferred error is detected the current command has @@ -326,17 +326,19 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) */ return SUCCESS; - /* these three are not supported */ + /* these are not supported */ case COPY_ABORTED: case VOLUME_OVERFLOW: case MISCOMPARE: - return SUCCESS; + case BLANK_CHECK: + case DATA_PROTECT: + return TARGET_ERROR; case MEDIUM_ERROR: if (sshdr.asc == 0x11 || /* UNRECOVERED READ ERR */ sshdr.asc == 0x13 || /* AMNF DATA FIELD */ sshdr.asc == 0x14) { /* RECORD NOT FOUND */ - return SUCCESS; + return TARGET_ERROR; } return NEEDS_RETRY; @@ -344,11 +346,9 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) if (scmd->device->retry_hwerror) return ADD_TO_MLQUEUE; else - return SUCCESS; + return TARGET_ERROR; case ILLEGAL_REQUEST: - case BLANK_CHECK: - case DATA_PROTECT: default: return SUCCESS; } @@ -787,6 +787,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, case SUCCESS: case NEEDS_RETRY: case FAILED: + case TARGET_ERROR: break; case ADD_TO_MLQUEUE: rtn = NEEDS_RETRY; @@ -1469,6 +1470,14 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) rtn = scsi_check_sense(scmd); if (rtn == NEEDS_RETRY) goto maybe_retry; + else if (rtn == TARGET_ERROR) { + /* + * Need to modify host byte to signal a + * permanent target failure + */ + scmd->result |= (DID_TARGET_FAILURE << 16); + rtn = SUCCESS; + } /* if rtn == FAILED, we have no sense information; * returning FAILED will wake the error handler thread * to collect the sense and redo the decide @@ -1486,6 +1495,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) case RESERVATION_CONFLICT: sdev_printk(KERN_INFO, scmd->device, "reservation conflict\n"); + scmd->result |= (DID_NEXUS_FAILURE << 16); return SUCCESS; /* causes immediate i/o error */ default: return FAILED; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 9045c52abd25..8d4ef8efa3cd 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -667,6 +667,30 @@ void scsi_release_buffers(struct scsi_cmnd *cmd) } EXPORT_SYMBOL(scsi_release_buffers); +static int __scsi_error_from_host_byte(struct scsi_cmnd *cmd, int result) +{ + int error = 0; + + switch(host_byte(result)) { + case DID_TRANSPORT_FAILFAST: + error = -ENOLINK; + break; + case DID_TARGET_FAILURE: + cmd->result |= (DID_OK << 16); + error = -EREMOTEIO; + break; + case DID_NEXUS_FAILURE: + cmd->result |= (DID_OK << 16); + error = -EBADE; + break; + default: + error = -EIO; + break; + } + + return error; +} + /* * Function: scsi_io_completion() * @@ -737,7 +761,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) req->sense_len = len; } if (!sense_deferred) - error = -EIO; + error = __scsi_error_from_host_byte(cmd, result); } req->resid_len = scsi_get_resid(cmd); @@ -796,7 +820,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) if (scsi_end_request(cmd, error, good_bytes, result == 0) == NULL) return; - error = -EIO; + error = __scsi_error_from_host_byte(cmd, result); if (host_byte(result) == DID_RESET) { /* Third party bus reset or reset for error recovery diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index 648d23358038..ead8dd054480 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -434,6 +434,10 @@ static inline int scsi_is_wlun(unsigned int lun) * recover the link. Transport class will * retry or fail IO */ #define DID_TRANSPORT_FAILFAST 0x0f /* Transport class fastfailed the io */ +#define DID_TARGET_FAILURE 0x10 /* Permanent target failure, do not retry on + * other paths */ +#define DID_NEXUS_FAILURE 0x11 /* Permanent nexus failure, retry on other + * paths might yield different results */ #define DRIVER_OK 0x00 /* Driver status */ /* @@ -463,6 +467,7 @@ static inline int scsi_is_wlun(unsigned int lun) #define TIMEOUT_ERROR 0x2007 #define SCSI_RETURN_NOT_HANDLED 0x2008 #define FAST_IO_FAIL 0x2009 +#define TARGET_ERROR 0x200A /* * Midlevel queue return values. -- cgit v1.2.3 From 96ad846445ae33dcae1805b68752e3d5c840e3ed Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Fri, 28 Jan 2011 16:04:02 -0800 Subject: [SCSI] libfc: add hook for FC-4 provider registration Allow FC-4 provider modules to hook into libfc, mostly for targets. This should allow any FC-4 module to handle PRLI requests and maintain process-association states. Each provider registers its ops with libfc and then will be called for any incoming PRLI for that FC-4 type on any instance. The provider can decide whether to handle that particular instance using any method it likes, such as ACLs or other configuration information. A count is kept of the number of successful PRLIs from the remote port. Providers are called back with an implicit PRLO when the remote port is about to be deleted or has been reset. fc_lport_recv_req() now sends incoming FC-4 requests to FC-4 providers, and there is a built-in provider always registered for handling incoming ELS requests. The call to provider recv() routines uses rcu_read_lock() so that providers aren't removed during the call. That lock is very cheap and shouldn't affect any performance on ELS requests. Providers can rely on the RCU lock to protect a session lookup as well. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/libfc/fc_libfc.c | 60 +++++++++++++++++++ drivers/scsi/libfc/fc_libfc.h | 11 ++++ drivers/scsi/libfc/fc_lport.c | 65 ++++++++++++++++++--- drivers/scsi/libfc/fc_rport.c | 133 +++++++++++++++++++++++++++++++++--------- include/scsi/libfc.h | 26 +++++++++ 5 files changed, 259 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c index 6a48c28e4420..ae3abef6523e 100644 --- a/drivers/scsi/libfc/fc_libfc.c +++ b/drivers/scsi/libfc/fc_libfc.c @@ -35,6 +35,23 @@ unsigned int fc_debug_logging; module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); +DEFINE_MUTEX(fc_prov_mutex); + +/* + * Providers which primarily send requests and PRLIs. + */ +struct fc4_prov *fc_active_prov[FC_FC4_PROV_SIZE] = { + [0] = &fc_rport_t0_prov, + [FC_TYPE_FCP] = &fc_rport_fcp_init, +}; + +/* + * Providers which receive requests. + */ +struct fc4_prov *fc_passive_prov[FC_FC4_PROV_SIZE] = { + [FC_TYPE_ELS] = &fc_lport_els_prov, +}; + /** * libfc_init() - Initialize libfc.ko */ @@ -210,3 +227,46 @@ void fc_fill_reply_hdr(struct fc_frame *fp, const struct fc_frame *in_fp, fc_fill_hdr(fp, in_fp, r_ctl, FC_FCTL_RESP, 0, parm_offset); } EXPORT_SYMBOL(fc_fill_reply_hdr); + +/** + * fc_fc4_register_provider() - register FC-4 upper-level provider. + * @type: FC-4 type, such as FC_TYPE_FCP + * @prov: structure describing provider including ops vector. + * + * Returns 0 on success, negative error otherwise. + */ +int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *prov) +{ + struct fc4_prov **prov_entry; + int ret = 0; + + if (type >= FC_FC4_PROV_SIZE) + return -EINVAL; + mutex_lock(&fc_prov_mutex); + prov_entry = (prov->recv ? fc_passive_prov : fc_active_prov) + type; + if (*prov_entry) + ret = -EBUSY; + else + *prov_entry = prov; + mutex_unlock(&fc_prov_mutex); + return ret; +} +EXPORT_SYMBOL(fc_fc4_register_provider); + +/** + * fc_fc4_deregister_provider() - deregister FC-4 upper-level provider. + * @type: FC-4 type, such as FC_TYPE_FCP + * @prov: structure describing provider including ops vector. + */ +void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov) +{ + BUG_ON(type >= FC_FC4_PROV_SIZE); + mutex_lock(&fc_prov_mutex); + if (prov->recv) + rcu_assign_pointer(fc_passive_prov[type], NULL); + else + rcu_assign_pointer(fc_active_prov[type], NULL); + mutex_unlock(&fc_prov_mutex); + synchronize_rcu(); +} +EXPORT_SYMBOL(fc_fc4_deregister_provider); diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h index eea0c3541b71..205de285e456 100644 --- a/drivers/scsi/libfc/fc_libfc.h +++ b/drivers/scsi/libfc/fc_libfc.h @@ -93,6 +93,17 @@ extern unsigned int fc_debug_logging; printk(KERN_INFO "host%u: scsi: " fmt, \ (lport)->host->host_no, ##args)) +/* + * FC-4 Providers. + */ +extern struct fc4_prov *fc_active_prov[]; /* providers without recv */ +extern struct fc4_prov *fc_passive_prov[]; /* providers with recv */ +extern struct mutex fc_prov_mutex; /* lock over table changes */ + +extern struct fc4_prov fc_rport_t0_prov; /* type 0 provider */ +extern struct fc4_prov fc_lport_els_prov; /* ELS provider */ +extern struct fc4_prov fc_rport_fcp_init; /* FCP initiator provider */ + /* * Set up direct-data placement for this I/O request */ diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index c5a10f94f845..e2cd087e71b2 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -849,7 +849,7 @@ out: } /** - * fc_lport_recv_req() - The generic lport request handler + * fc_lport_recv_els_req() - The generic lport ELS request handler * @lport: The local port that received the request * @fp: The request frame * @@ -859,9 +859,9 @@ out: * Locking Note: This function should not be called with the lport * lock held becuase it will grab the lock. */ -static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp) +static void fc_lport_recv_els_req(struct fc_lport *lport, + struct fc_frame *fp) { - struct fc_frame_header *fh = fc_frame_header_get(fp); void (*recv)(struct fc_lport *, struct fc_frame *); mutex_lock(&lport->lp_mutex); @@ -873,8 +873,7 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp) */ if (!lport->link_up) fc_frame_free(fp); - else if (fh->fh_type == FC_TYPE_ELS && - fh->fh_r_ctl == FC_RCTL_ELS_REQ) { + else { /* * Check opcode. */ @@ -903,14 +902,62 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp) } recv(lport, fp); - } else { - FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)\n", - fr_eof(fp)); - fc_frame_free(fp); } mutex_unlock(&lport->lp_mutex); } +static int fc_lport_els_prli(struct fc_rport_priv *rdata, u32 spp_len, + const struct fc_els_spp *spp_in, + struct fc_els_spp *spp_out) +{ + return FC_SPP_RESP_INVL; +} + +struct fc4_prov fc_lport_els_prov = { + .prli = fc_lport_els_prli, + .recv = fc_lport_recv_els_req, +}; + +/** + * fc_lport_recv_req() - The generic lport request handler + * @lport: The lport that received the request + * @fp: The frame the request is in + * + * Locking Note: This function should not be called with the lport + * lock held becuase it may grab the lock. + */ +static void fc_lport_recv_req(struct fc_lport *lport, + struct fc_frame *fp) +{ + struct fc_frame_header *fh = fc_frame_header_get(fp); + struct fc_seq *sp = fr_seq(fp); + struct fc4_prov *prov; + + /* + * Use RCU read lock and module_lock to be sure module doesn't + * deregister and get unloaded while we're calling it. + * try_module_get() is inlined and accepts a NULL parameter. + * Only ELSes and FCP target ops should come through here. + * The locking is unfortunate, and a better scheme is being sought. + */ + + rcu_read_lock(); + if (fh->fh_type >= FC_FC4_PROV_SIZE) + goto drop; + prov = rcu_dereference(fc_passive_prov[fh->fh_type]); + if (!prov || !try_module_get(prov->module)) + goto drop; + rcu_read_unlock(); + prov->recv(lport, fp); + module_put(prov->module); + return; +drop: + rcu_read_unlock(); + FC_LPORT_DBG(lport, "dropping unexpected frame type %x\n", fh->fh_type); + fc_frame_free(fp); + lport->tt.exch_done(sp); +} + /** * fc_lport_reset() - Reset a local port * @lport: The local port which should be reset diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index 309e3e713ea1..a92954c1f42f 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -257,6 +257,8 @@ static void fc_rport_work(struct work_struct *work) struct fc_rport_operations *rport_ops; struct fc_rport_identifiers ids; struct fc_rport *rport; + struct fc4_prov *prov; + u8 type; mutex_lock(&rdata->rp_mutex); event = rdata->event; @@ -306,6 +308,15 @@ static void fc_rport_work(struct work_struct *work) case RPORT_EV_FAILED: case RPORT_EV_LOGO: case RPORT_EV_STOP: + if (rdata->prli_count) { + mutex_lock(&fc_prov_mutex); + for (type = 1; type < FC_FC4_PROV_SIZE; type++) { + prov = fc_passive_prov[type]; + if (prov && prov->prlo) + prov->prlo(rdata); + } + mutex_unlock(&fc_prov_mutex); + } port_id = rdata->ids.port_id; mutex_unlock(&rdata->rp_mutex); @@ -1643,9 +1654,9 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata, unsigned int len; unsigned int plen; enum fc_els_spp_resp resp; + enum fc_els_spp_resp passive; struct fc_seq_els_data rjt_data; - u32 fcp_parm; - u32 roles = FC_RPORT_ROLE_UNKNOWN; + struct fc4_prov *prov; FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n", fc_rport_state(rdata)); @@ -1679,46 +1690,41 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata, pp->prli.prli_len = htons(len); len -= sizeof(struct fc_els_prli); - /* reinitialize remote port roles */ - rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN; - /* * Go through all the service parameter pages and build * response. If plen indicates longer SPP than standard, * use that. The entire response has been pre-cleared above. */ spp = &pp->spp; + mutex_lock(&fc_prov_mutex); while (len >= plen) { spp->spp_type = rspp->spp_type; spp->spp_type_ext = rspp->spp_type_ext; - spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR; - resp = FC_SPP_RESP_ACK; - - switch (rspp->spp_type) { - case 0: /* common to all FC-4 types */ - break; - case FC_TYPE_FCP: - fcp_parm = ntohl(rspp->spp_params); - if (fcp_parm & FCP_SPPF_RETRY) - rdata->flags |= FC_RP_FLAGS_RETRY; - rdata->supported_classes = FC_COS_CLASS3; - if (fcp_parm & FCP_SPPF_INIT_FCN) - roles |= FC_RPORT_ROLE_FCP_INITIATOR; - if (fcp_parm & FCP_SPPF_TARG_FCN) - roles |= FC_RPORT_ROLE_FCP_TARGET; - rdata->ids.roles = roles; - - spp->spp_params = htonl(lport->service_params); - break; - default: - resp = FC_SPP_RESP_INVL; - break; + resp = 0; + + if (rspp->spp_type < FC_FC4_PROV_SIZE) { + prov = fc_active_prov[rspp->spp_type]; + if (prov) + resp = prov->prli(rdata, plen, rspp, spp); + prov = fc_passive_prov[rspp->spp_type]; + if (prov) { + passive = prov->prli(rdata, plen, rspp, spp); + if (!resp || passive == FC_SPP_RESP_ACK) + resp = passive; + } + } + if (!resp) { + if (spp->spp_flags & FC_SPP_EST_IMG_PAIR) + resp |= FC_SPP_RESP_CONF; + else + resp |= FC_SPP_RESP_INVL; } spp->spp_flags |= resp; len -= plen; rspp = (struct fc_els_spp *)((char *)rspp + plen); spp = (struct fc_els_spp *)((char *)spp + plen); } + mutex_unlock(&fc_prov_mutex); /* * Send LS_ACC. If this fails, the originator should retry. @@ -1887,6 +1893,79 @@ int fc_rport_init(struct fc_lport *lport) } EXPORT_SYMBOL(fc_rport_init); +/** + * fc_rport_fcp_prli() - Handle incoming PRLI for the FCP initiator. + * @rdata: remote port private + * @spp_len: service parameter page length + * @rspp: received service parameter page + * @spp: response service parameter page + * + * Returns the value for the response code to be placed in spp_flags; + * Returns 0 if not an initiator. + */ +static int fc_rport_fcp_prli(struct fc_rport_priv *rdata, u32 spp_len, + const struct fc_els_spp *rspp, + struct fc_els_spp *spp) +{ + struct fc_lport *lport = rdata->local_port; + u32 fcp_parm; + + fcp_parm = ntohl(rspp->spp_params); + rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN; + if (fcp_parm & FCP_SPPF_INIT_FCN) + rdata->ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR; + if (fcp_parm & FCP_SPPF_TARG_FCN) + rdata->ids.roles |= FC_RPORT_ROLE_FCP_TARGET; + if (fcp_parm & FCP_SPPF_RETRY) + rdata->flags |= FC_RP_FLAGS_RETRY; + rdata->supported_classes = FC_COS_CLASS3; + + if (!(lport->service_params & FC_RPORT_ROLE_FCP_INITIATOR)) + return 0; + + spp->spp_flags |= rspp->spp_flags & FC_SPP_EST_IMG_PAIR; + + /* + * OR in our service parameters with other providers (target), if any. + */ + fcp_parm = ntohl(spp->spp_params); + spp->spp_params = htonl(fcp_parm | lport->service_params); + return FC_SPP_RESP_ACK; +} + +/* + * FC-4 provider ops for FCP initiator. + */ +struct fc4_prov fc_rport_fcp_init = { + .prli = fc_rport_fcp_prli, +}; + +/** + * fc_rport_t0_prli() - Handle incoming PRLI parameters for type 0 + * @rdata: remote port private + * @spp_len: service parameter page length + * @rspp: received service parameter page + * @spp: response service parameter page + */ +static int fc_rport_t0_prli(struct fc_rport_priv *rdata, u32 spp_len, + const struct fc_els_spp *rspp, + struct fc_els_spp *spp) +{ + if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR) + return FC_SPP_RESP_INVL; + return FC_SPP_RESP_ACK; +} + +/* + * FC-4 provider ops for type 0 service parameters. + * + * This handles the special case of type 0 which is always successful + * but doesn't do anything otherwise. + */ +struct fc4_prov fc_rport_t0_prov = { + .prli = fc_rport_t0_prli, +}; + /** * fc_setup_rport() - Initialize the rport_event_queue */ diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index f53c8e31d5fb..3ae2a760b4f3 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -35,6 +35,8 @@ #include +#define FC_FC4_PROV_SIZE (FC_TYPE_FCP + 1) /* size of tables */ + /* * libfc error codes */ @@ -179,6 +181,7 @@ struct fc_rport_libfc_priv { * @rp_mutex: The mutex that protects the remote port * @retry_work: Handle for retries * @event_callback: Callback when READY, FAILED or LOGO states complete + * @prli_count: Count of open PRLI sessions in providers * @rcu: Structure used for freeing in an RCU-safe manner */ struct fc_rport_priv { @@ -202,6 +205,7 @@ struct fc_rport_priv { struct list_head peers; struct work_struct event_work; u32 supported_classes; + u16 prli_count; struct rcu_head rcu; }; @@ -848,6 +852,28 @@ struct fc_lport { struct delayed_work retry_work; }; +/** + * struct fc4_prov - FC-4 provider registration + * @prli: Handler for incoming PRLI + * @prlo: Handler for session reset + * @recv: Handler for incoming request + * @module: Pointer to module. May be NULL. + */ +struct fc4_prov { + int (*prli)(struct fc_rport_priv *, u32 spp_len, + const struct fc_els_spp *spp_in, + struct fc_els_spp *spp_out); + void (*prlo)(struct fc_rport_priv *); + void (*recv)(struct fc_lport *, struct fc_frame *); + struct module *module; +}; + +/* + * Register FC-4 provider with libfc. + */ +int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *); +void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *); + /* * FC_LPORT HELPER FUNCTIONS *****************************/ -- cgit v1.2.3 From 1a5c2d7e5c8ef239804cb08b68363e0cd2f74a3d Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Fri, 28 Jan 2011 16:04:08 -0800 Subject: [SCSI] libfc: add method for setting handler for incoming exchange Add a method for setting handler for incoming exchange. For multi-sequence exchanges, this allows the target driver to add a response handler for handling subsequent sequences, and exchange manager resets. The new function is called fc_seq_set_resp(). Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/libfc/fc_exch.c | 19 +++++++++++++++++++ include/scsi/libfc.h | 10 ++++++++++ 2 files changed, 29 insertions(+) (limited to 'include') diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index e0b5b15c355b..1f124c0351b4 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -558,6 +558,22 @@ static struct fc_seq *fc_seq_start_next(struct fc_seq *sp) return sp; } +/* + * Set the response handler for the exchange associated with a sequence. + */ +static void fc_seq_set_resp(struct fc_seq *sp, + void (*resp)(struct fc_seq *, struct fc_frame *, + void *), + void *arg) +{ + struct fc_exch *ep = fc_seq_exch(sp); + + spin_lock_bh(&ep->ex_lock); + ep->resp = resp; + ep->arg = arg; + spin_unlock_bh(&ep->ex_lock); +} + /** * fc_seq_exch_abort() - Abort an exchange and sequence * @req_sp: The sequence to be aborted @@ -2329,6 +2345,9 @@ int fc_exch_init(struct fc_lport *lport) if (!lport->tt.seq_start_next) lport->tt.seq_start_next = fc_seq_start_next; + if (!lport->tt.seq_set_resp) + lport->tt.seq_set_resp = fc_seq_set_resp; + if (!lport->tt.exch_seq_send) lport->tt.exch_seq_send = fc_exch_seq_send; diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index 3ae2a760b4f3..3b8f5d83611b 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -554,6 +554,16 @@ struct libfc_function_template { */ struct fc_seq *(*seq_start_next)(struct fc_seq *); + /* + * Set a response handler for the exchange of the sequence. + * + * STATUS: OPTIONAL + */ + void (*seq_set_resp)(struct fc_seq *sp, + void (*resp)(struct fc_seq *, struct fc_frame *, + void *), + void *arg); + /* * Assign a sequence for an incoming request frame. * -- cgit v1.2.3 From baf9fdf076a8976431b5de565aef2b98816caecf Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Fri, 28 Jan 2011 16:04:13 -0800 Subject: [SCSI] libfc: add local port hook for provider session lookup The target provider needs a per-instance lookup table or other way to lookup sessions quickly without going through a linear list or serializing too much. Add a simple void * array indexed by FC-4 type to the fc_lport. Signed-off-by: Joe Eykholt Committed-by: Nicholas A. Bellinger Signed-off-by: Robert Love Signed-off-by: James Bottomley --- include/scsi/libfc.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index 3b8f5d83611b..a9aff25a399b 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -805,6 +805,7 @@ struct fc_disc { * @lp_mutex: Mutex to protect the local port * @list: Handle for list of local ports * @retry_work: Handle to local port for delayed retry context + * @prov: Pointers available for use by passive FC-4 providers */ struct fc_lport { /* Associations */ @@ -860,6 +861,7 @@ struct fc_lport { struct mutex lp_mutex; struct list_head list; struct delayed_work retry_work; + void *prov[FC_FC4_PROV_SIZE]; }; /** -- cgit v1.2.3 From 70d53b046a6221e3ceb3bd8eaa807ef6a1c53762 Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Fri, 28 Jan 2011 16:04:18 -0800 Subject: [SCSI] libfc: add hook to notify providers of local port changes When an SCST provider is registered, it needs to know what local ports are available for configuration as targets. Add a notifier chain that is invoked when any local port that is added or deleted. Maintain a global list of local ports and add an interator function that calls a given function for every existing local port. This is used when first loading a provider. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/libfc/fc_libfc.c | 41 +++++++++++++++++++++++++++++++++++++++++ drivers/scsi/libfc/fc_libfc.h | 2 ++ drivers/scsi/libfc/fc_lport.c | 2 ++ include/scsi/libfc.h | 14 +++++++++++++- 4 files changed, 58 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c index ae3abef6523e..5e40dab8f919 100644 --- a/drivers/scsi/libfc/fc_libfc.c +++ b/drivers/scsi/libfc/fc_libfc.c @@ -36,6 +36,10 @@ module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); DEFINE_MUTEX(fc_prov_mutex); +static LIST_HEAD(fc_local_ports); +struct blocking_notifier_head fc_lport_notifier_head = + BLOCKING_NOTIFIER_INIT(fc_lport_notifier_head); +EXPORT_SYMBOL(fc_lport_notifier_head); /* * Providers which primarily send requests and PRLIs. @@ -228,6 +232,17 @@ void fc_fill_reply_hdr(struct fc_frame *fp, const struct fc_frame *in_fp, } EXPORT_SYMBOL(fc_fill_reply_hdr); +void fc_lport_iterate(void (*notify)(struct fc_lport *, void *), void *arg) +{ + struct fc_lport *lport; + + mutex_lock(&fc_prov_mutex); + list_for_each_entry(lport, &fc_local_ports, lport_list) + notify(lport, arg); + mutex_unlock(&fc_prov_mutex); +} +EXPORT_SYMBOL(fc_lport_iterate); + /** * fc_fc4_register_provider() - register FC-4 upper-level provider. * @type: FC-4 type, such as FC_TYPE_FCP @@ -270,3 +285,29 @@ void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov) synchronize_rcu(); } EXPORT_SYMBOL(fc_fc4_deregister_provider); + +/** + * fc_fc4_add_lport() - add new local port to list and run notifiers. + * @lport: The new local port. + */ +void fc_fc4_add_lport(struct fc_lport *lport) +{ + mutex_lock(&fc_prov_mutex); + list_add_tail(&lport->lport_list, &fc_local_ports); + blocking_notifier_call_chain(&fc_lport_notifier_head, + FC_LPORT_EV_ADD, lport); + mutex_unlock(&fc_prov_mutex); +} + +/** + * fc_fc4_del_lport() - remove local port from list and run notifiers. + * @lport: The new local port. + */ +void fc_fc4_del_lport(struct fc_lport *lport) +{ + mutex_lock(&fc_prov_mutex); + list_del(&lport->lport_list); + blocking_notifier_call_chain(&fc_lport_notifier_head, + FC_LPORT_EV_DEL, lport); + mutex_unlock(&fc_prov_mutex); +} diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h index 205de285e456..8496f7020b97 100644 --- a/drivers/scsi/libfc/fc_libfc.h +++ b/drivers/scsi/libfc/fc_libfc.h @@ -123,6 +123,8 @@ void fc_destroy_fcp(void); * Internal libfc functions */ const char *fc_els_resp_type(struct fc_frame *); +extern void fc_fc4_add_lport(struct fc_lport *); +extern void fc_fc4_del_lport(struct fc_lport *); /* * Copies a buffer into an sg list diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index e2cd087e71b2..e0ef81426c33 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -633,6 +633,7 @@ int fc_lport_destroy(struct fc_lport *lport) lport->tt.fcp_abort_io(lport); lport->tt.disc_stop_final(lport); lport->tt.exch_mgr_reset(lport, 0, 0); + fc_fc4_del_lport(lport); return 0; } EXPORT_SYMBOL(fc_lport_destroy); @@ -1633,6 +1634,7 @@ int fc_lport_init(struct fc_lport *lport) fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_1GBIT; if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT) fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT; + fc_fc4_add_lport(lport); return 0; } diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index a9aff25a399b..79d1c76b4269 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -763,6 +763,15 @@ struct fc_disc { enum fc_disc_event); }; +/* + * Local port notifier and events. + */ +extern struct blocking_notifier_head fc_lport_notifier_head; +enum fc_lport_event { + FC_LPORT_EV_ADD, + FC_LPORT_EV_DEL, +}; + /** * struct fc_lport - Local port * @host: The SCSI host associated with a local port @@ -803,9 +812,10 @@ struct fc_disc { * @lso_max: The maximum large offload send size * @fcts: FC-4 type mask * @lp_mutex: Mutex to protect the local port - * @list: Handle for list of local ports + * @list: Linkage on list of vport peers * @retry_work: Handle to local port for delayed retry context * @prov: Pointers available for use by passive FC-4 providers + * @lport_list: Linkage on module-wide list of local ports */ struct fc_lport { /* Associations */ @@ -862,6 +872,7 @@ struct fc_lport { struct list_head list; struct delayed_work retry_work; void *prov[FC_FC4_PROV_SIZE]; + struct list_head lport_list; }; /** @@ -1016,6 +1027,7 @@ struct fc_lport *libfc_vport_create(struct fc_vport *, int privsize); struct fc_lport *fc_vport_id_lookup(struct fc_lport *, u32 port_id); int fc_lport_bsg_request(struct fc_bsg_job *); void fc_lport_set_local_id(struct fc_lport *, u32 port_id); +void fc_lport_iterate(void (*func)(struct fc_lport *, void *), void *); /* * REMOTE PORT LAYER -- cgit v1.2.3 From 62bdb6455e8326f864ae1b43b4c4db7f630edc1c Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Fri, 28 Jan 2011 16:04:34 -0800 Subject: [SCSI] libfc: export seq_release() for users of seq_assign() Target modules using lport->tt.seq_assign() get a hold on the exchange but have no way of releasing it. Add that. Signed-off-by: Joe Eykholt Signed-off-by: James Bottomley --- drivers/scsi/libfc/fc_exch.c | 14 ++++++++++++++ include/scsi/libfc.h | 7 +++++++ 2 files changed, 21 insertions(+) (limited to 'include') diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index 1f124c0351b4..a3d640289dcc 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -1282,6 +1282,8 @@ free: * @fp: The request frame * * On success, the sequence pointer will be returned and also in fr_seq(@fp). + * A reference will be held on the exchange/sequence for the caller, which + * must call fc_seq_release(). */ static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp) { @@ -1298,6 +1300,15 @@ static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp) return fr_seq(fp); } +/** + * fc_seq_release() - Release the hold + * @sp: The sequence. + */ +static void fc_seq_release(struct fc_seq *sp) +{ + fc_exch_release(fc_seq_exch(sp)); +} + /** * fc_exch_recv_req() - Handler for an incoming request * @lport: The local port that received the request @@ -2369,6 +2380,9 @@ int fc_exch_init(struct fc_lport *lport) if (!lport->tt.seq_assign) lport->tt.seq_assign = fc_seq_assign; + if (!lport->tt.seq_release) + lport->tt.seq_release = fc_seq_release; + return 0; } EXPORT_SYMBOL(fc_exch_init); diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index 79d1c76b4269..6d64e44bc3bf 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -571,6 +571,13 @@ struct libfc_function_template { */ struct fc_seq *(*seq_assign)(struct fc_lport *, struct fc_frame *); + /* + * Release the reference on the sequence returned by seq_assign(). + * + * STATUS: OPTIONAL + */ + void (*seq_release)(struct fc_seq *); + /* * Reset an exchange manager, completing all sequences and exchanges. * If s_id is non-zero, reset only exchanges originating from that FID. -- cgit v1.2.3 From 0ade7d290b6aa8b1626a4077b853c02cd12415c2 Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Fri, 28 Jan 2011 16:04:50 -0800 Subject: [SCSI] libfcoe: add fcoe_transport structure defines to include/scsi/libfcoe.h add the fcoe_transport struct to the common libfcoe.h header so all fcoe transport provides can use it to attach itself as an fcoe transport. This is the header part, and the next patch will be the transport code itself. Signed-off-by: Yi Zou Signed-off-by: Bhanu Prakash Gollapudi Signed-off-by: Robert Love Signed-off-by: James Bottomley --- include/scsi/libfcoe.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'include') diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index feb6a94c90ea..efb6ae5b94ad 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -231,5 +231,53 @@ static inline bool is_fip_mode(struct fcoe_ctlr *fip) return fip->state == FIP_ST_ENABLED; } +/* helper for FCoE SW HBA drivers, can include subven and subdev if needed. The + * modpost would use pci_device_id table to auto-generate formatted module alias + * into the corresponding .mod.c file, but there may or may not be a pci device + * id table for FCoE drivers so we use the following helper for build the fcoe + * driver module alias. + */ +#define MODULE_ALIAS_FCOE_PCI(ven, dev) \ + MODULE_ALIAS("fcoe-pci:" \ + "v" __stringify(ven) \ + "d" __stringify(dev) "sv*sd*bc*sc*i*") + +/* the name of the default FCoE transport driver fcoe.ko */ +#define FCOE_TRANSPORT_DEFAULT "fcoe" + +/* struct fcoe_transport - The FCoE transport interface + * @name: a vendor specific name for their FCoE transport driver + * @attached: whether this transport is already attached + * @list: list linkage to all attached transports + * @match: handler to allow the transport driver to match up a given netdev + * @create: handler to sysfs entry of create for FCoE instances + * @destroy: handler to sysfs entry of destroy for FCoE instances + * @enable: handler to sysfs entry of enable for FCoE instances + * @disable: handler to sysfs entry of disable for FCoE instances + */ +struct fcoe_transport { + char name[IFNAMSIZ]; + bool attached; + struct list_head list; + bool (*match) (struct net_device *device); + int (*create) (struct net_device *device, enum fip_state fip_mode); + int (*destroy) (struct net_device *device); + int (*enable) (struct net_device *device); + int (*disable) (struct net_device *device); +}; + +/** + * struct netdev_list + * A mapping from netdevice to fcoe_transport + */ +struct fcoe_netdev_mapping { + struct list_head list; + struct net_device *netdev; + struct fcoe_transport *ft; +}; + +/* fcoe transports registration and deregistration */ +int fcoe_transport_attach(struct fcoe_transport *ft); +int fcoe_transport_detach(struct fcoe_transport *ft); #endif /* _LIBFCOE_H */ -- cgit v1.2.3 From 75a2792df296c77004a72056c76628a1f835bc93 Mon Sep 17 00:00:00 2001 From: Bhanu Prakash Gollapudi Date: Fri, 28 Jan 2011 16:05:27 -0800 Subject: [SCSI] libfc: introduce LLD event callback This patch enables LLD to listen to rport events and perform LLD specific operations based on the rport event. This patch also stores sp_features and spp_type in rdata for further reference by LLD. Signed-off-by: Bhanu Prakash Gollapudi Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/libfc/fc_npiv.c | 1 + drivers/scsi/libfc/fc_rport.c | 19 ++++++++++++++++++- include/scsi/libfc.h | 15 +++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/scsi/libfc/fc_npiv.c b/drivers/scsi/libfc/fc_npiv.c index dd2b43bb1c70..076cd5ff0e40 100644 --- a/drivers/scsi/libfc/fc_npiv.c +++ b/drivers/scsi/libfc/fc_npiv.c @@ -86,6 +86,7 @@ struct fc_lport *fc_vport_id_lookup(struct fc_lport *n_port, u32 port_id) return lport; } +EXPORT_SYMBOL(fc_vport_id_lookup); /* * When setting the link state of vports during an lport state change, it's diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index 9ded6123ff6d..59b16bbb66a3 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -145,8 +145,10 @@ static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport, rdata->maxframe_size = FC_MIN_MAX_PAYLOAD; INIT_DELAYED_WORK(&rdata->retry_work, fc_rport_timeout); INIT_WORK(&rdata->event_work, fc_rport_work); - if (port_id != FC_FID_DIR_SERV) + if (port_id != FC_FID_DIR_SERV) { + rdata->lld_event_callback = lport->tt.rport_event_callback; list_add_rcu(&rdata->peers, &lport->disc.rports); + } return rdata; } @@ -302,6 +304,10 @@ static void fc_rport_work(struct work_struct *work) FC_RPORT_DBG(rdata, "callback ev %d\n", event); rport_ops->event_callback(lport, rdata, event); } + if (rdata->lld_event_callback) { + FC_RPORT_DBG(rdata, "lld callback ev %d\n", event); + rdata->lld_event_callback(lport, rdata, event); + } kref_put(&rdata->kref, lport->tt.rport_destroy); break; @@ -324,6 +330,10 @@ static void fc_rport_work(struct work_struct *work) FC_RPORT_DBG(rdata, "callback ev %d\n", event); rport_ops->event_callback(lport, rdata, event); } + if (rdata->lld_event_callback) { + FC_RPORT_DBG(rdata, "lld callback ev %d\n", event); + rdata->lld_event_callback(lport, rdata, event); + } cancel_delayed_work_sync(&rdata->retry_work); /* @@ -890,6 +900,9 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp, rdata->ids.port_name = get_unaligned_be64(&plp->fl_wwpn); rdata->ids.node_name = get_unaligned_be64(&plp->fl_wwnn); + /* save plogi response sp_features for further reference */ + rdata->sp_features = ntohs(plp->fl_csp.sp_features); + if (lport->point_to_multipoint) fc_rport_login_complete(rdata, fp); csp_seq = ntohs(plp->fl_csp.sp_tot_seq); @@ -997,6 +1010,7 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp, resp_code = (pp->spp.spp_flags & FC_SPP_RESP_MASK); FC_RPORT_DBG(rdata, "PRLI spp_flags = 0x%x\n", pp->spp.spp_flags); + rdata->spp_type = pp->spp.spp_type; if (resp_code != FC_SPP_RESP_ACK) { if (resp_code == FC_SPP_RESP_CONF) fc_rport_error(rdata, fp); @@ -1010,6 +1024,8 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp, fcp_parm = ntohl(pp->spp.spp_params); if (fcp_parm & FCP_SPPF_RETRY) rdata->flags |= FC_RP_FLAGS_RETRY; + if (fcp_parm & FCP_SPPF_CONF_COMPL) + rdata->flags |= FC_RP_FLAGS_CONF_REQ; prov = fc_passive_prov[FC_TYPE_FCP]; if (prov) { @@ -1719,6 +1735,7 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata, spp = &pp->spp; mutex_lock(&fc_prov_mutex); while (len >= plen) { + rdata->spp_type = rspp->spp_type; spp->spp_type = rspp->spp_type; spp->spp_type_ext = rspp->spp_type_ext; resp = 0; diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index 6d64e44bc3bf..24193c1b0da0 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -158,6 +158,7 @@ struct fc_rport_libfc_priv { #define FC_RP_FLAGS_REC_SUPPORTED (1 << 0) #define FC_RP_FLAGS_RETRY (1 << 1) #define FC_RP_STARTED (1 << 2) + #define FC_RP_FLAGS_CONF_REQ (1 << 3) unsigned int e_d_tov; unsigned int r_a_tov; }; @@ -207,6 +208,11 @@ struct fc_rport_priv { u32 supported_classes; u16 prli_count; struct rcu_head rcu; + u16 sp_features; + u8 spp_type; + void (*lld_event_callback)(struct fc_lport *, + struct fc_rport_priv *, + enum fc_rport_event); }; /** @@ -676,6 +682,15 @@ struct libfc_function_template { */ void (*rport_destroy)(struct kref *); + /* + * Callback routine after the remote port is logged in + * + * STATUS: OPTIONAL + */ + void (*rport_event_callback)(struct fc_lport *, + struct fc_rport_priv *, + enum fc_rport_event); + /* * Send a fcp cmd from fsp pkt. * Called with the SCSI host lock unlocked and irqs disabled. -- cgit v1.2.3 From 8597ae8bfe35f5e438b00ba5df852e97ebe1ac23 Mon Sep 17 00:00:00 2001 From: Bhanu Prakash Gollapudi Date: Fri, 28 Jan 2011 16:05:37 -0800 Subject: [SCSI] libfcoe: Move common code from fcoe to libfcoe module To facilitate LLDDs to reuse the code, skb queue related functions are moved to libfcoe, so that both fcoe and bnx2fc drivers can use them. The common structures fcoe_port, fcoe_percpu_s are moved to libfcoe. fcoe_port will now have an opaque pointer that points to corresponding driver's interface structure. Also, fcoe_start_io and fcoe_fc_crc are moved to libfcoe. As part of this change, fixed fcoe_start_io to return ENOMEM if skb_clone fails. Signed-off-by: Bhanu Prakash Gollapudi Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 217 ++++--------------------------------- drivers/scsi/fcoe/fcoe.h | 44 +------- drivers/scsi/fcoe/fcoe_transport.c | 200 ++++++++++++++++++++++++++++++++++ include/scsi/libfcoe.h | 51 +++++++++ 4 files changed, 275 insertions(+), 237 deletions(-) (limited to 'include') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 46c57e5755ae..495456fe4520 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -75,7 +75,6 @@ static int fcoe_xmit(struct fc_lport *, struct fc_frame *); static int fcoe_rcv(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); static int fcoe_percpu_receive_thread(void *); -static void fcoe_clean_pending_queue(struct fc_lport *); static void fcoe_percpu_clean(struct fc_lport *); static int fcoe_link_speed_update(struct fc_lport *); static int fcoe_link_ok(struct fc_lport *); @@ -83,7 +82,6 @@ static int fcoe_link_ok(struct fc_lport *); static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *); static int fcoe_hostlist_add(const struct fc_lport *); -static void fcoe_check_wait_queue(struct fc_lport *, struct sk_buff *); static int fcoe_device_notification(struct notifier_block *, ulong, void *); static void fcoe_dev_setup(void); static void fcoe_dev_cleanup(void); @@ -506,7 +504,7 @@ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb) static void fcoe_update_src_mac(struct fc_lport *lport, u8 *addr) { struct fcoe_port *port = lport_priv(lport); - struct fcoe_interface *fcoe = port->fcoe; + struct fcoe_interface *fcoe = port->priv; rtnl_lock(); if (!is_zero_ether_addr(port->data_src_addr)) @@ -561,17 +559,6 @@ static int fcoe_lport_config(struct fc_lport *lport) return 0; } -/** - * fcoe_queue_timer() - The fcoe queue timer - * @lport: The local port - * - * Calls fcoe_check_wait_queue on timeout - */ -static void fcoe_queue_timer(ulong lport) -{ - fcoe_check_wait_queue((struct fc_lport *)lport, NULL); -} - /** * fcoe_get_wwn() - Get the world wide name from LLD if it supports it * @netdev: the associated net device @@ -651,7 +638,7 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev) /* Setup lport private data to point to fcoe softc */ port = lport_priv(lport); - fcoe = port->fcoe; + fcoe = port->priv; /* * Determine max frame size based on underlying device and optional @@ -761,7 +748,7 @@ bool fcoe_oem_match(struct fc_frame *fp) static inline int fcoe_em_config(struct fc_lport *lport) { struct fcoe_port *port = lport_priv(lport); - struct fcoe_interface *fcoe = port->fcoe; + struct fcoe_interface *fcoe = port->priv; struct fcoe_interface *oldfcoe = NULL; struct net_device *old_real_dev, *cur_real_dev; u16 min_xid = FCOE_MIN_XID; @@ -845,7 +832,7 @@ skip_oem: static void fcoe_if_destroy(struct fc_lport *lport) { struct fcoe_port *port = lport_priv(lport); - struct fcoe_interface *fcoe = port->fcoe; + struct fcoe_interface *fcoe = port->priv; struct net_device *netdev = fcoe->netdev; FCOE_NETDEV_DBG(netdev, "Destroying interface\n"); @@ -966,7 +953,9 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, } port = lport_priv(lport); port->lport = lport; - port->fcoe = fcoe; + port->priv = fcoe; + port->max_queue_depth = FCOE_MAX_QUEUE_DEPTH; + port->min_queue_depth = FCOE_MIN_QUEUE_DEPTH; INIT_WORK(&port->destroy_work, fcoe_destroy_work); /* configure a fc_lport including the exchange manager */ @@ -1362,108 +1351,22 @@ err2: } /** - * fcoe_start_io() - Start FCoE I/O - * @skb: The packet to be transmitted - * - * This routine is called from the net device to start transmitting - * FCoE packets. - * - * Returns: 0 for success - */ -static inline int fcoe_start_io(struct sk_buff *skb) -{ - struct sk_buff *nskb; - int rc; - - nskb = skb_clone(skb, GFP_ATOMIC); - rc = dev_queue_xmit(nskb); - if (rc != 0) - return rc; - kfree_skb(skb); - return 0; -} - -/** - * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC + * fcoe_alloc_paged_crc_eof() - Allocate a page to be used for the trailer CRC * @skb: The packet to be transmitted * @tlen: The total length of the trailer * - * This routine allocates a page for frame trailers. The page is re-used if - * there is enough room left on it for the current trailer. If there isn't - * enough buffer left a new page is allocated for the trailer. Reference to - * the page from this function as well as the skbs using the page fragments - * ensure that the page is freed at the appropriate time. - * * Returns: 0 for success */ -static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen) +static int fcoe_alloc_paged_crc_eof(struct sk_buff *skb, int tlen) { struct fcoe_percpu_s *fps; - struct page *page; + int rc; fps = &get_cpu_var(fcoe_percpu); - page = fps->crc_eof_page; - if (!page) { - page = alloc_page(GFP_ATOMIC); - if (!page) { - put_cpu_var(fcoe_percpu); - return -ENOMEM; - } - fps->crc_eof_page = page; - fps->crc_eof_offset = 0; - } - - get_page(page); - skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, - fps->crc_eof_offset, tlen); - skb->len += tlen; - skb->data_len += tlen; - skb->truesize += tlen; - fps->crc_eof_offset += sizeof(struct fcoe_crc_eof); - - if (fps->crc_eof_offset >= PAGE_SIZE) { - fps->crc_eof_page = NULL; - fps->crc_eof_offset = 0; - put_page(page); - } + rc = fcoe_get_paged_crc_eof(skb, tlen, fps); put_cpu_var(fcoe_percpu); - return 0; -} -/** - * fcoe_fc_crc() - Calculates the CRC for a given frame - * @fp: The frame to be checksumed - * - * This uses crc32() routine to calculate the CRC for a frame - * - * Return: The 32 bit CRC value - */ -u32 fcoe_fc_crc(struct fc_frame *fp) -{ - struct sk_buff *skb = fp_skb(fp); - struct skb_frag_struct *frag; - unsigned char *data; - unsigned long off, len, clen; - u32 crc; - unsigned i; - - crc = crc32(~0, skb->data, skb_headlen(skb)); - - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - frag = &skb_shinfo(skb)->frags[i]; - off = frag->page_offset; - len = frag->size; - while (len > 0) { - clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK)); - data = kmap_atomic(frag->page + (off >> PAGE_SHIFT), - KM_SKB_DATA_SOFTIRQ); - crc = crc32(crc, data + (off & ~PAGE_MASK), clen); - kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ); - off += clen; - len -= clen; - } - } - return crc; + return rc; } /** @@ -1486,7 +1389,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) unsigned int tlen; /* trailer length */ unsigned int elen; /* eth header, may include vlan */ struct fcoe_port *port = lport_priv(lport); - struct fcoe_interface *fcoe = port->fcoe; + struct fcoe_interface *fcoe = port->priv; u8 sof, eof; struct fcoe_hdr *hp; @@ -1527,7 +1430,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) /* copy port crc and eof to the skb buff */ if (skb_is_nonlinear(skb)) { skb_frag_t *frag; - if (fcoe_get_paged_crc_eof(skb, tlen)) { + if (fcoe_alloc_paged_crc_eof(skb, tlen)) { kfree_skb(skb); return -ENOMEM; } @@ -1636,7 +1539,7 @@ static inline int fcoe_filter_frames(struct fc_lport *lport, if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && fh->fh_type == FC_TYPE_FCP) return 0; - fcoe = ((struct fcoe_port *)lport_priv(lport))->fcoe; + fcoe = ((struct fcoe_port *)lport_priv(lport))->priv; if (is_fip_mode(&fcoe->ctlr) && fc_frame_payload_op(fp) == ELS_LOGO && ntoh24(fh->fh_s_id) == FC_FID_FLOGI) { FCOE_DBG("fcoe: dropping FCoE lport LOGO in fip mode\n"); @@ -1770,64 +1673,6 @@ int fcoe_percpu_receive_thread(void *arg) return 0; } -/** - * fcoe_check_wait_queue() - Attempt to clear the transmit backlog - * @lport: The local port whose backlog is to be cleared - * - * This empties the wait_queue, dequeues the head of the wait_queue queue - * and calls fcoe_start_io() for each packet. If all skb have been - * transmitted it returns the qlen. If an error occurs it restores - * wait_queue (to try again later) and returns -1. - * - * The wait_queue is used when the skb transmit fails. The failed skb - * will go in the wait_queue which will be emptied by the timer function or - * by the next skb transmit. - */ -static void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb) -{ - struct fcoe_port *port = lport_priv(lport); - int rc; - - spin_lock_bh(&port->fcoe_pending_queue.lock); - - if (skb) - __skb_queue_tail(&port->fcoe_pending_queue, skb); - - if (port->fcoe_pending_queue_active) - goto out; - port->fcoe_pending_queue_active = 1; - - while (port->fcoe_pending_queue.qlen) { - /* keep qlen > 0 until fcoe_start_io succeeds */ - port->fcoe_pending_queue.qlen++; - skb = __skb_dequeue(&port->fcoe_pending_queue); - - spin_unlock_bh(&port->fcoe_pending_queue.lock); - rc = fcoe_start_io(skb); - spin_lock_bh(&port->fcoe_pending_queue.lock); - - if (rc) { - __skb_queue_head(&port->fcoe_pending_queue, skb); - /* undo temporary increment above */ - port->fcoe_pending_queue.qlen--; - break; - } - /* undo temporary increment above */ - port->fcoe_pending_queue.qlen--; - } - - if (port->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH) - lport->qfull = 0; - if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer)) - mod_timer(&port->timer, jiffies + 2); - port->fcoe_pending_queue_active = 0; -out: - if (port->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) - lport->qfull = 1; - spin_unlock_bh(&port->fcoe_pending_queue.lock); - return; -} - /** * fcoe_dev_setup() - Setup the link change notification interface */ @@ -2180,8 +2025,7 @@ out_nodev: */ int fcoe_link_speed_update(struct fc_lport *lport) { - struct fcoe_port *port = lport_priv(lport); - struct net_device *netdev = port->fcoe->netdev; + struct net_device *netdev = fcoe_netdev(lport); struct ethtool_cmd ecmd = { ETHTOOL_GSET }; if (!dev_ethtool_get_settings(netdev, &ecmd)) { @@ -2212,8 +2056,7 @@ int fcoe_link_speed_update(struct fc_lport *lport) */ int fcoe_link_ok(struct fc_lport *lport) { - struct fcoe_port *port = lport_priv(lport); - struct net_device *netdev = port->fcoe->netdev; + struct net_device *netdev = fcoe_netdev(lport); if (netif_oper_up(netdev)) return 0; @@ -2276,24 +2119,6 @@ void fcoe_percpu_clean(struct fc_lport *lport) } } -/** - * fcoe_clean_pending_queue() - Dequeue a skb and free it - * @lport: The local port to dequeue a skb on - */ -void fcoe_clean_pending_queue(struct fc_lport *lport) -{ - struct fcoe_port *port = lport_priv(lport); - struct sk_buff *skb; - - spin_lock_bh(&port->fcoe_pending_queue.lock); - while ((skb = __skb_dequeue(&port->fcoe_pending_queue)) != NULL) { - spin_unlock_bh(&port->fcoe_pending_queue.lock); - kfree_skb(skb); - spin_lock_bh(&port->fcoe_pending_queue.lock); - } - spin_unlock_bh(&port->fcoe_pending_queue.lock); -} - /** * fcoe_reset() - Reset a local port * @shost: The SCSI host associated with the local port to be reset @@ -2361,7 +2186,7 @@ static int fcoe_hostlist_add(const struct fc_lport *lport) fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport)); if (!fcoe) { port = lport_priv(lport); - fcoe = port->fcoe; + fcoe = port->priv; list_add_tail(&fcoe->list, &fcoe_hostlist); } return 0; @@ -2555,7 +2380,7 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did, void *arg, u32 timeout) { struct fcoe_port *port = lport_priv(lport); - struct fcoe_interface *fcoe = port->fcoe; + struct fcoe_interface *fcoe = port->priv; struct fcoe_ctlr *fip = &fcoe->ctlr; struct fc_frame_header *fh = fc_frame_header_get(fp); @@ -2588,7 +2413,7 @@ static int fcoe_vport_create(struct fc_vport *vport, bool disabled) struct Scsi_Host *shost = vport_to_shost(vport); struct fc_lport *n_port = shost_priv(shost); struct fcoe_port *port = lport_priv(n_port); - struct fcoe_interface *fcoe = port->fcoe; + struct fcoe_interface *fcoe = port->priv; struct net_device *netdev = fcoe->netdev; struct fc_lport *vn_port; @@ -2732,7 +2557,7 @@ static void fcoe_set_port_id(struct fc_lport *lport, u32 port_id, struct fc_frame *fp) { struct fcoe_port *port = lport_priv(lport); - struct fcoe_interface *fcoe = port->fcoe; + struct fcoe_interface *fcoe = port->priv; if (fp && fc_frame_payload_op(fp) == ELS_FLOGI) fcoe_ctlr_recv_flogi(&fcoe->ctlr, lport, fp); diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index c69b2c56c2d1..d775128398e9 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -24,7 +24,7 @@ #include #define FCOE_MAX_QUEUE_DEPTH 256 -#define FCOE_LOW_QUEUE_DEPTH 32 +#define FCOE_MIN_QUEUE_DEPTH 32 #define FCOE_WORD_TO_BYTE 4 @@ -70,21 +70,6 @@ do { \ printk(KERN_INFO "fcoe: %s: " fmt, \ netdev->name, ##args);) -/** - * struct fcoe_percpu_s - The per-CPU context for FCoE receive threads - * @thread: The thread context - * @fcoe_rx_list: The queue of pending packets to process - * @page: The memory page for calculating frame trailer CRCs - * @crc_eof_offset: The offset into the CRC page pointing to available - * memory for a new trailer - */ -struct fcoe_percpu_s { - struct task_struct *thread; - struct sk_buff_head fcoe_rx_list; - struct page *crc_eof_page; - int crc_eof_offset; -}; - /** * struct fcoe_interface - A FCoE interface * @list: Handle for a list of FCoE interfaces @@ -108,30 +93,6 @@ struct fcoe_interface { struct kref kref; }; -/** - * struct fcoe_port - The FCoE private structure - * @fcoe: The associated fcoe interface - * @lport: The associated local port - * @fcoe_pending_queue: The pending Rx queue of skbs - * @fcoe_pending_queue_active: Indicates if the pending queue is active - * @timer: The queue timer - * @destroy_work: Handle for work context - * (to prevent RTNL deadlocks) - * @data_srt_addr: Source address for data - * - * An instance of this structure is to be allocated along with the - * Scsi_Host and libfc fc_lport structures. - */ -struct fcoe_port { - struct fcoe_interface *fcoe; - struct fc_lport *lport; - struct sk_buff_head fcoe_pending_queue; - u8 fcoe_pending_queue_active; - struct timer_list timer; - struct work_struct destroy_work; - u8 data_src_addr[ETH_ALEN]; -}; - #define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr) /** @@ -140,7 +101,8 @@ struct fcoe_port { */ static inline struct net_device *fcoe_netdev(const struct fc_lport *lport) { - return ((struct fcoe_port *)lport_priv(lport))->fcoe->netdev; + return ((struct fcoe_interface *) + ((struct fcoe_port *)lport_priv(lport))->priv)->netdev; } #endif /* _FCOE_H_ */ diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index e5aef5639124..745eb9a22d64 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "libfcoe.h" @@ -74,6 +75,205 @@ module_param_call(disable, fcoe_transport_disable, NULL, NULL, S_IWUSR); __MODULE_PARM_TYPE(disable, "string"); MODULE_PARM_DESC(disable, " Disables fcoe on a ethernet interface."); +/** + * fcoe_fc_crc() - Calculates the CRC for a given frame + * @fp: The frame to be checksumed + * + * This uses crc32() routine to calculate the CRC for a frame + * + * Return: The 32 bit CRC value + */ +u32 fcoe_fc_crc(struct fc_frame *fp) +{ + struct sk_buff *skb = fp_skb(fp); + struct skb_frag_struct *frag; + unsigned char *data; + unsigned long off, len, clen; + u32 crc; + unsigned i; + + crc = crc32(~0, skb->data, skb_headlen(skb)); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + frag = &skb_shinfo(skb)->frags[i]; + off = frag->page_offset; + len = frag->size; + while (len > 0) { + clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK)); + data = kmap_atomic(frag->page + (off >> PAGE_SHIFT), + KM_SKB_DATA_SOFTIRQ); + crc = crc32(crc, data + (off & ~PAGE_MASK), clen); + kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ); + off += clen; + len -= clen; + } + } + return crc; +} +EXPORT_SYMBOL_GPL(fcoe_fc_crc); + +/** + * fcoe_start_io() - Start FCoE I/O + * @skb: The packet to be transmitted + * + * This routine is called from the net device to start transmitting + * FCoE packets. + * + * Returns: 0 for success + */ +int fcoe_start_io(struct sk_buff *skb) +{ + struct sk_buff *nskb; + int rc; + + nskb = skb_clone(skb, GFP_ATOMIC); + if (!nskb) + return -ENOMEM; + rc = dev_queue_xmit(nskb); + if (rc != 0) + return rc; + kfree_skb(skb); + return 0; +} +EXPORT_SYMBOL_GPL(fcoe_start_io); + + +/** + * fcoe_clean_pending_queue() - Dequeue a skb and free it + * @lport: The local port to dequeue a skb on + */ +void fcoe_clean_pending_queue(struct fc_lport *lport) +{ + struct fcoe_port *port = lport_priv(lport); + struct sk_buff *skb; + + spin_lock_bh(&port->fcoe_pending_queue.lock); + while ((skb = __skb_dequeue(&port->fcoe_pending_queue)) != NULL) { + spin_unlock_bh(&port->fcoe_pending_queue.lock); + kfree_skb(skb); + spin_lock_bh(&port->fcoe_pending_queue.lock); + } + spin_unlock_bh(&port->fcoe_pending_queue.lock); +} +EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue); + +/** + * fcoe_check_wait_queue() - Attempt to clear the transmit backlog + * @lport: The local port whose backlog is to be cleared + * + * This empties the wait_queue, dequeues the head of the wait_queue queue + * and calls fcoe_start_io() for each packet. If all skb have been + * transmitted it returns the qlen. If an error occurs it restores + * wait_queue (to try again later) and returns -1. + * + * The wait_queue is used when the skb transmit fails. The failed skb + * will go in the wait_queue which will be emptied by the timer function or + * by the next skb transmit. + */ +void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb) +{ + struct fcoe_port *port = lport_priv(lport); + int rc; + + spin_lock_bh(&port->fcoe_pending_queue.lock); + + if (skb) + __skb_queue_tail(&port->fcoe_pending_queue, skb); + + if (port->fcoe_pending_queue_active) + goto out; + port->fcoe_pending_queue_active = 1; + + while (port->fcoe_pending_queue.qlen) { + /* keep qlen > 0 until fcoe_start_io succeeds */ + port->fcoe_pending_queue.qlen++; + skb = __skb_dequeue(&port->fcoe_pending_queue); + + spin_unlock_bh(&port->fcoe_pending_queue.lock); + rc = fcoe_start_io(skb); + spin_lock_bh(&port->fcoe_pending_queue.lock); + + if (rc) { + __skb_queue_head(&port->fcoe_pending_queue, skb); + /* undo temporary increment above */ + port->fcoe_pending_queue.qlen--; + break; + } + /* undo temporary increment above */ + port->fcoe_pending_queue.qlen--; + } + + if (port->fcoe_pending_queue.qlen < port->min_queue_depth) + lport->qfull = 0; + if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer)) + mod_timer(&port->timer, jiffies + 2); + port->fcoe_pending_queue_active = 0; +out: + if (port->fcoe_pending_queue.qlen > port->max_queue_depth) + lport->qfull = 1; + spin_unlock_bh(&port->fcoe_pending_queue.lock); +} +EXPORT_SYMBOL_GPL(fcoe_check_wait_queue); + +/** + * fcoe_queue_timer() - The fcoe queue timer + * @lport: The local port + * + * Calls fcoe_check_wait_queue on timeout + */ +void fcoe_queue_timer(ulong lport) +{ + fcoe_check_wait_queue((struct fc_lport *)lport, NULL); +} +EXPORT_SYMBOL_GPL(fcoe_queue_timer); + +/** + * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC + * @skb: The packet to be transmitted + * @tlen: The total length of the trailer + * @fps: The fcoe context + * + * This routine allocates a page for frame trailers. The page is re-used if + * there is enough room left on it for the current trailer. If there isn't + * enough buffer left a new page is allocated for the trailer. Reference to + * the page from this function as well as the skbs using the page fragments + * ensure that the page is freed at the appropriate time. + * + * Returns: 0 for success + */ +int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen, + struct fcoe_percpu_s *fps) +{ + struct page *page; + + page = fps->crc_eof_page; + if (!page) { + page = alloc_page(GFP_ATOMIC); + if (!page) + return -ENOMEM; + + fps->crc_eof_page = page; + fps->crc_eof_offset = 0; + } + + get_page(page); + skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, + fps->crc_eof_offset, tlen); + skb->len += tlen; + skb->data_len += tlen; + skb->truesize += tlen; + fps->crc_eof_offset += sizeof(struct fcoe_crc_eof); + + if (fps->crc_eof_offset >= PAGE_SIZE) { + fps->crc_eof_page = NULL; + fps->crc_eof_offset = 0; + put_page(page); + } + + return 0; +} +EXPORT_SYMBOL_GPL(fcoe_get_paged_crc_eof); + /** * fcoe_transport_lookup - find an fcoe transport that matches a netdev * @netdev: The netdev to look for from all attached transports diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index efb6ae5b94ad..e5024634bfab 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -221,6 +221,8 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *, struct fc_lport *, u64 fcoe_wwn_from_mac(unsigned char mac[], unsigned int, unsigned int); int fcoe_libfc_config(struct fc_lport *, struct fcoe_ctlr *, const struct libfc_function_template *, int init_fcp); +u32 fcoe_fc_crc(struct fc_frame *fp); +int fcoe_start_io(struct sk_buff *skb); /** * is_fip_mode() - returns true if FIP mode selected. @@ -266,6 +268,55 @@ struct fcoe_transport { int (*disable) (struct net_device *device); }; +/** + * struct fcoe_percpu_s - The context for FCoE receive thread(s) + * @thread: The thread context + * @fcoe_rx_list: The queue of pending packets to process + * @page: The memory page for calculating frame trailer CRCs + * @crc_eof_offset: The offset into the CRC page pointing to available + * memory for a new trailer + */ +struct fcoe_percpu_s { + struct task_struct *thread; + struct sk_buff_head fcoe_rx_list; + struct page *crc_eof_page; + int crc_eof_offset; +}; + +/** + * struct fcoe_port - The FCoE private structure + * @priv: The associated fcoe interface. The structure is + * defined by the low level driver + * @lport: The associated local port + * @fcoe_pending_queue: The pending Rx queue of skbs + * @fcoe_pending_queue_active: Indicates if the pending queue is active + * @max_queue_depth: Max queue depth of pending queue + * @min_queue_depth: Min queue depth of pending queue + * @timer: The queue timer + * @destroy_work: Handle for work context + * (to prevent RTNL deadlocks) + * @data_srt_addr: Source address for data + * + * An instance of this structure is to be allocated along with the + * Scsi_Host and libfc fc_lport structures. + */ +struct fcoe_port { + void *priv; + struct fc_lport *lport; + struct sk_buff_head fcoe_pending_queue; + u8 fcoe_pending_queue_active; + u32 max_queue_depth; + u32 min_queue_depth; + struct timer_list timer; + struct work_struct destroy_work; + u8 data_src_addr[ETH_ALEN]; +}; +void fcoe_clean_pending_queue(struct fc_lport *); +void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb); +void fcoe_queue_timer(ulong lport); +int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen, + struct fcoe_percpu_s *fps); + /** * struct netdev_list * A mapping from netdevice to fcoe_transport -- cgit v1.2.3 From d59cfde2fb960b5970ccb5a38cea25d38b37a8e8 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 12 Feb 2011 00:46:06 +0000 Subject: net: remove the unnecessary dance around skb_bond_should_drop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to check (master) twice and to drive in and out the header file. Signed-off-by: Jiri Pirko Reviewed-by: Nicolas de Pesloüan Signed-off-by: David S. Miller --- include/linux/netdevice.h | 11 ----------- net/core/dev.c | 6 +++--- 2 files changed, 3 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c7d707452228..5a5baeaaa50f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2437,17 +2437,6 @@ static inline void netif_set_gso_max_size(struct net_device *dev, dev->gso_max_size = size; } -extern int __skb_bond_should_drop(struct sk_buff *skb, - struct net_device *master); - -static inline int skb_bond_should_drop(struct sk_buff *skb, - struct net_device *master) -{ - if (master) - return __skb_bond_should_drop(skb, master); - return 0; -} - extern struct pernet_operations __net_initdata loopback_net_ops; static inline int dev_ethtool_get_settings(struct net_device *dev, diff --git a/net/core/dev.c b/net/core/dev.c index 6392ea0a5910..d874fd1baf49 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3105,7 +3105,8 @@ static inline void skb_bond_set_mac_by_master(struct sk_buff *skb, * duplicates except for 802.3ad ETH_P_SLOW, alb non-mcast/bcast, and * ARP on active-backup slaves with arp_validate enabled. */ -int __skb_bond_should_drop(struct sk_buff *skb, struct net_device *master) +static int __skb_bond_should_drop(struct sk_buff *skb, + struct net_device *master) { struct net_device *dev = skb->dev; @@ -3139,7 +3140,6 @@ int __skb_bond_should_drop(struct sk_buff *skb, struct net_device *master) } return 0; } -EXPORT_SYMBOL(__skb_bond_should_drop); static int __netif_receive_skb(struct sk_buff *skb) { @@ -3177,7 +3177,7 @@ static int __netif_receive_skb(struct sk_buff *skb) if (skb->deliver_no_wcard) null_or_orig = orig_dev; else if (master) { - if (skb_bond_should_drop(skb, master)) { + if (__skb_bond_should_drop(skb, master)) { skb->deliver_no_wcard = 1; null_or_orig = orig_dev; /* deliver only exact match */ } else -- cgit v1.2.3 From 1765a575334f1a232c1478accdee5c7d19f4b3e3 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 12 Feb 2011 06:48:36 +0000 Subject: net: make dev->master general dev->master is now tightly connected to bonding driver. This patch makes this pointer more general and ready to be used by others. - netdev_set_master() - bond specifics moved to new function netdev_set_bond_master() - introduced netif_is_bond_slave() to check if device is a bonding slave Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/infiniband/hw/nes/nes.c | 3 ++- drivers/infiniband/hw/nes/nes_cm.c | 2 +- drivers/net/bonding/bond_main.c | 10 ++++---- drivers/net/cxgb3/cxgb3_offload.c | 3 ++- include/linux/netdevice.h | 7 ++++++ net/core/dev.c | 49 ++++++++++++++++++++++++++++---------- 6 files changed, 54 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c index 3b4ec3238ceb..3d7f3664b67b 100644 --- a/drivers/infiniband/hw/nes/nes.c +++ b/drivers/infiniband/hw/nes/nes.c @@ -153,7 +153,8 @@ static int nes_inetaddr_event(struct notifier_block *notifier, nesdev, nesdev->netdev[0]->name); netdev = nesdev->netdev[0]; nesvnic = netdev_priv(netdev); - is_bonded = (netdev->master == event_netdev); + is_bonded = netif_is_bond_slave(netdev) && + (netdev->master == event_netdev); if ((netdev == event_netdev) || is_bonded) { if (nesvnic->rdma_enabled == 0) { nes_debug(NES_DBG_NETDEV, "Returning without processing event for %s since" diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index 009ec814d517..ec3aa11c36cb 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c @@ -1118,7 +1118,7 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi return rc; } - if (nesvnic->netdev->master) + if (netif_is_bond_slave(netdev)) netdev = nesvnic->netdev->master; else netdev = nesvnic->netdev; diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 1df9f0ea9184..9f877878d636 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1594,9 +1594,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) } } - res = netdev_set_master(slave_dev, bond_dev); + res = netdev_set_bond_master(slave_dev, bond_dev); if (res) { - pr_debug("Error %d calling netdev_set_master\n", res); + pr_debug("Error %d calling netdev_set_bond_master\n", res); goto err_restore_mac; } /* open the slave since the application closed it */ @@ -1812,7 +1812,7 @@ err_close: dev_close(slave_dev); err_unset_master: - netdev_set_master(slave_dev, NULL); + netdev_set_bond_master(slave_dev, NULL); err_restore_mac: if (!bond->params.fail_over_mac) { @@ -1992,7 +1992,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) netif_addr_unlock_bh(bond_dev); } - netdev_set_master(slave_dev, NULL); + netdev_set_bond_master(slave_dev, NULL); #ifdef CONFIG_NET_POLL_CONTROLLER read_lock_bh(&bond->lock); @@ -2114,7 +2114,7 @@ static int bond_release_all(struct net_device *bond_dev) netif_addr_unlock_bh(bond_dev); } - netdev_set_master(slave_dev, NULL); + netdev_set_bond_master(slave_dev, NULL); /* close slave before restoring its mac address */ dev_close(slave_dev); diff --git a/drivers/net/cxgb3/cxgb3_offload.c b/drivers/net/cxgb3/cxgb3_offload.c index 7ea94b5205f8..862804f32b6e 100644 --- a/drivers/net/cxgb3/cxgb3_offload.c +++ b/drivers/net/cxgb3/cxgb3_offload.c @@ -186,9 +186,10 @@ static struct net_device *get_iff_from_mac(struct adapter *adapter, dev = NULL; if (grp) dev = vlan_group_get_device(grp, vlan); - } else + } else if (netif_is_bond_slave(dev)) { while (dev->master) dev = dev->master; + } return dev; } } diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5a5baeaaa50f..5a42b1003767 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2377,6 +2377,8 @@ extern int netdev_max_backlog; extern int netdev_tstamp_prequeue; extern int weight_p; extern int netdev_set_master(struct net_device *dev, struct net_device *master); +extern int netdev_set_bond_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, u32 features); #ifdef CONFIG_BUG @@ -2437,6 +2439,11 @@ static inline void netif_set_gso_max_size(struct net_device *dev, dev->gso_max_size = size; } +static inline int netif_is_bond_slave(struct net_device *dev) +{ + return dev->flags & IFF_SLAVE && dev->priv_flags & IFF_BONDING; +} + extern struct pernet_operations __net_initdata loopback_net_ops; static inline int dev_ethtool_get_settings(struct net_device *dev, diff --git a/net/core/dev.c b/net/core/dev.c index d874fd1baf49..a4132766d363 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3146,7 +3146,6 @@ static int __netif_receive_skb(struct sk_buff *skb) struct packet_type *ptype, *pt_prev; rx_handler_func_t *rx_handler; struct net_device *orig_dev; - struct net_device *master; struct net_device *null_or_orig; struct net_device *orig_or_bond; int ret = NET_RX_DROP; @@ -3173,15 +3172,19 @@ static int __netif_receive_skb(struct sk_buff *skb) */ null_or_orig = NULL; orig_dev = skb->dev; - master = ACCESS_ONCE(orig_dev->master); if (skb->deliver_no_wcard) null_or_orig = orig_dev; - else if (master) { - if (__skb_bond_should_drop(skb, master)) { - skb->deliver_no_wcard = 1; - null_or_orig = orig_dev; /* deliver only exact match */ - } else - skb->dev = master; + else if (netif_is_bond_slave(orig_dev)) { + struct net_device *bond_master = ACCESS_ONCE(orig_dev->master); + + if (likely(bond_master)) { + if (__skb_bond_should_drop(skb, bond_master)) { + skb->deliver_no_wcard = 1; + /* deliver only exact match */ + null_or_orig = orig_dev; + } else + skb->dev = bond_master; + } } __this_cpu_inc(softnet_data.processed); @@ -4346,15 +4349,14 @@ static int __init dev_proc_init(void) /** - * netdev_set_master - set up master/slave pair + * netdev_set_master - set up master pointer * @slave: slave device * @master: new master device * * Changes the master device of the slave. Pass %NULL to break the * bonding. The caller must hold the RTNL semaphore. On a failure * a negative errno code is returned. On success the reference counts - * are adjusted, %RTM_NEWLINK is sent to the routing socket and the - * function returns zero. + * are adjusted and the function returns zero. */ int netdev_set_master(struct net_device *slave, struct net_device *master) { @@ -4374,6 +4376,29 @@ int netdev_set_master(struct net_device *slave, struct net_device *master) synchronize_net(); dev_put(old); } + return 0; +} +EXPORT_SYMBOL(netdev_set_master); + +/** + * netdev_set_bond_master - set up bonding master/slave pair + * @slave: slave device + * @master: new master device + * + * Changes the master device of the slave. Pass %NULL to break the + * bonding. The caller must hold the RTNL semaphore. On a failure + * a negative errno code is returned. On success %RTM_NEWLINK is sent + * to the routing socket and the function returns zero. + */ +int netdev_set_bond_master(struct net_device *slave, struct net_device *master) +{ + int err; + + ASSERT_RTNL(); + + err = netdev_set_master(slave, master); + if (err) + return err; if (master) slave->flags |= IFF_SLAVE; else @@ -4382,7 +4407,7 @@ int netdev_set_master(struct net_device *slave, struct net_device *master) rtmsg_ifinfo(RTM_NEWLINK, slave, IFF_SLAVE); return 0; } -EXPORT_SYMBOL(netdev_set_master); +EXPORT_SYMBOL(netdev_set_bond_master); static void dev_change_rx_flags(struct net_device *dev, int flags) { -- cgit v1.2.3 From 64878c0eff5737e15b3ff06d02e7227eda4aa04c Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 23 Jan 2011 09:42:50 -0600 Subject: [SCSI] libata: separate error handler into usable components Right at the moment, the libata error handler is incredibly monolithic. This makes it impossible to use from composite drivers like libsas and ipr which have to handle error themselves in the first instance. The essence of the change is to split the monolithic error handler into two components: one which handles a queue of ata commands for processing and the other which handles the back end of readying a port. This allows the upper error handler fine grained control in calling libsas functions (and making sure they only get called for ATA commands whose lower errors have been fixed up). Cc: Tejun Heo Cc: Jeff Garzik Signed-off-by: James Bottomley --- drivers/ata/libata-eh.c | 53 ++++++++++++++++++++++++++++++++++++++++--------- include/linux/libata.h | 2 ++ 2 files changed, 46 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index fc3f339a8d3c..e02455b751ca 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -587,7 +587,6 @@ static void ata_eh_unload(struct ata_port *ap) void ata_scsi_error(struct Scsi_Host *host) { struct ata_port *ap = ata_shost_to_port(host); - int i; unsigned long flags; LIST_HEAD(eh_work_q); @@ -597,6 +596,34 @@ void ata_scsi_error(struct Scsi_Host *host) list_splice_init(&host->eh_cmd_q, &eh_work_q); spin_unlock_irqrestore(host->host_lock, flags); + ata_scsi_cmd_error_handler(host, ap, &eh_work_q); + + /* If we timed raced normal completion and there is nothing to + recover nr_timedout == 0 why exactly are we doing error recovery ? */ + ata_scsi_port_error_handler(host, ap); + + /* finish or retry handled scmd's and clean up */ + WARN_ON(host->host_failed || !list_empty(&eh_work_q)); + + DPRINTK("EXIT\n"); +} + +/** + * ata_scsi_cmd_error_handler - error callback for a list of commands + * @host: scsi host containing the port + * @ap: ATA port within the host + * @eh_work_q: list of commands to process + * + * process the given list of commands and return those finished to the + * ap->eh_done_q. This function is the first part of the libata error + * handler which processes a given list of failed commands. + */ +void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, + struct list_head *eh_work_q) +{ + int i; + unsigned long flags; + /* make sure sff pio task is not running */ ata_sff_flush_pio_task(ap); @@ -632,7 +659,7 @@ void ata_scsi_error(struct Scsi_Host *host) if (ap->ops->lost_interrupt) ap->ops->lost_interrupt(ap); - list_for_each_entry_safe(scmd, tmp, &eh_work_q, eh_entry) { + list_for_each_entry_safe(scmd, tmp, eh_work_q, eh_entry) { struct ata_queued_cmd *qc; for (i = 0; i < ATA_MAX_QUEUE; i++) { @@ -676,8 +703,20 @@ void ata_scsi_error(struct Scsi_Host *host) } else spin_unlock_wait(ap->lock); - /* If we timed raced normal completion and there is nothing to - recover nr_timedout == 0 why exactly are we doing error recovery ? */ +} +EXPORT_SYMBOL(ata_scsi_cmd_error_handler); + +/** + * ata_scsi_port_error_handler - recover the port after the commands + * @host: SCSI host containing the port + * @ap: the ATA port + * + * Handle the recovery of the port @ap after all the commands + * have been recovered. + */ +void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap) +{ + unsigned long flags; /* invoke error handler */ if (ap->ops->error_handler) { @@ -766,9 +805,6 @@ void ata_scsi_error(struct Scsi_Host *host) ap->ops->eng_timeout(ap); } - /* finish or retry handled scmd's and clean up */ - WARN_ON(host->host_failed || !list_empty(&eh_work_q)); - scsi_eh_flush_done_q(&ap->eh_done_q); /* clean up */ @@ -789,9 +825,8 @@ void ata_scsi_error(struct Scsi_Host *host) wake_up_all(&ap->eh_wait_q); spin_unlock_irqrestore(ap->lock, flags); - - DPRINTK("EXIT\n"); } +EXPORT_SYMBOL_GPL(ata_scsi_port_error_handler); /** * ata_port_wait_eh - Wait for the currently pending EH to complete diff --git a/include/linux/libata.h b/include/linux/libata.h index c9c5d7ad1a2b..9739317c707a 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1050,6 +1050,8 @@ extern int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth, int reason); extern struct ata_device *ata_dev_pair(struct ata_device *adev); extern int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev); +extern void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap); +extern void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, struct list_head *eh_q); extern int ata_cable_40wire(struct ata_port *ap); extern int ata_cable_80wire(struct ata_port *ap); -- cgit v1.2.3 From c299190b9398d4edfbf80a749875d5bac199bfdc Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 23 Jan 2011 09:44:12 -0600 Subject: [SCSI] libsas: convert to libata new error handler The conversion is quite complex given that the libata new error handler has to be hooked into the current libsas timeout and error handling. The way this is done is to process all the failed commands via libsas first, but if they have no underlying sas task (and they're on a sata device) assume they are destined for the libata error handler and send them accordingly. Finally, activate the port recovery of the libata error handler for each port known to the host. This is somewhat suboptimal, since that port may not need recovering, but given the current architecture of the libata error handler, it's the only way; and the spurious activation is harmless. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 87 ++++++++++++++++++++++++++++++++++--- drivers/scsi/libsas/sas_scsi_host.c | 14 +++++- include/scsi/sas_ata.h | 22 ++++++++++ 3 files changed, 115 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index c2a5cc75b407..16c5094bc86c 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -240,37 +240,43 @@ static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) return true; } -static void sas_ata_phy_reset(struct ata_port *ap) +static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, + unsigned long deadline) { + struct ata_port *ap = link->ap; struct domain_device *dev = ap->private_data; struct sas_internal *i = to_sas_internal(dev->port->ha->core.shost->transportt); int res = TMF_RESP_FUNC_FAILED; + int ret = 0; if (i->dft->lldd_I_T_nexus_reset) res = i->dft->lldd_I_T_nexus_reset(dev); - if (res != TMF_RESP_FUNC_COMPLETE) + if (res != TMF_RESP_FUNC_COMPLETE) { SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __func__); + ret = -EAGAIN; + } switch (dev->sata_dev.command_set) { case ATA_COMMAND_SET: SAS_DPRINTK("%s: Found ATA device.\n", __func__); - ap->link.device[0].class = ATA_DEV_ATA; + *class = ATA_DEV_ATA; break; case ATAPI_COMMAND_SET: SAS_DPRINTK("%s: Found ATAPI device.\n", __func__); - ap->link.device[0].class = ATA_DEV_ATAPI; + *class = ATA_DEV_ATAPI; break; default: SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", __func__, dev->sata_dev.command_set); - ap->link.device[0].class = ATA_DEV_UNKNOWN; + *class = ATA_DEV_UNKNOWN; break; } ap->cbl = ATA_CBL_SATA; + return ret; } static void sas_ata_post_internal(struct ata_queued_cmd *qc) @@ -302,7 +308,11 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) } static struct ata_port_operations sas_sata_ops = { - .phy_reset = sas_ata_phy_reset, + .prereset = ata_std_prereset, + .softreset = NULL, + .hardreset = sas_ata_hard_reset, + .postreset = ata_std_postreset, + .error_handler = ata_std_error_handler, .post_internal_cmd = sas_ata_post_internal, .qc_defer = ata_std_qc_defer, .qc_prep = ata_noop_qc_prep, @@ -732,3 +742,68 @@ int sas_discover_sata(struct domain_device *dev) return res; } + +void sas_ata_strategy_handler(struct Scsi_Host *shost) +{ + struct scsi_device *sdev; + + shost_for_each_device(sdev, shost) { + struct domain_device *ddev = sdev_to_domain_dev(sdev); + struct ata_port *ap = ddev->sata_dev.ap; + + if (!dev_is_sata(ddev)) + continue; + + ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler"); + ata_scsi_port_error_handler(shost, ap); + } +} + +int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task, + enum blk_eh_timer_return *rtn) +{ + struct domain_device *ddev = cmd_to_domain_dev(cmd); + + if (!dev_is_sata(ddev) || task) + return 0; + + /* we're a sata device with no task, so this must be a libata + * eh timeout. Ideally should hook into libata timeout + * handling, but there's no point, it just wants to activate + * the eh thread */ + *rtn = BLK_EH_NOT_HANDLED; + return 1; +} + +int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, + struct list_head *done_q) +{ + int rtn = 0; + struct scsi_cmnd *cmd, *n; + struct ata_port *ap; + + do { + LIST_HEAD(sata_q); + + ap = NULL; + + list_for_each_entry_safe(cmd, n, work_q, eh_entry) { + struct domain_device *ddev = cmd_to_domain_dev(cmd); + + if (!dev_is_sata(ddev) || TO_SAS_TASK(cmd)) + continue; + if (ap && ap != ddev->sata_dev.ap) + continue; + ap = ddev->sata_dev.ap; + rtn = 1; + list_move(&cmd->eh_entry, &sata_q); + } + + if (!list_empty(&sata_q)) { + ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata cmd error handler\n"); + ata_scsi_cmd_error_handler(shost, ap, &sata_q); + } + } while (ap); + + return rtn; +} diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 5815cbeb27a6..2119871aed3e 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -662,11 +662,16 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) * scsi_unjam_host does, but we skip scsi_eh_abort_cmds because any * command we see here has no sas_task and is thus unknown to the HA. */ - if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q)) - scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q); + if (!sas_ata_eh(shost, &eh_work_q, &ha->eh_done_q)) + if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q)) + scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q); out: + /* now link into libata eh --- if we have any ata devices */ + sas_ata_strategy_handler(shost); + scsi_eh_flush_done_q(&ha->eh_done_q); + SAS_DPRINTK("--- Exit %s\n", __func__); return; } @@ -675,6 +680,11 @@ enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) { struct sas_task *task = TO_SAS_TASK(cmd); unsigned long flags; + enum blk_eh_timer_return rtn; + + if (sas_ata_timed_out(cmd, task, &rtn)) + return rtn; + if (!task) { cmd->request->timeout /= 2; diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index c583193ae929..9c159f74c6d0 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -39,6 +39,11 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev, struct scsi_target *starget); void sas_ata_task_abort(struct sas_task *task); +void sas_ata_strategy_handler(struct Scsi_Host *shost); +int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task, + enum blk_eh_timer_return *rtn); +int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, + struct list_head *done_q); #else @@ -55,6 +60,23 @@ static inline int sas_ata_init_host_and_port(struct domain_device *found_dev, static inline void sas_ata_task_abort(struct sas_task *task) { } + +static inline void sas_ata_strategy_handler(struct Scsi_Host *shost) +{ +} + +static inline int sas_ata_timed_out(struct scsi_cmnd *cmd, + struct sas_task *task, + enum blk_eh_timer_return *rtn) +{ + return 0; +} +static inline int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, + struct list_head *done_q) +{ + return 0; +} + #endif #endif /* _SAS_ATA_H_ */ -- cgit v1.2.3 From fbaec0ea54f7d9131891ff98744e82c073ce03b1 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sun, 13 Feb 2011 10:15:37 +0000 Subject: rtnetlink: implement setting of master device This patch allows userspace to enslave/release slave devices via netlink interface using IFLA_MASTER. This introduces generic way to add/remove underling devices. Signed-off-by: Jiri Pirko Acked-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netdevice.h | 12 ++++++++++++ net/core/rtnetlink.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5a42b1003767..d08ef6538579 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -783,6 +783,14 @@ struct netdev_tc_txq { * 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. + * + * Slave management functions (for bridge, bonding, etc). User should + * call netdev_set_master() to set dev->master properly. + * int (*ndo_add_slave)(struct net_device *dev, struct net_device *slave_dev); + * Called to make another netdev an underling. + * + * int (*ndo_del_slave)(struct net_device *dev, struct net_device *slave_dev); + * Called to release previously enslaved netdev. */ #define HAVE_NET_DEVICE_OPS struct net_device_ops { @@ -862,6 +870,10 @@ struct net_device_ops { u16 rxq_index, u32 flow_id); #endif + int (*ndo_add_slave)(struct net_device *dev, + struct net_device *slave_dev); + int (*ndo_del_slave)(struct net_device *dev, + struct net_device *slave_dev); }; /* diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index da0fe457c858..49f7ea5b4c75 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1036,6 +1036,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_MAP] = { .len = sizeof(struct rtnl_link_ifmap) }, [IFLA_MTU] = { .type = NLA_U32 }, [IFLA_LINK] = { .type = NLA_U32 }, + [IFLA_MASTER] = { .type = NLA_U32 }, [IFLA_TXQLEN] = { .type = NLA_U32 }, [IFLA_WEIGHT] = { .type = NLA_U32 }, [IFLA_OPERSTATE] = { .type = NLA_U8 }, @@ -1178,6 +1179,41 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr *attr) return err; } +static int do_set_master(struct net_device *dev, int ifindex) +{ + struct net_device *master_dev; + const struct net_device_ops *ops; + int err; + + if (dev->master) { + if (dev->master->ifindex == ifindex) + return 0; + ops = dev->master->netdev_ops; + if (ops->ndo_del_slave) { + err = ops->ndo_del_slave(dev->master, dev); + if (err) + return err; + } else { + return -EOPNOTSUPP; + } + } + + if (ifindex) { + master_dev = __dev_get_by_index(dev_net(dev), ifindex); + if (!master_dev) + return -EINVAL; + ops = master_dev->netdev_ops; + if (ops->ndo_add_slave) { + err = ops->ndo_add_slave(master_dev, dev); + if (err) + return err; + } else { + return -EOPNOTSUPP; + } + } + return 0; +} + static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, struct nlattr **tb, char *ifname, int modified) { @@ -1301,6 +1337,13 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, goto errout; } + if (tb[IFLA_MASTER]) { + err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER])); + if (err) + goto errout; + modified = 1; + } + if (tb[IFLA_TXQLEN]) dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]); -- cgit v1.2.3 From fea952e5cc23ea94b4677ca20774cdc3cea014e2 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 14 Feb 2011 11:00:47 +0100 Subject: ALSA: core: sparse cleanups Change the core code where sparse complains. In most cases, this means just adding annotations to confirm that we indeed want to do the dirty things we're doing. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- include/sound/mixer_oss.h | 3 ++ include/sound/pcm.h | 91 +++++++++++++++++++++--------------------- sound/core/device.c | 7 ++-- sound/core/memalloc.c | 3 +- sound/core/oss/linear.c | 7 ++-- sound/core/oss/mixer_oss.c | 10 +++-- sound/core/oss/mulaw.c | 2 +- sound/core/oss/pcm_oss.c | 51 ++++++++++++----------- sound/core/oss/pcm_plugin.c | 40 ++++++++++--------- sound/core/oss/pcm_plugin.h | 11 ++--- sound/core/oss/route.c | 6 +-- sound/core/pcm.c | 10 ++--- sound/core/pcm_misc.c | 35 ++++++++-------- sound/core/pcm_native.c | 2 +- sound/core/seq/seq_clientmgr.c | 7 +--- sound/core/seq/seq_memory.c | 6 +-- sound/core/seq/seq_memory.h | 4 ++ sound/core/vmaster.c | 2 +- 18 files changed, 160 insertions(+), 137 deletions(-) (limited to 'include') diff --git a/include/sound/mixer_oss.h b/include/sound/mixer_oss.h index 51fbcb4a277a..13cb0b430a1b 100644 --- a/include/sound/mixer_oss.h +++ b/include/sound/mixer_oss.h @@ -73,6 +73,9 @@ struct snd_mixer_oss_file { struct snd_mixer_oss *mixer; }; +int snd_mixer_oss_ioctl_card(struct snd_card *card, + unsigned int cmd, unsigned long arg); + #endif /* CONFIG_SND_MIXER_OSS */ #endif /* __SOUND_MIXER_OSS_H */ diff --git a/include/sound/pcm.h b/include/sound/pcm.h index e731f8d71934..430a9cc045e2 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -136,48 +136,49 @@ struct snd_pcm_ops { SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000) #define SNDRV_PCM_RATE_8000_192000 (SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\ SNDRV_PCM_RATE_192000) -#define SNDRV_PCM_FMTBIT_S8 (1ULL << SNDRV_PCM_FORMAT_S8) -#define SNDRV_PCM_FMTBIT_U8 (1ULL << SNDRV_PCM_FORMAT_U8) -#define SNDRV_PCM_FMTBIT_S16_LE (1ULL << SNDRV_PCM_FORMAT_S16_LE) -#define SNDRV_PCM_FMTBIT_S16_BE (1ULL << SNDRV_PCM_FORMAT_S16_BE) -#define SNDRV_PCM_FMTBIT_U16_LE (1ULL << SNDRV_PCM_FORMAT_U16_LE) -#define SNDRV_PCM_FMTBIT_U16_BE (1ULL << SNDRV_PCM_FORMAT_U16_BE) -#define SNDRV_PCM_FMTBIT_S24_LE (1ULL << SNDRV_PCM_FORMAT_S24_LE) -#define SNDRV_PCM_FMTBIT_S24_BE (1ULL << SNDRV_PCM_FORMAT_S24_BE) -#define SNDRV_PCM_FMTBIT_U24_LE (1ULL << SNDRV_PCM_FORMAT_U24_LE) -#define SNDRV_PCM_FMTBIT_U24_BE (1ULL << SNDRV_PCM_FORMAT_U24_BE) -#define SNDRV_PCM_FMTBIT_S32_LE (1ULL << SNDRV_PCM_FORMAT_S32_LE) -#define SNDRV_PCM_FMTBIT_S32_BE (1ULL << SNDRV_PCM_FORMAT_S32_BE) -#define SNDRV_PCM_FMTBIT_U32_LE (1ULL << SNDRV_PCM_FORMAT_U32_LE) -#define SNDRV_PCM_FMTBIT_U32_BE (1ULL << SNDRV_PCM_FORMAT_U32_BE) -#define SNDRV_PCM_FMTBIT_FLOAT_LE (1ULL << SNDRV_PCM_FORMAT_FLOAT_LE) -#define SNDRV_PCM_FMTBIT_FLOAT_BE (1ULL << SNDRV_PCM_FORMAT_FLOAT_BE) -#define SNDRV_PCM_FMTBIT_FLOAT64_LE (1ULL << SNDRV_PCM_FORMAT_FLOAT64_LE) -#define SNDRV_PCM_FMTBIT_FLOAT64_BE (1ULL << SNDRV_PCM_FORMAT_FLOAT64_BE) -#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE) -#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE) -#define SNDRV_PCM_FMTBIT_MU_LAW (1ULL << SNDRV_PCM_FORMAT_MU_LAW) -#define SNDRV_PCM_FMTBIT_A_LAW (1ULL << SNDRV_PCM_FORMAT_A_LAW) -#define SNDRV_PCM_FMTBIT_IMA_ADPCM (1ULL << SNDRV_PCM_FORMAT_IMA_ADPCM) -#define SNDRV_PCM_FMTBIT_MPEG (1ULL << SNDRV_PCM_FORMAT_MPEG) -#define SNDRV_PCM_FMTBIT_GSM (1ULL << SNDRV_PCM_FORMAT_GSM) -#define SNDRV_PCM_FMTBIT_SPECIAL (1ULL << SNDRV_PCM_FORMAT_SPECIAL) -#define SNDRV_PCM_FMTBIT_S24_3LE (1ULL << SNDRV_PCM_FORMAT_S24_3LE) -#define SNDRV_PCM_FMTBIT_U24_3LE (1ULL << SNDRV_PCM_FORMAT_U24_3LE) -#define SNDRV_PCM_FMTBIT_S24_3BE (1ULL << SNDRV_PCM_FORMAT_S24_3BE) -#define SNDRV_PCM_FMTBIT_U24_3BE (1ULL << SNDRV_PCM_FORMAT_U24_3BE) -#define SNDRV_PCM_FMTBIT_S20_3LE (1ULL << SNDRV_PCM_FORMAT_S20_3LE) -#define SNDRV_PCM_FMTBIT_U20_3LE (1ULL << SNDRV_PCM_FORMAT_U20_3LE) -#define SNDRV_PCM_FMTBIT_S20_3BE (1ULL << SNDRV_PCM_FORMAT_S20_3BE) -#define SNDRV_PCM_FMTBIT_U20_3BE (1ULL << SNDRV_PCM_FORMAT_U20_3BE) -#define SNDRV_PCM_FMTBIT_S18_3LE (1ULL << SNDRV_PCM_FORMAT_S18_3LE) -#define SNDRV_PCM_FMTBIT_U18_3LE (1ULL << SNDRV_PCM_FORMAT_U18_3LE) -#define SNDRV_PCM_FMTBIT_S18_3BE (1ULL << SNDRV_PCM_FORMAT_S18_3BE) -#define SNDRV_PCM_FMTBIT_U18_3BE (1ULL << SNDRV_PCM_FORMAT_U18_3BE) -#define SNDRV_PCM_FMTBIT_G723_24 (1ULL << SNDRV_PCM_FORMAT_G723_24) -#define SNDRV_PCM_FMTBIT_G723_24_1B (1ULL << SNDRV_PCM_FORMAT_G723_24_1B) -#define SNDRV_PCM_FMTBIT_G723_40 (1ULL << SNDRV_PCM_FORMAT_G723_40) -#define SNDRV_PCM_FMTBIT_G723_40_1B (1ULL << SNDRV_PCM_FORMAT_G723_40_1B) +#define _SNDRV_PCM_FMTBIT(fmt) (1ULL << (__force int)SNDRV_PCM_FORMAT_##fmt) +#define SNDRV_PCM_FMTBIT_S8 _SNDRV_PCM_FMTBIT(S8) +#define SNDRV_PCM_FMTBIT_U8 _SNDRV_PCM_FMTBIT(U8) +#define SNDRV_PCM_FMTBIT_S16_LE _SNDRV_PCM_FMTBIT(S16_LE) +#define SNDRV_PCM_FMTBIT_S16_BE _SNDRV_PCM_FMTBIT(S16_BE) +#define SNDRV_PCM_FMTBIT_U16_LE _SNDRV_PCM_FMTBIT(U16_LE) +#define SNDRV_PCM_FMTBIT_U16_BE _SNDRV_PCM_FMTBIT(U16_BE) +#define SNDRV_PCM_FMTBIT_S24_LE _SNDRV_PCM_FMTBIT(S24_LE) +#define SNDRV_PCM_FMTBIT_S24_BE _SNDRV_PCM_FMTBIT(S24_BE) +#define SNDRV_PCM_FMTBIT_U24_LE _SNDRV_PCM_FMTBIT(U24_LE) +#define SNDRV_PCM_FMTBIT_U24_BE _SNDRV_PCM_FMTBIT(U24_BE) +#define SNDRV_PCM_FMTBIT_S32_LE _SNDRV_PCM_FMTBIT(S32_LE) +#define SNDRV_PCM_FMTBIT_S32_BE _SNDRV_PCM_FMTBIT(S32_BE) +#define SNDRV_PCM_FMTBIT_U32_LE _SNDRV_PCM_FMTBIT(U32_LE) +#define SNDRV_PCM_FMTBIT_U32_BE _SNDRV_PCM_FMTBIT(U32_BE) +#define SNDRV_PCM_FMTBIT_FLOAT_LE _SNDRV_PCM_FMTBIT(FLOAT_LE) +#define SNDRV_PCM_FMTBIT_FLOAT_BE _SNDRV_PCM_FMTBIT(FLOAT_BE) +#define SNDRV_PCM_FMTBIT_FLOAT64_LE _SNDRV_PCM_FMTBIT(FLOAT64_LE) +#define SNDRV_PCM_FMTBIT_FLOAT64_BE _SNDRV_PCM_FMTBIT(FLOAT64_BE) +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE _SNDRV_PCM_FMTBIT(IEC958_SUBFRAME_LE) +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE _SNDRV_PCM_FMTBIT(IEC958_SUBFRAME_BE) +#define SNDRV_PCM_FMTBIT_MU_LAW _SNDRV_PCM_FMTBIT(MU_LAW) +#define SNDRV_PCM_FMTBIT_A_LAW _SNDRV_PCM_FMTBIT(A_LAW) +#define SNDRV_PCM_FMTBIT_IMA_ADPCM _SNDRV_PCM_FMTBIT(IMA_ADPCM) +#define SNDRV_PCM_FMTBIT_MPEG _SNDRV_PCM_FMTBIT(MPEG) +#define SNDRV_PCM_FMTBIT_GSM _SNDRV_PCM_FMTBIT(GSM) +#define SNDRV_PCM_FMTBIT_SPECIAL _SNDRV_PCM_FMTBIT(SPECIAL) +#define SNDRV_PCM_FMTBIT_S24_3LE _SNDRV_PCM_FMTBIT(S24_3LE) +#define SNDRV_PCM_FMTBIT_U24_3LE _SNDRV_PCM_FMTBIT(U24_3LE) +#define SNDRV_PCM_FMTBIT_S24_3BE _SNDRV_PCM_FMTBIT(S24_3BE) +#define SNDRV_PCM_FMTBIT_U24_3BE _SNDRV_PCM_FMTBIT(U24_3BE) +#define SNDRV_PCM_FMTBIT_S20_3LE _SNDRV_PCM_FMTBIT(S20_3LE) +#define SNDRV_PCM_FMTBIT_U20_3LE _SNDRV_PCM_FMTBIT(U20_3LE) +#define SNDRV_PCM_FMTBIT_S20_3BE _SNDRV_PCM_FMTBIT(S20_3BE) +#define SNDRV_PCM_FMTBIT_U20_3BE _SNDRV_PCM_FMTBIT(U20_3BE) +#define SNDRV_PCM_FMTBIT_S18_3LE _SNDRV_PCM_FMTBIT(S18_3LE) +#define SNDRV_PCM_FMTBIT_U18_3LE _SNDRV_PCM_FMTBIT(U18_3LE) +#define SNDRV_PCM_FMTBIT_S18_3BE _SNDRV_PCM_FMTBIT(S18_3BE) +#define SNDRV_PCM_FMTBIT_U18_3BE _SNDRV_PCM_FMTBIT(U18_3BE) +#define SNDRV_PCM_FMTBIT_G723_24 _SNDRV_PCM_FMTBIT(G723_24) +#define SNDRV_PCM_FMTBIT_G723_24_1B _SNDRV_PCM_FMTBIT(G723_24_1B) +#define SNDRV_PCM_FMTBIT_G723_40 _SNDRV_PCM_FMTBIT(G723_40) +#define SNDRV_PCM_FMTBIT_G723_40_1B _SNDRV_PCM_FMTBIT(G723_40_1B) #ifdef SNDRV_LITTLE_ENDIAN #define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_LE @@ -490,7 +491,7 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream, int snd_pcm_status(struct snd_pcm_substream *substream, struct snd_pcm_status *status); int snd_pcm_start(struct snd_pcm_substream *substream); -int snd_pcm_stop(struct snd_pcm_substream *substream, int status); +int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t status); int snd_pcm_drain_done(struct snd_pcm_substream *substream); #ifdef CONFIG_PM int snd_pcm_suspend(struct snd_pcm_substream *substream); @@ -748,8 +749,8 @@ static inline const struct snd_interval *hw_param_interval_c(const struct snd_pc return ¶ms->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; } -#define params_access(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS)) -#define params_format(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_FORMAT)) +#define params_access(p) ((__force snd_pcm_access_t)snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS))) +#define params_format(p) ((__force snd_pcm_format_t)snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_FORMAT))) #define params_subformat(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_SUBFORMAT)) #define params_channels(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_CHANNELS)->min #define params_rate(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_RATE)->min diff --git a/sound/core/device.c b/sound/core/device.c index a67dfac08c03..2d1ad4b0cd65 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -225,15 +225,16 @@ int snd_device_free_all(struct snd_card *card, snd_device_cmd_t cmd) { struct snd_device *dev; int err; - unsigned int range_low, range_high; + unsigned int range_low, range_high, type; if (snd_BUG_ON(!card)) return -ENXIO; - range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE; + range_low = (__force unsigned int)cmd * SNDRV_DEV_TYPE_RANGE_SIZE; range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1; __again: list_for_each_entry(dev, &card->devices, list) { - if (dev->type >= range_low && dev->type <= range_high) { + type = (__force unsigned int)dev->type; + if (type >= range_low && type <= range_high) { if ((err = snd_device_free(card, dev->device_data)) < 0) return err; goto __again; diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 9e92441f9b78..16bd9c03679b 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -192,7 +192,8 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, dmab->bytes = 0; switch (type) { case SNDRV_DMA_TYPE_CONTINUOUS: - dmab->area = snd_malloc_pages(size, (unsigned long)device); + dmab->area = snd_malloc_pages(size, + (__force gfp_t)(unsigned long)device); dmab->addr = 0; break; #ifdef CONFIG_HAS_DMA diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c index 4c1d16827199..13b3f6f49fae 100644 --- a/sound/core/oss/linear.c +++ b/sound/core/oss/linear.c @@ -114,7 +114,8 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin, return frames; } -static void init_data(struct linear_priv *data, int src_format, int dst_format) +static void init_data(struct linear_priv *data, + snd_pcm_format_t src_format, snd_pcm_format_t dst_format) { int src_le, dst_le, src_bytes, dst_bytes; @@ -140,9 +141,9 @@ static void init_data(struct linear_priv *data, int src_format, int dst_format) if (snd_pcm_format_signed(src_format) != snd_pcm_format_signed(dst_format)) { if (dst_le) - data->flip = cpu_to_le32(0x80000000); + data->flip = (__force u32)cpu_to_le32(0x80000000); else - data->flip = cpu_to_be32(0x80000000); + data->flip = (__force u32)cpu_to_be32(0x80000000); } } diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 822dd56993ca..d8359cfeca15 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -190,9 +190,10 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer) return -EIO; if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ int err; - if ((err = mixer->get_recsrc(fmixer, &result)) < 0) + unsigned int index; + if ((err = mixer->get_recsrc(fmixer, &index)) < 0) return err; - result = 1 << result; + result = 1 << index; } else { struct snd_mixer_oss_slot *pslot; int chn; @@ -214,6 +215,7 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr struct snd_mixer_oss *mixer = fmixer->mixer; struct snd_mixer_oss_slot *pslot; int chn, active; + unsigned int index; int result = 0; if (mixer == NULL) @@ -222,8 +224,8 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr if (recsrc & ~mixer->oss_recsrc) recsrc &= ~mixer->oss_recsrc; mixer->put_recsrc(fmixer, ffz(~recsrc)); - mixer->get_recsrc(fmixer, &result); - result = 1 << result; + mixer->get_recsrc(fmixer, &index); + result = 1 << index; } for (chn = 0; chn < 31; chn++) { pslot = &mixer->slots[chn]; diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c index f7649d4d950b..7915564bd394 100644 --- a/sound/core/oss/mulaw.c +++ b/sound/core/oss/mulaw.c @@ -274,7 +274,7 @@ static snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin, return frames; } -static void init_data(struct mulaw_priv *data, int format) +static void init_data(struct mulaw_priv *data, snd_pcm_format_t format) { #ifdef SNDRV_LITTLE_ENDIAN data->cvt_endian = snd_pcm_format_big_endian(format) > 0; diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index a2e4eb324699..23c34a02894b 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -41,6 +41,7 @@ #include #include #include +#include #define OSS_ALSAEMULVER _SIOR ('M', 249, int) @@ -60,7 +61,6 @@ MODULE_PARM_DESC(nonblock_open, "Don't block opening busy PCM devices."); MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM); MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM1); -extern int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg); static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file); static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file); static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file); @@ -656,7 +656,7 @@ snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime) #define AFMT_AC3 0x00000400 #define AFMT_VORBIS 0x00000800 -static int snd_pcm_oss_format_from(int format) +static snd_pcm_format_t snd_pcm_oss_format_from(int format) { switch (format) { case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW; @@ -680,7 +680,7 @@ static int snd_pcm_oss_format_from(int format) } } -static int snd_pcm_oss_format_to(int format) +static int snd_pcm_oss_format_to(snd_pcm_format_t format) { switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW; @@ -843,7 +843,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) size_t oss_frame_size; int err; int direct; - int format, sformat, n; + snd_pcm_format_t format, sformat; + int n; struct snd_mask sformat_mask; struct snd_mask mask; @@ -868,11 +869,11 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) _snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0); snd_mask_none(&mask); if (atomic_read(&substream->mmap_count)) - snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); + snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); else { - snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED); + snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED); if (!direct) - snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); + snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); } err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask); if (err < 0) { @@ -891,19 +892,22 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) else sformat = snd_pcm_plug_slave_format(format, &sformat_mask); - if (sformat < 0 || !snd_mask_test(&sformat_mask, sformat)) { - for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) { - if (snd_mask_test(&sformat_mask, sformat) && + if ((__force int)sformat < 0 || + !snd_mask_test(&sformat_mask, (__force int)sformat)) { + for (sformat = (__force snd_pcm_format_t)0; + (__force int)sformat <= (__force int)SNDRV_PCM_FORMAT_LAST; + sformat = (__force snd_pcm_format_t)((__force int)sformat + 1)) { + if (snd_mask_test(&sformat_mask, (__force int)sformat) && snd_pcm_oss_format_to(sformat) >= 0) break; } - if (sformat > SNDRV_PCM_FORMAT_LAST) { + if ((__force int)sformat > (__force int)SNDRV_PCM_FORMAT_LAST) { snd_printd("Cannot find a format!!!\n"); err = -EINVAL; goto failure; } } - err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0); + err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, (__force int)sformat, 0); if (err < 0) goto failure; @@ -912,9 +916,9 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) } else { _snd_pcm_hw_params_any(params); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS, - SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); + (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT, - snd_pcm_oss_format_from(runtime->oss.format), 0); + (__force int)snd_pcm_oss_format_from(runtime->oss.format), 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE, @@ -1185,10 +1189,10 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const if (in_kernel) { mm_segment_t fs; fs = snd_enter_user(); - ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames); + ret = snd_pcm_lib_write(substream, (void __force __user *)ptr, frames); snd_leave_user(fs); } else { - ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames); + ret = snd_pcm_lib_write(substream, (void __force __user *)ptr, frames); } if (ret != -EPIPE && ret != -ESTRPIPE) break; @@ -1230,10 +1234,10 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p if (in_kernel) { mm_segment_t fs; fs = snd_enter_user(); - ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames); + ret = snd_pcm_lib_read(substream, (void __force __user *)ptr, frames); snd_leave_user(fs); } else { - ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames); + ret = snd_pcm_lib_read(substream, (void __force __user *)ptr, frames); } if (ret == -EPIPE) { if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { @@ -1333,7 +1337,7 @@ static ssize_t snd_pcm_oss_write2(struct snd_pcm_substream *substream, const cha struct snd_pcm_plugin_channel *channels; size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8; if (!in_kernel) { - if (copy_from_user(runtime->oss.buffer, (const char __user *)buf, bytes)) + if (copy_from_user(runtime->oss.buffer, (const char __force __user *)buf, bytes)) return -EFAULT; buf = runtime->oss.buffer; } @@ -1429,7 +1433,7 @@ static ssize_t snd_pcm_oss_read2(struct snd_pcm_substream *substream, char *buf, struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_sframes_t frames, frames1; #ifdef CONFIG_SND_PCM_OSS_PLUGINS - char __user *final_dst = (char __user *)buf; + char __user *final_dst = (char __force __user *)buf; if (runtime->oss.plugin_first) { struct snd_pcm_plugin_channel *channels; size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8; @@ -1549,6 +1553,7 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size) { struct snd_pcm_runtime *runtime; ssize_t result = 0; + snd_pcm_state_t state; long res; wait_queue_t wait; @@ -1570,9 +1575,9 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size) result = 0; set_current_state(TASK_INTERRUPTIBLE); snd_pcm_stream_lock_irq(substream); - res = runtime->status->state; + state = runtime->status->state; snd_pcm_stream_unlock_irq(substream); - if (res != SNDRV_PCM_STATE_RUNNING) { + if (state != SNDRV_PCM_STATE_RUNNING) { set_current_state(TASK_RUNNING); break; } @@ -1658,7 +1663,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) size1); size1 /= runtime->channels; /* frames */ fs = snd_enter_user(); - snd_pcm_lib_write(substream, (void __user *)runtime->oss.buffer, size1); + snd_pcm_lib_write(substream, (void __force __user *)runtime->oss.buffer, size1); snd_leave_user(fs); } } else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index 6751daa3bb50..71cc3ddf5c15 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -264,7 +264,7 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc return frames; } -static int snd_pcm_plug_formats(struct snd_mask *mask, int format) +static int snd_pcm_plug_formats(struct snd_mask *mask, snd_pcm_format_t format) { struct snd_mask formats = *mask; u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | @@ -276,16 +276,16 @@ static int snd_pcm_plug_formats(struct snd_mask *mask, int format) SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE); - snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW); + snd_mask_set(&formats, (__force int)SNDRV_PCM_FORMAT_MU_LAW); if (formats.bits[0] & (u32)linfmts) formats.bits[0] |= (u32)linfmts; if (formats.bits[1] & (u32)(linfmts >> 32)) formats.bits[1] |= (u32)(linfmts >> 32); - return snd_mask_test(&formats, format); + return snd_mask_test(&formats, (__force int)format); } -static int preferred_formats[] = { +static snd_pcm_format_t preferred_formats[] = { SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_FORMAT_S16_BE, SNDRV_PCM_FORMAT_U16_LE, @@ -306,24 +306,25 @@ static int preferred_formats[] = { SNDRV_PCM_FORMAT_U8 }; -int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask) +snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, + struct snd_mask *format_mask) { int i; - if (snd_mask_test(format_mask, format)) + if (snd_mask_test(format_mask, (__force int)format)) return format; - if (! snd_pcm_plug_formats(format_mask, format)) - return -EINVAL; + if (!snd_pcm_plug_formats(format_mask, format)) + return (__force snd_pcm_format_t)-EINVAL; if (snd_pcm_format_linear(format)) { unsigned int width = snd_pcm_format_width(format); int unsignd = snd_pcm_format_unsigned(format) > 0; int big = snd_pcm_format_big_endian(format) > 0; unsigned int badness, best = -1; - int best_format = -1; + snd_pcm_format_t best_format = (__force snd_pcm_format_t)-1; for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) { - int f = preferred_formats[i]; + snd_pcm_format_t f = preferred_formats[i]; unsigned int w; - if (!snd_mask_test(format_mask, f)) + if (!snd_mask_test(format_mask, (__force int)f)) continue; w = snd_pcm_format_width(f); if (w >= width) @@ -337,17 +338,20 @@ int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask) best = badness; } } - return best_format >= 0 ? best_format : -EINVAL; + if ((__force int)best_format >= 0) + return best_format; + else + return (__force snd_pcm_format_t)-EINVAL; } else { switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) { - int format1 = preferred_formats[i]; - if (snd_mask_test(format_mask, format1)) + snd_pcm_format_t format1 = preferred_formats[i]; + if (snd_mask_test(format_mask, (__force int)format1)) return format1; } default: - return -EINVAL; + return (__force snd_pcm_format_t)-EINVAL; } } } @@ -359,7 +363,7 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *plug, struct snd_pcm_plugin_format tmpformat; struct snd_pcm_plugin_format dstformat; struct snd_pcm_plugin_format srcformat; - int src_access, dst_access; + snd_pcm_access_t src_access, dst_access; struct snd_pcm_plugin *plugin = NULL; int err; int stream = snd_pcm_plug_stream(plug); @@ -641,7 +645,7 @@ snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, str } int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst_offset, - size_t samples, int format) + size_t samples, snd_pcm_format_t format) { /* FIXME: sub byte resolution and odd dst_offset */ unsigned char *dst; @@ -688,7 +692,7 @@ int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_area, size_t src_offset, const struct snd_pcm_channel_area *dst_area, size_t dst_offset, - size_t samples, int format) + size_t samples, snd_pcm_format_t format) { /* FIXME: sub byte resolution and odd dst_offset */ char *src, *dst; diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h index b9afab603711..a5035c2369a6 100644 --- a/sound/core/oss/pcm_plugin.h +++ b/sound/core/oss/pcm_plugin.h @@ -46,7 +46,7 @@ struct snd_pcm_plugin_channel { }; struct snd_pcm_plugin_format { - int format; + snd_pcm_format_t format; unsigned int rate; unsigned int channels; }; @@ -58,7 +58,7 @@ struct snd_pcm_plugin { struct snd_pcm_plugin_format dst_format; /* destination format */ int src_width; /* sample width in bits */ int dst_width; /* sample width in bits */ - int access; + snd_pcm_access_t access; snd_pcm_sframes_t (*src_frames)(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t dst_frames); snd_pcm_sframes_t (*dst_frames)(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t src_frames); snd_pcm_sframes_t (*client_channels)(struct snd_pcm_plugin *plugin, @@ -125,7 +125,8 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *slave_params); -int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask); +snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, + struct snd_mask *format_mask); int snd_pcm_plugin_append(struct snd_pcm_plugin *plugin); @@ -146,12 +147,12 @@ snd_pcm_sframes_t snd_pcm_plugin_client_channels(struct snd_pcm_plugin *plugin, int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_channel, size_t dst_offset, - size_t samples, int format); + size_t samples, snd_pcm_format_t format); int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_channel, size_t src_offset, const struct snd_pcm_channel_area *dst_channel, size_t dst_offset, - size_t samples, int format); + size_t samples, snd_pcm_format_t format); void *snd_pcm_plug_buf_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t size); void snd_pcm_plug_buf_unlock(struct snd_pcm_substream *plug, void *ptr); diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c index bbe25d8c450a..c8171f5783c8 100644 --- a/sound/core/oss/route.c +++ b/sound/core/oss/route.c @@ -25,7 +25,7 @@ #include "pcm_plugin.h" static void zero_areas(struct snd_pcm_plugin_channel *dvp, int ndsts, - snd_pcm_uframes_t frames, int format) + snd_pcm_uframes_t frames, snd_pcm_format_t format) { int dst = 0; for (; dst < ndsts; ++dst) { @@ -38,7 +38,7 @@ static void zero_areas(struct snd_pcm_plugin_channel *dvp, int ndsts, static inline void copy_area(const struct snd_pcm_plugin_channel *src_channel, struct snd_pcm_plugin_channel *dst_channel, - snd_pcm_uframes_t frames, int format) + snd_pcm_uframes_t frames, snd_pcm_format_t format) { dst_channel->enabled = 1; snd_pcm_area_copy(&src_channel->area, 0, &dst_channel->area, 0, frames, format); @@ -51,7 +51,7 @@ static snd_pcm_sframes_t route_transfer(struct snd_pcm_plugin *plugin, { int nsrcs, ndsts, dst; struct snd_pcm_plugin_channel *dvp; - int format; + snd_pcm_format_t format; if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) return -ENXIO; diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 6b4b1287b314..ee9abb2d9001 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -211,9 +211,9 @@ static char *snd_pcm_format_names[] = { const char *snd_pcm_format_name(snd_pcm_format_t format) { - if (format >= ARRAY_SIZE(snd_pcm_format_names)) + if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names)) return "Unknown"; - return snd_pcm_format_names[format]; + return snd_pcm_format_names[(__force unsigned int)format]; } EXPORT_SYMBOL_GPL(snd_pcm_format_name); @@ -269,12 +269,12 @@ static const char *snd_pcm_stream_name(int stream) static const char *snd_pcm_access_name(snd_pcm_access_t access) { - return snd_pcm_access_names[access]; + return snd_pcm_access_names[(__force int)access]; } static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) { - return snd_pcm_subformat_names[subformat]; + return snd_pcm_subformat_names[(__force int)subformat]; } static const char *snd_pcm_tstamp_mode_name(int mode) @@ -284,7 +284,7 @@ static const char *snd_pcm_tstamp_mode_name(int mode) static const char *snd_pcm_state_name(snd_pcm_state_t state) { - return snd_pcm_state_names[state]; + return snd_pcm_state_names[(__force int)state]; } #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 434af3c56d52..88f02e3866e0 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -35,7 +35,10 @@ struct pcm_format_data { unsigned char silence[8]; /* silence data to fill */ }; -static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = { +/* we do lots of calculations on snd_pcm_format_t; shut up sparse */ +#define INT __force int + +static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = { [SNDRV_PCM_FORMAT_S8] = { .width = 8, .phys = 8, .le = -1, .signd = 1, .silence = {}, @@ -215,9 +218,9 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = { int snd_pcm_format_signed(snd_pcm_format_t format) { int val; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; - if ((val = pcm_formats[format].signd) < 0) + if ((val = pcm_formats[(INT)format].signd) < 0) return -EINVAL; return val; } @@ -266,9 +269,9 @@ EXPORT_SYMBOL(snd_pcm_format_linear); int snd_pcm_format_little_endian(snd_pcm_format_t format) { int val; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; - if ((val = pcm_formats[format].le) < 0) + if ((val = pcm_formats[(INT)format].le) < 0) return -EINVAL; return val; } @@ -304,9 +307,9 @@ EXPORT_SYMBOL(snd_pcm_format_big_endian); int snd_pcm_format_width(snd_pcm_format_t format) { int val; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; - if ((val = pcm_formats[format].width) == 0) + if ((val = pcm_formats[(INT)format].width) == 0) return -EINVAL; return val; } @@ -323,9 +326,9 @@ EXPORT_SYMBOL(snd_pcm_format_width); int snd_pcm_format_physical_width(snd_pcm_format_t format) { int val; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; - if ((val = pcm_formats[format].phys) == 0) + if ((val = pcm_formats[(INT)format].phys) == 0) return -EINVAL; return val; } @@ -358,11 +361,11 @@ EXPORT_SYMBOL(snd_pcm_format_size); */ const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format) { - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return NULL; - if (! pcm_formats[format].phys) + if (! pcm_formats[(INT)format].phys) return NULL; - return pcm_formats[format].silence; + return pcm_formats[(INT)format].silence; } EXPORT_SYMBOL(snd_pcm_format_silence_64); @@ -382,16 +385,16 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int int width; unsigned char *dst, *pat; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; if (samples == 0) return 0; - width = pcm_formats[format].phys; /* physical width */ - pat = pcm_formats[format].silence; + width = pcm_formats[(INT)format].phys; /* physical width */ + pat = pcm_formats[(INT)format].silence; if (! width) return -EINVAL; /* signed or 1 byte data */ - if (pcm_formats[format].signd == 1 || width <= 8) { + if (pcm_formats[(INT)format].signd == 1 || width <= 8) { unsigned int bytes = samples * width / 8; memset(data, *pat, bytes); return 0; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 4be45e7be8ad..ae42b6509ce4 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -941,7 +941,7 @@ static struct action_ops snd_pcm_action_stop = { * * The state of each stream is then changed to the given state unconditionally. */ -int snd_pcm_stop(struct snd_pcm_substream *substream, int state) +int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t state) { return snd_pcm_action(&snd_pcm_action_stop, substream, state); } diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 99a485f13648..f2436d33fbf7 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1052,7 +1052,7 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf, } else { #ifdef CONFIG_COMPAT if (client->convert32 && snd_seq_ev_is_varusr(&event)) { - void *ptr = compat_ptr(event.data.raw32.d[1]); + void *ptr = (void __force *)compat_ptr(event.data.raw32.d[1]); event.data.ext.ptr = ptr; } #endif @@ -2407,7 +2407,7 @@ int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg) if (client == NULL) return -ENXIO; fs = snd_enter_user(); - result = snd_seq_do_ioctl(client, cmd, (void __user *)arg); + result = snd_seq_do_ioctl(client, cmd, (void __force __user *)arg); snd_leave_user(fs); return result; } @@ -2497,9 +2497,6 @@ static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer, } -void snd_seq_info_pool(struct snd_info_buffer *buffer, - struct snd_seq_pool *pool, char *space); - /* exported to seq_info.c */ void snd_seq_info_clients_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index 7fb55436287f..7f50c1437675 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -86,7 +86,7 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event, if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { char buf[32]; - char __user *curptr = (char __user *)event->data.ext.ptr; + char __user *curptr = (char __force __user *)event->data.ext.ptr; while (len > 0) { int size = sizeof(buf); if (len < size) @@ -157,7 +157,7 @@ int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { if (! in_kernel) return -EINVAL; - if (copy_from_user(buf, (void __user *)event->data.ext.ptr, len)) + if (copy_from_user(buf, (void __force __user *)event->data.ext.ptr, len)) return -EFAULT; return newlen; } @@ -343,7 +343,7 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event, tmp->event = src->event; src = src->next; } else if (is_usrptr) { - if (copy_from_user(&tmp->event, (char __user *)buf, size)) { + if (copy_from_user(&tmp->event, (char __force __user *)buf, size)) { err = -EFAULT; goto __error; } diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h index 63e91431a29f..4a2ec779b8a7 100644 --- a/sound/core/seq/seq_memory.h +++ b/sound/core/seq/seq_memory.h @@ -24,6 +24,8 @@ #include #include +struct snd_info_buffer; + /* container for sequencer event (internal use) */ struct snd_seq_event_cell { struct snd_seq_event event; @@ -99,5 +101,7 @@ void snd_sequencer_memory_done(void); /* polling */ int snd_seq_pool_poll_wait(struct snd_seq_pool *pool, struct file *file, poll_table *wait); +void snd_seq_info_pool(struct snd_info_buffer *buffer, + struct snd_seq_pool *pool, char *space); #endif diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c index 3b9b550109cb..a89948ae9e8d 100644 --- a/sound/core/vmaster.c +++ b/sound/core/vmaster.c @@ -18,7 +18,7 @@ * a subset of information returned via ctl info callback */ struct link_ctl_info { - int type; /* value type */ + snd_ctl_elem_type_t type; /* value type */ int count; /* item count */ int min_val, max_val; /* min, max values */ }; -- cgit v1.2.3 From dd3140588d9551235ebc2a0dacdca098e7677573 Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Mon, 7 Feb 2011 17:23:05 -0500 Subject: xen-gntalloc: Userspace grant allocation driver This allows a userspace application to allocate a shared page for implementing inter-domain communication or device drivers. These shared pages can be mapped using the gntdev device or by the kernel in another domain. Signed-off-by: Daniel De Graaf Signed-off-by: Konrad Rzeszutek Wilk --- drivers/xen/Kconfig | 8 + drivers/xen/Makefile | 2 + drivers/xen/gntalloc.c | 486 +++++++++++++++++++++++++++++++++++++++++++++++++ include/xen/gntalloc.h | 50 +++++ 4 files changed, 546 insertions(+) create mode 100644 drivers/xen/gntalloc.c create mode 100644 include/xen/gntalloc.h (limited to 'include') diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index 07bec09d1dad..a3d7afb86ae4 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -80,6 +80,14 @@ config XEN_GNTDEV help Allows userspace processes to use grants. +config XEN_GRANT_DEV_ALLOC + tristate "User-space grant reference allocator driver" + depends on XEN + help + Allows userspace processes to create pages with access granted + to other domains. This can be used to implement frontend drivers + or as part of an inter-domain shared memory channel. + config XEN_PLATFORM_PCI tristate "xen platform pci device driver" depends on XEN_PVHVM && PCI diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 5088cc2e6fe2..9585a1da52c6 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_XEN_XENCOMM) += xencomm.o obj-$(CONFIG_XEN_BALLOON) += balloon.o obj-$(CONFIG_XEN_DEV_EVTCHN) += xen-evtchn.o obj-$(CONFIG_XEN_GNTDEV) += xen-gntdev.o +obj-$(CONFIG_XEN_GRANT_DEV_ALLOC) += xen-gntalloc.o obj-$(CONFIG_XENFS) += xenfs/ obj-$(CONFIG_XEN_SYS_HYPERVISOR) += sys-hypervisor.o obj-$(CONFIG_XEN_PLATFORM_PCI) += xen-platform-pci.o @@ -18,5 +19,6 @@ obj-$(CONFIG_XEN_DOM0) += pci.o xen-evtchn-y := evtchn.o xen-gntdev-y := gntdev.o +xen-gntalloc-y := gntalloc.o xen-platform-pci-y := platform-pci.o diff --git a/drivers/xen/gntalloc.c b/drivers/xen/gntalloc.c new file mode 100644 index 000000000000..d06bf2b4cd07 --- /dev/null +++ b/drivers/xen/gntalloc.c @@ -0,0 +1,486 @@ +/****************************************************************************** + * gntalloc.c + * + * Device for creating grant references (in user-space) that may be shared + * with other domains. + * + * 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 + */ + +/* + * This driver exists to allow userspace programs in Linux to allocate kernel + * memory that will later be shared with another domain. Without this device, + * Linux userspace programs cannot create grant references. + * + * How this stuff works: + * X -> granting a page to Y + * Y -> mapping the grant from X + * + * 1. X uses the gntalloc device to allocate a page of kernel memory, P. + * 2. X creates an entry in the grant table that says domid(Y) can access P. + * This is done without a hypercall unless the grant table needs expansion. + * 3. X gives the grant reference identifier, GREF, to Y. + * 4. Y maps the page, either directly into kernel memory for use in a backend + * driver, or via a the gntdev device to map into the address space of an + * application running in Y. This is the first point at which Xen does any + * tracking of the page. + * 5. A program in X mmap()s a segment of the gntalloc device that corresponds + * to the shared page, and can now communicate with Y over the shared page. + * + * + * NOTE TO USERSPACE LIBRARIES: + * The grant allocation and mmap()ing are, naturally, two separate operations. + * You set up the sharing by calling the create ioctl() and then the mmap(). + * Teardown requires munmap() and either close() or ioctl(). + * + * WARNING: Since Xen does not allow a guest to forcibly end the use of a grant + * reference, this device can be used to consume kernel memory by leaving grant + * references mapped by another domain when an application exits. Therefore, + * there is a global limit on the number of pages that can be allocated. When + * all references to the page are unmapped, it will be freed during the next + * grant operation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static int limit = 1024; +module_param(limit, int, 0644); +MODULE_PARM_DESC(limit, "Maximum number of grants that may be allocated by " + "the gntalloc device"); + +static LIST_HEAD(gref_list); +static DEFINE_SPINLOCK(gref_lock); +static int gref_size; + +/* Metadata on a grant reference. */ +struct gntalloc_gref { + struct list_head next_gref; /* list entry gref_list */ + struct list_head next_file; /* list entry file->list, if open */ + struct page *page; /* The shared page */ + uint64_t file_index; /* File offset for mmap() */ + unsigned int users; /* Use count - when zero, waiting on Xen */ + grant_ref_t gref_id; /* The grant reference number */ +}; + +struct gntalloc_file_private_data { + struct list_head list; + uint64_t index; +}; + +static void __del_gref(struct gntalloc_gref *gref); + +static void do_cleanup(void) +{ + struct gntalloc_gref *gref, *n; + list_for_each_entry_safe(gref, n, &gref_list, next_gref) { + if (!gref->users) + __del_gref(gref); + } +} + +static int add_grefs(struct ioctl_gntalloc_alloc_gref *op, + uint32_t *gref_ids, struct gntalloc_file_private_data *priv) +{ + int i, rc, readonly; + LIST_HEAD(queue_gref); + LIST_HEAD(queue_file); + struct gntalloc_gref *gref; + + readonly = !(op->flags & GNTALLOC_FLAG_WRITABLE); + rc = -ENOMEM; + for (i = 0; i < op->count; i++) { + gref = kzalloc(sizeof(*gref), GFP_KERNEL); + if (!gref) + goto undo; + list_add_tail(&gref->next_gref, &queue_gref); + list_add_tail(&gref->next_file, &queue_file); + gref->users = 1; + gref->file_index = op->index + i * PAGE_SIZE; + gref->page = alloc_page(GFP_KERNEL|__GFP_ZERO); + if (!gref->page) + goto undo; + + /* Grant foreign access to the page. */ + gref->gref_id = gnttab_grant_foreign_access(op->domid, + pfn_to_mfn(page_to_pfn(gref->page)), readonly); + if (gref->gref_id < 0) { + rc = gref->gref_id; + goto undo; + } + gref_ids[i] = gref->gref_id; + } + + /* Add to gref lists. */ + spin_lock(&gref_lock); + list_splice_tail(&queue_gref, &gref_list); + list_splice_tail(&queue_file, &priv->list); + spin_unlock(&gref_lock); + + return 0; + +undo: + spin_lock(&gref_lock); + gref_size -= (op->count - i); + + list_for_each_entry(gref, &queue_file, next_file) { + /* __del_gref does not remove from queue_file */ + __del_gref(gref); + } + + /* It's possible for the target domain to map the just-allocated grant + * references by blindly guessing their IDs; if this is done, then + * __del_gref will leave them in the queue_gref list. They need to be + * added to the global list so that we can free them when they are no + * longer referenced. + */ + if (unlikely(!list_empty(&queue_gref))) + list_splice_tail(&queue_gref, &gref_list); + spin_unlock(&gref_lock); + return rc; +} + +static void __del_gref(struct gntalloc_gref *gref) +{ + if (gref->gref_id > 0) { + if (gnttab_query_foreign_access(gref->gref_id)) + return; + + if (!gnttab_end_foreign_access_ref(gref->gref_id, 0)) + return; + } + + gref_size--; + list_del(&gref->next_gref); + + if (gref->page) + __free_page(gref->page); + + kfree(gref); +} + +/* finds contiguous grant references in a file, returns the first */ +static struct gntalloc_gref *find_grefs(struct gntalloc_file_private_data *priv, + uint64_t index, uint32_t count) +{ + struct gntalloc_gref *rv = NULL, *gref; + list_for_each_entry(gref, &priv->list, next_file) { + if (gref->file_index == index && !rv) + rv = gref; + if (rv) { + if (gref->file_index != index) + return NULL; + index += PAGE_SIZE; + count--; + if (count == 0) + return rv; + } + } + return NULL; +} + +/* + * ------------------------------------- + * File operations. + * ------------------------------------- + */ +static int gntalloc_open(struct inode *inode, struct file *filp) +{ + struct gntalloc_file_private_data *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + goto out_nomem; + INIT_LIST_HEAD(&priv->list); + + filp->private_data = priv; + + pr_debug("%s: priv %p\n", __func__, priv); + + return 0; + +out_nomem: + return -ENOMEM; +} + +static int gntalloc_release(struct inode *inode, struct file *filp) +{ + struct gntalloc_file_private_data *priv = filp->private_data; + struct gntalloc_gref *gref; + + pr_debug("%s: priv %p\n", __func__, priv); + + spin_lock(&gref_lock); + while (!list_empty(&priv->list)) { + gref = list_entry(priv->list.next, + struct gntalloc_gref, next_file); + list_del(&gref->next_file); + gref->users--; + if (gref->users == 0) + __del_gref(gref); + } + kfree(priv); + spin_unlock(&gref_lock); + + return 0; +} + +static long gntalloc_ioctl_alloc(struct gntalloc_file_private_data *priv, + struct ioctl_gntalloc_alloc_gref __user *arg) +{ + int rc = 0; + struct ioctl_gntalloc_alloc_gref op; + uint32_t *gref_ids; + + pr_debug("%s: priv %p\n", __func__, priv); + + if (copy_from_user(&op, arg, sizeof(op))) { + rc = -EFAULT; + goto out; + } + + gref_ids = kzalloc(sizeof(gref_ids[0]) * op.count, GFP_TEMPORARY); + if (!gref_ids) { + rc = -ENOMEM; + goto out; + } + + spin_lock(&gref_lock); + /* Clean up pages that were at zero (local) users but were still mapped + * by remote domains. Since those pages count towards the limit that we + * are about to enforce, removing them here is a good idea. + */ + do_cleanup(); + if (gref_size + op.count > limit) { + spin_unlock(&gref_lock); + rc = -ENOSPC; + goto out_free; + } + gref_size += op.count; + op.index = priv->index; + priv->index += op.count * PAGE_SIZE; + spin_unlock(&gref_lock); + + rc = add_grefs(&op, gref_ids, priv); + if (rc < 0) + goto out_free; + + /* Once we finish add_grefs, it is unsafe to touch the new reference, + * since it is possible for a concurrent ioctl to remove it (by guessing + * its index). If the userspace application doesn't provide valid memory + * to write the IDs to, then it will need to close the file in order to + * release - which it will do by segfaulting when it tries to access the + * IDs to close them. + */ + if (copy_to_user(arg, &op, sizeof(op))) { + rc = -EFAULT; + goto out_free; + } + if (copy_to_user(arg->gref_ids, gref_ids, + sizeof(gref_ids[0]) * op.count)) { + rc = -EFAULT; + goto out_free; + } + +out_free: + kfree(gref_ids); +out: + return rc; +} + +static long gntalloc_ioctl_dealloc(struct gntalloc_file_private_data *priv, + void __user *arg) +{ + int i, rc = 0; + struct ioctl_gntalloc_dealloc_gref op; + struct gntalloc_gref *gref, *n; + + pr_debug("%s: priv %p\n", __func__, priv); + + if (copy_from_user(&op, arg, sizeof(op))) { + rc = -EFAULT; + goto dealloc_grant_out; + } + + spin_lock(&gref_lock); + gref = find_grefs(priv, op.index, op.count); + if (gref) { + /* Remove from the file list only, and decrease reference count. + * The later call to do_cleanup() will remove from gref_list and + * free the memory if the pages aren't mapped anywhere. + */ + for (i = 0; i < op.count; i++) { + n = list_entry(gref->next_file.next, + struct gntalloc_gref, next_file); + list_del(&gref->next_file); + gref->users--; + gref = n; + } + } else { + rc = -EINVAL; + } + + do_cleanup(); + + spin_unlock(&gref_lock); +dealloc_grant_out: + return rc; +} + +static long gntalloc_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct gntalloc_file_private_data *priv = filp->private_data; + + switch (cmd) { + case IOCTL_GNTALLOC_ALLOC_GREF: + return gntalloc_ioctl_alloc(priv, (void __user *)arg); + + case IOCTL_GNTALLOC_DEALLOC_GREF: + return gntalloc_ioctl_dealloc(priv, (void __user *)arg); + + default: + return -ENOIOCTLCMD; + } + + return 0; +} + +static void gntalloc_vma_close(struct vm_area_struct *vma) +{ + struct gntalloc_gref *gref = vma->vm_private_data; + if (!gref) + return; + + spin_lock(&gref_lock); + gref->users--; + if (gref->users == 0) + __del_gref(gref); + spin_unlock(&gref_lock); +} + +static struct vm_operations_struct gntalloc_vmops = { + .close = gntalloc_vma_close, +}; + +static int gntalloc_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct gntalloc_file_private_data *priv = filp->private_data; + struct gntalloc_gref *gref; + int count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + int rv, i; + + pr_debug("%s: priv %p, page %lu+%d\n", __func__, + priv, vma->vm_pgoff, count); + + if (!(vma->vm_flags & VM_SHARED)) { + printk(KERN_ERR "%s: Mapping must be shared.\n", __func__); + return -EINVAL; + } + + spin_lock(&gref_lock); + gref = find_grefs(priv, vma->vm_pgoff << PAGE_SHIFT, count); + if (gref == NULL) { + rv = -ENOENT; + pr_debug("%s: Could not find grant reference", + __func__); + goto out_unlock; + } + + vma->vm_private_data = gref; + + vma->vm_flags |= VM_RESERVED; + vma->vm_flags |= VM_DONTCOPY; + vma->vm_flags |= VM_PFNMAP | VM_PFN_AT_MMAP; + + vma->vm_ops = &gntalloc_vmops; + + for (i = 0; i < count; i++) { + gref->users++; + rv = vm_insert_page(vma, vma->vm_start + i * PAGE_SIZE, + gref->page); + if (rv) + goto out_unlock; + + gref = list_entry(gref->next_file.next, + struct gntalloc_gref, next_file); + } + rv = 0; + +out_unlock: + spin_unlock(&gref_lock); + return rv; +} + +static const struct file_operations gntalloc_fops = { + .owner = THIS_MODULE, + .open = gntalloc_open, + .release = gntalloc_release, + .unlocked_ioctl = gntalloc_ioctl, + .mmap = gntalloc_mmap +}; + +/* + * ------------------------------------- + * Module creation/destruction. + * ------------------------------------- + */ +static struct miscdevice gntalloc_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "xen/gntalloc", + .fops = &gntalloc_fops, +}; + +static int __init gntalloc_init(void) +{ + int err; + + if (!xen_domain()) + return -ENODEV; + + err = misc_register(&gntalloc_miscdev); + if (err != 0) { + printk(KERN_ERR "Could not register misc gntalloc device\n"); + return err; + } + + pr_debug("Created grant allocation device at %d,%d\n", + MISC_MAJOR, gntalloc_miscdev.minor); + + return 0; +} + +static void __exit gntalloc_exit(void) +{ + misc_deregister(&gntalloc_miscdev); +} + +module_init(gntalloc_init); +module_exit(gntalloc_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Carter Weatherly , " + "Daniel De Graaf "); +MODULE_DESCRIPTION("User-space grant reference allocator driver"); diff --git a/include/xen/gntalloc.h b/include/xen/gntalloc.h new file mode 100644 index 000000000000..bc3b85e8bff7 --- /dev/null +++ b/include/xen/gntalloc.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * gntalloc.h + * + * Interface to /dev/xen/gntalloc. + * + * Author: Daniel De Graaf + * + * This file is in the public domain. + */ + +#ifndef __LINUX_PUBLIC_GNTALLOC_H__ +#define __LINUX_PUBLIC_GNTALLOC_H__ + +/* + * Allocates a new page and creates a new grant reference. + */ +#define IOCTL_GNTALLOC_ALLOC_GREF \ +_IOC(_IOC_NONE, 'G', 5, sizeof(struct ioctl_gntalloc_alloc_gref)) +struct ioctl_gntalloc_alloc_gref { + /* IN parameters */ + /* The ID of the domain to be given access to the grants. */ + uint16_t domid; + /* Flags for this mapping */ + uint16_t flags; + /* Number of pages to map */ + uint32_t count; + /* OUT parameters */ + /* The offset to be used on a subsequent call to mmap(). */ + uint64_t index; + /* The grant references of the newly created grant, one per page */ + /* Variable size, depending on count */ + uint32_t gref_ids[1]; +}; + +#define GNTALLOC_FLAG_WRITABLE 1 + +/* + * Deallocates the grant reference, allowing the associated page to be freed if + * no other domains are using it. + */ +#define IOCTL_GNTALLOC_DEALLOC_GREF \ +_IOC(_IOC_NONE, 'G', 6, sizeof(struct ioctl_gntalloc_dealloc_gref)) +struct ioctl_gntalloc_dealloc_gref { + /* IN parameters */ + /* The offset returned in the map operation */ + uint64_t index; + /* Number of references to unmap */ + uint32_t count; +}; +#endif /* __LINUX_PUBLIC_GNTALLOC_H__ */ -- cgit v1.2.3 From bdc612dc6903c4ea06e40d02f84ad5e25d93459d Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Thu, 3 Feb 2011 12:19:04 -0500 Subject: xen/gntalloc,gntdev: Add unmap notify ioctl This ioctl allows the users of a shared page to be notified when the other end exits abnormally. [v2: updated description in structs] Signed-off-by: Daniel De Graaf Signed-off-by: Konrad Rzeszutek Wilk --- drivers/xen/gntalloc.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/xen/gntdev.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++- include/xen/gntalloc.h | 32 ++++++++++++++++++++++++++ include/xen/gntdev.h | 31 +++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/xen/gntalloc.c b/drivers/xen/gntalloc.c index d06bf2b4cd07..a7ffdfe19fc9 100644 --- a/drivers/xen/gntalloc.c +++ b/drivers/xen/gntalloc.c @@ -60,11 +60,13 @@ #include #include #include +#include #include #include #include #include +#include static int limit = 1024; module_param(limit, int, 0644); @@ -75,6 +77,12 @@ static LIST_HEAD(gref_list); static DEFINE_SPINLOCK(gref_lock); static int gref_size; +struct notify_info { + uint16_t pgoff:12; /* Bits 0-11: Offset of the byte to clear */ + uint16_t flags:2; /* Bits 12-13: Unmap notification flags */ + int event; /* Port (event channel) to notify */ +}; + /* Metadata on a grant reference. */ struct gntalloc_gref { struct list_head next_gref; /* list entry gref_list */ @@ -83,6 +91,7 @@ struct gntalloc_gref { uint64_t file_index; /* File offset for mmap() */ unsigned int users; /* Use count - when zero, waiting on Xen */ grant_ref_t gref_id; /* The grant reference number */ + struct notify_info notify; /* Unmap notification */ }; struct gntalloc_file_private_data { @@ -164,6 +173,16 @@ undo: static void __del_gref(struct gntalloc_gref *gref) { + if (gref->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) { + uint8_t *tmp = kmap(gref->page); + tmp[gref->notify.pgoff] = 0; + kunmap(gref->page); + } + if (gref->notify.flags & UNMAP_NOTIFY_SEND_EVENT) + notify_remote_via_evtchn(gref->notify.event); + + gref->notify.flags = 0; + if (gref->gref_id > 0) { if (gnttab_query_foreign_access(gref->gref_id)) return; @@ -349,6 +368,43 @@ dealloc_grant_out: return rc; } +static long gntalloc_ioctl_unmap_notify(struct gntalloc_file_private_data *priv, + void __user *arg) +{ + struct ioctl_gntalloc_unmap_notify op; + struct gntalloc_gref *gref; + uint64_t index; + int pgoff; + int rc; + + if (copy_from_user(&op, arg, sizeof(op))) + return -EFAULT; + + index = op.index & ~(PAGE_SIZE - 1); + pgoff = op.index & (PAGE_SIZE - 1); + + spin_lock(&gref_lock); + + gref = find_grefs(priv, index, 1); + if (!gref) { + rc = -ENOENT; + goto unlock_out; + } + + if (op.action & ~(UNMAP_NOTIFY_CLEAR_BYTE|UNMAP_NOTIFY_SEND_EVENT)) { + rc = -EINVAL; + goto unlock_out; + } + + gref->notify.flags = op.action; + gref->notify.pgoff = pgoff; + gref->notify.event = op.event_channel_port; + rc = 0; + unlock_out: + spin_unlock(&gref_lock); + return rc; +} + static long gntalloc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -361,6 +417,9 @@ static long gntalloc_ioctl(struct file *filp, unsigned int cmd, case IOCTL_GNTALLOC_DEALLOC_GREF: return gntalloc_ioctl_dealloc(priv, (void __user *)arg); + case IOCTL_GNTALLOC_SET_UNMAP_NOTIFY: + return gntalloc_ioctl_unmap_notify(priv, (void __user *)arg); + default: return -ENOIOCTLCMD; } diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index bcaf797216d1..9694a1a8b2e2 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +64,13 @@ struct gntdev_priv { struct mmu_notifier mn; }; +struct unmap_notify { + int flags; + /* Address relative to the start of the grant_map */ + int addr; + int event; +}; + struct grant_map { struct list_head next; struct vm_area_struct *vma; @@ -71,6 +79,7 @@ struct grant_map { int flags; int is_mapped; atomic_t users; + struct unmap_notify notify; struct ioctl_gntdev_grant_ref *grants; struct gnttab_map_grant_ref *map_ops; struct gnttab_unmap_grant_ref *unmap_ops; @@ -165,7 +174,7 @@ static struct grant_map *gntdev_find_map_index(struct gntdev_priv *priv, list_for_each_entry(map, &priv->maps, next) { if (map->index != index) continue; - if (map->count != count) + if (count && map->count != count) continue; return map; } @@ -184,6 +193,10 @@ static void gntdev_put_map(struct grant_map *map) atomic_sub(map->count, &pages_mapped); + if (map->notify.flags & UNMAP_NOTIFY_SEND_EVENT) { + notify_remote_via_evtchn(map->notify.event); + } + if (map->pages) { if (!use_ptemod) unmap_grant_pages(map, 0, map->count); @@ -274,6 +287,16 @@ static int unmap_grant_pages(struct grant_map *map, int offset, int pages) { int i, err = 0; + if (map->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) { + int pgno = (map->notify.addr >> PAGE_SHIFT); + if (pgno >= offset && pgno < offset + pages) { + uint8_t *tmp = kmap(map->pages[pgno]); + tmp[map->notify.addr & (PAGE_SIZE-1)] = 0; + kunmap(map->pages[pgno]); + map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE; + } + } + pr_debug("map %d+%d [%d+%d]\n", map->index, map->count, offset, pages); err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages, pages); if (err) @@ -519,6 +542,39 @@ static long gntdev_ioctl_get_offset_for_vaddr(struct gntdev_priv *priv, return 0; } +static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u) +{ + struct ioctl_gntdev_unmap_notify op; + struct grant_map *map; + int rc; + + if (copy_from_user(&op, u, sizeof(op))) + return -EFAULT; + + if (op.action & ~(UNMAP_NOTIFY_CLEAR_BYTE|UNMAP_NOTIFY_SEND_EVENT)) + return -EINVAL; + + spin_lock(&priv->lock); + + list_for_each_entry(map, &priv->maps, next) { + uint64_t begin = map->index << PAGE_SHIFT; + uint64_t end = (map->index + map->count) << PAGE_SHIFT; + if (op.index >= begin && op.index < end) + goto found; + } + rc = -ENOENT; + goto unlock_out; + + found: + map->notify.flags = op.action; + map->notify.addr = op.index - (map->index << PAGE_SHIFT); + map->notify.event = op.event_channel_port; + rc = 0; + unlock_out: + spin_unlock(&priv->lock); + return rc; +} + static long gntdev_ioctl(struct file *flip, unsigned int cmd, unsigned long arg) { @@ -535,6 +591,9 @@ static long gntdev_ioctl(struct file *flip, case IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR: return gntdev_ioctl_get_offset_for_vaddr(priv, ptr); + case IOCTL_GNTDEV_SET_UNMAP_NOTIFY: + return gntdev_ioctl_notify(priv, ptr); + default: pr_debug("priv %p, unknown cmd %x\n", priv, cmd); return -ENOIOCTLCMD; diff --git a/include/xen/gntalloc.h b/include/xen/gntalloc.h index bc3b85e8bff7..76bd58065f4f 100644 --- a/include/xen/gntalloc.h +++ b/include/xen/gntalloc.h @@ -47,4 +47,36 @@ struct ioctl_gntalloc_dealloc_gref { /* Number of references to unmap */ uint32_t count; }; + +/* + * Sets up an unmap notification within the page, so that the other side can do + * cleanup if this side crashes. Required to implement cross-domain robust + * mutexes or close notification on communication channels. + * + * Each mapped page only supports one notification; multiple calls referring to + * the same page overwrite the previous notification. You must clear the + * notification prior to the IOCTL_GNTALLOC_DEALLOC_GREF if you do not want it + * to occur. + */ +#define IOCTL_GNTALLOC_SET_UNMAP_NOTIFY \ +_IOC(_IOC_NONE, 'G', 7, sizeof(struct ioctl_gntalloc_unmap_notify)) +struct ioctl_gntalloc_unmap_notify { + /* IN parameters */ + /* Offset in the file descriptor for a byte within the page (same as + * used in mmap). If using UNMAP_NOTIFY_CLEAR_BYTE, this is the byte to + * be cleared. Otherwise, it can be any byte in the page whose + * notification we are adjusting. + */ + uint64_t index; + /* Action(s) to take on unmap */ + uint32_t action; + /* Event channel to notify */ + uint32_t event_channel_port; +}; + +/* Clear (set to zero) the byte specified by index */ +#define UNMAP_NOTIFY_CLEAR_BYTE 0x1 +/* Send an interrupt on the indicated event channel */ +#define UNMAP_NOTIFY_SEND_EVENT 0x2 + #endif /* __LINUX_PUBLIC_GNTALLOC_H__ */ diff --git a/include/xen/gntdev.h b/include/xen/gntdev.h index eb23f4188f5a..5304bd3c84c5 100644 --- a/include/xen/gntdev.h +++ b/include/xen/gntdev.h @@ -116,4 +116,35 @@ struct ioctl_gntdev_set_max_grants { uint32_t count; }; +/* + * Sets up an unmap notification within the page, so that the other side can do + * cleanup if this side crashes. Required to implement cross-domain robust + * mutexes or close notification on communication channels. + * + * Each mapped page only supports one notification; multiple calls referring to + * the same page overwrite the previous notification. You must clear the + * notification prior to the IOCTL_GNTALLOC_DEALLOC_GREF if you do not want it + * to occur. + */ +#define IOCTL_GNTDEV_SET_UNMAP_NOTIFY \ +_IOC(_IOC_NONE, 'G', 7, sizeof(struct ioctl_gntdev_unmap_notify)) +struct ioctl_gntdev_unmap_notify { + /* IN parameters */ + /* Offset in the file descriptor for a byte within the page (same as + * used in mmap). If using UNMAP_NOTIFY_CLEAR_BYTE, this is the byte to + * be cleared. Otherwise, it can be any byte in the page whose + * notification we are adjusting. + */ + uint64_t index; + /* Action(s) to take on unmap */ + uint32_t action; + /* Event channel to notify */ + uint32_t event_channel_port; +}; + +/* Clear (set to zero) the byte specified by index */ +#define UNMAP_NOTIFY_CLEAR_BYTE 0x1 +/* Send an interrupt on the indicated event channel */ +#define UNMAP_NOTIFY_SEND_EVENT 0x2 + #endif /* __LINUX_PUBLIC_GNTDEV_H__ */ -- cgit v1.2.3 From 642745184f82688eb3ef0cdfaa4ba632055be9af Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 7 Feb 2011 20:08:52 -0200 Subject: Bluetooth: Merge L2CAP and SCO modules into bluetooth.ko Actually doesn't make sense have these modules built separately. The L2CAP layer is needed by almost all Bluetooth protocols and profiles. There isn't any real use case without having L2CAP loaded. SCO is only essential for Audio transfers, but it is so small that we can have it loaded always in bluetooth.ko without problems. If you really doesn't want it you can disable SCO in the kernel config. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/bluetooth.h | 28 ++++++++++++++++++++++++++++ net/bluetooth/Kconfig | 10 ++-------- net/bluetooth/Makefile | 5 ++--- net/bluetooth/af_bluetooth.c | 32 ++++++++++++++++++++++++++++++-- net/bluetooth/l2cap_core.c | 16 ++-------------- net/bluetooth/sco.c | 16 ++-------------- 6 files changed, 66 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index ed7d775337e0..43750439c521 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -205,4 +205,32 @@ extern void bt_sysfs_cleanup(void); extern struct dentry *bt_debugfs; +#ifdef CONFIG_BT_L2CAP +int l2cap_init(void); +void l2cap_exit(void); +#else +static inline int l2cap_init(void) +{ + return 0; +} + +static inline void l2cap_exit(void) +{ +} +#endif + +#ifdef CONFIG_BT_SCO +int sco_init(void); +void sco_exit(void); +#else +static inline int sco_init(void) +{ + return 0; +} + +static inline void sco_exit(void) +{ +} +#endif + #endif /* __BLUETOOTH_H */ diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index e45eae66eaf3..c6f9c2fb4891 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -32,7 +32,7 @@ menuconfig BT more information, see . config BT_L2CAP - tristate "L2CAP protocol support" + bool "L2CAP protocol support" depends on BT select CRC16 help @@ -40,19 +40,13 @@ config BT_L2CAP connection oriented and connection-less data transport. L2CAP support is required for most Bluetooth applications. - Say Y here to compile L2CAP support into the kernel or say M to - compile it as module (l2cap). - config BT_SCO - tristate "SCO links support" + bool "SCO links support" depends on BT help SCO link provides voice transport over Bluetooth. SCO support is required for voice applications like Headset and Audio. - Say Y here to compile SCO support into the kernel or say M to - compile it as module (sco). - source "net/bluetooth/rfcomm/Kconfig" source "net/bluetooth/bnep/Kconfig" diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 339b42932b33..f04fe9a9d634 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -3,12 +3,11 @@ # obj-$(CONFIG_BT) += bluetooth.o -obj-$(CONFIG_BT_L2CAP) += l2cap.o -obj-$(CONFIG_BT_SCO) += sco.o obj-$(CONFIG_BT_RFCOMM) += rfcomm/ obj-$(CONFIG_BT_BNEP) += bnep/ obj-$(CONFIG_BT_CMTP) += cmtp/ obj-$(CONFIG_BT_HIDP) += hidp/ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o hci_sock.o hci_sysfs.o lib.o -l2cap-y := l2cap_core.o l2cap_sock.o +bluetooth-$(CONFIG_BT_L2CAP) += l2cap_core.o l2cap_sock.o +bluetooth-$(CONFIG_BT_SCO) += sco.o diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 2abfe2f30453..c258027bc8fe 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -40,7 +40,7 @@ #include -#define VERSION "2.15" +#define VERSION "2.16" /* Bluetooth sockets */ #define BT_MAX_PROTO 8 @@ -545,13 +545,41 @@ static int __init bt_init(void) BT_INFO("HCI device and connection manager initialized"); - hci_sock_init(); + err = hci_sock_init(); + if (err < 0) + goto error; + + err = l2cap_init(); + if (err < 0) { + hci_sock_cleanup(); + goto sock_err; + } + + err = sco_init(); + if (err < 0) { + l2cap_exit(); + goto sock_err; + } return 0; + +sock_err: + hci_sock_cleanup(); + +error: + sock_unregister(PF_BLUETOOTH); + bt_sysfs_cleanup(); + + return err; } static void __exit bt_exit(void) { + + sco_exit(); + + l2cap_exit(); + hci_sock_cleanup(); sock_unregister(PF_BLUETOOTH); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ba7f9da68998..6f054d906c6f 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -55,8 +55,6 @@ #include #include -#define VERSION "2.15" - int disable_ertm; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; @@ -3806,7 +3804,7 @@ static struct hci_proto l2cap_hci_proto = { .recv_acldata = l2cap_recv_acldata }; -static int __init l2cap_init(void) +int __init l2cap_init(void) { int err; @@ -3834,7 +3832,6 @@ static int __init l2cap_init(void) BT_ERR("Failed to create L2CAP debug file"); } - BT_INFO("L2CAP ver %s", VERSION); BT_INFO("L2CAP socket layer initialized"); return 0; @@ -3845,7 +3842,7 @@ error: return err; } -static void __exit l2cap_exit(void) +void l2cap_exit(void) { debugfs_remove(l2cap_debugfs); @@ -3866,14 +3863,5 @@ void l2cap_load(void) } EXPORT_SYMBOL(l2cap_load); -module_init(l2cap_init); -module_exit(l2cap_exit); - module_param(disable_ertm, bool, 0644); MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode"); - -MODULE_AUTHOR("Marcel Holtmann "); -MODULE_DESCRIPTION("Bluetooth L2CAP ver " VERSION); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("bt-proto-0"); diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 926ed39912ea..c9348ddda877 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -50,8 +50,6 @@ #include #include -#define VERSION "0.6" - static int disable_esco; static const struct proto_ops sco_sock_ops; @@ -1024,7 +1022,7 @@ static struct hci_proto sco_hci_proto = { .recv_scodata = sco_recv_scodata }; -static int __init sco_init(void) +int __init sco_init(void) { int err; @@ -1052,7 +1050,6 @@ static int __init sco_init(void) BT_ERR("Failed to create SCO debug file"); } - BT_INFO("SCO (Voice Link) ver %s", VERSION); BT_INFO("SCO socket layer initialized"); return 0; @@ -1062,7 +1059,7 @@ error: return err; } -static void __exit sco_exit(void) +void __exit sco_exit(void) { debugfs_remove(sco_debugfs); @@ -1075,14 +1072,5 @@ static void __exit sco_exit(void) proto_unregister(&sco_proto); } -module_init(sco_init); -module_exit(sco_exit); - module_param(disable_esco, bool, 0644); MODULE_PARM_DESC(disable_esco, "Disable eSCO connection creation"); - -MODULE_AUTHOR("Marcel Holtmann "); -MODULE_DESCRIPTION("Bluetooth SCO ver " VERSION); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("bt-proto-2"); -- cgit v1.2.3 From d606ef3fe0c57504b8e534c58498f73a6abc049a Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Mon, 14 Feb 2011 02:05:33 +0000 Subject: phy/micrel: add ability to support 50MHz RMII clock on KZS8051RNL Platform code can now set the MICREL_PHY_50MHZ_CLK bit of dev_flags in a fixup routine (registered with phy_register_fixup_for_uid()), to make the KZS8051RNL PHY work with 50MHz RMII reference clock. Cc: David J. Choi Signed-off-by: Baruch Siach Signed-off-by: David S. Miller --- drivers/net/phy/micrel.c | 24 ++++++++++++++++-------- include/linux/micrel_phy.h | 16 ++++++++++++++++ 2 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 include/linux/micrel_phy.h (limited to 'include') diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 0fd1678bc5a9..590f902deb6b 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -19,13 +19,7 @@ #include #include #include - -#define PHY_ID_KSZ9021 0x00221611 -#define PHY_ID_KS8737 0x00221720 -#define PHY_ID_KS8041 0x00221510 -#define PHY_ID_KS8051 0x00221550 -/* both for ks8001 Rev. A/B, and for ks8721 Rev 3. */ -#define PHY_ID_KS8001 0x0022161A +#include /* general Interrupt control/status reg in vendor specific block. */ #define MII_KSZPHY_INTCS 0x1B @@ -46,6 +40,7 @@ #define KSZPHY_CTRL_INT_ACTIVE_HIGH (1 << 9) #define KSZ9021_CTRL_INT_ACTIVE_HIGH (1 << 14) #define KS8737_CTRL_INT_ACTIVE_HIGH (1 << 14) +#define KSZ8051_RMII_50MHZ_CLK (1 << 7) static int kszphy_ack_interrupt(struct phy_device *phydev) { @@ -106,6 +101,19 @@ static int kszphy_config_init(struct phy_device *phydev) return 0; } +static int ks8051_config_init(struct phy_device *phydev) +{ + int regval; + + if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK) { + regval = phy_read(phydev, MII_KSZPHY_CTRL); + regval |= KSZ8051_RMII_50MHZ_CLK; + phy_write(phydev, MII_KSZPHY_CTRL, regval); + } + + return 0; +} + static struct phy_driver ks8737_driver = { .phy_id = PHY_ID_KS8737, .phy_id_mask = 0x00fffff0, @@ -142,7 +150,7 @@ static struct phy_driver ks8051_driver = { .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause), .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, - .config_init = kszphy_config_init, + .config_init = ks8051_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, diff --git a/include/linux/micrel_phy.h b/include/linux/micrel_phy.h new file mode 100644 index 000000000000..dd8da342a991 --- /dev/null +++ b/include/linux/micrel_phy.h @@ -0,0 +1,16 @@ +#ifndef _MICREL_PHY_H +#define _MICREL_PHY_H + +#define MICREL_PHY_ID_MASK 0x00fffff0 + +#define PHY_ID_KSZ9021 0x00221611 +#define PHY_ID_KS8737 0x00221720 +#define PHY_ID_KS8041 0x00221510 +#define PHY_ID_KS8051 0x00221550 +/* both for ks8001 Rev. A/B, and for ks8721 Rev 3. */ +#define PHY_ID_KS8001 0x0022161A + +/* struct phy_device dev_flags definitions */ +#define MICREL_PHY_50MHZ_CLK 0x00000001 + +#endif /* _MICREL_PHY_H */ -- cgit v1.2.3 From 54ebbe7ba51d97a28a9a406203d171d61858e4b9 Mon Sep 17 00:00:00 2001 From: Heinz Graalfs Date: Fri, 21 Jan 2011 10:06:54 +0000 Subject: oprofile: Introduce new oprofile sample add function (oprofile_add_ext_hw_sample) This patch introduces a new oprofile sample add function (oprofile_add_ext_hw_sample) that can also take task_struct as an argument, which is used by the hwsampler kernel module when copying hardware samples to OProfile buffers. Applied with following changes: * removed #include * whitespace changes * removed conditional compilation (CONFIG_HAVE_HWSAMPLER) * modified order of functions * fix missing function definition in header file Signed-off-by: Mahesh Salgaonkar Signed-off-by: Maran Pakkirisamy Signed-off-by: Heinz Graalfs Acked-by: Heiko Carstens Signed-off-by: Robert Richter --- drivers/oprofile/cpu_buffer.c | 24 +++++++++++++++++------- include/linux/oprofile.h | 7 +++++++ 2 files changed, 24 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index 59f55441e075..b8ef8ddcc292 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -258,8 +258,10 @@ op_add_sample(struct oprofile_cpu_buffer *cpu_buf, */ static int log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc, - unsigned long backtrace, int is_kernel, unsigned long event) + unsigned long backtrace, int is_kernel, unsigned long event, + struct task_struct *task) { + struct task_struct *tsk = task ? task : current; cpu_buf->sample_received++; if (pc == ESCAPE_CODE) { @@ -267,7 +269,7 @@ log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc, return 0; } - if (op_add_code(cpu_buf, backtrace, is_kernel, current)) + if (op_add_code(cpu_buf, backtrace, is_kernel, tsk)) goto fail; if (op_add_sample(cpu_buf, pc, event)) @@ -292,7 +294,8 @@ static inline void oprofile_end_trace(struct oprofile_cpu_buffer *cpu_buf) static inline void __oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, - unsigned long event, int is_kernel) + unsigned long event, int is_kernel, + struct task_struct *task) { struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(op_cpu_buffer); unsigned long backtrace = oprofile_backtrace_depth; @@ -301,7 +304,7 @@ __oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, * if log_sample() fail we can't backtrace since we lost the * source of this event */ - if (!log_sample(cpu_buf, pc, backtrace, is_kernel, event)) + if (!log_sample(cpu_buf, pc, backtrace, is_kernel, event, task)) /* failed */ return; @@ -313,10 +316,17 @@ __oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, oprofile_end_trace(cpu_buf); } +void oprofile_add_ext_hw_sample(unsigned long pc, struct pt_regs * const regs, + unsigned long event, int is_kernel, + struct task_struct *task) +{ + __oprofile_add_ext_sample(pc, regs, event, is_kernel, task); +} + void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, unsigned long event, int is_kernel) { - __oprofile_add_ext_sample(pc, regs, event, is_kernel); + __oprofile_add_ext_sample(pc, regs, event, is_kernel, NULL); } void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) @@ -332,7 +342,7 @@ void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) pc = ESCAPE_CODE; /* as this causes an early return. */ } - __oprofile_add_ext_sample(pc, regs, event, is_kernel); + __oprofile_add_ext_sample(pc, regs, event, is_kernel, NULL); } /* @@ -403,7 +413,7 @@ int oprofile_write_commit(struct op_entry *entry) void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event) { struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(op_cpu_buffer); - log_sample(cpu_buf, pc, 0, is_kernel, event); + log_sample(cpu_buf, pc, 0, is_kernel, event, NULL); } void oprofile_add_trace(unsigned long pc) diff --git a/include/linux/oprofile.h b/include/linux/oprofile.h index 1ca64113efe8..7f5cfd3b37dd 100644 --- a/include/linux/oprofile.h +++ b/include/linux/oprofile.h @@ -106,6 +106,13 @@ void oprofile_add_sample(struct pt_regs * const regs, unsigned long event); void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, unsigned long event, int is_kernel); +/** + * Add an hardware sample. + */ +void oprofile_add_ext_hw_sample(unsigned long pc, struct pt_regs * const regs, + unsigned long event, int is_kernel, + struct task_struct *task); + /* Use this instead when the PC value is not from the regs. Doesn't * backtrace. */ void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event); -- cgit v1.2.3 From 997dbb4967da248808850c250182ef2528fff2d1 Mon Sep 17 00:00:00 2001 From: Heinz Graalfs Date: Fri, 21 Jan 2011 10:06:53 +0000 Subject: oprofile, s390: Enhance OProfile to support System zs hardware sampling feature OProfile is enhanced to export all files for controlling System z's hardware sampling, and to invoke hwsampler exported functions to initialize and use System z's hardware sampling. The patch invokes hwsampler_setup() during oprofile init and exports following hwsampler files under oprofilefs if hwsampler's setup succeeded: A new directory for hardware sampling based files /dev/oprofile/hwsampling/ The userland daemon must explicitly write to the following files to disable (or enable) hardware based sampling /dev/oprofile/hwsampling/hwsampler to modify the actual sampling rate /dev/oprofile/hwsampling/hw_interval to modify the amount of sampling memory (measured in 4K pages) /dev/oprofile/hwsampling/hw_sdbt_blocks The following files are read only and show the possible minimum sampling rate /dev/oprofile/hwsampling/hw_min_interval the possible maximum sampling rate /dev/oprofile/hwsampling/hw_max_interval The patch splits the oprofile_timer_[init/exit] function so that it can be also called through user context (oprofilefs) to avoid kernel oops. Applied with following changes: * whitespace changes in Makefile and timer_int.c Signed-off-by: Mahesh Salgaonkar Signed-off-by: Maran Pakkirisamy Signed-off-by: Heinz Graalfs Acked-by: Heiko Carstens Signed-off-by: Robert Richter --- arch/s390/oprofile/Makefile | 2 +- arch/s390/oprofile/hwsampler_files.c | 146 +++++++++++++++++++++++++++++++++++ arch/s390/oprofile/init.c | 7 +- drivers/oprofile/oprof.c | 32 ++++++++ drivers/oprofile/oprof.h | 2 + drivers/oprofile/timer_int.c | 15 +++- include/linux/oprofile.h | 21 +++++ 7 files changed, 220 insertions(+), 5 deletions(-) create mode 100644 arch/s390/oprofile/hwsampler_files.c (limited to 'include') diff --git a/arch/s390/oprofile/Makefile b/arch/s390/oprofile/Makefile index d698cddcfbdd..00d8fc8e4429 100644 --- a/arch/s390/oprofile/Makefile +++ b/arch/s390/oprofile/Makefile @@ -6,4 +6,4 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ oprofilefs.o oprofile_stats.o \ timer_int.o ) -oprofile-y := $(DRIVER_OBJS) init.o backtrace.o hwsampler.o +oprofile-y := $(DRIVER_OBJS) init.o backtrace.o hwsampler.o hwsampler_files.o diff --git a/arch/s390/oprofile/hwsampler_files.c b/arch/s390/oprofile/hwsampler_files.c new file mode 100644 index 000000000000..493f7cc4e861 --- /dev/null +++ b/arch/s390/oprofile/hwsampler_files.c @@ -0,0 +1,146 @@ +/** + * arch/s390/oprofile/hwsampler_files.c + * + * Copyright IBM Corp. 2010 + * Author: Mahesh Salgaonkar (mahesh@linux.vnet.ibm.com) + */ +#include +#include +#include + +#include "hwsampler.h" + +#define DEFAULT_INTERVAL 4096 + +#define DEFAULT_SDBT_BLOCKS 1 +#define DEFAULT_SDB_BLOCKS 511 + +static unsigned long oprofile_hw_interval = DEFAULT_INTERVAL; +static unsigned long oprofile_min_interval; +static unsigned long oprofile_max_interval; + +static unsigned long oprofile_sdbt_blocks = DEFAULT_SDBT_BLOCKS; +static unsigned long oprofile_sdb_blocks = DEFAULT_SDB_BLOCKS; + +static unsigned long oprofile_hwsampler; + +static int oprofile_hwsampler_start(void) +{ + int retval; + + retval = hwsampler_allocate(oprofile_sdbt_blocks, oprofile_sdb_blocks); + if (retval) + return retval; + + retval = hwsampler_start_all(oprofile_hw_interval); + if (retval) + hwsampler_deallocate(); + + return retval; +} + +static void oprofile_hwsampler_stop(void) +{ + hwsampler_stop_all(); + hwsampler_deallocate(); + return; +} + +int oprofile_arch_set_hwsampler(struct oprofile_operations *ops) +{ + printk(KERN_INFO "oprofile: using hardware sampling\n"); + ops->start = oprofile_hwsampler_start; + ops->stop = oprofile_hwsampler_stop; + ops->cpu_type = "timer"; + + return 0; +} + +static ssize_t hwsampler_read(struct file *file, char __user *buf, + size_t count, loff_t *offset) +{ + return oprofilefs_ulong_to_user(oprofile_hwsampler, buf, count, offset); +} + +static ssize_t hwsampler_write(struct file *file, char const __user *buf, + size_t count, loff_t *offset) +{ + unsigned long val; + int retval; + + if (*offset) + return -EINVAL; + + retval = oprofilefs_ulong_from_user(&val, buf, count); + if (retval) + return retval; + + if (oprofile_hwsampler == val) + return -EINVAL; + + retval = oprofile_set_hwsampler(val); + + if (retval) + return retval; + + oprofile_hwsampler = val; + return count; +} + +static const struct file_operations hwsampler_fops = { + .read = hwsampler_read, + .write = hwsampler_write, +}; + +static int oprofile_create_hwsampling_files(struct super_block *sb, + struct dentry *root) +{ + struct dentry *hw_dir; + + /* reinitialize default values */ + oprofile_hwsampler = 1; + + hw_dir = oprofilefs_mkdir(sb, root, "hwsampling"); + if (!hw_dir) + return -EINVAL; + + oprofilefs_create_file(sb, hw_dir, "hwsampler", &hwsampler_fops); + oprofilefs_create_ulong(sb, hw_dir, "hw_interval", + &oprofile_hw_interval); + oprofilefs_create_ro_ulong(sb, hw_dir, "hw_min_interval", + &oprofile_min_interval); + oprofilefs_create_ro_ulong(sb, hw_dir, "hw_max_interval", + &oprofile_max_interval); + oprofilefs_create_ulong(sb, hw_dir, "hw_sdbt_blocks", + &oprofile_sdbt_blocks); + + return 0; +} + +int oprofile_hwsampler_init(struct oprofile_operations* ops) +{ + if (hwsampler_setup()) + return -ENODEV; + + /* + * create hwsampler files only if hwsampler_setup() succeeds. + */ + ops->create_files = oprofile_create_hwsampling_files; + oprofile_min_interval = hwsampler_query_min_interval(); + if (oprofile_min_interval < 0) { + oprofile_min_interval = 0; + return -ENODEV; + } + oprofile_max_interval = hwsampler_query_max_interval(); + if (oprofile_max_interval < 0) { + oprofile_max_interval = 0; + return -ENODEV; + } + oprofile_arch_set_hwsampler(ops); + return 0; +} + +void oprofile_hwsampler_exit(void) +{ + hwsampler_shutdown(); +} diff --git a/arch/s390/oprofile/init.c b/arch/s390/oprofile/init.c index 7a995113b918..f6b3f724f590 100644 --- a/arch/s390/oprofile/init.c +++ b/arch/s390/oprofile/init.c @@ -11,16 +11,21 @@ #include #include #include +#include +extern int oprofile_hwsampler_init(struct oprofile_operations* ops); +extern void oprofile_hwsampler_exit(void); extern void s390_backtrace(struct pt_regs * const regs, unsigned int depth); int __init oprofile_arch_init(struct oprofile_operations* ops) { ops->backtrace = s390_backtrace; - return -ENODEV; + + return oprofile_hwsampler_init(ops); } void oprofile_arch_exit(void) { + oprofile_hwsampler_exit(); } diff --git a/drivers/oprofile/oprof.c b/drivers/oprofile/oprof.c index f9bda64fcd1b..43b01daa91e1 100644 --- a/drivers/oprofile/oprof.c +++ b/drivers/oprofile/oprof.c @@ -239,6 +239,38 @@ int oprofile_set_ulong(unsigned long *addr, unsigned long val) return err; } +#ifdef CONFIG_HAVE_HWSAMPLER +int oprofile_set_hwsampler(unsigned long val) +{ + int err = 0; + + mutex_lock(&start_mutex); + + if (oprofile_started) { + err = -EBUSY; + goto out; + } + + switch (val) { + case 1: + /* Switch to hardware sampling. */ + __oprofile_timer_exit(); + err = oprofile_arch_set_hwsampler(&oprofile_ops); + break; + case 0: + printk(KERN_INFO "oprofile: using timer interrupt.\n"); + err = __oprofile_timer_init(&oprofile_ops); + break; + default: + err = -EINVAL; + } + +out: + mutex_unlock(&start_mutex); + return err; +} +#endif /* CONFIG_HAVE_HWSAMPLER */ + static int __init oprofile_init(void) { int err; diff --git a/drivers/oprofile/oprof.h b/drivers/oprofile/oprof.h index 177b73de5e5f..5a6ceb1954a2 100644 --- a/drivers/oprofile/oprof.h +++ b/drivers/oprofile/oprof.h @@ -35,7 +35,9 @@ struct dentry; void oprofile_create_files(struct super_block *sb, struct dentry *root); int oprofile_timer_init(struct oprofile_operations *ops); +int __oprofile_timer_init(struct oprofile_operations *ops); void oprofile_timer_exit(void); +void __oprofile_timer_exit(void); int oprofile_set_ulong(unsigned long *addr, unsigned long val); int oprofile_set_timeout(unsigned long time); diff --git a/drivers/oprofile/timer_int.c b/drivers/oprofile/timer_int.c index 010725117dbb..0099a458fd37 100644 --- a/drivers/oprofile/timer_int.c +++ b/drivers/oprofile/timer_int.c @@ -97,14 +97,13 @@ static struct notifier_block __refdata oprofile_cpu_notifier = { .notifier_call = oprofile_cpu_notify, }; -int __init oprofile_timer_init(struct oprofile_operations *ops) +int __oprofile_timer_init(struct oprofile_operations *ops) { int rc; rc = register_hotcpu_notifier(&oprofile_cpu_notifier); if (rc) return rc; - ops->create_files = NULL; ops->setup = NULL; ops->shutdown = NULL; ops->start = oprofile_hrtimer_start; @@ -113,7 +112,17 @@ int __init oprofile_timer_init(struct oprofile_operations *ops) return 0; } -void __exit oprofile_timer_exit(void) +int __init oprofile_timer_init(struct oprofile_operations *ops) +{ + return __oprofile_timer_init(ops); +} + +void __oprofile_timer_exit(void) { unregister_hotcpu_notifier(&oprofile_cpu_notifier); } + +void __exit oprofile_timer_exit(void) +{ + __oprofile_timer_exit(); +} diff --git a/include/linux/oprofile.h b/include/linux/oprofile.h index 7f5cfd3b37dd..b517d869e1ad 100644 --- a/include/linux/oprofile.h +++ b/include/linux/oprofile.h @@ -91,6 +91,27 @@ int oprofile_arch_init(struct oprofile_operations * ops); */ void oprofile_arch_exit(void); +#ifdef CONFIG_HAVE_HWSAMPLER +/** + * setup hardware sampler for oprofiling. + */ + +int oprofile_set_hwsampler(unsigned long); + +/** + * hardware sampler module initialization for the s390 arch + */ + +int oprofile_arch_set_hwsampler(struct oprofile_operations *ops); + +/** + * Add an s390 hardware sample. + */ +void oprofile_add_ext_hw_sample(unsigned long pc, struct pt_regs * const regs, + unsigned long event, int is_kernel, + struct task_struct *task); +#endif /* CONFIG_HAVE_HWSAMPLER */ + /** * Add a sample. This may be called from any context. */ -- cgit v1.2.3 From a0d76247e07abd14968adc4486aaa8e270e9c209 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Fri, 11 Feb 2011 17:31:44 +0100 Subject: oprofile, s390: Rework hwsampler implementation This patch is a rework of the hwsampler oprofile implementation that has been applied recently. Now there are less non-architectural changes. The only changes are: * introduction of oprofile_add_ext_hw_sample(), and * removal of section attributes of oprofile_timer_init/_exit(). To setup hwsampler for oprofile we need to modify start()/stop() callbacks and additional hwsampler control files in oprofilefs. We do not reinitialize the timer or hwsampler mode by restarting calling init/exit() anymore, instead hwsampler_running is used to switch the mode directly in oprofile_hwsampler_start/_stop(). For locking reasons there is also hwsampler_file that reflects the value in oprofilefs. The overall diffstat of the oprofile s390 hwsampler implemenation shows the low impact to non-architectural code: arch/Kconfig | 3 + arch/s390/Kconfig | 1 + arch/s390/oprofile/Makefile | 2 +- arch/s390/oprofile/hwsampler.c | 1256 ++++++++++++++++++++++++++++++++++ arch/s390/oprofile/hwsampler.h | 113 +++ arch/s390/oprofile/hwsampler_files.c | 162 +++++ arch/s390/oprofile/init.c | 6 +- drivers/oprofile/cpu_buffer.c | 24 +- drivers/oprofile/timer_int.c | 4 +- include/linux/oprofile.h | 7 + 10 files changed, 1567 insertions(+), 11 deletions(-) Acked-by: Heiko Carstens Signed-off-by: Robert Richter --- arch/s390/oprofile/hwsampler_files.c | 60 +++++++++++++++++++++++------------- arch/s390/oprofile/init.c | 1 - drivers/oprofile/oprof.c | 32 ------------------- drivers/oprofile/oprof.h | 2 -- drivers/oprofile/timer_int.c | 15 ++------- include/linux/oprofile.h | 21 ------------- 6 files changed, 41 insertions(+), 90 deletions(-) (limited to 'include') diff --git a/arch/s390/oprofile/hwsampler_files.c b/arch/s390/oprofile/hwsampler_files.c index 493f7cc4e861..2e1da2449ba9 100644 --- a/arch/s390/oprofile/hwsampler_files.c +++ b/arch/s390/oprofile/hwsampler_files.c @@ -8,6 +8,7 @@ #include #include +#include "../../../drivers/oprofile/oprof.h" #include "hwsampler.h" #define DEFAULT_INTERVAL 4096 @@ -22,12 +23,20 @@ static unsigned long oprofile_max_interval; static unsigned long oprofile_sdbt_blocks = DEFAULT_SDBT_BLOCKS; static unsigned long oprofile_sdb_blocks = DEFAULT_SDB_BLOCKS; -static unsigned long oprofile_hwsampler; +static int hwsampler_file; +static int hwsampler_running; /* start_mutex must be held to change */ + +static struct oprofile_operations timer_ops; static int oprofile_hwsampler_start(void) { int retval; + hwsampler_running = hwsampler_file; + + if (!hwsampler_running) + return timer_ops.start(); + retval = hwsampler_allocate(oprofile_sdbt_blocks, oprofile_sdb_blocks); if (retval) return retval; @@ -41,25 +50,20 @@ static int oprofile_hwsampler_start(void) static void oprofile_hwsampler_stop(void) { + if (!hwsampler_running) { + timer_ops.stop(); + return; + } + hwsampler_stop_all(); hwsampler_deallocate(); return; } -int oprofile_arch_set_hwsampler(struct oprofile_operations *ops) -{ - printk(KERN_INFO "oprofile: using hardware sampling\n"); - ops->start = oprofile_hwsampler_start; - ops->stop = oprofile_hwsampler_stop; - ops->cpu_type = "timer"; - - return 0; -} - static ssize_t hwsampler_read(struct file *file, char __user *buf, size_t count, loff_t *offset) { - return oprofilefs_ulong_to_user(oprofile_hwsampler, buf, count, offset); + return oprofilefs_ulong_to_user(hwsampler_file, buf, count, offset); } static ssize_t hwsampler_write(struct file *file, char const __user *buf, @@ -75,15 +79,16 @@ static ssize_t hwsampler_write(struct file *file, char const __user *buf, if (retval) return retval; - if (oprofile_hwsampler == val) - return -EINVAL; - - retval = oprofile_set_hwsampler(val); + if (oprofile_started) + /* + * save to do without locking as we set + * hwsampler_running in start() when start_mutex is + * held + */ + return -EBUSY; - if (retval) - return retval; + hwsampler_file = val; - oprofile_hwsampler = val; return count; } @@ -98,7 +103,7 @@ static int oprofile_create_hwsampling_files(struct super_block *sb, struct dentry *hw_dir; /* reinitialize default values */ - oprofile_hwsampler = 1; + hwsampler_file = 1; hw_dir = oprofilefs_mkdir(sb, root, "hwsampling"); if (!hw_dir) @@ -125,7 +130,6 @@ int oprofile_hwsampler_init(struct oprofile_operations* ops) /* * create hwsampler files only if hwsampler_setup() succeeds. */ - ops->create_files = oprofile_create_hwsampling_files; oprofile_min_interval = hwsampler_query_min_interval(); if (oprofile_min_interval < 0) { oprofile_min_interval = 0; @@ -136,11 +140,23 @@ int oprofile_hwsampler_init(struct oprofile_operations* ops) oprofile_max_interval = 0; return -ENODEV; } - oprofile_arch_set_hwsampler(ops); + + if (oprofile_timer_init(ops)) + return -ENODEV; + + printk(KERN_INFO "oprofile: using hardware sampling\n"); + + memcpy(&timer_ops, ops, sizeof(timer_ops)); + + ops->start = oprofile_hwsampler_start; + ops->stop = oprofile_hwsampler_stop; + ops->create_files = oprofile_create_hwsampling_files; + return 0; } void oprofile_hwsampler_exit(void) { + oprofile_timer_exit(); hwsampler_shutdown(); } diff --git a/arch/s390/oprofile/init.c b/arch/s390/oprofile/init.c index f6b3f724f590..059b44b9f171 100644 --- a/arch/s390/oprofile/init.c +++ b/arch/s390/oprofile/init.c @@ -11,7 +11,6 @@ #include #include #include -#include extern int oprofile_hwsampler_init(struct oprofile_operations* ops); extern void oprofile_hwsampler_exit(void); diff --git a/drivers/oprofile/oprof.c b/drivers/oprofile/oprof.c index 43b01daa91e1..f9bda64fcd1b 100644 --- a/drivers/oprofile/oprof.c +++ b/drivers/oprofile/oprof.c @@ -239,38 +239,6 @@ int oprofile_set_ulong(unsigned long *addr, unsigned long val) return err; } -#ifdef CONFIG_HAVE_HWSAMPLER -int oprofile_set_hwsampler(unsigned long val) -{ - int err = 0; - - mutex_lock(&start_mutex); - - if (oprofile_started) { - err = -EBUSY; - goto out; - } - - switch (val) { - case 1: - /* Switch to hardware sampling. */ - __oprofile_timer_exit(); - err = oprofile_arch_set_hwsampler(&oprofile_ops); - break; - case 0: - printk(KERN_INFO "oprofile: using timer interrupt.\n"); - err = __oprofile_timer_init(&oprofile_ops); - break; - default: - err = -EINVAL; - } - -out: - mutex_unlock(&start_mutex); - return err; -} -#endif /* CONFIG_HAVE_HWSAMPLER */ - static int __init oprofile_init(void) { int err; diff --git a/drivers/oprofile/oprof.h b/drivers/oprofile/oprof.h index 5a6ceb1954a2..177b73de5e5f 100644 --- a/drivers/oprofile/oprof.h +++ b/drivers/oprofile/oprof.h @@ -35,9 +35,7 @@ struct dentry; void oprofile_create_files(struct super_block *sb, struct dentry *root); int oprofile_timer_init(struct oprofile_operations *ops); -int __oprofile_timer_init(struct oprofile_operations *ops); void oprofile_timer_exit(void); -void __oprofile_timer_exit(void); int oprofile_set_ulong(unsigned long *addr, unsigned long val); int oprofile_set_timeout(unsigned long time); diff --git a/drivers/oprofile/timer_int.c b/drivers/oprofile/timer_int.c index 0099a458fd37..3ef44624f510 100644 --- a/drivers/oprofile/timer_int.c +++ b/drivers/oprofile/timer_int.c @@ -97,13 +97,14 @@ static struct notifier_block __refdata oprofile_cpu_notifier = { .notifier_call = oprofile_cpu_notify, }; -int __oprofile_timer_init(struct oprofile_operations *ops) +int oprofile_timer_init(struct oprofile_operations *ops) { int rc; rc = register_hotcpu_notifier(&oprofile_cpu_notifier); if (rc) return rc; + ops->create_files = NULL; ops->setup = NULL; ops->shutdown = NULL; ops->start = oprofile_hrtimer_start; @@ -112,17 +113,7 @@ int __oprofile_timer_init(struct oprofile_operations *ops) return 0; } -int __init oprofile_timer_init(struct oprofile_operations *ops) -{ - return __oprofile_timer_init(ops); -} - -void __oprofile_timer_exit(void) +void oprofile_timer_exit(void) { unregister_hotcpu_notifier(&oprofile_cpu_notifier); } - -void __exit oprofile_timer_exit(void) -{ - __oprofile_timer_exit(); -} diff --git a/include/linux/oprofile.h b/include/linux/oprofile.h index b517d869e1ad..7f5cfd3b37dd 100644 --- a/include/linux/oprofile.h +++ b/include/linux/oprofile.h @@ -91,27 +91,6 @@ int oprofile_arch_init(struct oprofile_operations * ops); */ void oprofile_arch_exit(void); -#ifdef CONFIG_HAVE_HWSAMPLER -/** - * setup hardware sampler for oprofiling. - */ - -int oprofile_set_hwsampler(unsigned long); - -/** - * hardware sampler module initialization for the s390 arch - */ - -int oprofile_arch_set_hwsampler(struct oprofile_operations *ops); - -/** - * Add an s390 hardware sample. - */ -void oprofile_add_ext_hw_sample(unsigned long pc, struct pt_regs * const regs, - unsigned long event, int is_kernel, - struct task_struct *task); -#endif /* CONFIG_HAVE_HWSAMPLER */ - /** * Add a sample. This may be called from any context. */ -- cgit v1.2.3 From c531a12ae63b6438a7859994aca23859f5706010 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 7 Feb 2011 20:19:30 -0200 Subject: Bluetooth: remove l2cap_load() hack l2cap_load() was added to trigger l2cap.ko module loading from the RFCOMM and BNEP modules. Now that L2CAP module is gone, we don't need it anymore. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 2 -- net/bluetooth/bnep/core.c | 2 -- net/bluetooth/cmtp/core.c | 2 -- net/bluetooth/hidp/core.c | 2 -- net/bluetooth/l2cap_core.c | 8 -------- net/bluetooth/rfcomm/core.c | 2 -- 6 files changed, 18 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 75ef0b2948f9..9fb87fe1aec3 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -455,6 +455,4 @@ void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int err); void l2cap_chan_del(struct sock *sk, int err); int l2cap_do_connect(struct sock *sk); -void l2cap_load(void); - #endif /* __L2CAP_H */ diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index 5868597534e5..03d4d1245d58 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -708,8 +708,6 @@ static int __init bnep_init(void) { char flt[50] = ""; - l2cap_load(); - #ifdef CONFIG_BT_BNEP_PROTO_FILTER strcat(flt, "protocol "); #endif diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c index 2cee71a714c4..964ea9126f9f 100644 --- a/net/bluetooth/cmtp/core.c +++ b/net/bluetooth/cmtp/core.c @@ -469,8 +469,6 @@ int cmtp_get_conninfo(struct cmtp_conninfo *ci) static int __init cmtp_init(void) { - l2cap_load(); - BT_INFO("CMTP (CAPI Emulation) ver %s", VERSION); cmtp_init_sockets(); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index e0de92952f32..2429ca2d7b06 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -1019,8 +1019,6 @@ static int __init hidp_init(void) { int ret; - l2cap_load(); - BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION); ret = hid_register_driver(&hidp_driver); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 6f054d906c6f..bd88641b4ae7 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3855,13 +3855,5 @@ void l2cap_exit(void) l2cap_cleanup_sockets(); } -void l2cap_load(void) -{ - /* Dummy function to trigger automatic L2CAP module loading by - * other modules that use L2CAP sockets but don't use any other - * symbols from it. */ -} -EXPORT_SYMBOL(l2cap_load); - module_param(disable_ertm, bool, 0644); MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode"); diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 6b83776534fb..c9973932456f 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -2154,8 +2154,6 @@ static int __init rfcomm_init(void) { int err; - l2cap_load(); - hci_register_cb(&rfcomm_cb); rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd"); -- cgit v1.2.3 From 41ac51eeda58a85b8a06d748cce7035cc77deebd Mon Sep 17 00:00:00 2001 From: Patrick Schaaf Date: Fri, 11 Feb 2011 14:01:12 +0100 Subject: ipvs: make "no destination available" message more informative When IP_VS schedulers do not find a destination, they output a terse "WLC: no destination available" message through kernel syslog, which I can not only make sense of because syslog puts them in a logfile together with keepalived checker results. This patch makes the output a bit more informative, by telling you which virtual service failed to find a destination. Example output: kernel: [1539214.552233] IPVS: wlc: TCP 192.168.8.30:22 - no destination available kernel: [1539299.674418] IPVS: wlc: FWM 22 0x00000016 - no destination available I have tested the code for IPv4 and FWM services, as you can see from the example; I do not have an IPv6 setup to test the third code path with. To avoid code duplication, I put a new function ip_vs_scheduler_err() into ip_vs_sched.c, and use that from the schedulers instead of calling IP_VS_ERR_RL directly. Signed-off-by: Patrick Schaaf Signed-off-by: Simon Horman --- include/net/ip_vs.h | 2 ++ net/netfilter/ipvs/ip_vs_lblc.c | 2 +- net/netfilter/ipvs/ip_vs_lblcr.c | 2 +- net/netfilter/ipvs/ip_vs_lc.c | 2 +- net/netfilter/ipvs/ip_vs_nq.c | 2 +- net/netfilter/ipvs/ip_vs_rr.c | 2 +- net/netfilter/ipvs/ip_vs_sched.c | 25 +++++++++++++++++++++++++ net/netfilter/ipvs/ip_vs_sed.c | 2 +- net/netfilter/ipvs/ip_vs_sh.c | 2 +- net/netfilter/ipvs/ip_vs_wlc.c | 2 +- net/netfilter/ipvs/ip_vs_wrr.c | 14 ++++++++------ 11 files changed, 43 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 5d75feadf4f4..93995494dfd4 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -1019,6 +1019,8 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb, extern int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, struct ip_vs_proto_data *pd); +extern void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg); + /* * IPVS control data and functions (from ip_vs_ctl.c) diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 00b5ffab3768..4a9c8cd19690 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -510,7 +510,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) /* No cache entry or it is invalid, time to schedule */ dest = __ip_vs_lblc_schedule(svc); if (!dest) { - IP_VS_ERR_RL("LBLC: no destination available\n"); + ip_vs_scheduler_err(svc, "no destination available"); return NULL; } diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index bfa25f1ea9e4..bd329b1e9589 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -692,7 +692,7 @@ ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) /* The cache entry is invalid, time to schedule */ dest = __ip_vs_lblcr_schedule(svc); if (!dest) { - IP_VS_ERR_RL("LBLCR: no destination available\n"); + ip_vs_scheduler_err(svc, "no destination available"); read_unlock(&svc->sched_lock); return NULL; } diff --git a/net/netfilter/ipvs/ip_vs_lc.c b/net/netfilter/ipvs/ip_vs_lc.c index 4f69db1fac56..60638007c6c7 100644 --- a/net/netfilter/ipvs/ip_vs_lc.c +++ b/net/netfilter/ipvs/ip_vs_lc.c @@ -70,7 +70,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) } if (!least) - IP_VS_ERR_RL("LC: no destination available\n"); + ip_vs_scheduler_err(svc, "no destination available"); else IP_VS_DBG_BUF(6, "LC: server %s:%u activeconns %d " "inactconns %d\n", diff --git a/net/netfilter/ipvs/ip_vs_nq.c b/net/netfilter/ipvs/ip_vs_nq.c index c413e1830823..984d9c137d84 100644 --- a/net/netfilter/ipvs/ip_vs_nq.c +++ b/net/netfilter/ipvs/ip_vs_nq.c @@ -99,7 +99,7 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) } if (!least) { - IP_VS_ERR_RL("NQ: no destination available\n"); + ip_vs_scheduler_err(svc, "no destination available"); return NULL; } diff --git a/net/netfilter/ipvs/ip_vs_rr.c b/net/netfilter/ipvs/ip_vs_rr.c index e210f37d8ea2..c49b388d1085 100644 --- a/net/netfilter/ipvs/ip_vs_rr.c +++ b/net/netfilter/ipvs/ip_vs_rr.c @@ -72,7 +72,7 @@ ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) q = q->next; } while (q != p); write_unlock(&svc->sched_lock); - IP_VS_ERR_RL("RR: no destination available\n"); + ip_vs_scheduler_err(svc, "no destination available"); return NULL; out: diff --git a/net/netfilter/ipvs/ip_vs_sched.c b/net/netfilter/ipvs/ip_vs_sched.c index 076ebe00435d..08dbdd5bc18f 100644 --- a/net/netfilter/ipvs/ip_vs_sched.c +++ b/net/netfilter/ipvs/ip_vs_sched.c @@ -29,6 +29,7 @@ #include +EXPORT_SYMBOL(ip_vs_scheduler_err); /* * IPVS scheduler list */ @@ -146,6 +147,30 @@ void ip_vs_scheduler_put(struct ip_vs_scheduler *scheduler) module_put(scheduler->module); } +/* + * Common error output helper for schedulers + */ + +void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg) +{ + if (svc->fwmark) { + IP_VS_ERR_RL("%s: FWM %u 0x%08X - %s\n", + svc->scheduler->name, svc->fwmark, + svc->fwmark, msg); +#ifdef CONFIG_IP_VS_IPV6 + } else if (svc->af == AF_INET6) { + IP_VS_ERR_RL("%s: %s [%pI6]:%d - %s\n", + svc->scheduler->name, + ip_vs_proto_name(svc->protocol), + &svc->addr.in6, ntohs(svc->port), msg); +#endif + } else { + IP_VS_ERR_RL("%s: %s %pI4:%d - %s\n", + svc->scheduler->name, + ip_vs_proto_name(svc->protocol), + &svc->addr.ip, ntohs(svc->port), msg); + } +} /* * Register a scheduler in the scheduler list diff --git a/net/netfilter/ipvs/ip_vs_sed.c b/net/netfilter/ipvs/ip_vs_sed.c index 1ab75a9dc400..89ead246ed3d 100644 --- a/net/netfilter/ipvs/ip_vs_sed.c +++ b/net/netfilter/ipvs/ip_vs_sed.c @@ -87,7 +87,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) goto nextstage; } } - IP_VS_ERR_RL("SED: no destination available\n"); + ip_vs_scheduler_err(svc, "no destination available"); return NULL; /* diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c index e6cc174fbc06..b5e2556c581a 100644 --- a/net/netfilter/ipvs/ip_vs_sh.c +++ b/net/netfilter/ipvs/ip_vs_sh.c @@ -223,7 +223,7 @@ ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) || !(dest->flags & IP_VS_DEST_F_AVAILABLE) || atomic_read(&dest->weight) <= 0 || is_overloaded(dest)) { - IP_VS_ERR_RL("SH: no destination available\n"); + ip_vs_scheduler_err(svc, "no destination available"); return NULL; } diff --git a/net/netfilter/ipvs/ip_vs_wlc.c b/net/netfilter/ipvs/ip_vs_wlc.c index bbddfdb10db2..fdf0f58962a4 100644 --- a/net/netfilter/ipvs/ip_vs_wlc.c +++ b/net/netfilter/ipvs/ip_vs_wlc.c @@ -75,7 +75,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) goto nextstage; } } - IP_VS_ERR_RL("WLC: no destination available\n"); + ip_vs_scheduler_err(svc, "no destination available"); return NULL; /* diff --git a/net/netfilter/ipvs/ip_vs_wrr.c b/net/netfilter/ipvs/ip_vs_wrr.c index 30db633f88f1..1ef41f50723c 100644 --- a/net/netfilter/ipvs/ip_vs_wrr.c +++ b/net/netfilter/ipvs/ip_vs_wrr.c @@ -147,8 +147,9 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) if (mark->cl == mark->cl->next) { /* no dest entry */ - IP_VS_ERR_RL("WRR: no destination available: " - "no destinations present\n"); + ip_vs_scheduler_err(svc, + "no destination available: " + "no destinations present"); dest = NULL; goto out; } @@ -162,8 +163,8 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) */ if (mark->cw == 0) { mark->cl = &svc->destinations; - IP_VS_ERR_RL("WRR: no destination " - "available\n"); + ip_vs_scheduler_err(svc, + "no destination available"); dest = NULL; goto out; } @@ -185,8 +186,9 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) /* back to the start, and no dest is found. It is only possible when all dests are OVERLOADED */ dest = NULL; - IP_VS_ERR_RL("WRR: no destination available: " - "all destinations are overloaded\n"); + ip_vs_scheduler_err(svc, + "no destination available: " + "all destinations are overloaded"); goto out; } } -- cgit v1.2.3 From d41d5a01631af821d3a3447e6613a316f5ee6c25 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 7 Feb 2011 17:02:20 +0100 Subject: cgroup: Fix cgroup_subsys::exit callback Make the ::exit method act like ::attach, it is after all very nearly the same thing. The bug had no effect on correctness - fixing it is an optimization for the scheduler. Also, later perf-cgroups patches rely on it. Signed-off-by: Peter Zijlstra Acked-by: Paul Menage LKML-Reference: <1297160655.13327.92.camel@laptop> Signed-off-by: Ingo Molnar --- include/linux/cgroup.h | 3 ++- kernel/cgroup.c | 31 ++++++++++++++++++------------- kernel/sched.c | 6 ++---- 3 files changed, 22 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index ce104e33cd22..38117d937332 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -474,7 +474,8 @@ struct cgroup_subsys { struct cgroup *old_cgrp, struct task_struct *tsk, bool threadgroup); void (*fork)(struct cgroup_subsys *ss, struct task_struct *task); - void (*exit)(struct cgroup_subsys *ss, struct task_struct *task); + void (*exit)(struct cgroup_subsys *ss, struct cgroup *cgrp, + struct cgroup *old_cgrp, struct task_struct *task); int (*populate)(struct cgroup_subsys *ss, struct cgroup *cgrp); void (*post_clone)(struct cgroup_subsys *ss, struct cgroup *cgrp); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index b24d7027b83c..f6495f33a355 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4230,20 +4230,8 @@ void cgroup_post_fork(struct task_struct *child) */ void cgroup_exit(struct task_struct *tsk, int run_callbacks) { - int i; struct css_set *cg; - - if (run_callbacks && need_forkexit_callback) { - /* - * modular subsystems can't use callbacks, so no need to lock - * the subsys array - */ - for (i = 0; i < CGROUP_BUILTIN_SUBSYS_COUNT; i++) { - struct cgroup_subsys *ss = subsys[i]; - if (ss->exit) - ss->exit(ss, tsk); - } - } + int i; /* * Unlink from the css_set task list if necessary. @@ -4261,7 +4249,24 @@ void cgroup_exit(struct task_struct *tsk, int run_callbacks) task_lock(tsk); cg = tsk->cgroups; tsk->cgroups = &init_css_set; + + if (run_callbacks && need_forkexit_callback) { + /* + * modular subsystems can't use callbacks, so no need to lock + * the subsys array + */ + for (i = 0; i < CGROUP_BUILTIN_SUBSYS_COUNT; i++) { + struct cgroup_subsys *ss = subsys[i]; + if (ss->exit) { + struct cgroup *old_cgrp = + rcu_dereference_raw(cg->subsys[i])->cgroup; + struct cgroup *cgrp = task_cgroup(tsk, i); + ss->exit(ss, cgrp, old_cgrp, tsk); + } + } + } task_unlock(tsk); + if (cg) put_css_set_taskexit(cg); } diff --git a/kernel/sched.c b/kernel/sched.c index e142e92f38da..79e611cd83dd 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -606,9 +606,6 @@ static inline struct task_group *task_group(struct task_struct *p) struct task_group *tg; struct cgroup_subsys_state *css; - if (p->flags & PF_EXITING) - return &root_task_group; - css = task_subsys_state_check(p, cpu_cgroup_subsys_id, lockdep_is_held(&task_rq(p)->lock)); tg = container_of(css, struct task_group, css); @@ -8863,7 +8860,8 @@ cpu_cgroup_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, } static void -cpu_cgroup_exit(struct cgroup_subsys *ss, struct task_struct *task) +cpu_cgroup_exit(struct cgroup_subsys *ss, struct cgroup *cgrp, + struct cgroup *old_cgrp, struct task_struct *task) { /* * cgroup_exit() is called in the copy_process() failure path. -- cgit v1.2.3 From e5d1367f17ba6a6fed5fd8b74e4d5720923e0c25 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Mon, 14 Feb 2011 11:20:01 +0200 Subject: perf: Add cgroup support This kernel patch adds the ability to filter monitoring based on container groups (cgroups). This is for use in per-cpu mode only. The cgroup to monitor is passed as a file descriptor in the pid argument to the syscall. The file descriptor must be opened to the cgroup name in the cgroup filesystem. For instance, if the cgroup name is foo and cgroupfs is mounted in /cgroup, then the file descriptor is opened to /cgroup/foo. Cgroup mode is activated by passing PERF_FLAG_PID_CGROUP in the flags argument to the syscall. For instance to measure in cgroup foo on CPU1 assuming cgroupfs is mounted under /cgroup: struct perf_event_attr attr; int cgroup_fd, fd; cgroup_fd = open("/cgroup/foo", O_RDONLY); fd = perf_event_open(&attr, cgroup_fd, 1, -1, PERF_FLAG_PID_CGROUP); close(cgroup_fd); Signed-off-by: Stephane Eranian [ added perf_cgroup_{exit,attach} ] Signed-off-by: Peter Zijlstra LKML-Reference: <4d590250.114ddf0a.689e.4482@mx.google.com> Signed-off-by: Ingo Molnar --- include/linux/cgroup.h | 1 + include/linux/cgroup_subsys.h | 4 + include/linux/perf_event.h | 33 ++- init/Kconfig | 10 + kernel/cgroup.c | 23 ++ kernel/perf_event.c | 638 +++++++++++++++++++++++++++++++++++++++--- 6 files changed, 671 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 38117d937332..e654fa239916 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -627,6 +627,7 @@ bool css_is_ancestor(struct cgroup_subsys_state *cg, /* Get id and depth of css */ unsigned short css_id(struct cgroup_subsys_state *css); unsigned short css_depth(struct cgroup_subsys_state *css); +struct cgroup_subsys_state *cgroup_css_from_dir(struct file *f, int id); #else /* !CONFIG_CGROUPS */ diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h index ccefff02b6cb..cdbfcb8780ec 100644 --- a/include/linux/cgroup_subsys.h +++ b/include/linux/cgroup_subsys.h @@ -65,4 +65,8 @@ SUBSYS(net_cls) SUBSYS(blkio) #endif +#ifdef CONFIG_CGROUP_PERF +SUBSYS(perf) +#endif + /* */ diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index dda5b0a3ff60..38c8b2554842 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -464,6 +464,7 @@ enum perf_callchain_context { #define PERF_FLAG_FD_NO_GROUP (1U << 0) #define PERF_FLAG_FD_OUTPUT (1U << 1) +#define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */ #ifdef __KERNEL__ /* @@ -471,6 +472,7 @@ enum perf_callchain_context { */ #ifdef CONFIG_PERF_EVENTS +# include # include # include #endif @@ -716,6 +718,22 @@ struct swevent_hlist { #define PERF_ATTACH_GROUP 0x02 #define PERF_ATTACH_TASK 0x04 +#ifdef CONFIG_CGROUP_PERF +/* + * perf_cgroup_info keeps track of time_enabled for a cgroup. + * This is a per-cpu dynamically allocated data structure. + */ +struct perf_cgroup_info { + u64 time; + u64 timestamp; +}; + +struct perf_cgroup { + struct cgroup_subsys_state css; + struct perf_cgroup_info *info; /* timing info, one per cpu */ +}; +#endif + /** * struct perf_event - performance event kernel representation: */ @@ -832,6 +850,11 @@ struct perf_event { struct event_filter *filter; #endif +#ifdef CONFIG_CGROUP_PERF + struct perf_cgroup *cgrp; /* cgroup event is attach to */ + int cgrp_defer_enabled; +#endif + #endif /* CONFIG_PERF_EVENTS */ }; @@ -886,6 +909,7 @@ struct perf_event_context { u64 generation; int pin_count; struct rcu_head rcu_head; + int nr_cgroups; /* cgroup events present */ }; /* @@ -905,6 +929,9 @@ struct perf_cpu_context { struct list_head rotation_list; int jiffies_interval; struct pmu *active_pmu; +#ifdef CONFIG_CGROUP_PERF + struct perf_cgroup *cgrp; +#endif }; struct perf_output_handle { @@ -1040,11 +1067,11 @@ have_event: __perf_sw_event(event_id, nr, nmi, regs, addr); } -extern atomic_t perf_task_events; +extern atomic_t perf_sched_events; static inline void perf_event_task_sched_in(struct task_struct *task) { - COND_STMT(&perf_task_events, __perf_event_task_sched_in(task)); + COND_STMT(&perf_sched_events, __perf_event_task_sched_in(task)); } static inline @@ -1052,7 +1079,7 @@ void perf_event_task_sched_out(struct task_struct *task, struct task_struct *nex { perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 1, NULL, 0); - COND_STMT(&perf_task_events, __perf_event_task_sched_out(task, next)); + COND_STMT(&perf_sched_events, __perf_event_task_sched_out(task, next)); } extern void perf_event_mmap(struct vm_area_struct *vma); diff --git a/init/Kconfig b/init/Kconfig index be788c0957d4..20d6bd919b8d 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -683,6 +683,16 @@ config CGROUP_MEM_RES_CTLR_SWAP_ENABLED select this option (if, for some reason, they need to disable it then noswapaccount does the trick). +config CGROUP_PERF + bool "Enable perf_event per-cpu per-container group (cgroup) monitoring" + depends on PERF_EVENTS && CGROUPS + help + This option extends the per-cpu mode to restrict monitoring to + threads which belong to the cgroup specificied and run on the + designated cpu. + + Say N if unsure. + menuconfig CGROUP_SCHED bool "Group CPU scheduler" depends on EXPERIMENTAL diff --git a/kernel/cgroup.c b/kernel/cgroup.c index f6495f33a355..95362d15128c 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4818,6 +4818,29 @@ css_get_next(struct cgroup_subsys *ss, int id, return ret; } +/* + * get corresponding css from file open on cgroupfs directory + */ +struct cgroup_subsys_state *cgroup_css_from_dir(struct file *f, int id) +{ + struct cgroup *cgrp; + struct inode *inode; + struct cgroup_subsys_state *css; + + inode = f->f_dentry->d_inode; + /* check in cgroup filesystem dir */ + if (inode->i_op != &cgroup_dir_inode_operations) + return ERR_PTR(-EBADF); + + if (id < 0 || id >= CGROUP_SUBSYS_COUNT) + return ERR_PTR(-EINVAL); + + /* get cgroup */ + cgrp = __d_cgrp(f->f_dentry); + css = cgrp->subsys[id]; + return css ? css : ERR_PTR(-ENOENT); +} + #ifdef CONFIG_CGROUP_DEBUG static struct cgroup_subsys_state *debug_create(struct cgroup_subsys *ss, struct cgroup *cont) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 3d3f282fa50e..65dcdc76d709 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -111,13 +111,23 @@ static int cpu_function_call(int cpu, int (*func) (void *info), void *info) return data.ret; } +#define PERF_FLAG_ALL (PERF_FLAG_FD_NO_GROUP |\ + PERF_FLAG_FD_OUTPUT |\ + PERF_FLAG_PID_CGROUP) + enum event_type_t { EVENT_FLEXIBLE = 0x1, EVENT_PINNED = 0x2, EVENT_ALL = EVENT_FLEXIBLE | EVENT_PINNED, }; -atomic_t perf_task_events __read_mostly; +/* + * perf_sched_events : >0 events exist + * perf_cgroup_events: >0 per-cpu cgroup events exist on this cpu + */ +atomic_t perf_sched_events __read_mostly; +static DEFINE_PER_CPU(atomic_t, perf_cgroup_events); + static atomic_t nr_mmap_events __read_mostly; static atomic_t nr_comm_events __read_mostly; static atomic_t nr_task_events __read_mostly; @@ -148,7 +158,11 @@ static void cpu_ctx_sched_out(struct perf_cpu_context *cpuctx, enum event_type_t event_type); static void cpu_ctx_sched_in(struct perf_cpu_context *cpuctx, - enum event_type_t event_type); + enum event_type_t event_type, + struct task_struct *task); + +static void update_context_time(struct perf_event_context *ctx); +static u64 perf_event_time(struct perf_event *event); void __weak perf_event_print_debug(void) { } @@ -162,6 +176,338 @@ static inline u64 perf_clock(void) return local_clock(); } +static inline struct perf_cpu_context * +__get_cpu_context(struct perf_event_context *ctx) +{ + return this_cpu_ptr(ctx->pmu->pmu_cpu_context); +} + +#ifdef CONFIG_CGROUP_PERF + +static inline struct perf_cgroup * +perf_cgroup_from_task(struct task_struct *task) +{ + return container_of(task_subsys_state(task, perf_subsys_id), + struct perf_cgroup, css); +} + +static inline bool +perf_cgroup_match(struct perf_event *event) +{ + struct perf_event_context *ctx = event->ctx; + struct perf_cpu_context *cpuctx = __get_cpu_context(ctx); + + return !event->cgrp || event->cgrp == cpuctx->cgrp; +} + +static inline void perf_get_cgroup(struct perf_event *event) +{ + css_get(&event->cgrp->css); +} + +static inline void perf_put_cgroup(struct perf_event *event) +{ + css_put(&event->cgrp->css); +} + +static inline void perf_detach_cgroup(struct perf_event *event) +{ + perf_put_cgroup(event); + event->cgrp = NULL; +} + +static inline int is_cgroup_event(struct perf_event *event) +{ + return event->cgrp != NULL; +} + +static inline u64 perf_cgroup_event_time(struct perf_event *event) +{ + struct perf_cgroup_info *t; + + t = per_cpu_ptr(event->cgrp->info, event->cpu); + return t->time; +} + +static inline void __update_cgrp_time(struct perf_cgroup *cgrp) +{ + struct perf_cgroup_info *info; + u64 now; + + now = perf_clock(); + + info = this_cpu_ptr(cgrp->info); + + info->time += now - info->timestamp; + info->timestamp = now; +} + +static inline void update_cgrp_time_from_cpuctx(struct perf_cpu_context *cpuctx) +{ + struct perf_cgroup *cgrp_out = cpuctx->cgrp; + if (cgrp_out) + __update_cgrp_time(cgrp_out); +} + +static inline void update_cgrp_time_from_event(struct perf_event *event) +{ + struct perf_cgroup *cgrp = perf_cgroup_from_task(current); + /* + * do not update time when cgroup is not active + */ + if (!event->cgrp || cgrp != event->cgrp) + return; + + __update_cgrp_time(event->cgrp); +} + +static inline void +perf_cgroup_set_timestamp(struct task_struct *task, u64 now) +{ + struct perf_cgroup *cgrp; + struct perf_cgroup_info *info; + + if (!task) + return; + + cgrp = perf_cgroup_from_task(task); + info = this_cpu_ptr(cgrp->info); + info->timestamp = now; +} + +#define PERF_CGROUP_SWOUT 0x1 /* cgroup switch out every event */ +#define PERF_CGROUP_SWIN 0x2 /* cgroup switch in events based on task */ + +/* + * reschedule events based on the cgroup constraint of task. + * + * mode SWOUT : schedule out everything + * mode SWIN : schedule in based on cgroup for next + */ +void perf_cgroup_switch(struct task_struct *task, int mode) +{ + struct perf_cpu_context *cpuctx; + struct pmu *pmu; + unsigned long flags; + + /* + * disable interrupts to avoid geting nr_cgroup + * changes via __perf_event_disable(). Also + * avoids preemption. + */ + local_irq_save(flags); + + /* + * we reschedule only in the presence of cgroup + * constrained events. + */ + rcu_read_lock(); + + list_for_each_entry_rcu(pmu, &pmus, entry) { + + cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); + + perf_pmu_disable(cpuctx->ctx.pmu); + + /* + * perf_cgroup_events says at least one + * context on this CPU has cgroup events. + * + * ctx->nr_cgroups reports the number of cgroup + * events for a context. + */ + if (cpuctx->ctx.nr_cgroups > 0) { + + if (mode & PERF_CGROUP_SWOUT) { + cpu_ctx_sched_out(cpuctx, EVENT_ALL); + /* + * must not be done before ctxswout due + * to event_filter_match() in event_sched_out() + */ + cpuctx->cgrp = NULL; + } + + if (mode & PERF_CGROUP_SWIN) { + /* set cgrp before ctxsw in to + * allow event_filter_match() to not + * have to pass task around + */ + cpuctx->cgrp = perf_cgroup_from_task(task); + cpu_ctx_sched_in(cpuctx, EVENT_ALL, task); + } + } + + perf_pmu_enable(cpuctx->ctx.pmu); + } + + rcu_read_unlock(); + + local_irq_restore(flags); +} + +static inline void perf_cgroup_sched_out(struct task_struct *task) +{ + perf_cgroup_switch(task, PERF_CGROUP_SWOUT); +} + +static inline void perf_cgroup_sched_in(struct task_struct *task) +{ + perf_cgroup_switch(task, PERF_CGROUP_SWIN); +} + +static inline int perf_cgroup_connect(int fd, struct perf_event *event, + struct perf_event_attr *attr, + struct perf_event *group_leader) +{ + struct perf_cgroup *cgrp; + struct cgroup_subsys_state *css; + struct file *file; + int ret = 0, fput_needed; + + file = fget_light(fd, &fput_needed); + if (!file) + return -EBADF; + + css = cgroup_css_from_dir(file, perf_subsys_id); + if (IS_ERR(css)) + return PTR_ERR(css); + + cgrp = container_of(css, struct perf_cgroup, css); + event->cgrp = cgrp; + + /* + * all events in a group must monitor + * the same cgroup because a task belongs + * to only one perf cgroup at a time + */ + if (group_leader && group_leader->cgrp != cgrp) { + perf_detach_cgroup(event); + ret = -EINVAL; + } else { + /* must be done before we fput() the file */ + perf_get_cgroup(event); + } + fput_light(file, fput_needed); + return ret; +} + +static inline void +perf_cgroup_set_shadow_time(struct perf_event *event, u64 now) +{ + struct perf_cgroup_info *t; + t = per_cpu_ptr(event->cgrp->info, event->cpu); + event->shadow_ctx_time = now - t->timestamp; +} + +static inline void +perf_cgroup_defer_enabled(struct perf_event *event) +{ + /* + * when the current task's perf cgroup does not match + * the event's, we need to remember to call the + * perf_mark_enable() function the first time a task with + * a matching perf cgroup is scheduled in. + */ + if (is_cgroup_event(event) && !perf_cgroup_match(event)) + event->cgrp_defer_enabled = 1; +} + +static inline void +perf_cgroup_mark_enabled(struct perf_event *event, + struct perf_event_context *ctx) +{ + struct perf_event *sub; + u64 tstamp = perf_event_time(event); + + if (!event->cgrp_defer_enabled) + return; + + event->cgrp_defer_enabled = 0; + + event->tstamp_enabled = tstamp - event->total_time_enabled; + list_for_each_entry(sub, &event->sibling_list, group_entry) { + if (sub->state >= PERF_EVENT_STATE_INACTIVE) { + sub->tstamp_enabled = tstamp - sub->total_time_enabled; + sub->cgrp_defer_enabled = 0; + } + } +} +#else /* !CONFIG_CGROUP_PERF */ + +static inline bool +perf_cgroup_match(struct perf_event *event) +{ + return true; +} + +static inline void perf_detach_cgroup(struct perf_event *event) +{} + +static inline int is_cgroup_event(struct perf_event *event) +{ + return 0; +} + +static inline u64 perf_cgroup_event_cgrp_time(struct perf_event *event) +{ + return 0; +} + +static inline void update_cgrp_time_from_event(struct perf_event *event) +{ +} + +static inline void update_cgrp_time_from_cpuctx(struct perf_cpu_context *cpuctx) +{ +} + +static inline void perf_cgroup_sched_out(struct task_struct *task) +{ +} + +static inline void perf_cgroup_sched_in(struct task_struct *task) +{ +} + +static inline int perf_cgroup_connect(pid_t pid, struct perf_event *event, + struct perf_event_attr *attr, + struct perf_event *group_leader) +{ + return -EINVAL; +} + +static inline void +perf_cgroup_set_timestamp(struct task_struct *task, u64 now) +{ +} + +void +perf_cgroup_switch(struct task_struct *task, struct task_struct *next) +{ +} + +static inline void +perf_cgroup_set_shadow_time(struct perf_event *event, u64 now) +{ +} + +static inline u64 perf_cgroup_event_time(struct perf_event *event) +{ + return 0; +} + +static inline void +perf_cgroup_defer_enabled(struct perf_event *event) +{ +} + +static inline void +perf_cgroup_mark_enabled(struct perf_event *event, + struct perf_event_context *ctx) +{ +} +#endif + void perf_pmu_disable(struct pmu *pmu) { int *count = this_cpu_ptr(pmu->pmu_disable_count); @@ -343,6 +689,10 @@ static void update_context_time(struct perf_event_context *ctx) static u64 perf_event_time(struct perf_event *event) { struct perf_event_context *ctx = event->ctx; + + if (is_cgroup_event(event)) + return perf_cgroup_event_time(event); + return ctx ? ctx->time : 0; } @@ -357,9 +707,20 @@ static void update_event_times(struct perf_event *event) if (event->state < PERF_EVENT_STATE_INACTIVE || event->group_leader->state < PERF_EVENT_STATE_INACTIVE) return; - - if (ctx->is_active) + /* + * in cgroup mode, time_enabled represents + * the time the event was enabled AND active + * tasks were in the monitored cgroup. This is + * independent of the activity of the context as + * there may be a mix of cgroup and non-cgroup events. + * + * That is why we treat cgroup events differently + * here. + */ + if (is_cgroup_event(event)) run_end = perf_event_time(event); + else if (ctx->is_active) + run_end = ctx->time; else run_end = event->tstamp_stopped; @@ -371,6 +732,7 @@ static void update_event_times(struct perf_event *event) run_end = perf_event_time(event); event->total_time_running = run_end - event->tstamp_running; + } /* @@ -419,6 +781,17 @@ list_add_event(struct perf_event *event, struct perf_event_context *ctx) list_add_tail(&event->group_entry, list); } + if (is_cgroup_event(event)) { + ctx->nr_cgroups++; + /* + * one more event: + * - that has cgroup constraint on event->cpu + * - that may need work on context switch + */ + atomic_inc(&per_cpu(perf_cgroup_events, event->cpu)); + jump_label_inc(&perf_sched_events); + } + list_add_rcu(&event->event_entry, &ctx->event_list); if (!ctx->nr_events) perf_pmu_rotate_start(ctx->pmu); @@ -545,6 +918,12 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx) event->attach_state &= ~PERF_ATTACH_CONTEXT; + if (is_cgroup_event(event)) { + ctx->nr_cgroups--; + atomic_dec(&per_cpu(perf_cgroup_events, event->cpu)); + jump_label_dec(&perf_sched_events); + } + ctx->nr_events--; if (event->attr.inherit_stat) ctx->nr_stat--; @@ -616,7 +995,8 @@ out: static inline int event_filter_match(struct perf_event *event) { - return event->cpu == -1 || event->cpu == smp_processor_id(); + return (event->cpu == -1 || event->cpu == smp_processor_id()) + && perf_cgroup_match(event); } static void @@ -634,7 +1014,7 @@ event_sched_out(struct perf_event *event, */ if (event->state == PERF_EVENT_STATE_INACTIVE && !event_filter_match(event)) { - delta = ctx->time - event->tstamp_stopped; + delta = tstamp - event->tstamp_stopped; event->tstamp_running += delta; event->tstamp_stopped = tstamp; } @@ -678,12 +1058,6 @@ group_sched_out(struct perf_event *group_event, cpuctx->exclusive = 0; } -static inline struct perf_cpu_context * -__get_cpu_context(struct perf_event_context *ctx) -{ - return this_cpu_ptr(ctx->pmu->pmu_cpu_context); -} - /* * Cross CPU call to remove a performance event * @@ -783,6 +1157,7 @@ static int __perf_event_disable(void *info) */ if (event->state >= PERF_EVENT_STATE_INACTIVE) { update_context_time(ctx); + update_cgrp_time_from_event(event); update_group_times(event); if (event == event->group_leader) group_sched_out(event, cpuctx, ctx); @@ -851,6 +1226,41 @@ retry: raw_spin_unlock_irq(&ctx->lock); } +static void perf_set_shadow_time(struct perf_event *event, + struct perf_event_context *ctx, + u64 tstamp) +{ + /* + * use the correct time source for the time snapshot + * + * We could get by without this by leveraging the + * fact that to get to this function, the caller + * has most likely already called update_context_time() + * and update_cgrp_time_xx() and thus both timestamp + * are identical (or very close). Given that tstamp is, + * already adjusted for cgroup, we could say that: + * tstamp - ctx->timestamp + * is equivalent to + * tstamp - cgrp->timestamp. + * + * Then, in perf_output_read(), the calculation would + * work with no changes because: + * - event is guaranteed scheduled in + * - no scheduled out in between + * - thus the timestamp would be the same + * + * But this is a bit hairy. + * + * So instead, we have an explicit cgroup call to remain + * within the time time source all along. We believe it + * is cleaner and simpler to understand. + */ + if (is_cgroup_event(event)) + perf_cgroup_set_shadow_time(event, tstamp); + else + event->shadow_ctx_time = tstamp - ctx->timestamp; +} + #define MAX_INTERRUPTS (~0ULL) static void perf_log_throttle(struct perf_event *event, int enable); @@ -891,7 +1301,7 @@ event_sched_in(struct perf_event *event, event->tstamp_running += tstamp - event->tstamp_stopped; - event->shadow_ctx_time = tstamp - ctx->timestamp; + perf_set_shadow_time(event, ctx, tstamp); if (!is_software_event(event)) cpuctx->active_oncpu++; @@ -1012,7 +1422,8 @@ static void add_event_to_ctx(struct perf_event *event, event->tstamp_stopped = tstamp; } -static void perf_event_context_sched_in(struct perf_event_context *ctx); +static void perf_event_context_sched_in(struct perf_event_context *ctx, + struct task_struct *tsk); /* * Cross CPU call to install and enable a performance event @@ -1033,11 +1444,17 @@ static int __perf_install_in_context(void *info) * which do context switches with IRQs enabled. */ if (ctx->task && !cpuctx->task_ctx) - perf_event_context_sched_in(ctx); + perf_event_context_sched_in(ctx, ctx->task); raw_spin_lock(&ctx->lock); ctx->is_active = 1; update_context_time(ctx); + /* + * update cgrp time only if current cgrp + * matches event->cgrp. Must be done before + * calling add_event_to_ctx() + */ + update_cgrp_time_from_event(event); add_event_to_ctx(event, ctx); @@ -1175,10 +1592,19 @@ static int __perf_event_enable(void *info) if (event->state >= PERF_EVENT_STATE_INACTIVE) goto unlock; + + /* + * set current task's cgroup time reference point + */ + perf_cgroup_set_timestamp(current, perf_clock()); + __perf_event_mark_enabled(event, ctx); - if (!event_filter_match(event)) + if (!event_filter_match(event)) { + if (is_cgroup_event(event)) + perf_cgroup_defer_enabled(event); goto unlock; + } /* * If the event is in a group and isn't the group leader, @@ -1307,6 +1733,7 @@ static void ctx_sched_out(struct perf_event_context *ctx, if (likely(!ctx->nr_events)) goto out; update_context_time(ctx); + update_cgrp_time_from_cpuctx(cpuctx); if (!ctx->nr_active) goto out; @@ -1496,6 +1923,14 @@ void __perf_event_task_sched_out(struct task_struct *task, for_each_task_context_nr(ctxn) perf_event_context_sched_out(task, ctxn, next); + + /* + * if cgroup events exist on this CPU, then we need + * to check if we have to switch out PMU state. + * cgroup event are system-wide mode only + */ + if (atomic_read(&__get_cpu_var(perf_cgroup_events))) + perf_cgroup_sched_out(task); } static void task_ctx_sched_out(struct perf_event_context *ctx, @@ -1534,6 +1969,10 @@ ctx_pinned_sched_in(struct perf_event_context *ctx, if (!event_filter_match(event)) continue; + /* may need to reset tstamp_enabled */ + if (is_cgroup_event(event)) + perf_cgroup_mark_enabled(event, ctx); + if (group_can_go_on(event, cpuctx, 1)) group_sched_in(event, cpuctx, ctx); @@ -1566,6 +2005,10 @@ ctx_flexible_sched_in(struct perf_event_context *ctx, if (!event_filter_match(event)) continue; + /* may need to reset tstamp_enabled */ + if (is_cgroup_event(event)) + perf_cgroup_mark_enabled(event, ctx); + if (group_can_go_on(event, cpuctx, can_add_hw)) { if (group_sched_in(event, cpuctx, ctx)) can_add_hw = 0; @@ -1576,15 +2019,19 @@ ctx_flexible_sched_in(struct perf_event_context *ctx, static void ctx_sched_in(struct perf_event_context *ctx, struct perf_cpu_context *cpuctx, - enum event_type_t event_type) + enum event_type_t event_type, + struct task_struct *task) { + u64 now; + raw_spin_lock(&ctx->lock); ctx->is_active = 1; if (likely(!ctx->nr_events)) goto out; - ctx->timestamp = perf_clock(); - + now = perf_clock(); + ctx->timestamp = now; + perf_cgroup_set_timestamp(task, now); /* * First go through the list and put on any pinned groups * in order to give them the best chance of going on. @@ -1601,11 +2048,12 @@ out: } static void cpu_ctx_sched_in(struct perf_cpu_context *cpuctx, - enum event_type_t event_type) + enum event_type_t event_type, + struct task_struct *task) { struct perf_event_context *ctx = &cpuctx->ctx; - ctx_sched_in(ctx, cpuctx, event_type); + ctx_sched_in(ctx, cpuctx, event_type, task); } static void task_ctx_sched_in(struct perf_event_context *ctx, @@ -1617,11 +2065,12 @@ static void task_ctx_sched_in(struct perf_event_context *ctx, if (cpuctx->task_ctx == ctx) return; - ctx_sched_in(ctx, cpuctx, event_type); + ctx_sched_in(ctx, cpuctx, event_type, NULL); cpuctx->task_ctx = ctx; } -static void perf_event_context_sched_in(struct perf_event_context *ctx) +static void perf_event_context_sched_in(struct perf_event_context *ctx, + struct task_struct *task) { struct perf_cpu_context *cpuctx; @@ -1637,9 +2086,9 @@ static void perf_event_context_sched_in(struct perf_event_context *ctx) */ cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE); - ctx_sched_in(ctx, cpuctx, EVENT_PINNED); - cpu_ctx_sched_in(cpuctx, EVENT_FLEXIBLE); - ctx_sched_in(ctx, cpuctx, EVENT_FLEXIBLE); + ctx_sched_in(ctx, cpuctx, EVENT_PINNED, task); + cpu_ctx_sched_in(cpuctx, EVENT_FLEXIBLE, task); + ctx_sched_in(ctx, cpuctx, EVENT_FLEXIBLE, task); cpuctx->task_ctx = ctx; @@ -1672,8 +2121,15 @@ void __perf_event_task_sched_in(struct task_struct *task) if (likely(!ctx)) continue; - perf_event_context_sched_in(ctx); + perf_event_context_sched_in(ctx, task); } + /* + * if cgroup events exist on this CPU, then we need + * to check if we have to switch in PMU state. + * cgroup event are system-wide mode only + */ + if (atomic_read(&__get_cpu_var(perf_cgroup_events))) + perf_cgroup_sched_in(task); } static u64 perf_calculate_period(struct perf_event *event, u64 nsec, u64 count) @@ -1873,7 +2329,7 @@ static void perf_rotate_context(struct perf_cpu_context *cpuctx) if (ctx) rotate_ctx(ctx); - cpu_ctx_sched_in(cpuctx, EVENT_FLEXIBLE); + cpu_ctx_sched_in(cpuctx, EVENT_FLEXIBLE, current); if (ctx) task_ctx_sched_in(ctx, EVENT_FLEXIBLE); @@ -1952,7 +2408,7 @@ static void perf_event_enable_on_exec(struct perf_event_context *ctx) raw_spin_unlock(&ctx->lock); - perf_event_context_sched_in(ctx); + perf_event_context_sched_in(ctx, ctx->task); out: local_irq_restore(flags); } @@ -1977,8 +2433,10 @@ static void __perf_event_read(void *info) return; raw_spin_lock(&ctx->lock); - if (ctx->is_active) + if (ctx->is_active) { update_context_time(ctx); + update_cgrp_time_from_event(event); + } update_event_times(event); if (event->state == PERF_EVENT_STATE_ACTIVE) event->pmu->read(event); @@ -2009,8 +2467,10 @@ static u64 perf_event_read(struct perf_event *event) * (e.g., thread is blocked), in that case * we cannot update context time */ - if (ctx->is_active) + if (ctx->is_active) { update_context_time(ctx); + update_cgrp_time_from_event(event); + } update_event_times(event); raw_spin_unlock_irqrestore(&ctx->lock, flags); } @@ -2395,7 +2855,7 @@ static void free_event(struct perf_event *event) if (!event->parent) { if (event->attach_state & PERF_ATTACH_TASK) - jump_label_dec(&perf_task_events); + jump_label_dec(&perf_sched_events); if (event->attr.mmap || event->attr.mmap_data) atomic_dec(&nr_mmap_events); if (event->attr.comm) @@ -2411,6 +2871,9 @@ static void free_event(struct perf_event *event) event->buffer = NULL; } + if (is_cgroup_event(event)) + perf_detach_cgroup(event); + if (event->destroy) event->destroy(event); @@ -5300,6 +5763,7 @@ static void task_clock_event_read(struct perf_event *event) if (!in_nmi()) { update_context_time(event->ctx); + update_cgrp_time_from_event(event); time = event->ctx->time; } else { u64 now = perf_clock(); @@ -5725,7 +6189,7 @@ done: if (!event->parent) { if (event->attach_state & PERF_ATTACH_TASK) - jump_label_inc(&perf_task_events); + jump_label_inc(&perf_sched_events); if (event->attr.mmap || event->attr.mmap_data) atomic_inc(&nr_mmap_events); if (event->attr.comm) @@ -5900,7 +6364,7 @@ SYSCALL_DEFINE5(perf_event_open, int err; /* for future expandability... */ - if (flags & ~(PERF_FLAG_FD_NO_GROUP | PERF_FLAG_FD_OUTPUT)) + if (flags & ~PERF_FLAG_ALL) return -EINVAL; err = perf_copy_attr(attr_uptr, &attr); @@ -5917,6 +6381,15 @@ SYSCALL_DEFINE5(perf_event_open, return -EINVAL; } + /* + * In cgroup mode, the pid argument is used to pass the fd + * opened to the cgroup directory in cgroupfs. The cpu argument + * designates the cpu on which to monitor threads from that + * cgroup. + */ + if ((flags & PERF_FLAG_PID_CGROUP) && (pid == -1 || cpu == -1)) + return -EINVAL; + event_fd = get_unused_fd_flags(O_RDWR); if (event_fd < 0) return event_fd; @@ -5934,7 +6407,7 @@ SYSCALL_DEFINE5(perf_event_open, group_leader = NULL; } - if (pid != -1) { + if (pid != -1 && !(flags & PERF_FLAG_PID_CGROUP)) { task = find_lively_task_by_vpid(pid); if (IS_ERR(task)) { err = PTR_ERR(task); @@ -5948,6 +6421,12 @@ SYSCALL_DEFINE5(perf_event_open, goto err_task; } + if (flags & PERF_FLAG_PID_CGROUP) { + err = perf_cgroup_connect(pid, event, &attr, group_leader); + if (err) + goto err_alloc; + } + /* * Special case software events and allow them to be part of * any hardware group. @@ -6808,3 +7287,92 @@ unlock: return ret; } device_initcall(perf_event_sysfs_init); + +#ifdef CONFIG_CGROUP_PERF +static struct cgroup_subsys_state *perf_cgroup_create( + struct cgroup_subsys *ss, struct cgroup *cont) +{ + struct perf_cgroup *jc; + struct perf_cgroup_info *t; + int c; + + jc = kmalloc(sizeof(*jc), GFP_KERNEL); + if (!jc) + return ERR_PTR(-ENOMEM); + + memset(jc, 0, sizeof(*jc)); + + jc->info = alloc_percpu(struct perf_cgroup_info); + if (!jc->info) { + kfree(jc); + return ERR_PTR(-ENOMEM); + } + + for_each_possible_cpu(c) { + t = per_cpu_ptr(jc->info, c); + t->time = 0; + t->timestamp = 0; + } + return &jc->css; +} + +static void perf_cgroup_destroy(struct cgroup_subsys *ss, + struct cgroup *cont) +{ + struct perf_cgroup *jc; + jc = container_of(cgroup_subsys_state(cont, perf_subsys_id), + struct perf_cgroup, css); + free_percpu(jc->info); + kfree(jc); +} + +static int __perf_cgroup_move(void *info) +{ + struct task_struct *task = info; + perf_cgroup_switch(task, PERF_CGROUP_SWOUT | PERF_CGROUP_SWIN); + return 0; +} + +static void perf_cgroup_move(struct task_struct *task) +{ + task_function_call(task, __perf_cgroup_move, task); +} + +static void perf_cgroup_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, + struct cgroup *old_cgrp, struct task_struct *task, + bool threadgroup) +{ + perf_cgroup_move(task); + if (threadgroup) { + struct task_struct *c; + rcu_read_lock(); + list_for_each_entry_rcu(c, &task->thread_group, thread_group) { + perf_cgroup_move(c); + } + rcu_read_unlock(); + } +} + +static void perf_cgroup_exit(struct cgroup_subsys *ss, struct cgroup *cgrp, + struct cgroup *old_cgrp, struct task_struct *task) +{ + /* + * cgroup_exit() is called in the copy_process() failure path. + * Ignore this case since the task hasn't ran yet, this avoids + * trying to poke a half freed task state from generic code. + */ + if (!(task->flags & PF_EXITING)) + return; + + perf_cgroup_move(task); +} + +struct cgroup_subsys perf_subsys = { + .name = "perf_event", + .subsys_id = perf_subsys_id, + .create = perf_cgroup_create, + .destroy = perf_cgroup_destroy, + .exit = perf_cgroup_exit, + .attach = perf_cgroup_attach, +}; +#endif /* CONFIG_CGROUP_PERF */ -- cgit v1.2.3 From 163ec4354a5135c6c38c3f4a9b46a31900ebdf48 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 16 Feb 2011 11:22:34 +0100 Subject: perf: Optimize throttling code By pre-computing the maximum number of samples per tick we can avoid a multiplication and a conditional since MAX_INTERRUPTS > max_samples_per_tick. Signed-off-by: Peter Zijlstra LKML-Reference: Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 4 ++++ kernel/perf_event.c | 43 ++++++++++++++++++++++++------------------- kernel/sysctl.c | 2 +- 3 files changed, 29 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 38c8b2554842..8ceb5a6fd9c9 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1110,6 +1110,10 @@ extern int sysctl_perf_event_paranoid; extern int sysctl_perf_event_mlock; extern int sysctl_perf_event_sample_rate; +extern int perf_proc_update_handler(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos); + static inline bool perf_paranoid_tracepoint_raw(void) { return sysctl_perf_event_paranoid > -1; diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 65dcdc76d709..e03be08d0ddf 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -150,7 +150,24 @@ int sysctl_perf_event_mlock __read_mostly = 512; /* 'free' kb per user */ /* * max perf event sample rate */ -int sysctl_perf_event_sample_rate __read_mostly = 100000; +#define DEFAULT_MAX_SAMPLE_RATE 100000 +int sysctl_perf_event_sample_rate __read_mostly = DEFAULT_MAX_SAMPLE_RATE; +static int max_samples_per_tick __read_mostly = + DIV_ROUND_UP(DEFAULT_MAX_SAMPLE_RATE, HZ); + +int perf_proc_update_handler(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int ret = proc_dointvec(table, write, buffer, lenp, ppos); + + if (ret || !write) + return ret; + + max_samples_per_tick = DIV_ROUND_UP(sysctl_perf_event_sample_rate, HZ); + + return 0; +} static atomic64_t perf_event_id; @@ -4941,26 +4958,14 @@ static int __perf_event_overflow(struct perf_event *event, int nmi, if (unlikely(!is_sampling_event(event))) return 0; - if (!throttle) { - hwc->interrupts++; - } else { - if (hwc->interrupts != MAX_INTERRUPTS) { - hwc->interrupts++; - if (HZ * hwc->interrupts > - (u64)sysctl_perf_event_sample_rate) { - hwc->interrupts = MAX_INTERRUPTS; - perf_log_throttle(event, 0); - ret = 1; - } - } else { - /* - * Keep re-disabling events even though on the previous - * pass we disabled it - just in case we raced with a - * sched-in and the event got enabled again: - */ + if (unlikely(hwc->interrupts >= max_samples_per_tick)) { + if (throttle) { + hwc->interrupts = MAX_INTERRUPTS; + perf_log_throttle(event, 0); ret = 1; } - } + } else + hwc->interrupts++; if (event->attr.freq) { u64 now = perf_clock(); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 0f1bd83db985..daef911cbadb 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -948,7 +948,7 @@ static struct ctl_table kern_table[] = { .data = &sysctl_perf_event_sample_rate, .maxlen = sizeof(sysctl_perf_event_sample_rate), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = perf_proc_update_handler, }, #endif #ifdef CONFIG_KMEMCHECK -- cgit v1.2.3 From 63185f64ef06464706b32c9a301f71f68cd93e52 Mon Sep 17 00:00:00 2001 From: Ville Tervo Date: Thu, 10 Feb 2011 22:38:46 -0300 Subject: Bluetooth: Add low energy commands and events Add needed HCI command and event structs to create LE connections. Signed-off-by: Ville Tervo Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 49 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 4bee030e4b52..802d2505f138 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -642,6 +642,36 @@ struct hci_rp_read_bd_addr { bdaddr_t bdaddr; } __packed; +#define HCI_OP_LE_SET_EVENT_MASK 0x2001 +struct hci_cp_le_set_event_mask { + __u8 mask[8]; +} __packed; + +#define HCI_OP_LE_READ_BUFFER_SIZE 0x2002 +struct hci_rp_le_read_buffer_size { + __u8 status; + __le16 le_mtu; + __u8 le_max_pkt; +} __packed; + +#define HCI_OP_LE_CREATE_CONN 0x200d +struct hci_cp_le_create_conn { + __le16 scan_interval; + __le16 scan_window; + __u8 filter_policy; + __u8 peer_addr_type; + bdaddr_t peer_addr; + __u8 own_address_type; + __le16 conn_interval_min; + __le16 conn_interval_max; + __le16 conn_latency; + __le16 supervision_timeout; + __le16 min_ce_len; + __le16 max_ce_len; +} __packed; + +#define HCI_OP_LE_CREATE_CONN_CANCEL 0x200e + /* ---- HCI Events ---- */ #define HCI_EV_INQUIRY_COMPLETE 0x01 @@ -902,6 +932,25 @@ struct hci_ev_remote_host_features { __u8 features[8]; } __packed; +#define HCI_EV_LE_META 0x3e +struct hci_ev_le_meta { + __u8 subevent; +} __packed; + +/* Low energy meta events */ +#define HCI_EV_LE_CONN_COMPLETE 0x01 +struct hci_ev_le_conn_complete { + __u8 status; + __le16 handle; + __u8 role; + __u8 bdaddr_type; + bdaddr_t bdaddr; + __le16 interval; + __le16 latency; + __le16 supervision_timeout; + __u8 clk_accurancy; +} __packed; + /* Internal events generated by Bluetooth stack */ #define HCI_EV_STACK_INTERNAL 0xfd struct hci_ev_stack_internal { -- cgit v1.2.3 From fcd89c09a59a054fb986861e0862aa2fff7d7c40 Mon Sep 17 00:00:00 2001 From: Ville Tervo Date: Thu, 10 Feb 2011 22:38:47 -0300 Subject: Bluetooth: Add LE connect support Bluetooth V4.0 adds support for Low Energy (LE) connections. Specification introduces new set of hci commands to control LE connection. This patch adds logic to create, cancel and disconnect LE connections. Signed-off-by: Ville Tervo Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 2 + include/net/bluetooth/hci_core.h | 25 +++++++++-- net/bluetooth/hci_conn.c | 51 ++++++++++++++++++++-- net/bluetooth/hci_event.c | 93 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 164 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 802d2505f138..e756f82a29e5 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -168,6 +168,8 @@ enum { #define SCO_LINK 0x00 #define ACL_LINK 0x01 #define ESCO_LINK 0x02 +/* Low Energy links do not have defined link type. Use invented one */ +#define LE_LINK 0x80 /* LMP features */ #define LMP_3SLOT 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6163bff6fa91..f434e96ce020 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -60,6 +60,7 @@ struct hci_conn_hash { spinlock_t lock; unsigned int acl_num; unsigned int sco_num; + unsigned int le_num; }; struct bdaddr_list { @@ -309,20 +310,36 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c) { struct hci_conn_hash *h = &hdev->conn_hash; list_add(&c->list, &h->list); - if (c->type == ACL_LINK) + switch (c->type) { + case ACL_LINK: h->acl_num++; - else + break; + case LE_LINK: + h->le_num++; + break; + case SCO_LINK: + case ESCO_LINK: h->sco_num++; + break; + } } static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) { struct hci_conn_hash *h = &hdev->conn_hash; list_del(&c->list); - if (c->type == ACL_LINK) + switch (c->type) { + case ACL_LINK: h->acl_num--; - else + break; + case LE_LINK: + h->le_num--; + break; + case SCO_LINK: + case ESCO_LINK: h->sco_num--; + break; + } } static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 42dc39f25b72..d0c470c18f9d 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -45,6 +45,32 @@ #include #include +static void hci_le_connect(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + struct hci_cp_le_create_conn cp; + + conn->state = BT_CONNECT; + conn->out = 1; + + memset(&cp, 0, sizeof(cp)); + cp.scan_interval = cpu_to_le16(0x0004); + cp.scan_window = cpu_to_le16(0x0004); + bacpy(&cp.peer_addr, &conn->dst); + cp.conn_interval_min = cpu_to_le16(0x0008); + cp.conn_interval_max = cpu_to_le16(0x0100); + cp.supervision_timeout = cpu_to_le16(0x0064); + cp.min_ce_len = cpu_to_le16(0x0001); + cp.max_ce_len = cpu_to_le16(0x0001); + + hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp); +} + +static void hci_le_connect_cancel(struct hci_conn *conn) +{ + hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL); +} + void hci_acl_connect(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; @@ -193,8 +219,12 @@ static void hci_conn_timeout(unsigned long arg) switch (conn->state) { case BT_CONNECT: case BT_CONNECT2: - if (conn->type == ACL_LINK && conn->out) - hci_acl_connect_cancel(conn); + if (conn->out) { + if (conn->type == ACL_LINK) + hci_acl_connect_cancel(conn); + else if (conn->type == LE_LINK) + hci_le_connect_cancel(conn); + } break; case BT_CONFIG: case BT_CONNECTED: @@ -361,15 +391,30 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) } EXPORT_SYMBOL(hci_get_route); -/* Create SCO or ACL connection. +/* Create SCO, ACL or LE connection. * Device _must_ be locked */ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type) { struct hci_conn *acl; struct hci_conn *sco; + struct hci_conn *le; BT_DBG("%s dst %s", hdev->name, batostr(dst)); + if (type == LE_LINK) { + le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst); + if (!le) + le = hci_conn_add(hdev, LE_LINK, dst); + if (!le) + return NULL; + if (le->state == BT_OPEN) + hci_le_connect(le); + + hci_conn_hold(le); + + return le; + } + acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); if (!acl) { acl = hci_conn_add(hdev, ACL_LINK, dst); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index cee46cbe7aeb..47c6e9316ce8 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1107,6 +1107,43 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status) hci_dev_unlock(hdev); } +static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) +{ + struct hci_cp_le_create_conn *cp; + struct hci_conn *conn; + + BT_DBG("%s status 0x%x", hdev->name, status); + + cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CONN); + if (!cp) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->peer_addr); + + BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&cp->peer_addr), + conn); + + if (status) { + if (conn && conn->state == BT_CONNECT) { + conn->state = BT_CLOSED; + hci_proto_connect_cfm(conn, status); + hci_conn_del(conn); + } + } else { + if (!conn) { + conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr); + if (conn) + conn->out = 1; + else + BT_ERR("No memory for new connection"); + } + } + + hci_dev_unlock(hdev); +} + static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -1738,6 +1775,10 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) mgmt_disconnect_failed(hdev->id); break; + case HCI_OP_LE_CREATE_CONN: + hci_cs_le_create_conn(hdev, ev->status); + break; + default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; @@ -2321,6 +2362,54 @@ static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_ hci_dev_unlock(hdev); } +static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_le_conn_complete *ev = (void *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s status %d", hdev->name, ev->status); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &ev->bdaddr); + if (!conn) + goto unlock; + + if (ev->status) { + hci_proto_connect_cfm(conn, ev->status); + conn->state = BT_CLOSED; + hci_conn_del(conn); + goto unlock; + } + + conn->handle = __le16_to_cpu(ev->handle); + conn->state = BT_CONNECTED; + + hci_conn_hold_device(conn); + hci_conn_add_sysfs(conn); + + hci_proto_connect_cfm(conn, ev->status); + +unlock: + hci_dev_unlock(hdev); +} + +static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_le_meta *le_ev = (void *) skb->data; + + skb_pull(skb, sizeof(*le_ev)); + + switch (le_ev->subevent) { + case HCI_EV_LE_CONN_COMPLETE: + hci_le_conn_complete_evt(hdev, skb); + break; + + default: + break; + } +} + void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_event_hdr *hdr = (void *) skb->data; @@ -2461,6 +2550,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_remote_host_features_evt(hdev, skb); break; + case HCI_EV_LE_META: + hci_le_meta_evt(hdev, skb); + break; + default: BT_DBG("%s event 0x%x", hdev->name, event); break; -- cgit v1.2.3 From 6ed58ec520ad2b2fe3f955c8a5fd0eecafccebdf Mon Sep 17 00:00:00 2001 From: Ville Tervo Date: Thu, 10 Feb 2011 22:38:48 -0300 Subject: Bluetooth: Use LE buffers for LE traffic Bluetooth chips may have separate buffers for LE traffic. This patch add support to use LE buffers provided by the chip. Signed-off-by: Ville Tervo Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 5 +++ net/bluetooth/hci_conn.c | 5 +++ net/bluetooth/hci_core.c | 74 +++++++++++++++++++++++++++++++++++++--- net/bluetooth/hci_event.c | 33 ++++++++++++++++++ 4 files changed, 113 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f434e96ce020..d30b93c82fd4 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -123,15 +123,19 @@ struct hci_dev { atomic_t cmd_cnt; unsigned int acl_cnt; unsigned int sco_cnt; + unsigned int le_cnt; unsigned int acl_mtu; unsigned int sco_mtu; + unsigned int le_mtu; unsigned int acl_pkts; unsigned int sco_pkts; + unsigned int le_pkts; unsigned long cmd_last_tx; unsigned long acl_last_tx; unsigned long sco_last_tx; + unsigned long le_last_tx; struct workqueue_struct *workqueue; @@ -521,6 +525,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_esco_capable(dev) ((dev)->features[3] & LMP_ESCO) #define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR) #define lmp_no_flush_capable(dev) ((dev)->features[6] & LMP_NO_FLUSH) +#define lmp_le_capable(dev) ((dev)->features[4] & LMP_LE) /* ----- HCI protocols ----- */ struct hci_proto { diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index d0c470c18f9d..aecd78e6cceb 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -326,6 +326,11 @@ int hci_conn_del(struct hci_conn *conn) /* Unacked frames */ hdev->acl_cnt += conn->sent; + } else if (conn->type == LE_LINK) { + if (hdev->le_pkts) + hdev->le_cnt += conn->sent; + else + hdev->acl_cnt += conn->sent; } else { struct hci_conn *acl = conn->link; if (acl) { diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 2f003224d2ea..92960532dea4 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -263,6 +263,14 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); } +static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt) +{ + BT_DBG("%s", hdev->name); + + /* Read LE buffer size */ + hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); +} + static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) { __u8 scan = opt; @@ -529,6 +537,10 @@ int hci_dev_open(__u16 dev) ret = __hci_request(hdev, hci_init_req, 0, msecs_to_jiffies(HCI_INIT_TIMEOUT)); + if (lmp_le_capable(hdev)) + ret = __hci_request(hdev, hci_le_init_req, 0, + msecs_to_jiffies(HCI_INIT_TIMEOUT)); + clear_bit(HCI_INIT, &hdev->flags); } @@ -671,7 +683,7 @@ int hci_dev_reset(__u16 dev) hdev->flush(hdev); atomic_set(&hdev->cmd_cnt, 1); - hdev->acl_cnt = 0; hdev->sco_cnt = 0; + hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0; if (!test_bit(HCI_RAW, &hdev->flags)) ret = __hci_request(hdev, hci_reset_req, 0, @@ -1672,8 +1684,25 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int } if (conn) { - int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt); - int q = cnt / num; + int cnt, q; + + switch (conn->type) { + case ACL_LINK: + cnt = hdev->acl_cnt; + break; + case SCO_LINK: + case ESCO_LINK: + cnt = hdev->sco_cnt; + break; + case LE_LINK: + cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt; + break; + default: + cnt = 0; + BT_ERR("Unknown link type"); + } + + q = cnt / num; *quote = q ? q : 1; } else *quote = 0; @@ -1772,6 +1801,40 @@ static inline void hci_sched_esco(struct hci_dev *hdev) } } +static inline void hci_sched_le(struct hci_dev *hdev) +{ + struct hci_conn *conn; + struct sk_buff *skb; + int quote, cnt; + + BT_DBG("%s", hdev->name); + + if (!test_bit(HCI_RAW, &hdev->flags)) { + /* LE tx timeout must be longer than maximum + * link supervision timeout (40.9 seconds) */ + if (!hdev->le_cnt && + time_after(jiffies, hdev->le_last_tx + HZ * 45)) + hci_acl_tx_to(hdev); + } + + cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt; + while (cnt && (conn = hci_low_sent(hdev, LE_LINK, "e))) { + while (quote-- && (skb = skb_dequeue(&conn->data_q))) { + BT_DBG("skb %p len %d", skb, skb->len); + + hci_send_frame(skb); + hdev->le_last_tx = jiffies; + + cnt--; + conn->sent++; + } + } + if (hdev->le_pkts) + hdev->le_cnt = cnt; + else + hdev->acl_cnt = cnt; +} + static void hci_tx_task(unsigned long arg) { struct hci_dev *hdev = (struct hci_dev *) arg; @@ -1779,7 +1842,8 @@ static void hci_tx_task(unsigned long arg) read_lock(&hci_task_lock); - BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt); + BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt, + hdev->sco_cnt, hdev->le_cnt); /* Schedule queues and send stuff to HCI driver */ @@ -1789,6 +1853,8 @@ static void hci_tx_task(unsigned long arg) hci_sched_esco(hdev); + hci_sched_le(hdev); + /* Send next queued raw (unknown type) packet */ while ((skb = skb_dequeue(&hdev->raw_q))) hci_send_frame(skb); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 47c6e9316ce8..3155ad588076 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -776,6 +776,25 @@ static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) mgmt_pin_code_neg_reply_complete(hdev->id, &rp->bdaddr, rp->status); } +static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_le_read_buffer_size *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + hdev->le_mtu = __le16_to_cpu(rp->le_mtu); + hdev->le_pkts = rp->le_max_pkt; + + hdev->le_cnt = hdev->le_pkts; + + BT_DBG("%s le mtu %d:%d", hdev->name, hdev->le_mtu, hdev->le_pkts); + + hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status); +} static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { @@ -1704,6 +1723,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_pin_code_neg_reply(hdev, skb); break; + case HCI_OP_LE_READ_BUFFER_SIZE: + hci_cc_le_read_buffer_size(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; @@ -1849,6 +1872,16 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s hdev->acl_cnt += count; if (hdev->acl_cnt > hdev->acl_pkts) hdev->acl_cnt = hdev->acl_pkts; + } else if (conn->type == LE_LINK) { + if (hdev->le_pkts) { + hdev->le_cnt += count; + if (hdev->le_cnt > hdev->le_pkts) + hdev->le_cnt = hdev->le_pkts; + } else { + hdev->acl_cnt += count; + if (hdev->acl_cnt > hdev->acl_pkts) + hdev->acl_cnt = hdev->acl_pkts; + } } else { hdev->sco_cnt += count; if (hdev->sco_cnt > hdev->sco_pkts) -- cgit v1.2.3 From acd7d3708555b3da7522e23c183cc21efc785f72 Mon Sep 17 00:00:00 2001 From: Ville Tervo Date: Thu, 10 Feb 2011 22:38:49 -0300 Subject: Bluetooth: Add LE connection support to L2CAP Add basic LE connection support to L2CAP. LE connection can be created by specifying cid in struct sockaddr_l2 Signed-off-by: Ville Tervo Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 3 +++ net/bluetooth/l2cap_core.c | 23 +++++++++++++++++++---- net/bluetooth/l2cap_sock.c | 7 ++++--- 3 files changed, 26 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 9fb87fe1aec3..cd7a64250e3c 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -160,6 +160,9 @@ struct l2cap_conn_rsp { /* channel indentifier */ #define L2CAP_CID_SIGNALING 0x0001 #define L2CAP_CID_CONN_LESS 0x0002 +#define L2CAP_CID_LE_DATA 0x0004 +#define L2CAP_CID_LE_SIGNALING 0x0005 +#define L2CAP_CID_SMP 0x0006 #define L2CAP_CID_DYN_START 0x0040 #define L2CAP_CID_DYN_END 0xffff diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a72d6e4eab4f..123c1bfa0ce6 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -593,6 +593,12 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { bh_lock_sock(sk); + if (conn->hcon->type == LE_LINK) { + l2cap_sock_clear_timer(sk); + sk->sk_state = BT_CONNECTED; + sk->sk_state_change(sk); + } + if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM) { l2cap_sock_clear_timer(sk); @@ -651,7 +657,11 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) BT_DBG("hcon %p conn %p", hcon, conn); - conn->mtu = hcon->hdev->acl_mtu; + if (hcon->hdev->le_mtu && hcon->type == LE_LINK) + conn->mtu = hcon->hdev->le_mtu; + else + conn->mtu = hcon->hdev->acl_mtu; + conn->src = &hcon->hdev->bdaddr; conn->dst = &hcon->dst; @@ -758,8 +768,13 @@ int l2cap_do_connect(struct sock *sk) auth_type = l2cap_get_auth_type(sk); - hcon = hci_connect(hdev, ACL_LINK, dst, + if (l2cap_pi(sk)->dcid == L2CAP_CID_LE_DATA) + hcon = hci_connect(hdev, LE_LINK, dst, l2cap_pi(sk)->sec_level, auth_type); + else + hcon = hci_connect(hdev, ACL_LINK, dst, + l2cap_pi(sk)->sec_level, auth_type); + if (!hcon) goto done; @@ -3520,7 +3535,7 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status) BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); - if (hcon->type != ACL_LINK) + if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK)) return -EINVAL; if (!status) { @@ -3549,7 +3564,7 @@ static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) { BT_DBG("hcon %p reason %d", hcon, reason); - if (hcon->type != ACL_LINK) + if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK)) return -EINVAL; l2cap_conn_del(hcon, bt_err(reason)); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 21f5385ca24d..f45d361e84d1 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -168,13 +168,13 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al len = min_t(unsigned int, sizeof(la), alen); memcpy(&la, addr, len); - if (la.l2_cid) + if (la.l2_cid && la.l2_psm) return -EINVAL; lock_sock(sk); if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) - && !la.l2_psm) { + && !(la.l2_psm || la.l2_cid)) { err = -EINVAL; goto done; } @@ -216,7 +216,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al /* PSM must be odd and lsb of upper byte must be 0 */ if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 && - sk->sk_type != SOCK_RAW) { + sk->sk_type != SOCK_RAW && !la.l2_cid) { err = -EINVAL; goto done; } @@ -224,6 +224,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al /* Set destination address and psm */ bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr); l2cap_pi(sk)->psm = la.l2_psm; + l2cap_pi(sk)->dcid = la.l2_cid; err = l2cap_do_connect(sk); if (err) -- cgit v1.2.3 From b62f328b8f20abe97cdbaaf44c6e4f5e7a610f18 Mon Sep 17 00:00:00 2001 From: Ville Tervo Date: Thu, 10 Feb 2011 22:38:50 -0300 Subject: Bluetooth: Add server socket support for LE connection Add support for LE server sockets. Signed-off-by: Ville Tervo Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/hci_event.c | 10 ++++- net/bluetooth/l2cap_core.c | 94 +++++++++++++++++++++++++++++++++++++++++-- net/bluetooth/l2cap_sock.c | 7 +++- 4 files changed, 105 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index cd7a64250e3c..41b3bc56f13f 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -38,6 +38,7 @@ #define L2CAP_DEFAULT_MAX_PDU_SIZE 1009 /* Sized for 3-DH5 packet */ #define L2CAP_DEFAULT_ACK_TO 200 #define L2CAP_LOCAL_BUSY_TRIES 12 +#define L2CAP_LE_DEFAULT_MTU 23 #define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */ #define L2CAP_INFO_TIMEOUT (4000) /* 4 seconds */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3155ad588076..74f04a27734d 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2405,8 +2405,14 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &ev->bdaddr); - if (!conn) - goto unlock; + if (!conn) { + conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr); + if (!conn) { + BT_ERR("No memory for new connection"); + hci_dev_unlock(hdev); + return; + } + } if (ev->status) { hci_proto_connect_cfm(conn, ev->status); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 123c1bfa0ce6..3079175065d4 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -181,8 +181,16 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so l2cap_pi(sk)->conn = conn; if (sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) { - /* Alloc CID for connection-oriented socket */ - l2cap_pi(sk)->scid = l2cap_alloc_cid(l); + if (conn->hcon->type == LE_LINK) { + /* LE connection */ + l2cap_pi(sk)->omtu = L2CAP_LE_DEFAULT_MTU; + l2cap_pi(sk)->scid = L2CAP_CID_LE_DATA; + l2cap_pi(sk)->dcid = L2CAP_CID_LE_DATA; + } else { + /* Alloc CID for connection-oriented socket */ + l2cap_pi(sk)->scid = l2cap_alloc_cid(l); + l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; + } } else if (sk->sk_type == SOCK_DGRAM) { /* Connectionless socket */ l2cap_pi(sk)->scid = L2CAP_CID_CONN_LESS; @@ -581,6 +589,82 @@ static void l2cap_conn_start(struct l2cap_conn *conn) } } +/* Find socket with cid and source bdaddr. + * Returns closest match, locked. + */ +static struct sock *l2cap_get_sock_by_scid(int state, __le16 cid, bdaddr_t *src) +{ + struct sock *s, *sk = NULL, *sk1 = NULL; + struct hlist_node *node; + + read_lock(&l2cap_sk_list.lock); + + sk_for_each(sk, node, &l2cap_sk_list.head) { + if (state && sk->sk_state != state) + continue; + + if (l2cap_pi(sk)->scid == cid) { + /* Exact match. */ + if (!bacmp(&bt_sk(sk)->src, src)) + break; + + /* Closest match */ + if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) + sk1 = sk; + } + } + s = node ? sk : sk1; + if (s) + bh_lock_sock(s); + read_unlock(&l2cap_sk_list.lock); + + return s; +} + +static void l2cap_le_conn_ready(struct l2cap_conn *conn) +{ + struct l2cap_chan_list *list = &conn->chan_list; + struct sock *parent, *uninitialized_var(sk); + + BT_DBG(""); + + /* Check if we have socket listening on cid */ + parent = l2cap_get_sock_by_scid(BT_LISTEN, L2CAP_CID_LE_DATA, + conn->src); + if (!parent) + return; + + /* Check for backlog size */ + if (sk_acceptq_is_full(parent)) { + BT_DBG("backlog full %d", parent->sk_ack_backlog); + goto clean; + } + + sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC); + if (!sk) + goto clean; + + write_lock_bh(&list->lock); + + hci_conn_hold(conn->hcon); + + l2cap_sock_init(sk, parent); + bacpy(&bt_sk(sk)->src, conn->src); + bacpy(&bt_sk(sk)->dst, conn->dst); + + __l2cap_chan_add(conn, sk, parent); + + l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + + sk->sk_state = BT_CONNECTED; + parent->sk_data_ready(parent, 0); + + write_unlock_bh(&list->lock); + +clean: + bh_unlock_sock(parent); +} + static void l2cap_conn_ready(struct l2cap_conn *conn) { struct l2cap_chan_list *l = &conn->chan_list; @@ -588,6 +672,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) BT_DBG("conn %p", conn); + if (!conn->hcon->out && conn->hcon->type == LE_LINK) + l2cap_le_conn_ready(conn); + read_lock(&l->lock); for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { @@ -670,7 +757,8 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) spin_lock_init(&conn->lock); rwlock_init(&conn->chan_list.lock); - setup_timer(&conn->info_timer, l2cap_info_timeout, + if (hcon->type != LE_LINK) + setup_timer(&conn->info_timer, l2cap_info_timeout, (unsigned long) conn); conn->disc_reason = 0x13; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index f45d361e84d1..a8d289373794 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -103,7 +103,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) len = min_t(unsigned int, sizeof(la), alen); memcpy(&la, addr, len); - if (la.l2_cid) + if (la.l2_cid && la.l2_psm) return -EINVAL; lock_sock(sk); @@ -145,6 +145,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; } + if (la.l2_cid) + l2cap_pi(sk)->scid = la.l2_cid; + write_unlock_bh(&l2cap_sk_list.lock); done: @@ -266,7 +269,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) goto done; } - if (!l2cap_pi(sk)->psm) { + if (!l2cap_pi(sk)->psm && !l2cap_pi(sk)->dcid) { bdaddr_t *src = &bt_sk(sk)->src; u16 psm; -- cgit v1.2.3 From aff2cae3546df9f47f9fe24f3e85a7a84e825de8 Mon Sep 17 00:00:00 2001 From: Ville Tervo Date: Thu, 10 Feb 2011 22:38:54 -0300 Subject: Bluetooth: Add SMP command structures Add command structures for security manager protocol. Signed-off-by: Ville Tervo Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/smp.h | 76 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 include/net/bluetooth/smp.h (limited to 'include') diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h new file mode 100644 index 000000000000..8f2edbf979dc --- /dev/null +++ b/include/net/bluetooth/smp.h @@ -0,0 +1,76 @@ +#ifndef __SMP_H +#define __SMP_H + +struct smp_command_hdr { + __u8 code; +} __packed; + +#define SMP_CMD_PAIRING_REQ 0x01 +#define SMP_CMD_PAIRING_RSP 0x02 +struct smp_cmd_pairing { + __u8 io_capability; + __u8 oob_flag; + __u8 auth_req; + __u8 max_key_size; + __u8 init_key_dist; + __u8 resp_key_dist; +} __packed; + +#define SMP_CMD_PAIRING_CONFIRM 0x03 +struct smp_cmd_pairing_confirm { + __u8 confirm_val[16]; +} __packed; + +#define SMP_CMD_PAIRING_RANDOM 0x04 +struct smp_cmd_pairing_random { + __u8 rand_val[16]; +} __packed; + +#define SMP_CMD_PAIRING_FAIL 0x05 +struct smp_cmd_pairing_fail { + __u8 reason; +} __packed; + +#define SMP_CMD_ENCRYPT_INFO 0x06 +struct smp_cmd_encrypt_info { + __u8 ltk[16]; +} __packed; + +#define SMP_CMD_MASTER_IDENT 0x07 +struct smp_cmd_master_ident { + __u16 ediv; + __u8 rand[8]; +} __packed; + +#define SMP_CMD_IDENT_INFO 0x08 +struct smp_cmd_ident_info { + __u8 irk[16]; +} __packed; + +#define SMP_CMD_IDENT_ADDR_INFO 0x09 +struct smp_cmd_ident_addr_info { + __u8 addr_type; + bdaddr_t bdaddr; +} __packed; + +#define SMP_CMD_SIGN_INFO 0x0a +struct smp_cmd_sign_info { + __u8 csrk[16]; +} __packed; + +#define SMP_CMD_SECURITY_REQ 0x0b +struct smp_cmd_security_req { + __u8 auth_req; +} __packed; + +#define SMP_PASSKEY_ENTRY_FAILED 0x01 +#define SMP_OOB_NOT_AVAIL 0x02 +#define SMP_AUTH_REQUIREMENTS 0x03 +#define SMP_CONFIRM_FAILED 0x04 +#define SMP_PAIRING_NOTSUPP 0x05 +#define SMP_ENC_KEY_SIZE 0x06 +#define SMP_CMD_NOTSUPP 0x07 +#define SMP_UNSPECIFIED 0x08 +#define SMP_REPEATED_ATTEMPTS 0x09 + +#endif /* __SMP_H */ -- cgit v1.2.3 From 3300d9a930a79508032e3e03ac2bde3a22dd048d Mon Sep 17 00:00:00 2001 From: Claudio Takahasi Date: Fri, 11 Feb 2011 19:28:54 -0200 Subject: Bluetooth: Add LE signaling commands handling This patch splits the L2CAP command handling function in order to have a clear separation between the commands related to BR/EDR and LE. Commands and responses in the LE signaling channel are not being handled yet, command reject is sent to all received requests. Bluetooth Core Specification, Volume 3, Part A, section 4 defines the signaling packets formats and allowed commands/responses over the LE signaling channel. Signed-off-by: Claudio Takahasi Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 2 + net/bluetooth/l2cap_core.c | 142 ++++++++++++++++++++++++++---------------- 2 files changed, 92 insertions(+), 52 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 41b3bc56f13f..06f245dcf6b2 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -89,6 +89,8 @@ struct l2cap_conninfo { #define L2CAP_ECHO_RSP 0x09 #define L2CAP_INFO_REQ 0x0a #define L2CAP_INFO_RSP 0x0b +#define L2CAP_CONN_PARAM_UPDATE_REQ 0x12 +#define L2CAP_CONN_PARAM_UPDATE_RSP 0x13 /* L2CAP feature mask */ #define L2CAP_FEAT_FLOWCTL 0x00000001 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 3079175065d4..ce781a43f1d5 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1428,7 +1428,11 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->len = cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen); - lh->cid = cpu_to_le16(L2CAP_CID_SIGNALING); + + if (conn->hcon->type == LE_LINK) + lh->cid = cpu_to_le16(L2CAP_CID_LE_SIGNALING); + else + lh->cid = cpu_to_le16(L2CAP_CID_SIGNALING); cmd = (struct l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE); cmd->code = code; @@ -2497,12 +2501,90 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm return 0; } -static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) +static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) +{ + int err = 0; + + switch (cmd->code) { + case L2CAP_COMMAND_REJ: + l2cap_command_rej(conn, cmd, data); + break; + + case L2CAP_CONN_REQ: + err = l2cap_connect_req(conn, cmd, data); + break; + + case L2CAP_CONN_RSP: + err = l2cap_connect_rsp(conn, cmd, data); + break; + + case L2CAP_CONF_REQ: + err = l2cap_config_req(conn, cmd, cmd_len, data); + break; + + case L2CAP_CONF_RSP: + err = l2cap_config_rsp(conn, cmd, data); + break; + + case L2CAP_DISCONN_REQ: + err = l2cap_disconnect_req(conn, cmd, data); + break; + + case L2CAP_DISCONN_RSP: + err = l2cap_disconnect_rsp(conn, cmd, data); + break; + + case L2CAP_ECHO_REQ: + l2cap_send_cmd(conn, cmd->ident, L2CAP_ECHO_RSP, cmd_len, data); + break; + + case L2CAP_ECHO_RSP: + break; + + case L2CAP_INFO_REQ: + err = l2cap_information_req(conn, cmd, data); + break; + + case L2CAP_INFO_RSP: + err = l2cap_information_rsp(conn, cmd, data); + break; + + default: + BT_ERR("Unknown BR/EDR signaling command 0x%2.2x", cmd->code); + err = -EINVAL; + break; + } + + return err; +} + +static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) +{ + switch (cmd->code) { + case L2CAP_COMMAND_REJ: + return 0; + + case L2CAP_CONN_PARAM_UPDATE_REQ: + return -EINVAL; + + case L2CAP_CONN_PARAM_UPDATE_RSP: + return 0; + + default: + BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code); + return -EINVAL; + } +} + +static inline void l2cap_sig_channel(struct l2cap_conn *conn, + struct sk_buff *skb) { u8 *data = skb->data; int len = skb->len; struct l2cap_cmd_hdr cmd; - int err = 0; + int err; l2cap_raw_recv(conn, skb); @@ -2521,55 +2603,10 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *sk break; } - switch (cmd.code) { - case L2CAP_COMMAND_REJ: - l2cap_command_rej(conn, &cmd, data); - break; - - case L2CAP_CONN_REQ: - err = l2cap_connect_req(conn, &cmd, data); - break; - - case L2CAP_CONN_RSP: - err = l2cap_connect_rsp(conn, &cmd, data); - break; - - case L2CAP_CONF_REQ: - err = l2cap_config_req(conn, &cmd, cmd_len, data); - break; - - case L2CAP_CONF_RSP: - err = l2cap_config_rsp(conn, &cmd, data); - break; - - case L2CAP_DISCONN_REQ: - err = l2cap_disconnect_req(conn, &cmd, data); - break; - - case L2CAP_DISCONN_RSP: - err = l2cap_disconnect_rsp(conn, &cmd, data); - break; - - case L2CAP_ECHO_REQ: - l2cap_send_cmd(conn, cmd.ident, L2CAP_ECHO_RSP, cmd_len, data); - break; - - case L2CAP_ECHO_RSP: - break; - - case L2CAP_INFO_REQ: - err = l2cap_information_req(conn, &cmd, data); - break; - - case L2CAP_INFO_RSP: - err = l2cap_information_rsp(conn, &cmd, data); - break; - - default: - BT_ERR("Unknown signaling command 0x%2.2x", cmd.code); - err = -EINVAL; - break; - } + if (conn->hcon->type == LE_LINK) + err = l2cap_le_sig_cmd(conn, &cmd, data); + else + err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data); if (err) { struct l2cap_cmd_rej rej; @@ -3566,6 +3603,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG("len %d, cid 0x%4.4x", len, cid); switch (cid) { + case L2CAP_CID_LE_SIGNALING: case L2CAP_CID_SIGNALING: l2cap_sig_channel(conn, skb); break; -- cgit v1.2.3 From de73115a7d67e1b81dbde2285a7657f3e3867703 Mon Sep 17 00:00:00 2001 From: Claudio Takahasi Date: Fri, 11 Feb 2011 19:28:55 -0200 Subject: Bluetooth: Add connection parameter update response Implements L2CAP Connection Parameter Update Response defined in the Bluetooth Core Specification, Volume 3, Part A, section 4.21. Address the LE Connection Parameter Procedure initiated by the slave. Connection Interval Minimum and Maximum have the same range: 6 to 3200. Time = N * 1.25ms. Minimum shall be less or equal to Maximum. The Slave Latency field shall have a value in the range of 0 to ((connSupervisionTimeout / connIntervalMax) - 1). Latency field shall be less than 500. connSupervisionTimeout = Timeout Multiplier * 10 ms. Multiplier field shall have a value in the range of 10 to 3200. Signed-off-by: Claudio Takahasi Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 15 +++++++++++ net/bluetooth/l2cap_core.c | 59 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 06f245dcf6b2..4f4bff1eaed6 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -261,6 +261,21 @@ struct l2cap_info_rsp { #define L2CAP_IR_SUCCESS 0x0000 #define L2CAP_IR_NOTSUPP 0x0001 +struct l2cap_conn_param_update_req { + __le16 min; + __le16 max; + __le16 latency; + __le16 to_multiplier; +} __packed; + +struct l2cap_conn_param_update_rsp { + __le16 result; +} __packed; + +/* Connection Parameters result */ +#define L2CAP_CONN_PARAM_ACCEPTED 0x0000 +#define L2CAP_CONN_PARAM_REJECTED 0x0001 + /* ----- L2CAP connections ----- */ struct l2cap_chan_list { struct sock *head; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ce781a43f1d5..e0e7b82cff02 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2501,6 +2501,63 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm return 0; } +static int inline l2cap_check_conn_param(u16 min, u16 max, u16 latency, + u16 to_multiplier) +{ + u16 max_latency; + + if (min > max || min < 6 || max > 3200) + return -EINVAL; + + if (to_multiplier < 10 || to_multiplier > 3200) + return -EINVAL; + + if (max >= to_multiplier * 8) + return -EINVAL; + + max_latency = (to_multiplier * 8 / max) - 1; + if (latency > 499 || latency > max_latency) + return -EINVAL; + + return 0; +} + +static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) +{ + struct hci_conn *hcon = conn->hcon; + struct l2cap_conn_param_update_req *req; + struct l2cap_conn_param_update_rsp rsp; + u16 min, max, latency, to_multiplier, cmd_len; + + if (!(hcon->link_mode & HCI_LM_MASTER)) + return -EINVAL; + + cmd_len = __le16_to_cpu(cmd->len); + if (cmd_len != sizeof(struct l2cap_conn_param_update_req)) + return -EPROTO; + + req = (struct l2cap_conn_param_update_req *) data; + min = __le16_to_cpu(req->min); + max = __le16_to_cpu(req->max); + latency = __le16_to_cpu(req->latency); + to_multiplier = __le16_to_cpu(req->to_multiplier); + + BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x Timeout: 0x%4.4x", + min, max, latency, to_multiplier); + + memset(&rsp, 0, sizeof(rsp)); + if (l2cap_check_conn_param(min, max, latency, to_multiplier)) + rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED); + else + rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED); + + l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP, + sizeof(rsp), &rsp); + + return 0; +} + static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) { @@ -2567,7 +2624,7 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, return 0; case L2CAP_CONN_PARAM_UPDATE_REQ: - return -EINVAL; + return l2cap_conn_param_update_req(conn, cmd, data); case L2CAP_CONN_PARAM_UPDATE_RSP: return 0; -- cgit v1.2.3 From 6bd32326cdaa9b14794416150c88e4832fb7e592 Mon Sep 17 00:00:00 2001 From: Ville Tervo Date: Wed, 16 Feb 2011 16:32:41 +0200 Subject: Bluetooth: Use proper timer for hci command timout Use proper timer instead of hci command flow control to timeout failed hci commands. Otherwise stack ends up sending commands when flow control is used to block new commands. 2010-09-01 18:29:41.592132 < HCI Command: Remote Name Request (0x01|0x0019) plen 10 bdaddr 00:16:CF:E1:C7:D7 mode 2 clkoffset 0x0000 2010-09-01 18:29:41.592681 > HCI Event: Command Status (0x0f) plen 4 Remote Name Request (0x01|0x0019) status 0x00 ncmd 0 2010-09-01 18:29:51.022033 < HCI Command: Remote Name Request Cancel (0x01|0x001a) plen 6 bdaddr 00:16:CF:E1:C7:D7 Signed-off-by: Ville Tervo Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 3 +++ include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 22 ++++++++++++++++------ net/bluetooth/hci_event.c | 6 ++++++ 4 files changed, 26 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index e756f82a29e5..6d4e11624fef 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -119,6 +119,7 @@ enum { #define HCI_PAIRING_TIMEOUT (60000) /* 60 seconds */ #define HCI_IDLE_TIMEOUT (6000) /* 6 seconds */ #define HCI_INIT_TIMEOUT (10000) /* 10 seconds */ +#define HCI_CMD_TIMEOUT (1000) /* 1 seconds */ /* HCI data types */ #define HCI_COMMAND_PKT 0x01 @@ -244,6 +245,8 @@ enum { #define HCI_AT_GENERAL_BONDING_MITM 0x05 /* ----- HCI Commands ---- */ +#define HCI_OP_NOP 0x0000 + #define HCI_OP_INQUIRY 0x0401 struct hci_cp_inquiry { __u8 lap[3]; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d30b93c82fd4..ecd2acf24420 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -132,7 +132,6 @@ struct hci_dev { unsigned int sco_pkts; unsigned int le_pkts; - unsigned long cmd_last_tx; unsigned long acl_last_tx; unsigned long sco_last_tx; unsigned long le_last_tx; @@ -143,6 +142,7 @@ struct hci_dev { struct work_struct power_off; struct timer_list off_timer; + struct timer_list cmd_timer; struct tasklet_struct cmd_task; struct tasklet_struct rx_task; struct tasklet_struct tx_task; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index c01415bc8946..702d5651c656 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -623,6 +624,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) /* Drop last sent command */ if (hdev->sent_cmd) { + del_timer_sync(&hdev->cmd_timer); kfree_skb(hdev->sent_cmd); hdev->sent_cmd = NULL; } @@ -1066,6 +1068,16 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) return 0; } +/* HCI command timer function */ +static void hci_cmd_timer(unsigned long arg) +{ + struct hci_dev *hdev = (void *) arg; + + BT_ERR("%s command tx timeout", hdev->name); + atomic_set(&hdev->cmd_cnt, 1); + tasklet_schedule(&hdev->cmd_task); +} + /* Register HCI device */ int hci_register_dev(struct hci_dev *hdev) { @@ -1112,6 +1124,8 @@ int hci_register_dev(struct hci_dev *hdev) skb_queue_head_init(&hdev->cmd_q); skb_queue_head_init(&hdev->raw_q); + setup_timer(&hdev->cmd_timer, hci_cmd_timer, (unsigned long) hdev); + for (i = 0; i < NUM_REASSEMBLY; i++) hdev->reassembly[i] = NULL; @@ -2004,11 +2018,6 @@ static void hci_cmd_task(unsigned long arg) BT_DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt)); - if (!atomic_read(&hdev->cmd_cnt) && time_after(jiffies, hdev->cmd_last_tx + HZ)) { - BT_ERR("%s command tx timeout", hdev->name); - atomic_set(&hdev->cmd_cnt, 1); - } - /* Send queued commands */ if (atomic_read(&hdev->cmd_cnt)) { skb = skb_dequeue(&hdev->cmd_q); @@ -2021,7 +2030,8 @@ static void hci_cmd_task(unsigned long arg) if (hdev->sent_cmd) { atomic_dec(&hdev->cmd_cnt); hci_send_frame(skb); - hdev->cmd_last_tx = jiffies; + mod_timer(&hdev->cmd_timer, + jiffies + msecs_to_jiffies(HCI_CMD_TIMEOUT)); } else { skb_queue_head(&hdev->cmd_q, skb); tasklet_schedule(&hdev->cmd_task); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 74f04a27734d..09cb29e8713a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1732,6 +1732,9 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk break; } + if (ev->opcode != HCI_OP_NOP) + del_timer(&hdev->cmd_timer); + if (ev->ncmd) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) @@ -1807,6 +1810,9 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) break; } + if (ev->opcode != HCI_OP_NOP) + del_timer(&hdev->cmd_timer); + if (ev->ncmd) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) -- cgit v1.2.3 From 3449248c8731e8474980856d76bbf9bac9b0682f Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 14 Feb 2011 12:27:50 -0800 Subject: PCI: fix tlan build when CONFIG_PCI is not enabled When CONFIG_PCI is not enabled, tlan.c has a build error: drivers/net/tlan.c:503: error: implicit declaration of function 'pci_wake_from_d3' so add an inline function stub for this function to pci.h when PCI is not enabled, similar to other stubbed PCI functions. Signed-off-by: Randy Dunlap Acked-by: David S. Miller Acked-by: Sakari Ailus Signed-off-by: Jesse Barnes --- include/linux/pci.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/pci.h b/include/linux/pci.h index 559d02897075..c77e730bcd38 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1191,6 +1191,11 @@ static inline int pci_set_power_state(struct pci_dev *dev, pci_power_t state) return 0; } +static inline int pci_wake_from_d3(struct pci_dev *dev, bool enable) +{ + return 0; +} + static inline pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state) { -- cgit v1.2.3 From 2ce603ebe1f1420c7c5b013638ec29b4fc975180 Mon Sep 17 00:00:00 2001 From: Claudio Takahasi Date: Wed, 16 Feb 2011 20:44:53 -0200 Subject: Bluetooth: Send LE Connection Update Command If the new connection update parameter are accepted, the LE master host sends the LE Connection Update Command to its controller informing the new requested parameters. Signed-off-by: Claudio Takahasi Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 11 +++++++++++ include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_conn.c | 20 ++++++++++++++++++++ net/bluetooth/l2cap_core.c | 8 +++++++- 4 files changed, 40 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 6d4e11624fef..a5f8c4684a32 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -677,6 +677,17 @@ struct hci_cp_le_create_conn { #define HCI_OP_LE_CREATE_CONN_CANCEL 0x200e +#define HCI_OP_LE_CONN_UPDATE 0x2013 +struct hci_cp_le_conn_update { + __le16 handle; + __le16 conn_interval_min; + __le16 conn_interval_max; + __le16 conn_latency; + __le16 supervision_timeout; + __le16 min_ce_len; + __le16 max_ce_len; +} __packed; + /* ---- HCI Events ---- */ #define HCI_EV_INQUIRY_COMPLETE 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ecd2acf24420..7ee921d78a94 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -777,4 +777,6 @@ struct hci_sec_filter { void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result); +void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, + u16 latency, u16 to_multiplier); #endif /* __HCI_CORE_H */ diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index efcd2b508f5d..a050a6984901 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -183,6 +183,26 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle) hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp); } +void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, + u16 latency, u16 to_multiplier) +{ + struct hci_cp_le_conn_update cp; + struct hci_dev *hdev = conn->hdev; + + memset(&cp, 0, sizeof(cp)); + + cp.handle = cpu_to_le16(conn->handle); + cp.conn_interval_min = cpu_to_le16(min); + cp.conn_interval_max = cpu_to_le16(max); + cp.conn_latency = cpu_to_le16(latency); + cp.supervision_timeout = cpu_to_le16(to_multiplier); + cp.min_ce_len = cpu_to_le16(0x0001); + cp.max_ce_len = cpu_to_le16(0x0001); + + hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp); +} +EXPORT_SYMBOL(hci_le_conn_update); + /* Device _must_ be locked */ void hci_sco_setup(struct hci_conn *conn, __u8 status) { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index e0e7b82cff02..bd3136710360 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2529,6 +2529,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, struct l2cap_conn_param_update_req *req; struct l2cap_conn_param_update_rsp rsp; u16 min, max, latency, to_multiplier, cmd_len; + int err; if (!(hcon->link_mode & HCI_LM_MASTER)) return -EINVAL; @@ -2547,7 +2548,9 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, min, max, latency, to_multiplier); memset(&rsp, 0, sizeof(rsp)); - if (l2cap_check_conn_param(min, max, latency, to_multiplier)) + + err = l2cap_check_conn_param(min, max, latency, to_multiplier); + if (err) rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED); else rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED); @@ -2555,6 +2558,9 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP, sizeof(rsp), &rsp); + if (!err) + hci_le_conn_update(hcon, min, max, latency, to_multiplier); + return 0; } -- cgit v1.2.3 From adc4266d87ba95e250e5ffa217c72b4b78c2b56a Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Thu, 17 Feb 2011 16:42:00 +0100 Subject: Bluetooth: Fix some code style issues in hci_core.h Signed-off-by: Szymon Janc Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 58 ++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7ee921d78a94..d5d8454236bf 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -199,37 +199,37 @@ struct hci_dev { struct hci_conn { struct list_head list; - atomic_t refcnt; - spinlock_t lock; - - bdaddr_t dst; - __u16 handle; - __u16 state; - __u8 mode; - __u8 type; - __u8 out; - __u8 attempt; - __u8 dev_class[3]; - __u8 features[8]; - __u8 ssp_mode; - __u16 interval; - __u16 pkt_type; - __u16 link_policy; - __u32 link_mode; - __u8 auth_type; - __u8 sec_level; - __u8 pending_sec_level; - __u8 pin_length; - __u8 io_capability; - __u8 power_save; - __u16 disc_timeout; - unsigned long pend; + atomic_t refcnt; + spinlock_t lock; + + bdaddr_t dst; + __u16 handle; + __u16 state; + __u8 mode; + __u8 type; + __u8 out; + __u8 attempt; + __u8 dev_class[3]; + __u8 features[8]; + __u8 ssp_mode; + __u16 interval; + __u16 pkt_type; + __u16 link_policy; + __u32 link_mode; + __u8 auth_type; + __u8 sec_level; + __u8 pending_sec_level; + __u8 pin_length; + __u8 io_capability; + __u8 power_save; + __u16 disc_timeout; + unsigned long pend; __u8 remote_cap; __u8 remote_oob; __u8 remote_auth; - unsigned int sent; + unsigned int sent; struct sk_buff_head data_q; @@ -347,7 +347,7 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) } static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev, - __u16 handle) + __u16 handle) { struct hci_conn_hash *h = &hdev->conn_hash; struct list_head *p; @@ -362,7 +362,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev, } static inline struct hci_conn *hci_conn_hash_lookup_ba(struct hci_dev *hdev, - __u8 type, bdaddr_t *ba) + __u8 type, bdaddr_t *ba) { struct hci_conn_hash *h = &hdev->conn_hash; struct list_head *p; @@ -377,7 +377,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_ba(struct hci_dev *hdev, } static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev, - __u8 type, __u16 state) + __u8 type, __u16 state) { struct hci_conn_hash *h = &hdev->conn_hash; struct list_head *p; -- cgit v1.2.3 From 03c2d87a2112a6548aa3f9635e76d3611c3df53c Mon Sep 17 00:00:00 2001 From: Andreas Mohr Date: Thu, 17 Feb 2011 00:17:53 +0100 Subject: ALSA: ac97: replace open-coded, error-prone stuff with AC97 bit defines Use AC97 macros (sometimes already existing, or newly added) instead of error-prone repetition of open-coded values. Signed-off-by: Andreas Mohr Signed-off-by: Takashi Iwai --- include/sound/ac97_codec.h | 5 +++ sound/pci/ac97/ac97_codec.c | 83 ++++++++++++++++++++++++++------------------- 2 files changed, 54 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index b602f475cdbb..f1dcefe4532b 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -96,6 +96,10 @@ #define AC97_FUNC_INFO 0x68 /* Function Information */ #define AC97_SENSE_INFO 0x6a /* Sense Details */ +/* volume controls */ +#define AC97_MUTE_MASK_MONO 0x8000 +#define AC97_MUTE_MASK_STEREO 0x8080 + /* slot allocation */ #define AC97_SLOT_TAG 0 #define AC97_SLOT_CMD_ADDR 1 @@ -138,6 +142,7 @@ #define AC97_BC_18BIT_ADC 0x0100 /* 18-bit ADC resolution */ #define AC97_BC_20BIT_ADC 0x0200 /* 20-bit ADC resolution */ #define AC97_BC_ADC_MASK 0x0300 +#define AC97_BC_3D_TECH_ID_MASK 0x7c00 /* Per-vendor ID of 3D enhancement */ /* general purpose */ #define AC97_GP_DRSS_MASK 0x0c00 /* double rate slot select */ diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index cb62d178b3e0..2cc56b5c5397 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -590,9 +590,9 @@ static int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol, snd_ac97_page_restore(ac97, page_save); #ifdef CONFIG_SND_AC97_POWER_SAVE /* check analog mixer power-down */ - if ((val_mask & 0x8000) && + if ((val_mask & AC97_PD_EAPD) && (kcontrol->private_value & (1<<30))) { - if (val & 0x8000) + if (val & AC97_PD_EAPD) ac97->power_up &= ~(1 << (reg>>1)); else ac97->power_up |= 1 << (reg>>1); @@ -1035,20 +1035,20 @@ static int snd_ac97_dev_free(struct snd_device *device) static int snd_ac97_try_volume_mix(struct snd_ac97 * ac97, int reg) { - unsigned short val, mask = 0x8000; + unsigned short val, mask = AC97_MUTE_MASK_MONO; if (! snd_ac97_valid_reg(ac97, reg)) return 0; switch (reg) { case AC97_MASTER_TONE: - return ac97->caps & 0x04 ? 1 : 0; + return ac97->caps & AC97_BC_BASS_TREBLE ? 1 : 0; case AC97_HEADPHONE: - return ac97->caps & 0x10 ? 1 : 0; + return ac97->caps & AC97_BC_HEADPHONE ? 1 : 0; case AC97_REC_GAIN_MIC: - return ac97->caps & 0x01 ? 1 : 0; + return ac97->caps & AC97_BC_DEDICATED_MIC ? 1 : 0; case AC97_3D_CONTROL: - if (ac97->caps & 0x7c00) { + if (ac97->caps & AC97_BC_3D_TECH_ID_MASK) { val = snd_ac97_read(ac97, reg); /* if nonzero - fixed and we can't set it */ return val == 0; @@ -1104,7 +1104,10 @@ static void check_volume_resolution(struct snd_ac97 *ac97, int reg, unsigned cha *lo_max = *hi_max = 0; for (i = 0 ; i < ARRAY_SIZE(cbit); i++) { unsigned short val; - snd_ac97_write(ac97, reg, 0x8080 | cbit[i] | (cbit[i] << 8)); + snd_ac97_write( + ac97, reg, + AC97_MUTE_MASK_STEREO | cbit[i] | (cbit[i] << 8) + ); /* Do the read twice due to buffers on some ac97 codecs. * e.g. The STAC9704 returns exactly what you wrote to the register * if you read it immediately. This causes the detect routine to fail. @@ -1139,14 +1142,14 @@ static void snd_ac97_change_volume_params2(struct snd_ac97 * ac97, int reg, int unsigned short val, val1; *max = 63; - val = 0x8080 | (0x20 << shift); + val = AC97_MUTE_MASK_STEREO | (0x20 << shift); snd_ac97_write(ac97, reg, val); val1 = snd_ac97_read(ac97, reg); if (val != val1) { *max = 31; } /* reset volume to zero */ - snd_ac97_write_cache(ac97, reg, 0x8080); + snd_ac97_write_cache(ac97, reg, AC97_MUTE_MASK_STEREO); } static inline int printable(unsigned int x) @@ -1183,16 +1186,16 @@ static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg, if (! snd_ac97_valid_reg(ac97, reg)) return 0; - mute_mask = 0x8000; + mute_mask = AC97_MUTE_MASK_MONO; val = snd_ac97_read(ac97, reg); if (check_stereo || (ac97->flags & AC97_STEREO_MUTES)) { /* check whether both mute bits work */ - val1 = val | 0x8080; + val1 = val | AC97_MUTE_MASK_STEREO; snd_ac97_write(ac97, reg, val1); if (val1 == snd_ac97_read(ac97, reg)) - mute_mask = 0x8080; + mute_mask = AC97_MUTE_MASK_STEREO; } - if (mute_mask == 0x8080) { + if (mute_mask == AC97_MUTE_MASK_STEREO) { struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1); if (check_amix) tmp.private_value |= (1 << 30); @@ -1268,9 +1271,11 @@ static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigne err = snd_ctl_add(card, kctl); if (err < 0) return err; - snd_ac97_write_cache(ac97, reg, - (snd_ac97_read(ac97, reg) & 0x8080) | - lo_max | (hi_max << 8)); + snd_ac97_write_cache( + ac97, reg, + (snd_ac97_read(ac97, reg) & AC97_MUTE_MASK_STEREO) + | lo_max | (hi_max << 8) + ); return 0; } @@ -1332,7 +1337,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) return err; } - ac97->regs[AC97_CENTER_LFE_MASTER] = 0x8080; + ac97->regs[AC97_CENTER_LFE_MASTER] = AC97_MUTE_MASK_STEREO; /* build center controls */ if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER)) @@ -1410,8 +1415,12 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0) return err; set_tlv_db_scale(kctl, db_scale_4bit); - snd_ac97_write_cache(ac97, AC97_PC_BEEP, - snd_ac97_read(ac97, AC97_PC_BEEP) | 0x801e); + snd_ac97_write_cache( + ac97, + AC97_PC_BEEP, + (snd_ac97_read(ac97, AC97_PC_BEEP) + | AC97_MUTE_MASK_MONO | 0x001e) + ); } /* build Phone controls */ @@ -1545,7 +1554,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) } /* build Simulated Stereo Enhancement control */ - if (ac97->caps & 0x0008) { + if (ac97->caps & AC97_BC_SIM_STEREO) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97))) < 0) return err; } @@ -1557,7 +1566,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) } /* build Loudness control */ - if (ac97->caps & 0x0020) { + if (ac97->caps & AC97_BC_LOUDNESS) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97))) < 0) return err; } @@ -2542,8 +2551,8 @@ void snd_ac97_resume(struct snd_ac97 *ac97) schedule_timeout_uninterruptible(1); } while (time_after_eq(end_time, jiffies)); /* FIXME: extra delay */ - ac97->bus->ops->write(ac97, AC97_MASTER, 0x8000); - if (snd_ac97_read(ac97, AC97_MASTER) != 0x8000) + ac97->bus->ops->write(ac97, AC97_MASTER, AC97_MUTE_MASK_MONO); + if (snd_ac97_read(ac97, AC97_MASTER) != AC97_MUTE_MASK_MONO) msleep(250); } else { end_time = jiffies + msecs_to_jiffies(100); @@ -2747,12 +2756,12 @@ static int master_mute_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem int rshift = (kcontrol->private_value >> 12) & 0x0f; unsigned short mask; if (shift != rshift) - mask = 0x8080; + mask = AC97_MUTE_MASK_STEREO; else - mask = 0x8000; - snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, + mask = AC97_MUTE_MASK_MONO; + snd_ac97_update_bits(ac97, AC97_POWERDOWN, AC97_PD_EAPD, (ac97->regs[AC97_MASTER] & mask) == mask ? - 0x8000 : 0); + AC97_PD_EAPD : 0); } return err; } @@ -2765,7 +2774,10 @@ static int tune_mute_led(struct snd_ac97 *ac97) return -ENOENT; msw->put = master_mute_sw_put; snd_ac97_remove_ctl(ac97, "External Amplifier", NULL); - snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */ + snd_ac97_update_bits( + ac97, AC97_POWERDOWN, + AC97_PD_EAPD, AC97_PD_EAPD /* mute LED on */ + ); ac97->scaps |= AC97_SCAP_EAPD_LED; return 0; } @@ -2780,12 +2792,12 @@ static int hp_master_mute_sw_put(struct snd_kcontrol *kcontrol, int rshift = (kcontrol->private_value >> 12) & 0x0f; unsigned short mask; if (shift != rshift) - mask = 0x8080; + mask = AC97_MUTE_MASK_STEREO; else - mask = 0x8000; - snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, + mask = AC97_MUTE_MASK_MONO; + snd_ac97_update_bits(ac97, AC97_POWERDOWN, AC97_PD_EAPD, (ac97->regs[AC97_MASTER] & mask) == mask ? - 0x8000 : 0); + AC97_PD_EAPD : 0); } return err; } @@ -2801,7 +2813,10 @@ static int tune_hp_mute_led(struct snd_ac97 *ac97) snd_ac97_remove_ctl(ac97, "External Amplifier", NULL); snd_ac97_remove_ctl(ac97, "Headphone Playback", "Switch"); snd_ac97_remove_ctl(ac97, "Headphone Playback", "Volume"); - snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */ + snd_ac97_update_bits( + ac97, AC97_POWERDOWN, + AC97_PD_EAPD, AC97_PD_EAPD /* mute LED on */ + ); return 0; } -- cgit v1.2.3 From bd9a4c7df256cee4e9f6a4b56baa3b89d63f0f1e Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Thu, 17 Feb 2011 09:52:03 -0800 Subject: drivers: hwspinlock: add framework Add a platform-independent hwspinlock framework. Hardware spinlock devices are needed, e.g., in order to access data that is shared between remote processors, that otherwise have no alternative mechanism to accomplish synchronization and mutual exclusion operations. Signed-off-by: Ohad Ben-Cohen Cc: Hari Kanigeri Cc: Benoit Cousson Cc: Kevin Hilman Cc: Grant Likely Cc: Paul Walmsley Cc: Russell King Acked-by: Arnd Bergmann Signed-off-by: Tony Lindgren --- Documentation/hwspinlock.txt | 293 +++++++++++++++++ drivers/Kconfig | 2 + drivers/Makefile | 2 + drivers/hwspinlock/Kconfig | 13 + drivers/hwspinlock/Makefile | 5 + drivers/hwspinlock/hwspinlock_core.c | 548 +++++++++++++++++++++++++++++++ drivers/hwspinlock/hwspinlock_internal.h | 61 ++++ include/linux/hwspinlock.h | 292 ++++++++++++++++ 8 files changed, 1216 insertions(+) create mode 100644 Documentation/hwspinlock.txt create mode 100644 drivers/hwspinlock/Kconfig create mode 100644 drivers/hwspinlock/Makefile create mode 100644 drivers/hwspinlock/hwspinlock_core.c create mode 100644 drivers/hwspinlock/hwspinlock_internal.h create mode 100644 include/linux/hwspinlock.h (limited to 'include') diff --git a/Documentation/hwspinlock.txt b/Documentation/hwspinlock.txt new file mode 100644 index 000000000000..7dcd1a4e726c --- /dev/null +++ b/Documentation/hwspinlock.txt @@ -0,0 +1,293 @@ +Hardware Spinlock Framework + +1. Introduction + +Hardware spinlock modules provide hardware assistance for synchronization +and mutual exclusion between heterogeneous processors and those not operating +under a single, shared operating system. + +For example, OMAP4 has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP, +each of which is running a different Operating System (the master, A9, +is usually running Linux and the slave processors, the M3 and the DSP, +are running some flavor of RTOS). + +A generic hwspinlock framework allows platform-independent drivers to use +the hwspinlock device in order to access data structures that are shared +between remote processors, that otherwise have no alternative mechanism +to accomplish synchronization and mutual exclusion operations. + +This is necessary, for example, for Inter-processor communications: +on OMAP4, cpu-intensive multimedia tasks are offloaded by the host to the +remote M3 and/or C64x+ slave processors (by an IPC subsystem called Syslink). + +To achieve fast message-based communications, a minimal kernel support +is needed to deliver messages arriving from a remote processor to the +appropriate user process. + +This communication is based on simple data structures that is shared between +the remote processors, and access to it is synchronized using the hwspinlock +module (remote processor directly places new messages in this shared data +structure). + +A common hwspinlock interface makes it possible to have generic, platform- +independent, drivers. + +2. User API + + struct hwspinlock *hwspin_lock_request(void); + - dynamically assign an hwspinlock and return its address, or NULL + in case an unused hwspinlock isn't available. Users of this + API will usually want to communicate the lock's id to the remote core + before it can be used to achieve synchronization. + Can be called from an atomic context (this function will not sleep) but + not from within interrupt context. + + struct hwspinlock *hwspin_lock_request_specific(unsigned int id); + - assign a specific hwspinlock id and return its address, or NULL + if that hwspinlock is already in use. Usually board code will + be calling this function in order to reserve specific hwspinlock + ids for predefined purposes. + Can be called from an atomic context (this function will not sleep) but + not from within interrupt context. + + int hwspin_lock_free(struct hwspinlock *hwlock); + - free a previously-assigned hwspinlock; returns 0 on success, or an + appropriate error code on failure (e.g. -EINVAL if the hwspinlock + is already free). + Can be called from an atomic context (this function will not sleep) but + not from within interrupt context. + + int hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int timeout); + - lock a previously-assigned hwspinlock with a timeout limit (specified in + msecs). If the hwspinlock is already taken, the function will busy loop + waiting for it to be released, but give up when the timeout elapses. + Upon a successful return from this function, preemption is disabled so + the caller must not sleep, and is advised to release the hwspinlock as + soon as possible, in order to minimize remote cores polling on the + hardware interconnect. + Returns 0 when successful and an appropriate error code otherwise (most + notably -ETIMEDOUT if the hwspinlock is still busy after timeout msecs). + The function will never sleep. + + int hwspin_lock_timeout_irq(struct hwspinlock *hwlock, unsigned int timeout); + - lock a previously-assigned hwspinlock with a timeout limit (specified in + msecs). If the hwspinlock is already taken, the function will busy loop + waiting for it to be released, but give up when the timeout elapses. + Upon a successful return from this function, preemption and the local + interrupts are disabled, so the caller must not sleep, and is advised to + release the hwspinlock as soon as possible. + Returns 0 when successful and an appropriate error code otherwise (most + notably -ETIMEDOUT if the hwspinlock is still busy after timeout msecs). + The function will never sleep. + + int hwspin_lock_timeout_irqsave(struct hwspinlock *hwlock, unsigned int to, + unsigned long *flags); + - lock a previously-assigned hwspinlock with a timeout limit (specified in + msecs). If the hwspinlock is already taken, the function will busy loop + waiting for it to be released, but give up when the timeout elapses. + Upon a successful return from this function, preemption is disabled, + local interrupts are disabled and their previous state is saved at the + given flags placeholder. The caller must not sleep, and is advised to + release the hwspinlock as soon as possible. + Returns 0 when successful and an appropriate error code otherwise (most + notably -ETIMEDOUT if the hwspinlock is still busy after timeout msecs). + The function will never sleep. + + int hwspin_trylock(struct hwspinlock *hwlock); + - attempt to lock a previously-assigned hwspinlock, but immediately fail if + it is already taken. + Upon a successful return from this function, preemption is disabled so + caller must not sleep, and is advised to release the hwspinlock as soon as + possible, in order to minimize remote cores polling on the hardware + interconnect. + Returns 0 on success and an appropriate error code otherwise (most + notably -EBUSY if the hwspinlock was already taken). + The function will never sleep. + + int hwspin_trylock_irq(struct hwspinlock *hwlock); + - attempt to lock a previously-assigned hwspinlock, but immediately fail if + it is already taken. + Upon a successful return from this function, preemption and the local + interrupts are disabled so caller must not sleep, and is advised to + release the hwspinlock as soon as possible. + Returns 0 on success and an appropriate error code otherwise (most + notably -EBUSY if the hwspinlock was already taken). + The function will never sleep. + + int hwspin_trylock_irqsave(struct hwspinlock *hwlock, unsigned long *flags); + - attempt to lock a previously-assigned hwspinlock, but immediately fail if + it is already taken. + Upon a successful return from this function, preemption is disabled, + the local interrupts are disabled and their previous state is saved + at the given flags placeholder. The caller must not sleep, and is advised + to release the hwspinlock as soon as possible. + Returns 0 on success and an appropriate error code otherwise (most + notably -EBUSY if the hwspinlock was already taken). + The function will never sleep. + + void hwspin_unlock(struct hwspinlock *hwlock); + - unlock a previously-locked hwspinlock. Always succeed, and can be called + from any context (the function never sleeps). Note: code should _never_ + unlock an hwspinlock which is already unlocked (there is no protection + against this). + + void hwspin_unlock_irq(struct hwspinlock *hwlock); + - unlock a previously-locked hwspinlock and enable local interrupts. + The caller should _never_ unlock an hwspinlock which is already unlocked. + Doing so is considered a bug (there is no protection against this). + Upon a successful return from this function, preemption and local + interrupts are enabled. This function will never sleep. + + void + hwspin_unlock_irqrestore(struct hwspinlock *hwlock, unsigned long *flags); + - unlock a previously-locked hwspinlock. + The caller should _never_ unlock an hwspinlock which is already unlocked. + Doing so is considered a bug (there is no protection against this). + Upon a successful return from this function, preemption is reenabled, + and the state of the local interrupts is restored to the state saved at + the given flags. This function will never sleep. + + int hwspin_lock_get_id(struct hwspinlock *hwlock); + - retrieve id number of a given hwspinlock. This is needed when an + hwspinlock is dynamically assigned: before it can be used to achieve + mutual exclusion with a remote cpu, the id number should be communicated + to the remote task with which we want to synchronize. + Returns the hwspinlock id number, or -EINVAL if hwlock is null. + +3. Typical usage + +#include +#include + +int hwspinlock_example1(void) +{ + struct hwspinlock *hwlock; + int ret; + + /* dynamically assign a hwspinlock */ + hwlock = hwspin_lock_request(); + if (!hwlock) + ... + + id = hwspin_lock_get_id(hwlock); + /* probably need to communicate id to a remote processor now */ + + /* take the lock, spin for 1 sec if it's already taken */ + ret = hwspin_lock_timeout(hwlock, 1000); + if (ret) + ... + + /* + * we took the lock, do our thing now, but do NOT sleep + */ + + /* release the lock */ + hwspin_unlock(hwlock); + + /* free the lock */ + ret = hwspin_lock_free(hwlock); + if (ret) + ... + + return ret; +} + +int hwspinlock_example2(void) +{ + struct hwspinlock *hwlock; + int ret; + + /* + * assign a specific hwspinlock id - this should be called early + * by board init code. + */ + hwlock = hwspin_lock_request_specific(PREDEFINED_LOCK_ID); + if (!hwlock) + ... + + /* try to take it, but don't spin on it */ + ret = hwspin_trylock(hwlock); + if (!ret) { + pr_info("lock is already taken\n"); + return -EBUSY; + } + + /* + * we took the lock, do our thing now, but do NOT sleep + */ + + /* release the lock */ + hwspin_unlock(hwlock); + + /* free the lock */ + ret = hwspin_lock_free(hwlock); + if (ret) + ... + + return ret; +} + + +4. API for implementors + + int hwspin_lock_register(struct hwspinlock *hwlock); + - to be called from the underlying platform-specific implementation, in + order to register a new hwspinlock instance. Can be called from an atomic + context (this function will not sleep) but not from within interrupt + context. Returns 0 on success, or appropriate error code on failure. + + struct hwspinlock *hwspin_lock_unregister(unsigned int id); + - to be called from the underlying vendor-specific implementation, in order + to unregister an existing (and unused) hwspinlock instance. + Can be called from an atomic context (will not sleep) but not from + within interrupt context. + Returns the address of hwspinlock on success, or NULL on error (e.g. + if the hwspinlock is sill in use). + +5. struct hwspinlock + +This struct represents an hwspinlock instance. It is registered by the +underlying hwspinlock implementation using the hwspin_lock_register() API. + +/** + * struct hwspinlock - vendor-specific hwspinlock implementation + * + * @dev: underlying device, will be used with runtime PM api + * @ops: vendor-specific hwspinlock handlers + * @id: a global, unique, system-wide, index of the lock. + * @lock: initialized and used by hwspinlock core + * @owner: underlying implementation module, used to maintain module ref count + */ +struct hwspinlock { + struct device *dev; + const struct hwspinlock_ops *ops; + int id; + spinlock_t lock; + struct module *owner; +}; + +The underlying implementation is responsible to assign the dev, ops, id and +owner members. The lock member, OTOH, is initialized and used by the hwspinlock +core. + +6. Implementation callbacks + +There are three possible callbacks defined in 'struct hwspinlock_ops': + +struct hwspinlock_ops { + int (*trylock)(struct hwspinlock *lock); + void (*unlock)(struct hwspinlock *lock); + void (*relax)(struct hwspinlock *lock); +}; + +The first two callbacks are mandatory: + +The ->trylock() callback should make a single attempt to take the lock, and +return 0 on failure and 1 on success. This callback may _not_ sleep. + +The ->unlock() callback releases the lock. It always succeed, and it, too, +may _not_ sleep. + +The ->relax() callback is optional. It is called by hwspinlock core while +spinning on a lock, and can be used by the underlying implementation to force +a delay between two successive invocations of ->trylock(). It may _not_ sleep. diff --git a/drivers/Kconfig b/drivers/Kconfig index 9bfb71ff3a6a..177c7d156933 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -117,4 +117,6 @@ source "drivers/staging/Kconfig" source "drivers/platform/Kconfig" source "drivers/clk/Kconfig" + +source "drivers/hwspinlock/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index b423bb16c3a8..3f135b6fb014 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -117,3 +117,5 @@ obj-y += platform/ obj-y += ieee802154/ #common clk code obj-y += clk/ + +obj-$(CONFIG_HWSPINLOCK) += hwspinlock/ diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig new file mode 100644 index 000000000000..9dd8db46606b --- /dev/null +++ b/drivers/hwspinlock/Kconfig @@ -0,0 +1,13 @@ +# +# Generic HWSPINLOCK framework +# + +config HWSPINLOCK + tristate "Generic Hardware Spinlock framework" + help + Say y here to support the generic hardware spinlock framework. + You only need to enable this if you have hardware spinlock module + on your system (usually only relevant if your system has remote slave + coprocessors). + + If unsure, say N. diff --git a/drivers/hwspinlock/Makefile b/drivers/hwspinlock/Makefile new file mode 100644 index 000000000000..b9d2b9f40491 --- /dev/null +++ b/drivers/hwspinlock/Makefile @@ -0,0 +1,5 @@ +# +# Generic Hardware Spinlock framework +# + +obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c new file mode 100644 index 000000000000..43a62714b4fb --- /dev/null +++ b/drivers/hwspinlock/hwspinlock_core.c @@ -0,0 +1,548 @@ +/* + * Hardware spinlock framework + * + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com + * + * Contact: Ohad Ben-Cohen + * + * 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hwspinlock_internal.h" + +/* radix tree tags */ +#define HWSPINLOCK_UNUSED (0) /* tags an hwspinlock as unused */ + +/* + * A radix tree is used to maintain the available hwspinlock instances. + * The tree associates hwspinlock pointers with their integer key id, + * and provides easy-to-use API which makes the hwspinlock core code simple + * and easy to read. + * + * Radix trees are quick on lookups, and reasonably efficient in terms of + * storage, especially with high density usages such as this framework + * requires (a continuous range of integer keys, beginning with zero, is + * used as the ID's of the hwspinlock instances). + * + * The radix tree API supports tagging items in the tree, which this + * framework uses to mark unused hwspinlock instances (see the + * HWSPINLOCK_UNUSED tag above). As a result, the process of querying the + * tree, looking for an unused hwspinlock instance, is now reduced to a + * single radix tree API call. + */ +static RADIX_TREE(hwspinlock_tree, GFP_KERNEL); + +/* + * Synchronization of access to the tree is achieved using this spinlock, + * as the radix-tree API requires that users provide all synchronisation. + */ +static DEFINE_SPINLOCK(hwspinlock_tree_lock); + +/** + * __hwspin_trylock() - attempt to lock a specific hwspinlock + * @hwlock: an hwspinlock which we want to trylock + * @mode: controls whether local interrupts are disabled or not + * @flags: a pointer where the caller's interrupt state will be saved at (if + * requested) + * + * This function attempts to lock an hwspinlock, and will immediately + * fail if the hwspinlock is already taken. + * + * Upon a successful return from this function, preemption (and possibly + * interrupts) is disabled, so the caller must not sleep, and is advised to + * release the hwspinlock as soon as possible. This is required in order to + * minimize remote cores polling on the hardware interconnect. + * + * The user decides whether local interrupts are disabled or not, and if yes, + * whether he wants their previous state to be saved. It is up to the user + * to choose the appropriate @mode of operation, exactly the same way users + * should decide between spin_trylock, spin_trylock_irq and + * spin_trylock_irqsave. + * + * Returns 0 if we successfully locked the hwspinlock or -EBUSY if + * the hwspinlock was already taken. + * This function will never sleep. + */ +int __hwspin_trylock(struct hwspinlock *hwlock, int mode, unsigned long *flags) +{ + int ret; + + BUG_ON(!hwlock); + BUG_ON(!flags && mode == HWLOCK_IRQSTATE); + + /* + * This spin_lock{_irq, _irqsave} serves three purposes: + * + * 1. Disable preemption, in order to minimize the period of time + * in which the hwspinlock is taken. This is important in order + * to minimize the possible polling on the hardware interconnect + * by a remote user of this lock. + * 2. Make the hwspinlock SMP-safe (so we can take it from + * additional contexts on the local host). + * 3. Ensure that in_atomic/might_sleep checks catch potential + * problems with hwspinlock usage (e.g. scheduler checks like + * 'scheduling while atomic' etc.) + */ + if (mode == HWLOCK_IRQSTATE) + ret = spin_trylock_irqsave(&hwlock->lock, *flags); + else if (mode == HWLOCK_IRQ) + ret = spin_trylock_irq(&hwlock->lock); + else + ret = spin_trylock(&hwlock->lock); + + /* is lock already taken by another context on the local cpu ? */ + if (!ret) + return -EBUSY; + + /* try to take the hwspinlock device */ + ret = hwlock->ops->trylock(hwlock); + + /* if hwlock is already taken, undo spin_trylock_* and exit */ + if (!ret) { + if (mode == HWLOCK_IRQSTATE) + spin_unlock_irqrestore(&hwlock->lock, *flags); + else if (mode == HWLOCK_IRQ) + spin_unlock_irq(&hwlock->lock); + else + spin_unlock(&hwlock->lock); + + return -EBUSY; + } + + /* + * We can be sure the other core's memory operations + * are observable to us only _after_ we successfully take + * the hwspinlock, and we must make sure that subsequent memory + * operations (both reads and writes) will not be reordered before + * we actually took the hwspinlock. + * + * Note: the implicit memory barrier of the spinlock above is too + * early, so we need this additional explicit memory barrier. + */ + mb(); + + return 0; +} +EXPORT_SYMBOL_GPL(__hwspin_trylock); + +/** + * __hwspin_lock_timeout() - lock an hwspinlock with timeout limit + * @hwlock: the hwspinlock to be locked + * @timeout: timeout value in msecs + * @mode: mode which controls whether local interrupts are disabled or not + * @flags: a pointer to where the caller's interrupt state will be saved at (if + * requested) + * + * This function locks the given @hwlock. If the @hwlock + * is already taken, the function will busy loop waiting for it to + * be released, but give up after @timeout msecs have elapsed. + * + * Upon a successful return from this function, preemption is disabled + * (and possibly local interrupts, too), so the caller must not sleep, + * and is advised to release the hwspinlock as soon as possible. + * This is required in order to minimize remote cores polling on the + * hardware interconnect. + * + * The user decides whether local interrupts are disabled or not, and if yes, + * whether he wants their previous state to be saved. It is up to the user + * to choose the appropriate @mode of operation, exactly the same way users + * should decide between spin_lock, spin_lock_irq and spin_lock_irqsave. + * + * Returns 0 when the @hwlock was successfully taken, and an appropriate + * error code otherwise (most notably -ETIMEDOUT if the @hwlock is still + * busy after @timeout msecs). The function will never sleep. + */ +int __hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int to, + int mode, unsigned long *flags) +{ + int ret; + unsigned long expire; + + expire = msecs_to_jiffies(to) + jiffies; + + for (;;) { + /* Try to take the hwspinlock */ + ret = __hwspin_trylock(hwlock, mode, flags); + if (ret != -EBUSY) + break; + + /* + * The lock is already taken, let's check if the user wants + * us to try again + */ + if (time_is_before_eq_jiffies(expire)) + return -ETIMEDOUT; + + /* + * Allow platform-specific relax handlers to prevent + * hogging the interconnect (no sleeping, though) + */ + if (hwlock->ops->relax) + hwlock->ops->relax(hwlock); + } + + return ret; +} +EXPORT_SYMBOL_GPL(__hwspin_lock_timeout); + +/** + * __hwspin_unlock() - unlock a specific hwspinlock + * @hwlock: a previously-acquired hwspinlock which we want to unlock + * @mode: controls whether local interrupts needs to be restored or not + * @flags: previous caller's interrupt state to restore (if requested) + * + * This function will unlock a specific hwspinlock, enable preemption and + * (possibly) enable interrupts or restore their previous state. + * @hwlock must be already locked before calling this function: it is a bug + * to call unlock on a @hwlock that is already unlocked. + * + * The user decides whether local interrupts should be enabled or not, and + * if yes, whether he wants their previous state to be restored. It is up + * to the user to choose the appropriate @mode of operation, exactly the + * same way users decide between spin_unlock, spin_unlock_irq and + * spin_unlock_irqrestore. + * + * The function will never sleep. + */ +void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags) +{ + BUG_ON(!hwlock); + BUG_ON(!flags && mode == HWLOCK_IRQSTATE); + + /* + * We must make sure that memory operations (both reads and writes), + * done before unlocking the hwspinlock, will not be reordered + * after the lock is released. + * + * That's the purpose of this explicit memory barrier. + * + * Note: the memory barrier induced by the spin_unlock below is too + * late; the other core is going to access memory soon after it will + * take the hwspinlock, and by then we want to be sure our memory + * operations are already observable. + */ + mb(); + + hwlock->ops->unlock(hwlock); + + /* Undo the spin_trylock{_irq, _irqsave} called while locking */ + if (mode == HWLOCK_IRQSTATE) + spin_unlock_irqrestore(&hwlock->lock, *flags); + else if (mode == HWLOCK_IRQ) + spin_unlock_irq(&hwlock->lock); + else + spin_unlock(&hwlock->lock); +} +EXPORT_SYMBOL_GPL(__hwspin_unlock); + +/** + * hwspin_lock_register() - register a new hw spinlock + * @hwlock: hwspinlock to register. + * + * This function should be called from the underlying platform-specific + * implementation, to register a new hwspinlock instance. + * + * Can be called from an atomic context (will not sleep) but not from + * within interrupt context. + * + * Returns 0 on success, or an appropriate error code on failure + */ +int hwspin_lock_register(struct hwspinlock *hwlock) +{ + struct hwspinlock *tmp; + int ret; + + if (!hwlock || !hwlock->ops || + !hwlock->ops->trylock || !hwlock->ops->unlock) { + pr_err("invalid parameters\n"); + return -EINVAL; + } + + spin_lock_init(&hwlock->lock); + + spin_lock(&hwspinlock_tree_lock); + + ret = radix_tree_insert(&hwspinlock_tree, hwlock->id, hwlock); + if (ret) + goto out; + + /* mark this hwspinlock as available */ + tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock->id, + HWSPINLOCK_UNUSED); + + /* self-sanity check which should never fail */ + WARN_ON(tmp != hwlock); + +out: + spin_unlock(&hwspinlock_tree_lock); + return ret; +} +EXPORT_SYMBOL_GPL(hwspin_lock_register); + +/** + * hwspin_lock_unregister() - unregister an hw spinlock + * @id: index of the specific hwspinlock to unregister + * + * This function should be called from the underlying platform-specific + * implementation, to unregister an existing (and unused) hwspinlock. + * + * Can be called from an atomic context (will not sleep) but not from + * within interrupt context. + * + * Returns the address of hwspinlock @id on success, or NULL on failure + */ +struct hwspinlock *hwspin_lock_unregister(unsigned int id) +{ + struct hwspinlock *hwlock = NULL; + int ret; + + spin_lock(&hwspinlock_tree_lock); + + /* make sure the hwspinlock is not in use (tag is set) */ + ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED); + if (ret == 0) { + pr_err("hwspinlock %d still in use (or not present)\n", id); + goto out; + } + + hwlock = radix_tree_delete(&hwspinlock_tree, id); + if (!hwlock) { + pr_err("failed to delete hwspinlock %d\n", id); + goto out; + } + +out: + spin_unlock(&hwspinlock_tree_lock); + return hwlock; +} +EXPORT_SYMBOL_GPL(hwspin_lock_unregister); + +/** + * __hwspin_lock_request() - tag an hwspinlock as used and power it up + * + * This is an internal function that prepares an hwspinlock instance + * before it is given to the user. The function assumes that + * hwspinlock_tree_lock is taken. + * + * Returns 0 or positive to indicate success, and a negative value to + * indicate an error (with the appropriate error code) + */ +static int __hwspin_lock_request(struct hwspinlock *hwlock) +{ + struct hwspinlock *tmp; + int ret; + + /* prevent underlying implementation from being removed */ + if (!try_module_get(hwlock->owner)) { + dev_err(hwlock->dev, "%s: can't get owner\n", __func__); + return -EINVAL; + } + + /* notify PM core that power is now needed */ + ret = pm_runtime_get_sync(hwlock->dev); + if (ret < 0) { + dev_err(hwlock->dev, "%s: can't power on device\n", __func__); + return ret; + } + + /* mark hwspinlock as used, should not fail */ + tmp = radix_tree_tag_clear(&hwspinlock_tree, hwlock->id, + HWSPINLOCK_UNUSED); + + /* self-sanity check that should never fail */ + WARN_ON(tmp != hwlock); + + return ret; +} + +/** + * hwspin_lock_get_id() - retrieve id number of a given hwspinlock + * @hwlock: a valid hwspinlock instance + * + * Returns the id number of a given @hwlock, or -EINVAL if @hwlock is invalid. + */ +int hwspin_lock_get_id(struct hwspinlock *hwlock) +{ + if (!hwlock) { + pr_err("invalid hwlock\n"); + return -EINVAL; + } + + return hwlock->id; +} +EXPORT_SYMBOL_GPL(hwspin_lock_get_id); + +/** + * hwspin_lock_request() - request an hwspinlock + * + * This function should be called by users of the hwspinlock device, + * in order to dynamically assign them an unused hwspinlock. + * Usually the user of this lock will then have to communicate the lock's id + * to the remote core before it can be used for synchronization (to get the + * id of a given hwlock, use hwspin_lock_get_id()). + * + * Can be called from an atomic context (will not sleep) but not from + * within interrupt context (simply because there is no use case for + * that yet). + * + * Returns the address of the assigned hwspinlock, or NULL on error + */ +struct hwspinlock *hwspin_lock_request(void) +{ + struct hwspinlock *hwlock; + int ret; + + spin_lock(&hwspinlock_tree_lock); + + /* look for an unused lock */ + ret = radix_tree_gang_lookup_tag(&hwspinlock_tree, (void **)&hwlock, + 0, 1, HWSPINLOCK_UNUSED); + if (ret == 0) { + pr_warn("a free hwspinlock is not available\n"); + hwlock = NULL; + goto out; + } + + /* sanity check that should never fail */ + WARN_ON(ret > 1); + + /* mark as used and power up */ + ret = __hwspin_lock_request(hwlock); + if (ret < 0) + hwlock = NULL; + +out: + spin_unlock(&hwspinlock_tree_lock); + return hwlock; +} +EXPORT_SYMBOL_GPL(hwspin_lock_request); + +/** + * hwspin_lock_request_specific() - request for a specific hwspinlock + * @id: index of the specific hwspinlock that is requested + * + * This function should be called by users of the hwspinlock module, + * in order to assign them a specific hwspinlock. + * Usually early board code will be calling this function in order to + * reserve specific hwspinlock ids for predefined purposes. + * + * Can be called from an atomic context (will not sleep) but not from + * within interrupt context (simply because there is no use case for + * that yet). + * + * Returns the address of the assigned hwspinlock, or NULL on error + */ +struct hwspinlock *hwspin_lock_request_specific(unsigned int id) +{ + struct hwspinlock *hwlock; + int ret; + + spin_lock(&hwspinlock_tree_lock); + + /* make sure this hwspinlock exists */ + hwlock = radix_tree_lookup(&hwspinlock_tree, id); + if (!hwlock) { + pr_warn("hwspinlock %u does not exist\n", id); + goto out; + } + + /* sanity check (this shouldn't happen) */ + WARN_ON(hwlock->id != id); + + /* make sure this hwspinlock is unused */ + ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED); + if (ret == 0) { + pr_warn("hwspinlock %u is already in use\n", id); + hwlock = NULL; + goto out; + } + + /* mark as used and power up */ + ret = __hwspin_lock_request(hwlock); + if (ret < 0) + hwlock = NULL; + +out: + spin_unlock(&hwspinlock_tree_lock); + return hwlock; +} +EXPORT_SYMBOL_GPL(hwspin_lock_request_specific); + +/** + * hwspin_lock_free() - free a specific hwspinlock + * @hwlock: the specific hwspinlock to free + * + * This function mark @hwlock as free again. + * Should only be called with an @hwlock that was retrieved from + * an earlier call to omap_hwspin_lock_request{_specific}. + * + * Can be called from an atomic context (will not sleep) but not from + * within interrupt context (simply because there is no use case for + * that yet). + * + * Returns 0 on success, or an appropriate error code on failure + */ +int hwspin_lock_free(struct hwspinlock *hwlock) +{ + struct hwspinlock *tmp; + int ret; + + if (!hwlock) { + pr_err("invalid hwlock\n"); + return -EINVAL; + } + + spin_lock(&hwspinlock_tree_lock); + + /* make sure the hwspinlock is used */ + ret = radix_tree_tag_get(&hwspinlock_tree, hwlock->id, + HWSPINLOCK_UNUSED); + if (ret == 1) { + dev_err(hwlock->dev, "%s: hwlock is already free\n", __func__); + dump_stack(); + ret = -EINVAL; + goto out; + } + + /* notify the underlying device that power is not needed */ + ret = pm_runtime_put(hwlock->dev); + if (ret < 0) + goto out; + + /* mark this hwspinlock as available */ + tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock->id, + HWSPINLOCK_UNUSED); + + /* sanity check (this shouldn't happen) */ + WARN_ON(tmp != hwlock); + + module_put(hwlock->owner); + +out: + spin_unlock(&hwspinlock_tree_lock); + return ret; +} +EXPORT_SYMBOL_GPL(hwspin_lock_free); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Hardware spinlock interface"); +MODULE_AUTHOR("Ohad Ben-Cohen "); diff --git a/drivers/hwspinlock/hwspinlock_internal.h b/drivers/hwspinlock/hwspinlock_internal.h new file mode 100644 index 000000000000..69935e6b93e5 --- /dev/null +++ b/drivers/hwspinlock/hwspinlock_internal.h @@ -0,0 +1,61 @@ +/* + * Hardware spinlocks internal header + * + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com + * + * Contact: Ohad Ben-Cohen + * + * 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. + */ + +#ifndef __HWSPINLOCK_HWSPINLOCK_H +#define __HWSPINLOCK_HWSPINLOCK_H + +#include +#include + +/** + * struct hwspinlock_ops - platform-specific hwspinlock handlers + * + * @trylock: make a single attempt to take the lock. returns 0 on + * failure and true on success. may _not_ sleep. + * @unlock: release the lock. always succeed. may _not_ sleep. + * @relax: optional, platform-specific relax handler, called by hwspinlock + * core while spinning on a lock, between two successive + * invocations of @trylock. may _not_ sleep. + */ +struct hwspinlock_ops { + int (*trylock)(struct hwspinlock *lock); + void (*unlock)(struct hwspinlock *lock); + void (*relax)(struct hwspinlock *lock); +}; + +/** + * struct hwspinlock - this struct represents a single hwspinlock instance + * + * @dev: underlying device, will be used to invoke runtime PM api + * @ops: platform-specific hwspinlock handlers + * @id: a global, unique, system-wide, index of the lock. + * @lock: initialized and used by hwspinlock core + * @owner: underlying implementation module, used to maintain module ref count + * + * Note: currently simplicity was opted for, but later we can squeeze some + * memory bytes by grouping the dev, ops and owner members in a single + * per-platform struct, and have all hwspinlocks point at it. + */ +struct hwspinlock { + struct device *dev; + const struct hwspinlock_ops *ops; + int id; + spinlock_t lock; + struct module *owner; +}; + +#endif /* __HWSPINLOCK_HWSPINLOCK_H */ diff --git a/include/linux/hwspinlock.h b/include/linux/hwspinlock.h new file mode 100644 index 000000000000..8390efc457eb --- /dev/null +++ b/include/linux/hwspinlock.h @@ -0,0 +1,292 @@ +/* + * Hardware spinlock public header + * + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com + * + * Contact: Ohad Ben-Cohen + * + * 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. + */ + +#ifndef __LINUX_HWSPINLOCK_H +#define __LINUX_HWSPINLOCK_H + +#include +#include + +/* hwspinlock mode argument */ +#define HWLOCK_IRQSTATE 0x01 /* Disable interrupts, save state */ +#define HWLOCK_IRQ 0x02 /* Disable interrupts, don't save state */ + +struct hwspinlock; + +#if defined(CONFIG_HWSPINLOCK) || defined(CONFIG_HWSPINLOCK_MODULE) + +int hwspin_lock_register(struct hwspinlock *lock); +struct hwspinlock *hwspin_lock_unregister(unsigned int id); +struct hwspinlock *hwspin_lock_request(void); +struct hwspinlock *hwspin_lock_request_specific(unsigned int id); +int hwspin_lock_free(struct hwspinlock *hwlock); +int hwspin_lock_get_id(struct hwspinlock *hwlock); +int __hwspin_lock_timeout(struct hwspinlock *, unsigned int, int, + unsigned long *); +int __hwspin_trylock(struct hwspinlock *, int, unsigned long *); +void __hwspin_unlock(struct hwspinlock *, int, unsigned long *); + +#else /* !CONFIG_HWSPINLOCK */ + +/* + * We don't want these functions to fail if CONFIG_HWSPINLOCK is not + * enabled. We prefer to silently succeed in this case, and let the + * code path get compiled away. This way, if CONFIG_HWSPINLOCK is not + * required on a given setup, users will still work. + * + * The only exception is hwspin_lock_register/hwspin_lock_unregister, with which + * we _do_ want users to fail (no point in registering hwspinlock instances if + * the framework is not available). + * + * Note: ERR_PTR(-ENODEV) will still be considered a success for NULL-checking + * users. Others, which care, can still check this with IS_ERR. + */ +static inline struct hwspinlock *hwspin_lock_request(void) +{ + return ERR_PTR(-ENODEV); +} + +static inline struct hwspinlock *hwspin_lock_request_specific(unsigned int id) +{ + return ERR_PTR(-ENODEV); +} + +static inline int hwspin_lock_free(struct hwspinlock *hwlock) +{ + return 0; +} + +static inline +int __hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int to, + int mode, unsigned long *flags) +{ + return 0; +} + +static inline +int __hwspin_trylock(struct hwspinlock *hwlock, int mode, unsigned long *flags) +{ + return 0; +} + +static inline +void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags) +{ + return 0; +} + +static inline int hwspin_lock_get_id(struct hwspinlock *hwlock) +{ + return 0; +} + +static inline int hwspin_lock_register(struct hwspinlock *hwlock) +{ + return -ENODEV; +} + +static inline struct hwspinlock *hwspin_lock_unregister(unsigned int id) +{ + return NULL; +} + +#endif /* !CONFIG_HWSPINLOCK */ + +/** + * hwspin_trylock_irqsave() - try to lock an hwspinlock, disable interrupts + * @hwlock: an hwspinlock which we want to trylock + * @flags: a pointer to where the caller's interrupt state will be saved at + * + * This function attempts to lock the underlying hwspinlock, and will + * immediately fail if the hwspinlock is already locked. + * + * Upon a successful return from this function, preemption and local + * interrupts are disabled (previous interrupts state is saved at @flags), + * so the caller must not sleep, and is advised to release the hwspinlock + * as soon as possible. + * + * Returns 0 if we successfully locked the hwspinlock, -EBUSY if + * the hwspinlock was already taken, and -EINVAL if @hwlock is invalid. + */ +static inline +int hwspin_trylock_irqsave(struct hwspinlock *hwlock, unsigned long *flags) +{ + return __hwspin_trylock(hwlock, HWLOCK_IRQSTATE, flags); +} + +/** + * hwspin_trylock_irq() - try to lock an hwspinlock, disable interrupts + * @hwlock: an hwspinlock which we want to trylock + * + * This function attempts to lock the underlying hwspinlock, and will + * immediately fail if the hwspinlock is already locked. + * + * Upon a successful return from this function, preemption and local + * interrupts are disabled, so the caller must not sleep, and is advised + * to release the hwspinlock as soon as possible. + * + * Returns 0 if we successfully locked the hwspinlock, -EBUSY if + * the hwspinlock was already taken, and -EINVAL if @hwlock is invalid. + */ +static inline int hwspin_trylock_irq(struct hwspinlock *hwlock) +{ + return __hwspin_trylock(hwlock, HWLOCK_IRQ, NULL); +} + +/** + * hwspin_trylock() - attempt to lock a specific hwspinlock + * @hwlock: an hwspinlock which we want to trylock + * + * This function attempts to lock an hwspinlock, and will immediately fail + * if the hwspinlock is already taken. + * + * Upon a successful return from this function, preemption is disabled, + * so the caller must not sleep, and is advised to release the hwspinlock + * as soon as possible. This is required in order to minimize remote cores + * polling on the hardware interconnect. + * + * Returns 0 if we successfully locked the hwspinlock, -EBUSY if + * the hwspinlock was already taken, and -EINVAL if @hwlock is invalid. + */ +static inline int hwspin_trylock(struct hwspinlock *hwlock) +{ + return __hwspin_trylock(hwlock, 0, NULL); +} + +/** + * hwspin_lock_timeout_irqsave() - lock hwspinlock, with timeout, disable irqs + * @hwlock: the hwspinlock to be locked + * @to: timeout value in msecs + * @flags: a pointer to where the caller's interrupt state will be saved at + * + * This function locks the underlying @hwlock. If the @hwlock + * is already taken, the function will busy loop waiting for it to + * be released, but give up when @timeout msecs have elapsed. + * + * Upon a successful return from this function, preemption and local interrupts + * are disabled (plus previous interrupt state is saved), so the caller must + * not sleep, and is advised to release the hwspinlock as soon as possible. + * + * Returns 0 when the @hwlock was successfully taken, and an appropriate + * error code otherwise (most notably an -ETIMEDOUT if the @hwlock is still + * busy after @timeout msecs). The function will never sleep. + */ +static inline int hwspin_lock_timeout_irqsave(struct hwspinlock *hwlock, + unsigned int to, unsigned long *flags) +{ + return __hwspin_lock_timeout(hwlock, to, HWLOCK_IRQSTATE, flags); +} + +/** + * hwspin_lock_timeout_irq() - lock hwspinlock, with timeout, disable irqs + * @hwlock: the hwspinlock to be locked + * @to: timeout value in msecs + * + * This function locks the underlying @hwlock. If the @hwlock + * is already taken, the function will busy loop waiting for it to + * be released, but give up when @timeout msecs have elapsed. + * + * Upon a successful return from this function, preemption and local interrupts + * are disabled so the caller must not sleep, and is advised to release the + * hwspinlock as soon as possible. + * + * Returns 0 when the @hwlock was successfully taken, and an appropriate + * error code otherwise (most notably an -ETIMEDOUT if the @hwlock is still + * busy after @timeout msecs). The function will never sleep. + */ +static inline +int hwspin_lock_timeout_irq(struct hwspinlock *hwlock, unsigned int to) +{ + return __hwspin_lock_timeout(hwlock, to, HWLOCK_IRQ, NULL); +} + +/** + * hwspin_lock_timeout() - lock an hwspinlock with timeout limit + * @hwlock: the hwspinlock to be locked + * @to: timeout value in msecs + * + * This function locks the underlying @hwlock. If the @hwlock + * is already taken, the function will busy loop waiting for it to + * be released, but give up when @timeout msecs have elapsed. + * + * Upon a successful return from this function, preemption is disabled + * so the caller must not sleep, and is advised to release the hwspinlock + * as soon as possible. + * This is required in order to minimize remote cores polling on the + * hardware interconnect. + * + * Returns 0 when the @hwlock was successfully taken, and an appropriate + * error code otherwise (most notably an -ETIMEDOUT if the @hwlock is still + * busy after @timeout msecs). The function will never sleep. + */ +static inline +int hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int to) +{ + return __hwspin_lock_timeout(hwlock, to, 0, NULL); +} + +/** + * hwspin_unlock_irqrestore() - unlock hwspinlock, restore irq state + * @hwlock: a previously-acquired hwspinlock which we want to unlock + * @flags: previous caller's interrupt state to restore + * + * This function will unlock a specific hwspinlock, enable preemption and + * restore the previous state of the local interrupts. It should be used + * to undo, e.g., hwspin_trylock_irqsave(). + * + * @hwlock must be already locked before calling this function: it is a bug + * to call unlock on a @hwlock that is already unlocked. + */ +static inline void hwspin_unlock_irqrestore(struct hwspinlock *hwlock, + unsigned long *flags) +{ + __hwspin_unlock(hwlock, HWLOCK_IRQSTATE, flags); +} + +/** + * hwspin_unlock_irq() - unlock hwspinlock, enable interrupts + * @hwlock: a previously-acquired hwspinlock which we want to unlock + * + * This function will unlock a specific hwspinlock, enable preemption and + * enable local interrupts. Should be used to undo hwspin_lock_irq(). + * + * @hwlock must be already locked (e.g. by hwspin_trylock_irq()) before + * calling this function: it is a bug to call unlock on a @hwlock that is + * already unlocked. + */ +static inline void hwspin_unlock_irq(struct hwspinlock *hwlock) +{ + __hwspin_unlock(hwlock, HWLOCK_IRQ, NULL); +} + +/** + * hwspin_unlock() - unlock hwspinlock + * @hwlock: a previously-acquired hwspinlock which we want to unlock + * + * This function will unlock a specific hwspinlock and enable preemption + * back. + * + * @hwlock must be already locked (e.g. by hwspin_trylock()) before calling + * this function: it is a bug to call unlock on a @hwlock that is already + * unlocked. + */ +static inline void hwspin_unlock(struct hwspinlock *hwlock) +{ + __hwspin_unlock(hwlock, 0, NULL); +} + +#endif /* __LINUX_HWSPINLOCK_H */ -- cgit v1.2.3 From b9f2e9a122a4d51dc13e2e9571034cb2d29dfe44 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Mon, 14 Feb 2011 23:06:15 +0100 Subject: USB: usb.h: Make comment match the defines it describes Signed-off-by: Paul Bolle Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/ch11.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/usb/ch11.h b/include/linux/usb/ch11.h index 10ec0699bea4..38c42b013641 100644 --- a/include/linux/usb/ch11.h +++ b/include/linux/usb/ch11.h @@ -132,8 +132,8 @@ struct usb_port_status { /* * wPortChange bit field - * See USB 2.0 spec Table 11-22 - * Bits 0 to 4 shown, bits 5 to 15 are reserved + * See USB 2.0 spec Table 11-22 and USB 2.0 LPM ECN Table-4.10 + * Bits 0 to 5 shown, bits 6 to 15 are reserved */ #define USB_PORT_STAT_C_CONNECTION 0x0001 #define USB_PORT_STAT_C_ENABLE 0x0002 -- cgit v1.2.3 From d8692748408fbec28dfb065f4127307e24187476 Mon Sep 17 00:00:00 2001 From: Hema HK Date: Thu, 17 Feb 2011 12:06:06 +0530 Subject: usb: otg: OMAP4430: Add phy_suspend function pointer to twl4030_usb_data Declare the .phy_suspend function pointer to twl4030_usb_data structure. OMAP internal phy suspend function will be hooked though this function pointer to use in the transceiver driver. Signed-off-by: Hema HK Cc: Tony Lindgren Cc: Paul Walmsley Signed-off-by: Felipe Balbi --- include/linux/i2c/twl.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 61b9609e55f2..a4bd05b7bd22 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -600,6 +600,8 @@ struct twl4030_usb_data { int (*phy_power)(struct device *dev, int iD, int on); /* enable/disable phy clocks */ int (*phy_set_clock)(struct device *dev, int on); + /* suspend/resume of phy */ + int (*phy_suspend)(struct device *dev, int suspend); }; struct twl4030_ins { -- cgit v1.2.3 From 9fc3de9c83565fcaa23df74c2fc414bb6e7efb0a Mon Sep 17 00:00:00 2001 From: Arthur Taylor Date: Fri, 4 Feb 2011 13:55:50 -0800 Subject: vt: Add virtual console keyboard mode OFF virtual console: add keyboard mode OFF Add a new mode for the virtual console keyboard OFF in which all input other than shift keys is ignored. Prevents vt input buffers from overflowing when a program opens but doesn't read from a tty, like X11 using evdev for input. Signed-off-by: Arthur Taylor Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 5 +++-- drivers/tty/vt/vt_ioctl.c | 3 +++ include/linux/kbd_kern.h | 3 ++- include/linux/kd.h | 1 + 4 files changed, 9 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index e95d7876ca6b..6dd3c68c13ad 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -654,7 +654,8 @@ static void k_spec(struct vc_data *vc, unsigned char value, char up_flag) if (value >= ARRAY_SIZE(fn_handler)) return; if ((kbd->kbdmode == VC_RAW || - kbd->kbdmode == VC_MEDIUMRAW) && + kbd->kbdmode == VC_MEDIUMRAW || + kbd->kbdmode == VC_OFF) && value != KVAL(K_SAK)) return; /* SAK is allowed even in raw mode */ fn_handler[value](vc); @@ -1295,7 +1296,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) if (rc == NOTIFY_STOP) return; - if (raw_mode && type != KT_SPEC && type != KT_SHIFT) + if ((raw_mode || kbd->kbdmode == VC_OFF) && type != KT_SPEC && type != KT_SHIFT) return; (*k_handler[type])(vc, keysym & 0xff, !down); diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 1235ebda6e1c..6bcf05bf4978 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -688,6 +688,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, kbd->kbdmode = VC_UNICODE; compute_shiftstate(); break; + case K_OFF: + kbd->kbdmode = VC_OFF; + break; default: ret = -EINVAL; goto out; diff --git a/include/linux/kbd_kern.h b/include/linux/kbd_kern.h index 506ad20c18f8..4b0761cc7dd9 100644 --- a/include/linux/kbd_kern.h +++ b/include/linux/kbd_kern.h @@ -50,11 +50,12 @@ struct kbd_struct { #define VC_CAPSLOCK 2 /* capslock mode */ #define VC_KANALOCK 3 /* kanalock mode */ - unsigned char kbdmode:2; /* one 2-bit value */ + unsigned char kbdmode:3; /* one 3-bit value */ #define VC_XLATE 0 /* translate keycodes using keymap */ #define VC_MEDIUMRAW 1 /* medium raw (keycode) mode */ #define VC_RAW 2 /* raw (scancode) mode */ #define VC_UNICODE 3 /* Unicode mode */ +#define VC_OFF 4 /* disabled mode */ unsigned char modeflags:5; #define VC_APPLIC 0 /* application key mode */ diff --git a/include/linux/kd.h b/include/linux/kd.h index 15f2853ea58f..c36d8476db55 100644 --- a/include/linux/kd.h +++ b/include/linux/kd.h @@ -81,6 +81,7 @@ struct unimapinit { #define K_XLATE 0x01 #define K_MEDIUMRAW 0x02 #define K_UNICODE 0x03 +#define K_OFF 0x04 #define KDGKBMODE 0x4B44 /* gets current keyboard mode */ #define KDSKBMODE 0x4B45 /* sets current keyboard mode */ -- cgit v1.2.3 From fcdba07ee390d9d9c15de8b2a17baef689284fcc Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 7 Feb 2011 19:31:25 +0100 Subject: tty,vcs removing con_buf/conf_buf_mtx seems there's no longer need for using con_buf/conf_buf_mtx as vcs_read/vcs_write buffer for user's data. The do_con_write function, that was the other user of this, is currently using its own kmalloc-ed buffer. Not sure when this got changed, as I was able to find this code in 2.6.9, but it's already gone as far as current git history goes - 2.6.12-rc2. AFAICS there's a behaviour change with the current change. The lseek is not completely mutually exclusive with the vcs_read/vcs_write - the file->f_pos might get updated via lseek callback during the vcs_read/vcs_write processing. I tried to find out if the prefered behaviour is to keep this in sync within read/write/lseek functions, but I did not find any pattern on different places. I guess if user end up calling write/lseek from different threads she should know what she's doing. If needed we could use dedicated fd mutex/buffer. Signed-off-by: Jiri Olsa Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vc_screen.c | 98 ++++++++++++++++++++++++---------------------- drivers/tty/vt/vt.c | 12 ------ include/linux/vt_kern.h | 8 ---- 3 files changed, 52 insertions(+), 66 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index 3c27c4bc6040..7b3bfbe2e6de 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -51,6 +50,8 @@ #undef addr #define HEADER_SIZE 4 +#define CON_BUF_SIZE (CONFIG_BASE_SMALL ? 256 : PAGE_SIZE) + struct vcs_poll_data { struct notifier_block notifier; unsigned int cons_num; @@ -131,21 +132,45 @@ vcs_poll_data_get(struct file *file) return poll; } +/* + * Returns VC for inode. + * Must be called with console_lock. + */ +static struct vc_data* +vcs_vc(struct inode *inode, int *viewed) +{ + unsigned int currcons = iminor(inode) & 127; + + WARN_CONSOLE_UNLOCKED(); + + if (currcons == 0) { + currcons = fg_console; + if (viewed) + *viewed = 1; + } else { + currcons--; + if (viewed) + *viewed = 0; + } + return vc_cons[currcons].d; +} + +/* + * Returns size for VC carried by inode. + * Must be called with console_lock. + */ static int vcs_size(struct inode *inode) { int size; int minor = iminor(inode); - int currcons = minor & 127; struct vc_data *vc; - if (currcons == 0) - currcons = fg_console; - else - currcons--; - if (!vc_cons_allocated(currcons)) + WARN_CONSOLE_UNLOCKED(); + + vc = vcs_vc(inode, NULL); + if (!vc) return -ENXIO; - vc = vc_cons[currcons].d; size = vc->vc_rows * vc->vc_cols; @@ -158,17 +183,13 @@ static loff_t vcs_lseek(struct file *file, loff_t offset, int orig) { int size; - mutex_lock(&con_buf_mtx); console_lock(); size = vcs_size(file->f_path.dentry->d_inode); console_unlock(); - if (size < 0) { - mutex_unlock(&con_buf_mtx); + if (size < 0) return size; - } switch (orig) { default: - mutex_unlock(&con_buf_mtx); return -EINVAL; case 2: offset += size; @@ -179,11 +200,9 @@ static loff_t vcs_lseek(struct file *file, loff_t offset, int orig) break; } if (offset < 0 || offset > size) { - mutex_unlock(&con_buf_mtx); return -EINVAL; } file->f_pos = offset; - mutex_unlock(&con_buf_mtx); return file->f_pos; } @@ -196,12 +215,15 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) struct vc_data *vc; struct vcs_poll_data *poll; long pos; - long viewed, attr, read; - int col, maxcol; + long attr, read; + int col, maxcol, viewed; unsigned short *org = NULL; ssize_t ret; + char *con_buf; - mutex_lock(&con_buf_mtx); + con_buf = (char *) __get_free_page(GFP_KERNEL); + if (!con_buf) + return -ENOMEM; pos = *ppos; @@ -211,18 +233,10 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) console_lock(); attr = (currcons & 128); - currcons = (currcons & 127); - if (currcons == 0) { - currcons = fg_console; - viewed = 1; - } else { - currcons--; - viewed = 0; - } ret = -ENXIO; - if (!vc_cons_allocated(currcons)) + vc = vcs_vc(inode, &viewed); + if (!vc) goto unlock_out; - vc = vc_cons[currcons].d; ret = -EINVAL; if (pos < 0) @@ -367,7 +381,7 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) ret = read; unlock_out: console_unlock(); - mutex_unlock(&con_buf_mtx); + free_page((unsigned long) con_buf); return ret; } @@ -378,13 +392,16 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) unsigned int currcons = iminor(inode); struct vc_data *vc; long pos; - long viewed, attr, size, written; + long attr, size, written; char *con_buf0; - int col, maxcol; + int col, maxcol, viewed; u16 *org0 = NULL, *org = NULL; size_t ret; + char *con_buf; - mutex_lock(&con_buf_mtx); + con_buf = (char *) __get_free_page(GFP_KERNEL); + if (!con_buf) + return -ENOMEM; pos = *ppos; @@ -394,19 +411,10 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) console_lock(); attr = (currcons & 128); - currcons = (currcons & 127); - - if (currcons == 0) { - currcons = fg_console; - viewed = 1; - } else { - currcons--; - viewed = 0; - } ret = -ENXIO; - if (!vc_cons_allocated(currcons)) + vc = vcs_vc(inode, &viewed); + if (!vc) goto unlock_out; - vc = vc_cons[currcons].d; size = vcs_size(inode); ret = -EINVAL; @@ -561,9 +569,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) unlock_out: console_unlock(); - - mutex_unlock(&con_buf_mtx); - + free_page((unsigned long) con_buf); return ret; } diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index d5669ff72df4..798df6f89110 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -2068,18 +2068,6 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) } } -/* This is a temporary buffer used to prepare a tty console write - * so that we can easily avoid touching user space while holding the - * console spinlock. It is allocated in con_init and is shared by - * this code and the vc_screen read/write tty calls. - * - * We have to allocate this statically in the kernel data section - * since console_init (and thus con_init) are called before any - * kernel memory allocation is available. - */ -char con_buf[CON_BUF_SIZE]; -DEFINE_MUTEX(con_buf_mtx); - /* is_double_width() is based on the wcwidth() implementation by * Markus Kuhn -- 2007-05-26 (Unicode 5.0) * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 6625cc1ab758..4d05e14ea60c 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -142,14 +142,6 @@ static inline bool vt_force_oops_output(struct vc_data *vc) return false; } -/* - * vc_screen.c shares this temporary buffer with the console write code so that - * we can easily avoid touching user space while holding the console spinlock. - */ - -#define CON_BUF_SIZE (CONFIG_BASE_SMALL ? 256 : PAGE_SIZE) -extern char con_buf[CON_BUF_SIZE]; -extern struct mutex con_buf_mtx; extern char vt_dont_switch; extern int default_utf8; extern int global_cursor_default; -- cgit v1.2.3 From 60b33c133ca0b7c0b6072c87234b63fee6e80558 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 14 Feb 2011 16:26:14 +0000 Subject: tiocmget: kill off the passing of the struct file We don't actually need this and it causes problems for internal use of this functionality. Currently there is a single use of the FILE * pointer. That is the serial core which uses it to check tty_hung_up_p. However if that is true then IO_ERROR is also already set so the check may be removed. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/amiserial.c | 2 +- drivers/char/cyclades.c | 2 +- drivers/char/epca.c | 4 ++-- drivers/char/ip2/ip2main.c | 4 ++-- drivers/char/isicom.c | 2 +- drivers/char/istallion.c | 2 +- drivers/char/moxa.c | 4 ++-- drivers/char/mxser.c | 2 +- drivers/char/nozomi.c | 2 +- drivers/char/pcmcia/ipwireless/tty.c | 2 +- drivers/char/pcmcia/synclink_cs.c | 4 ++-- drivers/char/riscom8.c | 2 +- drivers/char/rocket.c | 2 +- drivers/char/serial167.c | 2 +- drivers/char/specialix.c | 2 +- drivers/char/stallion.c | 2 +- drivers/char/sx.c | 2 +- drivers/char/synclink.c | 4 ++-- drivers/char/synclink_gt.c | 4 ++-- drivers/char/synclinkmp.c | 4 ++-- drivers/isdn/gigaset/interface.c | 4 ++-- drivers/isdn/i4l/isdn_tty.c | 2 +- drivers/mmc/card/sdio_uart.c | 2 +- drivers/net/usb/hso.c | 2 +- drivers/net/wan/pc300_tty.c | 4 ++-- drivers/staging/quatech_usb2/quatech_usb2.c | 2 +- drivers/staging/serqt_usb2/serqt_usb2.c | 6 +++--- drivers/tty/hvc/hvsi.c | 2 +- drivers/tty/n_gsm.c | 2 +- drivers/tty/serial/68360serial.c | 2 +- drivers/tty/serial/crisv10.c | 2 +- drivers/tty/serial/ifx6x60.c | 2 +- drivers/tty/serial/serial_core.c | 6 ++---- drivers/tty/tty_io.c | 6 +++--- drivers/usb/class/cdc-acm.c | 2 +- drivers/usb/serial/ark3116.c | 2 +- drivers/usb/serial/belkin_sa.c | 4 ++-- drivers/usb/serial/ch341.c | 2 +- drivers/usb/serial/cp210x.c | 4 ++-- drivers/usb/serial/cypress_m8.c | 4 ++-- drivers/usb/serial/digi_acceleport.c | 4 ++-- drivers/usb/serial/ftdi_sio.c | 4 ++-- drivers/usb/serial/io_edgeport.c | 4 ++-- drivers/usb/serial/io_ti.c | 2 +- drivers/usb/serial/iuu_phoenix.c | 2 +- drivers/usb/serial/keyspan.c | 2 +- drivers/usb/serial/keyspan.h | 3 +-- drivers/usb/serial/keyspan_pda.c | 2 +- drivers/usb/serial/kl5kusb105.c | 4 ++-- drivers/usb/serial/kobil_sct.c | 4 ++-- drivers/usb/serial/mct_u232.c | 4 ++-- drivers/usb/serial/mos7720.c | 4 ++-- drivers/usb/serial/mos7840.c | 2 +- drivers/usb/serial/opticon.c | 2 +- drivers/usb/serial/oti6858.c | 4 ++-- drivers/usb/serial/pl2303.c | 2 +- drivers/usb/serial/sierra.c | 2 +- drivers/usb/serial/spcp8x5.c | 2 +- drivers/usb/serial/ssu100.c | 2 +- drivers/usb/serial/ti_usb_3410_5052.c | 4 ++-- drivers/usb/serial/usb-serial.c | 4 ++-- drivers/usb/serial/usb-wwan.h | 2 +- drivers/usb/serial/usb_wwan.c | 2 +- drivers/usb/serial/whiteheat.c | 4 ++-- include/linux/tty_driver.h | 2 +- include/linux/usb/serial.h | 2 +- include/net/irda/ircomm_tty.h | 2 +- net/bluetooth/rfcomm/tty.c | 2 +- net/irda/ircomm/ircomm_tty_ioctl.c | 4 ++-- 69 files changed, 98 insertions(+), 101 deletions(-) (limited to 'include') diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index 6ee3348bc3e4..bc67e6839059 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -1194,7 +1194,7 @@ static int get_lsr_info(struct async_struct * info, unsigned int __user *value) } -static int rs_tiocmget(struct tty_struct *tty, struct file *file) +static int rs_tiocmget(struct tty_struct *tty) { struct async_struct * info = tty->driver_data; unsigned char control, status; diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 4f152c28f40e..e7945ddacd18 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -2429,7 +2429,7 @@ static int get_lsr_info(struct cyclades_port *info, unsigned int __user *value) return put_user(result, (unsigned long __user *)value); } -static int cy_tiocmget(struct tty_struct *tty, struct file *file) +static int cy_tiocmget(struct tty_struct *tty) { struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; diff --git a/drivers/char/epca.c b/drivers/char/epca.c index d9df46aa0fba..ecf6f0a889fc 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -1982,7 +1982,7 @@ static int info_ioctl(struct tty_struct *tty, struct file *file, return 0; } -static int pc_tiocmget(struct tty_struct *tty, struct file *file) +static int pc_tiocmget(struct tty_struct *tty) { struct channel *ch = tty->driver_data; struct board_chan __iomem *bc; @@ -2074,7 +2074,7 @@ static int pc_ioctl(struct tty_struct *tty, struct file *file, return -EINVAL; switch (cmd) { case TIOCMODG: - mflag = pc_tiocmget(tty, file); + mflag = pc_tiocmget(tty); if (put_user(mflag, (unsigned long __user *)argp)) return -EFAULT; break; diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index c3a025356b8b..476cd087118e 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -181,7 +181,7 @@ static void ip2_unthrottle(PTTY); static void ip2_stop(PTTY); static void ip2_start(PTTY); static void ip2_hangup(PTTY); -static int ip2_tiocmget(struct tty_struct *tty, struct file *file); +static int ip2_tiocmget(struct tty_struct *tty); static int ip2_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int ip2_get_icount(struct tty_struct *tty, @@ -2038,7 +2038,7 @@ ip2_stop ( PTTY tty ) /* Device Ioctl Section */ /******************************************************************************/ -static int ip2_tiocmget(struct tty_struct *tty, struct file *file) +static int ip2_tiocmget(struct tty_struct *tty) { i2ChanStrPtr pCh = DevTable[tty->index]; #ifdef ENABLE_DSSNOW diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index c27e9d21fea9..836370bc04c2 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -1065,7 +1065,7 @@ static int isicom_send_break(struct tty_struct *tty, int length) return 0; } -static int isicom_tiocmget(struct tty_struct *tty, struct file *file) +static int isicom_tiocmget(struct tty_struct *tty) { struct isi_port *port = tty->driver_data; /* just send the port status */ diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 7c6de4c92458..7843a847b76a 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -1501,7 +1501,7 @@ static int stli_setserial(struct tty_struct *tty, struct serial_struct __user *s /*****************************************************************************/ -static int stli_tiocmget(struct tty_struct *tty, struct file *file) +static int stli_tiocmget(struct tty_struct *tty) { struct stliport *portp = tty->driver_data; struct stlibrd *brdp; diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index 107b0bd58d19..fdf069bb702f 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -199,7 +199,7 @@ static void moxa_set_termios(struct tty_struct *, struct ktermios *); static void moxa_stop(struct tty_struct *); static void moxa_start(struct tty_struct *); static void moxa_hangup(struct tty_struct *); -static int moxa_tiocmget(struct tty_struct *tty, struct file *file); +static int moxa_tiocmget(struct tty_struct *tty); static int moxa_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static void moxa_poll(unsigned long); @@ -1257,7 +1257,7 @@ static int moxa_chars_in_buffer(struct tty_struct *tty) return chars; } -static int moxa_tiocmget(struct tty_struct *tty, struct file *file) +static int moxa_tiocmget(struct tty_struct *tty) { struct moxa_port *ch = tty->driver_data; int flag = 0, dtr, rts; diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index dd9d75351cd6..4d2f03ec06cd 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -1320,7 +1320,7 @@ static int mxser_get_lsr_info(struct mxser_port *info, return put_user(result, value); } -static int mxser_tiocmget(struct tty_struct *tty, struct file *file) +static int mxser_tiocmget(struct tty_struct *tty) { struct mxser_port *info = tty->driver_data; unsigned char control, status; diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index 294d03e8c61a..0e1dff2ffb1e 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -1750,7 +1750,7 @@ static int ntty_write_room(struct tty_struct *tty) } /* Gets io control parameters */ -static int ntty_tiocmget(struct tty_struct *tty, struct file *file) +static int ntty_tiocmget(struct tty_struct *tty) { const struct port *port = tty->driver_data; const struct ctrl_dl *ctrl_dl = &port->ctrl_dl; diff --git a/drivers/char/pcmcia/ipwireless/tty.c b/drivers/char/pcmcia/ipwireless/tty.c index f5eb28b6cb0f..7d2ef4909a73 100644 --- a/drivers/char/pcmcia/ipwireless/tty.c +++ b/drivers/char/pcmcia/ipwireless/tty.c @@ -395,7 +395,7 @@ static int set_control_lines(struct ipw_tty *tty, unsigned int set, return 0; } -static int ipw_tiocmget(struct tty_struct *linux_tty, struct file *file) +static int ipw_tiocmget(struct tty_struct *linux_tty) { struct ipw_tty *tty = linux_tty->driver_data; /* FIXME: Exactly how is the tty object locked here .. */ diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index eaa41992fbe2..7b68ba6609fe 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -418,7 +418,7 @@ static void bh_status(MGSLPC_INFO *info); /* * ioctl handlers */ -static int tiocmget(struct tty_struct *tty, struct file *file); +static int tiocmget(struct tty_struct *tty); static int tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int get_stats(MGSLPC_INFO *info, struct mgsl_icount __user *user_icount); @@ -2114,7 +2114,7 @@ static int modem_input_wait(MGSLPC_INFO *info,int arg) /* return the state of the serial control and status signals */ -static int tiocmget(struct tty_struct *tty, struct file *file) +static int tiocmget(struct tty_struct *tty) { MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; unsigned int result; diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index af4de1fe8445..5d0c98456c93 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -1086,7 +1086,7 @@ static int rc_chars_in_buffer(struct tty_struct *tty) return port->xmit_cnt; } -static int rc_tiocmget(struct tty_struct *tty, struct file *file) +static int rc_tiocmget(struct tty_struct *tty) { struct riscom_port *port = tty->driver_data; struct riscom_board *bp; diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index 3e4e73a0d7c1..75e98efbc8e9 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -1169,7 +1169,7 @@ static int sGetChanRI(CHANNEL_T * ChP) * Returns the state of the serial modem control lines. These next 2 functions * are the way kernel versions > 2.5 handle modem control lines rather than IOCTLs. */ -static int rp_tiocmget(struct tty_struct *tty, struct file *file) +static int rp_tiocmget(struct tty_struct *tty) { struct r_port *info = tty->driver_data; unsigned int control, result, ChanStatus; diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index 748c3b0ecd89..fda90643ead7 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -1308,7 +1308,7 @@ check_and_exit: return startup(info); } /* set_serial_info */ -static int cy_tiocmget(struct tty_struct *tty, struct file *file) +static int cy_tiocmget(struct tty_struct *tty) { struct cyclades_port *info = tty->driver_data; int channel; diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index c2bca3f25ef3..bfecfbef0895 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -1737,7 +1737,7 @@ static int sx_chars_in_buffer(struct tty_struct *tty) return port->xmit_cnt; } -static int sx_tiocmget(struct tty_struct *tty, struct file *file) +static int sx_tiocmget(struct tty_struct *tty) { struct specialix_port *port = tty->driver_data; struct specialix_board *bp; diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 461a5a045517..8c2bf3fb5b89 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -1094,7 +1094,7 @@ static int stl_setserial(struct tty_struct *tty, struct serial_struct __user *sp /*****************************************************************************/ -static int stl_tiocmget(struct tty_struct *tty, struct file *file) +static int stl_tiocmget(struct tty_struct *tty) { struct stlport *portp; diff --git a/drivers/char/sx.c b/drivers/char/sx.c index a786326cea2f..f46214e60d0c 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -1873,7 +1873,7 @@ static int sx_break(struct tty_struct *tty, int flag) return 0; } -static int sx_tiocmget(struct tty_struct *tty, struct file *file) +static int sx_tiocmget(struct tty_struct *tty) { struct sx_port *port = tty->driver_data; return sx_getsignals(port); diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 3a6824f12be2..d359e092904a 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -823,7 +823,7 @@ static isr_dispatch_func UscIsrTable[7] = /* * ioctl call handlers */ -static int tiocmget(struct tty_struct *tty, struct file *file); +static int tiocmget(struct tty_struct *tty); static int tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount @@ -2846,7 +2846,7 @@ static int modem_input_wait(struct mgsl_struct *info,int arg) /* return the state of the serial control and status signals */ -static int tiocmget(struct tty_struct *tty, struct file *file) +static int tiocmget(struct tty_struct *tty) { struct mgsl_struct *info = tty->driver_data; unsigned int result; diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index d01fffeac951..f18ab8af0e1c 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -512,7 +512,7 @@ static int tx_abort(struct slgt_info *info); static int rx_enable(struct slgt_info *info, int enable); static int modem_input_wait(struct slgt_info *info,int arg); static int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr); -static int tiocmget(struct tty_struct *tty, struct file *file); +static int tiocmget(struct tty_struct *tty); static int tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int set_break(struct tty_struct *tty, int break_state); @@ -3195,7 +3195,7 @@ static int modem_input_wait(struct slgt_info *info,int arg) /* * return state of serial control and status signals */ -static int tiocmget(struct tty_struct *tty, struct file *file) +static int tiocmget(struct tty_struct *tty) { struct slgt_info *info = tty->driver_data; unsigned int result; diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 2f9eb4b0dec1..5900213ae75b 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -546,7 +546,7 @@ static int tx_abort(SLMP_INFO *info); static int rx_enable(SLMP_INFO *info, int enable); static int modem_input_wait(SLMP_INFO *info,int arg); static int wait_mgsl_event(SLMP_INFO *info, int __user *mask_ptr); -static int tiocmget(struct tty_struct *tty, struct file *file); +static int tiocmget(struct tty_struct *tty); static int tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int set_break(struct tty_struct *tty, int break_state); @@ -3207,7 +3207,7 @@ static int modem_input_wait(SLMP_INFO *info,int arg) /* return the state of the serial control and status signals */ -static int tiocmget(struct tty_struct *tty, struct file *file) +static int tiocmget(struct tty_struct *tty) { SLMP_INFO *info = tty->driver_data; unsigned int result; diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c index bb710d16a526..e1a7c14f5f1d 100644 --- a/drivers/isdn/gigaset/interface.c +++ b/drivers/isdn/gigaset/interface.c @@ -122,7 +122,7 @@ static int if_chars_in_buffer(struct tty_struct *tty); static void if_throttle(struct tty_struct *tty); static void if_unthrottle(struct tty_struct *tty); static void if_set_termios(struct tty_struct *tty, struct ktermios *old); -static int if_tiocmget(struct tty_struct *tty, struct file *file); +static int if_tiocmget(struct tty_struct *tty); static int if_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int if_write(struct tty_struct *tty, @@ -280,7 +280,7 @@ static int if_ioctl(struct tty_struct *tty, struct file *file, return retval; } -static int if_tiocmget(struct tty_struct *tty, struct file *file) +static int if_tiocmget(struct tty_struct *tty) { struct cardstate *cs; int retval; diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index c463162843ba..ba6c2f124b58 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1345,7 +1345,7 @@ isdn_tty_get_lsr_info(modem_info * info, uint __user * value) static int -isdn_tty_tiocmget(struct tty_struct *tty, struct file *file) +isdn_tty_tiocmget(struct tty_struct *tty) { modem_info *info = (modem_info *) tty->driver_data; u_char control, status; diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index a0716967b7c8..86bb04d821b1 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -956,7 +956,7 @@ static int sdio_uart_break_ctl(struct tty_struct *tty, int break_state) return 0; } -static int sdio_uart_tiocmget(struct tty_struct *tty, struct file *file) +static int sdio_uart_tiocmget(struct tty_struct *tty) { struct sdio_uart_port *port = tty->driver_data; int result; diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index bed8fcedff49..7c68c456c035 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -1656,7 +1656,7 @@ static int hso_get_count(struct tty_struct *tty, } -static int hso_serial_tiocmget(struct tty_struct *tty, struct file *file) +static int hso_serial_tiocmget(struct tty_struct *tty) { int retval; struct hso_serial *serial = get_serial_by_tty(tty); diff --git a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c index 515d9b8af01e..d999e54a7730 100644 --- a/drivers/net/wan/pc300_tty.c +++ b/drivers/net/wan/pc300_tty.c @@ -133,7 +133,7 @@ static void cpc_tty_signal_on(pc300dev_t *pc300dev, unsigned char); static int pc300_tiocmset(struct tty_struct *, struct file *, unsigned int, unsigned int); -static int pc300_tiocmget(struct tty_struct *, struct file *); +static int pc300_tiocmget(struct tty_struct *); /* functions called by PC300 driver */ void cpc_tty_init(pc300dev_t *dev); @@ -570,7 +570,7 @@ static int pc300_tiocmset(struct tty_struct *tty, struct file *file, return 0; } -static int pc300_tiocmget(struct tty_struct *tty, struct file *file) +static int pc300_tiocmget(struct tty_struct *tty) { unsigned int result; unsigned char status; diff --git a/drivers/staging/quatech_usb2/quatech_usb2.c b/drivers/staging/quatech_usb2/quatech_usb2.c index ed58f482c963..1e50292aef74 100644 --- a/drivers/staging/quatech_usb2/quatech_usb2.c +++ b/drivers/staging/quatech_usb2/quatech_usb2.c @@ -1078,7 +1078,7 @@ static void qt2_set_termios(struct tty_struct *tty, } } -static int qt2_tiocmget(struct tty_struct *tty, struct file *file) +static int qt2_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; diff --git a/drivers/staging/serqt_usb2/serqt_usb2.c b/drivers/staging/serqt_usb2/serqt_usb2.c index 27841ef6a568..56ded56db7b4 100644 --- a/drivers/staging/serqt_usb2/serqt_usb2.c +++ b/drivers/staging/serqt_usb2/serqt_usb2.c @@ -1383,7 +1383,7 @@ static void qt_break(struct tty_struct *tty, int break_state) static inline int qt_real_tiocmget(struct tty_struct *tty, struct usb_serial_port *port, - struct file *file, struct usb_serial *serial) + struct usb_serial *serial) { u8 mcr; @@ -1462,7 +1462,7 @@ static inline int qt_real_tiocmset(struct tty_struct *tty, return 0; } -static int qt_tiocmget(struct tty_struct *tty, struct file *file) +static int qt_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = get_usb_serial(port, __func__); @@ -1480,7 +1480,7 @@ static int qt_tiocmget(struct tty_struct *tty, struct file *file) dbg("%s - port %d\n", __func__, port->number); dbg("%s - port->RxHolding = %d\n", __func__, qt_port->RxHolding); - retval = qt_real_tiocmget(tty, port, file, serial); + retval = qt_real_tiocmget(tty, port, serial); spin_unlock_irqrestore(&qt_port->lock, flags); return retval; diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index 67a75a502c01..55293105a56c 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -1095,7 +1095,7 @@ static void hvsi_unthrottle(struct tty_struct *tty) h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); } -static int hvsi_tiocmget(struct tty_struct *tty, struct file *file) +static int hvsi_tiocmget(struct tty_struct *tty) { struct hvsi_struct *hp = tty->driver_data; diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 44b8412a04e8..97e3d509ff82 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2648,7 +2648,7 @@ static void gsmtty_wait_until_sent(struct tty_struct *tty, int timeout) to do here */ } -static int gsmtty_tiocmget(struct tty_struct *tty, struct file *filp) +static int gsmtty_tiocmget(struct tty_struct *tty) { struct gsm_dlci *dlci = tty->driver_data; return dlci->modem_rx; diff --git a/drivers/tty/serial/68360serial.c b/drivers/tty/serial/68360serial.c index 88b13356ec10..2a52cf14ce50 100644 --- a/drivers/tty/serial/68360serial.c +++ b/drivers/tty/serial/68360serial.c @@ -1240,7 +1240,7 @@ static int get_lsr_info(struct async_struct * info, unsigned int *value) } #endif -static int rs_360_tiocmget(struct tty_struct *tty, struct file *file) +static int rs_360_tiocmget(struct tty_struct *tty) { ser_info_t *info = (ser_info_t *)tty->driver_data; unsigned int result = 0; diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index bcc31f2140ac..8cc5c0224b25 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3614,7 +3614,7 @@ rs_tiocmset(struct tty_struct *tty, struct file *file, } static int -rs_tiocmget(struct tty_struct *tty, struct file *file) +rs_tiocmget(struct tty_struct *tty) { struct e100_serial *info = (struct e100_serial *)tty->driver_data; unsigned int result; diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index b68b96f53e6c..4d26d39ec344 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -245,7 +245,7 @@ static void ifx_spi_timeout(unsigned long arg) * Map the signal state into Linux modem flags and report the value * in Linux terms */ -static int ifx_spi_tiocmget(struct tty_struct *tty, struct file *filp) +static int ifx_spi_tiocmget(struct tty_struct *tty) { unsigned int value; struct ifx_spi_device *ifx_dev = tty->driver_data; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 20563c509b21..53e490e47560 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -905,7 +905,7 @@ static int uart_get_lsr_info(struct tty_struct *tty, return put_user(result, value); } -static int uart_tiocmget(struct tty_struct *tty, struct file *file) +static int uart_tiocmget(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; @@ -913,10 +913,8 @@ static int uart_tiocmget(struct tty_struct *tty, struct file *file) int result = -EIO; mutex_lock(&port->mutex); - if ((!file || !tty_hung_up_p(file)) && - !(tty->flags & (1 << TTY_IO_ERROR))) { + if (!(tty->flags & (1 << TTY_IO_ERROR))) { result = uport->mctrl; - spin_lock_irq(&uport->lock); result |= uport->ops->get_mctrl(uport); spin_unlock_irq(&uport->lock); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 0065da4b11c1..fde5a4dae3dd 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2465,12 +2465,12 @@ out: * Locking: none (up to the driver) */ -static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p) +static int tty_tiocmget(struct tty_struct *tty, int __user *p) { int retval = -EINVAL; if (tty->ops->tiocmget) { - retval = tty->ops->tiocmget(tty, file); + retval = tty->ops->tiocmget(tty); if (retval >= 0) retval = put_user(retval, p); @@ -2655,7 +2655,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return send_break(tty, arg ? arg*100 : 250); case TIOCMGET: - return tty_tiocmget(tty, file, p); + return tty_tiocmget(tty, p); case TIOCMSET: case TIOCMBIC: case TIOCMBIS: diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index d6ede989ff22..2ae996b7ce7b 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -776,7 +776,7 @@ static int acm_tty_break_ctl(struct tty_struct *tty, int state) return retval; } -static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file) +static int acm_tty_tiocmget(struct tty_struct *tty) { struct acm *acm = tty->driver_data; diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 8f1d4fb19d24..35b610aa3f92 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -485,7 +485,7 @@ static int ark3116_ioctl(struct tty_struct *tty, struct file *file, return -ENOIOCTLCMD; } -static int ark3116_tiocmget(struct tty_struct *tty, struct file *file) +static int ark3116_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct ark3116_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 36df35295db2..48fb3bad3cd6 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -100,7 +100,7 @@ static void belkin_sa_process_read_urb(struct urb *urb); static void belkin_sa_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios * old); static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state); -static int belkin_sa_tiocmget(struct tty_struct *tty, struct file *file); +static int belkin_sa_tiocmget(struct tty_struct *tty); static int belkin_sa_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); @@ -497,7 +497,7 @@ static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state) dev_err(&port->dev, "Set break_ctl %d\n", break_state); } -static int belkin_sa_tiocmget(struct tty_struct *tty, struct file *file) +static int belkin_sa_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct belkin_sa_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 7b8815ddf368..aa0962b72f4c 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -572,7 +572,7 @@ static int ch341_ioctl(struct tty_struct *tty, struct file *file, return -ENOIOCTLCMD; } -static int ch341_tiocmget(struct tty_struct *tty, struct file *file) +static int ch341_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct ch341_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 735ea03157ab..b3873815035c 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -41,7 +41,7 @@ static void cp210x_get_termios_port(struct usb_serial_port *port, unsigned int *cflagp, unsigned int *baudp); static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *, struct ktermios*); -static int cp210x_tiocmget(struct tty_struct *, struct file *); +static int cp210x_tiocmget(struct tty_struct *); static int cp210x_tiocmset(struct tty_struct *, struct file *, unsigned int, unsigned int); static int cp210x_tiocmset_port(struct usb_serial_port *port, struct file *, @@ -742,7 +742,7 @@ static void cp210x_dtr_rts(struct usb_serial_port *p, int on) cp210x_tiocmset_port(p, NULL, 0, TIOCM_DTR|TIOCM_RTS); } -static int cp210x_tiocmget (struct tty_struct *tty, struct file *file) +static int cp210x_tiocmget (struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; unsigned int control; diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 2edf238b00b9..9c96cff691fd 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -173,7 +173,7 @@ static int cypress_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); static void cypress_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); -static int cypress_tiocmget(struct tty_struct *tty, struct file *file); +static int cypress_tiocmget(struct tty_struct *tty); static int cypress_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int cypress_chars_in_buffer(struct tty_struct *tty); @@ -864,7 +864,7 @@ static int cypress_write_room(struct tty_struct *tty) } -static int cypress_tiocmget(struct tty_struct *tty, struct file *file) +static int cypress_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 666e5a6edd82..08da46cb5825 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -445,7 +445,7 @@ static void digi_rx_unthrottle(struct tty_struct *tty); static void digi_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); static void digi_break_ctl(struct tty_struct *tty, int break_state); -static int digi_tiocmget(struct tty_struct *tty, struct file *file); +static int digi_tiocmget(struct tty_struct *tty); static int digi_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, @@ -1118,7 +1118,7 @@ static void digi_break_ctl(struct tty_struct *tty, int break_state) } -static int digi_tiocmget(struct tty_struct *tty, struct file *file) +static int digi_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 4787c0cd063f..281d18141051 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -856,7 +856,7 @@ static int ftdi_prepare_write_buffer(struct usb_serial_port *port, void *dest, size_t size); static void ftdi_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); -static int ftdi_tiocmget(struct tty_struct *tty, struct file *file); +static int ftdi_tiocmget(struct tty_struct *tty); static int ftdi_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int ftdi_ioctl(struct tty_struct *tty, struct file *file, @@ -2149,7 +2149,7 @@ static void ftdi_set_termios(struct tty_struct *tty, } } -static int ftdi_tiocmget(struct tty_struct *tty, struct file *file) +static int ftdi_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index cd769ef24f8a..e8fe4dcf72f0 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -219,7 +219,7 @@ static void edge_set_termios(struct tty_struct *tty, static int edge_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); static void edge_break(struct tty_struct *tty, int break_state); -static int edge_tiocmget(struct tty_struct *tty, struct file *file); +static int edge_tiocmget(struct tty_struct *tty); static int edge_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int edge_get_icount(struct tty_struct *tty, @@ -1599,7 +1599,7 @@ static int edge_tiocmset(struct tty_struct *tty, struct file *file, return 0; } -static int edge_tiocmget(struct tty_struct *tty, struct file *file) +static int edge_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 22506b095c4f..7cb9f5cb91f3 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2477,7 +2477,7 @@ static int edge_tiocmset(struct tty_struct *tty, struct file *file, return 0; } -static int edge_tiocmget(struct tty_struct *tty, struct file *file) +static int edge_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 99b97c04896f..1d96142f135a 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -179,7 +179,7 @@ static int iuu_tiocmset(struct tty_struct *tty, struct file *file, * When no card , the reader respond with TIOCM_CD * This is known as CD autodetect mechanism */ -static int iuu_tiocmget(struct tty_struct *tty, struct file *file) +static int iuu_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct iuu_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 0791778a66f3..1beebbb7a20a 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -301,7 +301,7 @@ static void keyspan_set_termios(struct tty_struct *tty, keyspan_send_setup(port, 0); } -static int keyspan_tiocmget(struct tty_struct *tty, struct file *file) +static int keyspan_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct keyspan_port_private *p_priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h index ce134dc28ddf..5e5fc71e68df 100644 --- a/drivers/usb/serial/keyspan.h +++ b/drivers/usb/serial/keyspan.h @@ -58,8 +58,7 @@ static void keyspan_set_termios (struct tty_struct *tty, struct ktermios *old); static void keyspan_break_ctl (struct tty_struct *tty, int break_state); -static int keyspan_tiocmget (struct tty_struct *tty, - struct file *file); +static int keyspan_tiocmget (struct tty_struct *tty); static int keyspan_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 554a8693a463..49ad2baf77cd 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -457,7 +457,7 @@ static int keyspan_pda_set_modem_info(struct usb_serial *serial, return rc; } -static int keyspan_pda_tiocmget(struct tty_struct *tty, struct file *file) +static int keyspan_pda_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index e8a65ce45a2f..a570f5201c73 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -68,7 +68,7 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port); static void klsi_105_close(struct usb_serial_port *port); static void klsi_105_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); -static int klsi_105_tiocmget(struct tty_struct *tty, struct file *file); +static int klsi_105_tiocmget(struct tty_struct *tty); static int klsi_105_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static void klsi_105_process_read_urb(struct urb *urb); @@ -637,7 +637,7 @@ static void mct_u232_break_ctl(struct tty_struct *tty, int break_state) } #endif -static int klsi_105_tiocmget(struct tty_struct *tty, struct file *file) +static int klsi_105_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct klsi_105_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index bd5bd8589e04..81d07fb299b8 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -77,7 +77,7 @@ static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, static int kobil_write_room(struct tty_struct *tty); static int kobil_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); -static int kobil_tiocmget(struct tty_struct *tty, struct file *file); +static int kobil_tiocmget(struct tty_struct *tty); static int kobil_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static void kobil_read_int_callback(struct urb *urb); @@ -504,7 +504,7 @@ static int kobil_write_room(struct tty_struct *tty) } -static int kobil_tiocmget(struct tty_struct *tty, struct file *file) +static int kobil_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct kobil_private *priv; diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 2849f8c32015..27447095feae 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -101,7 +101,7 @@ static void mct_u232_read_int_callback(struct urb *urb); static void mct_u232_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static void mct_u232_break_ctl(struct tty_struct *tty, int break_state); -static int mct_u232_tiocmget(struct tty_struct *tty, struct file *file); +static int mct_u232_tiocmget(struct tty_struct *tty); static int mct_u232_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static void mct_u232_throttle(struct tty_struct *tty); @@ -762,7 +762,7 @@ static void mct_u232_break_ctl(struct tty_struct *tty, int break_state) } /* mct_u232_break_ctl */ -static int mct_u232_tiocmget(struct tty_struct *tty, struct file *file) +static int mct_u232_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct mct_u232_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 7d3bc9a3e2b6..5d40d4151b5a 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1833,7 +1833,7 @@ static int get_lsr_info(struct tty_struct *tty, return 0; } -static int mos7720_tiocmget(struct tty_struct *tty, struct file *file) +static int mos7720_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port = usb_get_serial_port_data(port); @@ -1865,7 +1865,7 @@ static int mos7720_tiocmset(struct tty_struct *tty, struct file *file, struct moschip_port *mos7720_port = usb_get_serial_port_data(port); unsigned int mcr ; dbg("%s - port %d", __func__, port->number); - dbg("he was at tiocmget"); + dbg("he was at tiocmset"); mcr = mos7720_port->shadowMCR; diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 5627993f9e41..ee0dc9a0890c 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1644,7 +1644,7 @@ static void mos7840_unthrottle(struct tty_struct *tty) } } -static int mos7840_tiocmget(struct tty_struct *tty, struct file *file) +static int mos7840_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7840_port; diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index eda1f9266c4e..e305df807396 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -352,7 +352,7 @@ static void opticon_unthrottle(struct tty_struct *tty) } } -static int opticon_tiocmget(struct tty_struct *tty, struct file *file) +static int opticon_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct opticon_private *priv = usb_get_serial_data(port->serial); diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 73613205be7a..4cd3b0ef4e61 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -144,7 +144,7 @@ static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static int oti6858_write_room(struct tty_struct *tty); static int oti6858_chars_in_buffer(struct tty_struct *tty); -static int oti6858_tiocmget(struct tty_struct *tty, struct file *file); +static int oti6858_tiocmget(struct tty_struct *tty); static int oti6858_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int oti6858_startup(struct usb_serial *serial); @@ -657,7 +657,7 @@ static int oti6858_tiocmset(struct tty_struct *tty, struct file *file, return 0; } -static int oti6858_tiocmget(struct tty_struct *tty, struct file *file) +static int oti6858_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct oti6858_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 08c9181b8e48..6cb4f503a3f1 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -531,7 +531,7 @@ static int pl2303_tiocmset(struct tty_struct *tty, struct file *file, return set_control_lines(port->serial->dev, control); } -static int pl2303_tiocmget(struct tty_struct *tty, struct file *file) +static int pl2303_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct pl2303_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 7481ff8a49e4..66437f1e9e5b 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -389,7 +389,7 @@ static void sierra_set_termios(struct tty_struct *tty, sierra_send_setup(port); } -static int sierra_tiocmget(struct tty_struct *tty, struct file *file) +static int sierra_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; unsigned int value; diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index cbfb70bffdd0..cac13009fc5c 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -618,7 +618,7 @@ static int spcp8x5_tiocmset(struct tty_struct *tty, struct file *file, return spcp8x5_set_ctrlLine(port->serial->dev, control , priv->type); } -static int spcp8x5_tiocmget(struct tty_struct *tty, struct file *file) +static int spcp8x5_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct spcp8x5_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index 8359ec798959..b21583fa825c 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -484,7 +484,7 @@ static int ssu100_attach(struct usb_serial *serial) return ssu100_initdevice(serial->dev); } -static int ssu100_tiocmget(struct tty_struct *tty, struct file *file) +static int ssu100_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct usb_device *dev = port->serial->dev; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index b2902f307b47..223e60e31735 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -112,7 +112,7 @@ static int ti_get_icount(struct tty_struct *tty, struct serial_icounter_struct *icount); static void ti_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); -static int ti_tiocmget(struct tty_struct *tty, struct file *file); +static int ti_tiocmget(struct tty_struct *tty); static int ti_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static void ti_break(struct tty_struct *tty, int break_state); @@ -1000,7 +1000,7 @@ static void ti_set_termios(struct tty_struct *tty, } -static int ti_tiocmget(struct tty_struct *tty, struct file *file) +static int ti_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 546a52179bec..df105c6531a1 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -496,14 +496,14 @@ static const struct file_operations serial_proc_fops = { .release = single_release, }; -static int serial_tiocmget(struct tty_struct *tty, struct file *file) +static int serial_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); if (port->serial->type->tiocmget) - return port->serial->type->tiocmget(tty, file); + return port->serial->type->tiocmget(tty); return -EINVAL; } diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h index 3ab77c5d9819..8b68fc783d5f 100644 --- a/drivers/usb/serial/usb-wwan.h +++ b/drivers/usb/serial/usb-wwan.h @@ -15,7 +15,7 @@ extern int usb_wwan_write_room(struct tty_struct *tty); extern void usb_wwan_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); -extern int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file); +extern int usb_wwan_tiocmget(struct tty_struct *tty); extern int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); extern int usb_wwan_ioctl(struct tty_struct *tty, struct file *file, diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index b004b2a485c3..60f942632cb4 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -79,7 +79,7 @@ void usb_wwan_set_termios(struct tty_struct *tty, } EXPORT_SYMBOL(usb_wwan_set_termios); -int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file) +int usb_wwan_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; unsigned int value; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 3f9ac88d588c..bf850139e0b8 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -156,7 +156,7 @@ static int whiteheat_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); static void whiteheat_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); -static int whiteheat_tiocmget(struct tty_struct *tty, struct file *file); +static int whiteheat_tiocmget(struct tty_struct *tty); static int whiteheat_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static void whiteheat_break_ctl(struct tty_struct *tty, int break_state); @@ -833,7 +833,7 @@ static int whiteheat_write_room(struct tty_struct *tty) return (room); } -static int whiteheat_tiocmget(struct tty_struct *tty, struct file *file) +static int whiteheat_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index c3d43eb4150c..9539d74171db 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -271,7 +271,7 @@ struct tty_operations { void (*set_ldisc)(struct tty_struct *tty); void (*wait_until_sent)(struct tty_struct *tty, int timeout); void (*send_xchar)(struct tty_struct *tty, char ch); - int (*tiocmget)(struct tty_struct *tty, struct file *file); + int (*tiocmget)(struct tty_struct *tty); int (*tiocmset)(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); int (*resize)(struct tty_struct *tty, struct winsize *ws); diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index c9049139a7a5..30b945397d19 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -268,7 +268,7 @@ struct usb_serial_driver { int (*chars_in_buffer)(struct tty_struct *tty); void (*throttle)(struct tty_struct *tty); void (*unthrottle)(struct tty_struct *tty); - int (*tiocmget)(struct tty_struct *tty, struct file *file); + int (*tiocmget)(struct tty_struct *tty); int (*tiocmset)(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); int (*get_icount)(struct tty_struct *tty, diff --git a/include/net/irda/ircomm_tty.h b/include/net/irda/ircomm_tty.h index eea2e6152389..fa3793b5392d 100644 --- a/include/net/irda/ircomm_tty.h +++ b/include/net/irda/ircomm_tty.h @@ -120,7 +120,7 @@ struct ircomm_tty_cb { void ircomm_tty_start(struct tty_struct *tty); void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self); -extern int ircomm_tty_tiocmget(struct tty_struct *tty, struct file *file); +extern int ircomm_tty_tiocmget(struct tty_struct *tty); extern int ircomm_tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); extern int ircomm_tty_ioctl(struct tty_struct *tty, struct file *file, diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 2575c2db6404..7f67fa4f2f5e 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -1089,7 +1089,7 @@ static void rfcomm_tty_hangup(struct tty_struct *tty) } } -static int rfcomm_tty_tiocmget(struct tty_struct *tty, struct file *filp) +static int rfcomm_tty_tiocmget(struct tty_struct *tty) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; diff --git a/net/irda/ircomm/ircomm_tty_ioctl.c b/net/irda/ircomm/ircomm_tty_ioctl.c index 24cb3aa2bbfb..bb47caeba7e6 100644 --- a/net/irda/ircomm/ircomm_tty_ioctl.c +++ b/net/irda/ircomm/ircomm_tty_ioctl.c @@ -189,12 +189,12 @@ void ircomm_tty_set_termios(struct tty_struct *tty, } /* - * Function ircomm_tty_tiocmget (tty, file) + * Function ircomm_tty_tiocmget (tty) * * * */ -int ircomm_tty_tiocmget(struct tty_struct *tty, struct file *file) +int ircomm_tty_tiocmget(struct tty_struct *tty) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; unsigned int result; -- cgit v1.2.3 From 20b9d17715017ae4dd4ec87fabc36d33b9de708e Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 14 Feb 2011 16:26:50 +0000 Subject: tiocmset: kill the file pointer argument Doing tiocmget was such fun we should do tiocmset as well for the same reasons Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/amiserial.c | 4 ++-- drivers/char/cyclades.c | 2 +- drivers/char/epca.c | 4 ++-- drivers/char/ip2/ip2main.c | 4 ++-- drivers/char/isicom.c | 4 ++-- drivers/char/istallion.c | 2 +- drivers/char/moxa.c | 4 ++-- drivers/char/mxser.c | 2 +- drivers/char/nozomi.c | 4 ++-- drivers/char/pcmcia/ipwireless/tty.c | 2 +- drivers/char/pcmcia/synclink_cs.c | 6 +++--- drivers/char/riscom8.c | 4 ++-- drivers/char/rocket.c | 4 ++-- drivers/char/serial167.c | 3 +-- drivers/char/specialix.c | 2 +- drivers/char/stallion.c | 2 +- drivers/char/sx.c | 4 ++-- drivers/char/synclink.c | 6 +++--- drivers/char/synclink_gt.c | 6 +++--- drivers/char/synclinkmp.c | 8 ++++---- drivers/isdn/gigaset/interface.c | 4 ++-- drivers/isdn/gigaset/ser-gigaset.c | 2 +- drivers/isdn/i4l/isdn_tty.c | 2 +- drivers/mmc/card/sdio_uart.c | 2 +- drivers/net/irda/irtty-sir.c | 2 +- drivers/net/usb/hso.c | 6 +++--- drivers/net/wan/pc300_tty.c | 5 ++--- drivers/staging/quatech_usb2/quatech_usb2.c | 2 +- drivers/staging/serqt_usb2/serqt_usb2.c | 5 ++--- drivers/tty/hvc/hvsi.c | 4 ++-- drivers/tty/n_gsm.c | 2 +- drivers/tty/serial/68360serial.c | 2 +- drivers/tty/serial/crisv10.c | 3 +-- drivers/tty/serial/ifx6x60.c | 3 +-- drivers/tty/serial/serial_core.c | 6 ++---- drivers/tty/tty_io.c | 7 +++---- drivers/usb/class/cdc-acm.c | 2 +- drivers/usb/serial/ark3116.c | 2 +- drivers/usb/serial/belkin_sa.c | 4 ++-- drivers/usb/serial/ch341.c | 2 +- drivers/usb/serial/cp210x.c | 15 +++++++-------- drivers/usb/serial/cypress_m8.c | 4 ++-- drivers/usb/serial/digi_acceleport.c | 10 +++++----- drivers/usb/serial/ftdi_sio.c | 4 ++-- drivers/usb/serial/io_edgeport.c | 4 ++-- drivers/usb/serial/io_ti.c | 2 +- drivers/usb/serial/iuu_phoenix.c | 2 +- drivers/usb/serial/keyspan.c | 2 +- drivers/usb/serial/keyspan.h | 2 +- drivers/usb/serial/keyspan_pda.c | 2 +- drivers/usb/serial/kl5kusb105.c | 4 ++-- drivers/usb/serial/kobil_sct.c | 4 ++-- drivers/usb/serial/mct_u232.c | 4 ++-- drivers/usb/serial/mos7720.c | 2 +- drivers/usb/serial/mos7840.c | 2 +- drivers/usb/serial/oti6858.c | 4 ++-- drivers/usb/serial/pl2303.c | 2 +- drivers/usb/serial/sierra.c | 2 +- drivers/usb/serial/spcp8x5.c | 2 +- drivers/usb/serial/ssu100.c | 2 +- drivers/usb/serial/ti_usb_3410_5052.c | 6 +++--- drivers/usb/serial/usb-serial.c | 4 ++-- drivers/usb/serial/usb-wwan.h | 2 +- drivers/usb/serial/usb_wwan.c | 2 +- drivers/usb/serial/whiteheat.c | 4 ++-- include/linux/tty_driver.h | 2 +- include/linux/usb/serial.h | 2 +- include/net/irda/ircomm_tty.h | 2 +- net/bluetooth/rfcomm/tty.c | 2 +- net/irda/ircomm/ircomm_tty_ioctl.c | 4 ++-- 70 files changed, 120 insertions(+), 129 deletions(-) (limited to 'include') diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index bc67e6839059..5c15fad71ad2 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -1216,8 +1216,8 @@ static int rs_tiocmget(struct tty_struct *tty) | (!(status & SER_CTS) ? TIOCM_CTS : 0); } -static int rs_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) +static int rs_tiocmset(struct tty_struct *tty, unsigned int set, + unsigned int clear) { struct async_struct * info = tty->driver_data; unsigned long flags; diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index e7945ddacd18..942b6f2b70a4 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -2483,7 +2483,7 @@ end: } /* cy_tiomget */ static int -cy_tiocmset(struct tty_struct *tty, struct file *file, +cy_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct cyclades_port *info = tty->driver_data; diff --git a/drivers/char/epca.c b/drivers/char/epca.c index ecf6f0a889fc..e5872b59f9cd 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -2015,7 +2015,7 @@ static int pc_tiocmget(struct tty_struct *tty) return mflag; } -static int pc_tiocmset(struct tty_struct *tty, struct file *file, +static int pc_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct channel *ch = tty->driver_data; @@ -2081,7 +2081,7 @@ static int pc_ioctl(struct tty_struct *tty, struct file *file, case TIOCMODS: if (get_user(mstat, (unsigned __user *)argp)) return -EFAULT; - return pc_tiocmset(tty, file, mstat, ~mstat); + return pc_tiocmset(tty, mstat, ~mstat); case TIOCSDTR: spin_lock_irqsave(&epca_lock, flags); ch->omodem |= ch->m_dtr; diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index 476cd087118e..d5f866c7c672 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -182,7 +182,7 @@ static void ip2_stop(PTTY); static void ip2_start(PTTY); static void ip2_hangup(PTTY); static int ip2_tiocmget(struct tty_struct *tty); -static int ip2_tiocmset(struct tty_struct *tty, struct file *file, +static int ip2_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static int ip2_get_icount(struct tty_struct *tty, struct serial_icounter_struct *icount); @@ -2085,7 +2085,7 @@ static int ip2_tiocmget(struct tty_struct *tty) | ((pCh->dataSetIn & I2_CTS) ? TIOCM_CTS : 0); } -static int ip2_tiocmset(struct tty_struct *tty, struct file *file, +static int ip2_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { i2ChanStrPtr pCh = DevTable[tty->index]; diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 836370bc04c2..60f4d8ae7a49 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -1082,8 +1082,8 @@ static int isicom_tiocmget(struct tty_struct *tty) ((status & ISI_RI ) ? TIOCM_RI : 0); } -static int isicom_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) +static int isicom_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) { struct isi_port *port = tty->driver_data; unsigned long flags; diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 7843a847b76a..763b58d58255 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -1524,7 +1524,7 @@ static int stli_tiocmget(struct tty_struct *tty) return stli_mktiocm(portp->asig.sigvalue); } -static int stli_tiocmset(struct tty_struct *tty, struct file *file, +static int stli_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct stliport *portp = tty->driver_data; diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index fdf069bb702f..9f4cd8968a5a 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -200,7 +200,7 @@ static void moxa_stop(struct tty_struct *); static void moxa_start(struct tty_struct *); static void moxa_hangup(struct tty_struct *); static int moxa_tiocmget(struct tty_struct *tty); -static int moxa_tiocmset(struct tty_struct *tty, struct file *file, +static int moxa_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static void moxa_poll(unsigned long); static void moxa_set_tty_param(struct tty_struct *, struct ktermios *); @@ -1277,7 +1277,7 @@ static int moxa_tiocmget(struct tty_struct *tty) return flag; } -static int moxa_tiocmset(struct tty_struct *tty, struct file *file, +static int moxa_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct moxa_port *ch; diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 4d2f03ec06cd..150a862c4989 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -1347,7 +1347,7 @@ static int mxser_tiocmget(struct tty_struct *tty) ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); } -static int mxser_tiocmset(struct tty_struct *tty, struct file *file, +static int mxser_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct mxser_port *info = tty->driver_data; diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index 0e1dff2ffb1e..1b74c48c4016 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -1767,8 +1767,8 @@ static int ntty_tiocmget(struct tty_struct *tty) } /* Sets io controls parameters */ -static int ntty_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) +static int ntty_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) { struct nozomi *dc = get_dc_by_tty(tty); unsigned long flags; diff --git a/drivers/char/pcmcia/ipwireless/tty.c b/drivers/char/pcmcia/ipwireless/tty.c index 7d2ef4909a73..748190dfbab0 100644 --- a/drivers/char/pcmcia/ipwireless/tty.c +++ b/drivers/char/pcmcia/ipwireless/tty.c @@ -410,7 +410,7 @@ static int ipw_tiocmget(struct tty_struct *linux_tty) } static int -ipw_tiocmset(struct tty_struct *linux_tty, struct file *file, +ipw_tiocmset(struct tty_struct *linux_tty, unsigned int set, unsigned int clear) { struct ipw_tty *tty = linux_tty->driver_data; diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 7b68ba6609fe..02127cad0980 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -419,8 +419,8 @@ static void bh_status(MGSLPC_INFO *info); * ioctl handlers */ static int tiocmget(struct tty_struct *tty); -static int tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear); +static int tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear); static int get_stats(MGSLPC_INFO *info, struct mgsl_icount __user *user_icount); static int get_params(MGSLPC_INFO *info, MGSL_PARAMS __user *user_params); static int set_params(MGSLPC_INFO *info, MGSL_PARAMS __user *new_params, struct tty_struct *tty); @@ -2139,7 +2139,7 @@ static int tiocmget(struct tty_struct *tty) /* set modem control signals (DTR/RTS) */ -static int tiocmset(struct tty_struct *tty, struct file *file, +static int tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 5d0c98456c93..3666decc6438 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -1115,8 +1115,8 @@ static int rc_tiocmget(struct tty_struct *tty) return result; } -static int rc_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) +static int rc_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) { struct riscom_port *port = tty->driver_data; unsigned long flags; diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index 75e98efbc8e9..36c108811a81 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -1189,8 +1189,8 @@ static int rp_tiocmget(struct tty_struct *tty) /* * Sets the modem control lines */ -static int rp_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) +static int rp_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) { struct r_port *info = tty->driver_data; diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index fda90643ead7..89ac542ffff2 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -1331,8 +1331,7 @@ static int cy_tiocmget(struct tty_struct *tty) } /* cy_tiocmget */ static int -cy_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) +cy_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct cyclades_port *info = tty->driver_data; int channel; diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index bfecfbef0895..a6b23847e4a7 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -1778,7 +1778,7 @@ static int sx_tiocmget(struct tty_struct *tty) } -static int sx_tiocmset(struct tty_struct *tty, struct file *file, +static int sx_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct specialix_port *port = tty->driver_data; diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 8c2bf3fb5b89..c42dbffbed16 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -1107,7 +1107,7 @@ static int stl_tiocmget(struct tty_struct *tty) return stl_getsignals(portp); } -static int stl_tiocmset(struct tty_struct *tty, struct file *file, +static int stl_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct stlport *portp; diff --git a/drivers/char/sx.c b/drivers/char/sx.c index f46214e60d0c..342c6ae67da5 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -1879,8 +1879,8 @@ static int sx_tiocmget(struct tty_struct *tty) return sx_getsignals(port); } -static int sx_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) +static int sx_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) { struct sx_port *port = tty->driver_data; int rts = -1, dtr = -1; diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index d359e092904a..691e1094c20b 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -824,7 +824,7 @@ static isr_dispatch_func UscIsrTable[7] = * ioctl call handlers */ static int tiocmget(struct tty_struct *tty); -static int tiocmset(struct tty_struct *tty, struct file *file, +static int tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount __user *user_icount); @@ -2871,8 +2871,8 @@ static int tiocmget(struct tty_struct *tty) /* set modem control signals (DTR/RTS) */ -static int tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) +static int tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) { struct mgsl_struct *info = tty->driver_data; unsigned long flags; diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index f18ab8af0e1c..04da6d61dc4f 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -513,8 +513,8 @@ static int rx_enable(struct slgt_info *info, int enable); static int modem_input_wait(struct slgt_info *info,int arg); static int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr); static int tiocmget(struct tty_struct *tty); -static int tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear); +static int tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear); static int set_break(struct tty_struct *tty, int break_state); static int get_interface(struct slgt_info *info, int __user *if_mode); static int set_interface(struct slgt_info *info, int if_mode); @@ -3223,7 +3223,7 @@ static int tiocmget(struct tty_struct *tty) * TIOCMSET = set/clear signal values * value bit mask for command */ -static int tiocmset(struct tty_struct *tty, struct file *file, +static int tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct slgt_info *info = tty->driver_data; diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 5900213ae75b..1f9de97e8cfc 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -547,8 +547,8 @@ static int rx_enable(SLMP_INFO *info, int enable); static int modem_input_wait(SLMP_INFO *info,int arg); static int wait_mgsl_event(SLMP_INFO *info, int __user *mask_ptr); static int tiocmget(struct tty_struct *tty); -static int tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear); +static int tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear); static int set_break(struct tty_struct *tty, int break_state); static void add_device(SLMP_INFO *info); @@ -3232,8 +3232,8 @@ static int tiocmget(struct tty_struct *tty) /* set modem control signals (DTR/RTS) */ -static int tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) +static int tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) { SLMP_INFO *info = tty->driver_data; unsigned long flags; diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c index e1a7c14f5f1d..9b2bb491c614 100644 --- a/drivers/isdn/gigaset/interface.c +++ b/drivers/isdn/gigaset/interface.c @@ -123,7 +123,7 @@ static void if_throttle(struct tty_struct *tty); static void if_unthrottle(struct tty_struct *tty); static void if_set_termios(struct tty_struct *tty, struct ktermios *old); static int if_tiocmget(struct tty_struct *tty); -static int if_tiocmset(struct tty_struct *tty, struct file *file, +static int if_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static int if_write(struct tty_struct *tty, const unsigned char *buf, int count); @@ -303,7 +303,7 @@ static int if_tiocmget(struct tty_struct *tty) return retval; } -static int if_tiocmset(struct tty_struct *tty, struct file *file, +static int if_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct cardstate *cs; diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c index 0ef09d0eb96b..86a5c4f7775e 100644 --- a/drivers/isdn/gigaset/ser-gigaset.c +++ b/drivers/isdn/gigaset/ser-gigaset.c @@ -440,7 +440,7 @@ static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state, if (!set && !clear) return 0; gig_dbg(DEBUG_IF, "tiocmset set %x clear %x", set, clear); - return tty->ops->tiocmset(tty, NULL, set, clear); + return tty->ops->tiocmset(tty, set, clear); } static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag) diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index ba6c2f124b58..0341c69eb152 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1372,7 +1372,7 @@ isdn_tty_tiocmget(struct tty_struct *tty) } static int -isdn_tty_tiocmset(struct tty_struct *tty, struct file *file, +isdn_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { modem_info *info = (modem_info *) tty->driver_data; diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index 86bb04d821b1..c8c9edb3d7cb 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -970,7 +970,7 @@ static int sdio_uart_tiocmget(struct tty_struct *tty) return result; } -static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file, +static int sdio_uart_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct sdio_uart_port *port = tty->driver_data; diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c index ee1dde52e8fc..3352b2443e58 100644 --- a/drivers/net/irda/irtty-sir.c +++ b/drivers/net/irda/irtty-sir.c @@ -167,7 +167,7 @@ static int irtty_set_dtr_rts(struct sir_dev *dev, int dtr, int rts) * let's be careful... Jean II */ IRDA_ASSERT(priv->tty->ops->tiocmset != NULL, return -1;); - priv->tty->ops->tiocmset(priv->tty, NULL, set, clear); + priv->tty->ops->tiocmset(priv->tty, set, clear); return 0; } diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 7c68c456c035..956e1d6e72a5 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -324,7 +324,7 @@ struct hso_device { /* Prototypes */ /*****************************************************************************/ /* Serial driver functions */ -static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file, +static int hso_serial_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static void ctrl_callback(struct urb *urb); static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial); @@ -1335,7 +1335,7 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp) /* done */ if (result) - hso_serial_tiocmset(tty, NULL, TIOCM_RTS | TIOCM_DTR, 0); + hso_serial_tiocmset(tty, TIOCM_RTS | TIOCM_DTR, 0); err_out: mutex_unlock(&serial->parent->mutex); return result; @@ -1687,7 +1687,7 @@ static int hso_serial_tiocmget(struct tty_struct *tty) return retval; } -static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file, +static int hso_serial_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { int val = 0; diff --git a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c index d999e54a7730..1c65d1c33873 100644 --- a/drivers/net/wan/pc300_tty.c +++ b/drivers/net/wan/pc300_tty.c @@ -131,8 +131,7 @@ static void cpc_tty_trace(pc300dev_t *dev, char* buf, int len, char rxtx); static void cpc_tty_signal_off(pc300dev_t *pc300dev, unsigned char); static void cpc_tty_signal_on(pc300dev_t *pc300dev, unsigned char); -static int pc300_tiocmset(struct tty_struct *, struct file *, - unsigned int, unsigned int); +static int pc300_tiocmset(struct tty_struct *, unsigned int, unsigned int); static int pc300_tiocmget(struct tty_struct *); /* functions called by PC300 driver */ @@ -543,7 +542,7 @@ static int cpc_tty_chars_in_buffer(struct tty_struct *tty) return 0; } -static int pc300_tiocmset(struct tty_struct *tty, struct file *file, +static int pc300_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { st_cpc_tty_area *cpc_tty; diff --git a/drivers/staging/quatech_usb2/quatech_usb2.c b/drivers/staging/quatech_usb2/quatech_usb2.c index 1e50292aef74..3734448d1b82 100644 --- a/drivers/staging/quatech_usb2/quatech_usb2.c +++ b/drivers/staging/quatech_usb2/quatech_usb2.c @@ -1121,7 +1121,7 @@ static int qt2_tiocmget(struct tty_struct *tty) } } -static int qt2_tiocmset(struct tty_struct *tty, struct file *file, +static int qt2_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/staging/serqt_usb2/serqt_usb2.c b/drivers/staging/serqt_usb2/serqt_usb2.c index 56ded56db7b4..39776c1cf10f 100644 --- a/drivers/staging/serqt_usb2/serqt_usb2.c +++ b/drivers/staging/serqt_usb2/serqt_usb2.c @@ -1425,7 +1425,6 @@ static inline int qt_real_tiocmget(struct tty_struct *tty, static inline int qt_real_tiocmset(struct tty_struct *tty, struct usb_serial_port *port, - struct file *file, struct usb_serial *serial, unsigned int value) { @@ -1486,7 +1485,7 @@ static int qt_tiocmget(struct tty_struct *tty) return retval; } -static int qt_tiocmset(struct tty_struct *tty, struct file *file, +static int qt_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { @@ -1506,7 +1505,7 @@ static int qt_tiocmset(struct tty_struct *tty, struct file *file, dbg("%s - port %d\n", __func__, port->number); dbg("%s - qt_port->RxHolding = %d\n", __func__, qt_port->RxHolding); - retval = qt_real_tiocmset(tty, port, file, serial, set); + retval = qt_real_tiocmset(tty, port, serial, set); spin_unlock_irqrestore(&qt_port->lock, flags); return retval; diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index 55293105a56c..8a8d6373f164 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -1103,8 +1103,8 @@ static int hvsi_tiocmget(struct tty_struct *tty) return hp->mctrl; } -static int hvsi_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) +static int hvsi_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) { struct hvsi_struct *hp = tty->driver_data; unsigned long flags; diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 97e3d509ff82..88477d16b8b7 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2654,7 +2654,7 @@ static int gsmtty_tiocmget(struct tty_struct *tty) return dlci->modem_rx; } -static int gsmtty_tiocmset(struct tty_struct *tty, struct file *filp, +static int gsmtty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct gsm_dlci *dlci = tty->driver_data; diff --git a/drivers/tty/serial/68360serial.c b/drivers/tty/serial/68360serial.c index 2a52cf14ce50..217fe1c299e0 100644 --- a/drivers/tty/serial/68360serial.c +++ b/drivers/tty/serial/68360serial.c @@ -1271,7 +1271,7 @@ static int rs_360_tiocmget(struct tty_struct *tty) return result; } -static int rs_360_tiocmset(struct tty_struct *tty, struct file *file, +static int rs_360_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { #ifdef modem_control diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 8cc5c0224b25..b9fcd0bda60c 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3581,8 +3581,7 @@ rs_break(struct tty_struct *tty, int break_state) } static int -rs_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) +rs_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct e100_serial *info = (struct e100_serial *)tty->driver_data; unsigned long flags; diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 4d26d39ec344..8ee5a41d340d 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -263,7 +263,6 @@ static int ifx_spi_tiocmget(struct tty_struct *tty) /** * ifx_spi_tiocmset - set modem bits * @tty: the tty structure - * @filp: file handle issuing the request * @set: bits to set * @clear: bits to clear * @@ -272,7 +271,7 @@ static int ifx_spi_tiocmget(struct tty_struct *tty) * * FIXME: do we need to kick the tranfers when we do this ? */ -static int ifx_spi_tiocmset(struct tty_struct *tty, struct file *filp, +static int ifx_spi_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct ifx_spi_device *ifx_dev = tty->driver_data; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 53e490e47560..623d6bd911d7 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -925,8 +925,7 @@ static int uart_tiocmget(struct tty_struct *tty) } static int -uart_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) +uart_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct uart_state *state = tty->driver_data; struct uart_port *uport = state->uart_port; @@ -934,8 +933,7 @@ uart_tiocmset(struct tty_struct *tty, struct file *file, int ret = -EIO; mutex_lock(&port->mutex); - if ((!file || !tty_hung_up_p(file)) && - !(tty->flags & (1 << TTY_IO_ERROR))) { + if (!(tty->flags & (1 << TTY_IO_ERROR))) { uart_update_mctrl(uport, set, clear); ret = 0; } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index fde5a4dae3dd..83af24ca1e5e 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2481,7 +2481,6 @@ static int tty_tiocmget(struct tty_struct *tty, int __user *p) /** * tty_tiocmset - set modem status * @tty: tty device - * @file: user file pointer * @cmd: command - clear bits, set bits or set all * @p: pointer to desired bits * @@ -2491,7 +2490,7 @@ static int tty_tiocmget(struct tty_struct *tty, int __user *p) * Locking: none (up to the driver) */ -static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int cmd, +static int tty_tiocmset(struct tty_struct *tty, unsigned int cmd, unsigned __user *p) { int retval; @@ -2518,7 +2517,7 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int } set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP; clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP; - return tty->ops->tiocmset(tty, file, set, clear); + return tty->ops->tiocmset(tty, set, clear); } static int tty_tiocgicount(struct tty_struct *tty, void __user *arg) @@ -2659,7 +2658,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case TIOCMSET: case TIOCMBIC: case TIOCMBIS: - return tty_tiocmset(tty, file, cmd, p); + return tty_tiocmset(tty, cmd, p); case TIOCGICOUNT: retval = tty_tiocgicount(tty, p); /* For the moment allow fall through to the old method */ diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 2ae996b7ce7b..e9a26fbd079c 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -791,7 +791,7 @@ static int acm_tty_tiocmget(struct tty_struct *tty) TIOCM_CTS; } -static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file, +static int acm_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct acm *acm = tty->driver_data; diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 35b610aa3f92..2f837e983339 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -511,7 +511,7 @@ static int ark3116_tiocmget(struct tty_struct *tty) (ctrl & UART_MCR_OUT2 ? TIOCM_OUT2 : 0); } -static int ark3116_tiocmset(struct tty_struct *tty, struct file *file, +static int ark3116_tiocmset(struct tty_struct *tty, unsigned set, unsigned clr) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 48fb3bad3cd6..d6921fa1403c 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -101,7 +101,7 @@ static void belkin_sa_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios * old); static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state); static int belkin_sa_tiocmget(struct tty_struct *tty); -static int belkin_sa_tiocmset(struct tty_struct *tty, struct file *file, +static int belkin_sa_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); @@ -513,7 +513,7 @@ static int belkin_sa_tiocmget(struct tty_struct *tty) return control_state; } -static int belkin_sa_tiocmset(struct tty_struct *tty, struct file *file, +static int belkin_sa_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index aa0962b72f4c..5cbef313281e 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -431,7 +431,7 @@ out: kfree(break_reg); } -static int ch341_tiocmset(struct tty_struct *tty, struct file *file, +static int ch341_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index b3873815035c..4df3e0cecbae 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -42,9 +42,8 @@ static void cp210x_get_termios_port(struct usb_serial_port *port, static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *, struct ktermios*); static int cp210x_tiocmget(struct tty_struct *); -static int cp210x_tiocmset(struct tty_struct *, struct file *, - unsigned int, unsigned int); -static int cp210x_tiocmset_port(struct usb_serial_port *port, struct file *, +static int cp210x_tiocmset(struct tty_struct *, unsigned int, unsigned int); +static int cp210x_tiocmset_port(struct usb_serial_port *port, unsigned int, unsigned int); static void cp210x_break_ctl(struct tty_struct *, int); static int cp210x_startup(struct usb_serial *); @@ -698,14 +697,14 @@ static void cp210x_set_termios(struct tty_struct *tty, } -static int cp210x_tiocmset (struct tty_struct *tty, struct file *file, +static int cp210x_tiocmset (struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; - return cp210x_tiocmset_port(port, file, set, clear); + return cp210x_tiocmset_port(port, set, clear); } -static int cp210x_tiocmset_port(struct usb_serial_port *port, struct file *file, +static int cp210x_tiocmset_port(struct usb_serial_port *port, unsigned int set, unsigned int clear) { unsigned int control = 0; @@ -737,9 +736,9 @@ static int cp210x_tiocmset_port(struct usb_serial_port *port, struct file *file, static void cp210x_dtr_rts(struct usb_serial_port *p, int on) { if (on) - cp210x_tiocmset_port(p, NULL, TIOCM_DTR|TIOCM_RTS, 0); + cp210x_tiocmset_port(p, TIOCM_DTR|TIOCM_RTS, 0); else - cp210x_tiocmset_port(p, NULL, 0, TIOCM_DTR|TIOCM_RTS); + cp210x_tiocmset_port(p, 0, TIOCM_DTR|TIOCM_RTS); } static int cp210x_tiocmget (struct tty_struct *tty) diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 9c96cff691fd..2beb5a66180d 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -174,7 +174,7 @@ static int cypress_ioctl(struct tty_struct *tty, struct file *file, static void cypress_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static int cypress_tiocmget(struct tty_struct *tty); -static int cypress_tiocmset(struct tty_struct *tty, struct file *file, +static int cypress_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static int cypress_chars_in_buffer(struct tty_struct *tty); static void cypress_throttle(struct tty_struct *tty); @@ -892,7 +892,7 @@ static int cypress_tiocmget(struct tty_struct *tty) } -static int cypress_tiocmset(struct tty_struct *tty, struct file *file, +static int cypress_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 08da46cb5825..86fbba6336c9 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -446,10 +446,10 @@ static void digi_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); static void digi_break_ctl(struct tty_struct *tty, int break_state); static int digi_tiocmget(struct tty_struct *tty); -static int digi_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear); +static int digi_tiocmset(struct tty_struct *tty, unsigned int set, + unsigned int clear); static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, - const unsigned char *buf, int count); + const unsigned char *buf, int count); static void digi_write_bulk_callback(struct urb *urb); static int digi_write_room(struct tty_struct *tty); static int digi_chars_in_buffer(struct tty_struct *tty); @@ -1134,8 +1134,8 @@ static int digi_tiocmget(struct tty_struct *tty) } -static int digi_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) +static int digi_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 281d18141051..f521ab1eb60f 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -857,7 +857,7 @@ static int ftdi_prepare_write_buffer(struct usb_serial_port *port, static void ftdi_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static int ftdi_tiocmget(struct tty_struct *tty); -static int ftdi_tiocmset(struct tty_struct *tty, struct file *file, +static int ftdi_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static int ftdi_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); @@ -2202,7 +2202,7 @@ out: return ret; } -static int ftdi_tiocmset(struct tty_struct *tty, struct file *file, +static int ftdi_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index e8fe4dcf72f0..0b8846e27a79 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -220,7 +220,7 @@ static int edge_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); static void edge_break(struct tty_struct *tty, int break_state); static int edge_tiocmget(struct tty_struct *tty); -static int edge_tiocmset(struct tty_struct *tty, struct file *file, +static int edge_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static int edge_get_icount(struct tty_struct *tty, struct serial_icounter_struct *icount); @@ -1568,7 +1568,7 @@ static int get_lsr_info(struct edgeport_port *edge_port, return 0; } -static int edge_tiocmset(struct tty_struct *tty, struct file *file, +static int edge_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 7cb9f5cb91f3..88120523710b 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2444,7 +2444,7 @@ static void edge_set_termios(struct tty_struct *tty, change_port_settings(tty, edge_port, old_termios); } -static int edge_tiocmset(struct tty_struct *tty, struct file *file, +static int edge_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 1d96142f135a..6aca631a407a 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -150,7 +150,7 @@ static void iuu_release(struct usb_serial *serial) } } -static int iuu_tiocmset(struct tty_struct *tty, struct file *file, +static int iuu_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 1beebbb7a20a..c6e968f24e04 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -317,7 +317,7 @@ static int keyspan_tiocmget(struct tty_struct *tty) return value; } -static int keyspan_tiocmset(struct tty_struct *tty, struct file *file, +static int keyspan_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h index 5e5fc71e68df..13fa1d1cc900 100644 --- a/drivers/usb/serial/keyspan.h +++ b/drivers/usb/serial/keyspan.h @@ -60,7 +60,7 @@ static void keyspan_break_ctl (struct tty_struct *tty, int break_state); static int keyspan_tiocmget (struct tty_struct *tty); static int keyspan_tiocmset (struct tty_struct *tty, - struct file *file, unsigned int set, + unsigned int set, unsigned int clear); static int keyspan_fake_startup (struct usb_serial *serial); diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 49ad2baf77cd..207caabdc4ff 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -478,7 +478,7 @@ static int keyspan_pda_tiocmget(struct tty_struct *tty) return value; } -static int keyspan_pda_tiocmset(struct tty_struct *tty, struct file *file, +static int keyspan_pda_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index a570f5201c73..19373cb7c5bf 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -69,7 +69,7 @@ static void klsi_105_close(struct usb_serial_port *port); static void klsi_105_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static int klsi_105_tiocmget(struct tty_struct *tty); -static int klsi_105_tiocmset(struct tty_struct *tty, struct file *file, +static int klsi_105_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static void klsi_105_process_read_urb(struct urb *urb); static int klsi_105_prepare_write_buffer(struct usb_serial_port *port, @@ -661,7 +661,7 @@ static int klsi_105_tiocmget(struct tty_struct *tty) return (int)line_state; } -static int klsi_105_tiocmset(struct tty_struct *tty, struct file *file, +static int klsi_105_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { int retval = -EINVAL; diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 81d07fb299b8..22cd0c08f46f 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -78,7 +78,7 @@ static int kobil_write_room(struct tty_struct *tty); static int kobil_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); static int kobil_tiocmget(struct tty_struct *tty); -static int kobil_tiocmset(struct tty_struct *tty, struct file *file, +static int kobil_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static void kobil_read_int_callback(struct urb *urb); static void kobil_write_callback(struct urb *purb); @@ -544,7 +544,7 @@ static int kobil_tiocmget(struct tty_struct *tty) return result; } -static int kobil_tiocmset(struct tty_struct *tty, struct file *file, +static int kobil_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 27447095feae..ef49902c5a51 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -102,7 +102,7 @@ static void mct_u232_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static void mct_u232_break_ctl(struct tty_struct *tty, int break_state); static int mct_u232_tiocmget(struct tty_struct *tty); -static int mct_u232_tiocmset(struct tty_struct *tty, struct file *file, +static int mct_u232_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static void mct_u232_throttle(struct tty_struct *tty); static void mct_u232_unthrottle(struct tty_struct *tty); @@ -778,7 +778,7 @@ static int mct_u232_tiocmget(struct tty_struct *tty) return control_state; } -static int mct_u232_tiocmset(struct tty_struct *tty, struct file *file, +static int mct_u232_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 5d40d4151b5a..95b1c64cac03 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1858,7 +1858,7 @@ static int mos7720_tiocmget(struct tty_struct *tty) return result; } -static int mos7720_tiocmset(struct tty_struct *tty, struct file *file, +static int mos7720_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index ee0dc9a0890c..9424178c6689 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1674,7 +1674,7 @@ static int mos7840_tiocmget(struct tty_struct *tty) return result; } -static int mos7840_tiocmset(struct tty_struct *tty, struct file *file, +static int mos7840_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 4cd3b0ef4e61..63734cb0fb0f 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -145,7 +145,7 @@ static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port, static int oti6858_write_room(struct tty_struct *tty); static int oti6858_chars_in_buffer(struct tty_struct *tty); static int oti6858_tiocmget(struct tty_struct *tty); -static int oti6858_tiocmset(struct tty_struct *tty, struct file *file, +static int oti6858_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static int oti6858_startup(struct usb_serial *serial); static void oti6858_release(struct usb_serial *serial); @@ -624,7 +624,7 @@ static void oti6858_close(struct usb_serial_port *port) usb_kill_urb(port->interrupt_in_urb); } -static int oti6858_tiocmset(struct tty_struct *tty, struct file *file, +static int oti6858_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 6cb4f503a3f1..b797992fa54b 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -505,7 +505,7 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port) return 0; } -static int pl2303_tiocmset(struct tty_struct *tty, struct file *file, +static int pl2303_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 66437f1e9e5b..79ee6c79ad54 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -408,7 +408,7 @@ static int sierra_tiocmget(struct tty_struct *tty) return value; } -static int sierra_tiocmset(struct tty_struct *tty, struct file *file, +static int sierra_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index cac13009fc5c..dfbc543e0db8 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -595,7 +595,7 @@ static int spcp8x5_ioctl(struct tty_struct *tty, struct file *file, return -ENOIOCTLCMD; } -static int spcp8x5_tiocmset(struct tty_struct *tty, struct file *file, +static int spcp8x5_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index b21583fa825c..abceee9d3af9 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -517,7 +517,7 @@ mget_out: return r; } -static int ssu100_tiocmset(struct tty_struct *tty, struct file *file, +static int ssu100_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 223e60e31735..c7fea4a2a1be 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -113,7 +113,7 @@ static int ti_get_icount(struct tty_struct *tty, static void ti_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); static int ti_tiocmget(struct tty_struct *tty); -static int ti_tiocmset(struct tty_struct *tty, struct file *file, +static int ti_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static void ti_break(struct tty_struct *tty, int break_state); static void ti_interrupt_callback(struct urb *urb); @@ -1033,8 +1033,8 @@ static int ti_tiocmget(struct tty_struct *tty) } -static int ti_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) +static int ti_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index df105c6531a1..dab679e5b7ea 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -507,7 +507,7 @@ static int serial_tiocmget(struct tty_struct *tty) return -EINVAL; } -static int serial_tiocmset(struct tty_struct *tty, struct file *file, +static int serial_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; @@ -515,7 +515,7 @@ static int serial_tiocmset(struct tty_struct *tty, struct file *file, dbg("%s - port %d", __func__, port->number); if (port->serial->type->tiocmset) - return port->serial->type->tiocmset(tty, file, set, clear); + return port->serial->type->tiocmset(tty, set, clear); return -EINVAL; } diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h index 8b68fc783d5f..4d65f1c8dd93 100644 --- a/drivers/usb/serial/usb-wwan.h +++ b/drivers/usb/serial/usb-wwan.h @@ -16,7 +16,7 @@ extern void usb_wwan_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); extern int usb_wwan_tiocmget(struct tty_struct *tty); -extern int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file, +extern int usb_wwan_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); extern int usb_wwan_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 60f942632cb4..b72912027ae8 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -98,7 +98,7 @@ int usb_wwan_tiocmget(struct tty_struct *tty) } EXPORT_SYMBOL(usb_wwan_tiocmget); -int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file, +int usb_wwan_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index bf850139e0b8..6e0c397e869f 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -157,7 +157,7 @@ static int whiteheat_ioctl(struct tty_struct *tty, struct file *file, static void whiteheat_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static int whiteheat_tiocmget(struct tty_struct *tty); -static int whiteheat_tiocmset(struct tty_struct *tty, struct file *file, +static int whiteheat_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static void whiteheat_break_ctl(struct tty_struct *tty, int break_state); static int whiteheat_chars_in_buffer(struct tty_struct *tty); @@ -850,7 +850,7 @@ static int whiteheat_tiocmget(struct tty_struct *tty) return modem_signals; } -static int whiteheat_tiocmset(struct tty_struct *tty, struct file *file, +static int whiteheat_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 9539d74171db..5dabaa2e6da3 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -272,7 +272,7 @@ struct tty_operations { void (*wait_until_sent)(struct tty_struct *tty, int timeout); void (*send_xchar)(struct tty_struct *tty, char ch); int (*tiocmget)(struct tty_struct *tty); - int (*tiocmset)(struct tty_struct *tty, struct file *file, + int (*tiocmset)(struct tty_struct *tty, unsigned int set, unsigned int clear); int (*resize)(struct tty_struct *tty, struct winsize *ws); int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew); diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 30b945397d19..c1aa1b243ba3 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -269,7 +269,7 @@ struct usb_serial_driver { void (*throttle)(struct tty_struct *tty); void (*unthrottle)(struct tty_struct *tty); int (*tiocmget)(struct tty_struct *tty); - int (*tiocmset)(struct tty_struct *tty, struct file *file, + int (*tiocmset)(struct tty_struct *tty, unsigned int set, unsigned int clear); int (*get_icount)(struct tty_struct *tty, struct serial_icounter_struct *icount); diff --git a/include/net/irda/ircomm_tty.h b/include/net/irda/ircomm_tty.h index fa3793b5392d..980ccb66e1b4 100644 --- a/include/net/irda/ircomm_tty.h +++ b/include/net/irda/ircomm_tty.h @@ -121,7 +121,7 @@ void ircomm_tty_start(struct tty_struct *tty); void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self); extern int ircomm_tty_tiocmget(struct tty_struct *tty); -extern int ircomm_tty_tiocmset(struct tty_struct *tty, struct file *file, +extern int ircomm_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); extern int ircomm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 7f67fa4f2f5e..8e78e7447726 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -1098,7 +1098,7 @@ static int rfcomm_tty_tiocmget(struct tty_struct *tty) return dev->modem_status; } -static int rfcomm_tty_tiocmset(struct tty_struct *tty, struct file *filp, unsigned int set, unsigned int clear) +static int rfcomm_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; struct rfcomm_dlc *dlc = dev->dlc; diff --git a/net/irda/ircomm/ircomm_tty_ioctl.c b/net/irda/ircomm/ircomm_tty_ioctl.c index bb47caeba7e6..5e0e718c930a 100644 --- a/net/irda/ircomm/ircomm_tty_ioctl.c +++ b/net/irda/ircomm/ircomm_tty_ioctl.c @@ -214,12 +214,12 @@ int ircomm_tty_tiocmget(struct tty_struct *tty) } /* - * Function ircomm_tty_tiocmset (tty, file, set, clear) + * Function ircomm_tty_tiocmset (tty, set, clear) * * * */ -int ircomm_tty_tiocmset(struct tty_struct *tty, struct file *file, +int ircomm_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; -- cgit v1.2.3 From 00a0d0d65b61241a718d0aee96f46b9a2d93bf26 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 14 Feb 2011 16:27:06 +0000 Subject: tty: remove filp from the USB tty ioctls We don't use it so we can trim it from here as we try and stamp the file object dependencies out of the serial code. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/staging/quatech_usb2/quatech_usb2.c | 2 +- drivers/staging/serqt_usb2/serqt_usb2.c | 2 +- drivers/usb/serial/ark3116.c | 2 +- drivers/usb/serial/ch341.c | 3 +-- drivers/usb/serial/cypress_m8.c | 4 ++-- drivers/usb/serial/ftdi_sio.c | 4 ++-- drivers/usb/serial/io_edgeport.c | 4 ++-- drivers/usb/serial/io_ti.c | 2 +- drivers/usb/serial/kobil_sct.c | 4 ++-- drivers/usb/serial/mos7720.c | 2 +- drivers/usb/serial/mos7840.c | 2 +- drivers/usb/serial/opticon.c | 2 +- drivers/usb/serial/oti6858.c | 4 ++-- drivers/usb/serial/pl2303.c | 2 +- drivers/usb/serial/spcp8x5.c | 2 +- drivers/usb/serial/ssu100.c | 2 +- drivers/usb/serial/ti_usb_3410_5052.c | 4 ++-- drivers/usb/serial/usb-serial.c | 2 +- drivers/usb/serial/usb-wwan.h | 2 +- drivers/usb/serial/usb_wwan.c | 2 +- drivers/usb/serial/whiteheat.c | 4 ++-- include/linux/usb/serial.h | 2 +- 22 files changed, 29 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/staging/quatech_usb2/quatech_usb2.c b/drivers/staging/quatech_usb2/quatech_usb2.c index 3734448d1b82..36b18e302718 100644 --- a/drivers/staging/quatech_usb2/quatech_usb2.c +++ b/drivers/staging/quatech_usb2/quatech_usb2.c @@ -852,7 +852,7 @@ static int qt2_chars_in_buffer(struct tty_struct *tty) * TIOCMGET and TIOCMSET are filtered off to their own methods before they get * here, so we don't have to handle them. */ -static int qt2_ioctl(struct tty_struct *tty, struct file *file, +static int qt2_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/staging/serqt_usb2/serqt_usb2.c b/drivers/staging/serqt_usb2/serqt_usb2.c index 39776c1cf10f..e0aae86a8809 100644 --- a/drivers/staging/serqt_usb2/serqt_usb2.c +++ b/drivers/staging/serqt_usb2/serqt_usb2.c @@ -1191,7 +1191,7 @@ static int qt_write_room(struct tty_struct *tty) } -static int qt_ioctl(struct tty_struct *tty, struct file *file, +static int qt_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 2f837e983339..5cdb9d912275 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -431,7 +431,7 @@ static int ark3116_get_icount(struct tty_struct *tty, return 0; } -static int ark3116_ioctl(struct tty_struct *tty, struct file *file, +static int ark3116_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 5cbef313281e..d0b9feac5dc3 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -552,8 +552,7 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return 0; } -/*static int ch341_ioctl(struct usb_serial_port *port, struct file *file,*/ -static int ch341_ioctl(struct tty_struct *tty, struct file *file, +static int ch341_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 2beb5a66180d..987e9bf7bd02 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -169,7 +169,7 @@ static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static void cypress_send(struct usb_serial_port *port); static int cypress_write_room(struct tty_struct *tty); -static int cypress_ioctl(struct tty_struct *tty, struct file *file, +static int cypress_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static void cypress_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); @@ -917,7 +917,7 @@ static int cypress_tiocmset(struct tty_struct *tty, } -static int cypress_ioctl(struct tty_struct *tty, struct file *file, +static int cypress_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index f521ab1eb60f..e3e23a4e227d 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -859,7 +859,7 @@ static void ftdi_set_termios(struct tty_struct *tty, static int ftdi_tiocmget(struct tty_struct *tty); static int ftdi_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); -static int ftdi_ioctl(struct tty_struct *tty, struct file *file, +static int ftdi_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static void ftdi_break_ctl(struct tty_struct *tty, int break_state); @@ -2210,7 +2210,7 @@ static int ftdi_tiocmset(struct tty_struct *tty, return update_mctrl(port, set, clear); } -static int ftdi_ioctl(struct tty_struct *tty, struct file *file, +static int ftdi_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 0b8846e27a79..ae91bcdcb2bc 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -216,7 +216,7 @@ static void edge_unthrottle(struct tty_struct *tty); static void edge_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); -static int edge_ioctl(struct tty_struct *tty, struct file *file, +static int edge_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static void edge_break(struct tty_struct *tty, int break_state); static int edge_tiocmget(struct tty_struct *tty); @@ -1679,7 +1679,7 @@ static int get_serial_info(struct edgeport_port *edge_port, * SerialIoctl * this function handles any ioctl calls to the driver *****************************************************************************/ -static int edge_ioctl(struct tty_struct *tty, struct file *file, +static int edge_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 88120523710b..d8434910fa7b 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2552,7 +2552,7 @@ static int get_serial_info(struct edgeport_port *edge_port, return 0; } -static int edge_ioctl(struct tty_struct *tty, struct file *file, +static int edge_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 22cd0c08f46f..667863ef0625 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -75,7 +75,7 @@ static void kobil_close(struct usb_serial_port *port); static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static int kobil_write_room(struct tty_struct *tty); -static int kobil_ioctl(struct tty_struct *tty, struct file *file, +static int kobil_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static int kobil_tiocmget(struct tty_struct *tty); static int kobil_tiocmset(struct tty_struct *tty, @@ -668,7 +668,7 @@ static void kobil_set_termios(struct tty_struct *tty, ); } -static int kobil_ioctl(struct tty_struct *tty, struct file *file, +static int kobil_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 95b1c64cac03..d8b3e8fa14e9 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1987,7 +1987,7 @@ static int get_serial_info(struct moschip_port *mos7720_port, return 0; } -static int mos7720_ioctl(struct tty_struct *tty, struct file *file, +static int mos7720_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 9424178c6689..7b50aa122752 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -2235,7 +2235,7 @@ static int mos7840_get_icount(struct tty_struct *tty, * this function handles any ioctl calls to the driver *****************************************************************************/ -static int mos7840_ioctl(struct tty_struct *tty, struct file *file, +static int mos7840_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index e305df807396..8d603a1ca2eb 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -396,7 +396,7 @@ static int get_serial_info(struct opticon_private *priv, return 0; } -static int opticon_ioctl(struct tty_struct *tty, struct file *file, +static int opticon_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 63734cb0fb0f..4c29e6c2bda7 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -135,7 +135,7 @@ static void oti6858_close(struct usb_serial_port *port); static void oti6858_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static void oti6858_init_termios(struct tty_struct *tty); -static int oti6858_ioctl(struct tty_struct *tty, struct file *file, +static int oti6858_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static void oti6858_read_int_callback(struct urb *urb); static void oti6858_read_bulk_callback(struct urb *urb); @@ -728,7 +728,7 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return 0; } -static int oti6858_ioctl(struct tty_struct *tty, struct file *file, +static int oti6858_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index b797992fa54b..30461fcc2206 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -606,7 +606,7 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return 0; } -static int pl2303_ioctl(struct tty_struct *tty, struct file *file, +static int pl2303_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct serial_struct ser; diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index dfbc543e0db8..180ea6c7911c 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -576,7 +576,7 @@ static int spcp8x5_wait_modem_info(struct usb_serial_port *port, return 0; } -static int spcp8x5_ioctl(struct tty_struct *tty, struct file *file, +static int spcp8x5_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index abceee9d3af9..87362e48796e 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -439,7 +439,7 @@ static int ssu100_get_icount(struct tty_struct *tty, -static int ssu100_ioctl(struct tty_struct *tty, struct file *file, +static int ssu100_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index c7fea4a2a1be..f4a57ad27db8 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -106,7 +106,7 @@ static int ti_write_room(struct tty_struct *tty); static int ti_chars_in_buffer(struct tty_struct *tty); static void ti_throttle(struct tty_struct *tty); static void ti_unthrottle(struct tty_struct *tty); -static int ti_ioctl(struct tty_struct *tty, struct file *file, +static int ti_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static int ti_get_icount(struct tty_struct *tty, struct serial_icounter_struct *icount); @@ -818,7 +818,7 @@ static int ti_get_icount(struct tty_struct *tty, return 0; } -static int ti_ioctl(struct tty_struct *tty, struct file *file, +static int ti_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index dab679e5b7ea..b1110e136c33 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -417,7 +417,7 @@ static int serial_ioctl(struct tty_struct *tty, struct file *file, /* pass on to the driver specific version of this function if it is available */ if (port->serial->type->ioctl) { - retval = port->serial->type->ioctl(tty, file, cmd, arg); + retval = port->serial->type->ioctl(tty, cmd, arg); } else retval = -ENOIOCTLCMD; return retval; diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h index 4d65f1c8dd93..c47b6ec03063 100644 --- a/drivers/usb/serial/usb-wwan.h +++ b/drivers/usb/serial/usb-wwan.h @@ -18,7 +18,7 @@ extern void usb_wwan_set_termios(struct tty_struct *tty, extern int usb_wwan_tiocmget(struct tty_struct *tty); extern int usb_wwan_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); -extern int usb_wwan_ioctl(struct tty_struct *tty, struct file *file, +extern int usb_wwan_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); extern int usb_wwan_send_setup(struct usb_serial_port *port); extern int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port, diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index b72912027ae8..b38d77b389d4 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -178,7 +178,7 @@ static int set_serial_info(struct usb_serial_port *port, return retval; } -int usb_wwan_ioctl(struct tty_struct *tty, struct file *file, +int usb_wwan_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 6e0c397e869f..5b073bcc807b 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -152,7 +152,7 @@ static int whiteheat_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static int whiteheat_write_room(struct tty_struct *tty); -static int whiteheat_ioctl(struct tty_struct *tty, struct file *file, +static int whiteheat_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static void whiteheat_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); @@ -874,7 +874,7 @@ static int whiteheat_tiocmset(struct tty_struct *tty, } -static int whiteheat_ioctl(struct tty_struct *tty, struct file *file, +static int whiteheat_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index c1aa1b243ba3..00e98ee5fba0 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -260,7 +260,7 @@ struct usb_serial_driver { const unsigned char *buf, int count); /* Called only by the tty layer */ int (*write_room)(struct tty_struct *tty); - int (*ioctl)(struct tty_struct *tty, struct file *file, + int (*ioctl)(struct tty_struct *tty, unsigned int cmd, unsigned long arg); void (*set_termios)(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); -- cgit v1.2.3 From 6caa76b7786891b42b66a0e61e2c2fff2c884620 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 14 Feb 2011 16:27:22 +0000 Subject: tty: now phase out the ioctl file pointer for good Only oddities here are a couple of drivers that bogusly called the ldisc helpers instead of returning -ENOIOCTLCMD. Fix the bug and the rest goes away. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/amiserial.c | 2 +- drivers/char/cyclades.c | 2 +- drivers/char/epca.c | 8 ++++---- drivers/char/ip2/ip2main.c | 4 ++-- drivers/char/isicom.c | 2 +- drivers/char/istallion.c | 4 ++-- drivers/char/moxa.c | 2 +- drivers/char/mxser.c | 2 +- drivers/char/nozomi.c | 2 +- drivers/char/pcmcia/ipwireless/tty.c | 4 ++-- drivers/char/riscom8.c | 2 +- drivers/char/rocket.c | 2 +- drivers/char/ser_a2232.c | 6 +++--- drivers/char/serial167.c | 2 +- drivers/char/specialix.c | 2 +- drivers/char/stallion.c | 5 ++--- drivers/char/sx.c | 2 +- drivers/char/synclink.c | 3 +-- drivers/char/synclink_gt.c | 9 ++++----- drivers/char/synclinkmp.c | 5 ++--- drivers/char/ttyprintk.c | 2 +- drivers/char/vme_scc.c | 4 ++-- drivers/isdn/capi/capi.c | 10 ++-------- drivers/isdn/gigaset/interface.c | 4 ++-- drivers/isdn/i4l/isdn_tty.c | 3 +-- drivers/net/usb/hso.c | 2 +- drivers/tty/n_gsm.c | 2 +- drivers/tty/pty.c | 4 ++-- drivers/tty/serial/68328serial.c | 2 +- drivers/tty/serial/68360serial.c | 2 +- drivers/tty/serial/crisv10.c | 2 +- drivers/tty/serial/serial_core.c | 4 ++-- drivers/tty/tty_io.c | 4 ++-- drivers/tty/vt/vt_ioctl.c | 6 +++--- drivers/usb/class/cdc-acm.c | 2 +- drivers/usb/serial/usb-serial.c | 2 +- include/linux/tty.h | 2 +- include/linux/tty_driver.h | 9 ++++----- include/net/irda/ircomm_tty.h | 2 +- net/bluetooth/rfcomm/tty.c | 2 +- net/irda/ircomm/ircomm_tty_ioctl.c | 4 ++-- 41 files changed, 66 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index 5c15fad71ad2..f214e5022472 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -1293,7 +1293,7 @@ static int rs_get_icount(struct tty_struct *tty, return 0; } -static int rs_ioctl(struct tty_struct *tty, struct file * file, +static int rs_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct async_struct * info = tty->driver_data; diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 942b6f2b70a4..c99728f0cd9f 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -2680,7 +2680,7 @@ static int cy_cflags_changed(struct cyclades_port *info, unsigned long arg, * not recognized by the driver, it should return ENOIOCTLCMD. */ static int -cy_ioctl(struct tty_struct *tty, struct file *file, +cy_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct cyclades_port *info = tty->driver_data; diff --git a/drivers/char/epca.c b/drivers/char/epca.c index e5872b59f9cd..7ad3638967ae 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -175,9 +175,9 @@ static unsigned termios2digi_i(struct channel *ch, unsigned); static unsigned termios2digi_c(struct channel *ch, unsigned); static void epcaparam(struct tty_struct *, struct channel *); static void receive_data(struct channel *, struct tty_struct *tty); -static int pc_ioctl(struct tty_struct *, struct file *, +static int pc_ioctl(struct tty_struct *, unsigned int, unsigned long); -static int info_ioctl(struct tty_struct *, struct file *, +static int info_ioctl(struct tty_struct *, unsigned int, unsigned long); static void pc_set_termios(struct tty_struct *, struct ktermios *); static void do_softint(struct work_struct *work); @@ -1919,7 +1919,7 @@ static void receive_data(struct channel *ch, struct tty_struct *tty) tty_schedule_flip(tty); } -static int info_ioctl(struct tty_struct *tty, struct file *file, +static int info_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { switch (cmd) { @@ -2057,7 +2057,7 @@ static int pc_tiocmset(struct tty_struct *tty, return 0; } -static int pc_ioctl(struct tty_struct *tty, struct file *file, +static int pc_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { digiflow_t dflow; diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index d5f866c7c672..ea7a8fb08283 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -173,7 +173,7 @@ static void ip2_flush_chars(PTTY); static int ip2_write_room(PTTY); static int ip2_chars_in_buf(PTTY); static void ip2_flush_buffer(PTTY); -static int ip2_ioctl(PTTY, struct file *, UINT, ULONG); +static int ip2_ioctl(PTTY, UINT, ULONG); static void ip2_set_termios(PTTY, struct ktermios *); static void ip2_set_line_discipline(PTTY); static void ip2_throttle(PTTY); @@ -2127,7 +2127,7 @@ static int ip2_tiocmset(struct tty_struct *tty, /* */ /******************************************************************************/ static int -ip2_ioctl ( PTTY tty, struct file *pFile, UINT cmd, ULONG arg ) +ip2_ioctl ( PTTY tty, UINT cmd, ULONG arg ) { wait_queue_t wait; i2ChanStrPtr pCh = DevTable[tty->index]; diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 60f4d8ae7a49..db1cf9c328d8 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -1167,7 +1167,7 @@ static int isicom_get_serial_info(struct isi_port *port, return 0; } -static int isicom_ioctl(struct tty_struct *tty, struct file *filp, +static int isicom_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct isi_port *port = tty->driver_data; diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 763b58d58255..0b266272cccd 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -603,7 +603,7 @@ static int stli_putchar(struct tty_struct *tty, unsigned char ch); static void stli_flushchars(struct tty_struct *tty); static int stli_writeroom(struct tty_struct *tty); static int stli_charsinbuffer(struct tty_struct *tty); -static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); +static int stli_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static void stli_settermios(struct tty_struct *tty, struct ktermios *old); static void stli_throttle(struct tty_struct *tty); static void stli_unthrottle(struct tty_struct *tty); @@ -1556,7 +1556,7 @@ static int stli_tiocmset(struct tty_struct *tty, sizeof(asysigs_t), 0); } -static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) +static int stli_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct stliport *portp; struct stlibrd *brdp; diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index 9f4cd8968a5a..35b0c38590e6 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -287,7 +287,7 @@ static void moxa_low_water_check(void __iomem *ofsAddr) * TTY operations */ -static int moxa_ioctl(struct tty_struct *tty, struct file *file, +static int moxa_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct moxa_port *ch = tty->driver_data; diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 150a862c4989..d188f378684d 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -1655,7 +1655,7 @@ static int mxser_cflags_changed(struct mxser_port *info, unsigned long arg, return ret; } -static int mxser_ioctl(struct tty_struct *tty, struct file *file, +static int mxser_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct mxser_port *info = tty->driver_data; diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index 1b74c48c4016..513ba12064ea 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -1824,7 +1824,7 @@ static int ntty_tiocgicount(struct tty_struct *tty, return 0; } -static int ntty_ioctl(struct tty_struct *tty, struct file *file, +static int ntty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct port *port = tty->driver_data; diff --git a/drivers/char/pcmcia/ipwireless/tty.c b/drivers/char/pcmcia/ipwireless/tty.c index 748190dfbab0..ef92869502a7 100644 --- a/drivers/char/pcmcia/ipwireless/tty.c +++ b/drivers/char/pcmcia/ipwireless/tty.c @@ -425,7 +425,7 @@ ipw_tiocmset(struct tty_struct *linux_tty, return set_control_lines(tty, set, clear); } -static int ipw_ioctl(struct tty_struct *linux_tty, struct file *file, +static int ipw_ioctl(struct tty_struct *linux_tty, unsigned int cmd, unsigned long arg) { struct ipw_tty *tty = linux_tty->driver_data; @@ -484,7 +484,7 @@ static int ipw_ioctl(struct tty_struct *linux_tty, struct file *file, return tty_perform_flush(linux_tty, arg); } } - return tty_mode_ioctl(linux_tty, file, cmd , arg); + return -ENOIOCTLCMD; } static int add_tty(int j, diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 3666decc6438..602643a40b4b 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -1236,7 +1236,7 @@ static int rc_get_serial_info(struct riscom_port *port, return copy_to_user(retinfo, &tmp, sizeof(tmp)) ? -EFAULT : 0; } -static int rc_ioctl(struct tty_struct *tty, struct file *filp, +static int rc_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct riscom_port *port = tty->driver_data; diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index 36c108811a81..3780da8ad12d 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -1326,7 +1326,7 @@ static int get_version(struct r_port *info, struct rocket_version __user *retver } /* IOCTL call handler into the driver */ -static int rp_ioctl(struct tty_struct *tty, struct file *file, +static int rp_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct r_port *info = tty->driver_data; diff --git a/drivers/char/ser_a2232.c b/drivers/char/ser_a2232.c index 9610861d1f5f..3f47c2ead8e5 100644 --- a/drivers/char/ser_a2232.c +++ b/drivers/char/ser_a2232.c @@ -133,8 +133,8 @@ static void a2232_hungup(void *ptr); /* END GENERIC_SERIAL PROTOTYPES */ /* Functions that the TTY driver struct expects */ -static int a2232_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg); +static int a2232_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg); static void a2232_throttle(struct tty_struct *tty); static void a2232_unthrottle(struct tty_struct *tty); static int a2232_open(struct tty_struct * tty, struct file * filp); @@ -447,7 +447,7 @@ static void a2232_hungup(void *ptr) /*** END OF REAL_DRIVER FUNCTIONS ***/ /*** BEGIN FUNCTIONS EXPECTED BY TTY DRIVER STRUCTS ***/ -static int a2232_ioctl( struct tty_struct *tty, struct file *file, +static int a2232_ioctl( struct tty_struct *tty, unsigned int cmd, unsigned long arg) { return -ENOIOCTLCMD; diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index 89ac542ffff2..674af6933978 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -1492,7 +1492,7 @@ get_default_timeout(struct cyclades_port *info, unsigned long __user * value) } static int -cy_ioctl(struct tty_struct *tty, struct file *file, +cy_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct cyclades_port *info = tty->driver_data; diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index a6b23847e4a7..47e5753f732a 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -1928,7 +1928,7 @@ static int sx_get_serial_info(struct specialix_port *port, } -static int sx_ioctl(struct tty_struct *tty, struct file *filp, +static int sx_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct specialix_port *port = tty->driver_data; diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index c42dbffbed16..4fff5cd3b163 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -1132,14 +1132,13 @@ static int stl_tiocmset(struct tty_struct *tty, return 0; } -static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) +static int stl_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct stlport *portp; int rc; void __user *argp = (void __user *)arg; - pr_debug("stl_ioctl(tty=%p,file=%p,cmd=%x,arg=%lx)\n", tty, file, cmd, - arg); + pr_debug("stl_ioctl(tty=%p,cmd=%x,arg=%lx)\n", tty, cmd, arg); portp = tty->driver_data; if (portp == NULL) diff --git a/drivers/char/sx.c b/drivers/char/sx.c index 342c6ae67da5..1291462bcddb 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -1899,7 +1899,7 @@ static int sx_tiocmset(struct tty_struct *tty, return 0; } -static int sx_ioctl(struct tty_struct *tty, struct file *filp, +static int sx_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { int rc; diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 691e1094c20b..18888d005a0a 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -2962,13 +2962,12 @@ static int msgl_get_icount(struct tty_struct *tty, * Arguments: * * tty pointer to tty instance data - * file pointer to associated file object for device * cmd IOCTL command code * arg command argument/context * * Return Value: 0 if success, otherwise error code */ -static int mgsl_ioctl(struct tty_struct *tty, struct file * file, +static int mgsl_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct mgsl_struct * info = tty->driver_data; diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 04da6d61dc4f..a35dd549a008 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -154,7 +154,7 @@ static void flush_buffer(struct tty_struct *tty); static void tx_hold(struct tty_struct *tty); static void tx_release(struct tty_struct *tty); -static int ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); +static int ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static int chars_in_buffer(struct tty_struct *tty); static void throttle(struct tty_struct * tty); static void unthrottle(struct tty_struct * tty); @@ -1030,13 +1030,12 @@ static void tx_release(struct tty_struct *tty) * Arguments * * tty pointer to tty instance data - * file pointer to associated file object for device * cmd IOCTL command code * arg command argument/context * * Return 0 if success, otherwise error code */ -static int ioctl(struct tty_struct *tty, struct file *file, +static int ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct slgt_info *info = tty->driver_data; @@ -1200,7 +1199,7 @@ static long set_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *ne return 0; } -static long slgt_compat_ioctl(struct tty_struct *tty, struct file *file, +static long slgt_compat_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct slgt_info *info = tty->driver_data; @@ -1239,7 +1238,7 @@ static long slgt_compat_ioctl(struct tty_struct *tty, struct file *file, case MGSL_IOCSIF: case MGSL_IOCSXSYNC: case MGSL_IOCSXCTRL: - rc = ioctl(tty, file, cmd, arg); + rc = ioctl(tty, cmd, arg); break; } diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 1f9de97e8cfc..327343694473 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -520,7 +520,7 @@ static void flush_buffer(struct tty_struct *tty); static void tx_hold(struct tty_struct *tty); static void tx_release(struct tty_struct *tty); -static int ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); +static int ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static int chars_in_buffer(struct tty_struct *tty); static void throttle(struct tty_struct * tty); static void unthrottle(struct tty_struct * tty); @@ -1248,13 +1248,12 @@ static void tx_release(struct tty_struct *tty) * Arguments: * * tty pointer to tty instance data - * file pointer to associated file object for device * cmd IOCTL command code * arg command argument/context * * Return Value: 0 if success, otherwise error code */ -static int ioctl(struct tty_struct *tty, struct file *file, +static int ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { SLMP_INFO *info = tty->driver_data; diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index c40c1612c8a7..a1f68af4ccf4 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -144,7 +144,7 @@ static int tpk_write_room(struct tty_struct *tty) /* * TTY operations ioctl function. */ -static int tpk_ioctl(struct tty_struct *tty, struct file *file, +static int tpk_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct ttyprintk_port *tpkp = tty->driver_data; diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c index 12de1202d22c..96838640f575 100644 --- a/drivers/char/vme_scc.c +++ b/drivers/char/vme_scc.c @@ -75,7 +75,7 @@ static void scc_hungup(void *ptr); static void scc_close(void *ptr); static int scc_chars_in_buffer(void * ptr); static int scc_open(struct tty_struct * tty, struct file * filp); -static int scc_ioctl(struct tty_struct * tty, struct file * filp, +static int scc_ioctl(struct tty_struct * tty, unsigned int cmd, unsigned long arg); static void scc_throttle(struct tty_struct *tty); static void scc_unthrottle(struct tty_struct *tty); @@ -1046,7 +1046,7 @@ static void scc_unthrottle (struct tty_struct * tty) } -static int scc_ioctl(struct tty_struct *tty, struct file *file, +static int scc_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { return -ENOIOCTLCMD; diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index f80a7c48a35f..0d7088367038 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -1219,16 +1219,10 @@ static int capinc_tty_chars_in_buffer(struct tty_struct *tty) return mp->outbytes; } -static int capinc_tty_ioctl(struct tty_struct *tty, struct file * file, +static int capinc_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { - int error = 0; - switch (cmd) { - default: - error = n_tty_ioctl_helper(tty, file, cmd, arg); - break; - } - return error; + return -ENOIOCTLCMD; } static void capinc_tty_set_termios(struct tty_struct *tty, struct ktermios * old) diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c index 9b2bb491c614..59de638225fe 100644 --- a/drivers/isdn/gigaset/interface.c +++ b/drivers/isdn/gigaset/interface.c @@ -115,7 +115,7 @@ static int if_config(struct cardstate *cs, int *arg) static int if_open(struct tty_struct *tty, struct file *filp); static void if_close(struct tty_struct *tty, struct file *filp); -static int if_ioctl(struct tty_struct *tty, struct file *file, +static int if_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static int if_write_room(struct tty_struct *tty); static int if_chars_in_buffer(struct tty_struct *tty); @@ -205,7 +205,7 @@ static void if_close(struct tty_struct *tty, struct file *filp) module_put(cs->driver->owner); } -static int if_ioctl(struct tty_struct *tty, struct file *file, +static int if_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct cardstate *cs; diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 0341c69eb152..3d88f15aa218 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1413,8 +1413,7 @@ isdn_tty_tiocmset(struct tty_struct *tty, } static int -isdn_tty_ioctl(struct tty_struct *tty, struct file *file, - uint cmd, ulong arg) +isdn_tty_ioctl(struct tty_struct *tty, uint cmd, ulong arg) { modem_info *info = (modem_info *) tty->driver_data; int retval; diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 956e1d6e72a5..2ad58a0377b7 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -1730,7 +1730,7 @@ static int hso_serial_tiocmset(struct tty_struct *tty, USB_CTRL_SET_TIMEOUT); } -static int hso_serial_ioctl(struct tty_struct *tty, struct file *file, +static int hso_serial_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct hso_serial *serial = get_serial_by_tty(tty); diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 88477d16b8b7..50f3ffd610b7 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2671,7 +2671,7 @@ static int gsmtty_tiocmset(struct tty_struct *tty, } -static int gsmtty_ioctl(struct tty_struct *tty, struct file *filp, +static int gsmtty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { return -ENOIOCTLCMD; diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 923a48585501..c88029af84dd 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -334,7 +334,7 @@ free_mem_out: return -ENOMEM; } -static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file, +static int pty_bsd_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { switch (cmd) { @@ -489,7 +489,7 @@ static struct ctl_table pty_root_table[] = { }; -static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file, +static int pty_unix98_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { switch (cmd) { diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c index a9d99856c892..1de0e8d4bde1 100644 --- a/drivers/tty/serial/68328serial.c +++ b/drivers/tty/serial/68328serial.c @@ -945,7 +945,7 @@ static void send_break(struct m68k_serial * info, unsigned int duration) local_irq_restore(flags); } -static int rs_ioctl(struct tty_struct *tty, struct file * file, +static int rs_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { int error; diff --git a/drivers/tty/serial/68360serial.c b/drivers/tty/serial/68360serial.c index 217fe1c299e0..514a356d8d64 100644 --- a/drivers/tty/serial/68360serial.c +++ b/drivers/tty/serial/68360serial.c @@ -1405,7 +1405,7 @@ static int rs_360_get_icount(struct tty_struct *tty, return 0; } -static int rs_360_ioctl(struct tty_struct *tty, struct file * file, +static int rs_360_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { int error; diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index b9fcd0bda60c..225123b37f19 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3647,7 +3647,7 @@ rs_tiocmget(struct tty_struct *tty) static int -rs_ioctl(struct tty_struct *tty, struct file * file, +rs_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct e100_serial * info = (struct e100_serial *)tty->driver_data; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 623d6bd911d7..733fe8e73f0f 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1099,7 +1099,7 @@ static int uart_get_icount(struct tty_struct *tty, * Called via sys_ioctl. We can use spin_lock_irq() here. */ static int -uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, +uart_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct uart_state *state = tty->driver_data; @@ -1152,7 +1152,7 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, mutex_lock(&port->mutex); - if (tty_hung_up_p(filp)) { + if (tty->flags & (1 << TTY_IO_ERROR)) { ret = -EIO; goto out_up; } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 83af24ca1e5e..20a862a2a0c2 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2676,7 +2676,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } if (tty->ops->ioctl) { - retval = (tty->ops->ioctl)(tty, file, cmd, arg); + retval = (tty->ops->ioctl)(tty, cmd, arg); if (retval != -ENOIOCTLCMD) return retval; } @@ -2704,7 +2704,7 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd, return -EINVAL; if (tty->ops->compat_ioctl) { - retval = (tty->ops->compat_ioctl)(tty, file, cmd, arg); + retval = (tty->ops->compat_ioctl)(tty, cmd, arg); if (retval != -ENOIOCTLCMD) return retval; } diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 9e9a901442a3..b64804965316 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -495,7 +495,7 @@ do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, int perm, struct vc_ * We handle the console-specific ioctl's here. We allow the * capability to modify any console, not just the fg_console. */ -int vt_ioctl(struct tty_struct *tty, struct file * file, +int vt_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct vc_data *vc = tty->driver_data; @@ -1495,7 +1495,7 @@ compat_unimap_ioctl(unsigned int cmd, struct compat_unimapdesc __user *user_ud, return 0; } -long vt_compat_ioctl(struct tty_struct *tty, struct file * file, +long vt_compat_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct vc_data *vc = tty->driver_data; @@ -1581,7 +1581,7 @@ out: fallback: tty_unlock(); - return vt_ioctl(tty, file, cmd, arg); + return vt_ioctl(tty, cmd, arg); } diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index e9a26fbd079c..8d994a8bdc90 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -813,7 +813,7 @@ static int acm_tty_tiocmset(struct tty_struct *tty, return acm_set_control(acm, acm->ctrlout = newctrl); } -static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, +static int acm_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct acm *acm = tty->driver_data; diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index b1110e136c33..a72575349744 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -406,7 +406,7 @@ static void serial_unthrottle(struct tty_struct *tty) port->serial->type->unthrottle(tty); } -static int serial_ioctl(struct tty_struct *tty, struct file *file, +static int serial_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; diff --git a/include/linux/tty.h b/include/linux/tty.h index 54e4eaaa0561..483df15146d2 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -584,7 +584,7 @@ extern int pcxe_open(struct tty_struct *tty, struct file *filp); /* vt.c */ -extern int vt_ioctl(struct tty_struct *tty, struct file *file, +extern int vt_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); extern long vt_compat_ioctl(struct tty_struct *tty, struct file * file, diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 5dabaa2e6da3..9deeac855240 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -98,8 +98,7 @@ * * Note: Do not call this function directly, call tty_write_room * - * int (*ioctl)(struct tty_struct *tty, struct file * file, - * unsigned int cmd, unsigned long arg); + * int (*ioctl)(struct tty_struct *tty, unsigned int cmd, unsigned long arg); * * This routine allows the tty driver to implement * device-specific ioctls. If the ioctl number passed in cmd @@ -107,7 +106,7 @@ * * Optional * - * long (*compat_ioctl)(struct tty_struct *tty, struct file * file, + * long (*compat_ioctl)(struct tty_struct *tty,, * unsigned int cmd, unsigned long arg); * * implement ioctl processing for 32 bit process on 64 bit system @@ -256,9 +255,9 @@ struct tty_operations { void (*flush_chars)(struct tty_struct *tty); int (*write_room)(struct tty_struct *tty); int (*chars_in_buffer)(struct tty_struct *tty); - int (*ioctl)(struct tty_struct *tty, struct file * file, + int (*ioctl)(struct tty_struct *tty, unsigned int cmd, unsigned long arg); - long (*compat_ioctl)(struct tty_struct *tty, struct file * file, + long (*compat_ioctl)(struct tty_struct *tty, unsigned int cmd, unsigned long arg); void (*set_termios)(struct tty_struct *tty, struct ktermios * old); void (*throttle)(struct tty_struct * tty); diff --git a/include/net/irda/ircomm_tty.h b/include/net/irda/ircomm_tty.h index 980ccb66e1b4..59ba38bc400f 100644 --- a/include/net/irda/ircomm_tty.h +++ b/include/net/irda/ircomm_tty.h @@ -123,7 +123,7 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self); extern int ircomm_tty_tiocmget(struct tty_struct *tty); extern int ircomm_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); -extern int ircomm_tty_ioctl(struct tty_struct *tty, struct file *file, +extern int ircomm_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); extern void ircomm_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios); diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 8e78e7447726..b1805ff95415 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -830,7 +830,7 @@ static int rfcomm_tty_write_room(struct tty_struct *tty) return room; } -static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg) +static int rfcomm_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { BT_DBG("tty %p cmd 0x%02x", tty, cmd); diff --git a/net/irda/ircomm/ircomm_tty_ioctl.c b/net/irda/ircomm/ircomm_tty_ioctl.c index 5e0e718c930a..77c5e6499f8f 100644 --- a/net/irda/ircomm/ircomm_tty_ioctl.c +++ b/net/irda/ircomm/ircomm_tty_ioctl.c @@ -365,12 +365,12 @@ static int ircomm_tty_set_serial_info(struct ircomm_tty_cb *self, } /* - * Function ircomm_tty_ioctl (tty, file, cmd, arg) + * Function ircomm_tty_ioctl (tty, cmd, arg) * * * */ -int ircomm_tty_ioctl(struct tty_struct *tty, struct file *file, +int ircomm_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; -- cgit v1.2.3 From 94c2273d6c1b65eaaf2a6446c7147bdf6e5ae924 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 17 Feb 2011 12:02:51 -0800 Subject: tty: fix build error in vt_ioctl.c if CONFIG_COMPAT is enabled This was caused by the previous patch to remove the file pointer from the tty ioctl handler. Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- include/linux/tty.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/tty.h b/include/linux/tty.h index 483df15146d2..ef1e0123573b 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -587,7 +587,7 @@ extern int pcxe_open(struct tty_struct *tty, struct file *filp); extern int vt_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); -extern long vt_compat_ioctl(struct tty_struct *tty, struct file * file, +extern long vt_compat_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); /* tty_mutex.c */ -- cgit v1.2.3 From 8d075b199b9a66ad90296f898f1f15c0ae1511b8 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 14 Feb 2011 16:27:53 +0000 Subject: tty: add a helper for setting termios data from kernel side This basically encapsulates the small bit of locking knowledge needed. While we are at it make sure we blow up on any more abusers and unsafe misuses of ioctl for this kind of stuff. We change the function to return an argument as at some point it needs to honour the POSIX 'I asked for changes but got none of them' error reporting corner case. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ioctl.c | 14 +++++++++----- include/linux/tty.h | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 0c1889971459..1a1135d580a2 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -486,7 +486,7 @@ int tty_termios_hw_change(struct ktermios *a, struct ktermios *b) EXPORT_SYMBOL(tty_termios_hw_change); /** - * change_termios - update termios values + * tty_set_termios - update termios values * @tty: tty to update * @new_termios: desired new value * @@ -497,7 +497,7 @@ EXPORT_SYMBOL(tty_termios_hw_change); * Locking: termios_mutex */ -static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) +int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) { struct ktermios old_termios; struct tty_ldisc *ld; @@ -553,7 +553,9 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) tty_ldisc_deref(ld); } mutex_unlock(&tty->termios_mutex); + return 0; } +EXPORT_SYMBOL_GPL(tty_set_termios); /** * set_termios - set termios values for a tty @@ -562,7 +564,7 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) * @opt: option information * * Helper function to prepare termios data and run necessary other - * functions before using change_termios to do the actual changes. + * functions before using tty_set_termios to do the actual changes. * * Locking: * Called functions take ldisc and termios_mutex locks @@ -620,7 +622,7 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) return -EINTR; } - change_termios(tty, &tmp_termios); + tty_set_termios(tty, &tmp_termios); /* FIXME: Arguably if tmp_termios == tty->termios AND the actual requested termios was not tmp_termios then we may @@ -797,7 +799,7 @@ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) termios.c_ospeed); #endif mutex_unlock(&tty->termios_mutex); - change_termios(tty, &termios); + tty_set_termios(tty, &termios); return 0; } #endif @@ -951,6 +953,8 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, int ret = 0; struct ktermios kterm; + BUG_ON(file == NULL); + if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) real_tty = tty->link; diff --git a/include/linux/tty.h b/include/linux/tty.h index ef1e0123573b..4e53d4641b38 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -448,6 +448,7 @@ extern void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud); extern void tty_termios_copy_hw(struct ktermios *new, struct ktermios *old); extern int tty_termios_hw_change(struct ktermios *a, struct ktermios *b); +extern int tty_set_termios(struct tty_struct *tty, struct ktermios *kt); extern struct tty_ldisc *tty_ldisc_ref(struct tty_struct *); extern void tty_ldisc_deref(struct tty_ldisc *); -- cgit v1.2.3 From 3c95c985fa91ecf6a0e29622bbdd13dcfc5ce9f1 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Thu, 17 Feb 2011 18:39:28 +0100 Subject: tty: add TIOCVHANGUP to allow clean tty shutdown of all ttys This is useful for system management software so that it can kick off things like gettys and everything that's started from a tty, before we reuse it from/for something else or shut it down. Without this ioctl it would have to temporarily become the owner of the tty, then call vhangup() and then give it up again. Cc: Lennart Poettering Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- arch/alpha/include/asm/ioctls.h | 1 + arch/mips/include/asm/ioctls.h | 1 + arch/parisc/include/asm/ioctls.h | 1 + arch/powerpc/include/asm/ioctls.h | 1 + arch/sh/include/asm/ioctls.h | 1 + arch/sparc/include/asm/ioctls.h | 1 + arch/xtensa/include/asm/ioctls.h | 1 + drivers/tty/tty_io.c | 5 +++++ include/asm-generic/ioctls.h | 1 + 9 files changed, 13 insertions(+) (limited to 'include') diff --git a/arch/alpha/include/asm/ioctls.h b/arch/alpha/include/asm/ioctls.h index 034b6cf5d9f3..80e1cee90f1f 100644 --- a/arch/alpha/include/asm/ioctls.h +++ b/arch/alpha/include/asm/ioctls.h @@ -94,6 +94,7 @@ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ +#define TIOCVHANGUP 0x5437 #define TIOCSERCONFIG 0x5453 #define TIOCSERGWILD 0x5454 diff --git a/arch/mips/include/asm/ioctls.h b/arch/mips/include/asm/ioctls.h index d967b8997626..92403c3d6007 100644 --- a/arch/mips/include/asm/ioctls.h +++ b/arch/mips/include/asm/ioctls.h @@ -85,6 +85,7 @@ #define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */ #define TIOCGDEV _IOR('T', 0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T', 0x36, int) /* Generate signal on Pty slave */ +#define TIOCVHANGUP 0x5437 /* I hope the range from 0x5480 on is free ... */ #define TIOCSCTTY 0x5480 /* become controlling tty */ diff --git a/arch/parisc/include/asm/ioctls.h b/arch/parisc/include/asm/ioctls.h index 6ba80d03623a..054ec06f9e23 100644 --- a/arch/parisc/include/asm/ioctls.h +++ b/arch/parisc/include/asm/ioctls.h @@ -54,6 +54,7 @@ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ #define TIOCGDEV _IOR('T',0x32, int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ +#define TIOCVHANGUP 0x5437 #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ #define FIOCLEX 0x5451 diff --git a/arch/powerpc/include/asm/ioctls.h b/arch/powerpc/include/asm/ioctls.h index c7dc17cf84f1..e9b78870aaab 100644 --- a/arch/powerpc/include/asm/ioctls.h +++ b/arch/powerpc/include/asm/ioctls.h @@ -96,6 +96,7 @@ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ +#define TIOCVHANGUP 0x5437 #define TIOCSERCONFIG 0x5453 #define TIOCSERGWILD 0x5454 diff --git a/arch/sh/include/asm/ioctls.h b/arch/sh/include/asm/ioctls.h index 84e85a792638..a6769f352bf6 100644 --- a/arch/sh/include/asm/ioctls.h +++ b/arch/sh/include/asm/ioctls.h @@ -87,6 +87,7 @@ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ +#define TIOCVHANGUP _IO('T', 0x37) #define TIOCSERCONFIG _IO('T', 83) /* 0x5453 */ #define TIOCSERGWILD _IOR('T', 84, int) /* 0x5454 */ diff --git a/arch/sparc/include/asm/ioctls.h b/arch/sparc/include/asm/ioctls.h index ed3807b96bb5..28d0c8b02cc3 100644 --- a/arch/sparc/include/asm/ioctls.h +++ b/arch/sparc/include/asm/ioctls.h @@ -20,6 +20,7 @@ #define TCSETSW2 _IOW('T', 14, struct termios2) #define TCSETSF2 _IOW('T', 15, struct termios2) #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ +#define TIOCVHANGUP _IO('T', 0x37) /* Note that all the ioctls that are not available in Linux have a * double underscore on the front to: a) avoid some programs to diff --git a/arch/xtensa/include/asm/ioctls.h b/arch/xtensa/include/asm/ioctls.h index ccf1800f0b0c..fd1d1369a407 100644 --- a/arch/xtensa/include/asm/ioctls.h +++ b/arch/xtensa/include/asm/ioctls.h @@ -100,6 +100,7 @@ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ +#define TIOCVHANGUP _IO('T', 0x37) #define TIOCSERCONFIG _IO('T', 83) #define TIOCSERGWILD _IOR('T', 84, int) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 20a862a2a0c2..8ef2d69470ec 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2626,6 +2626,11 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return put_user(tty->ldisc->ops->num, (int __user *)p); case TIOCSETD: return tiocsetd(tty, p); + case TIOCVHANGUP: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + tty_vhangup(tty); + return 0; case TIOCGDEV: { unsigned int ret = new_encode_dev(tty_devnum(real_tty)); diff --git a/include/asm-generic/ioctls.h b/include/asm-generic/ioctls.h index 3f3f2d189fb8..199975fac395 100644 --- a/include/asm-generic/ioctls.h +++ b/include/asm-generic/ioctls.h @@ -73,6 +73,7 @@ #define TCSETXF 0x5434 #define TCSETXW 0x5435 #define TIOCSIG _IOW('T', 0x36, int) /* pty: generate signal */ +#define TIOCVHANGUP 0x5437 #define FIONCLEX 0x5450 #define FIOCLEX 0x5451 -- cgit v1.2.3 From 212b573f5552c60265da721ff9ce32e3462a2cdd Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Tue, 15 Feb 2011 16:59:16 +0000 Subject: ethtool: enable GSO and GRO by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Mirosław Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 +++ net/core/dev.c | 18 ++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d08ef6538579..168e3ad14daf 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -984,6 +984,9 @@ struct net_device { NETIF_F_SG | NETIF_F_HIGHDMA | \ NETIF_F_FRAGLIST) + /* changeable features with no special hardware requirements */ +#define NETIF_F_SOFT_FEATURES (NETIF_F_GSO | NETIF_F_GRO) + /* Interface index. Unique device identifier */ int ifindex; int iflink; diff --git a/net/core/dev.c b/net/core/dev.c index 4580460ebecd..8686f6ffe7f0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5274,6 +5274,12 @@ u32 netdev_fix_features(struct net_device *dev, u32 features) features &= ~NETIF_F_TSO; } + /* Software GSO depends on SG. */ + if ((features & NETIF_F_GSO) && !(features & NETIF_F_SG)) { + netdev_info(dev, "Dropping NETIF_F_GSO since no SG feature.\n"); + features &= ~NETIF_F_GSO; + } + /* UFO needs SG and checksumming */ if (features & NETIF_F_UFO) { /* maybe split UFO into V4 and V6? */ @@ -5430,11 +5436,15 @@ int register_netdevice(struct net_device *dev) if (dev->iflink == -1) dev->iflink = dev->ifindex; - dev->features = netdev_fix_features(dev, dev->features); + /* Enable software offloads by default - will be stripped in + * netdev_fix_features() if not supported. */ + dev->features |= NETIF_F_SOFT_FEATURES; - /* Enable software GSO if SG is supported. */ - if (dev->features & NETIF_F_SG) - dev->features |= NETIF_F_GSO; + /* Avoid warning from netdev_fix_features() for GSO without SG */ + if (!(dev->features & NETIF_F_SG)) + dev->features &= ~NETIF_F_GSO; + + dev->features = netdev_fix_features(dev, dev->features); /* Enable GRO and NETIF_F_HIGHDMA for vlans by default, * vlan_dev_init() will do the dev->features check, so these features -- cgit v1.2.3 From 0a417704777ed29d0e8c72b7274a328e61248e75 Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Tue, 15 Feb 2011 16:59:17 +0000 Subject: ethtool: factorize get/set_one_feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to enable GRO even if RX csum is disabled. GRO will not be used for packets without hardware checksum anyway. Signed-off-by: Michał Mirosław Signed-off-by: David S. Miller --- include/linux/netdevice.h | 6 + net/core/ethtool.c | 274 ++++++++++++++++++++++------------------------ 2 files changed, 138 insertions(+), 142 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 168e3ad14daf..dede3fdbb4be 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -976,6 +976,12 @@ struct net_device { #define NETIF_F_V6_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IPV6_CSUM) #define NETIF_F_ALL_CSUM (NETIF_F_V4_CSUM | NETIF_F_V6_CSUM) +#define NETIF_F_ALL_TSO (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN) + +#define NETIF_F_ALL_TX_OFFLOADS (NETIF_F_ALL_CSUM | NETIF_F_SG | \ + NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \ + NETIF_F_SCTP_CSUM | NETIF_F_FCOE_CRC) + /* * If one device supports one of these features, then enable them * for all in netdev_increment_features. diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 85aaeab862a8..c3fb8f90de6d 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -191,6 +191,109 @@ static void __ethtool_get_strings(struct net_device *dev, ops->get_strings(dev, stringset, data); } +static u32 ethtool_get_feature_mask(u32 eth_cmd) +{ + /* feature masks of legacy discrete ethtool ops */ + + switch (eth_cmd) { + case ETHTOOL_GTXCSUM: + case ETHTOOL_STXCSUM: + return NETIF_F_ALL_CSUM | NETIF_F_SCTP_CSUM; + case ETHTOOL_GSG: + case ETHTOOL_SSG: + return NETIF_F_SG; + case ETHTOOL_GTSO: + case ETHTOOL_STSO: + return NETIF_F_ALL_TSO; + case ETHTOOL_GUFO: + case ETHTOOL_SUFO: + return NETIF_F_UFO; + case ETHTOOL_GGSO: + case ETHTOOL_SGSO: + return NETIF_F_GSO; + case ETHTOOL_GGRO: + case ETHTOOL_SGRO: + return NETIF_F_GRO; + default: + BUG(); + } +} + +static void *__ethtool_get_one_feature_actor(struct net_device *dev, u32 ethcmd) +{ + const struct ethtool_ops *ops = dev->ethtool_ops; + + if (!ops) + return NULL; + + switch (ethcmd) { + case ETHTOOL_GTXCSUM: + return ops->get_tx_csum; + case ETHTOOL_SSG: + return ops->get_sg; + case ETHTOOL_STSO: + return ops->get_tso; + case ETHTOOL_SUFO: + return ops->get_ufo; + default: + return NULL; + } +} + +static int ethtool_get_one_feature(struct net_device *dev, + char __user *useraddr, u32 ethcmd) +{ + struct ethtool_value edata = { + .cmd = ethcmd, + .data = !!(dev->features & ethtool_get_feature_mask(ethcmd)), + }; + u32 (*actor)(struct net_device *); + + actor = __ethtool_get_one_feature_actor(dev, ethcmd); + if (actor) + edata.data = actor(dev); + + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; +} + +static int __ethtool_set_tx_csum(struct net_device *dev, u32 data); +static int __ethtool_set_sg(struct net_device *dev, u32 data); +static int __ethtool_set_tso(struct net_device *dev, u32 data); +static int __ethtool_set_ufo(struct net_device *dev, u32 data); + +static int ethtool_set_one_feature(struct net_device *dev, + void __user *useraddr, u32 ethcmd) +{ + struct ethtool_value edata; + u32 mask; + + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + + switch (ethcmd) { + case ETHTOOL_STXCSUM: + return __ethtool_set_tx_csum(dev, edata.data); + case ETHTOOL_SSG: + return __ethtool_set_sg(dev, edata.data); + case ETHTOOL_STSO: + return __ethtool_set_tso(dev, edata.data); + case ETHTOOL_SUFO: + return __ethtool_set_ufo(dev, edata.data); + case ETHTOOL_SGSO: + case ETHTOOL_SGRO: + mask = ethtool_get_feature_mask(ethcmd); + if (edata.data) + dev->features |= mask; + else + dev->features &= ~mask; + return 0; + default: + return -EOPNOTSUPP; + } +} + static int ethtool_get_settings(struct net_device *dev, void __user *useraddr) { struct ethtool_cmd cmd = { .cmd = ETHTOOL_GSET }; @@ -1107,6 +1210,9 @@ static int __ethtool_set_sg(struct net_device *dev, u32 data) { int err; + if (data && !(dev->features & NETIF_F_ALL_CSUM)) + return -EINVAL; + if (!data && dev->ethtool_ops->set_tso) { err = dev->ethtool_ops->set_tso(dev, 0); if (err) @@ -1121,24 +1227,20 @@ static int __ethtool_set_sg(struct net_device *dev, u32 data) return dev->ethtool_ops->set_sg(dev, data); } -static int ethtool_set_tx_csum(struct net_device *dev, char __user *useraddr) +static int __ethtool_set_tx_csum(struct net_device *dev, u32 data) { - struct ethtool_value edata; int err; if (!dev->ethtool_ops->set_tx_csum) return -EOPNOTSUPP; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - - if (!edata.data && dev->ethtool_ops->set_sg) { + if (!data && dev->ethtool_ops->set_sg) { err = __ethtool_set_sg(dev, 0); if (err) return err; } - return dev->ethtool_ops->set_tx_csum(dev, edata.data); + return dev->ethtool_ops->set_tx_csum(dev, data); } static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr) @@ -1157,108 +1259,28 @@ static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr) return dev->ethtool_ops->set_rx_csum(dev, edata.data); } -static int ethtool_set_sg(struct net_device *dev, char __user *useraddr) -{ - struct ethtool_value edata; - - if (!dev->ethtool_ops->set_sg) - return -EOPNOTSUPP; - - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - - if (edata.data && - !(dev->features & NETIF_F_ALL_CSUM)) - return -EINVAL; - - return __ethtool_set_sg(dev, edata.data); -} - -static int ethtool_set_tso(struct net_device *dev, char __user *useraddr) +static int __ethtool_set_tso(struct net_device *dev, u32 data) { - struct ethtool_value edata; - if (!dev->ethtool_ops->set_tso) return -EOPNOTSUPP; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - - if (edata.data && !(dev->features & NETIF_F_SG)) + if (data && !(dev->features & NETIF_F_SG)) return -EINVAL; - return dev->ethtool_ops->set_tso(dev, edata.data); + return dev->ethtool_ops->set_tso(dev, data); } -static int ethtool_set_ufo(struct net_device *dev, char __user *useraddr) +static int __ethtool_set_ufo(struct net_device *dev, u32 data) { - struct ethtool_value edata; - if (!dev->ethtool_ops->set_ufo) return -EOPNOTSUPP; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - if (edata.data && !(dev->features & NETIF_F_SG)) + if (data && !(dev->features & NETIF_F_SG)) return -EINVAL; - if (edata.data && !((dev->features & NETIF_F_GEN_CSUM) || + if (data && !((dev->features & NETIF_F_GEN_CSUM) || (dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM)) == (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) return -EINVAL; - return dev->ethtool_ops->set_ufo(dev, edata.data); -} - -static int ethtool_get_gso(struct net_device *dev, char __user *useraddr) -{ - struct ethtool_value edata = { ETHTOOL_GGSO }; - - edata.data = dev->features & NETIF_F_GSO; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; -} - -static int ethtool_set_gso(struct net_device *dev, char __user *useraddr) -{ - struct ethtool_value edata; - - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - if (edata.data) - dev->features |= NETIF_F_GSO; - else - dev->features &= ~NETIF_F_GSO; - return 0; -} - -static int ethtool_get_gro(struct net_device *dev, char __user *useraddr) -{ - struct ethtool_value edata = { ETHTOOL_GGRO }; - - edata.data = dev->features & NETIF_F_GRO; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; -} - -static int ethtool_set_gro(struct net_device *dev, char __user *useraddr) -{ - struct ethtool_value edata; - - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - - if (edata.data) { - u32 rxcsum = dev->ethtool_ops->get_rx_csum ? - dev->ethtool_ops->get_rx_csum(dev) : - ethtool_op_get_rx_csum(dev); - - if (!rxcsum) - return -EINVAL; - dev->features |= NETIF_F_GRO; - } else - dev->features &= ~NETIF_F_GRO; - - return 0; + return dev->ethtool_ops->set_ufo(dev, data); } static int ethtool_self_test(struct net_device *dev, char __user *useraddr) @@ -1590,33 +1612,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_SRXCSUM: rc = ethtool_set_rx_csum(dev, useraddr); break; - case ETHTOOL_GTXCSUM: - rc = ethtool_get_value(dev, useraddr, ethcmd, - (dev->ethtool_ops->get_tx_csum ? - dev->ethtool_ops->get_tx_csum : - ethtool_op_get_tx_csum)); - break; - case ETHTOOL_STXCSUM: - rc = ethtool_set_tx_csum(dev, useraddr); - break; - case ETHTOOL_GSG: - rc = ethtool_get_value(dev, useraddr, ethcmd, - (dev->ethtool_ops->get_sg ? - dev->ethtool_ops->get_sg : - ethtool_op_get_sg)); - break; - case ETHTOOL_SSG: - rc = ethtool_set_sg(dev, useraddr); - break; - case ETHTOOL_GTSO: - rc = ethtool_get_value(dev, useraddr, ethcmd, - (dev->ethtool_ops->get_tso ? - dev->ethtool_ops->get_tso : - ethtool_op_get_tso)); - break; - case ETHTOOL_STSO: - rc = ethtool_set_tso(dev, useraddr); - break; case ETHTOOL_TEST: rc = ethtool_self_test(dev, useraddr); break; @@ -1632,21 +1627,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_GPERMADDR: rc = ethtool_get_perm_addr(dev, useraddr); break; - case ETHTOOL_GUFO: - rc = ethtool_get_value(dev, useraddr, ethcmd, - (dev->ethtool_ops->get_ufo ? - dev->ethtool_ops->get_ufo : - ethtool_op_get_ufo)); - break; - case ETHTOOL_SUFO: - rc = ethtool_set_ufo(dev, useraddr); - break; - case ETHTOOL_GGSO: - rc = ethtool_get_gso(dev, useraddr); - break; - case ETHTOOL_SGSO: - rc = ethtool_set_gso(dev, useraddr); - break; case ETHTOOL_GFLAGS: rc = ethtool_get_value(dev, useraddr, ethcmd, (dev->ethtool_ops->get_flags ? @@ -1677,12 +1657,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_SRXCLSRLINS: rc = ethtool_set_rxnfc(dev, ethcmd, useraddr); break; - case ETHTOOL_GGRO: - rc = ethtool_get_gro(dev, useraddr); - break; - case ETHTOOL_SGRO: - rc = ethtool_set_gro(dev, useraddr); - break; case ETHTOOL_FLASHDEV: rc = ethtool_flash_device(dev, useraddr); break; @@ -1704,6 +1678,22 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_SRXFHINDIR: rc = ethtool_set_rxfh_indir(dev, useraddr); break; + case ETHTOOL_GTXCSUM: + case ETHTOOL_GSG: + case ETHTOOL_GTSO: + case ETHTOOL_GUFO: + case ETHTOOL_GGSO: + case ETHTOOL_GGRO: + rc = ethtool_get_one_feature(dev, useraddr, ethcmd); + break; + case ETHTOOL_STXCSUM: + case ETHTOOL_SSG: + case ETHTOOL_STSO: + case ETHTOOL_SUFO: + case ETHTOOL_SGSO: + case ETHTOOL_SGRO: + rc = ethtool_set_one_feature(dev, useraddr, ethcmd); + break; default: rc = -EOPNOTSUPP; } -- cgit v1.2.3 From 5455c6998d34dc983a8693500e4dffefc3682dc5 Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Tue, 15 Feb 2011 16:59:17 +0000 Subject: net: Introduce new feature setting ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This introduces a new framework to handle device features setting. It consists of: - new fields in struct net_device: + hw_features - features that hw/driver supports toggling + wanted_features - features that user wants enabled, when possible - new netdev_ops: + feat = ndo_fix_features(dev, feat) - API checking constraints for enabling features or their combinations + ndo_set_features(dev) - API updating hardware state to match changed dev->features - new ethtool commands: + ETHTOOL_GFEATURES/ETHTOOL_SFEATURES: get/set dev->wanted_features and trigger device reconfiguration if resulting dev->features changed + ETHTOOL_GSTRINGS(ETH_SS_FEATURES): get feature bits names (meaning) Signed-off-by: Michał Mirosław Signed-off-by: David S. Miller --- include/linux/ethtool.h | 85 +++++++++++++++++++++++++++++++ include/linux/netdevice.h | 37 +++++++++++++- net/core/dev.c | 46 ++++++++++++++--- net/core/ethtool.c | 125 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 283 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 1908929204a9..806e716bb4fb 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -251,6 +251,7 @@ enum ethtool_stringset { ETH_SS_STATS, ETH_SS_PRIV_FLAGS, ETH_SS_NTUPLE_FILTERS, + ETH_SS_FEATURES, }; /* for passing string sets for data tagging */ @@ -523,6 +524,87 @@ struct ethtool_flash { char data[ETHTOOL_FLASH_MAX_FILENAME]; }; +/* for returning and changing feature sets */ + +/** + * struct ethtool_get_features_block - block with state of 32 features + * @available: mask of changeable features + * @requested: mask of features requested to be enabled if possible + * @active: mask of currently enabled features + * @never_changed: mask of features not changeable for any device + */ +struct ethtool_get_features_block { + __u32 available; + __u32 requested; + __u32 active; + __u32 never_changed; +}; + +/** + * struct ethtool_gfeatures - command to get state of device's features + * @cmd: command number = %ETHTOOL_GFEATURES + * @size: in: number of elements in the features[] array; + * out: number of elements in features[] needed to hold all features + * @features: state of features + */ +struct ethtool_gfeatures { + __u32 cmd; + __u32 size; + struct ethtool_get_features_block features[0]; +}; + +/** + * struct ethtool_set_features_block - block with request for 32 features + * @valid: mask of features to be changed + * @requested: values of features to be changed + */ +struct ethtool_set_features_block { + __u32 valid; + __u32 requested; +}; + +/** + * struct ethtool_sfeatures - command to request change in device's features + * @cmd: command number = %ETHTOOL_SFEATURES + * @size: array size of the features[] array + * @features: feature change masks + */ +struct ethtool_sfeatures { + __u32 cmd; + __u32 size; + struct ethtool_set_features_block features[0]; +}; + +/* + * %ETHTOOL_SFEATURES changes features present in features[].valid to the + * values of corresponding bits in features[].requested. Bits in .requested + * not set in .valid or not changeable are ignored. + * + * Returns %EINVAL when .valid contains undefined or never-changable bits + * or size is not equal to required number of features words (32-bit blocks). + * Returns >= 0 if request was completed; bits set in the value mean: + * %ETHTOOL_F_UNSUPPORTED - there were bits set in .valid that are not + * changeable (not present in %ETHTOOL_GFEATURES' features[].available) + * those bits were ignored. + * %ETHTOOL_F_WISH - some or all changes requested were recorded but the + * resulting state of bits masked by .valid is not equal to .requested. + * Probably there are other device-specific constraints on some features + * in the set. When %ETHTOOL_F_UNSUPPORTED is set, .valid is considered + * here as though ignored bits were cleared. + * + * Meaning of bits in the masks are obtained by %ETHTOOL_GSSET_INFO (number of + * bits in the arrays - always multiple of 32) and %ETHTOOL_GSTRINGS commands + * for ETH_SS_FEATURES string set. First entry in the table corresponds to least + * significant bit in features[0] fields. Empty strings mark undefined features. + */ +enum ethtool_sfeatures_retval_bits { + ETHTOOL_F_UNSUPPORTED__BIT, + ETHTOOL_F_WISH__BIT, +}; + +#define ETHTOOL_F_UNSUPPORTED (1 << ETHTOOL_F_UNSUPPORTED__BIT) +#define ETHTOOL_F_WISH (1 << ETHTOOL_F_WISH__BIT) + #ifdef __KERNEL__ #include @@ -744,6 +826,9 @@ struct ethtool_ops { #define ETHTOOL_GRXFHINDIR 0x00000038 /* Get RX flow hash indir'n table */ #define ETHTOOL_SRXFHINDIR 0x00000039 /* Set RX flow hash indir'n table */ +#define ETHTOOL_GFEATURES 0x0000003a /* Get device offload settings */ +#define ETHTOOL_SFEATURES 0x0000003b /* Change device offload settings */ + /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET #define SPARC_ETH_SSET ETHTOOL_SSET diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index dede3fdbb4be..85f67e225f60 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -791,6 +791,18 @@ struct netdev_tc_txq { * * int (*ndo_del_slave)(struct net_device *dev, struct net_device *slave_dev); * Called to release previously enslaved netdev. + * + * Feature/offload setting functions. + * u32 (*ndo_fix_features)(struct net_device *dev, u32 features); + * Adjusts the requested feature flags according to device-specific + * constraints, and returns the resulting flags. Must not modify + * the device state. + * + * int (*ndo_set_features)(struct net_device *dev, u32 features); + * Called to update device configuration to new features. Passed + * feature set might be less than what was returned by ndo_fix_features()). + * Must return >0 or -errno if it changed dev->features itself. + * */ #define HAVE_NET_DEVICE_OPS struct net_device_ops { @@ -874,6 +886,10 @@ struct net_device_ops { struct net_device *slave_dev); int (*ndo_del_slave)(struct net_device *dev, struct net_device *slave_dev); + u32 (*ndo_fix_features)(struct net_device *dev, + u32 features); + int (*ndo_set_features)(struct net_device *dev, + u32 features); }; /* @@ -925,12 +941,18 @@ struct net_device { struct list_head napi_list; struct list_head unreg_list; - /* Net device features */ + /* currently active device features */ u32 features; - + /* user-changeable features */ + u32 hw_features; + /* user-requested features */ + u32 wanted_features; /* VLAN feature mask */ u32 vlan_features; + /* Net device feature bits; if you change something, + * also update netdev_features_strings[] in ethtool.c */ + #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. */ @@ -966,6 +988,12 @@ struct net_device { #define NETIF_F_TSO6 (SKB_GSO_TCPV6 << NETIF_F_GSO_SHIFT) #define NETIF_F_FSO (SKB_GSO_FCOE << NETIF_F_GSO_SHIFT) + /* Features valid for ethtool to change */ + /* = all defined minus driver/device-class-related */ +#define NETIF_F_NEVER_CHANGE (NETIF_F_HIGHDMA | NETIF_F_VLAN_CHALLENGED | \ + NETIF_F_LLTX | NETIF_F_NETNS_LOCAL) +#define NETIF_F_ETHTOOL_BITS (0x1f3fffff & ~NETIF_F_NEVER_CHANGE) + /* List of features with software fallbacks. */ #define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | \ NETIF_F_TSO6 | NETIF_F_UFO) @@ -2428,8 +2456,13 @@ extern char *netdev_drivername(const struct net_device *dev, char *buffer, int l extern void linkwatch_run_queue(void); +static inline u32 netdev_get_wanted_features(struct net_device *dev) +{ + return (dev->features & ~dev->hw_features) | dev->wanted_features; +} u32 netdev_increment_features(u32 all, u32 one, u32 mask); u32 netdev_fix_features(struct net_device *dev, u32 features); +void netdev_update_features(struct net_device *dev); void netif_stacked_transfer_operstate(const struct net_device *rootdev, struct net_device *dev); diff --git a/net/core/dev.c b/net/core/dev.c index 8686f6ffe7f0..4f6943928fe8 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5302,6 +5302,37 @@ u32 netdev_fix_features(struct net_device *dev, u32 features) } EXPORT_SYMBOL(netdev_fix_features); +void netdev_update_features(struct net_device *dev) +{ + u32 features; + int err = 0; + + features = netdev_get_wanted_features(dev); + + if (dev->netdev_ops->ndo_fix_features) + features = dev->netdev_ops->ndo_fix_features(dev, features); + + /* driver might be less strict about feature dependencies */ + features = netdev_fix_features(dev, features); + + if (dev->features == features) + return; + + netdev_info(dev, "Features changed: 0x%08x -> 0x%08x\n", + dev->features, features); + + if (dev->netdev_ops->ndo_set_features) + err = dev->netdev_ops->ndo_set_features(dev, features); + + if (!err) + dev->features = features; + else if (err < 0) + netdev_err(dev, + "set_features() failed (%d); wanted 0x%08x, left 0x%08x\n", + err, features, dev->features); +} +EXPORT_SYMBOL(netdev_update_features); + /** * netif_stacked_transfer_operstate - transfer operstate * @rootdev: the root or lower level device to transfer state from @@ -5436,15 +5467,18 @@ int register_netdevice(struct net_device *dev) if (dev->iflink == -1) dev->iflink = dev->ifindex; - /* Enable software offloads by default - will be stripped in - * netdev_fix_features() if not supported. */ - dev->features |= NETIF_F_SOFT_FEATURES; + /* Transfer changeable features to wanted_features and enable + * software offloads (GSO and GRO). + */ + dev->hw_features |= NETIF_F_SOFT_FEATURES; + dev->wanted_features = (dev->features & dev->hw_features) + | NETIF_F_SOFT_FEATURES; /* Avoid warning from netdev_fix_features() for GSO without SG */ - if (!(dev->features & NETIF_F_SG)) - dev->features &= ~NETIF_F_GSO; + if (!(dev->wanted_features & NETIF_F_SG)) + dev->wanted_features &= ~NETIF_F_GSO; - dev->features = netdev_fix_features(dev, dev->features); + netdev_update_features(dev); /* Enable GRO and NETIF_F_HIGHDMA for vlans by default, * vlan_dev_init() will do the dev->features check, so these features diff --git a/net/core/ethtool.c b/net/core/ethtool.c index c3fb8f90de6d..95773960dc77 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -172,10 +172,120 @@ EXPORT_SYMBOL(ethtool_ntuple_flush); /* Handlers for each ethtool command */ +#define ETHTOOL_DEV_FEATURE_WORDS 1 + +static int ethtool_get_features(struct net_device *dev, void __user *useraddr) +{ + struct ethtool_gfeatures cmd = { + .cmd = ETHTOOL_GFEATURES, + .size = ETHTOOL_DEV_FEATURE_WORDS, + }; + struct ethtool_get_features_block features[ETHTOOL_DEV_FEATURE_WORDS] = { + { + .available = dev->hw_features, + .requested = dev->wanted_features, + .active = dev->features, + .never_changed = NETIF_F_NEVER_CHANGE, + }, + }; + u32 __user *sizeaddr; + u32 copy_size; + + sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size); + if (get_user(copy_size, sizeaddr)) + return -EFAULT; + + if (copy_size > ETHTOOL_DEV_FEATURE_WORDS) + copy_size = ETHTOOL_DEV_FEATURE_WORDS; + + if (copy_to_user(useraddr, &cmd, sizeof(cmd))) + return -EFAULT; + useraddr += sizeof(cmd); + if (copy_to_user(useraddr, features, copy_size * sizeof(*features))) + return -EFAULT; + + return 0; +} + +static int ethtool_set_features(struct net_device *dev, void __user *useraddr) +{ + struct ethtool_sfeatures cmd; + struct ethtool_set_features_block features[ETHTOOL_DEV_FEATURE_WORDS]; + int ret = 0; + + if (copy_from_user(&cmd, useraddr, sizeof(cmd))) + return -EFAULT; + useraddr += sizeof(cmd); + + if (cmd.size != ETHTOOL_DEV_FEATURE_WORDS) + return -EINVAL; + + if (copy_from_user(features, useraddr, sizeof(features))) + return -EFAULT; + + if (features[0].valid & ~NETIF_F_ETHTOOL_BITS) + return -EINVAL; + + if (features[0].valid & ~dev->hw_features) { + features[0].valid &= dev->hw_features; + ret |= ETHTOOL_F_UNSUPPORTED; + } + + dev->wanted_features &= ~features[0].valid; + dev->wanted_features |= features[0].valid & features[0].requested; + netdev_update_features(dev); + + if ((dev->wanted_features ^ dev->features) & features[0].valid) + ret |= ETHTOOL_F_WISH; + + return ret; +} + +static const char netdev_features_strings[ETHTOOL_DEV_FEATURE_WORDS * 32][ETH_GSTRING_LEN] = { + /* NETIF_F_SG */ "tx-scatter-gather", + /* NETIF_F_IP_CSUM */ "tx-checksum-ipv4", + /* NETIF_F_NO_CSUM */ "tx-checksum-unneeded", + /* NETIF_F_HW_CSUM */ "tx-checksum-ip-generic", + /* NETIF_F_IPV6_CSUM */ "tx_checksum-ipv6", + /* NETIF_F_HIGHDMA */ "highdma", + /* NETIF_F_FRAGLIST */ "tx-scatter-gather-fraglist", + /* NETIF_F_HW_VLAN_TX */ "tx-vlan-hw-insert", + + /* NETIF_F_HW_VLAN_RX */ "rx-vlan-hw-parse", + /* NETIF_F_HW_VLAN_FILTER */ "rx-vlan-filter", + /* NETIF_F_VLAN_CHALLENGED */ "vlan-challenged", + /* NETIF_F_GSO */ "tx-generic-segmentation", + /* NETIF_F_LLTX */ "tx-lockless", + /* NETIF_F_NETNS_LOCAL */ "netns-local", + /* NETIF_F_GRO */ "rx-gro", + /* NETIF_F_LRO */ "rx-lro", + + /* NETIF_F_TSO */ "tx-tcp-segmentation", + /* NETIF_F_UFO */ "tx-udp-fragmentation", + /* NETIF_F_GSO_ROBUST */ "tx-gso-robust", + /* NETIF_F_TSO_ECN */ "tx-tcp-ecn-segmentation", + /* NETIF_F_TSO6 */ "tx-tcp6-segmentation", + /* NETIF_F_FSO */ "tx-fcoe-segmentation", + "", + "", + + /* NETIF_F_FCOE_CRC */ "tx-checksum-fcoe-crc", + /* NETIF_F_SCTP_CSUM */ "tx-checksum-sctp", + /* NETIF_F_FCOE_MTU */ "fcoe-mtu", + /* NETIF_F_NTUPLE */ "rx-ntuple-filter", + /* NETIF_F_RXHASH */ "rx-hashing", + "", + "", + "", +}; + static int __ethtool_get_sset_count(struct net_device *dev, int sset) { const struct ethtool_ops *ops = dev->ethtool_ops; + if (sset == ETH_SS_FEATURES) + return ARRAY_SIZE(netdev_features_strings); + if (ops && ops->get_sset_count && ops->get_strings) return ops->get_sset_count(dev, sset); else @@ -187,8 +297,12 @@ static void __ethtool_get_strings(struct net_device *dev, { const struct ethtool_ops *ops = dev->ethtool_ops; - /* ops->get_strings is valid because checked earlier */ - ops->get_strings(dev, stringset, data); + if (stringset == ETH_SS_FEATURES) + memcpy(data, netdev_features_strings, + sizeof(netdev_features_strings)); + else + /* ops->get_strings is valid because checked earlier */ + ops->get_strings(dev, stringset, data); } static u32 ethtool_get_feature_mask(u32 eth_cmd) @@ -1533,6 +1647,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_GRXCLSRLCNT: case ETHTOOL_GRXCLSRULE: case ETHTOOL_GRXCLSRLALL: + case ETHTOOL_GFEATURES: break; default: if (!capable(CAP_NET_ADMIN)) @@ -1678,6 +1793,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_SRXFHINDIR: rc = ethtool_set_rxfh_indir(dev, useraddr); break; + case ETHTOOL_GFEATURES: + rc = ethtool_get_features(dev, useraddr); + break; + case ETHTOOL_SFEATURES: + rc = ethtool_set_features(dev, useraddr); + break; case ETHTOOL_GTXCSUM: case ETHTOOL_GSG: case ETHTOOL_GTSO: -- cgit v1.2.3 From e83d360d9a7e5d71d55c13e96b19109a2ea23bf0 Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Tue, 15 Feb 2011 16:59:18 +0000 Subject: net: introduce NETIF_F_RXCSUM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce NETIF_F_RXCSUM to replace device-private flags for RX checksum offload. Integrate it with ndo_fix_features. ethtool_op_get_rx_csum() is removed altogether as nothing in-tree uses it. Signed-off-by: Michał Mirosław Signed-off-by: David S. Miller --- include/linux/ethtool.h | 1 - include/linux/netdevice.h | 5 ++++- net/core/ethtool.c | 47 +++++++++++++++++++++++------------------------ 3 files changed, 27 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 806e716bb4fb..54d776c2c1b5 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -625,7 +625,6 @@ struct net_device; /* Some generic methods drivers may use in their ethtool_ops */ u32 ethtool_op_get_link(struct net_device *dev); -u32 ethtool_op_get_rx_csum(struct net_device *dev); u32 ethtool_op_get_tx_csum(struct net_device *dev); int ethtool_op_set_tx_csum(struct net_device *dev, u32 data); int ethtool_op_set_tx_hw_csum(struct net_device *dev, u32 data); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 85f67e225f60..ffe56c16df8a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -977,6 +977,7 @@ struct net_device { #define NETIF_F_FCOE_MTU (1 << 26) /* Supports max FCoE MTU, 2158 bytes*/ #define NETIF_F_NTUPLE (1 << 27) /* N-tuple filters supported */ #define NETIF_F_RXHASH (1 << 28) /* Receive hashing offload */ +#define NETIF_F_RXCSUM (1 << 29) /* Receive checksumming offload */ /* Segmentation offload features */ #define NETIF_F_GSO_SHIFT 16 @@ -992,7 +993,7 @@ struct net_device { /* = all defined minus driver/device-class-related */ #define NETIF_F_NEVER_CHANGE (NETIF_F_HIGHDMA | NETIF_F_VLAN_CHALLENGED | \ NETIF_F_LLTX | NETIF_F_NETNS_LOCAL) -#define NETIF_F_ETHTOOL_BITS (0x1f3fffff & ~NETIF_F_NEVER_CHANGE) +#define NETIF_F_ETHTOOL_BITS (0x3f3fffff & ~NETIF_F_NEVER_CHANGE) /* List of features with software fallbacks. */ #define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | \ @@ -2510,6 +2511,8 @@ static inline int dev_ethtool_get_settings(struct net_device *dev, static inline u32 dev_ethtool_get_rx_csum(struct net_device *dev) { + if (dev->hw_features & NETIF_F_RXCSUM) + return !!(dev->features & NETIF_F_RXCSUM); if (!dev->ethtool_ops || !dev->ethtool_ops->get_rx_csum) return 0; return dev->ethtool_ops->get_rx_csum(dev); diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 65b3d50a6df2..66cdc76770ce 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -34,12 +34,6 @@ u32 ethtool_op_get_link(struct net_device *dev) } EXPORT_SYMBOL(ethtool_op_get_link); -u32 ethtool_op_get_rx_csum(struct net_device *dev) -{ - return (dev->features & NETIF_F_ALL_CSUM) != 0; -} -EXPORT_SYMBOL(ethtool_op_get_rx_csum); - u32 ethtool_op_get_tx_csum(struct net_device *dev) { return (dev->features & NETIF_F_ALL_CSUM) != 0; @@ -274,7 +268,7 @@ static const char netdev_features_strings[ETHTOOL_DEV_FEATURE_WORDS * 32][ETH_GS /* NETIF_F_FCOE_MTU */ "fcoe-mtu", /* NETIF_F_NTUPLE */ "rx-ntuple-filter", /* NETIF_F_RXHASH */ "rx-hashing", - "", + /* NETIF_F_RXCSUM */ "rx-checksum", "", "", }; @@ -313,6 +307,9 @@ static u32 ethtool_get_feature_mask(u32 eth_cmd) case ETHTOOL_GTXCSUM: case ETHTOOL_STXCSUM: return NETIF_F_ALL_CSUM | NETIF_F_SCTP_CSUM; + case ETHTOOL_GRXCSUM: + case ETHTOOL_SRXCSUM: + return NETIF_F_RXCSUM; case ETHTOOL_GSG: case ETHTOOL_SSG: return NETIF_F_SG; @@ -343,6 +340,8 @@ static void *__ethtool_get_one_feature_actor(struct net_device *dev, u32 ethcmd) switch (ethcmd) { case ETHTOOL_GTXCSUM: return ops->get_tx_csum; + case ETHTOOL_GRXCSUM: + return ops->get_rx_csum; case ETHTOOL_SSG: return ops->get_sg; case ETHTOOL_STSO: @@ -354,6 +353,11 @@ static void *__ethtool_get_one_feature_actor(struct net_device *dev, u32 ethcmd) } } +static u32 __ethtool_get_rx_csum_oldbug(struct net_device *dev) +{ + return !!(dev->features & NETIF_F_ALL_CSUM); +} + static int ethtool_get_one_feature(struct net_device *dev, char __user *useraddr, u32 ethcmd) { @@ -369,6 +373,10 @@ static int ethtool_get_one_feature(struct net_device *dev, actor = __ethtool_get_one_feature_actor(dev, ethcmd); + /* bug compatibility with old get_rx_csum */ + if (ethcmd == ETHTOOL_GRXCSUM && !actor) + actor = __ethtool_get_rx_csum_oldbug; + if (actor) edata.data = actor(dev); } @@ -379,6 +387,7 @@ static int ethtool_get_one_feature(struct net_device *dev, } static int __ethtool_set_tx_csum(struct net_device *dev, u32 data); +static int __ethtool_set_rx_csum(struct net_device *dev, u32 data); static int __ethtool_set_sg(struct net_device *dev, u32 data); static int __ethtool_set_tso(struct net_device *dev, u32 data); static int __ethtool_set_ufo(struct net_device *dev, u32 data); @@ -416,6 +425,8 @@ static int ethtool_set_one_feature(struct net_device *dev, switch (ethcmd) { case ETHTOOL_STXCSUM: return __ethtool_set_tx_csum(dev, edata.data); + case ETHTOOL_SRXCSUM: + return __ethtool_set_rx_csum(dev, edata.data); case ETHTOOL_SSG: return __ethtool_set_sg(dev, edata.data); case ETHTOOL_STSO: @@ -1404,20 +1415,15 @@ static int __ethtool_set_tx_csum(struct net_device *dev, u32 data) return dev->ethtool_ops->set_tx_csum(dev, data); } -static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr) +static int __ethtool_set_rx_csum(struct net_device *dev, u32 data) { - struct ethtool_value edata; - if (!dev->ethtool_ops->set_rx_csum) return -EOPNOTSUPP; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - - if (!edata.data && dev->ethtool_ops->set_sg) + if (!data) dev->features &= ~NETIF_F_GRO; - return dev->ethtool_ops->set_rx_csum(dev, edata.data); + return dev->ethtool_ops->set_rx_csum(dev, data); } static int __ethtool_set_tso(struct net_device *dev, u32 data) @@ -1765,15 +1771,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_SPAUSEPARAM: rc = ethtool_set_pauseparam(dev, useraddr); break; - case ETHTOOL_GRXCSUM: - rc = ethtool_get_value(dev, useraddr, ethcmd, - (dev->ethtool_ops->get_rx_csum ? - dev->ethtool_ops->get_rx_csum : - ethtool_op_get_rx_csum)); - break; - case ETHTOOL_SRXCSUM: - rc = ethtool_set_rx_csum(dev, useraddr); - break; case ETHTOOL_TEST: rc = ethtool_self_test(dev, useraddr); break; @@ -1846,6 +1843,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) rc = ethtool_set_features(dev, useraddr); break; case ETHTOOL_GTXCSUM: + case ETHTOOL_GRXCSUM: case ETHTOOL_GSG: case ETHTOOL_GTSO: case ETHTOOL_GUFO: @@ -1854,6 +1852,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) rc = ethtool_get_one_feature(dev, useraddr, ethcmd); break; case ETHTOOL_STXCSUM: + case ETHTOOL_SRXCSUM: case ETHTOOL_SSG: case ETHTOOL_STSO: case ETHTOOL_SUFO: -- cgit v1.2.3 From 3c7bd1a14071b99d6535b710bc998ae5d3abbb66 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 16 Feb 2011 14:08:44 -0800 Subject: net: Add initial_ref arg to dst_alloc(). This allows avoiding multiple writes to the initial __refcnt. The most simplest cases of wanting an initial reference of "1" in ipv4 and ipv6 have been converted, the rest have been left along and kept at the existing "0". Signed-off-by: David S. Miller --- include/net/dst.h | 2 +- net/core/dst.c | 4 ++-- net/decnet/dn_route.c | 4 ++-- net/ipv4/route.c | 7 ++----- net/ipv6/route.c | 5 ++--- net/xfrm/xfrm_policy.c | 2 +- 6 files changed, 10 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index e01855de21e8..23b564d3e110 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -352,7 +352,7 @@ static inline struct dst_entry *skb_dst_pop(struct sk_buff *skb) } extern int dst_discard(struct sk_buff *skb); -extern void * dst_alloc(struct dst_ops * ops); +extern void *dst_alloc(struct dst_ops * ops, int initial_ref); extern void __dst_free(struct dst_entry * dst); extern struct dst_entry *dst_destroy(struct dst_entry * dst); diff --git a/net/core/dst.c b/net/core/dst.c index c1674fde827d..91104d35de7d 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -166,7 +166,7 @@ EXPORT_SYMBOL(dst_discard); const u32 dst_default_metrics[RTAX_MAX]; -void *dst_alloc(struct dst_ops *ops) +void *dst_alloc(struct dst_ops *ops, int initial_ref) { struct dst_entry *dst; @@ -177,7 +177,7 @@ void *dst_alloc(struct dst_ops *ops) dst = kmem_cache_zalloc(ops->kmem_cachep, GFP_ATOMIC); if (!dst) return NULL; - atomic_set(&dst->__refcnt, 0); + atomic_set(&dst->__refcnt, initial_ref); dst->ops = ops; dst->lastuse = jiffies; dst->path = dst; diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 42c9c62d3417..06c054d5ccba 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1122,7 +1122,7 @@ make_route: if (dev_out->flags & IFF_LOOPBACK) flags |= RTCF_LOCAL; - rt = dst_alloc(&dn_dst_ops); + rt = dst_alloc(&dn_dst_ops, 0); if (rt == NULL) goto e_nobufs; @@ -1383,7 +1383,7 @@ static int dn_route_input_slow(struct sk_buff *skb) } make_route: - rt = dst_alloc(&dn_dst_ops); + rt = dst_alloc(&dn_dst_ops, 0); if (rt == NULL) goto e_nobufs; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 79a287181025..9841543c468d 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1818,12 +1818,10 @@ static void rt_set_nexthop(struct rtable *rt, struct fib_result *res, u32 itag) static struct rtable *rt_dst_alloc(bool nopolicy, bool noxfrm) { - struct rtable *rt = dst_alloc(&ipv4_dst_ops); + struct rtable *rt = dst_alloc(&ipv4_dst_ops, 1); if (rt) { rt->dst.obsolete = -1; - atomic_set(&rt->dst.__refcnt, 1); - rt->dst.flags = DST_HOST | (nopolicy ? DST_NOPOLICY : 0) | (noxfrm ? DST_NOXFRM : 0); @@ -2679,12 +2677,11 @@ static int ipv4_dst_blackhole(struct net *net, struct rtable **rp, struct flowi { struct rtable *ort = *rp; struct rtable *rt = (struct rtable *) - dst_alloc(&ipv4_dst_blackhole_ops); + dst_alloc(&ipv4_dst_blackhole_ops, 1); if (rt) { struct dst_entry *new = &rt->dst; - atomic_set(&new->__refcnt, 1); new->__use = 1; new->input = dst_discard; new->output = dst_discard; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index ad8556e6fd41..7946b53692da 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -221,7 +221,7 @@ static struct rt6_info ip6_blk_hole_entry_template = { /* allocate dst with ip6_dst_ops */ static inline struct rt6_info *ip6_dst_alloc(struct dst_ops *ops) { - return (struct rt6_info *)dst_alloc(ops); + return (struct rt6_info *)dst_alloc(ops, 0); } static void ip6_dst_destroy(struct dst_entry *dst) @@ -873,13 +873,12 @@ int ip6_dst_blackhole(struct sock *sk, struct dst_entry **dstp, struct flowi *fl { struct rt6_info *ort = (struct rt6_info *) *dstp; struct rt6_info *rt = (struct rt6_info *) - dst_alloc(&ip6_dst_blackhole_ops); + dst_alloc(&ip6_dst_blackhole_ops, 1); struct dst_entry *new = NULL; if (rt) { new = &rt->dst; - atomic_set(&new->__refcnt, 1); new->__use = 1; new->input = dst_discard; new->output = dst_discard; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 8b3ef404c794..3f1257add4f3 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1340,7 +1340,7 @@ static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family) default: BUG(); } - xdst = dst_alloc(dst_ops) ?: ERR_PTR(-ENOBUFS); + xdst = dst_alloc(dst_ops, 0) ?: ERR_PTR(-ENOBUFS); xfrm_policy_put_afinfo(afinfo); xdst->flo.ops = &xfrm_bundle_fc_ops; -- cgit v1.2.3 From 1435ca0fc1a269f9496343e24223a0fc430aff7a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 7 Feb 2011 10:46:58 +0200 Subject: OMAP: OneNAND: fix 104MHz support 104MHz needs a latency of 8 clock cycles and the VHF flag must be set. Also t_rdyo is specified as "not applicable" so pick a lower value, and force at least 1 clk between AVD High to OE Low. Signed-off-by: Adrian Hunter Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/gpmc-onenand.c | 23 +++++++++++++++++------ include/linux/mtd/onenand_regs.h | 1 + 2 files changed, 18 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-omap2/gpmc-onenand.c b/arch/arm/mach-omap2/gpmc-onenand.c index 3a7d25fb00ef..3a4307b8f7cf 100644 --- a/arch/arm/mach-omap2/gpmc-onenand.c +++ b/arch/arm/mach-omap2/gpmc-onenand.c @@ -94,7 +94,7 @@ static int omap2_onenand_set_async_mode(int cs, void __iomem *onenand_base) } static void set_onenand_cfg(void __iomem *onenand_base, int latency, - int sync_read, int sync_write, int hf) + int sync_read, int sync_write, int hf, int vhf) { u32 reg; @@ -114,6 +114,10 @@ static void set_onenand_cfg(void __iomem *onenand_base, int latency, reg |= ONENAND_SYS_CFG1_HF; else reg &= ~ONENAND_SYS_CFG1_HF; + if (vhf) + reg |= ONENAND_SYS_CFG1_VHF; + else + reg &= ~ONENAND_SYS_CFG1_VHF; writew(reg, onenand_base + ONENAND_REG_SYS_CFG1); } @@ -130,7 +134,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, const int t_wph = 30; int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo; int tick_ns, div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency; - int first_time = 0, hf = 0, sync_read = 0, sync_write = 0; + int first_time = 0, hf = 0, vhf = 0, sync_read = 0, sync_write = 0; int err, ticks_cez; int cs = cfg->cs; u32 reg; @@ -180,7 +184,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, t_avdh = 2; t_ach = 3; t_aavdh = 6; - t_rdyo = 9; + t_rdyo = 6; break; case 83: min_gpmc_clk_period = 12000; /* 83 MHz */ @@ -217,7 +221,11 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, gpmc_clk_ns = gpmc_ticks_to_ns(div); if (gpmc_clk_ns < 15) /* >66Mhz */ hf = 1; - if (hf) + if (gpmc_clk_ns < 12) /* >83Mhz */ + vhf = 1; + if (vhf) + latency = 8; + else if (hf) latency = 6; else if (gpmc_clk_ns >= 25) /* 40 MHz*/ latency = 3; @@ -226,7 +234,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, if (first_time) set_onenand_cfg(onenand_base, latency, - sync_read, sync_write, hf); + sync_read, sync_write, hf, vhf); if (div == 1) { reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2); @@ -264,6 +272,9 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, /* Read */ t.adv_rd_off = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_avdh)); t.oe_on = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_ach)); + /* Force at least 1 clk between AVD High to OE Low */ + if (t.oe_on <= t.adv_rd_off) + t.oe_on = t.adv_rd_off + gpmc_round_ns_to_ticks(1); t.access = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div); t.oe_off = t.access + gpmc_round_ns_to_ticks(1); t.cs_rd_off = t.oe_off; @@ -317,7 +328,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, if (err) return err; - set_onenand_cfg(onenand_base, latency, sync_read, sync_write, hf); + set_onenand_cfg(onenand_base, latency, sync_read, sync_write, hf, vhf); return 0; } diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index cd6f3b431195..d60130f88eed 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -168,6 +168,7 @@ #define ONENAND_SYS_CFG1_INT (1 << 6) #define ONENAND_SYS_CFG1_IOBE (1 << 5) #define ONENAND_SYS_CFG1_RDY_CONF (1 << 4) +#define ONENAND_SYS_CFG1_VHF (1 << 3) #define ONENAND_SYS_CFG1_HF (1 << 2) #define ONENAND_SYS_CFG1_SYNC_WRITE (1 << 1) -- cgit v1.2.3 From b6bf3ca032c9cd517526178f579e7a4e395c6e45 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 16 Feb 2011 22:04:57 -0800 Subject: ipv4: Mark fib_combine_itag()'s 'res' arg as const. Signed-off-by: David S. Miller --- include/net/ip_fib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 08b46b8c3031..b3019d89e8be 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -232,7 +232,7 @@ extern void fib_select_multipath(const struct flowi *flp, struct fib_result *res 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) +static inline void fib_combine_itag(u32 *itag, const struct fib_result *res) { #ifdef CONFIG_IP_ROUTE_CLASSID #ifdef CONFIG_IP_MULTIPLE_TABLES -- cgit v1.2.3 From 982721f3911b2619482e05910644e5699fbeb065 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 16 Feb 2011 21:44:24 -0800 Subject: ipv4: Use const'ify fib_result deep in the route call chains. The only troublesome bit here is __mkroute_output which wants to override res->fi and res->type, compute those in local variables instead. Signed-off-by: David S. Miller --- include/net/ip_fib.h | 2 +- net/ipv4/fib_rules.c | 2 +- net/ipv4/route.c | 32 +++++++++++++++++--------------- 3 files changed, 19 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index b3019d89e8be..523a170b0ecb 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -202,7 +202,7 @@ extern int __net_init fib4_rules_init(struct net *net); extern void __net_exit fib4_rules_exit(struct net *net); #ifdef CONFIG_IP_ROUTE_CLASSID -extern u32 fib_rules_tclass(struct fib_result *res); +extern u32 fib_rules_tclass(const struct fib_result *res); #endif extern int fib_lookup(struct net *n, struct flowi *flp, struct fib_result *res); diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 9cefe72029cf..3018efbaea77 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -47,7 +47,7 @@ struct fib4_rule { }; #ifdef CONFIG_IP_ROUTE_CLASSID -u32 fib_rules_tclass(struct fib_result *res) +u32 fib_rules_tclass(const struct fib_result *res) { return res->r ? ((struct fib4_rule *) res->r)->tclassid : 0; } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 9841543c468d..2facde0985f9 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1787,10 +1787,10 @@ static void rt_init_metrics(struct rtable *rt, struct fib_info *fi) } } -static void rt_set_nexthop(struct rtable *rt, struct fib_result *res, u32 itag) +static void rt_set_nexthop(struct rtable *rt, const struct fib_result *res, + struct fib_info *fi, u16 type, u32 itag) { struct dst_entry *dst = &rt->dst; - struct fib_info *fi = res->fi; if (fi) { if (FIB_RES_GW(*res) && @@ -1813,7 +1813,7 @@ static void rt_set_nexthop(struct rtable *rt, struct fib_result *res, u32 itag) #endif set_class_tag(rt, itag); #endif - rt->rt_type = res->type; + rt->rt_type = type; } static struct rtable *rt_dst_alloc(bool nopolicy, bool noxfrm) @@ -1939,7 +1939,7 @@ static void ip_handle_martian_source(struct net_device *dev, /* called in rcu_read_lock() section */ static int __mkroute_input(struct sk_buff *skb, - struct fib_result *res, + const struct fib_result *res, struct in_device *in_dev, __be32 daddr, __be32 saddr, u32 tos, struct rtable **result) @@ -2018,7 +2018,7 @@ static int __mkroute_input(struct sk_buff *skb, rth->dst.output = ip_output; rth->rt_genid = rt_genid(dev_net(rth->dst.dev)); - rt_set_nexthop(rth, res, itag); + rt_set_nexthop(rth, res, res->fi, res->type, itag); rth->rt_flags = flags; @@ -2319,23 +2319,25 @@ skip_cache: EXPORT_SYMBOL(ip_route_input_common); /* called with rcu_read_lock() */ -static struct rtable *__mkroute_output(struct fib_result *res, +static struct rtable *__mkroute_output(const struct fib_result *res, const struct flowi *fl, const struct flowi *oldflp, struct net_device *dev_out, unsigned int flags) { + struct fib_info *fi = res->fi; u32 tos = RT_FL_TOS(oldflp); struct in_device *in_dev; + u16 type = res->type; struct rtable *rth; if (ipv4_is_loopback(fl->fl4_src) && !(dev_out->flags & IFF_LOOPBACK)) return ERR_PTR(-EINVAL); if (ipv4_is_lbcast(fl->fl4_dst)) - res->type = RTN_BROADCAST; + type = RTN_BROADCAST; else if (ipv4_is_multicast(fl->fl4_dst)) - res->type = RTN_MULTICAST; + type = RTN_MULTICAST; else if (ipv4_is_zeronet(fl->fl4_dst)) return ERR_PTR(-EINVAL); @@ -2346,10 +2348,10 @@ static struct rtable *__mkroute_output(struct fib_result *res, if (!in_dev) return ERR_PTR(-EINVAL); - if (res->type == RTN_BROADCAST) { + if (type == RTN_BROADCAST) { flags |= RTCF_BROADCAST | RTCF_LOCAL; - res->fi = NULL; - } else if (res->type == RTN_MULTICAST) { + fi = NULL; + } else if (type == RTN_MULTICAST) { flags |= RTCF_MULTICAST | RTCF_LOCAL; if (!ip_check_mc(in_dev, oldflp->fl4_dst, oldflp->fl4_src, oldflp->proto)) @@ -2358,8 +2360,8 @@ static struct rtable *__mkroute_output(struct fib_result *res, * default one, but do not gateway in this case. * Yes, it is hack. */ - if (res->fi && res->prefixlen < 4) - res->fi = NULL; + if (fi && res->prefixlen < 4) + fi = NULL; } rth = rt_dst_alloc(IN_DEV_CONF_GET(in_dev, NOPOLICY), @@ -2399,7 +2401,7 @@ static struct rtable *__mkroute_output(struct fib_result *res, RT_CACHE_STAT_INC(out_slow_mc); } #ifdef CONFIG_IP_MROUTE - if (res->type == RTN_MULTICAST) { + if (type == RTN_MULTICAST) { if (IN_DEV_MFORWARD(in_dev) && !ipv4_is_local_multicast(oldflp->fl4_dst)) { rth->dst.input = ip_mr_input; @@ -2409,7 +2411,7 @@ static struct rtable *__mkroute_output(struct fib_result *res, #endif } - rt_set_nexthop(rth, res, 0); + rt_set_nexthop(rth, res, fi, type, 0); rth->rt_flags = flags; return rth; -- cgit v1.2.3 From 647b2d9c61fe9a842dd89eb01b5f01e9d437993c Mon Sep 17 00:00:00 2001 From: Hema HK Date: Thu, 17 Feb 2011 12:06:09 +0530 Subject: usb: otg: TWL6030 Save the last event in otg_transceiver Save the last event in the otg_transceiver so that it can used in the musb driver and gadget driver to configure the musb and enable the vbus for host mode and OTG mode, if the device is connected during boot. Signed-off-by: Hema HK Cc: Tony Lindgren Cc: Paul Walmsley Signed-off-by: Felipe Balbi --- drivers/usb/otg/twl6030-usb.c | 3 +++ include/linux/usb/otg.h | 1 + 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index b4eda02c97f7..05f17b77d54c 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -262,11 +262,13 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl) twl->otg.default_a = false; twl->otg.state = OTG_STATE_B_IDLE; twl->linkstat = status; + twl->otg.last_event = status; blocking_notifier_call_chain(&twl->otg.notifier, status, twl->otg.gadget); } else { status = USB_EVENT_NONE; twl->linkstat = status; + twl->otg.last_event = status; blocking_notifier_call_chain(&twl->otg.notifier, status, twl->otg.gadget); if (twl->asleep) { @@ -299,6 +301,7 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) twl->otg.default_a = true; twl->otg.state = OTG_STATE_A_IDLE; twl->linkstat = status; + twl->otg.last_event = status; blocking_notifier_call_chain(&twl->otg.notifier, status, twl->otg.gadget); } else { diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index a1a1e7a73ec9..da511eec3cb8 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -66,6 +66,7 @@ struct otg_transceiver { u8 default_a; enum usb_otg_state state; + enum usb_xceiv_events last_event; struct usb_bus *host; struct usb_gadget *gadget; -- cgit v1.2.3 From cccad6d4b103e53fb3d1fc1467f654ecb572d047 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 29 Sep 2010 10:55:49 +0300 Subject: usb: otg: notifier: switch to atomic notifier most of our notifications, will be called from IRQ context, so an atomic notifier suits the job better. Signed-off-by: Felipe Balbi --- drivers/usb/otg/ab8500-usb.c | 6 +++--- drivers/usb/otg/nop-usb-xceiv.c | 2 +- drivers/usb/otg/twl4030-usb.c | 6 +++--- drivers/usb/otg/twl6030-usb.c | 8 ++++---- include/linux/usb/otg.h | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index d14736b3107b..07ccea9ada40 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -212,7 +212,7 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab) break; } - blocking_notifier_call_chain(&ab->otg.notifier, event, v); + atomic_notifier_call_chain(&ab->otg.notifier, event, v); return 0; } @@ -281,7 +281,7 @@ static int ab8500_usb_set_power(struct otg_transceiver *otg, unsigned mA) ab->vbus_draw = mA; if (mA) - blocking_notifier_call_chain(&ab->otg.notifier, + atomic_notifier_call_chain(&ab->otg.notifier, USB_EVENT_ENUMERATED, ab->otg.gadget); return 0; } @@ -500,7 +500,7 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ab); - BLOCKING_INIT_NOTIFIER_HEAD(&ab->otg.notifier); + ATOMIC_INIT_NOTIFIER_HEAD(&ab->otg.notifier); /* v1: Wait for link status to become stable. * all: Updates form set_host and set_peripheral as they are atomic. diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index 8acf165fe13b..c1e360046435 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -132,7 +132,7 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev) platform_set_drvdata(pdev, nop); - BLOCKING_INIT_NOTIFIER_HEAD(&nop->otg.notifier); + ATOMIC_INIT_NOTIFIER_HEAD(&nop->otg.notifier); return 0; exit: diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 6ca505f333e4..2362d8352bc8 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -512,7 +512,7 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) else twl4030_phy_resume(twl); - blocking_notifier_call_chain(&twl->otg.notifier, status, + atomic_notifier_call_chain(&twl->otg.notifier, status, twl->otg.gadget); } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); @@ -534,7 +534,7 @@ static void twl4030_usb_phy_init(struct twl4030_usb *twl) twl->asleep = 0; } - blocking_notifier_call_chain(&twl->otg.notifier, status, + atomic_notifier_call_chain(&twl->otg.notifier, status, twl->otg.gadget); } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); @@ -623,7 +623,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) if (device_create_file(&pdev->dev, &dev_attr_vbus)) dev_warn(&pdev->dev, "could not create sysfs file\n"); - BLOCKING_INIT_NOTIFIER_HEAD(&twl->otg.notifier); + ATOMIC_INIT_NOTIFIER_HEAD(&twl->otg.notifier); /* Our job is to use irqs and status from the power module * to keep the transceiver disabled when nothing's connected. diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index 05f17b77d54c..8a91b4b832a1 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -263,13 +263,13 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl) twl->otg.state = OTG_STATE_B_IDLE; twl->linkstat = status; twl->otg.last_event = status; - blocking_notifier_call_chain(&twl->otg.notifier, + atomic_notifier_call_chain(&twl->otg.notifier, status, twl->otg.gadget); } else { status = USB_EVENT_NONE; twl->linkstat = status; twl->otg.last_event = status; - blocking_notifier_call_chain(&twl->otg.notifier, + atomic_notifier_call_chain(&twl->otg.notifier, status, twl->otg.gadget); if (twl->asleep) { regulator_disable(twl->usb3v3); @@ -302,7 +302,7 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) twl->otg.state = OTG_STATE_A_IDLE; twl->linkstat = status; twl->otg.last_event = status; - blocking_notifier_call_chain(&twl->otg.notifier, status, + atomic_notifier_call_chain(&twl->otg.notifier, status, twl->otg.gadget); } else { twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR, @@ -419,7 +419,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) if (device_create_file(&pdev->dev, &dev_attr_vbus)) dev_warn(&pdev->dev, "could not create sysfs file\n"); - BLOCKING_INIT_NOTIFIER_HEAD(&twl->otg.notifier); + ATOMIC_INIT_NOTIFIER_HEAD(&twl->otg.notifier); twl->irq_enabled = true; status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq, diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index da511eec3cb8..6e40718f5abe 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -75,7 +75,7 @@ struct otg_transceiver { void __iomem *io_priv; /* for notification of usb_xceiv_events */ - struct blocking_notifier_head notifier; + struct atomic_notifier_head notifier; /* to pass extra port status to the root hub */ u16 port_status; @@ -235,13 +235,13 @@ otg_start_srp(struct otg_transceiver *otg) static inline int otg_register_notifier(struct otg_transceiver *otg, struct notifier_block *nb) { - return blocking_notifier_chain_register(&otg->notifier, nb); + return atomic_notifier_chain_register(&otg->notifier, nb); } static inline void otg_unregister_notifier(struct otg_transceiver *otg, struct notifier_block *nb) { - blocking_notifier_chain_unregister(&otg->notifier, nb); + atomic_notifier_chain_unregister(&otg->notifier, nb); } /* for OTG controller drivers (and maybe other stuff) */ -- cgit v1.2.3 From b38360a284f8acab4ae431b387c05a4e19ff4129 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 6 Feb 2011 21:57:18 -0800 Subject: kobject.h: fix build when CONFIG_HOTPLUG is disabled When CONFIG_HOTPLUG is not enabled, the inline function add_uevent_var() needs to have its __attribute__ before the function name/parameters, otherwise there are syntax errors. linux-next-20110207/include/linux/kobject.h:232: error: expected ',' or ';' before '{' token Signed-off-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman --- include/linux/kobject.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 15e82c132f78..9229b64ee3aa 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -226,9 +226,8 @@ static inline int kobject_uevent_env(struct kobject *kobj, char *envp[]) { return 0; } -static inline int add_uevent_var(struct kobj_uevent_env *env, - const char *format, ...) - __attribute__((format(printf, 2, 3))) +static inline __attribute__((format(printf, 2, 3))) +int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...) { return 0; } static inline int kobject_action_type(const char *buf, size_t count, -- cgit v1.2.3 From 71d642908d4e8e7a2a4a6e0490432e719ff466d5 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Wed, 16 Feb 2011 23:23:27 +0100 Subject: Driver core: convert platform_{get,set}_drvdata to static inline functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch converts the macros for platform_{get,set}_drvdata to static inline functions to add typechecking. Signed-off-by: Marc Kleine-Budde Acked-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- include/linux/platform_device.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 2e700ec0601f..d96db9825708 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -130,8 +130,15 @@ extern void platform_driver_unregister(struct platform_driver *); extern int platform_driver_probe(struct platform_driver *driver, int (*probe)(struct platform_device *)); -#define platform_get_drvdata(_dev) dev_get_drvdata(&(_dev)->dev) -#define platform_set_drvdata(_dev,data) dev_set_drvdata(&(_dev)->dev, (data)) +static inline void *platform_get_drvdata(const struct platform_device *pdev) +{ + return dev_get_drvdata(&pdev->dev); +} + +static inline void platform_set_drvdata(struct platform_device *pdev, void *data) +{ + dev_set_drvdata(&pdev->dev, data); +} extern struct platform_device *platform_create_bundle(struct platform_driver *driver, int (*probe)(struct platform_device *), -- cgit v1.2.3 From 7887ab3a274dc5f1d1d94ca0cd41ae495d01f94f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Feb 2011 16:35:55 -0800 Subject: ASoC: Allow GPIO jack detection to be configured as a wake source Some systems wish to use jacks as wake sources. Provide a wake flag in the GPIO configuration which causes the driver to enable the IRQ as a wake source. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 3 +++ sound/soc/soc-jack.c | 8 ++++++++ 2 files changed, 11 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 4ccf1e4e0dd0..fb57c33482e5 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -436,6 +436,7 @@ struct snd_soc_jack_zone { * @report: value to report when jack detected * @invert: report presence in low state * @debouce_time: debouce time in ms + * @wake: enable as wake source */ #ifdef CONFIG_GPIOLIB struct snd_soc_jack_gpio { @@ -444,6 +445,8 @@ struct snd_soc_jack_gpio { int report; int invert; int debounce_time; + bool wake; + struct snd_soc_jack *jack; struct delayed_work work; diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 4579ee090bbf..1382251ed2a2 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -330,6 +330,14 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, if (ret) goto err; + if (gpios[i].wake) { + ret = set_irq_wake(gpio_to_irq(gpios[i].gpio), 1); + if (ret != 0) + printk(KERN_ERR + "Failed to mark GPIO %d as wake source: %d\n", + gpios[i].gpio, ret); + } + #ifdef CONFIG_GPIO_SYSFS /* Expose GPIO value over sysfs for diagnostic purposes */ gpio_export(gpios[i].gpio, false); -- cgit v1.2.3 From fadddc8753ccfab26ee57f3205d6926fe4be1350 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Feb 2011 16:41:42 -0800 Subject: ASoC: Add kerneldoc for jack_status_check callback Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index fb57c33482e5..65d865f7e8c0 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -437,6 +437,9 @@ struct snd_soc_jack_zone { * @invert: report presence in low state * @debouce_time: debouce time in ms * @wake: enable as wake source + * @jack_status_check: callback function which overrides the detection + * to provide more complex checks (eg, reading an + * ADC). */ #ifdef CONFIG_GPIOLIB struct snd_soc_jack_gpio { -- cgit v1.2.3 From fd23c3b31107e2fc483301ee923d8a1db14e53f4 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 18 Feb 2011 12:42:28 -0800 Subject: ipv4: Add hash table of interface addresses. This will be used to optimize __ip_dev_find() and friends. With help from Eric Dumazet. Signed-off-by: David S. Miller --- include/linux/inetdevice.h | 1 + net/ipv4/devinet.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) (limited to 'include') diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index ae8fdc54e0c0..5f8146695b7f 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -144,6 +144,7 @@ static inline void ipv4_devconf_setall(struct in_device *in_dev) #define IN_DEV_ARP_NOTIFY(in_dev) IN_DEV_MAXCONF((in_dev), ARP_NOTIFY) struct in_ifaddr { + struct hlist_node hash; struct in_ifaddr *ifa_next; struct in_device *ifa_dev; struct rcu_head rcu_head; diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 748cb5b337bd..2fe50765a672 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -51,6 +51,7 @@ #include #include #include +#include #ifdef CONFIG_SYSCTL #include #endif @@ -92,6 +93,38 @@ static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, }; +/* inet_addr_hash's shifting is dependent upon this IN4_ADDR_HSIZE + * value. So if you change this define, make appropriate changes to + * inet_addr_hash as well. + */ +#define IN4_ADDR_HSIZE 256 +static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE]; +static DEFINE_SPINLOCK(inet_addr_hash_lock); + +static inline unsigned int inet_addr_hash(struct net *net, __be32 addr) +{ + u32 val = (__force u32) addr ^ hash_ptr(net, 8); + + return ((val ^ (val >> 8) ^ (val >> 16) ^ (val >> 24)) & + (IN4_ADDR_HSIZE - 1)); +} + +static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) +{ + unsigned int hash = inet_addr_hash(net, ifa->ifa_address); + + spin_lock(&inet_addr_hash_lock); + hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]); + spin_unlock(&inet_addr_hash_lock); +} + +static void inet_hash_remove(struct in_ifaddr *ifa) +{ + spin_lock(&inet_addr_hash_lock); + hlist_del_init_rcu(&ifa->hash); + spin_unlock(&inet_addr_hash_lock); +} + static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); @@ -265,6 +298,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, } if (!do_promote) { + inet_hash_remove(ifa); *ifap1 = ifa->ifa_next; rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid); @@ -281,6 +315,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, /* 2. Unlink it */ *ifap = ifa1->ifa_next; + inet_hash_remove(ifa1); /* 3. Announce address deletion */ @@ -368,6 +403,8 @@ static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, ifa->ifa_next = *ifap; *ifap = ifa; + inet_hash_insert(dev_net(in_dev->dev), ifa); + /* Send message first, then call notifier. Notifier will trigger FIB update, so that listeners of netlink will know about new ifaddr */ @@ -521,6 +558,7 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh) if (tb[IFA_ADDRESS] == NULL) tb[IFA_ADDRESS] = tb[IFA_LOCAL]; + INIT_HLIST_NODE(&ifa->hash); ifa->ifa_prefixlen = ifm->ifa_prefixlen; ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); ifa->ifa_flags = ifm->ifa_flags; @@ -728,6 +766,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) if (!ifa) { ret = -ENOBUFS; ifa = inet_alloc_ifa(); + INIT_HLIST_NODE(&ifa->hash); if (!ifa) break; if (colon) @@ -1069,6 +1108,7 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, struct in_ifaddr *ifa = inet_alloc_ifa(); if (ifa) { + INIT_HLIST_NODE(&ifa->hash); ifa->ifa_local = ifa->ifa_address = htonl(INADDR_LOOPBACK); ifa->ifa_prefixlen = 8; @@ -1710,6 +1750,11 @@ static struct rtnl_af_ops inet_af_ops = { void __init devinet_init(void) { + int i; + + for (i = 0; i < IN4_ADDR_HSIZE; i++) + INIT_HLIST_HEAD(&inet_addr_lst[i]); + register_pernet_subsys(&devinet_ops); register_gifconf(PF_INET, inet_gifconf); -- cgit v1.2.3 From f2f1794835f1d8900d2b15d114c54e70c849809b Mon Sep 17 00:00:00 2001 From: Tony SIM Date: Tue, 15 Feb 2011 19:10:27 +0900 Subject: staging: iio: ak8975: add platform data. As some of the platform not support irq_to_gpio, we pass gpio port by platform data. Signed-off-by: Tony SIM Signed-off-by: Andrew Chew Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/magnetometer/ak8975.c | 8 +++++++- include/linux/input/ak8975.h | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 include/linux/input/ak8975.h (limited to 'include') diff --git a/drivers/staging/iio/magnetometer/ak8975.c b/drivers/staging/iio/magnetometer/ak8975.c index 420f206cf517..80c0f4137076 100644 --- a/drivers/staging/iio/magnetometer/ak8975.c +++ b/drivers/staging/iio/magnetometer/ak8975.c @@ -29,6 +29,7 @@ #include #include +#include #include "../iio.h" #include "magnet.h" @@ -435,6 +436,7 @@ static int ak8975_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ak8975_data *data; + struct ak8975_platform_data *pdata; int err; /* Allocate our device context. */ @@ -452,7 +454,11 @@ static int ak8975_probe(struct i2c_client *client, /* Grab and set up the supplied GPIO. */ data->eoc_irq = client->irq; - data->eoc_gpio = irq_to_gpio(client->irq); + pdata = client->dev.platform_data; + if (pdata) + data->eoc_gpio = pdata->gpio; + else + data->eoc_gpio = irq_to_gpio(client->irq); if (!data->eoc_gpio) { dev_err(&client->dev, "failed, no valid GPIO\n"); diff --git a/include/linux/input/ak8975.h b/include/linux/input/ak8975.h new file mode 100644 index 000000000000..25d41eb10c3e --- /dev/null +++ b/include/linux/input/ak8975.h @@ -0,0 +1,20 @@ +/* + * ak8975 platform support + * + * Copyright (C) 2010 Renesas Solutions Corp. + * + * Author: Tony SIM + * + * 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 _AK8975_H +#define _AK8975_H + +struct ak8975_platform_data { + int gpio; +}; + +#endif -- cgit v1.2.3 From 8b2988c13da00ac9d03f1764fdb26180c188f9e0 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Wed, 16 Feb 2011 13:58:26 +0100 Subject: ssb: remove invalid define SSB_TMSLOW_PHYCLK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was incorrectly introduced in d2730b2a6a019d14455556019d744ab051e6554b. We have already fixed function to use correct define, but forgot remove old one. Signed-off-by: Rafał Miłecki Cc: Gábor Stefanik Signed-off-by: John W. Linville --- include/linux/ssb/ssb_regs.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h index df9211a84634..9b1125bea1fc 100644 --- a/include/linux/ssb/ssb_regs.h +++ b/include/linux/ssb/ssb_regs.h @@ -97,7 +97,6 @@ #define SSB_TMSLOW_RESET 0x00000001 /* Reset */ #define SSB_TMSLOW_REJECT_22 0x00000002 /* Reject (Backplane rev 2.2) */ #define SSB_TMSLOW_REJECT_23 0x00000004 /* Reject (Backplane rev 2.3) */ -#define SSB_TMSLOW_PHYCLK 0x00000010 /* MAC PHY Clock Control Enable */ #define SSB_TMSLOW_CLOCK 0x00010000 /* Clock Enable */ #define SSB_TMSLOW_FGC 0x00020000 /* Force Gated Clocks On */ #define SSB_TMSLOW_PE 0x40000000 /* Power Management Enable */ -- cgit v1.2.3 From b1a1bcf714c4d79f7872a34138d100941ebb0a0b Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Thu, 17 Feb 2011 01:50:50 +0100 Subject: ssb: when needed, reject IM input while disabling device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/ssb/main.c | 16 +++++++++++++++- include/linux/ssb/ssb_regs.h | 2 ++ 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index 775c579817b4..06c0b6d5250d 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -1220,7 +1220,7 @@ static int ssb_wait_bits(struct ssb_device *dev, u16 reg, u32 bitmask, void ssb_device_disable(struct ssb_device *dev, u32 core_specific_flags) { - u32 reject; + u32 reject, val; if (ssb_read32(dev, SSB_TMSLOW) & SSB_TMSLOW_RESET) return; @@ -1229,12 +1229,26 @@ void ssb_device_disable(struct ssb_device *dev, u32 core_specific_flags) ssb_write32(dev, SSB_TMSLOW, reject | SSB_TMSLOW_CLOCK); ssb_wait_bits(dev, SSB_TMSLOW, reject, 1000, 1); ssb_wait_bits(dev, SSB_TMSHIGH, SSB_TMSHIGH_BUSY, 1000, 0); + + if (ssb_read32(dev, SSB_IDLOW) & SSB_IDLOW_INITIATOR) { + val = ssb_read32(dev, SSB_IMSTATE); + val |= SSB_IMSTATE_REJECT; + ssb_write32(dev, SSB_IMSTATE, val); + ssb_wait_bits(dev, SSB_IMSTATE, SSB_IMSTATE_BUSY, 1000, 0); + } + ssb_write32(dev, SSB_TMSLOW, SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | reject | SSB_TMSLOW_RESET | core_specific_flags); ssb_flush_tmslow(dev); + if (ssb_read32(dev, SSB_IDLOW) & SSB_IDLOW_INITIATOR) { + val = ssb_read32(dev, SSB_IMSTATE); + val &= ~SSB_IMSTATE_REJECT; + ssb_write32(dev, SSB_IMSTATE, val); + } + ssb_write32(dev, SSB_TMSLOW, reject | SSB_TMSLOW_RESET | core_specific_flags); diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h index 9b1125bea1fc..402955ae48ce 100644 --- a/include/linux/ssb/ssb_regs.h +++ b/include/linux/ssb/ssb_regs.h @@ -85,6 +85,8 @@ #define SSB_IMSTATE_AP_RSV 0x00000030 /* Reserved */ #define SSB_IMSTATE_IBE 0x00020000 /* In Band Error */ #define SSB_IMSTATE_TO 0x00040000 /* Timeout */ +#define SSB_IMSTATE_BUSY 0x01800000 /* Busy (Backplane rev >= 2.3 only) */ +#define SSB_IMSTATE_REJECT 0x02000000 /* Reject (Backplane rev >= 2.3 only) */ #define SSB_INTVEC 0x0F94 /* SB Interrupt Mask */ #define SSB_INTVEC_PCI 0x00000001 /* Enable interrupts for PCI */ #define SSB_INTVEC_ENET0 0x00000002 /* Enable interrupts for enet 0 */ -- cgit v1.2.3 From 9c49e4ab84dd46769e7fd9773946e10c95bab680 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 19 Jan 2011 21:13:33 +0000 Subject: ARM: clcd: use amba_part() to determine if we have a PL110 primecell Instead of matching the entire peripheral ID, match against just the part number using the amba_xxx() macros. Acked-by: Catalin Marinas Signed-off-by: Russell King --- include/linux/amba/clcd.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/amba/clcd.h b/include/linux/amba/clcd.h index be33b3affc8a..2e511219a161 100644 --- a/include/linux/amba/clcd.h +++ b/include/linux/amba/clcd.h @@ -212,12 +212,12 @@ static inline void clcdfb_decode(struct clcd_fb *fb, struct clcd_regs *regs) break; case 16: /* - * PL110 cannot choose between 5551 and 565 modes in - * its control register + * PL110 cannot choose between 5551 and 565 modes in its + * control register. It is possible to use 565 with + * custom external wiring. */ - if ((fb->dev->periphid & 0x000fffff) == 0x00041110) - val |= CNTL_LCDBPP16; - else if (fb->fb.var.green.length == 5) + if (amba_part(fb->dev) == 0x110 || + fb->fb.var.green.length == 5) val |= CNTL_LCDBPP16; else val |= CNTL_LCDBPP16_565; -- cgit v1.2.3 From 7b4e9ced69a120e7e7446e3303d2307aa29d891c Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 21 Jan 2011 14:03:28 +0000 Subject: ARM: clcd: add method for describing display capabilities The ARM CLCD PL110 controller in TFT mode provides two output formats based on whether the controller is in 24bpp mode or not - either 5551 or 888. PL111 augments this with a 444 and 565 modes. Some implementations provide an external MUX on the PL110 output to reassign the bits to achieve 565 mode. Provide a system of capability flags to allow the CLCD driver to work out what is supported by each panel and board, and therefore which display formats are permitted. Acked-by: Catalin Marinas Signed-off-by: Russell King --- drivers/video/amba-clcd.c | 95 +++++++++++++++++++++++++++++++++++++++++------ include/linux/amba/clcd.h | 82 +++++++++++++++++++++++++++++++--------- 2 files changed, 149 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c index aedbb345f8a9..8bd370697533 100644 --- a/drivers/video/amba-clcd.c +++ b/drivers/video/amba-clcd.c @@ -120,8 +120,23 @@ static void clcdfb_enable(struct clcd_fb *fb, u32 cntl) static int clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var) { + u32 caps; int ret = 0; + if (fb->panel->caps && fb->board->caps) + caps = fb->panel->caps & fb->board->caps; + else { + /* Old way of specifying what can be used */ + caps = fb->panel->cntl & CNTL_BGR ? + CLCD_CAP_BGR : CLCD_CAP_RGB; + /* But mask out 444 modes as they weren't supported */ + caps &= ~CLCD_CAP_444; + } + + /* Only TFT panels can do RGB888/BGR888 */ + if (!(fb->panel->cntl & CNTL_LCDTFT)) + caps &= ~CLCD_CAP_888; + memset(&var->transp, 0, sizeof(var->transp)); var->red.msb_right = 0; @@ -133,6 +148,13 @@ clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var) case 2: case 4: case 8: + /* If we can't do 5551, reject */ + caps &= CLCD_CAP_5551; + if (!caps) { + ret = -EINVAL; + break; + } + var->red.length = var->bits_per_pixel; var->red.offset = 0; var->green.length = var->bits_per_pixel; @@ -140,23 +162,61 @@ clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var) var->blue.length = var->bits_per_pixel; var->blue.offset = 0; break; + case 16: - var->red.length = 5; - var->blue.length = 5; + /* If we can't do 444, 5551 or 565, reject */ + if (!(caps & (CLCD_CAP_444 | CLCD_CAP_5551 | CLCD_CAP_565))) { + ret = -EINVAL; + break; + } + /* - * Green length can be 5 or 6 depending whether - * we're operating in RGB555 or RGB565 mode. + * Green length can be 4, 5 or 6 depending whether + * we're operating in 444, 5551 or 565 mode. */ - if (var->green.length != 5 && var->green.length != 6) - var->green.length = 6; + if (var->green.length == 4 && caps & CLCD_CAP_444) + caps &= CLCD_CAP_444; + if (var->green.length == 5 && caps & CLCD_CAP_5551) + caps &= CLCD_CAP_5551; + else if (var->green.length == 6 && caps & CLCD_CAP_565) + caps &= CLCD_CAP_565; + else { + /* + * PL110 officially only supports RGB555, + * but may be wired up to allow RGB565. + */ + if (caps & CLCD_CAP_565) { + var->green.length = 6; + caps &= CLCD_CAP_565; + } else if (caps & CLCD_CAP_5551) { + var->green.length = 5; + caps &= CLCD_CAP_5551; + } else { + var->green.length = 4; + caps &= CLCD_CAP_444; + } + } + + if (var->green.length >= 5) { + var->red.length = 5; + var->blue.length = 5; + } else { + var->red.length = 4; + var->blue.length = 4; + } break; case 32: - if (fb->panel->cntl & CNTL_LCDTFT) { - var->red.length = 8; - var->green.length = 8; - var->blue.length = 8; + /* If we can't do 888, reject */ + caps &= CLCD_CAP_888; + if (!caps) { + ret = -EINVAL; break; } + + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + break; default: ret = -EINVAL; break; @@ -168,7 +228,20 @@ clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var) * the bitfield length defined above. */ if (ret == 0 && var->bits_per_pixel >= 16) { - if (fb->panel->cntl & CNTL_BGR) { + bool bgr, rgb; + + bgr = caps & CLCD_CAP_BGR && var->blue.offset == 0; + rgb = caps & CLCD_CAP_RGB && var->red.offset == 0; + + if (!bgr && !rgb) + /* + * The requested format was not possible, try just + * our capabilities. One of BGR or RGB must be + * supported. + */ + bgr = caps & CLCD_CAP_BGR; + + if (bgr) { var->blue.offset = 0; var->green.offset = var->blue.offset + var->blue.length; var->red.offset = var->green.offset + var->green.length; diff --git a/include/linux/amba/clcd.h b/include/linux/amba/clcd.h index 2e511219a161..24d26efd1432 100644 --- a/include/linux/amba/clcd.h +++ b/include/linux/amba/clcd.h @@ -53,6 +53,7 @@ #define CNTL_LCDBPP8 (3 << 1) #define CNTL_LCDBPP16 (4 << 1) #define CNTL_LCDBPP16_565 (6 << 1) +#define CNTL_LCDBPP16_444 (7 << 1) #define CNTL_LCDBPP24 (5 << 1) #define CNTL_LCDBW (1 << 4) #define CNTL_LCDTFT (1 << 5) @@ -66,6 +67,32 @@ #define CNTL_LDMAFIFOTIME (1 << 15) #define CNTL_WATERMARK (1 << 16) +enum { + /* individual formats */ + CLCD_CAP_RGB444 = (1 << 0), + CLCD_CAP_RGB5551 = (1 << 1), + CLCD_CAP_RGB565 = (1 << 2), + CLCD_CAP_RGB888 = (1 << 3), + CLCD_CAP_BGR444 = (1 << 4), + CLCD_CAP_BGR5551 = (1 << 5), + CLCD_CAP_BGR565 = (1 << 6), + CLCD_CAP_BGR888 = (1 << 7), + + /* connection layouts */ + CLCD_CAP_444 = CLCD_CAP_RGB444 | CLCD_CAP_BGR444, + CLCD_CAP_5551 = CLCD_CAP_RGB5551 | CLCD_CAP_BGR5551, + CLCD_CAP_565 = CLCD_CAP_RGB565 | CLCD_CAP_BGR565, + CLCD_CAP_888 = CLCD_CAP_RGB888 | CLCD_CAP_BGR888, + + /* red/blue ordering */ + CLCD_CAP_RGB = CLCD_CAP_RGB444 | CLCD_CAP_RGB5551 | + CLCD_CAP_RGB565 | CLCD_CAP_RGB888, + CLCD_CAP_BGR = CLCD_CAP_BGR444 | CLCD_CAP_BGR5551 | + CLCD_CAP_BGR565 | CLCD_CAP_BGR888, + + CLCD_CAP_ALL = CLCD_CAP_BGR | CLCD_CAP_RGB, +}; + struct clcd_panel { struct fb_videomode mode; signed short width; /* width in mm */ @@ -73,6 +100,7 @@ struct clcd_panel { u32 tim2; u32 tim3; u32 cntl; + u32 caps; unsigned int bpp:8, fixedtimings:1, grayscale:1; @@ -96,6 +124,11 @@ struct clcd_fb; struct clcd_board { const char *name; + /* + * Optional. Hardware capability flags. + */ + u32 caps; + /* * Optional. Check whether the var structure is acceptable * for this display. @@ -155,34 +188,35 @@ struct clcd_fb { static inline void clcdfb_decode(struct clcd_fb *fb, struct clcd_regs *regs) { + struct fb_var_screeninfo *var = &fb->fb.var; u32 val, cpl; /* * Program the CLCD controller registers and start the CLCD */ - val = ((fb->fb.var.xres / 16) - 1) << 2; - val |= (fb->fb.var.hsync_len - 1) << 8; - val |= (fb->fb.var.right_margin - 1) << 16; - val |= (fb->fb.var.left_margin - 1) << 24; + val = ((var->xres / 16) - 1) << 2; + val |= (var->hsync_len - 1) << 8; + val |= (var->right_margin - 1) << 16; + val |= (var->left_margin - 1) << 24; regs->tim0 = val; - val = fb->fb.var.yres; + val = var->yres; if (fb->panel->cntl & CNTL_LCDDUAL) val /= 2; val -= 1; - val |= (fb->fb.var.vsync_len - 1) << 10; - val |= fb->fb.var.lower_margin << 16; - val |= fb->fb.var.upper_margin << 24; + val |= (var->vsync_len - 1) << 10; + val |= var->lower_margin << 16; + val |= var->upper_margin << 24; regs->tim1 = val; val = fb->panel->tim2; - val |= fb->fb.var.sync & FB_SYNC_HOR_HIGH_ACT ? 0 : TIM2_IHS; - val |= fb->fb.var.sync & FB_SYNC_VERT_HIGH_ACT ? 0 : TIM2_IVS; + val |= var->sync & FB_SYNC_HOR_HIGH_ACT ? 0 : TIM2_IHS; + val |= var->sync & FB_SYNC_VERT_HIGH_ACT ? 0 : TIM2_IVS; - cpl = fb->fb.var.xres_virtual; + cpl = var->xres_virtual; if (fb->panel->cntl & CNTL_LCDTFT) /* TFT */ /* / 1 */; - else if (!fb->fb.var.grayscale) /* STN color */ + else if (!var->grayscale) /* STN color */ cpl = cpl * 8 / 3; else if (fb->panel->cntl & CNTL_LCDMONO8) /* STN monochrome, 8bit */ cpl /= 8; @@ -194,10 +228,22 @@ static inline void clcdfb_decode(struct clcd_fb *fb, struct clcd_regs *regs) regs->tim3 = fb->panel->tim3; val = fb->panel->cntl; - if (fb->fb.var.grayscale) + if (var->grayscale) val |= CNTL_LCDBW; - switch (fb->fb.var.bits_per_pixel) { + if (fb->panel->caps && fb->board->caps && + var->bits_per_pixel >= 16) { + /* + * if board and panel supply capabilities, we can support + * changing BGR/RGB depending on supplied parameters + */ + if (var->red.offset == 0) + val &= ~CNTL_BGR; + else + val |= CNTL_BGR; + } + + switch (var->bits_per_pixel) { case 1: val |= CNTL_LCDBPP1; break; @@ -217,10 +263,12 @@ static inline void clcdfb_decode(struct clcd_fb *fb, struct clcd_regs *regs) * custom external wiring. */ if (amba_part(fb->dev) == 0x110 || - fb->fb.var.green.length == 5) + var->green.length == 5) val |= CNTL_LCDBPP16; - else + else if (var->green.length == 6) val |= CNTL_LCDBPP16_565; + else + val |= CNTL_LCDBPP16_444; break; case 32: val |= CNTL_LCDBPP24; @@ -228,7 +276,7 @@ static inline void clcdfb_decode(struct clcd_fb *fb, struct clcd_regs *regs) } regs->cntl = val; - regs->pixclock = fb->fb.var.pixclock; + regs->pixclock = var->pixclock; } static inline int clcdfb_check(struct clcd_fb *fb, struct fb_var_screeninfo *var) -- cgit v1.2.3 From a0cd9ca2b907d7ee26575e7b63ac92dad768a75e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 10 Feb 2011 11:36:33 +0100 Subject: genirq: Namespace cleanup The irq namespace has become quite convoluted. My bad. Clean it up and deprecate the old functions. All new functions follow the scheme: irq number based: irq_set/get/xxx/_xxx(unsigned int irq, ...) irq_data based: irq_data_set/get/xxx/_xxx(struct irq_data *d, ....) irq_desc based: irq_desc_get_xxx(struct irq_desc *desc) Signed-off-by: Thomas Gleixner --- include/linux/interrupt.h | 14 +++++++-- include/linux/irq.h | 79 ++++++++++++++++++++++++++++++++++++++--------- include/linux/irqdesc.h | 44 +++++++++++++++++++++++--- kernel/irq/chip.c | 28 ++++++++--------- kernel/irq/manage.c | 6 ++-- 5 files changed, 133 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 56b7c97aaf0a..7834726dd95b 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -346,16 +346,24 @@ static inline void enable_irq_lockdep_irqrestore(unsigned int irq, unsigned long } /* IRQ wakeup (PM) control: */ -extern int set_irq_wake(unsigned int irq, unsigned int on); +extern int irq_set_irq_wake(unsigned int irq, unsigned int on); + +#ifndef CONFIG_GENERIC_HARDIRQS_NO_COMPAT +/* Please do not use: Use the replacement functions instead */ +static inline int set_irq_wake(unsigned int irq, unsigned int on) +{ + return irq_set_irq_wake(irq, on); +} +#endif static inline int enable_irq_wake(unsigned int irq) { - return set_irq_wake(irq, 1); + return irq_set_irq_wake(irq, 1); } static inline int disable_irq_wake(unsigned int irq) { - return set_irq_wake(irq, 0); + return irq_set_irq_wake(irq, 0); } #else /* !CONFIG_GENERIC_HARDIRQS */ diff --git a/include/linux/irq.h b/include/linux/irq.h index 80fcb53057bc..e9f847d56c4d 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -292,8 +292,7 @@ set_irq_handler(unsigned int irq, irq_flow_handler_t handle) * IRQ_NOREQUEST and IRQ_NOPROBE) */ static inline void -set_irq_chained_handler(unsigned int irq, - irq_flow_handler_t handle) +set_irq_chained_handler(unsigned int irq, irq_flow_handler_t handle) { __set_irq_handler(irq, handle, 1, NULL); } @@ -312,12 +311,12 @@ static inline void irq_clear_status_flags(unsigned int irq, unsigned long clr) irq_modify_status(irq, clr, 0); } -static inline void set_irq_noprobe(unsigned int irq) +static inline void irq_set_noprobe(unsigned int irq) { irq_modify_status(irq, 0, IRQ_NOPROBE); } -static inline void set_irq_probe(unsigned int irq) +static inline void irq_set_probe(unsigned int irq) { irq_modify_status(irq, IRQ_NOPROBE, 0); } @@ -338,14 +337,14 @@ static inline void dynamic_irq_init(unsigned int irq) } /* Set/get chip/data for an IRQ: */ -extern int set_irq_chip(unsigned int irq, struct irq_chip *chip); -extern int set_irq_data(unsigned int irq, void *data); -extern int set_irq_chip_data(unsigned int irq, void *data); -extern int set_irq_type(unsigned int irq, unsigned int type); -extern int set_irq_msi(unsigned int irq, struct msi_desc *entry); +extern int irq_set_chip(unsigned int irq, struct irq_chip *chip); +extern int irq_set_handler_data(unsigned int irq, void *data); +extern int irq_set_chip_data(unsigned int irq, void *data); +extern int irq_set_irq_type(unsigned int irq, unsigned int type); +extern int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry); extern struct irq_data *irq_get_irq_data(unsigned int irq); -static inline struct irq_chip *get_irq_chip(unsigned int irq) +static inline struct irq_chip *irq_get_chip(unsigned int irq) { struct irq_data *d = irq_get_irq_data(irq); return d ? d->chip : NULL; @@ -356,7 +355,7 @@ static inline struct irq_chip *irq_data_get_irq_chip(struct irq_data *d) return d->chip; } -static inline void *get_irq_chip_data(unsigned int irq) +static inline void *irq_get_chip_data(unsigned int irq) { struct irq_data *d = irq_get_irq_data(irq); return d ? d->chip_data : NULL; @@ -367,18 +366,18 @@ static inline void *irq_data_get_irq_chip_data(struct irq_data *d) return d->chip_data; } -static inline void *get_irq_data(unsigned int irq) +static inline void *irq_get_handler_data(unsigned int irq) { struct irq_data *d = irq_get_irq_data(irq); return d ? d->handler_data : NULL; } -static inline void *irq_data_get_irq_data(struct irq_data *d) +static inline void *irq_data_get_irq_handler_data(struct irq_data *d) { return d->handler_data; } -static inline struct msi_desc *get_irq_msi(unsigned int irq) +static inline struct msi_desc *irq_get_msi_desc(unsigned int irq) { struct irq_data *d = irq_get_irq_data(irq); return d ? d->msi_desc : NULL; @@ -389,6 +388,58 @@ static inline struct msi_desc *irq_data_get_msi(struct irq_data *d) return d->msi_desc; } +#ifndef CONFIG_GENERIC_HARDIRQS_NO_COMPAT +/* Please do not use: Use the replacement functions instead */ +static inline int set_irq_chip(unsigned int irq, struct irq_chip *chip) +{ + return irq_set_chip(irq, chip); +} +static inline int set_irq_data(unsigned int irq, void *data) +{ + return irq_set_handler_data(irq, data); +} +static inline int set_irq_chip_data(unsigned int irq, void *data) +{ + return irq_set_chip_data(irq, data); +} +static inline int set_irq_type(unsigned int irq, unsigned int type) +{ + return irq_set_irq_type(irq, type); +} +static inline int set_irq_msi(unsigned int irq, struct msi_desc *entry) +{ + return irq_set_msi_desc(irq, entry); +} +static inline struct irq_chip *get_irq_chip(unsigned int irq) +{ + return irq_get_chip(irq); +} +static inline void *get_irq_chip_data(unsigned int irq) +{ + return irq_get_chip_data(irq); +} +static inline void *get_irq_data(unsigned int irq) +{ + return irq_get_handler_data(irq); +} +static inline void *irq_data_get_irq_data(struct irq_data *d) +{ + return irq_data_get_irq_handler_data(d); +} +static inline struct msi_desc *get_irq_msi(unsigned int irq) +{ + return irq_get_msi_desc(irq); +} +static inline void set_irq_noprobe(unsigned int irq) +{ + irq_set_noprobe(irq); +} +static inline void set_irq_probe(unsigned int irq) +{ + irq_set_probe(irq); +} +#endif + int irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node); void irq_free_descs(unsigned int irq, unsigned int cnt); int irq_reserve_irqs(unsigned int from, unsigned int cnt); diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index bfef56dadddb..64794dec93b6 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h @@ -98,10 +98,46 @@ static inline struct irq_desc *move_irq_desc(struct irq_desc *desc, int node) #ifdef CONFIG_GENERIC_HARDIRQS -#define get_irq_desc_chip(desc) ((desc)->irq_data.chip) -#define get_irq_desc_chip_data(desc) ((desc)->irq_data.chip_data) -#define get_irq_desc_data(desc) ((desc)->irq_data.handler_data) -#define get_irq_desc_msi(desc) ((desc)->irq_data.msi_desc) +static inline struct irq_chip *irq_desc_get_chip(struct irq_desc *desc) +{ + return desc->irq_data.chip; +} + +static inline void *irq_desc_get_chip_data(struct irq_desc *desc) +{ + return desc->irq_data.chip_data; +} + +static inline void *irq_desc_get_handler_data(struct irq_desc *desc) +{ + return desc->irq_data.handler_data; +} + +static inline struct msi_desc *irq_desc_get_msi_desc(struct irq_desc *desc) +{ + return desc->irq_data.msi_desc; +} + +#ifndef CONFIG_GENERIC_HARDIRQS_NO_COMPAT +static inline struct irq_chip *get_irq_desc_chip(struct irq_desc *desc) +{ + return irq_desc_get_chip(desc); +} +static inline void *get_irq_desc_data(struct irq_desc *desc) +{ + return irq_desc_get_handler_data(desc); +} + +static inline void *get_irq_desc_chip_data(struct irq_desc *desc) +{ + return irq_desc_get_chip_data(desc); +} + +static inline struct msi_desc *get_irq_desc_msi(struct irq_desc *desc) +{ + return irq_desc_get_msi_desc(desc); +} +#endif /* * Architectures call this to let the generic IRQ layer diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 9639ab8bece0..622b55ac0e09 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -19,11 +19,11 @@ #include "internals.h" /** - * set_irq_chip - set the irq chip for an irq + * irq_set_chip - set the irq chip for an irq * @irq: irq number * @chip: pointer to irq chip description structure */ -int set_irq_chip(unsigned int irq, struct irq_chip *chip) +int irq_set_chip(unsigned int irq, struct irq_chip *chip) { struct irq_desc *desc = irq_to_desc(irq); unsigned long flags; @@ -43,14 +43,14 @@ int set_irq_chip(unsigned int irq, struct irq_chip *chip) return 0; } -EXPORT_SYMBOL(set_irq_chip); +EXPORT_SYMBOL(irq_set_chip); /** - * set_irq_type - set the irq trigger type for an irq + * irq_set_type - set the irq trigger type for an irq * @irq: irq number * @type: IRQ_TYPE_{LEVEL,EDGE}_* value - see include/linux/irq.h */ -int set_irq_type(unsigned int irq, unsigned int type) +int irq_set_irq_type(unsigned int irq, unsigned int type) { struct irq_desc *desc = irq_to_desc(irq); unsigned long flags; @@ -72,16 +72,16 @@ int set_irq_type(unsigned int irq, unsigned int type) chip_bus_sync_unlock(desc); return ret; } -EXPORT_SYMBOL(set_irq_type); +EXPORT_SYMBOL(irq_set_irq_type); /** - * set_irq_data - set irq type data for an irq + * irq_set_handler_data - set irq handler data for an irq * @irq: Interrupt number * @data: Pointer to interrupt specific data * * Set the hardware irq controller data for an irq */ -int set_irq_data(unsigned int irq, void *data) +int irq_set_handler_data(unsigned int irq, void *data) { struct irq_desc *desc = irq_to_desc(irq); unsigned long flags; @@ -97,16 +97,16 @@ int set_irq_data(unsigned int irq, void *data) raw_spin_unlock_irqrestore(&desc->lock, flags); return 0; } -EXPORT_SYMBOL(set_irq_data); +EXPORT_SYMBOL(irq_set_handler_data); /** - * set_irq_msi - set MSI descriptor data for an irq + * irq_set_msi_desc - set MSI descriptor data for an irq * @irq: Interrupt number * @entry: Pointer to MSI descriptor data * * Set the MSI descriptor entry for an irq */ -int set_irq_msi(unsigned int irq, struct msi_desc *entry) +int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry) { struct irq_desc *desc = irq_to_desc(irq); unsigned long flags; @@ -126,13 +126,13 @@ int set_irq_msi(unsigned int irq, struct msi_desc *entry) } /** - * set_irq_chip_data - set irq chip data for an irq + * irq_set_chip_data - set irq chip data for an irq * @irq: Interrupt number * @data: Pointer to chip specific data * * Set the hardware irq chip data for an irq */ -int set_irq_chip_data(unsigned int irq, void *data) +int irq_set_chip_data(unsigned int irq, void *data) { struct irq_desc *desc = irq_to_desc(irq); unsigned long flags; @@ -154,7 +154,7 @@ int set_irq_chip_data(unsigned int irq, void *data) return 0; } -EXPORT_SYMBOL(set_irq_chip_data); +EXPORT_SYMBOL(irq_set_chip_data); struct irq_data *irq_get_irq_data(unsigned int irq) { diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index a400db220cf3..b1b4da9446e6 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -434,7 +434,7 @@ static int set_irq_wake_real(unsigned int irq, unsigned int on) } /** - * set_irq_wake - control irq power management wakeup + * irq_set_irq_wake - control irq power management wakeup * @irq: interrupt to control * @on: enable/disable power management wakeup * @@ -445,7 +445,7 @@ static int set_irq_wake_real(unsigned int irq, unsigned int on) * Wakeup mode lets this IRQ wake the system from sleep * states like "suspend to RAM". */ -int set_irq_wake(unsigned int irq, unsigned int on) +int irq_set_irq_wake(unsigned int irq, unsigned int on) { struct irq_desc *desc = irq_to_desc(irq); unsigned long flags; @@ -480,7 +480,7 @@ int set_irq_wake(unsigned int irq, unsigned int on) chip_bus_sync_unlock(desc); return ret; } -EXPORT_SYMBOL(set_irq_wake); +EXPORT_SYMBOL(irq_set_irq_wake); /* * Internal function that tells the architecture code whether a -- cgit v1.2.3 From 3b8249e759c701c4a82f99d957be651a7657bf6f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 7 Feb 2011 16:02:20 +0100 Subject: genirq: Do not copy affinity before set While rumaging through arch code I found that there are a few workarounds which deal with the fact that the initial affinity setting from request_irq() copies the mask into irq_data->affinity before the chip code is called. In the normal path we unconditionally copy the mask when the chip code returns 0. Copy after the code is called and add a return code IRQ_SET_MASK_OK_NOCOPY for the chip functions, which prevents the copy. That way we see the real mask when the chip function decided to truncate it further as some arches do. IRQ_SET_MASK_OK is 0, which is the current behaviour. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 11 +++++++++++ kernel/irq/internals.h | 2 +- kernel/irq/manage.c | 47 ++++++++++++++++++++++++++++++++++------------- kernel/irq/proc.c | 2 +- 4 files changed, 47 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index e9f847d56c4d..f5e900309d21 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -85,6 +85,17 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, # define IRQ_NO_BALANCING_MASK IRQ_NO_BALANCING #endif +/* + * Return value for chip->irq_set_affinity() + * + * IRQ_SET_MASK_OK - OK, core updates irq_data.affinity + * IRQ_SET_MASK_NOCPY - OK, chip did update irq_data.affinity + */ +enum { + IRQ_SET_MASK_OK = 0, + IRQ_SET_MASK_OK_NOCOPY, +}; + struct msi_desc; /** diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 99c3bc8a6fb4..b5bfa24aa6a6 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -43,7 +43,7 @@ static inline void unregister_handler_proc(unsigned int irq, struct irqaction *action) { } #endif -extern int irq_select_affinity_usr(unsigned int irq); +extern int irq_select_affinity_usr(unsigned int irq, struct cpumask *mask); extern void irq_set_thread_affinity(struct irq_desc *desc); diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index ade65bfb466d..dc95d53df510 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -148,9 +148,12 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *mask) if (irq_can_move_pcntxt(desc)) { ret = chip->irq_set_affinity(&desc->irq_data, mask, false); - if (!ret) { + switch (ret) { + case IRQ_SET_MASK_OK: cpumask_copy(desc->irq_data.affinity, mask); + case IRQ_SET_MASK_OK_NOCOPY: irq_set_thread_affinity(desc); + ret = 0; } } else { desc->status |= IRQ_MOVE_PENDING; @@ -254,9 +257,12 @@ EXPORT_SYMBOL_GPL(irq_set_affinity_notifier); /* * Generic version of the affinity autoselector. */ -static int setup_affinity(unsigned int irq, struct irq_desc *desc) +static int +setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask) { + struct irq_chip *chip = get_irq_desc_chip(desc); struct cpumask *set = irq_default_affinity; + int ret; /* Excludes PER_CPU and NO_BALANCE interrupts */ if (!irq_can_set_affinity(irq)) @@ -273,13 +279,20 @@ static int setup_affinity(unsigned int irq, struct irq_desc *desc) else desc->status &= ~IRQ_AFFINITY_SET; } - cpumask_and(desc->irq_data.affinity, cpu_online_mask, set); - desc->irq_data.chip->irq_set_affinity(&desc->irq_data, desc->irq_data.affinity, false); + cpumask_and(mask, cpu_online_mask, set); + ret = chip->irq_set_affinity(&desc->irq_data, mask, false); + switch (ret) { + case IRQ_SET_MASK_OK: + cpumask_copy(desc->irq_data.affinity, mask); + case IRQ_SET_MASK_OK_NOCOPY: + irq_set_thread_affinity(desc); + } return 0; } #else -static inline int setup_affinity(unsigned int irq, struct irq_desc *d) +static inline int +setup_affinity(unsigned int irq, struct irq_desc *d, struct cpumask *mask) { return irq_select_affinity(irq); } @@ -288,23 +301,23 @@ static inline int setup_affinity(unsigned int irq, struct irq_desc *d) /* * Called when affinity is set via /proc/irq */ -int irq_select_affinity_usr(unsigned int irq) +int irq_select_affinity_usr(unsigned int irq, struct cpumask *mask) { struct irq_desc *desc = irq_to_desc(irq); unsigned long flags; int ret; raw_spin_lock_irqsave(&desc->lock, flags); - ret = setup_affinity(irq, desc); + ret = setup_affinity(irq, desc, mask); if (!ret) irq_set_thread_affinity(desc); raw_spin_unlock_irqrestore(&desc->lock, flags); - return ret; } #else -static inline int setup_affinity(unsigned int irq, struct irq_desc *desc) +static inline int +setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask) { return 0; } @@ -765,8 +778,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) struct irqaction *old, **old_ptr; const char *old_name = NULL; unsigned long flags; - int nested, shared = 0; - int ret; + int ret, nested, shared = 0; + cpumask_var_t mask; if (!desc) return -EINVAL; @@ -831,6 +844,11 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) new->thread = t; } + if (!alloc_cpumask_var(&mask, GFP_KERNEL)) { + ret = -ENOMEM; + goto out_thread; + } + /* * The following block of code has to be executed atomically */ @@ -876,7 +894,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) new->flags & IRQF_TRIGGER_MASK); if (ret) - goto out_thread; + goto out_mask; } else compat_irq_chip_set_default_handler(desc); #if defined(CONFIG_IRQ_PER_CPU) @@ -903,7 +921,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) desc->status |= IRQ_NO_BALANCING; /* Set default affinity mask once everything is setup */ - setup_affinity(irq, desc); + setup_affinity(irq, desc, mask); } else if ((new->flags & IRQF_TRIGGER_MASK) && (new->flags & IRQF_TRIGGER_MASK) @@ -956,6 +974,9 @@ mismatch: #endif ret = -EBUSY; +out_mask: + free_cpumask_var(mask); + out_thread: raw_spin_unlock_irqrestore(&desc->lock, flags); if (new->thread) { diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index 6c8a2a9f8a7b..a46bd762db47 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c @@ -89,7 +89,7 @@ static ssize_t irq_affinity_proc_write(struct file *file, if (!cpumask_intersects(new_value, cpu_online_mask)) { /* Special case for empty set - allow the architecture code to set default SMP affinity. */ - err = irq_select_affinity_usr(irq) ? -EINVAL : count; + err = irq_select_affinity_usr(irq, new_value) ? -EINVAL : count; } else { irq_set_affinity(irq, new_value); err = count; -- cgit v1.2.3 From fe200ae48ef5c79bf7941fe8046ff9505c570ff6 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 7 Feb 2011 10:34:30 +0100 Subject: genirq: Mark polled irqs and defer the real handler With the chip.end() function gone we might run into a situation where a poll call runs and the real interrupt comes in, sees IRQ_INPROGRESS and disables the line. That might be a perfect working one, which will then be masked forever. So mark them polled while the poll runs. When the real handler sees IRQ_INPROGRESS it checks the poll flag and waits for the polling to complete. Add the necessary amount of sanity checks to it to avoid deadlocks. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 1 + kernel/irq/chip.c | 26 +++++++++++++++++++------ kernel/irq/internals.h | 11 +---------- kernel/irq/spurious.c | 51 ++++++++++++++++++++++++++++++++++++++------------ 4 files changed, 61 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index f5e900309d21..e32b64ccdc89 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -71,6 +71,7 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQ_SUSPENDED 0x04000000 /* IRQ has gone through suspend sequence */ #define IRQ_ONESHOT 0x08000000 /* IRQ is not unmasked after hardirq */ #define IRQ_NESTED_THREAD 0x10000000 /* IRQ is nested into another, no own handler thread */ +#define IRQ_POLL_INPROGRESS 0x20000000 /* IRQ poll is in progress */ #define IRQF_MODIFY_MASK \ (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \ diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 622b55ac0e09..31258782742c 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -448,6 +448,13 @@ out_unlock: } EXPORT_SYMBOL_GPL(handle_nested_irq); +static bool irq_check_poll(struct irq_desc *desc) +{ + if (!(desc->status & IRQ_POLL_INPROGRESS)) + return false; + return irq_wait_for_poll(desc); +} + /** * handle_simple_irq - Simple and software-decoded IRQs. * @irq: the interrupt number @@ -469,7 +476,9 @@ handle_simple_irq(unsigned int irq, struct irq_desc *desc) raw_spin_lock(&desc->lock); if (unlikely(desc->status & IRQ_INPROGRESS)) - goto out_unlock; + if (!irq_check_poll(desc)) + goto out_unlock; + desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); kstat_incr_irqs_this_cpu(irq, desc); @@ -510,7 +519,9 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc) mask_ack_irq(desc); if (unlikely(desc->status & IRQ_INPROGRESS)) - goto out_unlock; + if (!irq_check_poll(desc)) + goto out_unlock; + desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); kstat_incr_irqs_this_cpu(irq, desc); @@ -558,7 +569,8 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc) raw_spin_lock(&desc->lock); if (unlikely(desc->status & IRQ_INPROGRESS)) - goto out; + if (!irq_check_poll(desc)) + goto out; desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); kstat_incr_irqs_this_cpu(irq, desc); @@ -620,9 +632,11 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc) */ if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) || !desc->action)) { - desc->status |= (IRQ_PENDING | IRQ_MASKED); - mask_ack_irq(desc); - goto out_unlock; + if (!irq_check_poll(desc)) { + desc->status |= (IRQ_PENDING | IRQ_MASKED); + mask_ack_irq(desc); + goto out_unlock; + } } kstat_incr_irqs_this_cpu(irq, desc); diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index b5bfa24aa6a6..0eff7e92b1a9 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -28,6 +28,7 @@ extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr); /* Resending of interrupts :*/ void check_irq_resend(struct irq_desc *desc, unsigned int irq); +bool irq_wait_for_poll(struct irq_desc *desc); #ifdef CONFIG_PROC_FS extern void register_irq_proc(unsigned int irq, struct irq_desc *desc); @@ -47,16 +48,6 @@ extern int irq_select_affinity_usr(unsigned int irq, struct cpumask *mask); extern void irq_set_thread_affinity(struct irq_desc *desc); -#ifndef CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED -static inline void irq_end(unsigned int irq, struct irq_desc *desc) -{ - if (desc->irq_data.chip && desc->irq_data.chip->end) - desc->irq_data.chip->end(irq); -} -#else -static inline void irq_end(unsigned int irq, struct irq_desc *desc) { } -#endif - /* Inline functions for support of irq chips on slow busses */ static inline void chip_bus_lock(struct irq_desc *desc) { diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index 56ff8fffb8b0..f749d29bfd81 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c @@ -24,13 +24,45 @@ static DEFINE_TIMER(poll_spurious_irq_timer, poll_spurious_irqs, 0, 0); static int irq_poll_cpu; static atomic_t irq_poll_active; +/* + * We wait here for a poller to finish. + * + * If the poll runs on this CPU, then we yell loudly and return + * false. That will leave the interrupt line disabled in the worst + * case, but it should never happen. + * + * We wait until the poller is done and then recheck disabled and + * action (about to be disabled). Only if it's still active, we return + * true and let the handler run. + */ +bool irq_wait_for_poll(struct irq_desc *desc) +{ + if (WARN_ONCE(irq_poll_cpu == smp_processor_id(), + "irq poll in progress on cpu %d for irq %d\n", + smp_processor_id(), desc->irq_data.irq)) + return false; + +#ifdef CONFIG_SMP + do { + raw_spin_unlock(&desc->lock); + while (desc->status & IRQ_INPROGRESS) + cpu_relax(); + raw_spin_lock(&desc->lock); + } while (desc->status & IRQ_INPROGRESS); + /* Might have been disabled in meantime */ + return !(desc->status & IRQ_DISABLED) && desc->action; +#else + return false; +#endif +} + /* * Recovery handler for misrouted interrupts. */ static int try_one_irq(int irq, struct irq_desc *desc, bool force) { struct irqaction *action; - int ok = 0, work = 0; + int ok = 0; raw_spin_lock(&desc->lock); @@ -64,10 +96,9 @@ static int try_one_irq(int irq, struct irq_desc *desc, bool force) goto out; } - /* Honour the normal IRQ locking */ - desc->status |= IRQ_INPROGRESS; + /* Honour the normal IRQ locking and mark it poll in progress */ + desc->status |= IRQ_INPROGRESS | IRQ_POLL_INPROGRESS; do { - work++; desc->status &= ~IRQ_PENDING; raw_spin_unlock(&desc->lock); if (handle_IRQ_event(irq, action) != IRQ_NONE) @@ -76,14 +107,7 @@ static int try_one_irq(int irq, struct irq_desc *desc, bool force) action = desc->action; } while ((desc->status & IRQ_PENDING) && action); - desc->status &= ~IRQ_INPROGRESS; - /* - * If we did actual work for the real IRQ line we must let the - * IRQ controller clean up too - */ - if (work > 1) - irq_end(irq, desc); - + desc->status &= ~(IRQ_INPROGRESS | IRQ_POLL_INPROGRESS); out: raw_spin_unlock(&desc->lock); return ok; @@ -238,6 +262,9 @@ try_misrouted_irq(unsigned int irq, struct irq_desc *desc, void note_interrupt(unsigned int irq, struct irq_desc *desc, irqreturn_t action_ret) { + if (desc->status & IRQ_POLL_INPROGRESS) + return; + if (unlikely(action_ret != IRQ_HANDLED)) { /* * If we are seeing only the odd spurious IRQ caused by -- cgit v1.2.3 From 1535dfacbf21c4da1b73fcf07c39913da5bd5581 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 7 Feb 2011 01:55:43 +0100 Subject: genirq: Move irq thread flags to core Soleley used in core code. Signed-off-by: Thomas Gleixner --- include/linux/interrupt.h | 14 -------------- kernel/irq/internals.h | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 7834726dd95b..de97b958f478 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -73,20 +73,6 @@ #define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND) -/* - * Bits used by threaded handlers: - * IRQTF_RUNTHREAD - signals that the interrupt handler thread should run - * IRQTF_DIED - handler thread died - * IRQTF_WARNED - warning "IRQ_WAKE_THREAD w/o thread_fn" has been printed - * IRQTF_AFFINITY - irq thread is requested to adjust affinity - */ -enum { - IRQTF_RUNTHREAD, - IRQTF_DIED, - IRQTF_WARNED, - IRQTF_AFFINITY, -}; - /* * These values can be returned by request_any_context_irq() and * describe the context the interrupt will be run in. diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 0eff7e92b1a9..b17c98440400 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -11,6 +11,20 @@ extern int noirqdebug; +/* + * Bits used by threaded handlers: + * IRQTF_RUNTHREAD - signals that the interrupt handler thread should run + * IRQTF_DIED - handler thread died + * IRQTF_WARNED - warning "IRQ_WAKE_THREAD w/o thread_fn" has been printed + * IRQTF_AFFINITY - irq thread is requested to adjust affinity + */ +enum { + IRQTF_RUNTHREAD, + IRQTF_DIED, + IRQTF_WARNED, + IRQTF_AFFINITY, +}; + #define irq_data_to_desc(data) container_of(data, struct irq_desc, irq_data) /* Set default functions for irq_chip structures: */ -- cgit v1.2.3 From c78b9b65faa291def628dbd8539649f58299f0f3 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 16 Dec 2010 17:21:47 +0100 Subject: genirq: Implement generic irq_show_interrupts() All archs implement show_interrupts() in more or less the same way. That's tons of duplicated code with different bugs with no value. Implement a generic version and deprecate show_interrupts() Unfortunately we need some ifdeffery for !GENERIC_HARDIRQ archs. Signed-off-by: Thomas Gleixner --- include/linux/interrupt.h | 1 + kernel/irq/Kconfig | 3 +++ kernel/irq/proc.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) (limited to 'include') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index de97b958f478..8da6643e39a6 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -671,6 +671,7 @@ static inline void init_irq_proc(void) struct seq_file; int show_interrupts(struct seq_file *p, void *v); +int arch_show_interrupts(struct seq_file *p, int prec); extern int early_irq_init(void); extern int arch_probe_nr_irqs(void); diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index 8e42fec7686d..4cd5d7135e0f 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig @@ -20,6 +20,9 @@ config HAVE_SPARSE_IRQ config GENERIC_IRQ_PROBE def_bool n +config GENERIC_IRQ_SHOW + def_bool n + config GENERIC_PENDING_IRQ def_bool n diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index a46bd762db47..26449239bb46 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "internals.h" @@ -357,3 +358,65 @@ void init_irq_proc(void) } } +#ifdef CONFIG_GENERIC_IRQ_SHOW + +int __weak arch_show_interrupts(struct seq_file *p, int prec) +{ + return 0; +} + +int show_interrupts(struct seq_file *p, void *v) +{ + static int prec; + + unsigned long flags, any_count = 0; + int i = *(loff_t *) v, j; + struct irqaction *action; + struct irq_desc *desc; + + if (i > nr_irqs) + return 0; + + if (i == nr_irqs) + return arch_show_interrupts(p, prec); + + /* print header and calculate the width of the first column */ + if (i == 0) { + for (prec = 3, j = 1000; prec < 10 && j <= nr_irqs; ++prec) + j *= 10; + + seq_printf(p, "%*s", prec + 8, ""); + for_each_online_cpu(j) + seq_printf(p, "CPU%-8d", j); + seq_putc(p, '\n'); + } + + desc = irq_to_desc(i); + if (!desc) + return 0; + + raw_spin_lock_irqsave(&desc->lock, flags); + for_each_online_cpu(j) + any_count |= kstat_irqs_cpu(i, j); + action = desc->action; + if (!action && !any_count) + goto out; + + seq_printf(p, "%*d: ", prec, i); + for_each_online_cpu(j) + seq_printf(p, "%10u ", kstat_irqs_cpu(i, j)); + seq_printf(p, " %8s", desc->irq_data.chip->name); + seq_printf(p, "-%-8s", desc->name); + + if (action) { + seq_printf(p, " %s", action->name); + while ((action = action->next) != NULL) + seq_printf(p, ", %s", action->name); + } + + seq_putc(p, '\n'); +out: + raw_spin_unlock_irqrestore(&desc->lock, flags); + return 0; +} +#endif -- cgit v1.2.3 From dbec07bac614a61e3392c1e7c08cc6a49ad43f7a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 7 Feb 2011 20:19:55 +0100 Subject: genirq: Add internal state field to irq_desc That field will contain internal state information which is not going to be exposed to anything outside the core code - except via accessor functions. I'm tired of everyone fiddling in irq_desc.status. core_internal_state__do_not_mess_with_it is clear enough, annoying to type and easy to grep for. Offenders will be tracked down and slapped with stinking trouts. Signed-off-by: Thomas Gleixner --- include/linux/irqdesc.h | 3 ++- kernel/irq/internals.h | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index 64794dec93b6..782bf9851a9f 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h @@ -19,6 +19,7 @@ struct timer_rand_state; * @handle_irq: highlevel irq-events handler [if NULL, __do_IRQ()] * @action: the irq action chain * @status: status information + * @core_internal_state__do_not_mess_with_it: core internal status information * @depth: disable-depth, for nested irq_disable() calls * @wake_depth: enable depth, for multiple set_irq_wake() callers * @irq_count: stats field to detect stalled irqs @@ -63,7 +64,7 @@ struct irq_desc { irq_flow_handler_t handle_irq; struct irqaction *action; /* IRQ action list */ unsigned int status; /* IRQ status */ - + unsigned int core_internal_state__do_not_mess_with_it; unsigned int depth; /* nested irq disables */ unsigned int wake_depth; /* nested wake enables */ unsigned int irq_count; /* For detecting broken IRQs */ diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index b61824cdadc6..ae96e688f4e1 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -1,5 +1,9 @@ /* * IRQ subsystem internal functions and variables: + * + * Do not ever include this file from anything else than + * kernel/irq/. Do not even think about using any information outside + * of this file for your non core code. */ #include @@ -9,6 +13,8 @@ # define IRQ_BITMAP_BITS NR_IRQS #endif +#define istate core_internal_state__do_not_mess_with_it + extern int noirqdebug; /* -- cgit v1.2.3 From bd062e7667ac173afef57fbfe9327f3b914a9d4c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 7 Feb 2011 20:25:25 +0100 Subject: genirq: Move IRQ_AUTODETECT to internal state No users outside of core Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 1 - kernel/irq/autoprobe.c | 29 ++++++++++++----------------- kernel/irq/internals.h | 15 +++++++++++++-- kernel/irq/manage.c | 3 ++- 4 files changed, 27 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index e32b64ccdc89..d1f9c352cd1b 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -54,7 +54,6 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQ_DISABLED 0x00000200 /* IRQ disabled - do not enter! */ #define IRQ_PENDING 0x00000400 /* IRQ pending - replay on enable */ #define IRQ_REPLAY 0x00000800 /* IRQ has been replayed but not acked yet */ -#define IRQ_AUTODETECT 0x00001000 /* IRQ is being autodetected */ #define IRQ_WAITING 0x00002000 /* IRQ not yet seen - for autodetection */ #define IRQ_LEVEL 0x00004000 /* IRQ level triggered */ #define IRQ_MASKED 0x00008000 /* IRQ masked - shouldn't be seen again */ diff --git a/kernel/irq/autoprobe.c b/kernel/irq/autoprobe.c index 08947cb61725..916e56e10a2e 100644 --- a/kernel/irq/autoprobe.c +++ b/kernel/irq/autoprobe.c @@ -32,7 +32,6 @@ unsigned long probe_irq_on(void) { struct irq_desc *desc; unsigned long mask = 0; - unsigned int status; int i; /* @@ -76,7 +75,8 @@ unsigned long probe_irq_on(void) for_each_irq_desc_reverse(i, desc) { raw_spin_lock_irq(&desc->lock); if (!desc->action && !(desc->status & IRQ_NOPROBE)) { - desc->status |= IRQ_AUTODETECT | IRQ_WAITING; + desc->istate |= IRQS_AUTODETECT; + desc->status |= IRQ_WAITING; if (irq_startup(desc)) desc->status |= IRQ_PENDING; } @@ -93,12 +93,11 @@ unsigned long probe_irq_on(void) */ for_each_irq_desc(i, desc) { raw_spin_lock_irq(&desc->lock); - status = desc->status; - if (status & IRQ_AUTODETECT) { + if (desc->istate & IRQS_AUTODETECT) { /* It triggered already - consider it spurious. */ - if (!(status & IRQ_WAITING)) { - desc->status = status & ~IRQ_AUTODETECT; + if (!(desc->status & IRQ_WAITING)) { + desc->istate &= ~IRQS_AUTODETECT; irq_shutdown(desc); } else if (i < 32) @@ -125,19 +124,17 @@ EXPORT_SYMBOL(probe_irq_on); */ unsigned int probe_irq_mask(unsigned long val) { - unsigned int status, mask = 0; + unsigned int mask = 0; struct irq_desc *desc; int i; for_each_irq_desc(i, desc) { raw_spin_lock_irq(&desc->lock); - status = desc->status; - - if (status & IRQ_AUTODETECT) { - if (i < 16 && !(status & IRQ_WAITING)) + if (desc->istate & IRQS_AUTODETECT) { + if (i < 16 && !(desc->status & IRQ_WAITING)) mask |= 1 << i; - desc->status = status & ~IRQ_AUTODETECT; + desc->istate &= ~IRQS_AUTODETECT; irq_shutdown(desc); } raw_spin_unlock_irq(&desc->lock); @@ -169,19 +166,17 @@ int probe_irq_off(unsigned long val) { int i, irq_found = 0, nr_of_irqs = 0; struct irq_desc *desc; - unsigned int status; for_each_irq_desc(i, desc) { raw_spin_lock_irq(&desc->lock); - status = desc->status; - if (status & IRQ_AUTODETECT) { - if (!(status & IRQ_WAITING)) { + if (desc->istate & IRQS_AUTODETECT) { + if (!(desc->status & IRQ_WAITING)) { if (!nr_of_irqs) irq_found = i; nr_of_irqs++; } - desc->status = status & ~IRQ_AUTODETECT; + desc->istate &= ~IRQS_AUTODETECT; irq_shutdown(desc); } raw_spin_unlock_irq(&desc->lock); diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 8f200310a952..7ffd4f439b92 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -33,6 +33,15 @@ enum { IRQTF_AFFINITY, }; +/* + * Bit masks for desc->state + * + * IRQS_AUTODETECT - autodetection in progress + */ +enum { + IRQS_AUTODETECT = 0x00000001, +}; + #define irq_data_to_desc(data) container_of(data, struct irq_desc, irq_data) /* Set default functions for irq_chip structures: */ @@ -98,6 +107,7 @@ static inline void chip_bus_sync_unlock(struct irq_desc *desc) #include #define P(f) if (desc->status & f) printk("%14s set\n", #f) +#define PS(f) if (desc->istate & f) printk("%14s set\n", #f) static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) { @@ -117,7 +127,6 @@ static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) P(IRQ_DISABLED); P(IRQ_PENDING); P(IRQ_REPLAY); - P(IRQ_AUTODETECT); P(IRQ_WAITING); P(IRQ_LEVEL); P(IRQ_MASKED); @@ -127,7 +136,9 @@ static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) P(IRQ_NOPROBE); P(IRQ_NOREQUEST); P(IRQ_NOAUTOEN); + + PS(IRQS_AUTODETECT); } #undef P - +#undef PS diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index dd4e5c21b9e7..abe852c9449d 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -897,8 +897,9 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) desc->status |= IRQ_PER_CPU; #endif - desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_ONESHOT | + desc->status &= ~(IRQ_WAITING | IRQ_ONESHOT | IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED); + desc->istate &= ~IRQS_AUTODETECT; if (new->flags & IRQF_ONESHOT) desc->status |= IRQ_ONESHOT; -- cgit v1.2.3 From 7acdd53e5b2c55b6f7e3427e85e2f91fa814a4f9 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 7 Feb 2011 20:40:54 +0100 Subject: genirq: Move IRQ_SPURIOUS_DISABLED to core state No users outside. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 1 - kernel/irq/internals.h | 3 +++ kernel/irq/manage.c | 9 ++++----- kernel/irq/spurious.c | 8 ++++---- 4 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index d1f9c352cd1b..a900741b43ea 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -64,7 +64,6 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQ_WAKEUP 0x00100000 /* IRQ triggers system wakeup */ #define IRQ_MOVE_PENDING 0x00200000 /* need to re-target IRQ destination */ #define IRQ_NO_BALANCING 0x00400000 /* IRQ is excluded from balancing */ -#define IRQ_SPURIOUS_DISABLED 0x00800000 /* IRQ was disabled by the spurious trap */ #define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */ #define IRQ_AFFINITY_SET 0x02000000 /* IRQ affinity was set from userspace*/ #define IRQ_SUSPENDED 0x04000000 /* IRQ has gone through suspend sequence */ diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 7ffd4f439b92..dc5e21b84f9e 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -37,9 +37,12 @@ enum { * Bit masks for desc->state * * IRQS_AUTODETECT - autodetection in progress + * IRQS_SPURIOUS_DISABLED - was disabled due to spurious interrupt + * detection */ enum { IRQS_AUTODETECT = 0x00000001, + IRQS_SPURIOUS_DISABLED = 0x00000002, }; #define irq_data_to_desc(data) container_of(data, struct irq_desc, irq_data) diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index abe852c9449d..5b918ffa46af 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -897,9 +897,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) desc->status |= IRQ_PER_CPU; #endif - desc->status &= ~(IRQ_WAITING | IRQ_ONESHOT | - IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED); - desc->istate &= ~IRQS_AUTODETECT; + desc->status &= ~(IRQ_WAITING | IRQ_ONESHOT | IRQ_INPROGRESS); + desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED); if (new->flags & IRQF_ONESHOT) desc->status |= IRQ_ONESHOT; @@ -937,8 +936,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) * Check whether we disabled the irq via the spurious handler * before. Reenable it and give it another chance. */ - if (shared && (desc->status & IRQ_SPURIOUS_DISABLED)) { - desc->status &= ~IRQ_SPURIOUS_DISABLED; + if (shared && (desc->istate & IRQS_SPURIOUS_DISABLED)) { + desc->istate &= ~IRQS_SPURIOUS_DISABLED; __enable_irq(desc, irq, false); } diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index bc0620745d5f..2941d8a22df7 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c @@ -146,15 +146,15 @@ static void poll_spurious_irqs(unsigned long dummy) irq_poll_cpu = smp_processor_id(); for_each_irq_desc(i, desc) { - unsigned int status; + unsigned int state; if (!i) continue; /* Racy but it doesn't matter */ - status = desc->status; + state = desc->istate; barrier(); - if (!(status & IRQ_SPURIOUS_DISABLED)) + if (!(state & IRQS_SPURIOUS_DISABLED)) continue; local_irq_disable(); @@ -298,7 +298,7 @@ void note_interrupt(unsigned int irq, struct irq_desc *desc, * Now kill the IRQ */ printk(KERN_EMERG "Disabling IRQ #%d\n", irq); - desc->status |= IRQ_SPURIOUS_DISABLED; + desc->istate |= IRQS_SPURIOUS_DISABLED; desc->depth++; irq_disable(desc); -- cgit v1.2.3 From 6f91a52d9bb28396177662f1da0f2e2cef9cf5d0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 14 Feb 2011 13:33:16 +0100 Subject: genirq: Use modify_status for set_irq_nested_thread No need for a separate function in the core code. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 16 +++++++++++++--- kernel/irq/chip.c | 28 ---------------------------- 2 files changed, 13 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index a900741b43ea..67b77cfb2a34 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -74,7 +74,7 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQF_MODIFY_MASK \ (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \ IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \ - IRQ_PER_CPU) + IRQ_PER_CPU | IRQ_NESTED_THREAD) #ifdef CONFIG_IRQ_PER_CPU # define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU) @@ -307,8 +307,6 @@ set_irq_chained_handler(unsigned int irq, irq_flow_handler_t handle) __set_irq_handler(irq, handle, 1, NULL); } -extern void set_irq_nested_thread(unsigned int irq, int nest); - void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set); static inline void irq_set_status_flags(unsigned int irq, unsigned long set) @@ -331,6 +329,14 @@ static inline void irq_set_probe(unsigned int irq) irq_modify_status(irq, IRQ_NOPROBE, 0); } +static inline void irq_set_nested_thread(unsigned int irq, bool nest) +{ + if (nest) + irq_set_status_flags(irq, IRQ_NESTED_THREAD); + else + irq_clear_status_flags(irq, IRQ_NESTED_THREAD); +} + /* Handle dynamic irq creation and destruction */ extern unsigned int create_irq_nr(unsigned int irq_want, int node); extern int create_irq(void); @@ -448,6 +454,10 @@ static inline void set_irq_probe(unsigned int irq) { irq_set_probe(irq); } +static inline void set_irq_nested_thread(unsigned int irq, int nest) +{ + irq_set_nested_thread(irq, nest); +} #endif int irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node); diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 143eb2a9fa4e..bff21f233a02 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -164,34 +164,6 @@ struct irq_data *irq_get_irq_data(unsigned int irq) } EXPORT_SYMBOL_GPL(irq_get_irq_data); -/** - * set_irq_nested_thread - Set/Reset the IRQ_NESTED_THREAD flag of an irq - * - * @irq: Interrupt number - * @nest: 0 to clear / 1 to set the IRQ_NESTED_THREAD flag - * - * The IRQ_NESTED_THREAD flag indicates that on - * request_threaded_irq() no separate interrupt thread should be - * created for the irq as the handler are called nested in the - * context of a demultiplexing interrupt handler thread. - */ -void set_irq_nested_thread(unsigned int irq, int nest) -{ - struct irq_desc *desc = irq_to_desc(irq); - unsigned long flags; - - if (!desc) - return; - - raw_spin_lock_irqsave(&desc->lock, flags); - if (nest) - desc->status |= IRQ_NESTED_THREAD; - else - desc->status &= ~IRQ_NESTED_THREAD; - raw_spin_unlock_irqrestore(&desc->lock, flags); -} -EXPORT_SYMBOL_GPL(set_irq_nested_thread); - int irq_startup(struct irq_desc *desc) { desc->status &= ~IRQ_DISABLED; -- cgit v1.2.3 From 6954b75b488dd740950573f244ddd66fd28620aa Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 7 Feb 2011 20:55:35 +0100 Subject: genirq: Move IRQ_POLL_INPROGRESS to core No users outside of core. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 1 - kernel/irq/chip.c | 2 +- kernel/irq/internals.h | 2 ++ kernel/irq/spurious.c | 6 +++--- 4 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 67b77cfb2a34..047a695511df 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -69,7 +69,6 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQ_SUSPENDED 0x04000000 /* IRQ has gone through suspend sequence */ #define IRQ_ONESHOT 0x08000000 /* IRQ is not unmasked after hardirq */ #define IRQ_NESTED_THREAD 0x10000000 /* IRQ is nested into another, no own handler thread */ -#define IRQ_POLL_INPROGRESS 0x20000000 /* IRQ poll is in progress */ #define IRQF_MODIFY_MASK \ (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \ diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index bff21f233a02..34245e7d1213 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -400,7 +400,7 @@ EXPORT_SYMBOL_GPL(handle_nested_irq); static bool irq_check_poll(struct irq_desc *desc) { - if (!(desc->status & IRQ_POLL_INPROGRESS)) + if (!(desc->istate & IRQS_POLL_INPROGRESS)) return false; return irq_wait_for_poll(desc); } diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index dc5e21b84f9e..f5d28e1e1eda 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -39,10 +39,12 @@ enum { * IRQS_AUTODETECT - autodetection in progress * IRQS_SPURIOUS_DISABLED - was disabled due to spurious interrupt * detection + * IRQS_POLL_INPROGRESS - polling in progress */ enum { IRQS_AUTODETECT = 0x00000001, IRQS_SPURIOUS_DISABLED = 0x00000002, + IRQS_POLL_INPROGRESS = 0x00000008, }; #define irq_data_to_desc(data) container_of(data, struct irq_desc, irq_data) diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index 2941d8a22df7..21c46178b1a6 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c @@ -98,13 +98,13 @@ static int try_one_irq(int irq, struct irq_desc *desc, bool force) } /* Mark it poll in progress */ - desc->status |= IRQ_POLL_INPROGRESS; + desc->istate |= IRQS_POLL_INPROGRESS; do { if (handle_irq_event(desc) == IRQ_HANDLED) ret = IRQ_HANDLED; action = desc->action; } while ((desc->status & IRQ_PENDING) && action); - desc->status &= ~IRQ_POLL_INPROGRESS; + desc->istate &= ~IRQS_POLL_INPROGRESS; out: raw_spin_unlock(&desc->lock); return ret == IRQ_HANDLED; @@ -259,7 +259,7 @@ try_misrouted_irq(unsigned int irq, struct irq_desc *desc, void note_interrupt(unsigned int irq, struct irq_desc *desc, irqreturn_t action_ret) { - if (desc->status & IRQ_POLL_INPROGRESS) + if (desc->istate & IRQS_POLL_INPROGRESS) return; if (unlikely(action_ret != IRQ_HANDLED)) { -- cgit v1.2.3 From 009b4c3b8ad584b3462734127a5bec680d5d6af4 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 7 Feb 2011 21:48:49 +0100 Subject: genirq: Add IRQ_INPROGRESS to core We need to maintain the flag for now in both fields status and istate. Add a CONFIG_GENERIC_HARDIRQS_NO_COMPAT switch to allow testing w/o the status one. Wrap the access to status IRQ_INPROGRESS in a inline which can be turned of with CONFIG_GENERIC_HARDIRQS_NO_COMPAT along with the define. There is no reason that anything outside of core looks at this. That needs some modifications, but we'll get there. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 6 +++++- kernel/irq/Kconfig | 3 +++ kernel/irq/chip.c | 16 +++++++++------- kernel/irq/compat.h | 17 +++++++++++++++++ kernel/irq/handle.c | 6 ++++-- kernel/irq/internals.h | 5 ++++- kernel/irq/manage.c | 17 +++++++++-------- kernel/irq/settings.h | 3 +++ kernel/irq/spurious.c | 6 +++--- 9 files changed, 57 insertions(+), 22 deletions(-) create mode 100644 kernel/irq/compat.h (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 047a695511df..274590fc55a3 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -50,7 +50,11 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQ_TYPE_PROBE 0x00000010 /* Probing in progress */ /* Internal flags */ -#define IRQ_INPROGRESS 0x00000100 /* IRQ handler active - do not enter! */ + +#ifndef CONFIG_GENERIC_HARDIRQS_NO_COMPAT +#define IRQ_INPROGRESS 0x00000100 /* DEPRECATED */ +#endif + #define IRQ_DISABLED 0x00000200 /* IRQ disabled - do not enter! */ #define IRQ_PENDING 0x00000400 /* IRQ pending - replay on enable */ #define IRQ_REPLAY 0x00000800 /* IRQ has been replayed but not acked yet */ diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index 4cd5d7135e0f..9e2256de1d1a 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig @@ -13,6 +13,9 @@ config GENERIC_HARDIRQS config GENERIC_HARDIRQS_NO_DEPRECATED def_bool n +config GENERIC_HARDIRQS_NO_COMPAT + def_bool n + # Options selectable by the architecture code config HAVE_SPARSE_IRQ def_bool n diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 34245e7d1213..075385549dcd 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -383,7 +383,8 @@ void handle_nested_irq(unsigned int irq) if (unlikely(!action || (desc->status & IRQ_DISABLED))) goto out_unlock; - desc->status |= IRQ_INPROGRESS; + irq_compat_set_progress(desc); + desc->istate |= IRQS_INPROGRESS; raw_spin_unlock_irq(&desc->lock); action_ret = action->thread_fn(action->irq, action->dev_id); @@ -391,7 +392,8 @@ void handle_nested_irq(unsigned int irq) note_interrupt(irq, desc, action_ret); raw_spin_lock_irq(&desc->lock); - desc->status &= ~IRQ_INPROGRESS; + desc->istate &= ~IRQS_INPROGRESS; + irq_compat_clr_progress(desc); out_unlock: raw_spin_unlock_irq(&desc->lock); @@ -422,7 +424,7 @@ handle_simple_irq(unsigned int irq, struct irq_desc *desc) { raw_spin_lock(&desc->lock); - if (unlikely(desc->status & IRQ_INPROGRESS)) + if (unlikely(desc->istate & IRQS_INPROGRESS)) if (!irq_check_poll(desc)) goto out_unlock; @@ -454,7 +456,7 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc) raw_spin_lock(&desc->lock); mask_ack_irq(desc); - if (unlikely(desc->status & IRQ_INPROGRESS)) + if (unlikely(desc->istate & IRQS_INPROGRESS)) if (!irq_check_poll(desc)) goto out_unlock; @@ -492,7 +494,7 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc) { raw_spin_lock(&desc->lock); - if (unlikely(desc->status & IRQ_INPROGRESS)) + if (unlikely(desc->istate & IRQS_INPROGRESS)) if (!irq_check_poll(desc)) goto out; @@ -542,8 +544,8 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc) * we shouldn't process the IRQ. Mark it pending, handle * the necessary masking and go out */ - if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) || - !desc->action)) { + if (unlikely((desc->istate & (IRQS_INPROGRESS) || + (desc->status & IRQ_DISABLED) || !desc->action))) { if (!irq_check_poll(desc)) { desc->status |= IRQ_PENDING; mask_ack_irq(desc); diff --git a/kernel/irq/compat.h b/kernel/irq/compat.h new file mode 100644 index 000000000000..aac6e400e608 --- /dev/null +++ b/kernel/irq/compat.h @@ -0,0 +1,17 @@ +/* + * Compat layer for transition period + */ +#ifndef CONFIG_GENERIC_HARDIRQS_NO_COMPAT +static inline void irq_compat_set_progress(struct irq_desc *desc) +{ + desc->status |= IRQ_INPROGRESS; +} + +static inline void irq_compat_clr_progress(struct irq_desc *desc) +{ + desc->status &= ~IRQ_INPROGRESS; +} +#else +static inline void irq_compat_set_progress(struct irq_desc *desc) { } +static inline void irq_compat_clr_progress(struct irq_desc *desc) { } +#endif diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c index ff40e0f5e2e2..d4ae0b1ccc00 100644 --- a/kernel/irq/handle.c +++ b/kernel/irq/handle.c @@ -123,13 +123,15 @@ irqreturn_t handle_irq_event(struct irq_desc *desc) irqreturn_t ret; desc->status &= ~IRQ_PENDING; - desc->status |= IRQ_INPROGRESS; + irq_compat_set_progress(desc); + desc->istate |= IRQS_INPROGRESS; raw_spin_unlock(&desc->lock); ret = handle_irq_event_percpu(desc, action); raw_spin_lock(&desc->lock); - desc->status &= ~IRQ_INPROGRESS; + desc->istate &= ~IRQS_INPROGRESS; + irq_compat_clr_progress(desc); return ret; } diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index f5d28e1e1eda..d1cb1f8df6fe 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -13,6 +13,7 @@ # define IRQ_BITMAP_BITS NR_IRQS #endif +#include "compat.h" #include "settings.h" #define istate core_internal_state__do_not_mess_with_it @@ -40,11 +41,13 @@ enum { * IRQS_SPURIOUS_DISABLED - was disabled due to spurious interrupt * detection * IRQS_POLL_INPROGRESS - polling in progress + * IRQS_INPROGRESS - Interrupt in progress */ enum { IRQS_AUTODETECT = 0x00000001, IRQS_SPURIOUS_DISABLED = 0x00000002, IRQS_POLL_INPROGRESS = 0x00000008, + IRQS_INPROGRESS = 0x00000010, }; #define irq_data_to_desc(data) container_of(data, struct irq_desc, irq_data) @@ -128,7 +131,6 @@ static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) print_symbol("%s\n", (unsigned long)desc->action->handler); } - P(IRQ_INPROGRESS); P(IRQ_DISABLED); P(IRQ_PENDING); P(IRQ_REPLAY); @@ -143,6 +145,7 @@ static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) P(IRQ_NOAUTOEN); PS(IRQS_AUTODETECT); + PS(IRQS_INPROGRESS); } #undef P diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 5b918ffa46af..7e5a50825088 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -30,7 +30,7 @@ void synchronize_irq(unsigned int irq) { struct irq_desc *desc = irq_to_desc(irq); - unsigned int status; + unsigned int state; if (!desc) return; @@ -42,16 +42,16 @@ void synchronize_irq(unsigned int irq) * Wait until we're out of the critical section. This might * give the wrong answer due to the lack of memory barriers. */ - while (desc->status & IRQ_INPROGRESS) + while (desc->istate & IRQS_INPROGRESS) cpu_relax(); /* Ok, that indicated we're done: double-check carefully. */ raw_spin_lock_irqsave(&desc->lock, flags); - status = desc->status; + state = desc->istate; raw_spin_unlock_irqrestore(&desc->lock, flags); /* Oops, that failed? */ - } while (status & IRQ_INPROGRESS); + } while (state & IRQS_INPROGRESS); /* * We made sure that no hardirq handler is running. Now verify @@ -637,9 +637,9 @@ again: * The thread is faster done than the hard interrupt handler * on the other CPU. If we unmask the irq line then the * interrupt can come in again and masks the line, leaves due - * to IRQ_INPROGRESS and the irq line is masked forever. + * to IRQS_INPROGRESS and the irq line is masked forever. */ - if (unlikely(desc->status & IRQ_INPROGRESS)) { + if (unlikely(desc->istate & IRQS_INPROGRESS)) { raw_spin_unlock_irq(&desc->lock); chip_bus_sync_unlock(desc); cpu_relax(); @@ -897,8 +897,9 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) desc->status |= IRQ_PER_CPU; #endif - desc->status &= ~(IRQ_WAITING | IRQ_ONESHOT | IRQ_INPROGRESS); - desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED); + desc->status &= ~(IRQ_WAITING | IRQ_ONESHOT); + desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED | \ + IRQS_INPROGRESS); if (new->flags & IRQF_ONESHOT) desc->status |= IRQ_ONESHOT; diff --git a/kernel/irq/settings.h b/kernel/irq/settings.h index 610f55597ce7..a96140eea409 100644 --- a/kernel/irq/settings.h +++ b/kernel/irq/settings.h @@ -5,3 +5,6 @@ enum { _IRQ_DEFAULT_INIT_FLAGS = IRQ_DEFAULT_INIT_FLAGS, }; + +#undef IRQ_INPROGRESS +#define IRQ_INPROGRESS GOT_YOU_MORON diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index 21c46178b1a6..51504837d8cc 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c @@ -45,10 +45,10 @@ bool irq_wait_for_poll(struct irq_desc *desc) #ifdef CONFIG_SMP do { raw_spin_unlock(&desc->lock); - while (desc->status & IRQ_INPROGRESS) + while (desc->istate & IRQS_INPROGRESS) cpu_relax(); raw_spin_lock(&desc->lock); - } while (desc->status & IRQ_INPROGRESS); + } while (desc->istate & IRQS_INPROGRESS); /* Might have been disabled in meantime */ return !(desc->status & IRQ_DISABLED) && desc->action; #else @@ -88,7 +88,7 @@ static int try_one_irq(int irq, struct irq_desc *desc, bool force) goto out; /* Already running on another processor */ - if (desc->status & IRQ_INPROGRESS) { + if (desc->istate & IRQS_INPROGRESS) { /* * Already running: If it is shared get the other * CPU to go looking for our mystery interrupt too -- cgit v1.2.3 From 3d67baec7f1b01fc289ac1a2f1a7e6d5e43391c6 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 7 Feb 2011 21:02:10 +0100 Subject: genirq: Move IRQ_ONESHOT to core No users outside of core. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 1 - kernel/irq/chip.c | 2 +- kernel/irq/internals.h | 2 ++ kernel/irq/manage.c | 8 ++++---- 4 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 274590fc55a3..1a4c723e74e1 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -71,7 +71,6 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */ #define IRQ_AFFINITY_SET 0x02000000 /* IRQ affinity was set from userspace*/ #define IRQ_SUSPENDED 0x04000000 /* IRQ has gone through suspend sequence */ -#define IRQ_ONESHOT 0x08000000 /* IRQ is not unmasked after hardirq */ #define IRQ_NESTED_THREAD 0x10000000 /* IRQ is nested into another, no own handler thread */ #define IRQF_MODIFY_MASK \ diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 075385549dcd..420fa6bdb117 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -472,7 +472,7 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc) handle_irq_event(desc); - if (!(desc->status & (IRQ_DISABLED | IRQ_ONESHOT))) + if (!(desc->status & IRQ_DISABLED) && !(desc->istate & IRQS_ONESHOT)) unmask_irq(desc); out_unlock: raw_spin_unlock(&desc->lock); diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index d1cb1f8df6fe..36563f731ff8 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -42,12 +42,14 @@ enum { * detection * IRQS_POLL_INPROGRESS - polling in progress * IRQS_INPROGRESS - Interrupt in progress + * IRQS_ONESHOT - irq is not unmasked in primary handler */ enum { IRQS_AUTODETECT = 0x00000001, IRQS_SPURIOUS_DISABLED = 0x00000002, IRQS_POLL_INPROGRESS = 0x00000008, IRQS_INPROGRESS = 0x00000010, + IRQS_ONESHOT = 0x00000020, }; #define irq_data_to_desc(data) container_of(data, struct irq_desc, irq_data) diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 7e5a50825088..aca4208a03ac 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -697,7 +697,7 @@ static int irq_thread(void *data) }; struct irqaction *action = data; struct irq_desc *desc = irq_to_desc(action->irq); - int wake, oneshot = desc->status & IRQ_ONESHOT; + int wake, oneshot = desc->istate & IRQS_ONESHOT; sched_setscheduler(current, SCHED_FIFO, ¶m); current->irqaction = action; @@ -897,12 +897,12 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) desc->status |= IRQ_PER_CPU; #endif - desc->status &= ~(IRQ_WAITING | IRQ_ONESHOT); + desc->status &= ~IRQ_WAITING; desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED | \ - IRQS_INPROGRESS); + IRQS_INPROGRESS | IRQS_ONESHOT); if (new->flags & IRQF_ONESHOT) - desc->status |= IRQ_ONESHOT; + desc->istate |= IRQS_ONESHOT; if (!(desc->status & IRQ_NOAUTOEN)) irq_startup(desc); -- cgit v1.2.3 From 163ef3091195f514a06f064b12914597d2644c55 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 8 Feb 2011 11:39:15 +0100 Subject: genirq: Move IRQ_REPLAY and IRQ_WAITING to core No users outside of core. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 9 +++++---- kernel/irq/autoprobe.c | 11 +++++------ kernel/irq/chip.c | 9 ++++----- kernel/irq/internals.h | 8 ++++++-- kernel/irq/manage.c | 4 ++-- kernel/irq/resend.c | 7 +++++-- kernel/irq/settings.h | 4 ++++ 7 files changed, 31 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 1a4c723e74e1..c38dbd506656 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -53,12 +53,13 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #ifndef CONFIG_GENERIC_HARDIRQS_NO_COMPAT #define IRQ_INPROGRESS 0x00000100 /* DEPRECATED */ +#define IRQ_REPLAY 0x00000200 /* DEPRECATED */ +#define IRQ_WAITING 0x00000400 /* DEPRECATED */ #endif -#define IRQ_DISABLED 0x00000200 /* IRQ disabled - do not enter! */ -#define IRQ_PENDING 0x00000400 /* IRQ pending - replay on enable */ -#define IRQ_REPLAY 0x00000800 /* IRQ has been replayed but not acked yet */ -#define IRQ_WAITING 0x00002000 /* IRQ not yet seen - for autodetection */ +#define IRQ_DISABLED 0x00000800 /* IRQ disabled - do not enter! */ +#define IRQ_PENDING 0x00001000 /* IRQ pending - replay on enable */ + #define IRQ_LEVEL 0x00004000 /* IRQ level triggered */ #define IRQ_MASKED 0x00008000 /* IRQ masked - shouldn't be seen again */ #define IRQ_PER_CPU 0x00010000 /* IRQ is per CPU */ diff --git a/kernel/irq/autoprobe.c b/kernel/irq/autoprobe.c index 916e56e10a2e..9ea8bb99f7c1 100644 --- a/kernel/irq/autoprobe.c +++ b/kernel/irq/autoprobe.c @@ -17,7 +17,7 @@ /* * Autodetection depends on the fact that any interrupt that * comes in on to an unassigned handler will get stuck with - * "IRQ_WAITING" cleared and the interrupt disabled. + * "IRQS_WAITING" cleared and the interrupt disabled. */ static DEFINE_MUTEX(probing_active); @@ -75,8 +75,7 @@ unsigned long probe_irq_on(void) for_each_irq_desc_reverse(i, desc) { raw_spin_lock_irq(&desc->lock); if (!desc->action && !(desc->status & IRQ_NOPROBE)) { - desc->istate |= IRQS_AUTODETECT; - desc->status |= IRQ_WAITING; + desc->istate |= IRQS_AUTODETECT | IRQS_WAITING; if (irq_startup(desc)) desc->status |= IRQ_PENDING; } @@ -96,7 +95,7 @@ unsigned long probe_irq_on(void) if (desc->istate & IRQS_AUTODETECT) { /* It triggered already - consider it spurious. */ - if (!(desc->status & IRQ_WAITING)) { + if (!(desc->istate & IRQS_WAITING)) { desc->istate &= ~IRQS_AUTODETECT; irq_shutdown(desc); } else @@ -131,7 +130,7 @@ unsigned int probe_irq_mask(unsigned long val) for_each_irq_desc(i, desc) { raw_spin_lock_irq(&desc->lock); if (desc->istate & IRQS_AUTODETECT) { - if (i < 16 && !(desc->status & IRQ_WAITING)) + if (i < 16 && !(desc->istate & IRQS_WAITING)) mask |= 1 << i; desc->istate &= ~IRQS_AUTODETECT; @@ -171,7 +170,7 @@ int probe_irq_off(unsigned long val) raw_spin_lock_irq(&desc->lock); if (desc->istate & IRQS_AUTODETECT) { - if (!(desc->status & IRQ_WAITING)) { + if (!(desc->istate & IRQS_WAITING)) { if (!nr_of_irqs) irq_found = i; nr_of_irqs++; diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 420fa6bdb117..59ae14527ecd 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -428,7 +428,7 @@ handle_simple_irq(unsigned int irq, struct irq_desc *desc) if (!irq_check_poll(desc)) goto out_unlock; - desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); + desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); kstat_incr_irqs_this_cpu(irq, desc); if (unlikely(!desc->action || (desc->status & IRQ_DISABLED))) @@ -460,7 +460,7 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc) if (!irq_check_poll(desc)) goto out_unlock; - desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); + desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); kstat_incr_irqs_this_cpu(irq, desc); /* @@ -498,7 +498,7 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc) if (!irq_check_poll(desc)) goto out; - desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); + desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); kstat_incr_irqs_this_cpu(irq, desc); /* @@ -537,8 +537,7 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc) { raw_spin_lock(&desc->lock); - desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); - + desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); /* * If we're currently running this IRQ, or its disabled, * we shouldn't process the IRQ. Mark it pending, handle diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 36563f731ff8..54037533af7a 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -43,6 +43,8 @@ enum { * IRQS_POLL_INPROGRESS - polling in progress * IRQS_INPROGRESS - Interrupt in progress * IRQS_ONESHOT - irq is not unmasked in primary handler + * IRQS_REPLAY - irq is replayed + * IRQS_WAITING - irq is waiting */ enum { IRQS_AUTODETECT = 0x00000001, @@ -50,6 +52,8 @@ enum { IRQS_POLL_INPROGRESS = 0x00000008, IRQS_INPROGRESS = 0x00000010, IRQS_ONESHOT = 0x00000020, + IRQS_REPLAY = 0x00000040, + IRQS_WAITING = 0x00000080, }; #define irq_data_to_desc(data) container_of(data, struct irq_desc, irq_data) @@ -135,8 +139,6 @@ static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) P(IRQ_DISABLED); P(IRQ_PENDING); - P(IRQ_REPLAY); - P(IRQ_WAITING); P(IRQ_LEVEL); P(IRQ_MASKED); #ifdef CONFIG_IRQ_PER_CPU @@ -148,6 +150,8 @@ static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) PS(IRQS_AUTODETECT); PS(IRQS_INPROGRESS); + PS(IRQS_REPLAY); + PS(IRQS_WAITING); } #undef P diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index aca4208a03ac..7971df53d6a9 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -897,9 +897,9 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) desc->status |= IRQ_PER_CPU; #endif - desc->status &= ~IRQ_WAITING; desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED | \ - IRQS_INPROGRESS | IRQS_ONESHOT); + IRQS_INPROGRESS | IRQS_ONESHOT | \ + IRQS_WAITING); if (new->flags & IRQF_ONESHOT) desc->istate |= IRQS_ONESHOT; diff --git a/kernel/irq/resend.c b/kernel/irq/resend.c index 60b20261041b..f83387cd11f3 100644 --- a/kernel/irq/resend.c +++ b/kernel/irq/resend.c @@ -62,8 +62,11 @@ void check_irq_resend(struct irq_desc *desc, unsigned int irq) */ if (desc->status & IRQ_LEVEL) return; - if ((desc->status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { - desc->status = (desc->status & ~IRQ_PENDING) | IRQ_REPLAY; + if (desc->istate & IRQS_REPLAY) + return; + if (desc->status & IRQ_PENDING) { + desc->status &= ~IRQ_PENDING; + desc->istate |= IRQS_REPLAY; if (!desc->irq_data.chip->irq_retrigger || !desc->irq_data.chip->irq_retrigger(&desc->irq_data)) { diff --git a/kernel/irq/settings.h b/kernel/irq/settings.h index a96140eea409..2e7d08fff0ce 100644 --- a/kernel/irq/settings.h +++ b/kernel/irq/settings.h @@ -8,3 +8,7 @@ enum { #undef IRQ_INPROGRESS #define IRQ_INPROGRESS GOT_YOU_MORON +#undef IRQ_REPLAY +#define IRQ_REPLAY GOT_YOU_MORON +#undef IRQ_WAITING +#define IRQ_WAITING GOT_YOU_MORON -- cgit v1.2.3 From c1594b77e46124bb462f961e536120e471c67446 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 7 Feb 2011 22:11:30 +0100 Subject: genirq: Move IRQ_DISABLED to core Keep status in sync until all abusers are fixed. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 4 ++-- kernel/irq/chip.c | 48 +++++++++++++++++++++++++++++++----------------- kernel/irq/compat.h | 12 ++++++++++++ kernel/irq/internals.h | 4 +++- kernel/irq/irqdesc.c | 2 ++ kernel/irq/manage.c | 4 ++-- kernel/irq/migration.c | 2 +- kernel/irq/settings.h | 2 ++ kernel/irq/spurious.c | 4 ++-- 9 files changed, 57 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index c38dbd506656..32efca71ce88 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -55,9 +55,9 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQ_INPROGRESS 0x00000100 /* DEPRECATED */ #define IRQ_REPLAY 0x00000200 /* DEPRECATED */ #define IRQ_WAITING 0x00000400 /* DEPRECATED */ +#define IRQ_DISABLED 0x00000800 /* DEPRECATED */ #endif -#define IRQ_DISABLED 0x00000800 /* IRQ disabled - do not enter! */ #define IRQ_PENDING 0x00001000 /* IRQ pending - replay on enable */ #define IRQ_LEVEL 0x00004000 /* IRQ level triggered */ @@ -231,7 +231,7 @@ struct irq_chip { # define ARCH_IRQ_INIT_FLAGS 0 #endif -#define IRQ_DEFAULT_INIT_FLAGS (IRQ_DISABLED | ARCH_IRQ_INIT_FLAGS) +#define IRQ_DEFAULT_INIT_FLAGS ARCH_IRQ_INIT_FLAGS struct irqaction; extern int setup_irq(unsigned int irq, struct irqaction *new); diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 59ae14527ecd..527df7ab1b05 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -164,9 +164,21 @@ struct irq_data *irq_get_irq_data(unsigned int irq) } EXPORT_SYMBOL_GPL(irq_get_irq_data); +static void irq_state_clr_disabled(struct irq_desc *desc) +{ + desc->istate &= ~IRQS_DISABLED; + irq_compat_clr_disabled(desc); +} + +static void irq_state_set_disabled(struct irq_desc *desc) +{ + desc->istate |= IRQS_DISABLED; + irq_compat_set_disabled(desc); +} + int irq_startup(struct irq_desc *desc) { - desc->status &= ~IRQ_DISABLED; + irq_state_clr_disabled(desc); desc->depth = 0; if (desc->irq_data.chip->irq_startup) { @@ -181,7 +193,7 @@ int irq_startup(struct irq_desc *desc) void irq_shutdown(struct irq_desc *desc) { - desc->status |= IRQ_DISABLED; + irq_state_set_disabled(desc); desc->depth = 1; if (desc->irq_data.chip->irq_shutdown) desc->irq_data.chip->irq_shutdown(&desc->irq_data); @@ -194,7 +206,7 @@ void irq_shutdown(struct irq_desc *desc) void irq_enable(struct irq_desc *desc) { - desc->status &= ~IRQ_DISABLED; + irq_state_clr_disabled(desc); if (desc->irq_data.chip->irq_enable) desc->irq_data.chip->irq_enable(&desc->irq_data); else @@ -204,7 +216,7 @@ void irq_enable(struct irq_desc *desc) void irq_disable(struct irq_desc *desc) { - desc->status |= IRQ_DISABLED; + irq_state_set_disabled(desc); if (desc->irq_data.chip->irq_disable) { desc->irq_data.chip->irq_disable(&desc->irq_data); desc->status |= IRQ_MASKED; @@ -380,7 +392,7 @@ void handle_nested_irq(unsigned int irq) kstat_incr_irqs_this_cpu(irq, desc); action = desc->action; - if (unlikely(!action || (desc->status & IRQ_DISABLED))) + if (unlikely(!action || (desc->istate & IRQS_DISABLED))) goto out_unlock; irq_compat_set_progress(desc); @@ -431,7 +443,7 @@ handle_simple_irq(unsigned int irq, struct irq_desc *desc) desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); kstat_incr_irqs_this_cpu(irq, desc); - if (unlikely(!desc->action || (desc->status & IRQ_DISABLED))) + if (unlikely(!desc->action || (desc->istate & IRQS_DISABLED))) goto out_unlock; handle_irq_event(desc); @@ -467,12 +479,12 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc) * If its disabled or no action available * keep it masked and get out of here */ - if (unlikely(!desc->action || (desc->status & IRQ_DISABLED))) + if (unlikely(!desc->action || (desc->istate & IRQS_DISABLED))) goto out_unlock; handle_irq_event(desc); - if (!(desc->status & IRQ_DISABLED) && !(desc->istate & IRQS_ONESHOT)) + if (!(desc->istate & (IRQS_DISABLED | IRQS_ONESHOT))) unmask_irq(desc); out_unlock: raw_spin_unlock(&desc->lock); @@ -505,7 +517,7 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc) * If its disabled or no action available * then mask it and get out of here: */ - if (unlikely(!desc->action || (desc->status & IRQ_DISABLED))) { + if (unlikely(!desc->action || (desc->istate & IRQS_DISABLED))) { desc->status |= IRQ_PENDING; mask_irq(desc); goto out; @@ -543,8 +555,8 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc) * we shouldn't process the IRQ. Mark it pending, handle * the necessary masking and go out */ - if (unlikely((desc->istate & (IRQS_INPROGRESS) || - (desc->status & IRQ_DISABLED) || !desc->action))) { + if (unlikely((desc->istate & (IRQS_DISABLED | IRQS_INPROGRESS) || + !desc->action))) { if (!irq_check_poll(desc)) { desc->status |= IRQ_PENDING; mask_ack_irq(desc); @@ -567,15 +579,16 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc) * one, we could have masked the irq. * Renable it, if it was not disabled in meantime. */ - if (unlikely((desc->status & - (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) == - (IRQ_PENDING | IRQ_MASKED))) { - unmask_irq(desc); + if (unlikely(desc->status & IRQ_PENDING)) { + if (!(desc->istate & IRQS_DISABLED) && + (desc->status & IRQ_MASKED)) + unmask_irq(desc); } handle_irq_event(desc); - } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING); + } while ((desc->status & IRQ_PENDING) && + !(desc->istate & IRQS_DISABLED)); out_unlock: raw_spin_unlock(&desc->lock); @@ -639,7 +652,8 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, if (handle == handle_bad_irq) { if (desc->irq_data.chip != &no_irq_chip) mask_ack_irq(desc); - desc->status |= IRQ_DISABLED; + irq_compat_set_disabled(desc); + desc->istate |= IRQS_DISABLED; desc->depth = 1; } desc->handle_irq = handle; diff --git a/kernel/irq/compat.h b/kernel/irq/compat.h index aac6e400e608..bc0c2a501e82 100644 --- a/kernel/irq/compat.h +++ b/kernel/irq/compat.h @@ -11,7 +11,19 @@ static inline void irq_compat_clr_progress(struct irq_desc *desc) { desc->status &= ~IRQ_INPROGRESS; } +static inline void irq_compat_set_disabled(struct irq_desc *desc) +{ + desc->status |= IRQ_DISABLED; +} + +static inline void irq_compat_clr_disabled(struct irq_desc *desc) +{ + desc->status &= ~IRQ_DISABLED; +} #else static inline void irq_compat_set_progress(struct irq_desc *desc) { } static inline void irq_compat_clr_progress(struct irq_desc *desc) { } +static inline void irq_compat_set_disabled(struct irq_desc *desc) { } +static inline void irq_compat_clr_disabled(struct irq_desc *desc) { } #endif + diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 54037533af7a..919d2dd0bb33 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -45,6 +45,7 @@ enum { * IRQS_ONESHOT - irq is not unmasked in primary handler * IRQS_REPLAY - irq is replayed * IRQS_WAITING - irq is waiting + * IRQS_DISABLED - irq is disabled */ enum { IRQS_AUTODETECT = 0x00000001, @@ -54,6 +55,7 @@ enum { IRQS_ONESHOT = 0x00000020, IRQS_REPLAY = 0x00000040, IRQS_WAITING = 0x00000080, + IRQS_DISABLED = 0x00000100, }; #define irq_data_to_desc(data) container_of(data, struct irq_desc, irq_data) @@ -137,7 +139,6 @@ static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) print_symbol("%s\n", (unsigned long)desc->action->handler); } - P(IRQ_DISABLED); P(IRQ_PENDING); P(IRQ_LEVEL); P(IRQ_MASKED); @@ -152,6 +153,7 @@ static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) PS(IRQS_INPROGRESS); PS(IRQS_REPLAY); PS(IRQS_WAITING); + PS(IRQS_DISABLED); } #undef P diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index 8b87f2ce0203..78866d050bc9 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c @@ -80,6 +80,7 @@ static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node) desc->irq_data.handler_data = NULL; desc->irq_data.msi_desc = NULL; desc->status = _IRQ_DEFAULT_INIT_FLAGS; + desc->istate = IRQS_DISABLED; desc->handle_irq = handle_bad_irq; desc->depth = 1; desc->irq_count = 0; @@ -247,6 +248,7 @@ int __init early_irq_init(void) struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = { [0 ... NR_IRQS-1] = { .status = _IRQ_DEFAULT_INIT_FLAGS, + .istate = IRQS_DISABLED, .handle_irq = handle_bad_irq, .depth = 1, .lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock), diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 7971df53d6a9..77ff275b54cf 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -646,7 +646,7 @@ again: goto again; } - if (!(desc->status & IRQ_DISABLED) && (desc->status & IRQ_MASKED)) { + if (!(desc->istate & IRQS_DISABLED) && (desc->status & IRQ_MASKED)) { desc->status &= ~IRQ_MASKED; desc->irq_data.chip->irq_unmask(&desc->irq_data); } @@ -709,7 +709,7 @@ static int irq_thread(void *data) atomic_inc(&desc->threads_active); raw_spin_lock_irq(&desc->lock); - if (unlikely(desc->status & IRQ_DISABLED)) { + if (unlikely(desc->istate & IRQS_DISABLED)) { /* * CHECKME: We might need a dedicated * IRQ_THREAD_PENDING flag here, which diff --git a/kernel/irq/migration.c b/kernel/irq/migration.c index 441fd629ff04..8c68cb8555a7 100644 --- a/kernel/irq/migration.c +++ b/kernel/irq/migration.c @@ -61,7 +61,7 @@ void move_native_irq(int irq) if (likely(!(desc->status & IRQ_MOVE_PENDING))) return; - if (unlikely(desc->status & IRQ_DISABLED)) + if (unlikely(desc->istate & IRQS_DISABLED)) return; /* diff --git a/kernel/irq/settings.h b/kernel/irq/settings.h index 2e7d08fff0ce..5e3411c7c62b 100644 --- a/kernel/irq/settings.h +++ b/kernel/irq/settings.h @@ -12,3 +12,5 @@ enum { #define IRQ_REPLAY GOT_YOU_MORON #undef IRQ_WAITING #define IRQ_WAITING GOT_YOU_MORON +#undef IRQ_DISABLED +#define IRQ_DISABLED GOT_YOU_MORON diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index 51504837d8cc..367614f858ff 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c @@ -50,7 +50,7 @@ bool irq_wait_for_poll(struct irq_desc *desc) raw_spin_lock(&desc->lock); } while (desc->istate & IRQS_INPROGRESS); /* Might have been disabled in meantime */ - return !(desc->status & IRQ_DISABLED) && desc->action; + return !(desc->istate & IRQS_DISABLED) && desc->action; #else return false; #endif @@ -75,7 +75,7 @@ static int try_one_irq(int irq, struct irq_desc *desc, bool force) * Do not poll disabled interrupts unless the spurious * disabled poller asks explicitely. */ - if ((desc->status & IRQ_DISABLED) && !force) + if ((desc->istate & IRQS_DISABLED) && !force) goto out; /* -- cgit v1.2.3 From 2a0d6fb335d4428285dab2d254911748e6040807 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 8 Feb 2011 12:17:57 +0100 Subject: genirq: Move IRQ_PENDING flag to core Keep status in sync until all users are fixed. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 2 +- kernel/irq/autoprobe.c | 6 ++++-- kernel/irq/chip.c | 10 ++++++---- kernel/irq/compat.h | 12 +++++++++++- kernel/irq/handle.c | 3 ++- kernel/irq/internals.h | 4 +++- kernel/irq/manage.c | 5 +++-- kernel/irq/pm.c | 3 ++- kernel/irq/resend.c | 5 +++-- kernel/irq/settings.h | 2 ++ kernel/irq/spurious.c | 5 +++-- 11 files changed, 40 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 32efca71ce88..7ca55c9deba4 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -56,9 +56,9 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQ_REPLAY 0x00000200 /* DEPRECATED */ #define IRQ_WAITING 0x00000400 /* DEPRECATED */ #define IRQ_DISABLED 0x00000800 /* DEPRECATED */ +#define IRQ_PENDING 0x00001000 /* DEPRECATED */ #endif -#define IRQ_PENDING 0x00001000 /* IRQ pending - replay on enable */ #define IRQ_LEVEL 0x00004000 /* IRQ level triggered */ #define IRQ_MASKED 0x00008000 /* IRQ masked - shouldn't be seen again */ diff --git a/kernel/irq/autoprobe.c b/kernel/irq/autoprobe.c index 9ea8bb99f7c1..aab64c262726 100644 --- a/kernel/irq/autoprobe.c +++ b/kernel/irq/autoprobe.c @@ -76,8 +76,10 @@ unsigned long probe_irq_on(void) raw_spin_lock_irq(&desc->lock); if (!desc->action && !(desc->status & IRQ_NOPROBE)) { desc->istate |= IRQS_AUTODETECT | IRQS_WAITING; - if (irq_startup(desc)) - desc->status |= IRQ_PENDING; + if (irq_startup(desc)) { + irq_compat_set_pending(desc); + desc->istate |= IRQS_PENDING; + } } raw_spin_unlock_irq(&desc->lock); } diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 527df7ab1b05..17c87865bfb1 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -518,7 +518,8 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc) * then mask it and get out of here: */ if (unlikely(!desc->action || (desc->istate & IRQS_DISABLED))) { - desc->status |= IRQ_PENDING; + irq_compat_set_pending(desc); + desc->istate |= IRQS_PENDING; mask_irq(desc); goto out; } @@ -558,7 +559,8 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc) if (unlikely((desc->istate & (IRQS_DISABLED | IRQS_INPROGRESS) || !desc->action))) { if (!irq_check_poll(desc)) { - desc->status |= IRQ_PENDING; + irq_compat_set_pending(desc); + desc->istate |= IRQS_PENDING; mask_ack_irq(desc); goto out_unlock; } @@ -579,7 +581,7 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc) * one, we could have masked the irq. * Renable it, if it was not disabled in meantime. */ - if (unlikely(desc->status & IRQ_PENDING)) { + if (unlikely(desc->istate & IRQS_PENDING)) { if (!(desc->istate & IRQS_DISABLED) && (desc->status & IRQ_MASKED)) unmask_irq(desc); @@ -587,7 +589,7 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc) handle_irq_event(desc); - } while ((desc->status & IRQ_PENDING) && + } while ((desc->istate & IRQS_PENDING) && !(desc->istate & IRQS_DISABLED)); out_unlock: diff --git a/kernel/irq/compat.h b/kernel/irq/compat.h index bc0c2a501e82..0067a69781f4 100644 --- a/kernel/irq/compat.h +++ b/kernel/irq/compat.h @@ -15,15 +15,25 @@ static inline void irq_compat_set_disabled(struct irq_desc *desc) { desc->status |= IRQ_DISABLED; } - static inline void irq_compat_clr_disabled(struct irq_desc *desc) { desc->status &= ~IRQ_DISABLED; } +static inline void irq_compat_set_pending(struct irq_desc *desc) +{ + desc->status |= IRQ_PENDING; +} + +static inline void irq_compat_clr_pending(struct irq_desc *desc) +{ + desc->status &= ~IRQ_PENDING; +} #else static inline void irq_compat_set_progress(struct irq_desc *desc) { } static inline void irq_compat_clr_progress(struct irq_desc *desc) { } static inline void irq_compat_set_disabled(struct irq_desc *desc) { } static inline void irq_compat_clr_disabled(struct irq_desc *desc) { } +static inline void irq_compat_set_pending(struct irq_desc *desc) { } +static inline void irq_compat_clr_pending(struct irq_desc *desc) { } #endif diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c index d4ae0b1ccc00..6e34bdbeb26a 100644 --- a/kernel/irq/handle.c +++ b/kernel/irq/handle.c @@ -122,7 +122,8 @@ irqreturn_t handle_irq_event(struct irq_desc *desc) struct irqaction *action = desc->action; irqreturn_t ret; - desc->status &= ~IRQ_PENDING; + irq_compat_clr_pending(desc); + desc->istate &= ~IRQS_PENDING; irq_compat_set_progress(desc); desc->istate |= IRQS_INPROGRESS; raw_spin_unlock(&desc->lock); diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 919d2dd0bb33..fdf2524437eb 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -46,6 +46,7 @@ enum { * IRQS_REPLAY - irq is replayed * IRQS_WAITING - irq is waiting * IRQS_DISABLED - irq is disabled + * IRQS_PENDING - irq is pending and replayed later */ enum { IRQS_AUTODETECT = 0x00000001, @@ -56,6 +57,7 @@ enum { IRQS_REPLAY = 0x00000040, IRQS_WAITING = 0x00000080, IRQS_DISABLED = 0x00000100, + IRQS_PENDING = 0x00000200, }; #define irq_data_to_desc(data) container_of(data, struct irq_desc, irq_data) @@ -139,7 +141,6 @@ static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) print_symbol("%s\n", (unsigned long)desc->action->handler); } - P(IRQ_PENDING); P(IRQ_LEVEL); P(IRQ_MASKED); #ifdef CONFIG_IRQ_PER_CPU @@ -154,6 +155,7 @@ static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) PS(IRQS_REPLAY); PS(IRQS_WAITING); PS(IRQS_DISABLED); + PS(IRQS_PENDING); } #undef P diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 77ff275b54cf..ac060814a787 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -714,10 +714,11 @@ static int irq_thread(void *data) * CHECKME: We might need a dedicated * IRQ_THREAD_PENDING flag here, which * retriggers the thread in check_irq_resend() - * but AFAICT IRQ_PENDING should be fine as it + * but AFAICT IRQS_PENDING should be fine as it * retriggers the interrupt itself --- tglx */ - desc->status |= IRQ_PENDING; + irq_compat_set_pending(desc); + desc->istate |= IRQS_PENDING; raw_spin_unlock_irq(&desc->lock); } else { raw_spin_unlock_irq(&desc->lock); diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c index d6bfb89cce91..d7389418e91a 100644 --- a/kernel/irq/pm.c +++ b/kernel/irq/pm.c @@ -69,7 +69,8 @@ int check_wakeup_irqs(void) int irq; for_each_irq_desc(irq, desc) - if ((desc->status & IRQ_WAKEUP) && (desc->status & IRQ_PENDING)) + if ((desc->status & IRQ_WAKEUP) && + (desc->istate & IRQS_PENDING)) return -EBUSY; return 0; diff --git a/kernel/irq/resend.c b/kernel/irq/resend.c index f83387cd11f3..ff1fea060014 100644 --- a/kernel/irq/resend.c +++ b/kernel/irq/resend.c @@ -64,8 +64,9 @@ void check_irq_resend(struct irq_desc *desc, unsigned int irq) return; if (desc->istate & IRQS_REPLAY) return; - if (desc->status & IRQ_PENDING) { - desc->status &= ~IRQ_PENDING; + if (desc->istate & IRQS_PENDING) { + irq_compat_clr_pending(desc); + desc->istate &= ~IRQS_PENDING; desc->istate |= IRQS_REPLAY; if (!desc->irq_data.chip->irq_retrigger || diff --git a/kernel/irq/settings.h b/kernel/irq/settings.h index 5e3411c7c62b..623fcf83e7de 100644 --- a/kernel/irq/settings.h +++ b/kernel/irq/settings.h @@ -14,3 +14,5 @@ enum { #define IRQ_WAITING GOT_YOU_MORON #undef IRQ_DISABLED #define IRQ_DISABLED GOT_YOU_MORON +#undef IRQ_PENDING +#define IRQ_PENDING GOT_YOU_MORON diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index 367614f858ff..692ce2bae302 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c @@ -93,7 +93,8 @@ static int try_one_irq(int irq, struct irq_desc *desc, bool force) * Already running: If it is shared get the other * CPU to go looking for our mystery interrupt too */ - desc->status |= IRQ_PENDING; + irq_compat_set_pending(desc); + desc->istate |= IRQS_PENDING; goto out; } @@ -103,7 +104,7 @@ static int try_one_irq(int irq, struct irq_desc *desc, bool force) if (handle_irq_event(desc) == IRQ_HANDLED) ret = IRQ_HANDLED; action = desc->action; - } while ((desc->status & IRQ_PENDING) && action); + } while ((desc->istate & IRQS_PENDING) && action); desc->istate &= ~IRQS_POLL_INPROGRESS; out: raw_spin_unlock(&desc->lock); -- cgit v1.2.3 From 6e40262ea43c4b0e3f435b3a083e4461ef921c17 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 8 Feb 2011 12:36:06 +0100 Subject: genirq: Move IRQ_MASKED to core Keep status in sync until all users are fixed. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 2 +- kernel/irq/chip.c | 28 ++++++++++++++++++++-------- kernel/irq/compat.h | 11 +++++++++++ kernel/irq/internals.h | 4 +++- kernel/irq/manage.c | 5 +++-- kernel/irq/migration.c | 2 +- kernel/irq/settings.h | 2 ++ 7 files changed, 41 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 7ca55c9deba4..9800bac4c398 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -57,11 +57,11 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQ_WAITING 0x00000400 /* DEPRECATED */ #define IRQ_DISABLED 0x00000800 /* DEPRECATED */ #define IRQ_PENDING 0x00001000 /* DEPRECATED */ +#define IRQ_MASKED 0x00002000 /* DEPRECATED */ #endif #define IRQ_LEVEL 0x00004000 /* IRQ level triggered */ -#define IRQ_MASKED 0x00008000 /* IRQ masked - shouldn't be seen again */ #define IRQ_PER_CPU 0x00010000 /* IRQ is per CPU */ #define IRQ_NOPROBE 0x00020000 /* IRQ is not valid for probing */ #define IRQ_NOREQUEST 0x00040000 /* IRQ cannot be requested */ diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 17c87865bfb1..73b2e7e00934 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -176,6 +176,18 @@ static void irq_state_set_disabled(struct irq_desc *desc) irq_compat_set_disabled(desc); } +static void irq_state_clr_masked(struct irq_desc *desc) +{ + desc->istate &= ~IRQS_MASKED; + irq_compat_clr_masked(desc); +} + +static void irq_state_set_masked(struct irq_desc *desc) +{ + desc->istate |= IRQS_MASKED; + irq_compat_set_masked(desc); +} + int irq_startup(struct irq_desc *desc) { irq_state_clr_disabled(desc); @@ -183,7 +195,7 @@ int irq_startup(struct irq_desc *desc) if (desc->irq_data.chip->irq_startup) { int ret = desc->irq_data.chip->irq_startup(&desc->irq_data); - desc->status &= ~IRQ_MASKED; + irq_state_clr_masked(desc); return ret; } @@ -201,7 +213,7 @@ void irq_shutdown(struct irq_desc *desc) desc->irq_data.chip->irq_disable(&desc->irq_data); else desc->irq_data.chip->irq_mask(&desc->irq_data); - desc->status |= IRQ_MASKED; + irq_state_set_masked(desc); } void irq_enable(struct irq_desc *desc) @@ -211,7 +223,7 @@ void irq_enable(struct irq_desc *desc) desc->irq_data.chip->irq_enable(&desc->irq_data); else desc->irq_data.chip->irq_unmask(&desc->irq_data); - desc->status &= ~IRQ_MASKED; + irq_state_clr_masked(desc); } void irq_disable(struct irq_desc *desc) @@ -219,8 +231,8 @@ void irq_disable(struct irq_desc *desc) irq_state_set_disabled(desc); if (desc->irq_data.chip->irq_disable) { desc->irq_data.chip->irq_disable(&desc->irq_data); - desc->status |= IRQ_MASKED; } + irq_state_set_masked(desc); } #ifndef CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED @@ -352,14 +364,14 @@ static inline void mask_ack_irq(struct irq_desc *desc) if (desc->irq_data.chip->irq_ack) desc->irq_data.chip->irq_ack(&desc->irq_data); } - desc->status |= IRQ_MASKED; + irq_state_set_masked(desc); } static inline void mask_irq(struct irq_desc *desc) { if (desc->irq_data.chip->irq_mask) { desc->irq_data.chip->irq_mask(&desc->irq_data); - desc->status |= IRQ_MASKED; + irq_state_set_masked(desc); } } @@ -367,7 +379,7 @@ static inline void unmask_irq(struct irq_desc *desc) { if (desc->irq_data.chip->irq_unmask) { desc->irq_data.chip->irq_unmask(&desc->irq_data); - desc->status &= ~IRQ_MASKED; + irq_state_clr_masked(desc); } } @@ -583,7 +595,7 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc) */ if (unlikely(desc->istate & IRQS_PENDING)) { if (!(desc->istate & IRQS_DISABLED) && - (desc->status & IRQ_MASKED)) + (desc->istate & IRQS_MASKED)) unmask_irq(desc); } diff --git a/kernel/irq/compat.h b/kernel/irq/compat.h index 0067a69781f4..593abecbcc44 100644 --- a/kernel/irq/compat.h +++ b/kernel/irq/compat.h @@ -28,6 +28,15 @@ static inline void irq_compat_clr_pending(struct irq_desc *desc) { desc->status &= ~IRQ_PENDING; } +static inline void irq_compat_set_masked(struct irq_desc *desc) +{ + desc->status |= IRQ_MASKED; +} + +static inline void irq_compat_clr_masked(struct irq_desc *desc) +{ + desc->status &= ~IRQ_MASKED; +} #else static inline void irq_compat_set_progress(struct irq_desc *desc) { } static inline void irq_compat_clr_progress(struct irq_desc *desc) { } @@ -35,5 +44,7 @@ static inline void irq_compat_set_disabled(struct irq_desc *desc) { } static inline void irq_compat_clr_disabled(struct irq_desc *desc) { } static inline void irq_compat_set_pending(struct irq_desc *desc) { } static inline void irq_compat_clr_pending(struct irq_desc *desc) { } +static inline void irq_compat_set_masked(struct irq_desc *desc) { } +static inline void irq_compat_clr_masked(struct irq_desc *desc) { } #endif diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index fdf2524437eb..3f2fcc194dcc 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -47,6 +47,7 @@ enum { * IRQS_WAITING - irq is waiting * IRQS_DISABLED - irq is disabled * IRQS_PENDING - irq is pending and replayed later + * IRQS_MASKED - irq is masked */ enum { IRQS_AUTODETECT = 0x00000001, @@ -58,6 +59,7 @@ enum { IRQS_WAITING = 0x00000080, IRQS_DISABLED = 0x00000100, IRQS_PENDING = 0x00000200, + IRQS_MASKED = 0x00000400, }; #define irq_data_to_desc(data) container_of(data, struct irq_desc, irq_data) @@ -142,7 +144,6 @@ static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) } P(IRQ_LEVEL); - P(IRQ_MASKED); #ifdef CONFIG_IRQ_PER_CPU P(IRQ_PER_CPU); #endif @@ -156,6 +157,7 @@ static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) PS(IRQS_WAITING); PS(IRQS_DISABLED); PS(IRQS_PENDING); + PS(IRQS_MASKED); } #undef P diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index ac060814a787..83fd20194e5b 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -646,8 +646,9 @@ again: goto again; } - if (!(desc->istate & IRQS_DISABLED) && (desc->status & IRQ_MASKED)) { - desc->status &= ~IRQ_MASKED; + if (!(desc->istate & IRQS_DISABLED) && (desc->istate & IRQS_MASKED)) { + irq_compat_clr_masked(desc); + desc->istate &= ~IRQS_MASKED; desc->irq_data.chip->irq_unmask(&desc->irq_data); } raw_spin_unlock_irq(&desc->lock); diff --git a/kernel/irq/migration.c b/kernel/irq/migration.c index 8c68cb8555a7..6f2f98480354 100644 --- a/kernel/irq/migration.c +++ b/kernel/irq/migration.c @@ -69,7 +69,7 @@ void move_native_irq(int irq) * threaded interrupt with ONESHOT set, we can end up with an * interrupt storm. */ - masked = desc->status & IRQ_MASKED; + masked = desc->istate & IRQS_MASKED; if (!masked) desc->irq_data.chip->irq_mask(&desc->irq_data); move_masked_irq(irq); diff --git a/kernel/irq/settings.h b/kernel/irq/settings.h index 623fcf83e7de..2cd45fd5ec8a 100644 --- a/kernel/irq/settings.h +++ b/kernel/irq/settings.h @@ -16,3 +16,5 @@ enum { #define IRQ_DISABLED GOT_YOU_MORON #undef IRQ_PENDING #define IRQ_PENDING GOT_YOU_MORON +#undef IRQ_MASKED +#define IRQ_MASKED GOT_YOU_MORON -- cgit v1.2.3 From c531e8361f1968d664e6e97fbd3bfa4cf0e62e42 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 8 Feb 2011 12:44:58 +0100 Subject: genirq: Move IRQ_SUSPENDED to core No users outside of core. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 2 -- kernel/irq/internals.h | 2 ++ kernel/irq/manage.c | 8 ++++---- kernel/irq/pm.c | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 9800bac4c398..3ce45c257edb 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -60,7 +60,6 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQ_MASKED 0x00002000 /* DEPRECATED */ #endif - #define IRQ_LEVEL 0x00004000 /* IRQ level triggered */ #define IRQ_PER_CPU 0x00010000 /* IRQ is per CPU */ #define IRQ_NOPROBE 0x00020000 /* IRQ is not valid for probing */ @@ -71,7 +70,6 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQ_NO_BALANCING 0x00400000 /* IRQ is excluded from balancing */ #define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */ #define IRQ_AFFINITY_SET 0x02000000 /* IRQ affinity was set from userspace*/ -#define IRQ_SUSPENDED 0x04000000 /* IRQ has gone through suspend sequence */ #define IRQ_NESTED_THREAD 0x10000000 /* IRQ is nested into another, no own handler thread */ #define IRQF_MODIFY_MASK \ diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 3f2fcc194dcc..46889119e6a6 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -48,6 +48,7 @@ enum { * IRQS_DISABLED - irq is disabled * IRQS_PENDING - irq is pending and replayed later * IRQS_MASKED - irq is masked + * IRQS_SUSPENDED - irq is suspended */ enum { IRQS_AUTODETECT = 0x00000001, @@ -60,6 +61,7 @@ enum { IRQS_DISABLED = 0x00000100, IRQS_PENDING = 0x00000200, IRQS_MASKED = 0x00000400, + IRQS_SUSPENDED = 0x00000800, }; #define irq_data_to_desc(data) container_of(data, struct irq_desc, irq_data) diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 83fd20194e5b..b912de4ff4de 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -326,7 +326,7 @@ void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend) if (suspend) { if (!desc->action || (desc->action->flags & IRQF_NO_SUSPEND)) return; - desc->status |= IRQ_SUSPENDED; + desc->istate |= IRQS_SUSPENDED; } if (!desc->depth++) @@ -388,7 +388,7 @@ EXPORT_SYMBOL(disable_irq); void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume) { if (resume) { - if (!(desc->status & IRQ_SUSPENDED)) { + if (!(desc->istate & IRQS_SUSPENDED)) { if (!desc->action) return; if (!(desc->action->flags & IRQF_FORCE_RESUME)) @@ -396,7 +396,7 @@ void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume) /* Pretend that it got disabled ! */ desc->depth++; } - desc->status &= ~IRQ_SUSPENDED; + desc->istate &= ~IRQS_SUSPENDED; } switch (desc->depth) { @@ -405,7 +405,7 @@ void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume) WARN(1, KERN_WARNING "Unbalanced enable for IRQ %d\n", irq); break; case 1: { - if (desc->status & IRQ_SUSPENDED) + if (desc->istate & IRQS_SUSPENDED) goto err_out; /* Prevent probing on this irq: */ desc->status |= IRQ_NOPROBE; diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c index d7389418e91a..d81337fc1cff 100644 --- a/kernel/irq/pm.c +++ b/kernel/irq/pm.c @@ -18,7 +18,7 @@ * During system-wide suspend or hibernation device drivers need to be prevented * from receiving interrupts and this function is provided for this purpose. * It marks all interrupt lines in use, except for the timer ones, as disabled - * and sets the IRQ_SUSPENDED flag for each of them. + * and sets the IRQS_SUSPENDED flag for each of them. */ void suspend_device_irqs(void) { @@ -34,7 +34,7 @@ void suspend_device_irqs(void) } for_each_irq_desc(irq, desc) - if (desc->status & IRQ_SUSPENDED) + if (desc->istate & IRQS_SUSPENDED) synchronize_irq(irq); } EXPORT_SYMBOL_GPL(suspend_device_irqs); @@ -43,7 +43,7 @@ EXPORT_SYMBOL_GPL(suspend_device_irqs); * resume_device_irqs - enable interrupt lines disabled by suspend_device_irqs() * * Enable all interrupt lines previously disabled by suspend_device_irqs() that - * have the IRQ_SUSPENDED flag set. + * have the IRQS_SUSPENDED flag set. */ void resume_device_irqs(void) { -- cgit v1.2.3 From 91c499178139d6597e68db19638e4135510a34b8 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 3 Feb 2011 20:48:29 +0100 Subject: genirq: Add state field to irq_data Some chip implementations need to access certain status flags. With sparse irqs that requires a lookup of the irq descriptor. Add a state field which contains such flags. Name it in a way which will make coders happy to access it with the proper accessor functions. And it's easy to grep for. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 3 +++ include/linux/irqdesc.h | 1 + 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 3ce45c257edb..62bb08e4af13 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -102,6 +102,8 @@ struct msi_desc; * struct irq_data - per irq and irq chip data passed down to chip functions * @irq: interrupt number * @node: node index useful for balancing + * @state_use_accessor: status information for irq chip functions. + * Use accessor functions to deal with it * @chip: low level interrupt hardware access * @handler_data: per-IRQ data for the irq_chip methods * @chip_data: platform-specific per-chip private data for the chip @@ -116,6 +118,7 @@ struct msi_desc; struct irq_data { unsigned int irq; unsigned int node; + unsigned int state_use_accessors; struct irq_chip *chip; void *handler_data; void *chip_data; diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index 782bf9851a9f..581d9665fd38 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h @@ -48,6 +48,7 @@ struct irq_desc { struct { unsigned int irq; unsigned int node; + unsigned int pad_do_not_even_think_about_it; struct irq_chip *chip; void *handler_data; void *chip_data; -- cgit v1.2.3 From f230b6d5c48f8d12f4dfa1f8b5ab0b0320076d21 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 5 Feb 2011 15:20:04 +0100 Subject: genirq: Add IRQ_MOVE_PENDING to irq_data.state chip implementations need to know about it. Keep status in sync until all users are fixed. Accessor function: irqd_is_setaffinity_pending(irqdata) Coders who access them directly will be tracked down and slapped with stinking trouts. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 20 ++++++++++++++++++-- kernel/irq/compat.h | 11 +++++++++++ kernel/irq/internals.h | 15 +++++++++++++++ kernel/irq/manage.c | 4 ++-- kernel/irq/migration.c | 6 +++--- kernel/irq/proc.c | 2 +- kernel/irq/settings.h | 2 ++ 7 files changed, 52 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 62bb08e4af13..2899905bfac7 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -58,15 +58,16 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQ_DISABLED 0x00000800 /* DEPRECATED */ #define IRQ_PENDING 0x00001000 /* DEPRECATED */ #define IRQ_MASKED 0x00002000 /* DEPRECATED */ +/* DEPRECATED use irq_setaffinity_pending() instead*/ +#define IRQ_MOVE_PENDING 0x00004000 #endif -#define IRQ_LEVEL 0x00004000 /* IRQ level triggered */ +#define IRQ_LEVEL 0x00008000 /* IRQ level triggered */ #define IRQ_PER_CPU 0x00010000 /* IRQ is per CPU */ #define IRQ_NOPROBE 0x00020000 /* IRQ is not valid for probing */ #define IRQ_NOREQUEST 0x00040000 /* IRQ cannot be requested */ #define IRQ_NOAUTOEN 0x00080000 /* IRQ will not be enabled on request irq */ #define IRQ_WAKEUP 0x00100000 /* IRQ triggers system wakeup */ -#define IRQ_MOVE_PENDING 0x00200000 /* need to re-target IRQ destination */ #define IRQ_NO_BALANCING 0x00400000 /* IRQ is excluded from balancing */ #define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */ #define IRQ_AFFINITY_SET 0x02000000 /* IRQ affinity was set from userspace*/ @@ -128,6 +129,21 @@ struct irq_data { #endif }; +/* + * Bit masks for irq_data.state + * + * IRQD_SETAFFINITY_PENDING - Affinity setting is pending + */ +enum { + /* Bit 0 - 7 reserved for TYPE will use later */ + IRQD_SETAFFINITY_PENDING = (1 << 8), +}; + +static inline bool irqd_is_setaffinity_pending(struct irq_data *d) +{ + return d->state_use_accessors & IRQD_SETAFFINITY_PENDING; +} + /** * struct irq_chip - hardware interrupt chip descriptor * diff --git a/kernel/irq/compat.h b/kernel/irq/compat.h index 593abecbcc44..5e33aadadacc 100644 --- a/kernel/irq/compat.h +++ b/kernel/irq/compat.h @@ -37,6 +37,15 @@ static inline void irq_compat_clr_masked(struct irq_desc *desc) { desc->status &= ~IRQ_MASKED; } +static inline void irq_compat_set_move_pending(struct irq_desc *desc) +{ + desc->status |= IRQ_MOVE_PENDING; +} + +static inline void irq_compat_clr_move_pending(struct irq_desc *desc) +{ + desc->status &= ~IRQ_MOVE_PENDING; +} #else static inline void irq_compat_set_progress(struct irq_desc *desc) { } static inline void irq_compat_clr_progress(struct irq_desc *desc) { } @@ -46,5 +55,7 @@ static inline void irq_compat_set_pending(struct irq_desc *desc) { } static inline void irq_compat_clr_pending(struct irq_desc *desc) { } static inline void irq_compat_set_masked(struct irq_desc *desc) { } static inline void irq_compat_clr_masked(struct irq_desc *desc) { } +static inline void irq_compat_set_move_pending(struct irq_desc *desc) { } +static inline void irq_compat_clr_move_pending(struct irq_desc *desc) { } #endif diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index cef0849dcfa5..e93e6090cd47 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -124,6 +124,21 @@ static inline void chip_bus_sync_unlock(struct irq_desc *desc) desc->irq_data.chip->irq_bus_sync_unlock(&desc->irq_data); } +/* + * Manipulation functions for irq_data.state + */ +static inline void irqd_set_move_pending(struct irq_data *d) +{ + d->state_use_accessors |= IRQD_SETAFFINITY_PENDING; + irq_compat_set_move_pending(irq_data_to_desc(d)); +} + +static inline void irqd_clr_move_pending(struct irq_data *d) +{ + d->state_use_accessors &= ~IRQD_SETAFFINITY_PENDING; + irq_compat_clr_move_pending(irq_data_to_desc(d)); +} + /* * Debugging printout: */ diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index ccc9389909ff..9a99c471d470 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -107,7 +107,7 @@ static inline bool irq_can_move_pcntxt(struct irq_desc *desc) } static inline bool irq_move_pending(struct irq_desc *desc) { - return desc->status & IRQ_MOVE_PENDING; + return irqd_is_setaffinity_pending(&desc->irq_data); } static inline void irq_copy_pending(struct irq_desc *desc, const struct cpumask *mask) @@ -156,7 +156,7 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *mask) ret = 0; } } else { - desc->status |= IRQ_MOVE_PENDING; + irqd_set_move_pending(&desc->irq_data); irq_copy_pending(desc, mask); } diff --git a/kernel/irq/migration.c b/kernel/irq/migration.c index 6f2f98480354..9485ae081dcd 100644 --- a/kernel/irq/migration.c +++ b/kernel/irq/migration.c @@ -9,7 +9,7 @@ void move_masked_irq(int irq) struct irq_desc *desc = irq_to_desc(irq); struct irq_chip *chip = desc->irq_data.chip; - if (likely(!(desc->status & IRQ_MOVE_PENDING))) + if (likely(!irqd_is_setaffinity_pending(&desc->irq_data))) return; /* @@ -20,7 +20,7 @@ void move_masked_irq(int irq) return; } - desc->status &= ~IRQ_MOVE_PENDING; + irqd_clr_move_pending(&desc->irq_data); if (unlikely(cpumask_empty(desc->pending_mask))) return; @@ -58,7 +58,7 @@ void move_native_irq(int irq) struct irq_desc *desc = irq_to_desc(irq); bool masked; - if (likely(!(desc->status & IRQ_MOVE_PENDING))) + if (likely(!irqd_is_setaffinity_pending(&desc->irq_data))) return; if (unlikely(desc->istate & IRQS_DISABLED)) diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index 26449239bb46..afe4e6803148 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c @@ -25,7 +25,7 @@ static int irq_affinity_proc_show(struct seq_file *m, void *v) const struct cpumask *mask = desc->irq_data.affinity; #ifdef CONFIG_GENERIC_PENDING_IRQ - if (desc->status & IRQ_MOVE_PENDING) + if (irqd_is_setaffinity_pending(&desc->irq_data)) mask = desc->pending_mask; #endif seq_cpumask(m, mask); diff --git a/kernel/irq/settings.h b/kernel/irq/settings.h index ef09824e4b32..bb104a2dce73 100644 --- a/kernel/irq/settings.h +++ b/kernel/irq/settings.h @@ -20,3 +20,5 @@ enum { #define IRQ_MASKED GOT_YOU_MORON #undef IRQ_WAKEUP #define IRQ_WAKEUP GOT_YOU_MORON +#undef IRQ_MOVE_PENDING +#define IRQ_MOVE_PENDING GOT_YOU_MORON -- cgit v1.2.3 From 6a58fb3bad099076f36f0f30f44507bc3275cdb6 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 8 Feb 2011 15:40:05 +0100 Subject: genirq: Remove CONFIG_IRQ_PER_CPU The saving of this switch is minimal versus the ifdef mess it creates. Simple enable PER_CPU unconditionally and remove the config switch. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 5 ----- kernel/irq/Kconfig | 3 --- kernel/irq/internals.h | 2 -- kernel/irq/manage.c | 9 +++------ 4 files changed, 3 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 2899905bfac7..ab708f27a33b 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -78,13 +78,8 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \ IRQ_PER_CPU | IRQ_NESTED_THREAD) -#ifdef CONFIG_IRQ_PER_CPU # define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU) # define IRQ_NO_BALANCING_MASK (IRQ_PER_CPU | IRQ_NO_BALANCING) -#else -# define CHECK_IRQ_PER_CPU(var) 0 -# define IRQ_NO_BALANCING_MASK IRQ_NO_BALANCING -#endif /* * Return value for chip->irq_set_affinity() diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index 9e2256de1d1a..48ad25f5fa59 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig @@ -32,9 +32,6 @@ config GENERIC_PENDING_IRQ config AUTO_IRQ_AFFINITY def_bool n -config IRQ_PER_CPU - def_bool n - config HARDIRQS_SW_RESEND def_bool n diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index e93e6090cd47..9e32b3d35d35 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -163,9 +163,7 @@ static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) } P(IRQ_LEVEL); -#ifdef CONFIG_IRQ_PER_CPU P(IRQ_PER_CPU); -#endif P(IRQ_NOPROBE); P(IRQ_NOREQUEST); P(IRQ_NOAUTOEN); diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 9a99c471d470..056aa49698b4 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -865,12 +865,10 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) goto mismatch; } -#if defined(CONFIG_IRQ_PER_CPU) /* All handlers must agree on per-cpuness */ if ((old->flags & IRQF_PERCPU) != (new->flags & IRQF_PERCPU)) goto mismatch; -#endif /* add new interrupt at end of irq queue */ do { @@ -894,15 +892,14 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) goto out_mask; } else compat_irq_chip_set_default_handler(desc); -#if defined(CONFIG_IRQ_PER_CPU) - if (new->flags & IRQF_PERCPU) - desc->status |= IRQ_PER_CPU; -#endif desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED | \ IRQS_INPROGRESS | IRQS_ONESHOT | \ IRQS_WAITING); + if (new->flags & IRQF_PERCPU) + desc->status |= IRQ_PER_CPU; + if (new->flags & IRQF_ONESHOT) desc->istate |= IRQS_ONESHOT; -- cgit v1.2.3 From 8f53f92404bead2ab2154d45c8f508880bb5d95d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 8 Feb 2011 16:50:00 +0100 Subject: genirq: Make CHECK_IRQ_PER_CPU an inline and deprecate it Its' too ugly and needs to go. The only users are core code and parisc. Core code does not need it and parisc gets a new check once IRQ_PER_CPU is reflected in irq_data.state. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index ab708f27a33b..3f607ad94220 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -78,8 +78,12 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \ IRQ_PER_CPU | IRQ_NESTED_THREAD) -# define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU) -# define IRQ_NO_BALANCING_MASK (IRQ_PER_CPU | IRQ_NO_BALANCING) +#define IRQ_NO_BALANCING_MASK (IRQ_PER_CPU | IRQ_NO_BALANCING) + +static inline __deprecated bool CHECK_IRQ_PER_CPU(unsigned int status) +{ + return status & IRQ_PER_CPU; +} /* * Return value for chip->irq_set_affinity() -- cgit v1.2.3 From a005677b3dd05decdd8880cf3044ae709856f58f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 8 Feb 2011 17:11:03 +0100 Subject: genirq: Mirror IRQ_PER_CPU and IRQ_NO_BALANCING in irq_data.state That's the right data structure to look at for arch code. Accessor functions are provided. irqd_is_per_cpu(irqdata); irqd_can_balance(irqdata); Coders who access them directly will be tracked down and slapped with stinking trouts. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 16 +++++++++++++++- kernel/irq/chip.c | 15 +++++++++------ kernel/irq/internals.h | 11 +++++++++++ kernel/irq/manage.c | 16 ++++++++++------ kernel/irq/migration.c | 2 +- kernel/irq/settings.h | 36 ++++++++++++++++++++++++++++++++++++ kernel/irq/spurious.c | 3 ++- 7 files changed, 84 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 3f607ad94220..d5312e6fe1aa 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -132,10 +132,14 @@ struct irq_data { * Bit masks for irq_data.state * * IRQD_SETAFFINITY_PENDING - Affinity setting is pending + * IRQD_NO_BALANCING - Balancing disabled for this IRQ + * IRQD_PER_CPU - Interrupt is per cpu */ enum { /* Bit 0 - 7 reserved for TYPE will use later */ - IRQD_SETAFFINITY_PENDING = (1 << 8), + IRQD_SETAFFINITY_PENDING = (1 << 8), + IRQD_NO_BALANCING = (1 << 10), + IRQD_PER_CPU = (1 << 11), }; static inline bool irqd_is_setaffinity_pending(struct irq_data *d) @@ -143,6 +147,16 @@ static inline bool irqd_is_setaffinity_pending(struct irq_data *d) return d->state_use_accessors & IRQD_SETAFFINITY_PENDING; } +static inline bool irqd_is_per_cpu(struct irq_data *d) +{ + return d->state_use_accessors & IRQD_PER_CPU; +} + +static inline bool irqd_can_balance(struct irq_data *d) +{ + return !(d->state_use_accessors & (IRQD_PER_CPU | IRQD_NO_BALANCING)); +} + /** * struct irq_chip - hardware interrupt chip descriptor * diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 73b2e7e00934..b8aa3dfe8301 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -706,12 +706,15 @@ void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set) if (!desc) return; - /* Sanitize flags */ - set &= IRQF_MODIFY_MASK; - clr &= IRQF_MODIFY_MASK; - raw_spin_lock_irqsave(&desc->lock, flags); - desc->status &= ~clr; - desc->status |= set; + + irq_settings_clr_and_set(desc, clr, set); + + irqd_clear(&desc->irq_data, IRQD_NO_BALANCING | IRQD_PER_CPU); + if (irq_settings_has_no_balance_set(desc)) + irqd_set(&desc->irq_data, IRQD_NO_BALANCING); + if (irq_settings_is_per_cpu(desc)) + irqd_set(&desc->irq_data, IRQD_PER_CPU); + raw_spin_unlock_irqrestore(&desc->lock, flags); } diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index b2ba59e73f21..a80b44d2735e 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -139,3 +139,14 @@ static inline void irqd_clr_move_pending(struct irq_data *d) d->state_use_accessors &= ~IRQD_SETAFFINITY_PENDING; irq_compat_clr_move_pending(irq_data_to_desc(d)); } + +static inline void irqd_clear(struct irq_data *d, unsigned int mask) +{ + d->state_use_accessors &= ~mask; +} + +static inline void irqd_set(struct irq_data *d, unsigned int mask) +{ + d->state_use_accessors |= mask; +} + diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index f1cfa271ba70..84a0a9c22226 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -73,8 +73,8 @@ int irq_can_set_affinity(unsigned int irq) { struct irq_desc *desc = irq_to_desc(irq); - if ((desc->status & (IRQ_PER_CPU | IRQ_NO_BALANCING)) || - !desc->irq_data.chip || !desc->irq_data.chip->irq_set_affinity) + if (!irqd_can_balance(&desc->irq_data) || !desc->irq_data.chip || + !desc->irq_data.chip->irq_set_affinity) return 0; return 1; @@ -897,8 +897,10 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) IRQS_INPROGRESS | IRQS_ONESHOT | \ IRQS_WAITING); - if (new->flags & IRQF_PERCPU) - desc->status |= IRQ_PER_CPU; + if (new->flags & IRQF_PERCPU) { + irqd_set(&desc->irq_data, IRQD_PER_CPU); + irq_settings_set_per_cpu(desc); + } if (new->flags & IRQF_ONESHOT) desc->istate |= IRQS_ONESHOT; @@ -910,8 +912,10 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) desc->depth = 1; /* Exclude IRQ from balancing if requested */ - if (new->flags & IRQF_NOBALANCING) - desc->status |= IRQ_NO_BALANCING; + if (new->flags & IRQF_NOBALANCING) { + irq_settings_set_no_balancing(desc); + irqd_set(&desc->irq_data, IRQD_NO_BALANCING); + } /* Set default affinity mask once everything is setup */ setup_affinity(irq, desc, mask); diff --git a/kernel/irq/migration.c b/kernel/irq/migration.c index 24f53caddf47..7a93c6b88b25 100644 --- a/kernel/irq/migration.c +++ b/kernel/irq/migration.c @@ -15,7 +15,7 @@ void move_masked_irq(int irq) /* * Paranoia: cpu-local interrupts shouldn't be calling in here anyway. */ - if (desc->status & (IRQ_PER_CPU | IRQ_NO_BALANCING)) { + if (!irqd_can_balance(&desc->irq_data)) { WARN_ON(1); return; } diff --git a/kernel/irq/settings.h b/kernel/irq/settings.h index bb104a2dce73..ba0fffe410ad 100644 --- a/kernel/irq/settings.h +++ b/kernel/irq/settings.h @@ -4,6 +4,9 @@ */ enum { _IRQ_DEFAULT_INIT_FLAGS = IRQ_DEFAULT_INIT_FLAGS, + _IRQ_PER_CPU = IRQ_PER_CPU, + _IRQ_NO_BALANCING = IRQ_NO_BALANCING, + _IRQF_MODIFY_MASK = IRQF_MODIFY_MASK, }; #undef IRQ_INPROGRESS @@ -22,3 +25,36 @@ enum { #define IRQ_WAKEUP GOT_YOU_MORON #undef IRQ_MOVE_PENDING #define IRQ_MOVE_PENDING GOT_YOU_MORON +#undef IRQ_PER_CPU +#define IRQ_PER_CPU GOT_YOU_MORON +#undef IRQ_NO_BALANCING +#define IRQ_NO_BALANCING GOT_YOU_MORON +#undef IRQF_MODIFY_MASK +#define IRQF_MODIFY_MASK GOT_YOU_MORON + +static inline void +irq_settings_clr_and_set(struct irq_desc *desc, u32 clr, u32 set) +{ + desc->status &= ~(clr & _IRQF_MODIFY_MASK); + desc->status |= (set & _IRQF_MODIFY_MASK); +} + +static inline bool irq_settings_is_per_cpu(struct irq_desc *desc) +{ + return desc->status & _IRQ_PER_CPU; +} + +static inline void irq_settings_set_per_cpu(struct irq_desc *desc) +{ + desc->status |= _IRQ_PER_CPU; +} + +static inline void irq_settings_set_no_balancing(struct irq_desc *desc) +{ + desc->status |= _IRQ_NO_BALANCING; +} + +static inline bool irq_settings_has_no_balance_set(struct irq_desc *desc) +{ + return desc->status & _IRQ_NO_BALANCING; +} diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index 692ce2bae302..226ed7d26a84 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c @@ -68,7 +68,8 @@ static int try_one_irq(int irq, struct irq_desc *desc, bool force) raw_spin_lock(&desc->lock); /* PER_CPU and nested thread interrupts are never polled */ - if (desc->status & (IRQ_PER_CPU | IRQ_NESTED_THREAD)) + if (irq_settings_is_per_cpu(desc) || + (desc->status & IRQ_NESTED_THREAD)) goto out; /* -- cgit v1.2.3 From 2bdd10558c8d93009cb6c32ce9e30800fbb08add Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 8 Feb 2011 17:22:00 +0100 Subject: genirq: Move IRQ_AFFINITY_SET to core Keep status in sync until last abuser is gone. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 9 ++++++++- kernel/irq/compat.h | 11 +++++++++++ kernel/irq/internals.h | 4 ++++ kernel/irq/manage.c | 11 +++++++---- kernel/irq/settings.h | 2 ++ 5 files changed, 32 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index d5312e6fe1aa..8da1782ecfca 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -60,6 +60,7 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQ_MASKED 0x00002000 /* DEPRECATED */ /* DEPRECATED use irq_setaffinity_pending() instead*/ #define IRQ_MOVE_PENDING 0x00004000 +#define IRQ_AFFINITY_SET 0x02000000 /* DEPRECATED */ #endif #define IRQ_LEVEL 0x00008000 /* IRQ level triggered */ @@ -70,7 +71,6 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQ_WAKEUP 0x00100000 /* IRQ triggers system wakeup */ #define IRQ_NO_BALANCING 0x00400000 /* IRQ is excluded from balancing */ #define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */ -#define IRQ_AFFINITY_SET 0x02000000 /* IRQ affinity was set from userspace*/ #define IRQ_NESTED_THREAD 0x10000000 /* IRQ is nested into another, no own handler thread */ #define IRQF_MODIFY_MASK \ @@ -134,12 +134,14 @@ struct irq_data { * IRQD_SETAFFINITY_PENDING - Affinity setting is pending * IRQD_NO_BALANCING - Balancing disabled for this IRQ * IRQD_PER_CPU - Interrupt is per cpu + * IRQD_AFFINITY_SET - Interrupt affinity was set */ enum { /* Bit 0 - 7 reserved for TYPE will use later */ IRQD_SETAFFINITY_PENDING = (1 << 8), IRQD_NO_BALANCING = (1 << 10), IRQD_PER_CPU = (1 << 11), + IRQD_AFFINITY_SET = (1 << 12), }; static inline bool irqd_is_setaffinity_pending(struct irq_data *d) @@ -157,6 +159,11 @@ static inline bool irqd_can_balance(struct irq_data *d) return !(d->state_use_accessors & (IRQD_PER_CPU | IRQD_NO_BALANCING)); } +static inline bool irqd_affinity_was_set(struct irq_data *d) +{ + return d->state_use_accessors & IRQD_AFFINITY_SET; +} + /** * struct irq_chip - hardware interrupt chip descriptor * diff --git a/kernel/irq/compat.h b/kernel/irq/compat.h index 5e33aadadacc..6bbaf66aca85 100644 --- a/kernel/irq/compat.h +++ b/kernel/irq/compat.h @@ -46,6 +46,15 @@ static inline void irq_compat_clr_move_pending(struct irq_desc *desc) { desc->status &= ~IRQ_MOVE_PENDING; } +static inline void irq_compat_set_affinity(struct irq_desc *desc) +{ + desc->status |= IRQ_AFFINITY_SET; +} + +static inline void irq_compat_clr_affinity(struct irq_desc *desc) +{ + desc->status &= ~IRQ_AFFINITY_SET; +} #else static inline void irq_compat_set_progress(struct irq_desc *desc) { } static inline void irq_compat_clr_progress(struct irq_desc *desc) { } @@ -57,5 +66,7 @@ static inline void irq_compat_set_masked(struct irq_desc *desc) { } static inline void irq_compat_clr_masked(struct irq_desc *desc) { } static inline void irq_compat_set_move_pending(struct irq_desc *desc) { } static inline void irq_compat_clr_move_pending(struct irq_desc *desc) { } +static inline void irq_compat_set_affinity(struct irq_desc *desc) { } +static inline void irq_compat_clr_affinity(struct irq_desc *desc) { } #endif diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index a80b44d2735e..6776453c454c 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -150,3 +150,7 @@ static inline void irqd_set(struct irq_data *d, unsigned int mask) d->state_use_accessors |= mask; } +static inline bool irqd_has_set(struct irq_data *d, unsigned int mask) +{ + return d->state_use_accessors & mask; +} diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 550ae97a0040..8246afc81956 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -164,7 +164,8 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *mask) kref_get(&desc->affinity_notify->kref); schedule_work(&desc->affinity_notify->work); } - desc->status |= IRQ_AFFINITY_SET; + irq_compat_set_affinity(desc); + irqd_set(&desc->irq_data, IRQD_AFFINITY_SET); raw_spin_unlock_irqrestore(&desc->lock, flags); return ret; } @@ -272,12 +273,14 @@ setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask) * Preserve an userspace affinity setup, but make sure that * one of the targets is online. */ - if (desc->status & (IRQ_AFFINITY_SET)) { + if (irqd_has_set(&desc->irq_data, IRQD_AFFINITY_SET)) { if (cpumask_intersects(desc->irq_data.affinity, cpu_online_mask)) set = desc->irq_data.affinity; - else - desc->status &= ~IRQ_AFFINITY_SET; + else { + irq_compat_clr_affinity(desc); + irqd_clear(&desc->irq_data, IRQD_AFFINITY_SET); + } } cpumask_and(mask, cpu_online_mask, set); diff --git a/kernel/irq/settings.h b/kernel/irq/settings.h index ba0fffe410ad..da5acb446b1c 100644 --- a/kernel/irq/settings.h +++ b/kernel/irq/settings.h @@ -29,6 +29,8 @@ enum { #define IRQ_PER_CPU GOT_YOU_MORON #undef IRQ_NO_BALANCING #define IRQ_NO_BALANCING GOT_YOU_MORON +#undef IRQ_AFFINITY_SET +#define IRQ_AFFINITY_SET GOT_YOU_MORON #undef IRQF_MODIFY_MASK #define IRQF_MODIFY_MASK GOT_YOU_MORON -- cgit v1.2.3 From 876dbd4cc1b35c1a4cb96a2be1d43ea0eabce3b4 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 8 Feb 2011 17:28:12 +0100 Subject: genirq: Mirror irq trigger type bits in irq_data.state That's the data structure chip functions get provided. Also allow them to signal the core code that they updated the flags in irq_data.state by returning IRQ_SET_MASK_OK_NOCOPY. The default is unchanged. The type bits should be accessed via: val = irqd_get_trigger_type(irqdata); and irqd_set_trigger_type(irqdata, val); Coders who access them directly will be tracked down and slapped with stinking trouts. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 26 +++++++++++++++++++++++++- kernel/irq/chip.c | 5 ++++- kernel/irq/manage.c | 44 +++++++++++++++++++++++++++----------------- kernel/irq/resend.c | 2 +- kernel/irq/settings.h | 30 ++++++++++++++++++++++++++++++ 5 files changed, 87 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 8da1782ecfca..be73c0a3c19d 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -46,7 +46,9 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, #define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING) #define IRQ_TYPE_LEVEL_HIGH 0x00000004 /* Level high type */ #define IRQ_TYPE_LEVEL_LOW 0x00000008 /* Level low type */ +#define IRQ_TYPE_LEVEL_MASK (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH) #define IRQ_TYPE_SENSE_MASK 0x0000000f /* Mask of the above */ + #define IRQ_TYPE_PROBE 0x00000010 /* Probing in progress */ /* Internal flags */ @@ -131,17 +133,20 @@ struct irq_data { /* * Bit masks for irq_data.state * + * IRQD_TRIGGER_MASK - Mask for the trigger type bits * IRQD_SETAFFINITY_PENDING - Affinity setting is pending * IRQD_NO_BALANCING - Balancing disabled for this IRQ * IRQD_PER_CPU - Interrupt is per cpu * IRQD_AFFINITY_SET - Interrupt affinity was set + * IRQD_LEVEL - Interrupt is level triggered */ enum { - /* Bit 0 - 7 reserved for TYPE will use later */ + IRQD_TRIGGER_MASK = 0xf, IRQD_SETAFFINITY_PENDING = (1 << 8), IRQD_NO_BALANCING = (1 << 10), IRQD_PER_CPU = (1 << 11), IRQD_AFFINITY_SET = (1 << 12), + IRQD_LEVEL = (1 << 13), }; static inline bool irqd_is_setaffinity_pending(struct irq_data *d) @@ -164,6 +169,25 @@ static inline bool irqd_affinity_was_set(struct irq_data *d) return d->state_use_accessors & IRQD_AFFINITY_SET; } +static inline u32 irqd_get_trigger_type(struct irq_data *d) +{ + return d->state_use_accessors & IRQD_TRIGGER_MASK; +} + +/* + * Must only be called inside irq_chip.irq_set_type() functions. + */ +static inline void irqd_set_trigger_type(struct irq_data *d, u32 type) +{ + d->state_use_accessors &= ~IRQD_TRIGGER_MASK; + d->state_use_accessors |= type & IRQD_TRIGGER_MASK; +} + +static inline bool irqd_is_level_type(struct irq_data *d) +{ + return d->state_use_accessors & IRQD_LEVEL; +} + /** * struct irq_chip - hardware interrupt chip descriptor * diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index b8aa3dfe8301..9c9b573a718e 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -710,11 +710,14 @@ void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set) irq_settings_clr_and_set(desc, clr, set); - irqd_clear(&desc->irq_data, IRQD_NO_BALANCING | IRQD_PER_CPU); + irqd_clear(&desc->irq_data, IRQD_NO_BALANCING | IRQD_PER_CPU | + IRQD_TRIGGER_MASK | IRQD_LEVEL); if (irq_settings_has_no_balance_set(desc)) irqd_set(&desc->irq_data, IRQD_NO_BALANCING); if (irq_settings_is_per_cpu(desc)) irqd_set(&desc->irq_data, IRQD_PER_CPU); + irqd_set(&desc->irq_data, irq_settings_get_trigger_mask(desc)); + raw_spin_unlock_irqrestore(&desc->lock, flags); } diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 8246afc81956..9ae758ed8e66 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -567,23 +567,32 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq, return 0; } + flags &= IRQ_TYPE_SENSE_MASK; /* caller masked out all except trigger mode flags */ ret = chip->irq_set_type(&desc->irq_data, flags); - if (ret) - pr_err("setting trigger mode %lu for irq %u failed (%pF)\n", - flags, irq, chip->irq_set_type); - else { - if (flags & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) - flags |= IRQ_LEVEL; - /* note that IRQF_TRIGGER_MASK == IRQ_TYPE_SENSE_MASK */ - desc->status &= ~(IRQ_LEVEL | IRQ_TYPE_SENSE_MASK); - desc->status |= flags; + switch (ret) { + case IRQ_SET_MASK_OK: + irqd_clear(&desc->irq_data, IRQD_TRIGGER_MASK); + irqd_set(&desc->irq_data, flags); + + case IRQ_SET_MASK_OK_NOCOPY: + flags = irqd_get_trigger_type(&desc->irq_data); + irq_settings_set_trigger_mask(desc, flags); + irqd_clear(&desc->irq_data, IRQD_LEVEL); + irq_settings_clr_level(desc); + if (flags & IRQ_TYPE_LEVEL_MASK) { + irq_settings_set_level(desc); + irqd_set(&desc->irq_data, IRQD_LEVEL); + } if (chip != desc->irq_data.chip) irq_chip_set_defaults(desc->irq_data.chip); + return 0; + default: + pr_err("setting trigger mode %lu for irq %u failed (%pF)\n", + flags, irq, chip->irq_set_type); } - return ret; } @@ -923,13 +932,14 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) /* Set default affinity mask once everything is setup */ setup_affinity(irq, desc, mask); - } else if ((new->flags & IRQF_TRIGGER_MASK) - && (new->flags & IRQF_TRIGGER_MASK) - != (desc->status & IRQ_TYPE_SENSE_MASK)) { - /* hope the handler works with the actual trigger mode... */ - pr_warning("IRQ %d uses trigger mode %d; requested %d\n", - irq, (int)(desc->status & IRQ_TYPE_SENSE_MASK), - (int)(new->flags & IRQF_TRIGGER_MASK)); + } else if (new->flags & IRQF_TRIGGER_MASK) { + unsigned int nmsk = new->flags & IRQF_TRIGGER_MASK; + unsigned int omsk = irq_settings_get_trigger_mask(desc); + + if (nmsk != omsk) + /* hope the handler works with current trigger mode */ + pr_warning("IRQ %d uses trigger mode %u; requested %u\n", + irq, nmsk, omsk); } new->irq = irq; diff --git a/kernel/irq/resend.c b/kernel/irq/resend.c index ff1fea060014..ad683a99b1ec 100644 --- a/kernel/irq/resend.c +++ b/kernel/irq/resend.c @@ -60,7 +60,7 @@ void check_irq_resend(struct irq_desc *desc, unsigned int irq) * interrupts are resent by hardware when they are still * active. */ - if (desc->status & IRQ_LEVEL) + if (irq_settings_is_level(desc)) return; if (desc->istate & IRQS_REPLAY) return; diff --git a/kernel/irq/settings.h b/kernel/irq/settings.h index da5acb446b1c..2201f2aaa9a0 100644 --- a/kernel/irq/settings.h +++ b/kernel/irq/settings.h @@ -5,6 +5,7 @@ enum { _IRQ_DEFAULT_INIT_FLAGS = IRQ_DEFAULT_INIT_FLAGS, _IRQ_PER_CPU = IRQ_PER_CPU, + _IRQ_LEVEL = IRQ_LEVEL, _IRQ_NO_BALANCING = IRQ_NO_BALANCING, _IRQF_MODIFY_MASK = IRQF_MODIFY_MASK, }; @@ -31,6 +32,8 @@ enum { #define IRQ_NO_BALANCING GOT_YOU_MORON #undef IRQ_AFFINITY_SET #define IRQ_AFFINITY_SET GOT_YOU_MORON +#undef IRQ_LEVEL +#define IRQ_LEVEL GOT_YOU_MORON #undef IRQF_MODIFY_MASK #define IRQF_MODIFY_MASK GOT_YOU_MORON @@ -60,3 +63,30 @@ static inline bool irq_settings_has_no_balance_set(struct irq_desc *desc) { return desc->status & _IRQ_NO_BALANCING; } + +static inline u32 irq_settings_get_trigger_mask(struct irq_desc *desc) +{ + return desc->status & IRQ_TYPE_SENSE_MASK; +} + +static inline void +irq_settings_set_trigger_mask(struct irq_desc *desc, u32 mask) +{ + desc->status &= ~IRQ_TYPE_SENSE_MASK; + desc->status |= mask & IRQ_TYPE_SENSE_MASK; +} + +static inline bool irq_settings_is_level(struct irq_desc *desc) +{ + return desc->status & _IRQ_LEVEL; +} + +static inline void irq_settings_clr_level(struct irq_desc *desc) +{ + desc->status &= ~_IRQ_LEVEL; +} + +static inline void irq_settings_set_level(struct irq_desc *desc) +{ + desc->status |= _IRQ_LEVEL; +} -- cgit v1.2.3 From 5d4d8fc9ac3e9a90bbdf90bae6864cb2c01f2208 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 8 Feb 2011 17:27:18 +0100 Subject: genirq: Cleanup irq.h Put the constants into an enum and document them. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 95 +++++++++++++++++++++++++++++++++------------------ kernel/irq/settings.h | 16 --------- 2 files changed, 62 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index be73c0a3c19d..2e3d1e5f0408 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -36,44 +36,73 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, /* * IRQ line status. * - * Bits 0-7 are reserved for the IRQF_* bits in linux/interrupt.h + * Bits 0-7 are the same as the IRQF_* bits in linux/interrupt.h + * + * IRQ_TYPE_NONE - default, unspecified type + * IRQ_TYPE_EDGE_RISING - rising edge triggered + * IRQ_TYPE_EDGE_FALLING - falling edge triggered + * IRQ_TYPE_EDGE_BOTH - rising and falling edge triggered + * IRQ_TYPE_LEVEL_HIGH - high level triggered + * IRQ_TYPE_LEVEL_LOW - low level triggered + * IRQ_TYPE_LEVEL_MASK - Mask to filter out the level bits + * IRQ_TYPE_SENSE_MASK - Mask for all the above bits + * IRQ_TYPE_PROBE - Special flag for probing in progress + * + * Bits which can be modified via irq_set/clear/modify_status_flags() + * IRQ_LEVEL - Interrupt is level type. Will be also + * updated in the code when the above trigger + * bits are modified via set_irq_type() + * IRQ_PER_CPU - Mark an interrupt PER_CPU. Will protect + * it from affinity setting + * IRQ_NOPROBE - Interrupt cannot be probed by autoprobing + * IRQ_NOREQUEST - Interrupt cannot be requested via + * request_irq() + * IRQ_NOAUTOEN - Interrupt is not automatically enabled in + * request/setup_irq() + * IRQ_NO_BALANCING - Interrupt cannot be balanced (affinity set) + * IRQ_MOVE_PCNTXT - Interrupt can be migrated from process context + * IRQ_NESTED_TRHEAD - Interrupt nests into another thread + * + * Deprecated bits. They are kept updated as long as + * CONFIG_GENERIC_HARDIRQS_NO_COMPAT is not set. Will go away soon. These bits + * are internal state of the core code and if you really need to acces + * them then talk to the genirq maintainer instead of hacking + * something weird. * - * IRQ types */ -#define IRQ_TYPE_NONE 0x00000000 /* Default, unspecified type */ -#define IRQ_TYPE_EDGE_RISING 0x00000001 /* Edge rising type */ -#define IRQ_TYPE_EDGE_FALLING 0x00000002 /* Edge falling type */ -#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING) -#define IRQ_TYPE_LEVEL_HIGH 0x00000004 /* Level high type */ -#define IRQ_TYPE_LEVEL_LOW 0x00000008 /* Level low type */ -#define IRQ_TYPE_LEVEL_MASK (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH) -#define IRQ_TYPE_SENSE_MASK 0x0000000f /* Mask of the above */ - -#define IRQ_TYPE_PROBE 0x00000010 /* Probing in progress */ - -/* Internal flags */ +enum { + IRQ_TYPE_NONE = 0x00000000, + IRQ_TYPE_EDGE_RISING = 0x00000001, + IRQ_TYPE_EDGE_FALLING = 0x00000002, + IRQ_TYPE_EDGE_BOTH = (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING), + IRQ_TYPE_LEVEL_HIGH = 0x00000004, + IRQ_TYPE_LEVEL_LOW = 0x00000008, + IRQ_TYPE_LEVEL_MASK = (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH), + IRQ_TYPE_SENSE_MASK = 0x0000000f, + + IRQ_TYPE_PROBE = 0x00000010, + + IRQ_LEVEL = (1 << 8), + IRQ_PER_CPU = (1 << 9), + IRQ_NOPROBE = (1 << 10), + IRQ_NOREQUEST = (1 << 11), + IRQ_NOAUTOEN = (1 << 12), + IRQ_NO_BALANCING = (1 << 13), + IRQ_MOVE_PCNTXT = (1 << 14), + IRQ_NESTED_THREAD = (1 << 15), #ifndef CONFIG_GENERIC_HARDIRQS_NO_COMPAT -#define IRQ_INPROGRESS 0x00000100 /* DEPRECATED */ -#define IRQ_REPLAY 0x00000200 /* DEPRECATED */ -#define IRQ_WAITING 0x00000400 /* DEPRECATED */ -#define IRQ_DISABLED 0x00000800 /* DEPRECATED */ -#define IRQ_PENDING 0x00001000 /* DEPRECATED */ -#define IRQ_MASKED 0x00002000 /* DEPRECATED */ -/* DEPRECATED use irq_setaffinity_pending() instead*/ -#define IRQ_MOVE_PENDING 0x00004000 -#define IRQ_AFFINITY_SET 0x02000000 /* DEPRECATED */ + IRQ_INPROGRESS = (1 << 16), + IRQ_REPLAY = (1 << 17), + IRQ_WAITING = (1 << 18), + IRQ_DISABLED = (1 << 19), + IRQ_PENDING = (1 << 20), + IRQ_MASKED = (1 << 21), + IRQ_MOVE_PENDING = (1 << 22), + IRQ_AFFINITY_SET = (1 << 23), + IRQ_WAKEUP = (1 << 24), #endif - -#define IRQ_LEVEL 0x00008000 /* IRQ level triggered */ -#define IRQ_PER_CPU 0x00010000 /* IRQ is per CPU */ -#define IRQ_NOPROBE 0x00020000 /* IRQ is not valid for probing */ -#define IRQ_NOREQUEST 0x00040000 /* IRQ cannot be requested */ -#define IRQ_NOAUTOEN 0x00080000 /* IRQ will not be enabled on request irq */ -#define IRQ_WAKEUP 0x00100000 /* IRQ triggers system wakeup */ -#define IRQ_NO_BALANCING 0x00400000 /* IRQ is excluded from balancing */ -#define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */ -#define IRQ_NESTED_THREAD 0x10000000 /* IRQ is nested into another, no own handler thread */ +}; #define IRQF_MODIFY_MASK \ (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \ diff --git a/kernel/irq/settings.h b/kernel/irq/settings.h index 47bcd3b9f399..55ebe1e09da4 100644 --- a/kernel/irq/settings.h +++ b/kernel/irq/settings.h @@ -15,37 +15,21 @@ enum { _IRQF_MODIFY_MASK = IRQF_MODIFY_MASK, }; -#undef IRQ_INPROGRESS #define IRQ_INPROGRESS GOT_YOU_MORON -#undef IRQ_REPLAY #define IRQ_REPLAY GOT_YOU_MORON -#undef IRQ_WAITING #define IRQ_WAITING GOT_YOU_MORON -#undef IRQ_DISABLED #define IRQ_DISABLED GOT_YOU_MORON -#undef IRQ_PENDING #define IRQ_PENDING GOT_YOU_MORON -#undef IRQ_MASKED #define IRQ_MASKED GOT_YOU_MORON -#undef IRQ_WAKEUP #define IRQ_WAKEUP GOT_YOU_MORON -#undef IRQ_MOVE_PENDING #define IRQ_MOVE_PENDING GOT_YOU_MORON -#undef IRQ_PER_CPU #define IRQ_PER_CPU GOT_YOU_MORON -#undef IRQ_NO_BALANCING #define IRQ_NO_BALANCING GOT_YOU_MORON -#undef IRQ_AFFINITY_SET #define IRQ_AFFINITY_SET GOT_YOU_MORON -#undef IRQ_LEVEL #define IRQ_LEVEL GOT_YOU_MORON -#undef IRQ_NOPROBE #define IRQ_NOPROBE GOT_YOU_MORON -#undef IRQ_NOREQUEST #define IRQ_NOREQUEST GOT_YOU_MORON -#undef IRQ_NOAUTOEN #define IRQ_NOAUTOEN GOT_YOU_MORON -#undef IRQ_NESTED_THREAD #define IRQ_NESTED_THREAD GOT_YOU_MORON #undef IRQF_MODIFY_MASK #define IRQF_MODIFY_MASK GOT_YOU_MORON -- cgit v1.2.3 From 2bff17ad2107c66fc8ca96501a7128dd7fa7a390 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 10 Feb 2011 13:08:38 +0100 Subject: genirq: Add flags to irq_chip Looking through irq_chip implementations I noticed that some of them have special requirements, like setting the type masked and therefor fiddle in irq_desc->status. Add a flag field, so the core code can handle it. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 2e3d1e5f0408..aefb30bbcf0e 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -253,6 +253,7 @@ static inline bool irqd_is_level_type(struct irq_data *d) * @irq_set_wake: enable/disable power-management wake-on of an IRQ * @irq_bus_lock: function to lock access to slow bus (i2c) chips * @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips + * @flags: chip specific flags * * @release: release function solely used by UML */ @@ -299,6 +300,8 @@ struct irq_chip { void (*irq_bus_lock)(struct irq_data *data); void (*irq_bus_sync_unlock)(struct irq_data *data); + unsigned long flags; + /* Currently used only by UML, might disappear one day.*/ #ifdef CONFIG_IRQ_RELEASE_METHOD void (*release)(unsigned int irq, void *dev_id); -- cgit v1.2.3 From d4d5e08960844a062da8387ee5f16ca7a33200d0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 10 Feb 2011 13:16:14 +0100 Subject: genirq: Add IRQCHIP_SET_TYPE_MASKED flag irq_chips, which require to mask the chip before changing the trigger type should set this flag. So the core takes care of it and the requirement for looking into desc->status in the chip goes away. Signed-off-by: Thomas Gleixner Cc: Linus Walleij Cc: Lars-Peter Clausen --- include/linux/irq.h | 9 +++++++++ kernel/irq/chip.c | 4 ++-- kernel/irq/internals.h | 2 ++ kernel/irq/manage.c | 16 +++++++++++++--- 4 files changed, 26 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index aefb30bbcf0e..ef6b66dc9d03 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -308,6 +308,15 @@ struct irq_chip { #endif }; +/* + * irq_chip specific flags + * + * IRQCHIP_SET_TYPE_MASKED: Mask before calling chip.irq_set_type() + */ +enum { + IRQCHIP_SET_TYPE_MASKED = (1 << 0), +}; + /* This include will go away once we isolated irq_desc usage to core code */ #include diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 9e9220da4deb..4687457fe7f0 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -367,7 +367,7 @@ static inline void mask_ack_irq(struct irq_desc *desc) irq_state_set_masked(desc); } -static inline void mask_irq(struct irq_desc *desc) +void mask_irq(struct irq_desc *desc) { if (desc->irq_data.chip->irq_mask) { desc->irq_data.chip->irq_mask(&desc->irq_data); @@ -375,7 +375,7 @@ static inline void mask_irq(struct irq_desc *desc) } } -static inline void unmask_irq(struct irq_desc *desc) +void unmask_irq(struct irq_desc *desc) { if (desc->irq_data.chip->irq_unmask) { desc->irq_data.chip->irq_unmask(&desc->irq_data); diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 6776453c454c..1d500fbde0d4 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -84,6 +84,8 @@ extern int irq_startup(struct irq_desc *desc); extern void irq_shutdown(struct irq_desc *desc); extern void irq_enable(struct irq_desc *desc); extern void irq_disable(struct irq_desc *desc); +extern void mask_irq(struct irq_desc *desc); +extern void unmask_irq(struct irq_desc *desc); extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr); diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index b5de828e58d9..50809c79c7ad 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -554,8 +554,8 @@ void compat_irq_chip_set_default_handler(struct irq_desc *desc) int __irq_set_trigger(struct irq_desc *desc, unsigned int irq, unsigned long flags) { - int ret; struct irq_chip *chip = desc->irq_data.chip; + int ret, unmask = 0; if (!chip || !chip->irq_set_type) { /* @@ -568,6 +568,14 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq, } flags &= IRQ_TYPE_SENSE_MASK; + + if (chip->flags & IRQCHIP_SET_TYPE_MASKED) { + if (!(desc->istate & IRQS_MASKED)) + mask_irq(desc); + if (!(desc->istate & IRQS_DISABLED)) + unmask = 1; + } + /* caller masked out all except trigger mode flags */ ret = chip->irq_set_type(&desc->irq_data, flags); @@ -588,11 +596,13 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq, if (chip != desc->irq_data.chip) irq_chip_set_defaults(desc->irq_data.chip); - return 0; + ret = 0; default: pr_err("setting trigger mode %lu for irq %u failed (%pF)\n", flags, irq, chip->irq_set_type); } + if (unmask) + unmask_irq(desc); return ret; } @@ -669,7 +679,7 @@ again: #ifdef CONFIG_SMP /* - * Check whether we need to change the affinity of the interrupt thread. + * Check whether we need to chasnge the affinity of the interrupt thread. */ static void irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action) -- cgit v1.2.3 From 7f94226f03299f1ca32f118f02f2a0295e0e5e93 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 10 Feb 2011 19:46:26 +0100 Subject: genirq: Move wakeup state to irq_data Some irq_chips need to know the state of wakeup mode for setting the trigger type etc. Reflect it in irq_data state. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 8 ++++++++ kernel/irq/internals.h | 2 -- kernel/irq/manage.c | 4 ++-- kernel/irq/pm.c | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index ef6b66dc9d03..94c8f5bb548f 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -168,6 +168,8 @@ struct irq_data { * IRQD_PER_CPU - Interrupt is per cpu * IRQD_AFFINITY_SET - Interrupt affinity was set * IRQD_LEVEL - Interrupt is level triggered + * IRQD_WAKEUP_STATE - Interrupt is configured for wakeup + * from suspend */ enum { IRQD_TRIGGER_MASK = 0xf, @@ -176,6 +178,7 @@ enum { IRQD_PER_CPU = (1 << 11), IRQD_AFFINITY_SET = (1 << 12), IRQD_LEVEL = (1 << 13), + IRQD_WAKEUP_STATE = (1 << 14), }; static inline bool irqd_is_setaffinity_pending(struct irq_data *d) @@ -217,6 +220,11 @@ static inline bool irqd_is_level_type(struct irq_data *d) return d->state_use_accessors & IRQD_LEVEL; } +static inline bool irqd_is_wakeup_set(struct irq_data *d) +{ + return d->state_use_accessors & IRQD_WAKEUP_STATE; +} + /** * struct irq_chip - hardware interrupt chip descriptor * diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 1d500fbde0d4..5e2366da9f38 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -46,7 +46,6 @@ enum { * IRQS_PENDING - irq is pending and replayed later * IRQS_MASKED - irq is masked * IRQS_SUSPENDED - irq is suspended - * IRQS_WAKEUP - irq triggers system wakeup from suspend */ enum { IRQS_AUTODETECT = 0x00000001, @@ -60,7 +59,6 @@ enum { IRQS_PENDING = 0x00000200, IRQS_MASKED = 0x00000400, IRQS_SUSPENDED = 0x00000800, - IRQS_WAKEUP = 0x00001000, }; #include "compat.h" diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 50809c79c7ad..ea6add6036b1 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -492,7 +492,7 @@ int irq_set_irq_wake(unsigned int irq, unsigned int on) if (ret) desc->wake_depth = 0; else - desc->istate |= IRQS_WAKEUP; + irqd_set(&desc->irq_data, IRQD_WAKEUP_STATE); } } else { if (desc->wake_depth == 0) { @@ -502,7 +502,7 @@ int irq_set_irq_wake(unsigned int irq, unsigned int on) if (ret) desc->wake_depth = 1; else - desc->istate &= ~IRQS_WAKEUP; + irqd_clear(&desc->irq_data, IRQD_WAKEUP_STATE); } } diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c index f39383d8672d..1329f0eff49e 100644 --- a/kernel/irq/pm.c +++ b/kernel/irq/pm.c @@ -69,7 +69,7 @@ int check_wakeup_irqs(void) int irq; for_each_irq_desc(irq, desc) - if ((desc->istate & IRQS_WAKEUP) && + if (irqd_is_wakeup_set(&desc->irq_data) && (desc->istate & IRQS_PENDING)) return -EBUSY; -- cgit v1.2.3 From e1ef824146131709d7466e37f889f2dab24ca98e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 10 Feb 2011 22:25:31 +0100 Subject: genirq: Reflect IRQ_MOVE_PCNTXT in irq_data state Required by x86. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 8 ++++++++ kernel/irq/chip.c | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 94c8f5bb548f..c101ad4b821f 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -170,6 +170,8 @@ struct irq_data { * IRQD_LEVEL - Interrupt is level triggered * IRQD_WAKEUP_STATE - Interrupt is configured for wakeup * from suspend + * IRDQ_MOVE_PCNTXT - Interrupt can be moved in process + * context */ enum { IRQD_TRIGGER_MASK = 0xf, @@ -179,6 +181,7 @@ enum { IRQD_AFFINITY_SET = (1 << 12), IRQD_LEVEL = (1 << 13), IRQD_WAKEUP_STATE = (1 << 14), + IRQD_MOVE_PCNTXT = (1 << 15), }; static inline bool irqd_is_setaffinity_pending(struct irq_data *d) @@ -225,6 +228,11 @@ static inline bool irqd_is_wakeup_set(struct irq_data *d) return d->state_use_accessors & IRQD_WAKEUP_STATE; } +static inline bool irqd_can_move_in_process_context(struct irq_data *d) +{ + return d->state_use_accessors & IRQD_MOVE_PCNTXT; +} + /** * struct irq_chip - hardware interrupt chip descriptor * diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 4687457fe7f0..2b0f9192a830 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -712,11 +712,13 @@ void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set) irq_settings_clr_and_set(desc, clr, set); irqd_clear(&desc->irq_data, IRQD_NO_BALANCING | IRQD_PER_CPU | - IRQD_TRIGGER_MASK | IRQD_LEVEL); + IRQD_TRIGGER_MASK | IRQD_LEVEL | IRQD_MOVE_PCNTXT); if (irq_settings_has_no_balance_set(desc)) irqd_set(&desc->irq_data, IRQD_NO_BALANCING); if (irq_settings_is_per_cpu(desc)) irqd_set(&desc->irq_data, IRQD_PER_CPU); + if (irq_settings_can_move_pcntxt(desc)) + irqd_set(&desc->irq_data, IRQD_MOVE_PCNTXT); irqd_set(&desc->irq_data, irq_settings_get_trigger_mask(desc)); -- cgit v1.2.3 From a6967caf00ebbb2d4acdebcb72a25f2e9ba43fd2 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 10 Feb 2011 22:01:25 +0100 Subject: genirq: Remove desc->status when GENERIC_HARDIRQS_NO_COMPAT=y If everything uses the right accessors, then enabling GENERIC_HARDIRQS_NO_COMPAT should just work. If not it will tell you. Don't be lazy and use the trick which I use in the core code! git grep status_use_accessors will unearth it in a split second. Offenders are tracked down and not slapped with stinking trouts. This time we use frozen shark for a better educational value. Signed-off-by: Thomas Gleixner --- include/linux/irqdesc.h | 6 ++++++ kernel/irq/internals.h | 4 ++++ kernel/irq/settings.h | 1 + 3 files changed, 11 insertions(+) (limited to 'include') diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index 581d9665fd38..36c95f08023d 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h @@ -64,7 +64,11 @@ struct irq_desc { unsigned int __percpu *kstat_irqs; irq_flow_handler_t handle_irq; struct irqaction *action; /* IRQ action list */ +#ifdef CONFIG_GENERIC_HARDIRQS_NO_COMPAT + unsigned int status_use_accessors; +#else unsigned int status; /* IRQ status */ +#endif unsigned int core_internal_state__do_not_mess_with_it; unsigned int depth; /* nested irq disables */ unsigned int wake_depth; /* nested wake enables */ @@ -164,6 +168,7 @@ static inline int irq_has_action(unsigned int irq) return desc->action != NULL; } +#ifndef CONFIG_GENERIC_HARDIRQS_NO_COMPAT static inline int irq_balancing_disabled(unsigned int irq) { struct irq_desc *desc; @@ -171,6 +176,7 @@ static inline int irq_balancing_disabled(unsigned int irq) desc = irq_to_desc(irq); return desc->status & IRQ_NO_BALANCING_MASK; } +#endif /* caller has locked the irq_desc and both params are valid */ static inline void __set_irq_handler_unlocked(int irq, diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 5e2366da9f38..fd5777ab2d34 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -15,6 +15,10 @@ #define istate core_internal_state__do_not_mess_with_it +#ifdef CONFIG_GENERIC_HARDIRQS_NO_COMPAT +# define status status_use_accessors +#endif + extern int noirqdebug; /* diff --git a/kernel/irq/settings.h b/kernel/irq/settings.h index 55ebe1e09da4..0227ad358272 100644 --- a/kernel/irq/settings.h +++ b/kernel/irq/settings.h @@ -134,4 +134,5 @@ static inline bool irq_settings_is_nested_thread(struct irq_desc *desc) } /* Nothing should touch desc->status from now on */ +#undef status #define status USE_THE_PROPER_WRAPPERS_YOU_MORON -- cgit v1.2.3 From 3836ca08aad4575c120ccf328652f3873eea9063 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 14 Feb 2011 20:09:19 +0100 Subject: genirq: Consolidate set_chip_handler functions No need to have separate functions if we have one plus inline wrappers. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 51 +++++++++++++++++++++++++++++++++++++++------------ kernel/irq/chip.c | 16 ++++------------ 2 files changed, 43 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index c101ad4b821f..3e29e2f42e04 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -398,23 +398,23 @@ extern struct irq_chip no_irq_chip; extern struct irq_chip dummy_irq_chip; extern void -set_irq_chip_and_handler(unsigned int irq, struct irq_chip *chip, - irq_flow_handler_t handle); -extern void -set_irq_chip_and_handler_name(unsigned int irq, struct irq_chip *chip, +irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip, irq_flow_handler_t handle, const char *name); +static inline void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *chip, + irq_flow_handler_t handle) +{ + irq_set_chip_and_handler_name(irq, chip, handle, NULL); +} + extern void -__set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, +__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, const char *name); -/* - * Set a highlevel flow handler for a given IRQ: - */ static inline void -set_irq_handler(unsigned int irq, irq_flow_handler_t handle) +irq_set_handler(unsigned int irq, irq_flow_handler_t handle) { - __set_irq_handler(irq, handle, 0, NULL); + __irq_set_handler(irq, handle, 0, NULL); } /* @@ -423,9 +423,9 @@ set_irq_handler(unsigned int irq, irq_flow_handler_t handle) * IRQ_NOREQUEST and IRQ_NOPROBE) */ static inline void -set_irq_chained_handler(unsigned int irq, irq_flow_handler_t handle) +irq_set_chained_handler(unsigned int irq, irq_flow_handler_t handle) { - __set_irq_handler(irq, handle, 1, NULL); + __irq_set_handler(irq, handle, 1, NULL); } void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set); @@ -579,6 +579,33 @@ static inline void set_irq_nested_thread(unsigned int irq, int nest) { irq_set_nested_thread(irq, nest); } +static inline void +set_irq_chip_and_handler_name(unsigned int irq, struct irq_chip *chip, + irq_flow_handler_t handle, const char *name) +{ + irq_set_chip_and_handler_name(irq, chip, handle, name); +} +static inline void +set_irq_chip_and_handler(unsigned int irq, struct irq_chip *chip, + irq_flow_handler_t handle) +{ + irq_set_chip_and_handler(irq, chip, handle); +} +static inline void +__set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, + const char *name) +{ + __irq_set_handler(irq, handle, is_chained, name); +} +static inline void set_irq_handler(unsigned int irq, irq_flow_handler_t handle) +{ + irq_set_handler(irq, handle); +} +static inline void +set_irq_chained_handler(unsigned int irq, irq_flow_handler_t handle) +{ + irq_set_chained_handler(irq, handle); +} #endif int irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node); diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 3ea6aecd99c0..2a36038b8f59 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -600,7 +600,7 @@ handle_percpu_irq(unsigned int irq, struct irq_desc *desc) } void -__set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, +__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, const char *name) { unsigned long flags; @@ -635,22 +635,14 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, out: irq_put_desc_busunlock(desc, flags); } -EXPORT_SYMBOL_GPL(__set_irq_handler); +EXPORT_SYMBOL_GPL(__irq_set_handler); void -set_irq_chip_and_handler(unsigned int irq, struct irq_chip *chip, - irq_flow_handler_t handle) -{ - irq_set_chip(irq, chip); - __set_irq_handler(irq, handle, 0, NULL); -} - -void -set_irq_chip_and_handler_name(unsigned int irq, struct irq_chip *chip, +irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip, irq_flow_handler_t handle, const char *name) { irq_set_chip(irq, chip); - __set_irq_handler(irq, handle, 0, name); + __irq_set_handler(irq, handle, 0, name); } void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set) -- cgit v1.2.3 From 781295762defc709a609efc01d8bb065276cd9a2 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 10 Feb 2011 15:14:20 +0100 Subject: genirq: Add preflow handler support sparc64 needs to call a preflow handler on certain interrupts befor calling the action chain. Integrate it into handle_fasteoi_irq. Must be enabled via CONFIG_IRQ_FASTEOI_PREFLOW. No impact when disabled. Signed-off-by: Thomas Gleixner Cc: David S. Miller --- include/linux/irq.h | 3 ++- include/linux/irqdesc.h | 14 ++++++++++++++ kernel/irq/Kconfig | 3 +++ kernel/irq/chip.c | 11 +++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 3e29e2f42e04..36390970693c 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -29,9 +29,10 @@ #include struct irq_desc; +struct irq_data; typedef void (*irq_flow_handler_t)(unsigned int irq, struct irq_desc *desc); - +typedef void (*irq_preflow_handler_t)(struct irq_data *data); /* * IRQ line status. diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index 36c95f08023d..2f87d6441302 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h @@ -63,6 +63,9 @@ struct irq_desc { struct timer_rand_state *timer_rand_state; unsigned int __percpu *kstat_irqs; irq_flow_handler_t handle_irq; +#ifdef CONFIG_IRQ_PREFLOW_FASTEOI + irq_preflow_handler_t preflow_handler; +#endif struct irqaction *action; /* IRQ action list */ #ifdef CONFIG_GENERIC_HARDIRQS_NO_COMPAT unsigned int status_use_accessors; @@ -187,6 +190,17 @@ static inline void __set_irq_handler_unlocked(int irq, desc = irq_to_desc(irq); desc->handle_irq = handler; } + +#ifdef CONFIG_IRQ_PREFLOW_FASTEOI +static inline void +__irq_set_preflow_handler(unsigned int irq, irq_preflow_handler_t handler) +{ + struct irq_desc *desc; + + desc = irq_to_desc(irq); + desc->preflow_handler = handler; +} +#endif #endif #endif diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index 48ad25f5fa59..9149be729e45 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig @@ -35,6 +35,9 @@ config AUTO_IRQ_AFFINITY config HARDIRQS_SW_RESEND def_bool n +config IRQ_PREFLOW_FASTEOI + def_bool n + config SPARSE_IRQ bool "Support sparse irq numbering" depends on HAVE_SPARSE_IRQ diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 2a36038b8f59..08be5d182be3 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -471,6 +471,16 @@ out_unlock: } EXPORT_SYMBOL_GPL(handle_level_irq); +#ifdef CONFIG_IRQ_PREFLOW_FASTEOI +static inline void preflow_handler(struct irq_desc *desc) +{ + if (desc->preflow_handler) + desc->preflow_handler(&desc->irq_data); +} +#else +static inline void preflow_handler(struct irq_desc *desc) { } +#endif + /** * handle_fasteoi_irq - irq handler for transparent controllers * @irq: the interrupt number @@ -503,6 +513,7 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc) mask_irq(desc); goto out; } + preflow_handler(desc); handle_irq_event(desc); out: desc->irq_data.chip->irq_eoi(&desc->irq_data); -- cgit v1.2.3 From 77694b408abb8f92195ad5ed6ce5492f1d794c77 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 15 Feb 2011 10:33:57 +0100 Subject: genirq; Add fasteoi irq_chip quirk Some chips want irq_eoi() only called when an interrupt is actually handled. So they have checks for INPROGRESS and DISABLED in their irq_eoi callbacks. Add a chip flag, which allows to handle that in the generic code. No impact on the fastpath. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 4 +++- kernel/irq/chip.c | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 36390970693c..ea2970c294aa 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -328,10 +328,12 @@ struct irq_chip { /* * irq_chip specific flags * - * IRQCHIP_SET_TYPE_MASKED: Mask before calling chip.irq_set_type() + * IRQCHIP_SET_TYPE_MASKED: Mask before calling chip.irq_set_type() + * IRQCHIP_EOI_IF_HANDLED: Only issue irq_eoi() when irq was handled */ enum { IRQCHIP_SET_TYPE_MASKED = (1 << 0), + IRQCHIP_EOI_IF_HANDLED = (1 << 1), }; /* This include will go away once we isolated irq_desc usage to core code */ diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 08be5d182be3..1d3e25e68b0c 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -515,9 +515,16 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc) } preflow_handler(desc); handle_irq_event(desc); -out: + +out_eoi: desc->irq_data.chip->irq_eoi(&desc->irq_data); +out_unlock: raw_spin_unlock(&desc->lock); + return; +out: + if (!(desc->irq_data.chip->flags & IRQCHIP_EOI_IF_HANDLED)) + goto out_eoi; + goto out_unlock; } /** -- cgit v1.2.3 From a439520f8b18917b322f576be04c54aba84bb044 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 4 Feb 2011 18:46:16 +0100 Subject: genirq: Implement irq_data based move_*_irq() versions No need to lookup the irq descriptor when calling from a chip callback function which has irq_data already handy. Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 4 ++++ kernel/irq/migration.c | 28 +++++++++++++++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index ea2970c294aa..ff62d0145b8f 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -363,9 +363,13 @@ extern void remove_irq(unsigned int irq, struct irqaction *act); #if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_PENDING_IRQ) void move_native_irq(int irq); void move_masked_irq(int irq); +void irq_move_irq(struct irq_data *data); +void irq_move_masked_irq(struct irq_data *data); #else static inline void move_native_irq(int irq) { } static inline void move_masked_irq(int irq) { } +static inline void irq_move_irq(struct irq_data *data) { } +static inline void irq_move_masked_irq(struct irq_data *data) { } #endif extern int no_irq_affinity; diff --git a/kernel/irq/migration.c b/kernel/irq/migration.c index 7a93c6b88b25..ec4806d4778b 100644 --- a/kernel/irq/migration.c +++ b/kernel/irq/migration.c @@ -4,10 +4,10 @@ #include "internals.h" -void move_masked_irq(int irq) +void irq_move_masked_irq(struct irq_data *idata) { - struct irq_desc *desc = irq_to_desc(irq); - struct irq_chip *chip = desc->irq_data.chip; + struct irq_desc *desc = irq_data_to_desc(idata); + struct irq_chip *chip = idata->chip; if (likely(!irqd_is_setaffinity_pending(&desc->irq_data))) return; @@ -53,12 +53,17 @@ void move_masked_irq(int irq) cpumask_clear(desc->pending_mask); } -void move_native_irq(int irq) +void move_masked_irq(int irq) +{ + irq_move_masked_irq(irq_get_irq_data(irq)); +} + +void irq_move_irq(struct irq_data *idata) { - struct irq_desc *desc = irq_to_desc(irq); + struct irq_desc *desc = irq_data_to_desc(idata); bool masked; - if (likely(!irqd_is_setaffinity_pending(&desc->irq_data))) + if (likely(!irqd_is_setaffinity_pending(idata))) return; if (unlikely(desc->istate & IRQS_DISABLED)) @@ -71,8 +76,13 @@ void move_native_irq(int irq) */ masked = desc->istate & IRQS_MASKED; if (!masked) - desc->irq_data.chip->irq_mask(&desc->irq_data); - move_masked_irq(irq); + idata->chip->irq_mask(idata); + irq_move_masked_irq(idata); if (!masked) - desc->irq_data.chip->irq_unmask(&desc->irq_data); + idata->chip->irq_unmask(idata); +} + +void move_native_irq(int irq) +{ + irq_move_irq(irq_get_irq_data(irq)); } -- cgit v1.2.3 From 089c34827e52346f0303d1e6a7b744c1f4da3095 Mon Sep 17 00:00:00 2001 From: Shan Wei Date: Sat, 19 Feb 2011 21:55:45 +0000 Subject: tcp: Remove debug macro of TCP_CHECK_TIMER Now, TCP_CHECK_TIMER is not used for debuging, it does nothing. And, it has been there for several years, maybe 6 years. Remove it to keep code clearer. Signed-off-by: Shan Wei Signed-off-by: David S. Miller --- include/net/tcp.h | 2 -- net/ipv4/tcp.c | 9 --------- net/ipv4/tcp_ipv4.c | 5 ----- net/ipv4/tcp_timer.c | 3 --- net/ipv6/tcp_ipv6.c | 4 ---- 5 files changed, 23 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index adfe6dbe9053..cda30ea354a2 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1068,8 +1068,6 @@ static inline int tcp_paws_reject(const struct tcp_options_received *rx_opt, return 1; } -#define TCP_CHECK_TIMER(sk) do { } while (0) - static inline void tcp_mib_init(struct net *net) { /* See RFC 2012 */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index f9867d2dbef4..a17a5a72b98d 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -873,9 +873,7 @@ int tcp_sendpage(struct sock *sk, struct page *page, int offset, flags); lock_sock(sk); - TCP_CHECK_TIMER(sk); res = do_tcp_sendpages(sk, &page, offset, size, flags); - TCP_CHECK_TIMER(sk); release_sock(sk); return res; } @@ -916,7 +914,6 @@ int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, long timeo; lock_sock(sk); - TCP_CHECK_TIMER(sk); flags = msg->msg_flags; timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT); @@ -1104,7 +1101,6 @@ wait_for_memory: out: if (copied) tcp_push(sk, flags, mss_now, tp->nonagle); - TCP_CHECK_TIMER(sk); release_sock(sk); return copied; @@ -1123,7 +1119,6 @@ do_error: goto out; out_err: err = sk_stream_error(sk, flags, err); - TCP_CHECK_TIMER(sk); release_sock(sk); return err; } @@ -1415,8 +1410,6 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, lock_sock(sk); - TCP_CHECK_TIMER(sk); - err = -ENOTCONN; if (sk->sk_state == TCP_LISTEN) goto out; @@ -1767,12 +1760,10 @@ skip_copy: /* Clean up data we have read: This will do ACK frames. */ tcp_cleanup_rbuf(sk, copied); - TCP_CHECK_TIMER(sk); release_sock(sk); return copied; out: - TCP_CHECK_TIMER(sk); release_sock(sk); return err; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index e2b9be27f226..ef5a90beb9b0 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1556,12 +1556,10 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ sock_rps_save_rxhash(sk, skb->rxhash); - TCP_CHECK_TIMER(sk); if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) { rsk = sk; goto reset; } - TCP_CHECK_TIMER(sk); return 0; } @@ -1583,13 +1581,10 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) } else sock_rps_save_rxhash(sk, skb->rxhash); - - TCP_CHECK_TIMER(sk); if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) { rsk = sk; goto reset; } - TCP_CHECK_TIMER(sk); return 0; reset: diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 74a6aa003657..ecd44b0c45f1 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -259,7 +259,6 @@ static void tcp_delack_timer(unsigned long data) tcp_send_ack(sk); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_DELAYEDACKS); } - TCP_CHECK_TIMER(sk); out: if (tcp_memory_pressure) @@ -481,7 +480,6 @@ static void tcp_write_timer(unsigned long data) tcp_probe_timer(sk); break; } - TCP_CHECK_TIMER(sk); out: sk_mem_reclaim(sk); @@ -589,7 +587,6 @@ static void tcp_keepalive_timer (unsigned long data) elapsed = keepalive_time_when(tp) - elapsed; } - TCP_CHECK_TIMER(sk); sk_mem_reclaim(sk); resched: diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index d6954e318324..1d0ab5570904 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1636,10 +1636,8 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) opt_skb = skb_clone(skb, GFP_ATOMIC); if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ - TCP_CHECK_TIMER(sk); if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) goto reset; - TCP_CHECK_TIMER(sk); if (opt_skb) goto ipv6_pktoptions; return 0; @@ -1667,10 +1665,8 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) } } - TCP_CHECK_TIMER(sk); if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) goto reset; - TCP_CHECK_TIMER(sk); if (opt_skb) goto ipv6_pktoptions; return 0; -- cgit v1.2.3 From 964de52147c2842092642512e6f654fc2ab51408 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 2 Feb 2011 23:21:58 -0800 Subject: Input: qt602240_ts - rename to atmel_mxt_ts Since the driver will be supporting whole range of Atmels mXT touchscreen controllers we better rename it to atmel_mxt_ts. Acked-by: Iiro Valkonen Signed-off-by: Dmitry Torokhov --- arch/arm/mach-s5pv210/mach-goni.c | 2 +- drivers/input/touchscreen/Kconfig | 24 +- drivers/input/touchscreen/Makefile | 2 +- drivers/input/touchscreen/atmel_mxt_ts.c | 1406 ++++++++++++++++++++++++++++++ drivers/input/touchscreen/qt602240_ts.c | 1406 ------------------------------ include/linux/i2c/atmel_mxt_ts.h | 38 + include/linux/i2c/qt602240_ts.h | 38 - 7 files changed, 1458 insertions(+), 1458 deletions(-) create mode 100644 drivers/input/touchscreen/atmel_mxt_ts.c delete mode 100644 drivers/input/touchscreen/qt602240_ts.c create mode 100644 include/linux/i2c/atmel_mxt_ts.h delete mode 100644 include/linux/i2c/qt602240_ts.h (limited to 'include') diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index e22d5112fd44..fd2747fd8905 100644 --- a/arch/arm/mach-s5pv210/mach-goni.c +++ b/arch/arm/mach-s5pv210/mach-goni.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 258e43e98ec3..2fbd3d39b7eb 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -86,6 +86,18 @@ config TOUCHSCREEN_AD7879_SPI To compile this driver as a module, choose M here: the module will be called ad7879-spi. +config TOUCHSCREEN_ATMEL_MXT + tristate "Atmel mXT I2C Touchscreen" + depends on I2C + help + Say Y here if you have Atmel mXT series I2C touchscreen, + such as AT42QT602240/ATMXT224, connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called atmel_mxt_ts. + config TOUCHSCREEN_BITSY tristate "Compaq iPAQ H3600 (Bitsy) touchscreen" depends on SA1100_BITSY @@ -339,18 +351,6 @@ config TOUCHSCREEN_PENMOUNT To compile this driver as a module, choose M here: the module will be called penmount. -config TOUCHSCREEN_QT602240 - tristate "QT602240 I2C Touchscreen" - depends on I2C - help - Say Y here if you have the AT42QT602240/ATMXT224 I2C touchscreen - connected to your system. - - If unsure, say N. - - To compile this driver as a module, choose M here: the - module will be called qt602240_ts. - config TOUCHSCREEN_MIGOR tristate "Renesas MIGO-R touchscreen" depends on SH_MIGOR && I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index e8dcfab7c2dc..756156409b83 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o +obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o @@ -37,7 +38,6 @@ obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o -obj-$(CONFIG_TOUCHSCREEN_QT602240) += qt602240_ts.o obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c new file mode 100644 index 000000000000..47067789b747 --- /dev/null +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -0,0 +1,1406 @@ +/* + * AT42QT602240/ATMXT224 Touchscreen driver + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Version */ +#define QT602240_VER_20 20 +#define QT602240_VER_21 21 +#define QT602240_VER_22 22 + +/* Slave addresses */ +#define QT602240_APP_LOW 0x4a +#define QT602240_APP_HIGH 0x4b +#define QT602240_BOOT_LOW 0x24 +#define QT602240_BOOT_HIGH 0x25 + +/* Firmware */ +#define QT602240_FW_NAME "qt602240.fw" + +/* Registers */ +#define QT602240_FAMILY_ID 0x00 +#define QT602240_VARIANT_ID 0x01 +#define QT602240_VERSION 0x02 +#define QT602240_BUILD 0x03 +#define QT602240_MATRIX_X_SIZE 0x04 +#define QT602240_MATRIX_Y_SIZE 0x05 +#define QT602240_OBJECT_NUM 0x06 +#define QT602240_OBJECT_START 0x07 + +#define QT602240_OBJECT_SIZE 6 + +/* Object types */ +#define QT602240_DEBUG_DIAGNOSTIC 37 +#define QT602240_GEN_MESSAGE 5 +#define QT602240_GEN_COMMAND 6 +#define QT602240_GEN_POWER 7 +#define QT602240_GEN_ACQUIRE 8 +#define QT602240_TOUCH_MULTI 9 +#define QT602240_TOUCH_KEYARRAY 15 +#define QT602240_TOUCH_PROXIMITY 23 +#define QT602240_PROCI_GRIPFACE 20 +#define QT602240_PROCG_NOISE 22 +#define QT602240_PROCI_ONETOUCH 24 +#define QT602240_PROCI_TWOTOUCH 27 +#define QT602240_SPT_COMMSCONFIG 18 /* firmware ver 21 over */ +#define QT602240_SPT_GPIOPWM 19 +#define QT602240_SPT_SELFTEST 25 +#define QT602240_SPT_CTECONFIG 28 +#define QT602240_SPT_USERDATA 38 /* firmware ver 21 over */ + +/* QT602240_GEN_COMMAND field */ +#define QT602240_COMMAND_RESET 0 +#define QT602240_COMMAND_BACKUPNV 1 +#define QT602240_COMMAND_CALIBRATE 2 +#define QT602240_COMMAND_REPORTALL 3 +#define QT602240_COMMAND_DIAGNOSTIC 5 + +/* QT602240_GEN_POWER field */ +#define QT602240_POWER_IDLEACQINT 0 +#define QT602240_POWER_ACTVACQINT 1 +#define QT602240_POWER_ACTV2IDLETO 2 + +/* QT602240_GEN_ACQUIRE field */ +#define QT602240_ACQUIRE_CHRGTIME 0 +#define QT602240_ACQUIRE_TCHDRIFT 2 +#define QT602240_ACQUIRE_DRIFTST 3 +#define QT602240_ACQUIRE_TCHAUTOCAL 4 +#define QT602240_ACQUIRE_SYNC 5 +#define QT602240_ACQUIRE_ATCHCALST 6 +#define QT602240_ACQUIRE_ATCHCALSTHR 7 + +/* QT602240_TOUCH_MULTI field */ +#define QT602240_TOUCH_CTRL 0 +#define QT602240_TOUCH_XORIGIN 1 +#define QT602240_TOUCH_YORIGIN 2 +#define QT602240_TOUCH_XSIZE 3 +#define QT602240_TOUCH_YSIZE 4 +#define QT602240_TOUCH_BLEN 6 +#define QT602240_TOUCH_TCHTHR 7 +#define QT602240_TOUCH_TCHDI 8 +#define QT602240_TOUCH_ORIENT 9 +#define QT602240_TOUCH_MOVHYSTI 11 +#define QT602240_TOUCH_MOVHYSTN 12 +#define QT602240_TOUCH_NUMTOUCH 14 +#define QT602240_TOUCH_MRGHYST 15 +#define QT602240_TOUCH_MRGTHR 16 +#define QT602240_TOUCH_AMPHYST 17 +#define QT602240_TOUCH_XRANGE_LSB 18 +#define QT602240_TOUCH_XRANGE_MSB 19 +#define QT602240_TOUCH_YRANGE_LSB 20 +#define QT602240_TOUCH_YRANGE_MSB 21 +#define QT602240_TOUCH_XLOCLIP 22 +#define QT602240_TOUCH_XHICLIP 23 +#define QT602240_TOUCH_YLOCLIP 24 +#define QT602240_TOUCH_YHICLIP 25 +#define QT602240_TOUCH_XEDGECTRL 26 +#define QT602240_TOUCH_XEDGEDIST 27 +#define QT602240_TOUCH_YEDGECTRL 28 +#define QT602240_TOUCH_YEDGEDIST 29 +#define QT602240_TOUCH_JUMPLIMIT 30 /* firmware ver 22 over */ + +/* QT602240_PROCI_GRIPFACE field */ +#define QT602240_GRIPFACE_CTRL 0 +#define QT602240_GRIPFACE_XLOGRIP 1 +#define QT602240_GRIPFACE_XHIGRIP 2 +#define QT602240_GRIPFACE_YLOGRIP 3 +#define QT602240_GRIPFACE_YHIGRIP 4 +#define QT602240_GRIPFACE_MAXTCHS 5 +#define QT602240_GRIPFACE_SZTHR1 7 +#define QT602240_GRIPFACE_SZTHR2 8 +#define QT602240_GRIPFACE_SHPTHR1 9 +#define QT602240_GRIPFACE_SHPTHR2 10 +#define QT602240_GRIPFACE_SUPEXTTO 11 + +/* QT602240_PROCI_NOISE field */ +#define QT602240_NOISE_CTRL 0 +#define QT602240_NOISE_OUTFLEN 1 +#define QT602240_NOISE_GCAFUL_LSB 3 +#define QT602240_NOISE_GCAFUL_MSB 4 +#define QT602240_NOISE_GCAFLL_LSB 5 +#define QT602240_NOISE_GCAFLL_MSB 6 +#define QT602240_NOISE_ACTVGCAFVALID 7 +#define QT602240_NOISE_NOISETHR 8 +#define QT602240_NOISE_FREQHOPSCALE 10 +#define QT602240_NOISE_FREQ0 11 +#define QT602240_NOISE_FREQ1 12 +#define QT602240_NOISE_FREQ2 13 +#define QT602240_NOISE_FREQ3 14 +#define QT602240_NOISE_FREQ4 15 +#define QT602240_NOISE_IDLEGCAFVALID 16 + +/* QT602240_SPT_COMMSCONFIG */ +#define QT602240_COMMS_CTRL 0 +#define QT602240_COMMS_CMD 1 + +/* QT602240_SPT_CTECONFIG field */ +#define QT602240_CTE_CTRL 0 +#define QT602240_CTE_CMD 1 +#define QT602240_CTE_MODE 2 +#define QT602240_CTE_IDLEGCAFDEPTH 3 +#define QT602240_CTE_ACTVGCAFDEPTH 4 +#define QT602240_CTE_VOLTAGE 5 /* firmware ver 21 over */ + +#define QT602240_VOLTAGE_DEFAULT 2700000 +#define QT602240_VOLTAGE_STEP 10000 + +/* Define for QT602240_GEN_COMMAND */ +#define QT602240_BOOT_VALUE 0xa5 +#define QT602240_BACKUP_VALUE 0x55 +#define QT602240_BACKUP_TIME 25 /* msec */ +#define QT602240_RESET_TIME 65 /* msec */ + +#define QT602240_FWRESET_TIME 175 /* msec */ + +/* Command to unlock bootloader */ +#define QT602240_UNLOCK_CMD_MSB 0xaa +#define QT602240_UNLOCK_CMD_LSB 0xdc + +/* Bootloader mode status */ +#define QT602240_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */ +#define QT602240_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */ +#define QT602240_FRAME_CRC_CHECK 0x02 +#define QT602240_FRAME_CRC_FAIL 0x03 +#define QT602240_FRAME_CRC_PASS 0x04 +#define QT602240_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ +#define QT602240_BOOT_STATUS_MASK 0x3f + +/* Touch status */ +#define QT602240_SUPPRESS (1 << 1) +#define QT602240_AMP (1 << 2) +#define QT602240_VECTOR (1 << 3) +#define QT602240_MOVE (1 << 4) +#define QT602240_RELEASE (1 << 5) +#define QT602240_PRESS (1 << 6) +#define QT602240_DETECT (1 << 7) + +/* Touchscreen absolute values */ +#define QT602240_MAX_XC 0x3ff +#define QT602240_MAX_YC 0x3ff +#define QT602240_MAX_AREA 0xff + +#define QT602240_MAX_FINGER 10 + +/* Initial register values recommended from chip vendor */ +static const u8 init_vals_ver_20[] = { + /* QT602240_GEN_COMMAND(6) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_GEN_POWER(7) */ + 0x20, 0xff, 0x32, + /* QT602240_GEN_ACQUIRE(8) */ + 0x08, 0x05, 0x05, 0x00, 0x00, 0x00, 0x05, 0x14, + /* QT602240_TOUCH_MULTI(9) */ + 0x00, 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x64, + /* QT602240_TOUCH_KEYARRAY(15) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* QT602240_SPT_GPIOPWM(19) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + /* QT602240_PROCI_GRIPFACE(20) */ + 0x00, 0x64, 0x64, 0x64, 0x64, 0x00, 0x00, 0x1e, 0x14, 0x04, + 0x1e, 0x00, + /* QT602240_PROCG_NOISE(22) */ + 0x05, 0x00, 0x00, 0x19, 0x00, 0xe7, 0xff, 0x04, 0x32, 0x00, + 0x01, 0x0a, 0x0f, 0x14, 0x00, 0x00, 0xe8, + /* QT602240_TOUCH_PROXIMITY(23) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + /* QT602240_PROCI_ONETOUCH(24) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_SELFTEST(25) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_TWOTOUCH(27) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_CTECONFIG(28) */ + 0x00, 0x00, 0x00, 0x04, 0x08, +}; + +static const u8 init_vals_ver_21[] = { + /* QT602240_GEN_COMMAND(6) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_GEN_POWER(7) */ + 0x20, 0xff, 0x32, + /* QT602240_GEN_ACQUIRE(8) */ + 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, + /* QT602240_TOUCH_MULTI(9) */ + 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_TOUCH_KEYARRAY(15) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* QT602240_SPT_GPIOPWM(19) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_GRIPFACE(20) */ + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, + 0x0f, 0x0a, + /* QT602240_PROCG_NOISE(22) */ + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, + 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, + /* QT602240_TOUCH_PROXIMITY(23) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + /* QT602240_PROCI_ONETOUCH(24) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_SELFTEST(25) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_TWOTOUCH(27) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_CTECONFIG(28) */ + 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, +}; + +static const u8 init_vals_ver_22[] = { + /* QT602240_GEN_COMMAND(6) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_GEN_POWER(7) */ + 0x20, 0xff, 0x32, + /* QT602240_GEN_ACQUIRE(8) */ + 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, + /* QT602240_TOUCH_MULTI(9) */ + 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* QT602240_TOUCH_KEYARRAY(15) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* QT602240_SPT_GPIOPWM(19) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_GRIPFACE(20) */ + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, + 0x0f, 0x0a, + /* QT602240_PROCG_NOISE(22) */ + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, + 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, + /* QT602240_TOUCH_PROXIMITY(23) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_ONETOUCH(24) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_SELFTEST(25) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_TWOTOUCH(27) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_CTECONFIG(28) */ + 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, +}; + +struct qt602240_info { + u8 family_id; + u8 variant_id; + u8 version; + u8 build; + u8 matrix_xsize; + u8 matrix_ysize; + u8 object_num; +}; + +struct qt602240_object { + u8 type; + u16 start_address; + u8 size; + u8 instances; + u8 num_report_ids; + + /* to map object and message */ + u8 max_reportid; +}; + +struct qt602240_message { + u8 reportid; + u8 message[7]; + u8 checksum; +}; + +struct qt602240_finger { + int status; + int x; + int y; + int area; +}; + +/* Each client has this additional data */ +struct qt602240_data { + struct i2c_client *client; + struct input_dev *input_dev; + const struct qt602240_platform_data *pdata; + struct qt602240_object *object_table; + struct qt602240_info info; + struct qt602240_finger finger[QT602240_MAX_FINGER]; + unsigned int irq; +}; + +static bool qt602240_object_readable(unsigned int type) +{ + switch (type) { + case QT602240_GEN_MESSAGE: + case QT602240_GEN_COMMAND: + case QT602240_GEN_POWER: + case QT602240_GEN_ACQUIRE: + case QT602240_TOUCH_MULTI: + case QT602240_TOUCH_KEYARRAY: + case QT602240_TOUCH_PROXIMITY: + case QT602240_PROCI_GRIPFACE: + case QT602240_PROCG_NOISE: + case QT602240_PROCI_ONETOUCH: + case QT602240_PROCI_TWOTOUCH: + case QT602240_SPT_COMMSCONFIG: + case QT602240_SPT_GPIOPWM: + case QT602240_SPT_SELFTEST: + case QT602240_SPT_CTECONFIG: + case QT602240_SPT_USERDATA: + return true; + default: + return false; + } +} + +static bool qt602240_object_writable(unsigned int type) +{ + switch (type) { + case QT602240_GEN_COMMAND: + case QT602240_GEN_POWER: + case QT602240_GEN_ACQUIRE: + case QT602240_TOUCH_MULTI: + case QT602240_TOUCH_KEYARRAY: + case QT602240_TOUCH_PROXIMITY: + case QT602240_PROCI_GRIPFACE: + case QT602240_PROCG_NOISE: + case QT602240_PROCI_ONETOUCH: + case QT602240_PROCI_TWOTOUCH: + case QT602240_SPT_GPIOPWM: + case QT602240_SPT_SELFTEST: + case QT602240_SPT_CTECONFIG: + return true; + default: + return false; + } +} + +static void qt602240_dump_message(struct device *dev, + struct qt602240_message *message) +{ + dev_dbg(dev, "reportid:\t0x%x\n", message->reportid); + dev_dbg(dev, "message1:\t0x%x\n", message->message[0]); + dev_dbg(dev, "message2:\t0x%x\n", message->message[1]); + dev_dbg(dev, "message3:\t0x%x\n", message->message[2]); + dev_dbg(dev, "message4:\t0x%x\n", message->message[3]); + dev_dbg(dev, "message5:\t0x%x\n", message->message[4]); + dev_dbg(dev, "message6:\t0x%x\n", message->message[5]); + dev_dbg(dev, "message7:\t0x%x\n", message->message[6]); + dev_dbg(dev, "checksum:\t0x%x\n", message->checksum); +} + +static int qt602240_check_bootloader(struct i2c_client *client, + unsigned int state) +{ + u8 val; + +recheck: + if (i2c_master_recv(client, &val, 1) != 1) { + dev_err(&client->dev, "%s: i2c recv failed\n", __func__); + return -EIO; + } + + switch (state) { + case QT602240_WAITING_BOOTLOAD_CMD: + case QT602240_WAITING_FRAME_DATA: + val &= ~QT602240_BOOT_STATUS_MASK; + break; + case QT602240_FRAME_CRC_PASS: + if (val == QT602240_FRAME_CRC_CHECK) + goto recheck; + break; + default: + return -EINVAL; + } + + if (val != state) { + dev_err(&client->dev, "Unvalid bootloader mode state\n"); + return -EINVAL; + } + + return 0; +} + +static int qt602240_unlock_bootloader(struct i2c_client *client) +{ + u8 buf[2]; + + buf[0] = QT602240_UNLOCK_CMD_LSB; + buf[1] = QT602240_UNLOCK_CMD_MSB; + + if (i2c_master_send(client, buf, 2) != 2) { + dev_err(&client->dev, "%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int qt602240_fw_write(struct i2c_client *client, + const u8 *data, unsigned int frame_size) +{ + if (i2c_master_send(client, data, frame_size) != frame_size) { + dev_err(&client->dev, "%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int __qt602240_read_reg(struct i2c_client *client, + u16 reg, u16 len, void *val) +{ + struct i2c_msg xfer[2]; + u8 buf[2]; + + buf[0] = reg & 0xff; + buf[1] = (reg >> 8) & 0xff; + + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 2; + xfer[0].buf = buf; + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = len; + xfer[1].buf = val; + + if (i2c_transfer(client->adapter, xfer, 2) != 2) { + dev_err(&client->dev, "%s: i2c transfer failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int qt602240_read_reg(struct i2c_client *client, u16 reg, u8 *val) +{ + return __qt602240_read_reg(client, reg, 1, val); +} + +static int qt602240_write_reg(struct i2c_client *client, u16 reg, u8 val) +{ + u8 buf[3]; + + buf[0] = reg & 0xff; + buf[1] = (reg >> 8) & 0xff; + buf[2] = val; + + if (i2c_master_send(client, buf, 3) != 3) { + dev_err(&client->dev, "%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int qt602240_read_object_table(struct i2c_client *client, + u16 reg, u8 *object_buf) +{ + return __qt602240_read_reg(client, reg, QT602240_OBJECT_SIZE, + object_buf); +} + +static struct qt602240_object * +qt602240_get_object(struct qt602240_data *data, u8 type) +{ + struct qt602240_object *object; + int i; + + for (i = 0; i < data->info.object_num; i++) { + object = data->object_table + i; + if (object->type == type) + return object; + } + + dev_err(&data->client->dev, "Invalid object type\n"); + return NULL; +} + +static int qt602240_read_message(struct qt602240_data *data, + struct qt602240_message *message) +{ + struct qt602240_object *object; + u16 reg; + + object = qt602240_get_object(data, QT602240_GEN_MESSAGE); + if (!object) + return -EINVAL; + + reg = object->start_address; + return __qt602240_read_reg(data->client, reg, + sizeof(struct qt602240_message), message); +} + +static int qt602240_read_object(struct qt602240_data *data, + u8 type, u8 offset, u8 *val) +{ + struct qt602240_object *object; + u16 reg; + + object = qt602240_get_object(data, type); + if (!object) + return -EINVAL; + + reg = object->start_address; + return __qt602240_read_reg(data->client, reg + offset, 1, val); +} + +static int qt602240_write_object(struct qt602240_data *data, + u8 type, u8 offset, u8 val) +{ + struct qt602240_object *object; + u16 reg; + + object = qt602240_get_object(data, type); + if (!object) + return -EINVAL; + + reg = object->start_address; + return qt602240_write_reg(data->client, reg + offset, val); +} + +static void qt602240_input_report(struct qt602240_data *data, int single_id) +{ + struct qt602240_finger *finger = data->finger; + struct input_dev *input_dev = data->input_dev; + int status = finger[single_id].status; + int finger_num = 0; + int id; + + for (id = 0; id < QT602240_MAX_FINGER; id++) { + if (!finger[id].status) + continue; + + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + finger[id].status != QT602240_RELEASE ? + finger[id].area : 0); + input_report_abs(input_dev, ABS_MT_POSITION_X, + finger[id].x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, + finger[id].y); + input_mt_sync(input_dev); + + if (finger[id].status == QT602240_RELEASE) + finger[id].status = 0; + else + finger_num++; + } + + input_report_key(input_dev, BTN_TOUCH, finger_num > 0); + + if (status != QT602240_RELEASE) { + input_report_abs(input_dev, ABS_X, finger[single_id].x); + input_report_abs(input_dev, ABS_Y, finger[single_id].y); + } + + input_sync(input_dev); +} + +static void qt602240_input_touchevent(struct qt602240_data *data, + struct qt602240_message *message, int id) +{ + struct qt602240_finger *finger = data->finger; + struct device *dev = &data->client->dev; + u8 status = message->message[0]; + int x; + int y; + int area; + + /* Check the touch is present on the screen */ + if (!(status & QT602240_DETECT)) { + if (status & QT602240_RELEASE) { + dev_dbg(dev, "[%d] released\n", id); + + finger[id].status = QT602240_RELEASE; + qt602240_input_report(data, id); + } + return; + } + + /* Check only AMP detection */ + if (!(status & (QT602240_PRESS | QT602240_MOVE))) + return; + + x = (message->message[1] << 2) | ((message->message[3] & ~0x3f) >> 6); + y = (message->message[2] << 2) | ((message->message[3] & ~0xf3) >> 2); + area = message->message[4]; + + dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id, + status & QT602240_MOVE ? "moved" : "pressed", + x, y, area); + + finger[id].status = status & QT602240_MOVE ? + QT602240_MOVE : QT602240_PRESS; + finger[id].x = x; + finger[id].y = y; + finger[id].area = area; + + qt602240_input_report(data, id); +} + +static irqreturn_t qt602240_interrupt(int irq, void *dev_id) +{ + struct qt602240_data *data = dev_id; + struct qt602240_message message; + struct qt602240_object *object; + struct device *dev = &data->client->dev; + int id; + u8 reportid; + u8 max_reportid; + u8 min_reportid; + + do { + if (qt602240_read_message(data, &message)) { + dev_err(dev, "Failed to read message\n"); + goto end; + } + + reportid = message.reportid; + + /* whether reportid is thing of QT602240_TOUCH_MULTI */ + object = qt602240_get_object(data, QT602240_TOUCH_MULTI); + if (!object) + goto end; + + max_reportid = object->max_reportid; + min_reportid = max_reportid - object->num_report_ids + 1; + id = reportid - min_reportid; + + if (reportid >= min_reportid && reportid <= max_reportid) + qt602240_input_touchevent(data, &message, id); + else + qt602240_dump_message(dev, &message); + } while (reportid != 0xff); + +end: + return IRQ_HANDLED; +} + +static int qt602240_check_reg_init(struct qt602240_data *data) +{ + struct qt602240_object *object; + struct device *dev = &data->client->dev; + int index = 0; + int i, j; + u8 version = data->info.version; + u8 *init_vals; + + switch (version) { + case QT602240_VER_20: + init_vals = (u8 *)init_vals_ver_20; + break; + case QT602240_VER_21: + init_vals = (u8 *)init_vals_ver_21; + break; + case QT602240_VER_22: + init_vals = (u8 *)init_vals_ver_22; + break; + default: + dev_err(dev, "Firmware version %d doesn't support\n", version); + return -EINVAL; + } + + for (i = 0; i < data->info.object_num; i++) { + object = data->object_table + i; + + if (!qt602240_object_writable(object->type)) + continue; + + for (j = 0; j < object->size + 1; j++) + qt602240_write_object(data, object->type, j, + init_vals[index + j]); + + index += object->size + 1; + } + + return 0; +} + +static int qt602240_check_matrix_size(struct qt602240_data *data) +{ + const struct qt602240_platform_data *pdata = data->pdata; + struct device *dev = &data->client->dev; + int mode = -1; + int error; + u8 val; + + dev_dbg(dev, "Number of X lines: %d\n", pdata->x_line); + dev_dbg(dev, "Number of Y lines: %d\n", pdata->y_line); + + switch (pdata->x_line) { + case 0 ... 15: + if (pdata->y_line <= 14) + mode = 0; + break; + case 16: + if (pdata->y_line <= 12) + mode = 1; + if (pdata->y_line == 13 || pdata->y_line == 14) + mode = 0; + break; + case 17: + if (pdata->y_line <= 11) + mode = 2; + if (pdata->y_line == 12 || pdata->y_line == 13) + mode = 1; + break; + case 18: + if (pdata->y_line <= 10) + mode = 3; + if (pdata->y_line == 11 || pdata->y_line == 12) + mode = 2; + break; + case 19: + if (pdata->y_line <= 9) + mode = 4; + if (pdata->y_line == 10 || pdata->y_line == 11) + mode = 3; + break; + case 20: + mode = 4; + } + + if (mode < 0) { + dev_err(dev, "Invalid X/Y lines\n"); + return -EINVAL; + } + + error = qt602240_read_object(data, QT602240_SPT_CTECONFIG, + QT602240_CTE_MODE, &val); + if (error) + return error; + + if (mode == val) + return 0; + + /* Change the CTE configuration */ + qt602240_write_object(data, QT602240_SPT_CTECONFIG, + QT602240_CTE_CTRL, 1); + qt602240_write_object(data, QT602240_SPT_CTECONFIG, + QT602240_CTE_MODE, mode); + qt602240_write_object(data, QT602240_SPT_CTECONFIG, + QT602240_CTE_CTRL, 0); + + return 0; +} + +static int qt602240_make_highchg(struct qt602240_data *data) +{ + struct device *dev = &data->client->dev; + int count = 10; + int error; + u8 val; + + /* Read dummy message to make high CHG pin */ + do { + error = qt602240_read_object(data, QT602240_GEN_MESSAGE, 0, &val); + if (error) + return error; + } while ((val != 0xff) && --count); + + if (!count) { + dev_err(dev, "CHG pin isn't cleared\n"); + return -EBUSY; + } + + return 0; +} + +static void qt602240_handle_pdata(struct qt602240_data *data) +{ + const struct qt602240_platform_data *pdata = data->pdata; + u8 voltage; + + /* Set touchscreen lines */ + qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_XSIZE, + pdata->x_line); + qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_YSIZE, + pdata->y_line); + + /* Set touchscreen orient */ + qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_ORIENT, + pdata->orient); + + /* Set touchscreen burst length */ + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_BLEN, pdata->blen); + + /* Set touchscreen threshold */ + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_TCHTHR, pdata->threshold); + + /* Set touchscreen resolution */ + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); + + /* Set touchscreen voltage */ + if (data->info.version >= QT602240_VER_21 && pdata->voltage) { + if (pdata->voltage < QT602240_VOLTAGE_DEFAULT) { + voltage = (QT602240_VOLTAGE_DEFAULT - pdata->voltage) / + QT602240_VOLTAGE_STEP; + voltage = 0xff - voltage + 1; + } else + voltage = (pdata->voltage - QT602240_VOLTAGE_DEFAULT) / + QT602240_VOLTAGE_STEP; + + qt602240_write_object(data, QT602240_SPT_CTECONFIG, + QT602240_CTE_VOLTAGE, voltage); + } +} + +static int qt602240_get_info(struct qt602240_data *data) +{ + struct i2c_client *client = data->client; + struct qt602240_info *info = &data->info; + int error; + u8 val; + + error = qt602240_read_reg(client, QT602240_FAMILY_ID, &val); + if (error) + return error; + info->family_id = val; + + error = qt602240_read_reg(client, QT602240_VARIANT_ID, &val); + if (error) + return error; + info->variant_id = val; + + error = qt602240_read_reg(client, QT602240_VERSION, &val); + if (error) + return error; + info->version = val; + + error = qt602240_read_reg(client, QT602240_BUILD, &val); + if (error) + return error; + info->build = val; + + error = qt602240_read_reg(client, QT602240_OBJECT_NUM, &val); + if (error) + return error; + info->object_num = val; + + return 0; +} + +static int qt602240_get_object_table(struct qt602240_data *data) +{ + int error; + int i; + u16 reg; + u8 reportid = 0; + u8 buf[QT602240_OBJECT_SIZE]; + + for (i = 0; i < data->info.object_num; i++) { + struct qt602240_object *object = data->object_table + i; + + reg = QT602240_OBJECT_START + QT602240_OBJECT_SIZE * i; + error = qt602240_read_object_table(data->client, reg, buf); + if (error) + return error; + + object->type = buf[0]; + object->start_address = (buf[2] << 8) | buf[1]; + object->size = buf[3]; + object->instances = buf[4]; + object->num_report_ids = buf[5]; + + if (object->num_report_ids) { + reportid += object->num_report_ids * + (object->instances + 1); + object->max_reportid = reportid; + } + } + + return 0; +} + +static int qt602240_initialize(struct qt602240_data *data) +{ + struct i2c_client *client = data->client; + struct qt602240_info *info = &data->info; + int error; + u8 val; + + error = qt602240_get_info(data); + if (error) + return error; + + data->object_table = kcalloc(info->object_num, + sizeof(struct qt602240_object), + GFP_KERNEL); + if (!data->object_table) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + /* Get object table information */ + error = qt602240_get_object_table(data); + if (error) + return error; + + /* Check register init values */ + error = qt602240_check_reg_init(data); + if (error) + return error; + + /* Check X/Y matrix size */ + error = qt602240_check_matrix_size(data); + if (error) + return error; + + error = qt602240_make_highchg(data); + if (error) + return error; + + qt602240_handle_pdata(data); + + /* Backup to memory */ + qt602240_write_object(data, QT602240_GEN_COMMAND, + QT602240_COMMAND_BACKUPNV, + QT602240_BACKUP_VALUE); + msleep(QT602240_BACKUP_TIME); + + /* Soft reset */ + qt602240_write_object(data, QT602240_GEN_COMMAND, + QT602240_COMMAND_RESET, 1); + msleep(QT602240_RESET_TIME); + + /* Update matrix size at info struct */ + error = qt602240_read_reg(client, QT602240_MATRIX_X_SIZE, &val); + if (error) + return error; + info->matrix_xsize = val; + + error = qt602240_read_reg(client, QT602240_MATRIX_Y_SIZE, &val); + if (error) + return error; + info->matrix_ysize = val; + + dev_info(&client->dev, + "Family ID: %d Variant ID: %d Version: %d Build: %d\n", + info->family_id, info->variant_id, info->version, + info->build); + + dev_info(&client->dev, + "Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n", + info->matrix_xsize, info->matrix_ysize, + info->object_num); + + return 0; +} + +static ssize_t qt602240_object_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qt602240_data *data = dev_get_drvdata(dev); + struct qt602240_object *object; + int count = 0; + int i, j; + int error; + u8 val; + + for (i = 0; i < data->info.object_num; i++) { + object = data->object_table + i; + + count += sprintf(buf + count, + "Object Table Element %d(Type %d)\n", + i + 1, object->type); + + if (!qt602240_object_readable(object->type)) { + count += sprintf(buf + count, "\n"); + continue; + } + + for (j = 0; j < object->size + 1; j++) { + error = qt602240_read_object(data, + object->type, j, &val); + if (error) + return error; + + count += sprintf(buf + count, + " Byte %d: 0x%x (%d)\n", j, val, val); + } + + count += sprintf(buf + count, "\n"); + } + + return count; +} + +static int qt602240_load_fw(struct device *dev, const char *fn) +{ + struct qt602240_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + const struct firmware *fw = NULL; + unsigned int frame_size; + unsigned int pos = 0; + int ret; + + ret = request_firmware(&fw, fn, dev); + if (ret) { + dev_err(dev, "Unable to open firmware %s\n", fn); + return ret; + } + + /* Change to the bootloader mode */ + qt602240_write_object(data, QT602240_GEN_COMMAND, + QT602240_COMMAND_RESET, QT602240_BOOT_VALUE); + msleep(QT602240_RESET_TIME); + + /* Change to slave address of bootloader */ + if (client->addr == QT602240_APP_LOW) + client->addr = QT602240_BOOT_LOW; + else + client->addr = QT602240_BOOT_HIGH; + + ret = qt602240_check_bootloader(client, QT602240_WAITING_BOOTLOAD_CMD); + if (ret) + goto out; + + /* Unlock bootloader */ + qt602240_unlock_bootloader(client); + + while (pos < fw->size) { + ret = qt602240_check_bootloader(client, + QT602240_WAITING_FRAME_DATA); + if (ret) + goto out; + + frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); + + /* We should add 2 at frame size as the the firmware data is not + * included the CRC bytes. + */ + frame_size += 2; + + /* Write one frame to device */ + qt602240_fw_write(client, fw->data + pos, frame_size); + + ret = qt602240_check_bootloader(client, + QT602240_FRAME_CRC_PASS); + if (ret) + goto out; + + pos += frame_size; + + dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); + } + +out: + release_firmware(fw); + + /* Change to slave address of application */ + if (client->addr == QT602240_BOOT_LOW) + client->addr = QT602240_APP_LOW; + else + client->addr = QT602240_APP_HIGH; + + return ret; +} + +static ssize_t qt602240_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct qt602240_data *data = dev_get_drvdata(dev); + unsigned int version; + int error; + + if (sscanf(buf, "%u", &version) != 1) { + dev_err(dev, "Invalid values\n"); + return -EINVAL; + } + + if (data->info.version < QT602240_VER_21 || version < QT602240_VER_21) { + dev_err(dev, "FW update supported starting with version 21\n"); + return -EINVAL; + } + + disable_irq(data->irq); + + error = qt602240_load_fw(dev, QT602240_FW_NAME); + if (error) { + dev_err(dev, "The firmware update failed(%d)\n", error); + count = error; + } else { + dev_dbg(dev, "The firmware update succeeded\n"); + + /* Wait for reset */ + msleep(QT602240_FWRESET_TIME); + + kfree(data->object_table); + data->object_table = NULL; + + qt602240_initialize(data); + } + + enable_irq(data->irq); + + return count; +} + +static DEVICE_ATTR(object, 0444, qt602240_object_show, NULL); +static DEVICE_ATTR(update_fw, 0664, NULL, qt602240_update_fw_store); + +static struct attribute *qt602240_attrs[] = { + &dev_attr_object.attr, + &dev_attr_update_fw.attr, + NULL +}; + +static const struct attribute_group qt602240_attr_group = { + .attrs = qt602240_attrs, +}; + +static void qt602240_start(struct qt602240_data *data) +{ + /* Touch enable */ + qt602240_write_object(data, + QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0x83); +} + +static void qt602240_stop(struct qt602240_data *data) +{ + /* Touch disable */ + qt602240_write_object(data, + QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0); +} + +static int qt602240_input_open(struct input_dev *dev) +{ + struct qt602240_data *data = input_get_drvdata(dev); + + qt602240_start(data); + + return 0; +} + +static void qt602240_input_close(struct input_dev *dev) +{ + struct qt602240_data *data = input_get_drvdata(dev); + + qt602240_stop(data); +} + +static int __devinit qt602240_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct qt602240_data *data; + struct input_dev *input_dev; + int error; + + if (!client->dev.platform_data) + return -EINVAL; + + data = kzalloc(sizeof(struct qt602240_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + input_dev->name = "AT42QT602240/ATMXT224 Touchscreen"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_dev->open = qt602240_input_open; + input_dev->close = qt602240_input_close; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + + /* For single touch */ + input_set_abs_params(input_dev, ABS_X, + 0, QT602240_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, QT602240_MAX_YC, 0, 0); + + /* For multi touch */ + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, QT602240_MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, QT602240_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, QT602240_MAX_YC, 0, 0); + + input_set_drvdata(input_dev, data); + + data->client = client; + data->input_dev = input_dev; + data->pdata = client->dev.platform_data; + data->irq = client->irq; + + i2c_set_clientdata(client, data); + + error = qt602240_initialize(data); + if (error) + goto err_free_object; + + error = request_threaded_irq(client->irq, NULL, qt602240_interrupt, + IRQF_TRIGGER_FALLING, client->dev.driver->name, data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_object; + } + + error = input_register_device(input_dev); + if (error) + goto err_free_irq; + + error = sysfs_create_group(&client->dev.kobj, &qt602240_attr_group); + if (error) + goto err_unregister_device; + + return 0; + +err_unregister_device: + input_unregister_device(input_dev); + input_dev = NULL; +err_free_irq: + free_irq(client->irq, data); +err_free_object: + kfree(data->object_table); +err_free_mem: + input_free_device(input_dev); + kfree(data); + return error; +} + +static int __devexit qt602240_remove(struct i2c_client *client) +{ + struct qt602240_data *data = i2c_get_clientdata(client); + + sysfs_remove_group(&client->dev.kobj, &qt602240_attr_group); + free_irq(data->irq, data); + input_unregister_device(data->input_dev); + kfree(data->object_table); + kfree(data); + + return 0; +} + +#ifdef CONFIG_PM +static int qt602240_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct qt602240_data *data = i2c_get_clientdata(client); + struct input_dev *input_dev = data->input_dev; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + qt602240_stop(data); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int qt602240_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct qt602240_data *data = i2c_get_clientdata(client); + struct input_dev *input_dev = data->input_dev; + + /* Soft reset */ + qt602240_write_object(data, QT602240_GEN_COMMAND, + QT602240_COMMAND_RESET, 1); + + msleep(QT602240_RESET_TIME); + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + qt602240_start(data); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static const struct dev_pm_ops qt602240_pm_ops = { + .suspend = qt602240_suspend, + .resume = qt602240_resume, +}; +#endif + +static const struct i2c_device_id qt602240_id[] = { + { "qt602240_ts", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, qt602240_id); + +static struct i2c_driver qt602240_driver = { + .driver = { + .name = "qt602240_ts", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &qt602240_pm_ops, +#endif + }, + .probe = qt602240_probe, + .remove = __devexit_p(qt602240_remove), + .id_table = qt602240_id, +}; + +static int __init qt602240_init(void) +{ + return i2c_add_driver(&qt602240_driver); +} + +static void __exit qt602240_exit(void) +{ + i2c_del_driver(&qt602240_driver); +} + +module_init(qt602240_init); +module_exit(qt602240_exit); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_DESCRIPTION("AT42QT602240/ATMXT224 Touchscreen driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/qt602240_ts.c b/drivers/input/touchscreen/qt602240_ts.c deleted file mode 100644 index 4dcb0e872f6a..000000000000 --- a/drivers/input/touchscreen/qt602240_ts.c +++ /dev/null @@ -1,1406 +0,0 @@ -/* - * AT42QT602240/ATMXT224 Touchscreen driver - * - * Copyright (C) 2010 Samsung Electronics Co.Ltd - * Author: Joonyoung Shim - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Version */ -#define QT602240_VER_20 20 -#define QT602240_VER_21 21 -#define QT602240_VER_22 22 - -/* Slave addresses */ -#define QT602240_APP_LOW 0x4a -#define QT602240_APP_HIGH 0x4b -#define QT602240_BOOT_LOW 0x24 -#define QT602240_BOOT_HIGH 0x25 - -/* Firmware */ -#define QT602240_FW_NAME "qt602240.fw" - -/* Registers */ -#define QT602240_FAMILY_ID 0x00 -#define QT602240_VARIANT_ID 0x01 -#define QT602240_VERSION 0x02 -#define QT602240_BUILD 0x03 -#define QT602240_MATRIX_X_SIZE 0x04 -#define QT602240_MATRIX_Y_SIZE 0x05 -#define QT602240_OBJECT_NUM 0x06 -#define QT602240_OBJECT_START 0x07 - -#define QT602240_OBJECT_SIZE 6 - -/* Object types */ -#define QT602240_DEBUG_DIAGNOSTIC 37 -#define QT602240_GEN_MESSAGE 5 -#define QT602240_GEN_COMMAND 6 -#define QT602240_GEN_POWER 7 -#define QT602240_GEN_ACQUIRE 8 -#define QT602240_TOUCH_MULTI 9 -#define QT602240_TOUCH_KEYARRAY 15 -#define QT602240_TOUCH_PROXIMITY 23 -#define QT602240_PROCI_GRIPFACE 20 -#define QT602240_PROCG_NOISE 22 -#define QT602240_PROCI_ONETOUCH 24 -#define QT602240_PROCI_TWOTOUCH 27 -#define QT602240_SPT_COMMSCONFIG 18 /* firmware ver 21 over */ -#define QT602240_SPT_GPIOPWM 19 -#define QT602240_SPT_SELFTEST 25 -#define QT602240_SPT_CTECONFIG 28 -#define QT602240_SPT_USERDATA 38 /* firmware ver 21 over */ - -/* QT602240_GEN_COMMAND field */ -#define QT602240_COMMAND_RESET 0 -#define QT602240_COMMAND_BACKUPNV 1 -#define QT602240_COMMAND_CALIBRATE 2 -#define QT602240_COMMAND_REPORTALL 3 -#define QT602240_COMMAND_DIAGNOSTIC 5 - -/* QT602240_GEN_POWER field */ -#define QT602240_POWER_IDLEACQINT 0 -#define QT602240_POWER_ACTVACQINT 1 -#define QT602240_POWER_ACTV2IDLETO 2 - -/* QT602240_GEN_ACQUIRE field */ -#define QT602240_ACQUIRE_CHRGTIME 0 -#define QT602240_ACQUIRE_TCHDRIFT 2 -#define QT602240_ACQUIRE_DRIFTST 3 -#define QT602240_ACQUIRE_TCHAUTOCAL 4 -#define QT602240_ACQUIRE_SYNC 5 -#define QT602240_ACQUIRE_ATCHCALST 6 -#define QT602240_ACQUIRE_ATCHCALSTHR 7 - -/* QT602240_TOUCH_MULTI field */ -#define QT602240_TOUCH_CTRL 0 -#define QT602240_TOUCH_XORIGIN 1 -#define QT602240_TOUCH_YORIGIN 2 -#define QT602240_TOUCH_XSIZE 3 -#define QT602240_TOUCH_YSIZE 4 -#define QT602240_TOUCH_BLEN 6 -#define QT602240_TOUCH_TCHTHR 7 -#define QT602240_TOUCH_TCHDI 8 -#define QT602240_TOUCH_ORIENT 9 -#define QT602240_TOUCH_MOVHYSTI 11 -#define QT602240_TOUCH_MOVHYSTN 12 -#define QT602240_TOUCH_NUMTOUCH 14 -#define QT602240_TOUCH_MRGHYST 15 -#define QT602240_TOUCH_MRGTHR 16 -#define QT602240_TOUCH_AMPHYST 17 -#define QT602240_TOUCH_XRANGE_LSB 18 -#define QT602240_TOUCH_XRANGE_MSB 19 -#define QT602240_TOUCH_YRANGE_LSB 20 -#define QT602240_TOUCH_YRANGE_MSB 21 -#define QT602240_TOUCH_XLOCLIP 22 -#define QT602240_TOUCH_XHICLIP 23 -#define QT602240_TOUCH_YLOCLIP 24 -#define QT602240_TOUCH_YHICLIP 25 -#define QT602240_TOUCH_XEDGECTRL 26 -#define QT602240_TOUCH_XEDGEDIST 27 -#define QT602240_TOUCH_YEDGECTRL 28 -#define QT602240_TOUCH_YEDGEDIST 29 -#define QT602240_TOUCH_JUMPLIMIT 30 /* firmware ver 22 over */ - -/* QT602240_PROCI_GRIPFACE field */ -#define QT602240_GRIPFACE_CTRL 0 -#define QT602240_GRIPFACE_XLOGRIP 1 -#define QT602240_GRIPFACE_XHIGRIP 2 -#define QT602240_GRIPFACE_YLOGRIP 3 -#define QT602240_GRIPFACE_YHIGRIP 4 -#define QT602240_GRIPFACE_MAXTCHS 5 -#define QT602240_GRIPFACE_SZTHR1 7 -#define QT602240_GRIPFACE_SZTHR2 8 -#define QT602240_GRIPFACE_SHPTHR1 9 -#define QT602240_GRIPFACE_SHPTHR2 10 -#define QT602240_GRIPFACE_SUPEXTTO 11 - -/* QT602240_PROCI_NOISE field */ -#define QT602240_NOISE_CTRL 0 -#define QT602240_NOISE_OUTFLEN 1 -#define QT602240_NOISE_GCAFUL_LSB 3 -#define QT602240_NOISE_GCAFUL_MSB 4 -#define QT602240_NOISE_GCAFLL_LSB 5 -#define QT602240_NOISE_GCAFLL_MSB 6 -#define QT602240_NOISE_ACTVGCAFVALID 7 -#define QT602240_NOISE_NOISETHR 8 -#define QT602240_NOISE_FREQHOPSCALE 10 -#define QT602240_NOISE_FREQ0 11 -#define QT602240_NOISE_FREQ1 12 -#define QT602240_NOISE_FREQ2 13 -#define QT602240_NOISE_FREQ3 14 -#define QT602240_NOISE_FREQ4 15 -#define QT602240_NOISE_IDLEGCAFVALID 16 - -/* QT602240_SPT_COMMSCONFIG */ -#define QT602240_COMMS_CTRL 0 -#define QT602240_COMMS_CMD 1 - -/* QT602240_SPT_CTECONFIG field */ -#define QT602240_CTE_CTRL 0 -#define QT602240_CTE_CMD 1 -#define QT602240_CTE_MODE 2 -#define QT602240_CTE_IDLEGCAFDEPTH 3 -#define QT602240_CTE_ACTVGCAFDEPTH 4 -#define QT602240_CTE_VOLTAGE 5 /* firmware ver 21 over */ - -#define QT602240_VOLTAGE_DEFAULT 2700000 -#define QT602240_VOLTAGE_STEP 10000 - -/* Define for QT602240_GEN_COMMAND */ -#define QT602240_BOOT_VALUE 0xa5 -#define QT602240_BACKUP_VALUE 0x55 -#define QT602240_BACKUP_TIME 25 /* msec */ -#define QT602240_RESET_TIME 65 /* msec */ - -#define QT602240_FWRESET_TIME 175 /* msec */ - -/* Command to unlock bootloader */ -#define QT602240_UNLOCK_CMD_MSB 0xaa -#define QT602240_UNLOCK_CMD_LSB 0xdc - -/* Bootloader mode status */ -#define QT602240_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */ -#define QT602240_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */ -#define QT602240_FRAME_CRC_CHECK 0x02 -#define QT602240_FRAME_CRC_FAIL 0x03 -#define QT602240_FRAME_CRC_PASS 0x04 -#define QT602240_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ -#define QT602240_BOOT_STATUS_MASK 0x3f - -/* Touch status */ -#define QT602240_SUPPRESS (1 << 1) -#define QT602240_AMP (1 << 2) -#define QT602240_VECTOR (1 << 3) -#define QT602240_MOVE (1 << 4) -#define QT602240_RELEASE (1 << 5) -#define QT602240_PRESS (1 << 6) -#define QT602240_DETECT (1 << 7) - -/* Touchscreen absolute values */ -#define QT602240_MAX_XC 0x3ff -#define QT602240_MAX_YC 0x3ff -#define QT602240_MAX_AREA 0xff - -#define QT602240_MAX_FINGER 10 - -/* Initial register values recommended from chip vendor */ -static const u8 init_vals_ver_20[] = { - /* QT602240_GEN_COMMAND(6) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_GEN_POWER(7) */ - 0x20, 0xff, 0x32, - /* QT602240_GEN_ACQUIRE(8) */ - 0x08, 0x05, 0x05, 0x00, 0x00, 0x00, 0x05, 0x14, - /* QT602240_TOUCH_MULTI(9) */ - 0x00, 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x64, - /* QT602240_TOUCH_KEYARRAY(15) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - /* QT602240_SPT_GPIOPWM(19) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - /* QT602240_PROCI_GRIPFACE(20) */ - 0x00, 0x64, 0x64, 0x64, 0x64, 0x00, 0x00, 0x1e, 0x14, 0x04, - 0x1e, 0x00, - /* QT602240_PROCG_NOISE(22) */ - 0x05, 0x00, 0x00, 0x19, 0x00, 0xe7, 0xff, 0x04, 0x32, 0x00, - 0x01, 0x0a, 0x0f, 0x14, 0x00, 0x00, 0xe8, - /* QT602240_TOUCH_PROXIMITY(23) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - /* QT602240_PROCI_ONETOUCH(24) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_SELFTEST(25) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_TWOTOUCH(27) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_CTECONFIG(28) */ - 0x00, 0x00, 0x00, 0x04, 0x08, -}; - -static const u8 init_vals_ver_21[] = { - /* QT602240_GEN_COMMAND(6) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_GEN_POWER(7) */ - 0x20, 0xff, 0x32, - /* QT602240_GEN_ACQUIRE(8) */ - 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, - /* QT602240_TOUCH_MULTI(9) */ - 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_TOUCH_KEYARRAY(15) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - /* QT602240_SPT_GPIOPWM(19) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_GRIPFACE(20) */ - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, - 0x0f, 0x0a, - /* QT602240_PROCG_NOISE(22) */ - 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, - 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, - /* QT602240_TOUCH_PROXIMITY(23) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - /* QT602240_PROCI_ONETOUCH(24) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_SELFTEST(25) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_TWOTOUCH(27) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_CTECONFIG(28) */ - 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, -}; - -static const u8 init_vals_ver_22[] = { - /* QT602240_GEN_COMMAND(6) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_GEN_POWER(7) */ - 0x20, 0xff, 0x32, - /* QT602240_GEN_ACQUIRE(8) */ - 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, - /* QT602240_TOUCH_MULTI(9) */ - 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - /* QT602240_TOUCH_KEYARRAY(15) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - /* QT602240_SPT_GPIOPWM(19) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_GRIPFACE(20) */ - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, - 0x0f, 0x0a, - /* QT602240_PROCG_NOISE(22) */ - 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, - 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, - /* QT602240_TOUCH_PROXIMITY(23) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_ONETOUCH(24) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_SELFTEST(25) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_TWOTOUCH(27) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_CTECONFIG(28) */ - 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, -}; - -struct qt602240_info { - u8 family_id; - u8 variant_id; - u8 version; - u8 build; - u8 matrix_xsize; - u8 matrix_ysize; - u8 object_num; -}; - -struct qt602240_object { - u8 type; - u16 start_address; - u8 size; - u8 instances; - u8 num_report_ids; - - /* to map object and message */ - u8 max_reportid; -}; - -struct qt602240_message { - u8 reportid; - u8 message[7]; - u8 checksum; -}; - -struct qt602240_finger { - int status; - int x; - int y; - int area; -}; - -/* Each client has this additional data */ -struct qt602240_data { - struct i2c_client *client; - struct input_dev *input_dev; - const struct qt602240_platform_data *pdata; - struct qt602240_object *object_table; - struct qt602240_info info; - struct qt602240_finger finger[QT602240_MAX_FINGER]; - unsigned int irq; -}; - -static bool qt602240_object_readable(unsigned int type) -{ - switch (type) { - case QT602240_GEN_MESSAGE: - case QT602240_GEN_COMMAND: - case QT602240_GEN_POWER: - case QT602240_GEN_ACQUIRE: - case QT602240_TOUCH_MULTI: - case QT602240_TOUCH_KEYARRAY: - case QT602240_TOUCH_PROXIMITY: - case QT602240_PROCI_GRIPFACE: - case QT602240_PROCG_NOISE: - case QT602240_PROCI_ONETOUCH: - case QT602240_PROCI_TWOTOUCH: - case QT602240_SPT_COMMSCONFIG: - case QT602240_SPT_GPIOPWM: - case QT602240_SPT_SELFTEST: - case QT602240_SPT_CTECONFIG: - case QT602240_SPT_USERDATA: - return true; - default: - return false; - } -} - -static bool qt602240_object_writable(unsigned int type) -{ - switch (type) { - case QT602240_GEN_COMMAND: - case QT602240_GEN_POWER: - case QT602240_GEN_ACQUIRE: - case QT602240_TOUCH_MULTI: - case QT602240_TOUCH_KEYARRAY: - case QT602240_TOUCH_PROXIMITY: - case QT602240_PROCI_GRIPFACE: - case QT602240_PROCG_NOISE: - case QT602240_PROCI_ONETOUCH: - case QT602240_PROCI_TWOTOUCH: - case QT602240_SPT_GPIOPWM: - case QT602240_SPT_SELFTEST: - case QT602240_SPT_CTECONFIG: - return true; - default: - return false; - } -} - -static void qt602240_dump_message(struct device *dev, - struct qt602240_message *message) -{ - dev_dbg(dev, "reportid:\t0x%x\n", message->reportid); - dev_dbg(dev, "message1:\t0x%x\n", message->message[0]); - dev_dbg(dev, "message2:\t0x%x\n", message->message[1]); - dev_dbg(dev, "message3:\t0x%x\n", message->message[2]); - dev_dbg(dev, "message4:\t0x%x\n", message->message[3]); - dev_dbg(dev, "message5:\t0x%x\n", message->message[4]); - dev_dbg(dev, "message6:\t0x%x\n", message->message[5]); - dev_dbg(dev, "message7:\t0x%x\n", message->message[6]); - dev_dbg(dev, "checksum:\t0x%x\n", message->checksum); -} - -static int qt602240_check_bootloader(struct i2c_client *client, - unsigned int state) -{ - u8 val; - -recheck: - if (i2c_master_recv(client, &val, 1) != 1) { - dev_err(&client->dev, "%s: i2c recv failed\n", __func__); - return -EIO; - } - - switch (state) { - case QT602240_WAITING_BOOTLOAD_CMD: - case QT602240_WAITING_FRAME_DATA: - val &= ~QT602240_BOOT_STATUS_MASK; - break; - case QT602240_FRAME_CRC_PASS: - if (val == QT602240_FRAME_CRC_CHECK) - goto recheck; - break; - default: - return -EINVAL; - } - - if (val != state) { - dev_err(&client->dev, "Unvalid bootloader mode state\n"); - return -EINVAL; - } - - return 0; -} - -static int qt602240_unlock_bootloader(struct i2c_client *client) -{ - u8 buf[2]; - - buf[0] = QT602240_UNLOCK_CMD_LSB; - buf[1] = QT602240_UNLOCK_CMD_MSB; - - if (i2c_master_send(client, buf, 2) != 2) { - dev_err(&client->dev, "%s: i2c send failed\n", __func__); - return -EIO; - } - - return 0; -} - -static int qt602240_fw_write(struct i2c_client *client, - const u8 *data, unsigned int frame_size) -{ - if (i2c_master_send(client, data, frame_size) != frame_size) { - dev_err(&client->dev, "%s: i2c send failed\n", __func__); - return -EIO; - } - - return 0; -} - -static int __qt602240_read_reg(struct i2c_client *client, - u16 reg, u16 len, void *val) -{ - struct i2c_msg xfer[2]; - u8 buf[2]; - - buf[0] = reg & 0xff; - buf[1] = (reg >> 8) & 0xff; - - /* Write register */ - xfer[0].addr = client->addr; - xfer[0].flags = 0; - xfer[0].len = 2; - xfer[0].buf = buf; - - /* Read data */ - xfer[1].addr = client->addr; - xfer[1].flags = I2C_M_RD; - xfer[1].len = len; - xfer[1].buf = val; - - if (i2c_transfer(client->adapter, xfer, 2) != 2) { - dev_err(&client->dev, "%s: i2c transfer failed\n", __func__); - return -EIO; - } - - return 0; -} - -static int qt602240_read_reg(struct i2c_client *client, u16 reg, u8 *val) -{ - return __qt602240_read_reg(client, reg, 1, val); -} - -static int qt602240_write_reg(struct i2c_client *client, u16 reg, u8 val) -{ - u8 buf[3]; - - buf[0] = reg & 0xff; - buf[1] = (reg >> 8) & 0xff; - buf[2] = val; - - if (i2c_master_send(client, buf, 3) != 3) { - dev_err(&client->dev, "%s: i2c send failed\n", __func__); - return -EIO; - } - - return 0; -} - -static int qt602240_read_object_table(struct i2c_client *client, - u16 reg, u8 *object_buf) -{ - return __qt602240_read_reg(client, reg, QT602240_OBJECT_SIZE, - object_buf); -} - -static struct qt602240_object * -qt602240_get_object(struct qt602240_data *data, u8 type) -{ - struct qt602240_object *object; - int i; - - for (i = 0; i < data->info.object_num; i++) { - object = data->object_table + i; - if (object->type == type) - return object; - } - - dev_err(&data->client->dev, "Invalid object type\n"); - return NULL; -} - -static int qt602240_read_message(struct qt602240_data *data, - struct qt602240_message *message) -{ - struct qt602240_object *object; - u16 reg; - - object = qt602240_get_object(data, QT602240_GEN_MESSAGE); - if (!object) - return -EINVAL; - - reg = object->start_address; - return __qt602240_read_reg(data->client, reg, - sizeof(struct qt602240_message), message); -} - -static int qt602240_read_object(struct qt602240_data *data, - u8 type, u8 offset, u8 *val) -{ - struct qt602240_object *object; - u16 reg; - - object = qt602240_get_object(data, type); - if (!object) - return -EINVAL; - - reg = object->start_address; - return __qt602240_read_reg(data->client, reg + offset, 1, val); -} - -static int qt602240_write_object(struct qt602240_data *data, - u8 type, u8 offset, u8 val) -{ - struct qt602240_object *object; - u16 reg; - - object = qt602240_get_object(data, type); - if (!object) - return -EINVAL; - - reg = object->start_address; - return qt602240_write_reg(data->client, reg + offset, val); -} - -static void qt602240_input_report(struct qt602240_data *data, int single_id) -{ - struct qt602240_finger *finger = data->finger; - struct input_dev *input_dev = data->input_dev; - int status = finger[single_id].status; - int finger_num = 0; - int id; - - for (id = 0; id < QT602240_MAX_FINGER; id++) { - if (!finger[id].status) - continue; - - input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, - finger[id].status != QT602240_RELEASE ? - finger[id].area : 0); - input_report_abs(input_dev, ABS_MT_POSITION_X, - finger[id].x); - input_report_abs(input_dev, ABS_MT_POSITION_Y, - finger[id].y); - input_mt_sync(input_dev); - - if (finger[id].status == QT602240_RELEASE) - finger[id].status = 0; - else - finger_num++; - } - - input_report_key(input_dev, BTN_TOUCH, finger_num > 0); - - if (status != QT602240_RELEASE) { - input_report_abs(input_dev, ABS_X, finger[single_id].x); - input_report_abs(input_dev, ABS_Y, finger[single_id].y); - } - - input_sync(input_dev); -} - -static void qt602240_input_touchevent(struct qt602240_data *data, - struct qt602240_message *message, int id) -{ - struct qt602240_finger *finger = data->finger; - struct device *dev = &data->client->dev; - u8 status = message->message[0]; - int x; - int y; - int area; - - /* Check the touch is present on the screen */ - if (!(status & QT602240_DETECT)) { - if (status & QT602240_RELEASE) { - dev_dbg(dev, "[%d] released\n", id); - - finger[id].status = QT602240_RELEASE; - qt602240_input_report(data, id); - } - return; - } - - /* Check only AMP detection */ - if (!(status & (QT602240_PRESS | QT602240_MOVE))) - return; - - x = (message->message[1] << 2) | ((message->message[3] & ~0x3f) >> 6); - y = (message->message[2] << 2) | ((message->message[3] & ~0xf3) >> 2); - area = message->message[4]; - - dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id, - status & QT602240_MOVE ? "moved" : "pressed", - x, y, area); - - finger[id].status = status & QT602240_MOVE ? - QT602240_MOVE : QT602240_PRESS; - finger[id].x = x; - finger[id].y = y; - finger[id].area = area; - - qt602240_input_report(data, id); -} - -static irqreturn_t qt602240_interrupt(int irq, void *dev_id) -{ - struct qt602240_data *data = dev_id; - struct qt602240_message message; - struct qt602240_object *object; - struct device *dev = &data->client->dev; - int id; - u8 reportid; - u8 max_reportid; - u8 min_reportid; - - do { - if (qt602240_read_message(data, &message)) { - dev_err(dev, "Failed to read message\n"); - goto end; - } - - reportid = message.reportid; - - /* whether reportid is thing of QT602240_TOUCH_MULTI */ - object = qt602240_get_object(data, QT602240_TOUCH_MULTI); - if (!object) - goto end; - - max_reportid = object->max_reportid; - min_reportid = max_reportid - object->num_report_ids + 1; - id = reportid - min_reportid; - - if (reportid >= min_reportid && reportid <= max_reportid) - qt602240_input_touchevent(data, &message, id); - else - qt602240_dump_message(dev, &message); - } while (reportid != 0xff); - -end: - return IRQ_HANDLED; -} - -static int qt602240_check_reg_init(struct qt602240_data *data) -{ - struct qt602240_object *object; - struct device *dev = &data->client->dev; - int index = 0; - int i, j; - u8 version = data->info.version; - u8 *init_vals; - - switch (version) { - case QT602240_VER_20: - init_vals = (u8 *)init_vals_ver_20; - break; - case QT602240_VER_21: - init_vals = (u8 *)init_vals_ver_21; - break; - case QT602240_VER_22: - init_vals = (u8 *)init_vals_ver_22; - break; - default: - dev_err(dev, "Firmware version %d doesn't support\n", version); - return -EINVAL; - } - - for (i = 0; i < data->info.object_num; i++) { - object = data->object_table + i; - - if (!qt602240_object_writable(object->type)) - continue; - - for (j = 0; j < object->size + 1; j++) - qt602240_write_object(data, object->type, j, - init_vals[index + j]); - - index += object->size + 1; - } - - return 0; -} - -static int qt602240_check_matrix_size(struct qt602240_data *data) -{ - const struct qt602240_platform_data *pdata = data->pdata; - struct device *dev = &data->client->dev; - int mode = -1; - int error; - u8 val; - - dev_dbg(dev, "Number of X lines: %d\n", pdata->x_line); - dev_dbg(dev, "Number of Y lines: %d\n", pdata->y_line); - - switch (pdata->x_line) { - case 0 ... 15: - if (pdata->y_line <= 14) - mode = 0; - break; - case 16: - if (pdata->y_line <= 12) - mode = 1; - if (pdata->y_line == 13 || pdata->y_line == 14) - mode = 0; - break; - case 17: - if (pdata->y_line <= 11) - mode = 2; - if (pdata->y_line == 12 || pdata->y_line == 13) - mode = 1; - break; - case 18: - if (pdata->y_line <= 10) - mode = 3; - if (pdata->y_line == 11 || pdata->y_line == 12) - mode = 2; - break; - case 19: - if (pdata->y_line <= 9) - mode = 4; - if (pdata->y_line == 10 || pdata->y_line == 11) - mode = 3; - break; - case 20: - mode = 4; - } - - if (mode < 0) { - dev_err(dev, "Invalid X/Y lines\n"); - return -EINVAL; - } - - error = qt602240_read_object(data, QT602240_SPT_CTECONFIG, - QT602240_CTE_MODE, &val); - if (error) - return error; - - if (mode == val) - return 0; - - /* Change the CTE configuration */ - qt602240_write_object(data, QT602240_SPT_CTECONFIG, - QT602240_CTE_CTRL, 1); - qt602240_write_object(data, QT602240_SPT_CTECONFIG, - QT602240_CTE_MODE, mode); - qt602240_write_object(data, QT602240_SPT_CTECONFIG, - QT602240_CTE_CTRL, 0); - - return 0; -} - -static int qt602240_make_highchg(struct qt602240_data *data) -{ - struct device *dev = &data->client->dev; - int count = 10; - int error; - u8 val; - - /* Read dummy message to make high CHG pin */ - do { - error = qt602240_read_object(data, QT602240_GEN_MESSAGE, 0, &val); - if (error) - return error; - } while ((val != 0xff) && --count); - - if (!count) { - dev_err(dev, "CHG pin isn't cleared\n"); - return -EBUSY; - } - - return 0; -} - -static void qt602240_handle_pdata(struct qt602240_data *data) -{ - const struct qt602240_platform_data *pdata = data->pdata; - u8 voltage; - - /* Set touchscreen lines */ - qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_XSIZE, - pdata->x_line); - qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_YSIZE, - pdata->y_line); - - /* Set touchscreen orient */ - qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_ORIENT, - pdata->orient); - - /* Set touchscreen burst length */ - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_BLEN, pdata->blen); - - /* Set touchscreen threshold */ - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_TCHTHR, pdata->threshold); - - /* Set touchscreen resolution */ - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); - - /* Set touchscreen voltage */ - if (data->info.version >= QT602240_VER_21 && pdata->voltage) { - if (pdata->voltage < QT602240_VOLTAGE_DEFAULT) { - voltage = (QT602240_VOLTAGE_DEFAULT - pdata->voltage) / - QT602240_VOLTAGE_STEP; - voltage = 0xff - voltage + 1; - } else - voltage = (pdata->voltage - QT602240_VOLTAGE_DEFAULT) / - QT602240_VOLTAGE_STEP; - - qt602240_write_object(data, QT602240_SPT_CTECONFIG, - QT602240_CTE_VOLTAGE, voltage); - } -} - -static int qt602240_get_info(struct qt602240_data *data) -{ - struct i2c_client *client = data->client; - struct qt602240_info *info = &data->info; - int error; - u8 val; - - error = qt602240_read_reg(client, QT602240_FAMILY_ID, &val); - if (error) - return error; - info->family_id = val; - - error = qt602240_read_reg(client, QT602240_VARIANT_ID, &val); - if (error) - return error; - info->variant_id = val; - - error = qt602240_read_reg(client, QT602240_VERSION, &val); - if (error) - return error; - info->version = val; - - error = qt602240_read_reg(client, QT602240_BUILD, &val); - if (error) - return error; - info->build = val; - - error = qt602240_read_reg(client, QT602240_OBJECT_NUM, &val); - if (error) - return error; - info->object_num = val; - - return 0; -} - -static int qt602240_get_object_table(struct qt602240_data *data) -{ - int error; - int i; - u16 reg; - u8 reportid = 0; - u8 buf[QT602240_OBJECT_SIZE]; - - for (i = 0; i < data->info.object_num; i++) { - struct qt602240_object *object = data->object_table + i; - - reg = QT602240_OBJECT_START + QT602240_OBJECT_SIZE * i; - error = qt602240_read_object_table(data->client, reg, buf); - if (error) - return error; - - object->type = buf[0]; - object->start_address = (buf[2] << 8) | buf[1]; - object->size = buf[3]; - object->instances = buf[4]; - object->num_report_ids = buf[5]; - - if (object->num_report_ids) { - reportid += object->num_report_ids * - (object->instances + 1); - object->max_reportid = reportid; - } - } - - return 0; -} - -static int qt602240_initialize(struct qt602240_data *data) -{ - struct i2c_client *client = data->client; - struct qt602240_info *info = &data->info; - int error; - u8 val; - - error = qt602240_get_info(data); - if (error) - return error; - - data->object_table = kcalloc(info->object_num, - sizeof(struct qt602240_object), - GFP_KERNEL); - if (!data->object_table) { - dev_err(&client->dev, "Failed to allocate memory\n"); - return -ENOMEM; - } - - /* Get object table information */ - error = qt602240_get_object_table(data); - if (error) - return error; - - /* Check register init values */ - error = qt602240_check_reg_init(data); - if (error) - return error; - - /* Check X/Y matrix size */ - error = qt602240_check_matrix_size(data); - if (error) - return error; - - error = qt602240_make_highchg(data); - if (error) - return error; - - qt602240_handle_pdata(data); - - /* Backup to memory */ - qt602240_write_object(data, QT602240_GEN_COMMAND, - QT602240_COMMAND_BACKUPNV, - QT602240_BACKUP_VALUE); - msleep(QT602240_BACKUP_TIME); - - /* Soft reset */ - qt602240_write_object(data, QT602240_GEN_COMMAND, - QT602240_COMMAND_RESET, 1); - msleep(QT602240_RESET_TIME); - - /* Update matrix size at info struct */ - error = qt602240_read_reg(client, QT602240_MATRIX_X_SIZE, &val); - if (error) - return error; - info->matrix_xsize = val; - - error = qt602240_read_reg(client, QT602240_MATRIX_Y_SIZE, &val); - if (error) - return error; - info->matrix_ysize = val; - - dev_info(&client->dev, - "Family ID: %d Variant ID: %d Version: %d Build: %d\n", - info->family_id, info->variant_id, info->version, - info->build); - - dev_info(&client->dev, - "Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n", - info->matrix_xsize, info->matrix_ysize, - info->object_num); - - return 0; -} - -static ssize_t qt602240_object_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct qt602240_data *data = dev_get_drvdata(dev); - struct qt602240_object *object; - int count = 0; - int i, j; - int error; - u8 val; - - for (i = 0; i < data->info.object_num; i++) { - object = data->object_table + i; - - count += sprintf(buf + count, - "Object Table Element %d(Type %d)\n", - i + 1, object->type); - - if (!qt602240_object_readable(object->type)) { - count += sprintf(buf + count, "\n"); - continue; - } - - for (j = 0; j < object->size + 1; j++) { - error = qt602240_read_object(data, - object->type, j, &val); - if (error) - return error; - - count += sprintf(buf + count, - " Byte %d: 0x%x (%d)\n", j, val, val); - } - - count += sprintf(buf + count, "\n"); - } - - return count; -} - -static int qt602240_load_fw(struct device *dev, const char *fn) -{ - struct qt602240_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - const struct firmware *fw = NULL; - unsigned int frame_size; - unsigned int pos = 0; - int ret; - - ret = request_firmware(&fw, fn, dev); - if (ret) { - dev_err(dev, "Unable to open firmware %s\n", fn); - return ret; - } - - /* Change to the bootloader mode */ - qt602240_write_object(data, QT602240_GEN_COMMAND, - QT602240_COMMAND_RESET, QT602240_BOOT_VALUE); - msleep(QT602240_RESET_TIME); - - /* Change to slave address of bootloader */ - if (client->addr == QT602240_APP_LOW) - client->addr = QT602240_BOOT_LOW; - else - client->addr = QT602240_BOOT_HIGH; - - ret = qt602240_check_bootloader(client, QT602240_WAITING_BOOTLOAD_CMD); - if (ret) - goto out; - - /* Unlock bootloader */ - qt602240_unlock_bootloader(client); - - while (pos < fw->size) { - ret = qt602240_check_bootloader(client, - QT602240_WAITING_FRAME_DATA); - if (ret) - goto out; - - frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); - - /* We should add 2 at frame size as the the firmware data is not - * included the CRC bytes. - */ - frame_size += 2; - - /* Write one frame to device */ - qt602240_fw_write(client, fw->data + pos, frame_size); - - ret = qt602240_check_bootloader(client, - QT602240_FRAME_CRC_PASS); - if (ret) - goto out; - - pos += frame_size; - - dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); - } - -out: - release_firmware(fw); - - /* Change to slave address of application */ - if (client->addr == QT602240_BOOT_LOW) - client->addr = QT602240_APP_LOW; - else - client->addr = QT602240_APP_HIGH; - - return ret; -} - -static ssize_t qt602240_update_fw_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct qt602240_data *data = dev_get_drvdata(dev); - unsigned int version; - int error; - - if (sscanf(buf, "%u", &version) != 1) { - dev_err(dev, "Invalid values\n"); - return -EINVAL; - } - - if (data->info.version < QT602240_VER_21 || version < QT602240_VER_21) { - dev_err(dev, "FW update supported starting with version 21\n"); - return -EINVAL; - } - - disable_irq(data->irq); - - error = qt602240_load_fw(dev, QT602240_FW_NAME); - if (error) { - dev_err(dev, "The firmware update failed(%d)\n", error); - count = error; - } else { - dev_dbg(dev, "The firmware update succeeded\n"); - - /* Wait for reset */ - msleep(QT602240_FWRESET_TIME); - - kfree(data->object_table); - data->object_table = NULL; - - qt602240_initialize(data); - } - - enable_irq(data->irq); - - return count; -} - -static DEVICE_ATTR(object, 0444, qt602240_object_show, NULL); -static DEVICE_ATTR(update_fw, 0664, NULL, qt602240_update_fw_store); - -static struct attribute *qt602240_attrs[] = { - &dev_attr_object.attr, - &dev_attr_update_fw.attr, - NULL -}; - -static const struct attribute_group qt602240_attr_group = { - .attrs = qt602240_attrs, -}; - -static void qt602240_start(struct qt602240_data *data) -{ - /* Touch enable */ - qt602240_write_object(data, - QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0x83); -} - -static void qt602240_stop(struct qt602240_data *data) -{ - /* Touch disable */ - qt602240_write_object(data, - QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0); -} - -static int qt602240_input_open(struct input_dev *dev) -{ - struct qt602240_data *data = input_get_drvdata(dev); - - qt602240_start(data); - - return 0; -} - -static void qt602240_input_close(struct input_dev *dev) -{ - struct qt602240_data *data = input_get_drvdata(dev); - - qt602240_stop(data); -} - -static int __devinit qt602240_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct qt602240_data *data; - struct input_dev *input_dev; - int error; - - if (!client->dev.platform_data) - return -EINVAL; - - data = kzalloc(sizeof(struct qt602240_data), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!data || !input_dev) { - dev_err(&client->dev, "Failed to allocate memory\n"); - error = -ENOMEM; - goto err_free_mem; - } - - input_dev->name = "AT42QT602240/ATMXT224 Touchscreen"; - input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = &client->dev; - input_dev->open = qt602240_input_open; - input_dev->close = qt602240_input_close; - - __set_bit(EV_ABS, input_dev->evbit); - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(BTN_TOUCH, input_dev->keybit); - - /* For single touch */ - input_set_abs_params(input_dev, ABS_X, - 0, QT602240_MAX_XC, 0, 0); - input_set_abs_params(input_dev, ABS_Y, - 0, QT602240_MAX_YC, 0, 0); - - /* For multi touch */ - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, - 0, QT602240_MAX_AREA, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_X, - 0, QT602240_MAX_XC, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, - 0, QT602240_MAX_YC, 0, 0); - - input_set_drvdata(input_dev, data); - - data->client = client; - data->input_dev = input_dev; - data->pdata = client->dev.platform_data; - data->irq = client->irq; - - i2c_set_clientdata(client, data); - - error = qt602240_initialize(data); - if (error) - goto err_free_object; - - error = request_threaded_irq(client->irq, NULL, qt602240_interrupt, - IRQF_TRIGGER_FALLING, client->dev.driver->name, data); - if (error) { - dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_free_object; - } - - error = input_register_device(input_dev); - if (error) - goto err_free_irq; - - error = sysfs_create_group(&client->dev.kobj, &qt602240_attr_group); - if (error) - goto err_unregister_device; - - return 0; - -err_unregister_device: - input_unregister_device(input_dev); - input_dev = NULL; -err_free_irq: - free_irq(client->irq, data); -err_free_object: - kfree(data->object_table); -err_free_mem: - input_free_device(input_dev); - kfree(data); - return error; -} - -static int __devexit qt602240_remove(struct i2c_client *client) -{ - struct qt602240_data *data = i2c_get_clientdata(client); - - sysfs_remove_group(&client->dev.kobj, &qt602240_attr_group); - free_irq(data->irq, data); - input_unregister_device(data->input_dev); - kfree(data->object_table); - kfree(data); - - return 0; -} - -#ifdef CONFIG_PM -static int qt602240_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct qt602240_data *data = i2c_get_clientdata(client); - struct input_dev *input_dev = data->input_dev; - - mutex_lock(&input_dev->mutex); - - if (input_dev->users) - qt602240_stop(data); - - mutex_unlock(&input_dev->mutex); - - return 0; -} - -static int qt602240_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct qt602240_data *data = i2c_get_clientdata(client); - struct input_dev *input_dev = data->input_dev; - - /* Soft reset */ - qt602240_write_object(data, QT602240_GEN_COMMAND, - QT602240_COMMAND_RESET, 1); - - msleep(QT602240_RESET_TIME); - - mutex_lock(&input_dev->mutex); - - if (input_dev->users) - qt602240_start(data); - - mutex_unlock(&input_dev->mutex); - - return 0; -} - -static const struct dev_pm_ops qt602240_pm_ops = { - .suspend = qt602240_suspend, - .resume = qt602240_resume, -}; -#endif - -static const struct i2c_device_id qt602240_id[] = { - { "qt602240_ts", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, qt602240_id); - -static struct i2c_driver qt602240_driver = { - .driver = { - .name = "qt602240_ts", - .owner = THIS_MODULE, -#ifdef CONFIG_PM - .pm = &qt602240_pm_ops, -#endif - }, - .probe = qt602240_probe, - .remove = __devexit_p(qt602240_remove), - .id_table = qt602240_id, -}; - -static int __init qt602240_init(void) -{ - return i2c_add_driver(&qt602240_driver); -} - -static void __exit qt602240_exit(void) -{ - i2c_del_driver(&qt602240_driver); -} - -module_init(qt602240_init); -module_exit(qt602240_exit); - -/* Module information */ -MODULE_AUTHOR("Joonyoung Shim "); -MODULE_DESCRIPTION("AT42QT602240/ATMXT224 Touchscreen driver"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h new file mode 100644 index 000000000000..c5033e101094 --- /dev/null +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -0,0 +1,38 @@ +/* + * AT42QT602240/ATMXT224 Touchscreen driver + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __LINUX_QT602240_TS_H +#define __LINUX_QT602240_TS_H + +/* Orient */ +#define QT602240_NORMAL 0x0 +#define QT602240_DIAGONAL 0x1 +#define QT602240_HORIZONTAL_FLIP 0x2 +#define QT602240_ROTATED_90_COUNTER 0x3 +#define QT602240_VERTICAL_FLIP 0x4 +#define QT602240_ROTATED_90 0x5 +#define QT602240_ROTATED_180 0x6 +#define QT602240_DIAGONAL_COUNTER 0x7 + +/* The platform data for the AT42QT602240/ATMXT224 touchscreen driver */ +struct qt602240_platform_data { + unsigned int x_line; + unsigned int y_line; + unsigned int x_size; + unsigned int y_size; + unsigned int blen; + unsigned int threshold; + unsigned int voltage; + unsigned char orient; +}; + +#endif /* __LINUX_QT602240_TS_H */ diff --git a/include/linux/i2c/qt602240_ts.h b/include/linux/i2c/qt602240_ts.h deleted file mode 100644 index c5033e101094..000000000000 --- a/include/linux/i2c/qt602240_ts.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * AT42QT602240/ATMXT224 Touchscreen driver - * - * Copyright (C) 2010 Samsung Electronics Co.Ltd - * Author: Joonyoung Shim - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifndef __LINUX_QT602240_TS_H -#define __LINUX_QT602240_TS_H - -/* Orient */ -#define QT602240_NORMAL 0x0 -#define QT602240_DIAGONAL 0x1 -#define QT602240_HORIZONTAL_FLIP 0x2 -#define QT602240_ROTATED_90_COUNTER 0x3 -#define QT602240_VERTICAL_FLIP 0x4 -#define QT602240_ROTATED_90 0x5 -#define QT602240_ROTATED_180 0x6 -#define QT602240_DIAGONAL_COUNTER 0x7 - -/* The platform data for the AT42QT602240/ATMXT224 touchscreen driver */ -struct qt602240_platform_data { - unsigned int x_line; - unsigned int y_line; - unsigned int x_size; - unsigned int y_size; - unsigned int blen; - unsigned int threshold; - unsigned int voltage; - unsigned char orient; -}; - -#endif /* __LINUX_QT602240_TS_H */ -- cgit v1.2.3 From 7686b108d8ef5c32f429d5228798636f3a1caf5a Mon Sep 17 00:00:00 2001 From: Iiro Valkonen Date: Wed, 2 Feb 2011 23:21:58 -0800 Subject: Input: atmel_mxt_ts - get rid of qt602240 prefixes in names Change prefixes from qt602240 to mxt to reflect that the driver supports whole line of mXT touchscreens. Signed-off-by: Iiro Valkonen Signed-off-by: Dmitry Torokhov --- arch/arm/mach-s5pv210/mach-goni.c | 4 +- drivers/input/touchscreen/atmel_mxt_ts.c | 929 ++++++++++++++++--------------- include/linux/i2c/atmel_mxt_ts.h | 28 +- 3 files changed, 481 insertions(+), 480 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index fd2747fd8905..e9a7d340f4e1 100644 --- a/arch/arm/mach-s5pv210/mach-goni.c +++ b/arch/arm/mach-s5pv210/mach-goni.c @@ -222,7 +222,7 @@ static void __init goni_radio_init(void) } /* TSP */ -static struct qt602240_platform_data qt602240_platform_data = { +static struct mxt_platform_data qt602240_platform_data = { .x_line = 17, .y_line = 11, .x_size = 800, @@ -230,7 +230,7 @@ static struct qt602240_platform_data qt602240_platform_data = { .blen = 0x21, .threshold = 0x28, .voltage = 2800000, /* 2.8V */ - .orient = QT602240_DIAGONAL, + .orient = MXT_DIAGONAL, }; static struct s3c2410_platform_i2c i2c2_data __initdata = { diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 47067789b747..5dca78a00c94 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1,5 +1,5 @@ /* - * AT42QT602240/ATMXT224 Touchscreen driver + * Atmel maXTouch Touchscreen driver * * Copyright (C) 2010 Samsung Electronics Co.Ltd * Author: Joonyoung Shim @@ -22,300 +22,300 @@ #include /* Version */ -#define QT602240_VER_20 20 -#define QT602240_VER_21 21 -#define QT602240_VER_22 22 +#define MXT_VER_20 20 +#define MXT_VER_21 21 +#define MXT_VER_22 22 /* Slave addresses */ -#define QT602240_APP_LOW 0x4a -#define QT602240_APP_HIGH 0x4b -#define QT602240_BOOT_LOW 0x24 -#define QT602240_BOOT_HIGH 0x25 +#define MXT_APP_LOW 0x4a +#define MXT_APP_HIGH 0x4b +#define MXT_BOOT_LOW 0x24 +#define MXT_BOOT_HIGH 0x25 /* Firmware */ -#define QT602240_FW_NAME "qt602240.fw" +#define MXT_FW_NAME "maxtouch.fw" /* Registers */ -#define QT602240_FAMILY_ID 0x00 -#define QT602240_VARIANT_ID 0x01 -#define QT602240_VERSION 0x02 -#define QT602240_BUILD 0x03 -#define QT602240_MATRIX_X_SIZE 0x04 -#define QT602240_MATRIX_Y_SIZE 0x05 -#define QT602240_OBJECT_NUM 0x06 -#define QT602240_OBJECT_START 0x07 +#define MXT_FAMILY_ID 0x00 +#define MXT_VARIANT_ID 0x01 +#define MXT_VERSION 0x02 +#define MXT_BUILD 0x03 +#define MXT_MATRIX_X_SIZE 0x04 +#define MXT_MATRIX_Y_SIZE 0x05 +#define MXT_OBJECT_NUM 0x06 +#define MXT_OBJECT_START 0x07 -#define QT602240_OBJECT_SIZE 6 +#define MXT_OBJECT_SIZE 6 /* Object types */ -#define QT602240_DEBUG_DIAGNOSTIC 37 -#define QT602240_GEN_MESSAGE 5 -#define QT602240_GEN_COMMAND 6 -#define QT602240_GEN_POWER 7 -#define QT602240_GEN_ACQUIRE 8 -#define QT602240_TOUCH_MULTI 9 -#define QT602240_TOUCH_KEYARRAY 15 -#define QT602240_TOUCH_PROXIMITY 23 -#define QT602240_PROCI_GRIPFACE 20 -#define QT602240_PROCG_NOISE 22 -#define QT602240_PROCI_ONETOUCH 24 -#define QT602240_PROCI_TWOTOUCH 27 -#define QT602240_SPT_COMMSCONFIG 18 /* firmware ver 21 over */ -#define QT602240_SPT_GPIOPWM 19 -#define QT602240_SPT_SELFTEST 25 -#define QT602240_SPT_CTECONFIG 28 -#define QT602240_SPT_USERDATA 38 /* firmware ver 21 over */ - -/* QT602240_GEN_COMMAND field */ -#define QT602240_COMMAND_RESET 0 -#define QT602240_COMMAND_BACKUPNV 1 -#define QT602240_COMMAND_CALIBRATE 2 -#define QT602240_COMMAND_REPORTALL 3 -#define QT602240_COMMAND_DIAGNOSTIC 5 - -/* QT602240_GEN_POWER field */ -#define QT602240_POWER_IDLEACQINT 0 -#define QT602240_POWER_ACTVACQINT 1 -#define QT602240_POWER_ACTV2IDLETO 2 - -/* QT602240_GEN_ACQUIRE field */ -#define QT602240_ACQUIRE_CHRGTIME 0 -#define QT602240_ACQUIRE_TCHDRIFT 2 -#define QT602240_ACQUIRE_DRIFTST 3 -#define QT602240_ACQUIRE_TCHAUTOCAL 4 -#define QT602240_ACQUIRE_SYNC 5 -#define QT602240_ACQUIRE_ATCHCALST 6 -#define QT602240_ACQUIRE_ATCHCALSTHR 7 - -/* QT602240_TOUCH_MULTI field */ -#define QT602240_TOUCH_CTRL 0 -#define QT602240_TOUCH_XORIGIN 1 -#define QT602240_TOUCH_YORIGIN 2 -#define QT602240_TOUCH_XSIZE 3 -#define QT602240_TOUCH_YSIZE 4 -#define QT602240_TOUCH_BLEN 6 -#define QT602240_TOUCH_TCHTHR 7 -#define QT602240_TOUCH_TCHDI 8 -#define QT602240_TOUCH_ORIENT 9 -#define QT602240_TOUCH_MOVHYSTI 11 -#define QT602240_TOUCH_MOVHYSTN 12 -#define QT602240_TOUCH_NUMTOUCH 14 -#define QT602240_TOUCH_MRGHYST 15 -#define QT602240_TOUCH_MRGTHR 16 -#define QT602240_TOUCH_AMPHYST 17 -#define QT602240_TOUCH_XRANGE_LSB 18 -#define QT602240_TOUCH_XRANGE_MSB 19 -#define QT602240_TOUCH_YRANGE_LSB 20 -#define QT602240_TOUCH_YRANGE_MSB 21 -#define QT602240_TOUCH_XLOCLIP 22 -#define QT602240_TOUCH_XHICLIP 23 -#define QT602240_TOUCH_YLOCLIP 24 -#define QT602240_TOUCH_YHICLIP 25 -#define QT602240_TOUCH_XEDGECTRL 26 -#define QT602240_TOUCH_XEDGEDIST 27 -#define QT602240_TOUCH_YEDGECTRL 28 -#define QT602240_TOUCH_YEDGEDIST 29 -#define QT602240_TOUCH_JUMPLIMIT 30 /* firmware ver 22 over */ - -/* QT602240_PROCI_GRIPFACE field */ -#define QT602240_GRIPFACE_CTRL 0 -#define QT602240_GRIPFACE_XLOGRIP 1 -#define QT602240_GRIPFACE_XHIGRIP 2 -#define QT602240_GRIPFACE_YLOGRIP 3 -#define QT602240_GRIPFACE_YHIGRIP 4 -#define QT602240_GRIPFACE_MAXTCHS 5 -#define QT602240_GRIPFACE_SZTHR1 7 -#define QT602240_GRIPFACE_SZTHR2 8 -#define QT602240_GRIPFACE_SHPTHR1 9 -#define QT602240_GRIPFACE_SHPTHR2 10 -#define QT602240_GRIPFACE_SUPEXTTO 11 - -/* QT602240_PROCI_NOISE field */ -#define QT602240_NOISE_CTRL 0 -#define QT602240_NOISE_OUTFLEN 1 -#define QT602240_NOISE_GCAFUL_LSB 3 -#define QT602240_NOISE_GCAFUL_MSB 4 -#define QT602240_NOISE_GCAFLL_LSB 5 -#define QT602240_NOISE_GCAFLL_MSB 6 -#define QT602240_NOISE_ACTVGCAFVALID 7 -#define QT602240_NOISE_NOISETHR 8 -#define QT602240_NOISE_FREQHOPSCALE 10 -#define QT602240_NOISE_FREQ0 11 -#define QT602240_NOISE_FREQ1 12 -#define QT602240_NOISE_FREQ2 13 -#define QT602240_NOISE_FREQ3 14 -#define QT602240_NOISE_FREQ4 15 -#define QT602240_NOISE_IDLEGCAFVALID 16 - -/* QT602240_SPT_COMMSCONFIG */ -#define QT602240_COMMS_CTRL 0 -#define QT602240_COMMS_CMD 1 - -/* QT602240_SPT_CTECONFIG field */ -#define QT602240_CTE_CTRL 0 -#define QT602240_CTE_CMD 1 -#define QT602240_CTE_MODE 2 -#define QT602240_CTE_IDLEGCAFDEPTH 3 -#define QT602240_CTE_ACTVGCAFDEPTH 4 -#define QT602240_CTE_VOLTAGE 5 /* firmware ver 21 over */ - -#define QT602240_VOLTAGE_DEFAULT 2700000 -#define QT602240_VOLTAGE_STEP 10000 - -/* Define for QT602240_GEN_COMMAND */ -#define QT602240_BOOT_VALUE 0xa5 -#define QT602240_BACKUP_VALUE 0x55 -#define QT602240_BACKUP_TIME 25 /* msec */ -#define QT602240_RESET_TIME 65 /* msec */ - -#define QT602240_FWRESET_TIME 175 /* msec */ +#define MXT_DEBUG_DIAGNOSTIC 37 +#define MXT_GEN_MESSAGE 5 +#define MXT_GEN_COMMAND 6 +#define MXT_GEN_POWER 7 +#define MXT_GEN_ACQUIRE 8 +#define MXT_TOUCH_MULTI 9 +#define MXT_TOUCH_KEYARRAY 15 +#define MXT_TOUCH_PROXIMITY 23 +#define MXT_PROCI_GRIPFACE 20 +#define MXT_PROCG_NOISE 22 +#define MXT_PROCI_ONETOUCH 24 +#define MXT_PROCI_TWOTOUCH 27 +#define MXT_SPT_COMMSCONFIG 18 /* firmware ver 21 over */ +#define MXT_SPT_GPIOPWM 19 +#define MXT_SPT_SELFTEST 25 +#define MXT_SPT_CTECONFIG 28 +#define MXT_SPT_USERDATA 38 /* firmware ver 21 over */ + +/* MXT_GEN_COMMAND field */ +#define MXT_COMMAND_RESET 0 +#define MXT_COMMAND_BACKUPNV 1 +#define MXT_COMMAND_CALIBRATE 2 +#define MXT_COMMAND_REPORTALL 3 +#define MXT_COMMAND_DIAGNOSTIC 5 + +/* MXT_GEN_POWER field */ +#define MXT_POWER_IDLEACQINT 0 +#define MXT_POWER_ACTVACQINT 1 +#define MXT_POWER_ACTV2IDLETO 2 + +/* MXT_GEN_ACQUIRE field */ +#define MXT_ACQUIRE_CHRGTIME 0 +#define MXT_ACQUIRE_TCHDRIFT 2 +#define MXT_ACQUIRE_DRIFTST 3 +#define MXT_ACQUIRE_TCHAUTOCAL 4 +#define MXT_ACQUIRE_SYNC 5 +#define MXT_ACQUIRE_ATCHCALST 6 +#define MXT_ACQUIRE_ATCHCALSTHR 7 + +/* MXT_TOUCH_MULTI field */ +#define MXT_TOUCH_CTRL 0 +#define MXT_TOUCH_XORIGIN 1 +#define MXT_TOUCH_YORIGIN 2 +#define MXT_TOUCH_XSIZE 3 +#define MXT_TOUCH_YSIZE 4 +#define MXT_TOUCH_BLEN 6 +#define MXT_TOUCH_TCHTHR 7 +#define MXT_TOUCH_TCHDI 8 +#define MXT_TOUCH_ORIENT 9 +#define MXT_TOUCH_MOVHYSTI 11 +#define MXT_TOUCH_MOVHYSTN 12 +#define MXT_TOUCH_NUMTOUCH 14 +#define MXT_TOUCH_MRGHYST 15 +#define MXT_TOUCH_MRGTHR 16 +#define MXT_TOUCH_AMPHYST 17 +#define MXT_TOUCH_XRANGE_LSB 18 +#define MXT_TOUCH_XRANGE_MSB 19 +#define MXT_TOUCH_YRANGE_LSB 20 +#define MXT_TOUCH_YRANGE_MSB 21 +#define MXT_TOUCH_XLOCLIP 22 +#define MXT_TOUCH_XHICLIP 23 +#define MXT_TOUCH_YLOCLIP 24 +#define MXT_TOUCH_YHICLIP 25 +#define MXT_TOUCH_XEDGECTRL 26 +#define MXT_TOUCH_XEDGEDIST 27 +#define MXT_TOUCH_YEDGECTRL 28 +#define MXT_TOUCH_YEDGEDIST 29 +#define MXT_TOUCH_JUMPLIMIT 30 /* firmware ver 22 over */ + +/* MXT_PROCI_GRIPFACE field */ +#define MXT_GRIPFACE_CTRL 0 +#define MXT_GRIPFACE_XLOGRIP 1 +#define MXT_GRIPFACE_XHIGRIP 2 +#define MXT_GRIPFACE_YLOGRIP 3 +#define MXT_GRIPFACE_YHIGRIP 4 +#define MXT_GRIPFACE_MAXTCHS 5 +#define MXT_GRIPFACE_SZTHR1 7 +#define MXT_GRIPFACE_SZTHR2 8 +#define MXT_GRIPFACE_SHPTHR1 9 +#define MXT_GRIPFACE_SHPTHR2 10 +#define MXT_GRIPFACE_SUPEXTTO 11 + +/* MXT_PROCI_NOISE field */ +#define MXT_NOISE_CTRL 0 +#define MXT_NOISE_OUTFLEN 1 +#define MXT_NOISE_GCAFUL_LSB 3 +#define MXT_NOISE_GCAFUL_MSB 4 +#define MXT_NOISE_GCAFLL_LSB 5 +#define MXT_NOISE_GCAFLL_MSB 6 +#define MXT_NOISE_ACTVGCAFVALID 7 +#define MXT_NOISE_NOISETHR 8 +#define MXT_NOISE_FREQHOPSCALE 10 +#define MXT_NOISE_FREQ0 11 +#define MXT_NOISE_FREQ1 12 +#define MXT_NOISE_FREQ2 13 +#define MXT_NOISE_FREQ3 14 +#define MXT_NOISE_FREQ4 15 +#define MXT_NOISE_IDLEGCAFVALID 16 + +/* MXT_SPT_COMMSCONFIG */ +#define MXT_COMMS_CTRL 0 +#define MXT_COMMS_CMD 1 + +/* MXT_SPT_CTECONFIG field */ +#define MXT_CTE_CTRL 0 +#define MXT_CTE_CMD 1 +#define MXT_CTE_MODE 2 +#define MXT_CTE_IDLEGCAFDEPTH 3 +#define MXT_CTE_ACTVGCAFDEPTH 4 +#define MXT_CTE_VOLTAGE 5 /* firmware ver 21 over */ + +#define MXT_VOLTAGE_DEFAULT 2700000 +#define MXT_VOLTAGE_STEP 10000 + +/* Define for MXT_GEN_COMMAND */ +#define MXT_BOOT_VALUE 0xa5 +#define MXT_BACKUP_VALUE 0x55 +#define MXT_BACKUP_TIME 25 /* msec */ +#define MXT_RESET_TIME 65 /* msec */ + +#define MXT_FWRESET_TIME 175 /* msec */ /* Command to unlock bootloader */ -#define QT602240_UNLOCK_CMD_MSB 0xaa -#define QT602240_UNLOCK_CMD_LSB 0xdc +#define MXT_UNLOCK_CMD_MSB 0xaa +#define MXT_UNLOCK_CMD_LSB 0xdc /* Bootloader mode status */ -#define QT602240_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */ -#define QT602240_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */ -#define QT602240_FRAME_CRC_CHECK 0x02 -#define QT602240_FRAME_CRC_FAIL 0x03 -#define QT602240_FRAME_CRC_PASS 0x04 -#define QT602240_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ -#define QT602240_BOOT_STATUS_MASK 0x3f +#define MXT_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */ +#define MXT_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */ +#define MXT_FRAME_CRC_CHECK 0x02 +#define MXT_FRAME_CRC_FAIL 0x03 +#define MXT_FRAME_CRC_PASS 0x04 +#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ +#define MXT_BOOT_STATUS_MASK 0x3f /* Touch status */ -#define QT602240_SUPPRESS (1 << 1) -#define QT602240_AMP (1 << 2) -#define QT602240_VECTOR (1 << 3) -#define QT602240_MOVE (1 << 4) -#define QT602240_RELEASE (1 << 5) -#define QT602240_PRESS (1 << 6) -#define QT602240_DETECT (1 << 7) +#define MXT_SUPPRESS (1 << 1) +#define MXT_AMP (1 << 2) +#define MXT_VECTOR (1 << 3) +#define MXT_MOVE (1 << 4) +#define MXT_RELEASE (1 << 5) +#define MXT_PRESS (1 << 6) +#define MXT_DETECT (1 << 7) /* Touchscreen absolute values */ -#define QT602240_MAX_XC 0x3ff -#define QT602240_MAX_YC 0x3ff -#define QT602240_MAX_AREA 0xff +#define MXT_MAX_XC 0x3ff +#define MXT_MAX_YC 0x3ff +#define MXT_MAX_AREA 0xff -#define QT602240_MAX_FINGER 10 +#define MXT_MAX_FINGER 10 /* Initial register values recommended from chip vendor */ static const u8 init_vals_ver_20[] = { - /* QT602240_GEN_COMMAND(6) */ + /* MXT_GEN_COMMAND(6) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_GEN_POWER(7) */ + /* MXT_GEN_POWER(7) */ 0x20, 0xff, 0x32, - /* QT602240_GEN_ACQUIRE(8) */ + /* MXT_GEN_ACQUIRE(8) */ 0x08, 0x05, 0x05, 0x00, 0x00, 0x00, 0x05, 0x14, - /* QT602240_TOUCH_MULTI(9) */ + /* MXT_TOUCH_MULTI(9) */ 0x00, 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x64, - /* QT602240_TOUCH_KEYARRAY(15) */ + /* MXT_TOUCH_KEYARRAY(15) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_GPIOPWM(19) */ + /* MXT_SPT_GPIOPWM(19) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_GRIPFACE(20) */ + /* MXT_PROCI_GRIPFACE(20) */ 0x00, 0x64, 0x64, 0x64, 0x64, 0x00, 0x00, 0x1e, 0x14, 0x04, 0x1e, 0x00, - /* QT602240_PROCG_NOISE(22) */ + /* MXT_PROCG_NOISE(22) */ 0x05, 0x00, 0x00, 0x19, 0x00, 0xe7, 0xff, 0x04, 0x32, 0x00, 0x01, 0x0a, 0x0f, 0x14, 0x00, 0x00, 0xe8, - /* QT602240_TOUCH_PROXIMITY(23) */ + /* MXT_TOUCH_PROXIMITY(23) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_ONETOUCH(24) */ + /* MXT_PROCI_ONETOUCH(24) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_SELFTEST(25) */ + /* MXT_SPT_SELFTEST(25) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_TWOTOUCH(27) */ + /* MXT_PROCI_TWOTOUCH(27) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_CTECONFIG(28) */ + /* MXT_SPT_CTECONFIG(28) */ 0x00, 0x00, 0x00, 0x04, 0x08, }; static const u8 init_vals_ver_21[] = { - /* QT602240_GEN_COMMAND(6) */ + /* MXT_GEN_COMMAND(6) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_GEN_POWER(7) */ + /* MXT_GEN_POWER(7) */ 0x20, 0xff, 0x32, - /* QT602240_GEN_ACQUIRE(8) */ + /* MXT_GEN_ACQUIRE(8) */ 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, - /* QT602240_TOUCH_MULTI(9) */ + /* MXT_TOUCH_MULTI(9) */ 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_TOUCH_KEYARRAY(15) */ + /* MXT_TOUCH_KEYARRAY(15) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_GPIOPWM(19) */ + /* MXT_SPT_GPIOPWM(19) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_GRIPFACE(20) */ + /* MXT_PROCI_GRIPFACE(20) */ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, 0x0f, 0x0a, - /* QT602240_PROCG_NOISE(22) */ + /* MXT_PROCG_NOISE(22) */ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, - /* QT602240_TOUCH_PROXIMITY(23) */ + /* MXT_TOUCH_PROXIMITY(23) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_ONETOUCH(24) */ + /* MXT_PROCI_ONETOUCH(24) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_SELFTEST(25) */ + /* MXT_SPT_SELFTEST(25) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_TWOTOUCH(27) */ + /* MXT_PROCI_TWOTOUCH(27) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_CTECONFIG(28) */ + /* MXT_SPT_CTECONFIG(28) */ 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, }; static const u8 init_vals_ver_22[] = { - /* QT602240_GEN_COMMAND(6) */ + /* MXT_GEN_COMMAND(6) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_GEN_POWER(7) */ + /* MXT_GEN_POWER(7) */ 0x20, 0xff, 0x32, - /* QT602240_GEN_ACQUIRE(8) */ + /* MXT_GEN_ACQUIRE(8) */ 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, - /* QT602240_TOUCH_MULTI(9) */ + /* MXT_TOUCH_MULTI(9) */ 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_TOUCH_KEYARRAY(15) */ + /* MXT_TOUCH_KEYARRAY(15) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_GPIOPWM(19) */ + /* MXT_SPT_GPIOPWM(19) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_GRIPFACE(20) */ + /* MXT_PROCI_GRIPFACE(20) */ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, 0x0f, 0x0a, - /* QT602240_PROCG_NOISE(22) */ + /* MXT_PROCG_NOISE(22) */ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, - /* QT602240_TOUCH_PROXIMITY(23) */ + /* MXT_TOUCH_PROXIMITY(23) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_ONETOUCH(24) */ + /* MXT_PROCI_ONETOUCH(24) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_SELFTEST(25) */ + /* MXT_SPT_SELFTEST(25) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_TWOTOUCH(27) */ + /* MXT_PROCI_TWOTOUCH(27) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_CTECONFIG(28) */ + /* MXT_SPT_CTECONFIG(28) */ 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, }; -struct qt602240_info { +struct mxt_info { u8 family_id; u8 variant_id; u8 version; @@ -325,7 +325,7 @@ struct qt602240_info { u8 object_num; }; -struct qt602240_object { +struct mxt_object { u8 type; u16 start_address; u8 size; @@ -336,13 +336,13 @@ struct qt602240_object { u8 max_reportid; }; -struct qt602240_message { +struct mxt_message { u8 reportid; u8 message[7]; u8 checksum; }; -struct qt602240_finger { +struct mxt_finger { int status; int x; int y; @@ -350,65 +350,65 @@ struct qt602240_finger { }; /* Each client has this additional data */ -struct qt602240_data { +struct mxt_data { struct i2c_client *client; struct input_dev *input_dev; - const struct qt602240_platform_data *pdata; - struct qt602240_object *object_table; - struct qt602240_info info; - struct qt602240_finger finger[QT602240_MAX_FINGER]; + const struct mxt_platform_data *pdata; + struct mxt_object *object_table; + struct mxt_info info; + struct mxt_finger finger[MXT_MAX_FINGER]; unsigned int irq; }; -static bool qt602240_object_readable(unsigned int type) +static bool mxt_object_readable(unsigned int type) { switch (type) { - case QT602240_GEN_MESSAGE: - case QT602240_GEN_COMMAND: - case QT602240_GEN_POWER: - case QT602240_GEN_ACQUIRE: - case QT602240_TOUCH_MULTI: - case QT602240_TOUCH_KEYARRAY: - case QT602240_TOUCH_PROXIMITY: - case QT602240_PROCI_GRIPFACE: - case QT602240_PROCG_NOISE: - case QT602240_PROCI_ONETOUCH: - case QT602240_PROCI_TWOTOUCH: - case QT602240_SPT_COMMSCONFIG: - case QT602240_SPT_GPIOPWM: - case QT602240_SPT_SELFTEST: - case QT602240_SPT_CTECONFIG: - case QT602240_SPT_USERDATA: + case MXT_GEN_MESSAGE: + case MXT_GEN_COMMAND: + case MXT_GEN_POWER: + case MXT_GEN_ACQUIRE: + case MXT_TOUCH_MULTI: + case MXT_TOUCH_KEYARRAY: + case MXT_TOUCH_PROXIMITY: + case MXT_PROCI_GRIPFACE: + case MXT_PROCG_NOISE: + case MXT_PROCI_ONETOUCH: + case MXT_PROCI_TWOTOUCH: + case MXT_SPT_COMMSCONFIG: + case MXT_SPT_GPIOPWM: + case MXT_SPT_SELFTEST: + case MXT_SPT_CTECONFIG: + case MXT_SPT_USERDATA: return true; default: return false; } } -static bool qt602240_object_writable(unsigned int type) +static bool mxt_object_writable(unsigned int type) { switch (type) { - case QT602240_GEN_COMMAND: - case QT602240_GEN_POWER: - case QT602240_GEN_ACQUIRE: - case QT602240_TOUCH_MULTI: - case QT602240_TOUCH_KEYARRAY: - case QT602240_TOUCH_PROXIMITY: - case QT602240_PROCI_GRIPFACE: - case QT602240_PROCG_NOISE: - case QT602240_PROCI_ONETOUCH: - case QT602240_PROCI_TWOTOUCH: - case QT602240_SPT_GPIOPWM: - case QT602240_SPT_SELFTEST: - case QT602240_SPT_CTECONFIG: + case MXT_GEN_COMMAND: + case MXT_GEN_POWER: + case MXT_GEN_ACQUIRE: + case MXT_TOUCH_MULTI: + case MXT_TOUCH_KEYARRAY: + case MXT_TOUCH_PROXIMITY: + case MXT_PROCI_GRIPFACE: + case MXT_PROCG_NOISE: + case MXT_PROCI_ONETOUCH: + case MXT_PROCI_TWOTOUCH: + case MXT_SPT_GPIOPWM: + case MXT_SPT_SELFTEST: + case MXT_SPT_CTECONFIG: return true; default: return false; } } -static void qt602240_dump_message(struct device *dev, - struct qt602240_message *message) +static void mxt_dump_message(struct device *dev, + struct mxt_message *message) { dev_dbg(dev, "reportid:\t0x%x\n", message->reportid); dev_dbg(dev, "message1:\t0x%x\n", message->message[0]); @@ -421,7 +421,7 @@ static void qt602240_dump_message(struct device *dev, dev_dbg(dev, "checksum:\t0x%x\n", message->checksum); } -static int qt602240_check_bootloader(struct i2c_client *client, +static int mxt_check_bootloader(struct i2c_client *client, unsigned int state) { u8 val; @@ -433,12 +433,12 @@ recheck: } switch (state) { - case QT602240_WAITING_BOOTLOAD_CMD: - case QT602240_WAITING_FRAME_DATA: - val &= ~QT602240_BOOT_STATUS_MASK; + case MXT_WAITING_BOOTLOAD_CMD: + case MXT_WAITING_FRAME_DATA: + val &= ~MXT_BOOT_STATUS_MASK; break; - case QT602240_FRAME_CRC_PASS: - if (val == QT602240_FRAME_CRC_CHECK) + case MXT_FRAME_CRC_PASS: + if (val == MXT_FRAME_CRC_CHECK) goto recheck; break; default: @@ -453,12 +453,12 @@ recheck: return 0; } -static int qt602240_unlock_bootloader(struct i2c_client *client) +static int mxt_unlock_bootloader(struct i2c_client *client) { u8 buf[2]; - buf[0] = QT602240_UNLOCK_CMD_LSB; - buf[1] = QT602240_UNLOCK_CMD_MSB; + buf[0] = MXT_UNLOCK_CMD_LSB; + buf[1] = MXT_UNLOCK_CMD_MSB; if (i2c_master_send(client, buf, 2) != 2) { dev_err(&client->dev, "%s: i2c send failed\n", __func__); @@ -468,7 +468,7 @@ static int qt602240_unlock_bootloader(struct i2c_client *client) return 0; } -static int qt602240_fw_write(struct i2c_client *client, +static int mxt_fw_write(struct i2c_client *client, const u8 *data, unsigned int frame_size) { if (i2c_master_send(client, data, frame_size) != frame_size) { @@ -479,7 +479,7 @@ static int qt602240_fw_write(struct i2c_client *client, return 0; } -static int __qt602240_read_reg(struct i2c_client *client, +static int __mxt_read_reg(struct i2c_client *client, u16 reg, u16 len, void *val) { struct i2c_msg xfer[2]; @@ -508,12 +508,12 @@ static int __qt602240_read_reg(struct i2c_client *client, return 0; } -static int qt602240_read_reg(struct i2c_client *client, u16 reg, u8 *val) +static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val) { - return __qt602240_read_reg(client, reg, 1, val); + return __mxt_read_reg(client, reg, 1, val); } -static int qt602240_write_reg(struct i2c_client *client, u16 reg, u8 val) +static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val) { u8 buf[3]; @@ -529,17 +529,17 @@ static int qt602240_write_reg(struct i2c_client *client, u16 reg, u8 val) return 0; } -static int qt602240_read_object_table(struct i2c_client *client, +static int mxt_read_object_table(struct i2c_client *client, u16 reg, u8 *object_buf) { - return __qt602240_read_reg(client, reg, QT602240_OBJECT_SIZE, + return __mxt_read_reg(client, reg, MXT_OBJECT_SIZE, object_buf); } -static struct qt602240_object * -qt602240_get_object(struct qt602240_data *data, u8 type) +static struct mxt_object * +mxt_get_object(struct mxt_data *data, u8 type) { - struct qt602240_object *object; + struct mxt_object *object; int i; for (i = 0; i < data->info.object_num; i++) { @@ -552,63 +552,63 @@ qt602240_get_object(struct qt602240_data *data, u8 type) return NULL; } -static int qt602240_read_message(struct qt602240_data *data, - struct qt602240_message *message) +static int mxt_read_message(struct mxt_data *data, + struct mxt_message *message) { - struct qt602240_object *object; + struct mxt_object *object; u16 reg; - object = qt602240_get_object(data, QT602240_GEN_MESSAGE); + object = mxt_get_object(data, MXT_GEN_MESSAGE); if (!object) return -EINVAL; reg = object->start_address; - return __qt602240_read_reg(data->client, reg, - sizeof(struct qt602240_message), message); + return __mxt_read_reg(data->client, reg, + sizeof(struct mxt_message), message); } -static int qt602240_read_object(struct qt602240_data *data, +static int mxt_read_object(struct mxt_data *data, u8 type, u8 offset, u8 *val) { - struct qt602240_object *object; + struct mxt_object *object; u16 reg; - object = qt602240_get_object(data, type); + object = mxt_get_object(data, type); if (!object) return -EINVAL; reg = object->start_address; - return __qt602240_read_reg(data->client, reg + offset, 1, val); + return __mxt_read_reg(data->client, reg + offset, 1, val); } -static int qt602240_write_object(struct qt602240_data *data, +static int mxt_write_object(struct mxt_data *data, u8 type, u8 offset, u8 val) { - struct qt602240_object *object; + struct mxt_object *object; u16 reg; - object = qt602240_get_object(data, type); + object = mxt_get_object(data, type); if (!object) return -EINVAL; reg = object->start_address; - return qt602240_write_reg(data->client, reg + offset, val); + return mxt_write_reg(data->client, reg + offset, val); } -static void qt602240_input_report(struct qt602240_data *data, int single_id) +static void mxt_input_report(struct mxt_data *data, int single_id) { - struct qt602240_finger *finger = data->finger; + struct mxt_finger *finger = data->finger; struct input_dev *input_dev = data->input_dev; int status = finger[single_id].status; int finger_num = 0; int id; - for (id = 0; id < QT602240_MAX_FINGER; id++) { + for (id = 0; id < MXT_MAX_FINGER; id++) { if (!finger[id].status) continue; input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, - finger[id].status != QT602240_RELEASE ? + finger[id].status != MXT_RELEASE ? finger[id].area : 0); input_report_abs(input_dev, ABS_MT_POSITION_X, finger[id].x); @@ -616,7 +616,7 @@ static void qt602240_input_report(struct qt602240_data *data, int single_id) finger[id].y); input_mt_sync(input_dev); - if (finger[id].status == QT602240_RELEASE) + if (finger[id].status == MXT_RELEASE) finger[id].status = 0; else finger_num++; @@ -624,7 +624,7 @@ static void qt602240_input_report(struct qt602240_data *data, int single_id) input_report_key(input_dev, BTN_TOUCH, finger_num > 0); - if (status != QT602240_RELEASE) { + if (status != MXT_RELEASE) { input_report_abs(input_dev, ABS_X, finger[single_id].x); input_report_abs(input_dev, ABS_Y, finger[single_id].y); } @@ -632,10 +632,10 @@ static void qt602240_input_report(struct qt602240_data *data, int single_id) input_sync(input_dev); } -static void qt602240_input_touchevent(struct qt602240_data *data, - struct qt602240_message *message, int id) +static void mxt_input_touchevent(struct mxt_data *data, + struct mxt_message *message, int id) { - struct qt602240_finger *finger = data->finger; + struct mxt_finger *finger = data->finger; struct device *dev = &data->client->dev; u8 status = message->message[0]; int x; @@ -643,18 +643,18 @@ static void qt602240_input_touchevent(struct qt602240_data *data, int area; /* Check the touch is present on the screen */ - if (!(status & QT602240_DETECT)) { - if (status & QT602240_RELEASE) { + if (!(status & MXT_DETECT)) { + if (status & MXT_RELEASE) { dev_dbg(dev, "[%d] released\n", id); - finger[id].status = QT602240_RELEASE; - qt602240_input_report(data, id); + finger[id].status = MXT_RELEASE; + mxt_input_report(data, id); } return; } /* Check only AMP detection */ - if (!(status & (QT602240_PRESS | QT602240_MOVE))) + if (!(status & (MXT_PRESS | MXT_MOVE))) return; x = (message->message[1] << 2) | ((message->message[3] & ~0x3f) >> 6); @@ -662,23 +662,23 @@ static void qt602240_input_touchevent(struct qt602240_data *data, area = message->message[4]; dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id, - status & QT602240_MOVE ? "moved" : "pressed", + status & MXT_MOVE ? "moved" : "pressed", x, y, area); - finger[id].status = status & QT602240_MOVE ? - QT602240_MOVE : QT602240_PRESS; + finger[id].status = status & MXT_MOVE ? + MXT_MOVE : MXT_PRESS; finger[id].x = x; finger[id].y = y; finger[id].area = area; - qt602240_input_report(data, id); + mxt_input_report(data, id); } -static irqreturn_t qt602240_interrupt(int irq, void *dev_id) +static irqreturn_t mxt_interrupt(int irq, void *dev_id) { - struct qt602240_data *data = dev_id; - struct qt602240_message message; - struct qt602240_object *object; + struct mxt_data *data = dev_id; + struct mxt_message message; + struct mxt_object *object; struct device *dev = &data->client->dev; int id; u8 reportid; @@ -686,15 +686,15 @@ static irqreturn_t qt602240_interrupt(int irq, void *dev_id) u8 min_reportid; do { - if (qt602240_read_message(data, &message)) { + if (mxt_read_message(data, &message)) { dev_err(dev, "Failed to read message\n"); goto end; } reportid = message.reportid; - /* whether reportid is thing of QT602240_TOUCH_MULTI */ - object = qt602240_get_object(data, QT602240_TOUCH_MULTI); + /* whether reportid is thing of MXT_TOUCH_MULTI */ + object = mxt_get_object(data, MXT_TOUCH_MULTI); if (!object) goto end; @@ -703,18 +703,18 @@ static irqreturn_t qt602240_interrupt(int irq, void *dev_id) id = reportid - min_reportid; if (reportid >= min_reportid && reportid <= max_reportid) - qt602240_input_touchevent(data, &message, id); + mxt_input_touchevent(data, &message, id); else - qt602240_dump_message(dev, &message); + mxt_dump_message(dev, &message); } while (reportid != 0xff); end: return IRQ_HANDLED; } -static int qt602240_check_reg_init(struct qt602240_data *data) +static int mxt_check_reg_init(struct mxt_data *data) { - struct qt602240_object *object; + struct mxt_object *object; struct device *dev = &data->client->dev; int index = 0; int i, j; @@ -722,13 +722,13 @@ static int qt602240_check_reg_init(struct qt602240_data *data) u8 *init_vals; switch (version) { - case QT602240_VER_20: + case MXT_VER_20: init_vals = (u8 *)init_vals_ver_20; break; - case QT602240_VER_21: + case MXT_VER_21: init_vals = (u8 *)init_vals_ver_21; break; - case QT602240_VER_22: + case MXT_VER_22: init_vals = (u8 *)init_vals_ver_22; break; default: @@ -739,11 +739,11 @@ static int qt602240_check_reg_init(struct qt602240_data *data) for (i = 0; i < data->info.object_num; i++) { object = data->object_table + i; - if (!qt602240_object_writable(object->type)) + if (!mxt_object_writable(object->type)) continue; for (j = 0; j < object->size + 1; j++) - qt602240_write_object(data, object->type, j, + mxt_write_object(data, object->type, j, init_vals[index + j]); index += object->size + 1; @@ -752,9 +752,9 @@ static int qt602240_check_reg_init(struct qt602240_data *data) return 0; } -static int qt602240_check_matrix_size(struct qt602240_data *data) +static int mxt_check_matrix_size(struct mxt_data *data) { - const struct qt602240_platform_data *pdata = data->pdata; + const struct mxt_platform_data *pdata = data->pdata; struct device *dev = &data->client->dev; int mode = -1; int error; @@ -801,8 +801,8 @@ static int qt602240_check_matrix_size(struct qt602240_data *data) return -EINVAL; } - error = qt602240_read_object(data, QT602240_SPT_CTECONFIG, - QT602240_CTE_MODE, &val); + error = mxt_read_object(data, MXT_SPT_CTECONFIG, + MXT_CTE_MODE, &val); if (error) return error; @@ -810,17 +810,17 @@ static int qt602240_check_matrix_size(struct qt602240_data *data) return 0; /* Change the CTE configuration */ - qt602240_write_object(data, QT602240_SPT_CTECONFIG, - QT602240_CTE_CTRL, 1); - qt602240_write_object(data, QT602240_SPT_CTECONFIG, - QT602240_CTE_MODE, mode); - qt602240_write_object(data, QT602240_SPT_CTECONFIG, - QT602240_CTE_CTRL, 0); + mxt_write_object(data, MXT_SPT_CTECONFIG, + MXT_CTE_CTRL, 1); + mxt_write_object(data, MXT_SPT_CTECONFIG, + MXT_CTE_MODE, mode); + mxt_write_object(data, MXT_SPT_CTECONFIG, + MXT_CTE_CTRL, 0); return 0; } -static int qt602240_make_highchg(struct qt602240_data *data) +static int mxt_make_highchg(struct mxt_data *data) { struct device *dev = &data->client->dev; int count = 10; @@ -829,7 +829,7 @@ static int qt602240_make_highchg(struct qt602240_data *data) /* Read dummy message to make high CHG pin */ do { - error = qt602240_read_object(data, QT602240_GEN_MESSAGE, 0, &val); + error = mxt_read_object(data, MXT_GEN_MESSAGE, 0, &val); if (error) return error; } while ((val != 0xff) && --count); @@ -842,82 +842,82 @@ static int qt602240_make_highchg(struct qt602240_data *data) return 0; } -static void qt602240_handle_pdata(struct qt602240_data *data) +static void mxt_handle_pdata(struct mxt_data *data) { - const struct qt602240_platform_data *pdata = data->pdata; + const struct mxt_platform_data *pdata = data->pdata; u8 voltage; /* Set touchscreen lines */ - qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_XSIZE, + mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_XSIZE, pdata->x_line); - qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_YSIZE, + mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_YSIZE, pdata->y_line); /* Set touchscreen orient */ - qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_ORIENT, + mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_ORIENT, pdata->orient); /* Set touchscreen burst length */ - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_BLEN, pdata->blen); + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_BLEN, pdata->blen); /* Set touchscreen threshold */ - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_TCHTHR, pdata->threshold); + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_TCHTHR, pdata->threshold); /* Set touchscreen resolution */ - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); /* Set touchscreen voltage */ - if (data->info.version >= QT602240_VER_21 && pdata->voltage) { - if (pdata->voltage < QT602240_VOLTAGE_DEFAULT) { - voltage = (QT602240_VOLTAGE_DEFAULT - pdata->voltage) / - QT602240_VOLTAGE_STEP; + if (data->info.version >= MXT_VER_21 && pdata->voltage) { + if (pdata->voltage < MXT_VOLTAGE_DEFAULT) { + voltage = (MXT_VOLTAGE_DEFAULT - pdata->voltage) / + MXT_VOLTAGE_STEP; voltage = 0xff - voltage + 1; } else - voltage = (pdata->voltage - QT602240_VOLTAGE_DEFAULT) / - QT602240_VOLTAGE_STEP; + voltage = (pdata->voltage - MXT_VOLTAGE_DEFAULT) / + MXT_VOLTAGE_STEP; - qt602240_write_object(data, QT602240_SPT_CTECONFIG, - QT602240_CTE_VOLTAGE, voltage); + mxt_write_object(data, MXT_SPT_CTECONFIG, + MXT_CTE_VOLTAGE, voltage); } } -static int qt602240_get_info(struct qt602240_data *data) +static int mxt_get_info(struct mxt_data *data) { struct i2c_client *client = data->client; - struct qt602240_info *info = &data->info; + struct mxt_info *info = &data->info; int error; u8 val; - error = qt602240_read_reg(client, QT602240_FAMILY_ID, &val); + error = mxt_read_reg(client, MXT_FAMILY_ID, &val); if (error) return error; info->family_id = val; - error = qt602240_read_reg(client, QT602240_VARIANT_ID, &val); + error = mxt_read_reg(client, MXT_VARIANT_ID, &val); if (error) return error; info->variant_id = val; - error = qt602240_read_reg(client, QT602240_VERSION, &val); + error = mxt_read_reg(client, MXT_VERSION, &val); if (error) return error; info->version = val; - error = qt602240_read_reg(client, QT602240_BUILD, &val); + error = mxt_read_reg(client, MXT_BUILD, &val); if (error) return error; info->build = val; - error = qt602240_read_reg(client, QT602240_OBJECT_NUM, &val); + error = mxt_read_reg(client, MXT_OBJECT_NUM, &val); if (error) return error; info->object_num = val; @@ -925,19 +925,19 @@ static int qt602240_get_info(struct qt602240_data *data) return 0; } -static int qt602240_get_object_table(struct qt602240_data *data) +static int mxt_get_object_table(struct mxt_data *data) { int error; int i; u16 reg; u8 reportid = 0; - u8 buf[QT602240_OBJECT_SIZE]; + u8 buf[MXT_OBJECT_SIZE]; for (i = 0; i < data->info.object_num; i++) { - struct qt602240_object *object = data->object_table + i; + struct mxt_object *object = data->object_table + i; - reg = QT602240_OBJECT_START + QT602240_OBJECT_SIZE * i; - error = qt602240_read_object_table(data->client, reg, buf); + reg = MXT_OBJECT_START + MXT_OBJECT_SIZE * i; + error = mxt_read_object_table(data->client, reg, buf); if (error) return error; @@ -957,19 +957,19 @@ static int qt602240_get_object_table(struct qt602240_data *data) return 0; } -static int qt602240_initialize(struct qt602240_data *data) +static int mxt_initialize(struct mxt_data *data) { struct i2c_client *client = data->client; - struct qt602240_info *info = &data->info; + struct mxt_info *info = &data->info; int error; u8 val; - error = qt602240_get_info(data); + error = mxt_get_info(data); if (error) return error; data->object_table = kcalloc(info->object_num, - sizeof(struct qt602240_object), + sizeof(struct mxt_object), GFP_KERNEL); if (!data->object_table) { dev_err(&client->dev, "Failed to allocate memory\n"); @@ -977,44 +977,44 @@ static int qt602240_initialize(struct qt602240_data *data) } /* Get object table information */ - error = qt602240_get_object_table(data); + error = mxt_get_object_table(data); if (error) return error; /* Check register init values */ - error = qt602240_check_reg_init(data); + error = mxt_check_reg_init(data); if (error) return error; /* Check X/Y matrix size */ - error = qt602240_check_matrix_size(data); + error = mxt_check_matrix_size(data); if (error) return error; - error = qt602240_make_highchg(data); + error = mxt_make_highchg(data); if (error) return error; - qt602240_handle_pdata(data); + mxt_handle_pdata(data); /* Backup to memory */ - qt602240_write_object(data, QT602240_GEN_COMMAND, - QT602240_COMMAND_BACKUPNV, - QT602240_BACKUP_VALUE); - msleep(QT602240_BACKUP_TIME); + mxt_write_object(data, MXT_GEN_COMMAND, + MXT_COMMAND_BACKUPNV, + MXT_BACKUP_VALUE); + msleep(MXT_BACKUP_TIME); /* Soft reset */ - qt602240_write_object(data, QT602240_GEN_COMMAND, - QT602240_COMMAND_RESET, 1); - msleep(QT602240_RESET_TIME); + mxt_write_object(data, MXT_GEN_COMMAND, + MXT_COMMAND_RESET, 1); + msleep(MXT_RESET_TIME); /* Update matrix size at info struct */ - error = qt602240_read_reg(client, QT602240_MATRIX_X_SIZE, &val); + error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val); if (error) return error; info->matrix_xsize = val; - error = qt602240_read_reg(client, QT602240_MATRIX_Y_SIZE, &val); + error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, &val); if (error) return error; info->matrix_ysize = val; @@ -1032,11 +1032,11 @@ static int qt602240_initialize(struct qt602240_data *data) return 0; } -static ssize_t qt602240_object_show(struct device *dev, +static ssize_t mxt_object_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct qt602240_data *data = dev_get_drvdata(dev); - struct qt602240_object *object; + struct mxt_data *data = dev_get_drvdata(dev); + struct mxt_object *object; int count = 0; int i, j; int error; @@ -1049,13 +1049,13 @@ static ssize_t qt602240_object_show(struct device *dev, "Object Table Element %d(Type %d)\n", i + 1, object->type); - if (!qt602240_object_readable(object->type)) { + if (!mxt_object_readable(object->type)) { count += sprintf(buf + count, "\n"); continue; } for (j = 0; j < object->size + 1; j++) { - error = qt602240_read_object(data, + error = mxt_read_object(data, object->type, j, &val); if (error) return error; @@ -1070,9 +1070,9 @@ static ssize_t qt602240_object_show(struct device *dev, return count; } -static int qt602240_load_fw(struct device *dev, const char *fn) +static int mxt_load_fw(struct device *dev, const char *fn) { - struct qt602240_data *data = dev_get_drvdata(dev); + struct mxt_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; const struct firmware *fw = NULL; unsigned int frame_size; @@ -1086,26 +1086,26 @@ static int qt602240_load_fw(struct device *dev, const char *fn) } /* Change to the bootloader mode */ - qt602240_write_object(data, QT602240_GEN_COMMAND, - QT602240_COMMAND_RESET, QT602240_BOOT_VALUE); - msleep(QT602240_RESET_TIME); + mxt_write_object(data, MXT_GEN_COMMAND, + MXT_COMMAND_RESET, MXT_BOOT_VALUE); + msleep(MXT_RESET_TIME); /* Change to slave address of bootloader */ - if (client->addr == QT602240_APP_LOW) - client->addr = QT602240_BOOT_LOW; + if (client->addr == MXT_APP_LOW) + client->addr = MXT_BOOT_LOW; else - client->addr = QT602240_BOOT_HIGH; + client->addr = MXT_BOOT_HIGH; - ret = qt602240_check_bootloader(client, QT602240_WAITING_BOOTLOAD_CMD); + ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD); if (ret) goto out; /* Unlock bootloader */ - qt602240_unlock_bootloader(client); + mxt_unlock_bootloader(client); while (pos < fw->size) { - ret = qt602240_check_bootloader(client, - QT602240_WAITING_FRAME_DATA); + ret = mxt_check_bootloader(client, + MXT_WAITING_FRAME_DATA); if (ret) goto out; @@ -1117,10 +1117,10 @@ static int qt602240_load_fw(struct device *dev, const char *fn) frame_size += 2; /* Write one frame to device */ - qt602240_fw_write(client, fw->data + pos, frame_size); + mxt_fw_write(client, fw->data + pos, frame_size); - ret = qt602240_check_bootloader(client, - QT602240_FRAME_CRC_PASS); + ret = mxt_check_bootloader(client, + MXT_FRAME_CRC_PASS); if (ret) goto out; @@ -1133,19 +1133,19 @@ out: release_firmware(fw); /* Change to slave address of application */ - if (client->addr == QT602240_BOOT_LOW) - client->addr = QT602240_APP_LOW; + if (client->addr == MXT_BOOT_LOW) + client->addr = MXT_APP_LOW; else - client->addr = QT602240_APP_HIGH; + client->addr = MXT_APP_HIGH; return ret; } -static ssize_t qt602240_update_fw_store(struct device *dev, +static ssize_t mxt_update_fw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qt602240_data *data = dev_get_drvdata(dev); + struct mxt_data *data = dev_get_drvdata(dev); unsigned int version; int error; @@ -1154,14 +1154,14 @@ static ssize_t qt602240_update_fw_store(struct device *dev, return -EINVAL; } - if (data->info.version < QT602240_VER_21 || version < QT602240_VER_21) { + if (data->info.version < MXT_VER_21 || version < MXT_VER_21) { dev_err(dev, "FW update supported starting with version 21\n"); return -EINVAL; } disable_irq(data->irq); - error = qt602240_load_fw(dev, QT602240_FW_NAME); + error = mxt_load_fw(dev, MXT_FW_NAME); if (error) { dev_err(dev, "The firmware update failed(%d)\n", error); count = error; @@ -1169,12 +1169,12 @@ static ssize_t qt602240_update_fw_store(struct device *dev, dev_dbg(dev, "The firmware update succeeded\n"); /* Wait for reset */ - msleep(QT602240_FWRESET_TIME); + msleep(MXT_FWRESET_TIME); kfree(data->object_table); data->object_table = NULL; - qt602240_initialize(data); + mxt_initialize(data); } enable_irq(data->irq); @@ -1182,60 +1182,60 @@ static ssize_t qt602240_update_fw_store(struct device *dev, return count; } -static DEVICE_ATTR(object, 0444, qt602240_object_show, NULL); -static DEVICE_ATTR(update_fw, 0664, NULL, qt602240_update_fw_store); +static DEVICE_ATTR(object, 0444, mxt_object_show, NULL); +static DEVICE_ATTR(update_fw, 0664, NULL, mxt_update_fw_store); -static struct attribute *qt602240_attrs[] = { +static struct attribute *mxt_attrs[] = { &dev_attr_object.attr, &dev_attr_update_fw.attr, NULL }; -static const struct attribute_group qt602240_attr_group = { - .attrs = qt602240_attrs, +static const struct attribute_group mxt_attr_group = { + .attrs = mxt_attrs, }; -static void qt602240_start(struct qt602240_data *data) +static void mxt_start(struct mxt_data *data) { /* Touch enable */ - qt602240_write_object(data, - QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0x83); + mxt_write_object(data, + MXT_TOUCH_MULTI, MXT_TOUCH_CTRL, 0x83); } -static void qt602240_stop(struct qt602240_data *data) +static void mxt_stop(struct mxt_data *data) { /* Touch disable */ - qt602240_write_object(data, - QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0); + mxt_write_object(data, + MXT_TOUCH_MULTI, MXT_TOUCH_CTRL, 0); } -static int qt602240_input_open(struct input_dev *dev) +static int mxt_input_open(struct input_dev *dev) { - struct qt602240_data *data = input_get_drvdata(dev); + struct mxt_data *data = input_get_drvdata(dev); - qt602240_start(data); + mxt_start(data); return 0; } -static void qt602240_input_close(struct input_dev *dev) +static void mxt_input_close(struct input_dev *dev) { - struct qt602240_data *data = input_get_drvdata(dev); + struct mxt_data *data = input_get_drvdata(dev); - qt602240_stop(data); + mxt_stop(data); } -static int __devinit qt602240_probe(struct i2c_client *client, +static int __devinit mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct qt602240_data *data; + struct mxt_data *data; struct input_dev *input_dev; int error; if (!client->dev.platform_data) return -EINVAL; - data = kzalloc(sizeof(struct qt602240_data), GFP_KERNEL); + data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); input_dev = input_allocate_device(); if (!data || !input_dev) { dev_err(&client->dev, "Failed to allocate memory\n"); @@ -1243,11 +1243,11 @@ static int __devinit qt602240_probe(struct i2c_client *client, goto err_free_mem; } - input_dev->name = "AT42QT602240/ATMXT224 Touchscreen"; + input_dev->name = "Atmel maXTouch Touchscreen"; input_dev->id.bustype = BUS_I2C; input_dev->dev.parent = &client->dev; - input_dev->open = qt602240_input_open; - input_dev->close = qt602240_input_close; + input_dev->open = mxt_input_open; + input_dev->close = mxt_input_close; __set_bit(EV_ABS, input_dev->evbit); __set_bit(EV_KEY, input_dev->evbit); @@ -1255,17 +1255,17 @@ static int __devinit qt602240_probe(struct i2c_client *client, /* For single touch */ input_set_abs_params(input_dev, ABS_X, - 0, QT602240_MAX_XC, 0, 0); + 0, MXT_MAX_XC, 0, 0); input_set_abs_params(input_dev, ABS_Y, - 0, QT602240_MAX_YC, 0, 0); + 0, MXT_MAX_YC, 0, 0); /* For multi touch */ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, - 0, QT602240_MAX_AREA, 0, 0); + 0, MXT_MAX_AREA, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_X, - 0, QT602240_MAX_XC, 0, 0); + 0, MXT_MAX_XC, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_Y, - 0, QT602240_MAX_YC, 0, 0); + 0, MXT_MAX_YC, 0, 0); input_set_drvdata(input_dev, data); @@ -1276,11 +1276,11 @@ static int __devinit qt602240_probe(struct i2c_client *client, i2c_set_clientdata(client, data); - error = qt602240_initialize(data); + error = mxt_initialize(data); if (error) goto err_free_object; - error = request_threaded_irq(client->irq, NULL, qt602240_interrupt, + error = request_threaded_irq(client->irq, NULL, mxt_interrupt, IRQF_TRIGGER_FALLING, client->dev.driver->name, data); if (error) { dev_err(&client->dev, "Failed to register interrupt\n"); @@ -1291,7 +1291,7 @@ static int __devinit qt602240_probe(struct i2c_client *client, if (error) goto err_free_irq; - error = sysfs_create_group(&client->dev.kobj, &qt602240_attr_group); + error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); if (error) goto err_unregister_device; @@ -1310,11 +1310,11 @@ err_free_mem: return error; } -static int __devexit qt602240_remove(struct i2c_client *client) +static int __devexit mxt_remove(struct i2c_client *client) { - struct qt602240_data *data = i2c_get_clientdata(client); + struct mxt_data *data = i2c_get_clientdata(client); - sysfs_remove_group(&client->dev.kobj, &qt602240_attr_group); + sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); free_irq(data->irq, data); input_unregister_device(data->input_dev); kfree(data->object_table); @@ -1324,83 +1324,84 @@ static int __devexit qt602240_remove(struct i2c_client *client) } #ifdef CONFIG_PM -static int qt602240_suspend(struct device *dev) +static int mxt_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - struct qt602240_data *data = i2c_get_clientdata(client); + struct mxt_data *data = i2c_get_clientdata(client); struct input_dev *input_dev = data->input_dev; mutex_lock(&input_dev->mutex); if (input_dev->users) - qt602240_stop(data); + mxt_stop(data); mutex_unlock(&input_dev->mutex); return 0; } -static int qt602240_resume(struct device *dev) +static int mxt_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - struct qt602240_data *data = i2c_get_clientdata(client); + struct mxt_data *data = i2c_get_clientdata(client); struct input_dev *input_dev = data->input_dev; /* Soft reset */ - qt602240_write_object(data, QT602240_GEN_COMMAND, - QT602240_COMMAND_RESET, 1); + mxt_write_object(data, MXT_GEN_COMMAND, + MXT_COMMAND_RESET, 1); - msleep(QT602240_RESET_TIME); + msleep(MXT_RESET_TIME); mutex_lock(&input_dev->mutex); if (input_dev->users) - qt602240_start(data); + mxt_start(data); mutex_unlock(&input_dev->mutex); return 0; } -static const struct dev_pm_ops qt602240_pm_ops = { - .suspend = qt602240_suspend, - .resume = qt602240_resume, +static const struct dev_pm_ops mxt_pm_ops = { + .suspend = mxt_suspend, + .resume = mxt_resume, }; #endif -static const struct i2c_device_id qt602240_id[] = { +static const struct i2c_device_id mxt_id[] = { { "qt602240_ts", 0 }, + { "atmel_mxt_ts", 0 }, { } }; -MODULE_DEVICE_TABLE(i2c, qt602240_id); +MODULE_DEVICE_TABLE(i2c, mxt_id); -static struct i2c_driver qt602240_driver = { +static struct i2c_driver mxt_driver = { .driver = { - .name = "qt602240_ts", + .name = "atmel_mxt_ts", .owner = THIS_MODULE, #ifdef CONFIG_PM - .pm = &qt602240_pm_ops, + .pm = &mxt_pm_ops, #endif }, - .probe = qt602240_probe, - .remove = __devexit_p(qt602240_remove), - .id_table = qt602240_id, + .probe = mxt_probe, + .remove = __devexit_p(mxt_remove), + .id_table = mxt_id, }; -static int __init qt602240_init(void) +static int __init mxt_init(void) { - return i2c_add_driver(&qt602240_driver); + return i2c_add_driver(&mxt_driver); } -static void __exit qt602240_exit(void) +static void __exit mxt_exit(void) { - i2c_del_driver(&qt602240_driver); + i2c_del_driver(&mxt_driver); } -module_init(qt602240_init); -module_exit(qt602240_exit); +module_init(mxt_init); +module_exit(mxt_exit); /* Module information */ MODULE_AUTHOR("Joonyoung Shim "); -MODULE_DESCRIPTION("AT42QT602240/ATMXT224 Touchscreen driver"); +MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver"); MODULE_LICENSE("GPL"); diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index c5033e101094..671967cf4d1a 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -1,5 +1,5 @@ /* - * AT42QT602240/ATMXT224 Touchscreen driver + * Atmel maXTouch Touchscreen driver * * Copyright (C) 2010 Samsung Electronics Co.Ltd * Author: Joonyoung Shim @@ -10,21 +10,21 @@ * option) any later version. */ -#ifndef __LINUX_QT602240_TS_H -#define __LINUX_QT602240_TS_H +#ifndef __LINUX_ATMEL_MXT_TS_H +#define __LINUX_ATMEL_MXT_TS_H /* Orient */ -#define QT602240_NORMAL 0x0 -#define QT602240_DIAGONAL 0x1 -#define QT602240_HORIZONTAL_FLIP 0x2 -#define QT602240_ROTATED_90_COUNTER 0x3 -#define QT602240_VERTICAL_FLIP 0x4 -#define QT602240_ROTATED_90 0x5 -#define QT602240_ROTATED_180 0x6 -#define QT602240_DIAGONAL_COUNTER 0x7 +#define MXT_NORMAL 0x0 +#define MXT_DIAGONAL 0x1 +#define MXT_HORIZONTAL_FLIP 0x2 +#define MXT_ROTATED_90_COUNTER 0x3 +#define MXT_VERTICAL_FLIP 0x4 +#define MXT_ROTATED_90 0x5 +#define MXT_ROTATED_180 0x6 +#define MXT_DIAGONAL_COUNTER 0x7 -/* The platform data for the AT42QT602240/ATMXT224 touchscreen driver */ -struct qt602240_platform_data { +/* The platform data for the Atmel maXTouch touchscreen driver */ +struct mxt_platform_data { unsigned int x_line; unsigned int y_line; unsigned int x_size; @@ -35,4 +35,4 @@ struct qt602240_platform_data { unsigned char orient; }; -#endif /* __LINUX_QT602240_TS_H */ +#endif /* __LINUX_ATMEL_MXT_TS_H */ -- cgit v1.2.3 From 71749f5c66e797a39600dae9de58aab3858dc488 Mon Sep 17 00:00:00 2001 From: Iiro Valkonen Date: Tue, 15 Feb 2011 13:36:52 -0800 Subject: Input: atmel_mxt_ts - allow board code to suppliy controller config As there is no common configuration settings that would work in every situation, remove the fixed config data from driver code and add config data to platform data. Signed-off-by: Iiro Valkonen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 148 +++---------------------------- include/linux/i2c/atmel_mxt_ts.h | 5 ++ 2 files changed, 18 insertions(+), 135 deletions(-) (limited to 'include') diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index fe8902e1f010..52032ef3d3ab 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -199,122 +199,6 @@ #define MXT_MAX_FINGER 10 -/* Initial register values recommended from chip vendor */ -static const u8 init_vals_ver_20[] = { - /* MXT_GEN_COMMAND(6) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_GEN_POWER(7) */ - 0x20, 0xff, 0x32, - /* MXT_GEN_ACQUIRE(8) */ - 0x08, 0x05, 0x05, 0x00, 0x00, 0x00, 0x05, 0x14, - /* MXT_TOUCH_MULTI(9) */ - 0x00, 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x64, - /* MXT_TOUCH_KEYARRAY(15) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - /* MXT_SPT_GPIOPWM(19) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - /* MXT_PROCI_GRIPFACE(20) */ - 0x00, 0x64, 0x64, 0x64, 0x64, 0x00, 0x00, 0x1e, 0x14, 0x04, - 0x1e, 0x00, - /* MXT_PROCG_NOISE(22) */ - 0x05, 0x00, 0x00, 0x19, 0x00, 0xe7, 0xff, 0x04, 0x32, 0x00, - 0x01, 0x0a, 0x0f, 0x14, 0x00, 0x00, 0xe8, - /* MXT_TOUCH_PROXIMITY(23) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - /* MXT_PROCI_ONETOUCH(24) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_SPT_SELFTEST(25) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - /* MXT_PROCI_TWOTOUCH(27) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_SPT_CTECONFIG(28) */ - 0x00, 0x00, 0x00, 0x04, 0x08, -}; - -static const u8 init_vals_ver_21[] = { - /* MXT_GEN_COMMAND(6) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_GEN_POWER(7) */ - 0x20, 0xff, 0x32, - /* MXT_GEN_ACQUIRE(8) */ - 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, - /* MXT_TOUCH_MULTI(9) */ - 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_TOUCH_KEYARRAY(15) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - /* MXT_SPT_GPIOPWM(19) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_PROCI_GRIPFACE(20) */ - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, - 0x0f, 0x0a, - /* MXT_PROCG_NOISE(22) */ - 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, - 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, - /* MXT_TOUCH_PROXIMITY(23) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - /* MXT_PROCI_ONETOUCH(24) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_SPT_SELFTEST(25) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - /* MXT_PROCI_TWOTOUCH(27) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_SPT_CTECONFIG(28) */ - 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, -}; - -static const u8 init_vals_ver_22[] = { - /* MXT_GEN_COMMAND(6) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_GEN_POWER(7) */ - 0x20, 0xff, 0x32, - /* MXT_GEN_ACQUIRE(8) */ - 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, - /* MXT_TOUCH_MULTI(9) */ - 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - /* MXT_TOUCH_KEYARRAY(15) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - /* MXT_SPT_GPIOPWM(19) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_PROCI_GRIPFACE(20) */ - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, - 0x0f, 0x0a, - /* MXT_PROCG_NOISE(22) */ - 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, - 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, - /* MXT_TOUCH_PROXIMITY(23) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_PROCI_ONETOUCH(24) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_SPT_SELFTEST(25) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - /* MXT_PROCI_TWOTOUCH(27) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* MXT_SPT_CTECONFIG(28) */ - 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, -}; - struct mxt_info { u8 family_id; u8 variant_id; @@ -714,26 +598,15 @@ end: static int mxt_check_reg_init(struct mxt_data *data) { + const struct mxt_platform_data *pdata = data->pdata; struct mxt_object *object; struct device *dev = &data->client->dev; int index = 0; - int i, j; - u8 version = data->info.version; - u8 *init_vals; + int i, j, config_offset; - switch (version) { - case MXT_VER_20: - init_vals = (u8 *)init_vals_ver_20; - break; - case MXT_VER_21: - init_vals = (u8 *)init_vals_ver_21; - break; - case MXT_VER_22: - init_vals = (u8 *)init_vals_ver_22; - break; - default: - dev_err(dev, "Firmware version %d doesn't support\n", version); - return -EINVAL; + if (!pdata->config) { + dev_dbg(dev, "No cfg data defined, skipping reg init\n"); + return 0; } for (i = 0; i < data->info.object_num; i++) { @@ -742,10 +615,15 @@ static int mxt_check_reg_init(struct mxt_data *data) if (!mxt_object_writable(object->type)) continue; - for (j = 0; j < object->size + 1; j++) + for (j = 0; j < object->size + 1; j++) { + config_offset = index + j; + if (config_offset > pdata->config_length) { + dev_err(dev, "Not enough config data!\n"); + return -EINVAL; + } mxt_write_object(data, object->type, j, - init_vals[index + j]); - + pdata->config[config_offset]); + } index += object->size + 1; } diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index 671967cf4d1a..b8297685f489 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -13,6 +13,8 @@ #ifndef __LINUX_ATMEL_MXT_TS_H #define __LINUX_ATMEL_MXT_TS_H +#include + /* Orient */ #define MXT_NORMAL 0x0 #define MXT_DIAGONAL 0x1 @@ -25,6 +27,9 @@ /* The platform data for the Atmel maXTouch touchscreen driver */ struct mxt_platform_data { + const u8 *config; + size_t config_length; + unsigned int x_line; unsigned int y_line; unsigned int x_size; -- cgit v1.2.3 From 919ed895f0b4227da26ea1b0a1347db5010f105e Mon Sep 17 00:00:00 2001 From: Iiro Valkonen Date: Tue, 15 Feb 2011 13:36:52 -0800 Subject: Input: atmel_mxt_ts - allow board code to specify IRQ flags Different board have different requirements/setups so let's be more flexible. Signed-off-by: Iiro Valkonen Signed-off-by: Dmitry Torokhov --- arch/arm/mach-s5pv210/mach-goni.c | 2 ++ drivers/input/touchscreen/atmel_mxt_ts.c | 7 ++++--- include/linux/i2c/atmel_mxt_ts.h | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index e9a7d340f4e1..be0eb7a2e612 100644 --- a/arch/arm/mach-s5pv210/mach-goni.c +++ b/arch/arm/mach-s5pv210/mach-goni.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -231,6 +232,7 @@ static struct mxt_platform_data qt602240_platform_data = { .threshold = 0x28, .voltage = 2800000, /* 2.8V */ .orient = MXT_DIAGONAL, + .irqflags = IRQF_TRIGGER_FALLING, }; static struct s3c2410_platform_i2c i2c2_data __initdata = { diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 52032ef3d3ab..d2e5864ca096 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1106,11 +1106,12 @@ static void mxt_input_close(struct input_dev *dev) static int __devinit mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) { + const struct mxt_platform_data *pdata = client->dev.platform_data; struct mxt_data *data; struct input_dev *input_dev; int error; - if (!client->dev.platform_data) + if (!pdata) return -EINVAL; data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); @@ -1149,7 +1150,7 @@ static int __devinit mxt_probe(struct i2c_client *client, data->client = client; data->input_dev = input_dev; - data->pdata = client->dev.platform_data; + data->pdata = pdata; data->irq = client->irq; i2c_set_clientdata(client, data); @@ -1159,7 +1160,7 @@ static int __devinit mxt_probe(struct i2c_client *client, goto err_free_object; error = request_threaded_irq(client->irq, NULL, mxt_interrupt, - IRQF_TRIGGER_FALLING, client->dev.driver->name, data); + pdata->irqflags, client->dev.driver->name, data); if (error) { dev_err(&client->dev, "Failed to register interrupt\n"); goto err_free_object; diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index b8297685f489..f027f7a63511 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -38,6 +38,7 @@ struct mxt_platform_data { unsigned int threshold; unsigned int voltage; unsigned char orient; + unsigned long irqflags; }; #endif /* __LINUX_ATMEL_MXT_TS_H */ -- cgit v1.2.3 From 24d51add7438f9696a7205927bf9de3c5c787a58 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 21 Feb 2011 09:52:50 +0100 Subject: workqueue: fix build failure introduced by s/freezeable/freezable/ wq:fixes-2.6.38 does s/WQ_FREEZEABLE/WQ_FREEZABLE and wq:for-2.6.39 adds new usage of the flag. The combination of the two creates a build failure after merge. Fix it by renaming all freezeables to freezables. Signed-off-by: Tejun Heo Reported-by: Stephen Rothwell --- include/linux/workqueue.h | 6 +++--- kernel/workqueue.c | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index d110cc4f9fed..f584aba78ca9 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -287,14 +287,14 @@ enum { * executed immediately as long as max_active limit is not reached and * resources are available. * - * system_freezeable_wq is equivalent to system_wq except that it's - * freezeable. + * system_freezable_wq is equivalent to system_wq except that it's + * freezable. */ extern struct workqueue_struct *system_wq; extern struct workqueue_struct *system_long_wq; extern struct workqueue_struct *system_nrt_wq; extern struct workqueue_struct *system_unbound_wq; -extern struct workqueue_struct *system_freezeable_wq; +extern struct workqueue_struct *system_freezable_wq; extern struct workqueue_struct * __alloc_workqueue_key(const char *name, unsigned int flags, int max_active, diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 572f559f6cb9..1b64d225f067 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -251,12 +251,12 @@ struct workqueue_struct *system_wq __read_mostly; struct workqueue_struct *system_long_wq __read_mostly; struct workqueue_struct *system_nrt_wq __read_mostly; struct workqueue_struct *system_unbound_wq __read_mostly; -struct workqueue_struct *system_freezeable_wq __read_mostly; +struct workqueue_struct *system_freezable_wq __read_mostly; EXPORT_SYMBOL_GPL(system_wq); EXPORT_SYMBOL_GPL(system_long_wq); EXPORT_SYMBOL_GPL(system_nrt_wq); EXPORT_SYMBOL_GPL(system_unbound_wq); -EXPORT_SYMBOL_GPL(system_freezeable_wq); +EXPORT_SYMBOL_GPL(system_freezable_wq); #define CREATE_TRACE_POINTS #include @@ -3777,10 +3777,10 @@ static int __init init_workqueues(void) system_nrt_wq = alloc_workqueue("events_nrt", WQ_NON_REENTRANT, 0); system_unbound_wq = alloc_workqueue("events_unbound", WQ_UNBOUND, WQ_UNBOUND_MAX_ACTIVE); - system_freezeable_wq = alloc_workqueue("events_freezeable", - WQ_FREEZEABLE, 0); + system_freezable_wq = alloc_workqueue("events_freezable", + WQ_FREEZABLE, 0); BUG_ON(!system_wq || !system_long_wq || !system_nrt_wq || - !system_unbound_wq || !system_freezeable_wq); + !system_unbound_wq || !system_freezable_wq); return 0; } early_initcall(init_workqueues); -- cgit v1.2.3 From e9a416b5ce0c0f93819f55d34cf6882196e9c3b2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 19 Feb 2011 12:05:56 -0300 Subject: Bluetooth: Add mgmt_pair_device command This patch adds a new mgmt_pair_device which can be used to initiate a dedicated bonding procedure. Some extra callbacks are added to the hci_conn struct so that the pairing code can get notified of the completion of the procedure. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 16 +++++ include/net/bluetooth/mgmt.h | 12 ++++ net/bluetooth/mgmt.c | 133 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d5d8454236bf..506f25089207 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -248,6 +248,10 @@ struct hci_conn { void *priv; struct hci_conn *link; + + void (*connect_cfm_cb) (struct hci_conn *conn, u8 status); + void (*security_cfm_cb) (struct hci_conn *conn, u8 status); + void (*disconn_cfm_cb) (struct hci_conn *conn, u8 reason); }; extern struct hci_proto *hci_proto[]; @@ -571,6 +575,9 @@ static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status) hp = hci_proto[HCI_PROTO_SCO]; if (hp && hp->connect_cfm) hp->connect_cfm(conn, status); + + if (conn->connect_cfm_cb) + conn->connect_cfm_cb(conn, status); } static inline int hci_proto_disconn_ind(struct hci_conn *conn) @@ -600,6 +607,9 @@ static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason) hp = hci_proto[HCI_PROTO_SCO]; if (hp && hp->disconn_cfm) hp->disconn_cfm(conn, reason); + + if (conn->disconn_cfm_cb) + conn->disconn_cfm_cb(conn, reason); } static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status) @@ -619,6 +629,9 @@ static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status) hp = hci_proto[HCI_PROTO_SCO]; if (hp && hp->security_cfm) hp->security_cfm(conn, status, encrypt); + + if (conn->security_cfm_cb) + conn->security_cfm_cb(conn, status); } static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encrypt) @@ -632,6 +645,9 @@ static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u hp = hci_proto[HCI_PROTO_SCO]; if (hp && hp->security_cfm) hp->security_cfm(conn, status, encrypt); + + if (conn->security_cfm_cb) + conn->security_cfm_cb(conn, status); } int hci_register_proto(struct hci_proto *hproto); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 44ac55c85079..1d25c59be2e3 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -160,6 +160,18 @@ struct mgmt_cp_set_io_capability { __u8 io_capability; } __packed; +#define MGMT_OP_PAIR_DEVICE 0x0014 +struct mgmt_cp_pair_device { + __le16 index; + bdaddr_t bdaddr; + __u8 io_cap; +} __packed; +struct mgmt_rp_pair_device { + __le16 index; + bdaddr_t bdaddr; + __u8 status; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 52e5f88b753a..d7fc54dcbc9e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -38,6 +38,7 @@ struct pending_cmd { int index; void *cmd; struct sock *sk; + void *user_data; }; LIST_HEAD(cmd_list); @@ -1063,6 +1064,135 @@ static int set_io_capability(struct sock *sk, unsigned char *data, u16 len) &dev_id, sizeof(dev_id)); } +static inline struct pending_cmd *find_pairing(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + struct list_head *p; + + list_for_each(p, &cmd_list) { + struct pending_cmd *cmd; + + cmd = list_entry(p, struct pending_cmd, list); + + if (cmd->opcode != MGMT_OP_PAIR_DEVICE) + continue; + + if (cmd->index != hdev->id) + continue; + + if (cmd->user_data != conn) + continue; + + return cmd; + } + + return NULL; +} + +static void pairing_complete(struct pending_cmd *cmd, u8 status) +{ + struct mgmt_rp_pair_device rp; + struct hci_conn *conn = cmd->user_data; + + rp.index = cmd->index; + bacpy(&rp.bdaddr, &conn->dst); + rp.status = status; + + cmd_complete(cmd->sk, MGMT_OP_PAIR_DEVICE, &rp, sizeof(rp)); + + /* So we don't get further callbacks for this connection */ + conn->connect_cfm_cb = NULL; + conn->security_cfm_cb = NULL; + conn->disconn_cfm_cb = NULL; + + hci_conn_put(conn); + + list_del(&cmd->list); + mgmt_pending_free(cmd); +} + +static void pairing_complete_cb(struct hci_conn *conn, u8 status) +{ + struct pending_cmd *cmd; + + BT_DBG("status %u", status); + + cmd = find_pairing(conn); + if (!cmd) { + BT_DBG("Unable to find a pending command"); + return; + } + + pairing_complete(cmd, status); +} + +static int pair_device(struct sock *sk, unsigned char *data, u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_pair_device *cp; + struct pending_cmd *cmd; + u8 sec_level, auth_type; + struct hci_conn *conn; + u16 dev_id; + int err; + + BT_DBG(""); + + cp = (void *) data; + dev_id = get_unaligned_le16(&cp->index); + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, MGMT_OP_PAIR_DEVICE, ENODEV); + + hci_dev_lock_bh(hdev); + + if (cp->io_cap == 0x03) { + sec_level = BT_SECURITY_MEDIUM; + auth_type = HCI_AT_DEDICATED_BONDING; + } else { + sec_level = BT_SECURITY_HIGH; + auth_type = HCI_AT_DEDICATED_BONDING_MITM; + } + + conn = hci_connect(hdev, ACL_LINK, &cp->bdaddr, sec_level, auth_type); + if (!conn) { + err = -ENOMEM; + goto unlock; + } + + if (conn->connect_cfm_cb) { + hci_conn_put(conn); + err = cmd_status(sk, MGMT_OP_PAIR_DEVICE, EBUSY); + goto unlock; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, dev_id, data, len); + if (!cmd) { + err = -ENOMEM; + hci_conn_put(conn); + goto unlock; + } + + conn->connect_cfm_cb = pairing_complete_cb; + conn->security_cfm_cb = pairing_complete_cb; + conn->disconn_cfm_cb = pairing_complete_cb; + conn->io_capability = cp->io_cap; + cmd->user_data = conn; + + if (conn->state == BT_CONNECTED && + hci_conn_security(conn, sec_level, auth_type)) + pairing_complete(cmd, 0); + + err = 0; + +unlock: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -1148,6 +1278,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_SET_IO_CAPABILITY: err = set_io_capability(sk, buf + sizeof(*hdr), len); break; + case MGMT_OP_PAIR_DEVICE: + err = pair_device(sk, buf + sizeof(*hdr), len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, opcode, 0x01); -- cgit v1.2.3 From a5c296832b4fde7d32c01cff9cdd27d9c7c1c4f5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 19 Feb 2011 12:05:57 -0300 Subject: Bluetooth: Add management support for user confirmation request This patch adds support for the user confirmation (numeric comparison) Secure Simple Pairing authentication method. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 17 +++++++ include/net/bluetooth/hci_core.h | 4 ++ include/net/bluetooth/mgmt.h | 20 ++++++++ net/bluetooth/hci_event.c | 50 +++++++++++++++++++ net/bluetooth/mgmt.c | 103 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 194 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index a5f8c4684a32..ec6acf2f1c0b 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -415,6 +415,17 @@ struct hci_cp_io_capability_reply { __u8 authentication; } __packed; +#define HCI_OP_USER_CONFIRM_REPLY 0x042c +struct hci_cp_user_confirm_reply { + bdaddr_t bdaddr; +} __packed; +struct hci_rp_user_confirm_reply { + __u8 status; + bdaddr_t bdaddr; +} __packed; + +#define HCI_OP_USER_CONFIRM_NEG_REPLY 0x042d + #define HCI_OP_IO_CAPABILITY_NEG_REPLY 0x0434 struct hci_cp_io_capability_neg_reply { bdaddr_t bdaddr; @@ -936,6 +947,12 @@ struct hci_ev_io_capa_reply { __u8 authentication; } __packed; +#define HCI_EV_USER_CONFIRM_REQUEST 0x33 +struct hci_ev_user_confirm_req { + bdaddr_t bdaddr; + __le32 passkey; +} __packed; + #define HCI_EV_SIMPLE_PAIR_COMPLETE 0x36 struct hci_ev_simple_pair_complete { __u8 status; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 506f25089207..05f4706e6c34 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -762,6 +762,10 @@ int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status); int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr); int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); +int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value); +int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); +int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr, + u8 status); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 1d25c59be2e3..52376a3295ca 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -172,6 +172,19 @@ struct mgmt_rp_pair_device { __u8 status; } __packed; +#define MGMT_OP_USER_CONFIRM_REPLY 0x0015 +struct mgmt_cp_user_confirm_reply { + __le16 index; + bdaddr_t bdaddr; +} __packed; +struct mgmt_rp_user_confirm_reply { + __le16 index; + bdaddr_t bdaddr; + __u8 status; +} __packed; + +#define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x0016 + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; @@ -239,3 +252,10 @@ struct mgmt_ev_pin_code_request { __le16 index; bdaddr_t bdaddr; } __packed; + +#define MGMT_EV_USER_CONFIRM_REQUEST 0x000F +struct mgmt_ev_user_confirm_request { + __le16 index; + bdaddr_t bdaddr; + __le32 value; +} __packed; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 98b5764e4315..604c7b5fee97 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -796,6 +796,29 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status); } +static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_user_confirm_reply *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (test_bit(HCI_MGMT, &hdev->flags)) + mgmt_user_confirm_reply_complete(hdev->id, &rp->bdaddr, + rp->status); +} + +static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_user_confirm_reply *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (test_bit(HCI_MGMT, &hdev->flags)) + mgmt_user_confirm_neg_reply_complete(hdev->id, &rp->bdaddr, + rp->status); +} + static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%x", hdev->name, status); @@ -1728,6 +1751,14 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_le_read_buffer_size(hdev, skb); break; + case HCI_OP_USER_CONFIRM_REPLY: + hci_cc_user_confirm_reply(hdev, skb); + break; + + case HCI_OP_USER_CONFIRM_NEG_REPLY: + hci_cc_user_confirm_neg_reply(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; @@ -2362,6 +2393,21 @@ unlock: hci_dev_unlock(hdev); } +static inline void hci_user_confirm_request_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_user_confirm_req *ev = (void *) skb->data; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + if (test_bit(HCI_MGMT, &hdev->flags)) + mgmt_user_confirm_request(hdev->id, &ev->bdaddr, ev->passkey); + + hci_dev_unlock(hdev); +} + static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_simple_pair_complete *ev = (void *) skb->data; @@ -2580,6 +2626,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_io_capa_reply_evt(hdev, skb); break; + case HCI_EV_USER_CONFIRM_REQUEST: + hci_user_confirm_request_evt(hdev, skb); + break; + case HCI_EV_SIMPLE_PAIR_COMPLETE: hci_simple_pair_complete_evt(hdev, skb); break; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d7fc54dcbc9e..fdcc9742bb00 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1193,6 +1193,55 @@ unlock: return err; } +static int user_confirm_reply(struct sock *sk, unsigned char *data, u16 len, + int success) +{ + struct mgmt_cp_user_confirm_reply *cp = (void *) data; + u16 dev_id, mgmt_op, hci_op; + struct pending_cmd *cmd; + struct hci_dev *hdev; + int err; + + BT_DBG(""); + + dev_id = get_unaligned_le16(&cp->index); + + if (success) { + mgmt_op = MGMT_OP_USER_CONFIRM_REPLY; + hci_op = HCI_OP_USER_CONFIRM_REPLY; + } else { + mgmt_op = MGMT_OP_USER_CONFIRM_NEG_REPLY; + hci_op = HCI_OP_USER_CONFIRM_NEG_REPLY; + } + + hdev = hci_dev_get(dev_id); + if (!hdev) + return cmd_status(sk, mgmt_op, ENODEV); + + if (!test_bit(HCI_UP, &hdev->flags)) { + err = cmd_status(sk, mgmt_op, ENETDOWN); + goto failed; + } + + cmd = mgmt_pending_add(sk, mgmt_op, dev_id, data, len); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + + err = hci_send_cmd(hdev, hci_op, sizeof(cp->bdaddr), &cp->bdaddr); + if (err < 0) { + list_del(&cmd->list); + mgmt_pending_free(cmd); + } + +failed: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -1281,6 +1330,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_PAIR_DEVICE: err = pair_device(sk, buf + sizeof(*hdr), len); break; + case MGMT_OP_USER_CONFIRM_REPLY: + err = user_confirm_reply(sk, buf + sizeof(*hdr), len, 1); + break; + case MGMT_OP_USER_CONFIRM_NEG_REPLY: + err = user_confirm_reply(sk, buf + sizeof(*hdr), len, 0); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, opcode, 0x01); @@ -1541,3 +1596,51 @@ int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) return err; } + +int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value) +{ + struct mgmt_ev_user_confirm_request ev; + + BT_DBG("hci%u", index); + + put_unaligned_le16(index, &ev.index); + bacpy(&ev.bdaddr, bdaddr); + put_unaligned_le32(value, &ev.value); + + return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, &ev, sizeof(ev), NULL); +} + +static int confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status, + u8 opcode) +{ + struct pending_cmd *cmd; + struct mgmt_rp_user_confirm_reply rp; + int err; + + cmd = mgmt_pending_find(opcode, index); + if (!cmd) + return -ENOENT; + + put_unaligned_le16(index, &rp.index); + bacpy(&rp.bdaddr, bdaddr); + rp.status = status; + err = cmd_complete(cmd->sk, opcode, &rp, sizeof(rp)); + + list_del(&cmd->list); + mgmt_pending_free(cmd); + + return err; +} + +int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +{ + return confirm_reply_complete(index, bdaddr, status, + MGMT_OP_USER_CONFIRM_REPLY); +} + +int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr, + u8 status) +{ + return confirm_reply_complete(index, bdaddr, status, + MGMT_OP_USER_CONFIRM_NEG_REPLY); +} -- cgit v1.2.3 From ac56fb13c0508181b4227b8ada6d47aaaf72794c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 19 Feb 2011 12:05:59 -0300 Subject: Bluetooth: Fix mgmt_pin_code_reply return parameters The command complete event for mgmt_pin_code_reply & mgmt_pin_code_neg_reply should have the adapter index, Bluetooth address as well as the status. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/mgmt.h | 5 +++++ net/bluetooth/mgmt.c | 23 +++++++++++++---------- 2 files changed, 18 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 52376a3295ca..5aee200e5e36 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -147,6 +147,11 @@ struct mgmt_cp_pin_code_reply { __u8 pin_len; __u8 pin_code[16]; } __packed; +struct mgmt_rp_pin_code_reply { + __le16 index; + bdaddr_t bdaddr; + uint8_t status; +} __packed; #define MGMT_OP_PIN_CODE_NEG_REPLY 0x0012 struct mgmt_cp_pin_code_neg_reply { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d1d9b8c3a1b0..0d3d613baac2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1558,17 +1558,18 @@ int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr) int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) { struct pending_cmd *cmd; + struct mgmt_rp_pin_code_reply rp; int err; cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, index); if (!cmd) return -ENOENT; - if (status != 0) - err = cmd_status(cmd->sk, MGMT_OP_PIN_CODE_REPLY, status); - else - err = cmd_complete(cmd->sk, MGMT_OP_PIN_CODE_REPLY, - bdaddr, sizeof(*bdaddr)); + put_unaligned_le16(index, &rp.index); + bacpy(&rp.bdaddr, bdaddr); + rp.status = status; + + err = cmd_complete(cmd->sk, MGMT_OP_PIN_CODE_REPLY, &rp, sizeof(rp)); list_del(&cmd->list); mgmt_pending_free(cmd); @@ -1579,17 +1580,19 @@ int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) { struct pending_cmd *cmd; + struct mgmt_rp_pin_code_reply rp; int err; cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, index); if (!cmd) return -ENOENT; - if (status != 0) - err = cmd_status(cmd->sk, MGMT_OP_PIN_CODE_NEG_REPLY, status); - else - err = cmd_complete(cmd->sk, MGMT_OP_PIN_CODE_NEG_REPLY, - bdaddr, sizeof(*bdaddr)); + put_unaligned_le16(index, &rp.index); + bacpy(&rp.bdaddr, bdaddr); + rp.status = status; + + err = cmd_complete(cmd->sk, MGMT_OP_PIN_CODE_NEG_REPLY, + &rp, sizeof(rp)); list_del(&cmd->list); mgmt_pending_free(cmd); -- cgit v1.2.3 From 2a61169209c72317d4933f8d22f749a6a61a3d36 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 19 Feb 2011 12:06:00 -0300 Subject: Bluetooth: Add mgmt_auth_failed event To properly track bonding completion an event to indicate authentication failure is needed. This event will be sent whenever an authentication complete HCI event with a non-zero status comes. It will also be sent when we're acting in acceptor role for SSP authentication in which case the controller will send a Simple Pairing Complete event. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/mgmt.h | 7 +++++++ net/bluetooth/hci_event.c | 19 ++++++++++++++++--- net/bluetooth/mgmt.c | 11 +++++++++++ 4 files changed, 35 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 05f4706e6c34..441dadbf6a89 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -766,6 +766,7 @@ int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value); int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); +int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 5aee200e5e36..1e63c3141a78 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -264,3 +264,10 @@ struct mgmt_ev_user_confirm_request { bdaddr_t bdaddr; __le32 value; } __packed; + +#define MGMT_EV_AUTH_FAILED 0x0010 +struct mgmt_ev_auth_failed { + __le16 index; + bdaddr_t bdaddr; + __u8 status; +} __packed; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 604c7b5fee97..3fbfa50c2bff 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1424,8 +1424,10 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s if (!ev->status) { conn->link_mode |= HCI_LM_AUTH; conn->sec_level = conn->pending_sec_level; - } else + } else { + mgmt_auth_failed(hdev->id, &conn->dst, ev->status); conn->sec_level = BT_SECURITY_LOW; + } clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); @@ -2418,9 +2420,20 @@ static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_ hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (conn) - hci_conn_put(conn); + if (!conn) + goto unlock; + + /* To avoid duplicate auth_failed events to user space we check + * the HCI_CONN_AUTH_PEND flag which will be set if we + * initiated the authentication. A traditional auth_complete + * event gets always produced as initiator and is also mapped to + * the mgmt_auth_failed event */ + if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend) && ev->status != 0) + mgmt_auth_failed(hdev->id, &conn->dst, ev->status); + hci_conn_put(conn); + +unlock: hci_dev_unlock(hdev); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0d3d613baac2..46e2c39c8956 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1647,3 +1647,14 @@ int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr, return confirm_reply_complete(index, bdaddr, status, MGMT_OP_USER_CONFIRM_NEG_REPLY); } + +int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status) +{ + struct mgmt_ev_auth_failed ev; + + put_unaligned_le16(index, &ev.index); + bacpy(&ev.bdaddr, bdaddr); + ev.status = status; + + return mgmt_event(MGMT_EV_AUTH_FAILED, &ev, sizeof(ev), NULL); +} -- cgit v1.2.3 From e06383db9ec591696a06654257474b85bac1f8cb Mon Sep 17 00:00:00 2001 From: John Stultz Date: Tue, 14 Dec 2010 19:37:07 -0800 Subject: hrtimers: extend hrtimer base code to handle more then 2 clockids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hrtimer code is written mainly with CLOCK_REALTIME and CLOCK_MONOTONIC in mind. These are clockids 0 and 1 resepctively. However, if we are to introduce any new hrtimer bases, using new clockids, we have to skip the cputimers (clockids 2,3) as well as other clockids that may not impelement timers. This patch adds a little bit of indirection between the clockid and the base, so that we can extend the base by one when we add a new clockid at number 7 or so. CC: Jamie Lokier CC: Thomas Gleixner CC: Alexander Shishkin CC: Arve Hjønnevåg Signed-off-by: John Stultz --- include/linux/hrtimer.h | 6 +++++- kernel/hrtimer.c | 40 +++++++++++++++++++++++++++------------- 2 files changed, 32 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index f376ddc64c4d..20b8e6601a04 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -148,7 +148,11 @@ struct hrtimer_clock_base { #endif }; -#define HRTIMER_MAX_CLOCK_BASES 2 +enum hrtimer_base_type { + HRTIMER_BASE_REALTIME, + HRTIMER_BASE_MONOTONIC, + HRTIMER_MAX_CLOCK_BASES, +}; /* * struct hrtimer_cpu_base - the per cpu clock bases diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 57c4d33c9a9d..ca99e2454600 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -53,11 +53,10 @@ /* * The timer bases: * - * Note: If we want to add new timer bases, we have to skip the two - * clock ids captured by the cpu-timers. We do this by holding empty - * entries rather than doing math adjustment of the clock ids. - * This ensures that we capture erroneous accesses to these clock ids - * rather than moving them into the range of valid clock id's. + * There are more clockids then hrtimer bases. Thus, we index + * into the timer bases by the hrtimer_base_type enum. When trying + * to reach a base using a clockid, hrtimer_clockid_to_base() + * is used to convert from clockid to the proper hrtimer_base_type. */ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) = { @@ -77,6 +76,14 @@ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) = } }; +static int hrtimer_clock_to_base_table[MAX_CLOCKS]; + +static inline int hrtimer_clockid_to_base(clockid_t clock_id) +{ + return hrtimer_clock_to_base_table[clock_id]; +} + + /* * Get the coarse grained time at the softirq based on xtime and * wall_to_monotonic. @@ -90,8 +97,8 @@ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base) xtim = timespec_to_ktime(xts); tomono = timespec_to_ktime(tom); - base->clock_base[CLOCK_REALTIME].softirq_time = xtim; - base->clock_base[CLOCK_MONOTONIC].softirq_time = + base->clock_base[HRTIMER_BASE_REALTIME].softirq_time = xtim; + base->clock_base[HRTIMER_BASE_MONOTONIC].softirq_time = ktime_add(xtim, tomono); } @@ -179,10 +186,11 @@ switch_hrtimer_base(struct hrtimer *timer, struct hrtimer_clock_base *base, struct hrtimer_cpu_base *new_cpu_base; int this_cpu = smp_processor_id(); int cpu = hrtimer_get_target(this_cpu, pinned); + int basenum = hrtimer_clockid_to_base(base->index); again: new_cpu_base = &per_cpu(hrtimer_bases, cpu); - new_base = &new_cpu_base->clock_base[base->index]; + new_base = &new_cpu_base->clock_base[basenum]; if (base != new_base) { /* @@ -618,7 +626,7 @@ static void retrigger_next_event(void *arg) /* Adjust CLOCK_REALTIME offset */ raw_spin_lock(&base->lock); - base->clock_base[CLOCK_REALTIME].offset = + base->clock_base[HRTIMER_BASE_REALTIME].offset = timespec_to_ktime(realtime_offset); hrtimer_force_reprogram(base, 0); @@ -716,8 +724,8 @@ static int hrtimer_switch_to_hres(void) return 0; } base->hres_active = 1; - base->clock_base[CLOCK_REALTIME].resolution = KTIME_HIGH_RES; - base->clock_base[CLOCK_MONOTONIC].resolution = KTIME_HIGH_RES; + base->clock_base[HRTIMER_BASE_REALTIME].resolution = KTIME_HIGH_RES; + base->clock_base[HRTIMER_BASE_MONOTONIC].resolution = KTIME_HIGH_RES; tick_setup_sched_timer(); @@ -1112,6 +1120,7 @@ static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id, enum hrtimer_mode mode) { struct hrtimer_cpu_base *cpu_base; + int base; memset(timer, 0, sizeof(struct hrtimer)); @@ -1120,7 +1129,8 @@ static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id, if (clock_id == CLOCK_REALTIME && mode != HRTIMER_MODE_ABS) clock_id = CLOCK_MONOTONIC; - timer->base = &cpu_base->clock_base[clock_id]; + base = hrtimer_clockid_to_base(clock_id); + timer->base = &cpu_base->clock_base[base]; hrtimer_init_timer_hres(timer); timerqueue_init(&timer->node); @@ -1156,9 +1166,10 @@ EXPORT_SYMBOL_GPL(hrtimer_init); int hrtimer_get_res(const clockid_t which_clock, struct timespec *tp) { struct hrtimer_cpu_base *cpu_base; + int base = hrtimer_clockid_to_base(which_clock); cpu_base = &__raw_get_cpu_var(hrtimer_bases); - *tp = ktime_to_timespec(cpu_base->clock_base[which_clock].resolution); + *tp = ktime_to_timespec(cpu_base->clock_base[base].resolution); return 0; } @@ -1705,6 +1716,9 @@ static struct notifier_block __cpuinitdata hrtimers_nb = { void __init hrtimers_init(void) { + hrtimer_clock_to_base_table[CLOCK_REALTIME] = HRTIMER_BASE_REALTIME; + hrtimer_clock_to_base_table[CLOCK_MONOTONIC] = HRTIMER_BASE_MONOTONIC; + hrtimer_cpu_notify(&hrtimers_nb, (unsigned long)CPU_UP_PREPARE, (void *)(long)smp_processor_id()); register_cpu_notifier(&hrtimers_nb); -- cgit v1.2.3 From abb3a4ea2e0ea7114a4475745da2f32bd9ad5b73 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 14 Feb 2011 17:52:09 -0800 Subject: time: Introduce get_monotonic_boottime and ktime_get_boottime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds new functions that return the monotonic time since boot (in other words, CLOCK_MONOTONIC + suspend time). CC: Jamie Lokier CC: Thomas Gleixner CC: Alexander Shishkin CC: Arve Hjønnevåg Signed-off-by: John Stultz --- include/linux/hrtimer.h | 1 + include/linux/time.h | 1 + kernel/time/timekeeping.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 20b8e6601a04..7a9e7ee0f35c 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -312,6 +312,7 @@ static inline int hrtimer_is_hres_active(struct hrtimer *timer) extern ktime_t ktime_get(void); extern ktime_t ktime_get_real(void); +extern ktime_t ktime_get_boottime(void); DECLARE_PER_CPU(struct tick_device, tick_cpu_device); diff --git a/include/linux/time.h b/include/linux/time.h index 379b9037b5b4..fa39150cb816 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -161,6 +161,7 @@ extern void getnstime_raw_and_real(struct timespec *ts_raw, struct timespec *ts_real); extern void getboottime(struct timespec *ts); extern void monotonic_to_bootbased(struct timespec *ts); +extern void get_monotonic_boottime(struct timespec *ts); extern struct timespec timespec_trunc(struct timespec t, unsigned gran); extern int timekeeping_valid_for_hres(void); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 6262c1d18397..5fbd9aa7df95 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -907,7 +907,7 @@ static void update_wall_time(void) * getboottime - Return the real time of system boot. * @ts: pointer to the timespec to be set * - * Returns the time of day in a timespec. + * Returns the wall-time of boot in a timespec. * * This is based on the wall_to_monotonic offset and the total suspend * time. Calls to settimeofday will affect the value returned (which @@ -925,6 +925,55 @@ void getboottime(struct timespec *ts) } EXPORT_SYMBOL_GPL(getboottime); + +/** + * get_monotonic_boottime - Returns monotonic time since boot + * @ts: pointer to the timespec to be set + * + * Returns the monotonic time since boot in a timespec. + * + * This is similar to CLOCK_MONTONIC/ktime_get_ts, but also + * includes the time spent in suspend. + */ +void get_monotonic_boottime(struct timespec *ts) +{ + struct timespec tomono, sleep; + unsigned int seq; + s64 nsecs; + + WARN_ON(timekeeping_suspended); + + do { + seq = read_seqbegin(&xtime_lock); + *ts = xtime; + tomono = wall_to_monotonic; + sleep = total_sleep_time; + nsecs = timekeeping_get_ns(); + + } while (read_seqretry(&xtime_lock, seq)); + + set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec + sleep.tv_sec, + ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec + nsecs); +} +EXPORT_SYMBOL_GPL(get_monotonic_boottime); + +/** + * ktime_get_boottime - Returns monotonic time since boot in a ktime + * + * Returns the monotonic time since boot in a ktime + * + * This is similar to CLOCK_MONTONIC/ktime_get, but also + * includes the time spent in suspend. + */ +ktime_t ktime_get_boottime(void) +{ + struct timespec ts; + + get_monotonic_boottime(&ts); + return timespec_to_ktime(ts); +} +EXPORT_SYMBOL_GPL(ktime_get_boottime); + /** * monotonic_to_bootbased - Convert the monotonic time to boot based. * @ts: pointer to the timespec to be converted -- cgit v1.2.3 From 314ac37150011ebb398f522db528d2dbcc611189 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 14 Feb 2011 18:43:08 -0800 Subject: time: Extend get_xtime_and_monotonic_offset() to also return sleep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend get_xtime_and_monotonic_offset to get_xtime_and_monotonic_and_sleep_offset(). CC: Jamie Lokier CC: Thomas Gleixner CC: Alexander Shishkin CC: Arve Hjønnevåg Signed-off-by: John Stultz --- include/linux/time.h | 3 ++- kernel/hrtimer.c | 9 +++++---- kernel/time/timekeeping.c | 8 ++++++-- 3 files changed, 13 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/time.h b/include/linux/time.h index fa39150cb816..02d48fb30b41 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -124,7 +124,8 @@ 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_monotonic_coarse(void); -void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom); +void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, + struct timespec *wtom, struct timespec *sleep); #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 ca99e2454600..e8bf3ad99063 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -91,9 +91,9 @@ static inline int hrtimer_clockid_to_base(clockid_t clock_id) static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base) { ktime_t xtim, tomono; - struct timespec xts, tom; + struct timespec xts, tom, slp; - get_xtime_and_monotonic_offset(&xts, &tom); + get_xtime_and_monotonic_and_sleep_offset(&xts, &tom, &slp); xtim = timespec_to_ktime(xts); tomono = timespec_to_ktime(tom); @@ -614,12 +614,13 @@ static int hrtimer_reprogram(struct hrtimer *timer, static void retrigger_next_event(void *arg) { struct hrtimer_cpu_base *base; - struct timespec realtime_offset, wtm; + struct timespec realtime_offset, wtm, sleep; if (!hrtimer_hres_active()) return; - get_xtime_and_monotonic_offset(&realtime_offset, &wtm); + get_xtime_and_monotonic_and_sleep_offset(&realtime_offset, &wtm, + &sleep); 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 5fbd9aa7df95..3bd7e3d5c632 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1040,11 +1040,14 @@ void do_timer(unsigned long ticks) } /** - * get_xtime_and_monotonic_offset() - get xtime and wall_to_monotonic + * get_xtime_and_monotonic_and_sleep_offset() - get xtime, wall_to_monotonic, + * and sleep offsets. * @xtim: pointer to timespec to be set with xtime * @wtom: pointer to timespec to be set with wall_to_monotonic + * @sleep: pointer to timespec to be set with time in suspend */ -void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom) +void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, + struct timespec *wtom, struct timespec *sleep) { unsigned long seq; @@ -1052,6 +1055,7 @@ void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom seq = read_seqbegin(&xtime_lock); *xtim = xtime; *wtom = wall_to_monotonic; + *sleep = total_sleep_time; } while (read_seqretry(&xtime_lock, seq)); } -- cgit v1.2.3 From 70a08cca1227dc31c784ec930099a4417a06e7d0 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Tue, 15 Feb 2011 10:45:16 -0800 Subject: timers: Add CLOCK_BOOTTIME hrtimer base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CLOCK_MONOTONIC stops while the system is in suspend. This is because to applications system suspend is invisible. However, there is a growing set of applications that are wanting to be suspend-aware, but do not want to deal with the complications of CLOCK_REALTIME (which might jump around if settimeofday is called). For these applications, I propose a new clockid: CLOCK_BOOTTIME. CLOCK_BOOTTIME is idential to CLOCK_MONOTONIC, except it also includes any time spent in suspend. This patch add hrtimer base for CLOCK_BOOTTIME, using get_monotonic_boottime/ktime_get_boottime, to allow in kernel users to set timers against. CC: Jamie Lokier CC: Thomas Gleixner CC: Alexander Shishkin CC: Arve Hjønnevåg Signed-off-by: John Stultz --- include/linux/hrtimer.h | 1 + include/linux/time.h | 1 + kernel/hrtimer.c | 16 ++++++++++++---- 3 files changed, 14 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 7a9e7ee0f35c..6bc1804bfbfa 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -151,6 +151,7 @@ struct hrtimer_clock_base { enum hrtimer_base_type { HRTIMER_BASE_REALTIME, HRTIMER_BASE_MONOTONIC, + HRTIMER_BASE_BOOTTIME, HRTIMER_MAX_CLOCK_BASES, }; diff --git a/include/linux/time.h b/include/linux/time.h index 02d48fb30b41..454a26205787 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -293,6 +293,7 @@ struct itimerval { #define CLOCK_MONOTONIC_RAW 4 #define CLOCK_REALTIME_COARSE 5 #define CLOCK_MONOTONIC_COARSE 6 +#define CLOCK_BOOTTIME 7 /* * The IDs of various hardware clocks: diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index e8bf3ad99063..4c53af18734b 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -73,6 +73,11 @@ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) = .get_time = &ktime_get, .resolution = KTIME_LOW_RES, }, + { + .index = CLOCK_BOOTTIME, + .get_time = &ktime_get_boottime, + .resolution = KTIME_LOW_RES, + }, } }; @@ -90,16 +95,17 @@ static inline int hrtimer_clockid_to_base(clockid_t clock_id) */ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base) { - ktime_t xtim, tomono; + ktime_t xtim, mono, boot; struct timespec xts, tom, slp; get_xtime_and_monotonic_and_sleep_offset(&xts, &tom, &slp); xtim = timespec_to_ktime(xts); - tomono = timespec_to_ktime(tom); + mono = ktime_add(xtim, timespec_to_ktime(tom)); + boot = ktime_add(mono, timespec_to_ktime(slp)); base->clock_base[HRTIMER_BASE_REALTIME].softirq_time = xtim; - base->clock_base[HRTIMER_BASE_MONOTONIC].softirq_time = - ktime_add(xtim, tomono); + base->clock_base[HRTIMER_BASE_MONOTONIC].softirq_time = mono; + base->clock_base[HRTIMER_BASE_BOOTTIME].softirq_time = boot; } /* @@ -727,6 +733,7 @@ static int hrtimer_switch_to_hres(void) base->hres_active = 1; base->clock_base[HRTIMER_BASE_REALTIME].resolution = KTIME_HIGH_RES; base->clock_base[HRTIMER_BASE_MONOTONIC].resolution = KTIME_HIGH_RES; + base->clock_base[HRTIMER_BASE_BOOTTIME].resolution = KTIME_HIGH_RES; tick_setup_sched_timer(); @@ -1719,6 +1726,7 @@ void __init hrtimers_init(void) { hrtimer_clock_to_base_table[CLOCK_REALTIME] = HRTIMER_BASE_REALTIME; hrtimer_clock_to_base_table[CLOCK_MONOTONIC] = HRTIMER_BASE_MONOTONIC; + hrtimer_clock_to_base_table[CLOCK_BOOTTIME] = HRTIMER_BASE_BOOTTIME; hrtimer_cpu_notify(&hrtimers_nb, (unsigned long)CPU_UP_PREPARE, (void *)(long)smp_processor_id()); -- cgit v1.2.3 From 731109e78415b4cc6c2f8de6c11b37f0e40741f8 Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Sat, 19 Feb 2011 18:05:08 +0800 Subject: ipvs: use hlist instead of list Signed-off-by: Changli Gao Signed-off-by: Simon Horman --- include/net/ip_vs.h | 2 +- net/netfilter/ipvs/ip_vs_conn.c | 52 +++++++++++++++++++++++------------------ 2 files changed, 30 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 93995494dfd4..17b01b2d48f9 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -494,7 +494,7 @@ struct ip_vs_conn_param { * IP_VS structure allocated for each dynamically scheduled connection */ struct ip_vs_conn { - struct list_head c_list; /* hashed list heads */ + struct hlist_node c_list; /* hashed list heads */ #ifdef CONFIG_NET_NS struct net *net; /* Name space */ #endif diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 83233fe24a08..9c2a517b69c8 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -59,7 +59,7 @@ static int ip_vs_conn_tab_mask __read_mostly; /* * Connection hash table: for input and output packets lookups of IPVS */ -static struct list_head *ip_vs_conn_tab __read_mostly; +static struct hlist_head *ip_vs_conn_tab __read_mostly; /* SLAB cache for IPVS connections */ static struct kmem_cache *ip_vs_conn_cachep __read_mostly; @@ -201,7 +201,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp) spin_lock(&cp->lock); if (!(cp->flags & IP_VS_CONN_F_HASHED)) { - list_add(&cp->c_list, &ip_vs_conn_tab[hash]); + hlist_add_head(&cp->c_list, &ip_vs_conn_tab[hash]); cp->flags |= IP_VS_CONN_F_HASHED; atomic_inc(&cp->refcnt); ret = 1; @@ -234,7 +234,7 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp) spin_lock(&cp->lock); if (cp->flags & IP_VS_CONN_F_HASHED) { - list_del(&cp->c_list); + hlist_del(&cp->c_list); cp->flags &= ~IP_VS_CONN_F_HASHED; atomic_dec(&cp->refcnt); ret = 1; @@ -259,12 +259,13 @@ __ip_vs_conn_in_get(const struct ip_vs_conn_param *p) { unsigned hash; struct ip_vs_conn *cp; + struct hlist_node *n; hash = ip_vs_conn_hashkey_param(p, false); ct_read_lock(hash); - list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { + hlist_for_each_entry(cp, n, &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) && @@ -345,12 +346,13 @@ struct ip_vs_conn *ip_vs_ct_in_get(const struct ip_vs_conn_param *p) { unsigned hash; struct ip_vs_conn *cp; + struct hlist_node *n; hash = ip_vs_conn_hashkey_param(p, false); ct_read_lock(hash); - list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { + hlist_for_each_entry(cp, n, &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) { @@ -394,6 +396,7 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p) { unsigned hash; struct ip_vs_conn *cp, *ret=NULL; + struct hlist_node *n; /* * Check for "full" addressed entries @@ -402,7 +405,7 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p) ct_read_lock(hash); - list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { + hlist_for_each_entry(cp, n, &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) && @@ -818,7 +821,7 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, return NULL; } - INIT_LIST_HEAD(&cp->c_list); + INIT_HLIST_NODE(&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; @@ -894,8 +897,8 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, */ #ifdef CONFIG_PROC_FS struct ip_vs_iter_state { - struct seq_net_private p; - struct list_head *l; + struct seq_net_private p; + struct hlist_head *l; }; static void *ip_vs_conn_array(struct seq_file *seq, loff_t pos) @@ -903,13 +906,14 @@ 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; + struct hlist_node *n; 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) { + hlist_for_each_entry(cp, n, &ip_vs_conn_tab[idx], c_list) { if (pos-- == 0) { iter->l = &ip_vs_conn_tab[idx]; - return cp; + return cp; } } ct_read_unlock_bh(idx); @@ -930,7 +934,8 @@ static void *ip_vs_conn_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct ip_vs_conn *cp = v; struct ip_vs_iter_state *iter = seq->private; - struct list_head *e, *l = iter->l; + struct hlist_node *e; + struct hlist_head *l = iter->l; int idx; ++*pos; @@ -938,15 +943,15 @@ static void *ip_vs_conn_seq_next(struct seq_file *seq, void *v, loff_t *pos) return ip_vs_conn_array(seq, 0); /* more on same hash chain? */ - if ((e = cp->c_list.next) != l) - return list_entry(e, struct ip_vs_conn, c_list); + if ((e = cp->c_list.next)) + return hlist_entry(e, struct ip_vs_conn, c_list); idx = l - ip_vs_conn_tab; ct_read_unlock_bh(idx); while (++idx < ip_vs_conn_tab_size) { ct_read_lock_bh(idx); - list_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) { + hlist_for_each_entry(cp, e, &ip_vs_conn_tab[idx], c_list) { iter->l = &ip_vs_conn_tab[idx]; return cp; } @@ -959,7 +964,7 @@ static void *ip_vs_conn_seq_next(struct seq_file *seq, void *v, loff_t *pos) static void ip_vs_conn_seq_stop(struct seq_file *seq, void *v) { struct ip_vs_iter_state *iter = seq->private; - struct list_head *l = iter->l; + struct hlist_head *l = iter->l; if (l) ct_read_unlock_bh(l - ip_vs_conn_tab); @@ -1148,13 +1153,14 @@ void ip_vs_random_dropentry(struct net *net) */ for (idx = 0; idx < (ip_vs_conn_tab_size>>5); idx++) { unsigned hash = net_random() & ip_vs_conn_tab_mask; + struct hlist_node *n; /* * Lock is actually needed in this loop. */ ct_write_lock_bh(hash); - list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { + hlist_for_each_entry(cp, n, &ip_vs_conn_tab[hash], c_list) { if (cp->flags & IP_VS_CONN_F_TEMPLATE) /* connection template */ continue; @@ -1202,12 +1208,14 @@ static void ip_vs_conn_flush(struct net *net) flush_again: for (idx = 0; idx < ip_vs_conn_tab_size; idx++) { + struct hlist_node *n; + /* * Lock is actually needed in this loop. */ ct_write_lock_bh(idx); - list_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) { + hlist_for_each_entry(cp, n, &ip_vs_conn_tab[idx], c_list) { if (!ip_vs_conn_net_eq(cp, net)) continue; IP_VS_DBG(4, "del connection\n"); @@ -1265,8 +1273,7 @@ int __init ip_vs_conn_init(void) /* * Allocate the connection hash table and initialize its list heads */ - ip_vs_conn_tab = vmalloc(ip_vs_conn_tab_size * - sizeof(struct list_head)); + ip_vs_conn_tab = vmalloc(ip_vs_conn_tab_size * sizeof(*ip_vs_conn_tab)); if (!ip_vs_conn_tab) return -ENOMEM; @@ -1286,9 +1293,8 @@ int __init ip_vs_conn_init(void) IP_VS_DBG(0, "Each connection entry needs %Zd bytes at least\n", sizeof(struct ip_vs_conn)); - for (idx = 0; idx < ip_vs_conn_tab_size; idx++) { - INIT_LIST_HEAD(&ip_vs_conn_tab[idx]); - } + for (idx = 0; idx < ip_vs_conn_tab_size; idx++) + INIT_HLIST_HEAD(&ip_vs_conn_tab[idx]); for (idx = 0; idx < CT_LOCKARRAY_SIZE; idx++) { rwlock_init(&__ip_vs_conntbl_lock_array[idx].l); -- cgit v1.2.3 From 7fb7ba588c0f276609609565b21fcc853284a9a0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 24 May 2010 19:55:27 +0200 Subject: bq27x00: Add bq27000 support This patch adds support for the bq27000 battery to the bq27x00 driver. The bq27000 is similar to the bq27200 except that it uses the HDQ bus instead of I2C to communicate with the host system. The driver is implemented as a platform driver. The driver expects to be provided with a read callback function through its platform data. The read function is assumed to do the lowlevel HDQ handling and read out the value of a certain register. Signed-off-by: Lars-Peter Clausen Tested-by: Grazvydas Ignotas --- drivers/power/Kconfig | 14 +++ drivers/power/bq27x00_battery.c | 184 +++++++++++++++++++++++++++++++--- include/linux/power/bq27x00_battery.h | 19 ++++ 3 files changed, 202 insertions(+), 15 deletions(-) create mode 100644 include/linux/power/bq27x00_battery.h (limited to 'include') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 61bf5d724139..52a462fc6b84 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -117,10 +117,24 @@ config BATTERY_BQ20Z75 config BATTERY_BQ27x00 tristate "BQ27x00 battery driver" + help + Say Y here to enable support for batteries with BQ27x00 (I2C/HDQ) chips. + +config BATTERY_BQ27X00_I2C + bool "BQ27200/BQ27500 support" + depends on BATTERY_BQ27x00 depends on I2C + default y help Say Y here to enable support for batteries with BQ27x00 (I2C) chips. +config BATTERY_BQ27X00_PLATFORM + bool "BQ27000 support" + depends on BATTERY_BQ27x00 + default y + help + Say Y here to enable support for batteries with BQ27000 (HDQ) chips. + config BATTERY_DA9030 tristate "DA9030 battery driver" depends on PMIC_DA903X diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index def951d8fc2b..44bc76be0780 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -3,6 +3,7 @@ * * Copyright (C) 2008 Rodolfo Giometti * Copyright (C) 2008 Eurotech S.p.A. + * Copyright (C) 2010-2011 Lars-Peter Clausen * * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc. * @@ -27,6 +28,8 @@ #include #include +#include + #define DRIVER_VERSION "1.1.0" #define BQ27x00_REG_TEMP 0x06 @@ -46,12 +49,6 @@ #define BQ27000_RS 20 /* Resistor sense */ -/* If the system has several batteries we need a different name for each - * of them... - */ -static DEFINE_IDR(battery_id); -static DEFINE_MUTEX(battery_mutex); - struct bq27x00_device_info; struct bq27x00_access_methods { int (*read)(struct bq27x00_device_info *, u8 reg, int *rt_value, @@ -317,9 +314,15 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di) return 0; } -/* - * i2c specific code + +/* i2c specific code */ +#ifdef CONFIG_BATTERY_BQ27X00_I2C + +/* If the system has several batteries we need a different name for each + * of them... */ +static DEFINE_IDR(battery_id); +static DEFINE_MUTEX(battery_mutex); static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, int *rt_value, bool single) @@ -434,10 +437,6 @@ static int bq27x00_battery_remove(struct i2c_client *client) return 0; } -/* - * Module stuff - */ - static const struct i2c_device_id bq27x00_id[] = { { "bq27200", BQ27000 }, /* bq27200 is same as bq27000, but with i2c */ { "bq27500", BQ27500 }, @@ -453,13 +452,167 @@ static struct i2c_driver bq27x00_battery_driver = { .id_table = bq27x00_id, }; +static inline int bq27x00_battery_i2c_init(void) +{ + int ret = i2c_add_driver(&bq27x00_battery_driver); + if (ret) + printk(KERN_ERR "Unable to register BQ27x00 i2c driver\n"); + + return ret; +} + +static inline void bq27x00_battery_i2c_exit(void) +{ + i2c_del_driver(&bq27x00_battery_driver); +} + +#else + +static inline int bq27x00_battery_i2c_init(void) { return 0; } +static inline void bq27x00_battery_i2c_exit(void) {}; + +#endif + +/* platform specific code */ +#ifdef CONFIG_BATTERY_BQ27X00_PLATFORM + +static int bq27000_read_platform(struct bq27x00_device_info *di, u8 reg, + int *rt_value, bool single) +{ + struct device *dev = di->dev; + struct bq27000_platform_data *pdata = dev->platform_data; + unsigned int timeout = 3; + int upper, lower; + int temp; + + if (!single) { + /* Make sure the value has not changed in between reading the + * lower and the upper part */ + upper = pdata->read(dev, reg + 1); + do { + temp = upper; + if (upper < 0) + return upper; + + lower = pdata->read(dev, reg); + if (lower < 0) + return lower; + + upper = pdata->read(dev, reg + 1); + } while (temp != upper && --timeout); + + if (timeout == 0) + return -EIO; + + *rt_value = (upper << 8) | lower; + } else { + lower = pdata->read(dev, reg); + if (lower < 0) + return lower; + *rt_value = lower; + } + return 0; +} + +static int __devinit bq27000_battery_probe(struct platform_device *pdev) +{ + struct bq27x00_device_info *di; + struct bq27000_platform_data *pdata = pdev->dev.platform_data; + int ret; + + if (!pdata) { + dev_err(&pdev->dev, "no platform_data supplied\n"); + return -EINVAL; + } + + if (!pdata->read) { + dev_err(&pdev->dev, "no hdq read callback supplied\n"); + return -EINVAL; + } + + di = kzalloc(sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&pdev->dev, "failed to allocate device info data\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, di); + + di->dev = &pdev->dev; + di->chip = BQ27000; + + di->bat.name = pdata->name ?: dev_name(&pdev->dev); + di->bus.read = &bq27000_read_platform; + + ret = bq27x00_powersupply_init(di); + if (ret) + goto err_free; + + return 0; + +err_free: + platform_set_drvdata(pdev, NULL); + kfree(di); + + return ret; +} + +static int __devexit bq27000_battery_remove(struct platform_device *pdev) +{ + struct bq27x00_device_info *di = platform_get_drvdata(pdev); + + power_supply_unregister(&di->bat); + platform_set_drvdata(pdev, NULL); + kfree(di); + + return 0; +} + +static struct platform_driver bq27000_battery_driver = { + .probe = bq27000_battery_probe, + .remove = __devexit_p(bq27000_battery_remove), + .driver = { + .name = "bq27000-battery", + .owner = THIS_MODULE, + }, +}; + +static inline int bq27x00_battery_platform_init(void) +{ + int ret = platform_driver_register(&bq27000_battery_driver); + if (ret) + printk(KERN_ERR "Unable to register BQ27000 platform driver\n"); + + return ret; +} + +static inline void bq27x00_battery_platform_exit(void) +{ + platform_driver_unregister(&bq27000_battery_driver); +} + +#else + +static inline int bq27x00_battery_platform_init(void) { return 0; } +static inline void bq27x00_battery_platform_exit(void) {}; + +#endif + +/* + * Module stuff + */ + static int __init bq27x00_battery_init(void) { int ret; - ret = i2c_add_driver(&bq27x00_battery_driver); + ret = bq27x00_battery_i2c_init(); + if (ret) + return ret; + + ret = bq27x00_battery_platform_init(); if (ret) - printk(KERN_ERR "Unable to register BQ27x00 driver\n"); + bq27x00_battery_i2c_exit(); return ret; } @@ -467,7 +620,8 @@ module_init(bq27x00_battery_init); static void __exit bq27x00_battery_exit(void) { - i2c_del_driver(&bq27x00_battery_driver); + bq27x00_battery_platform_exit(); + bq27x00_battery_i2c_exit(); } module_exit(bq27x00_battery_exit); diff --git a/include/linux/power/bq27x00_battery.h b/include/linux/power/bq27x00_battery.h new file mode 100644 index 000000000000..a857f719bf40 --- /dev/null +++ b/include/linux/power/bq27x00_battery.h @@ -0,0 +1,19 @@ +#ifndef __LINUX_BQ27X00_BATTERY_H__ +#define __LINUX_BQ27X00_BATTERY_H__ + +/** + * struct bq27000_plaform_data - Platform data for bq27000 devices + * @name: Name of the battery. If NULL the driver will fallback to "bq27000". + * @read: HDQ read callback. + * This function should provide access to the HDQ bus the battery is + * connected to. + * The first parameter is a pointer to the battery device, the second the + * register to be read. The return value should either be the content of + * the passed register or an error value. + */ +struct bq27000_platform_data { + const char *name; + int (*read)(struct device *dev, unsigned int); +}; + +#endif -- cgit v1.2.3 From eaefd1105bc431ef329599e307a07f2a36ae7872 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 18 Feb 2011 03:26:36 +0000 Subject: net: add __rcu annotations to sk_wq and wq Add proper RCU annotations/verbs to sk_wq and wq members Fix __sctp_write_space() sk_sleep() abuse (and sock->wq access) Fix sunrpc sk_sleep() abuse too Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/net.h | 3 ++- include/net/sock.h | 7 ++++--- net/sctp/socket.c | 9 +++++---- net/socket.c | 23 ++++++++++++++--------- net/sunrpc/svcsock.c | 32 ++++++++++++++++++++------------ net/unix/af_unix.c | 2 +- 6 files changed, 46 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/linux/net.h b/include/linux/net.h index 16faa130088c..94de83c0f877 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -118,6 +118,7 @@ enum sock_shutdown_cmd { }; struct socket_wq { + /* Note: wait MUST be first field of socket_wq */ wait_queue_head_t wait; struct fasync_struct *fasync_list; struct rcu_head rcu; @@ -142,7 +143,7 @@ struct socket { unsigned long flags; - struct socket_wq *wq; + struct socket_wq __rcu *wq; struct file *file; struct sock *sk; diff --git a/include/net/sock.h b/include/net/sock.h index e3893a2b5d25..da0534d3401c 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -281,7 +281,7 @@ struct sock { int sk_rcvbuf; struct sk_filter __rcu *sk_filter; - struct socket_wq *sk_wq; + struct socket_wq __rcu *sk_wq; #ifdef CONFIG_NET_DMA struct sk_buff_head sk_async_wait_queue; @@ -1266,7 +1266,8 @@ static inline void sk_set_socket(struct sock *sk, struct socket *sock) static inline wait_queue_head_t *sk_sleep(struct sock *sk) { - return &sk->sk_wq->wait; + BUILD_BUG_ON(offsetof(struct socket_wq, wait) != 0); + return &rcu_dereference_raw(sk->sk_wq)->wait; } /* Detach socket from process context. * Announce socket dead, detach it from wait queue and inode. @@ -1287,7 +1288,7 @@ static inline void sock_orphan(struct sock *sk) static inline void sock_graft(struct sock *sk, struct socket *parent) { write_lock_bh(&sk->sk_callback_lock); - rcu_assign_pointer(sk->sk_wq, parent->wq); + sk->sk_wq = parent->wq; parent->sk = sk; sk_set_socket(sk, parent); security_sock_graft(sk, parent); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 8e02550ff3e8..b53b2ebbb198 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -6102,15 +6102,16 @@ static void __sctp_write_space(struct sctp_association *asoc) wake_up_interruptible(&asoc->wait); if (sctp_writeable(sk)) { - if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) - wake_up_interruptible(sk_sleep(sk)); + wait_queue_head_t *wq = sk_sleep(sk); + + if (wq && waitqueue_active(wq)) + wake_up_interruptible(wq); /* Note that we try to include the Async I/O support * here by modeling from the current TCP/UDP code. * We have not tested with it yet. */ - if (sock->wq->fasync_list && - !(sk->sk_shutdown & SEND_SHUTDOWN)) + if (!(sk->sk_shutdown & SEND_SHUTDOWN)) sock_wake_async(sock, SOCK_WAKE_SPACE, POLL_OUT); } diff --git a/net/socket.c b/net/socket.c index ac2219f90d5d..9fa1e3b4366e 100644 --- a/net/socket.c +++ b/net/socket.c @@ -240,17 +240,19 @@ static struct kmem_cache *sock_inode_cachep __read_mostly; static struct inode *sock_alloc_inode(struct super_block *sb) { struct socket_alloc *ei; + struct socket_wq *wq; ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL); if (!ei) return NULL; - ei->socket.wq = kmalloc(sizeof(struct socket_wq), GFP_KERNEL); - if (!ei->socket.wq) { + wq = kmalloc(sizeof(*wq), GFP_KERNEL); + if (!wq) { kmem_cache_free(sock_inode_cachep, ei); return NULL; } - init_waitqueue_head(&ei->socket.wq->wait); - ei->socket.wq->fasync_list = NULL; + init_waitqueue_head(&wq->wait); + wq->fasync_list = NULL; + RCU_INIT_POINTER(ei->socket.wq, wq); ei->socket.state = SS_UNCONNECTED; ei->socket.flags = 0; @@ -273,9 +275,11 @@ static void wq_free_rcu(struct rcu_head *head) static void sock_destroy_inode(struct inode *inode) { struct socket_alloc *ei; + struct socket_wq *wq; ei = container_of(inode, struct socket_alloc, vfs_inode); - call_rcu(&ei->socket.wq->rcu, wq_free_rcu); + wq = rcu_dereference_protected(ei->socket.wq, 1); + call_rcu(&wq->rcu, wq_free_rcu); kmem_cache_free(sock_inode_cachep, ei); } @@ -524,7 +528,7 @@ void sock_release(struct socket *sock) module_put(owner); } - if (sock->wq->fasync_list) + if (rcu_dereference_protected(sock->wq, 1)->fasync_list) printk(KERN_ERR "sock_release: fasync list not empty!\n"); percpu_sub(sockets_in_use, 1); @@ -1108,15 +1112,16 @@ static int sock_fasync(int fd, struct file *filp, int on) { struct socket *sock = filp->private_data; struct sock *sk = sock->sk; + struct socket_wq *wq; if (sk == NULL) return -EINVAL; lock_sock(sk); + wq = rcu_dereference_protected(sock->wq, sock_owned_by_user(sk)); + fasync_helper(fd, filp, on, &wq->fasync_list); - fasync_helper(fd, filp, on, &sock->wq->fasync_list); - - if (!sock->wq->fasync_list) + if (!wq->fasync_list) sock_reset_flag(sk, SOCK_FASYNC); else sock_set_flag(sk, SOCK_FASYNC); diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index d802e941d365..b7d435c3f19e 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -420,6 +420,7 @@ static void svc_sock_setbufsize(struct socket *sock, unsigned int snd, static void svc_udp_data_ready(struct sock *sk, int count) { struct svc_sock *svsk = (struct svc_sock *)sk->sk_user_data; + wait_queue_head_t *wq = sk_sleep(sk); if (svsk) { dprintk("svc: socket %p(inet %p), count=%d, busy=%d\n", @@ -428,8 +429,8 @@ static void svc_udp_data_ready(struct sock *sk, int count) set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); svc_xprt_enqueue(&svsk->sk_xprt); } - if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) - wake_up_interruptible(sk_sleep(sk)); + if (wq && waitqueue_active(wq)) + wake_up_interruptible(wq); } /* @@ -438,6 +439,7 @@ static void svc_udp_data_ready(struct sock *sk, int count) static void svc_write_space(struct sock *sk) { struct svc_sock *svsk = (struct svc_sock *)(sk->sk_user_data); + wait_queue_head_t *wq = sk_sleep(sk); if (svsk) { dprintk("svc: socket %p(inet %p), write_space busy=%d\n", @@ -445,10 +447,10 @@ static void svc_write_space(struct sock *sk) svc_xprt_enqueue(&svsk->sk_xprt); } - if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) { + if (wq && waitqueue_active(wq)) { dprintk("RPC svc_write_space: someone sleeping on %p\n", svsk); - wake_up_interruptible(sk_sleep(sk)); + wake_up_interruptible(wq); } } @@ -739,6 +741,7 @@ static void svc_udp_init(struct svc_sock *svsk, struct svc_serv *serv) static void svc_tcp_listen_data_ready(struct sock *sk, int count_unused) { struct svc_sock *svsk = (struct svc_sock *)sk->sk_user_data; + wait_queue_head_t *wq; dprintk("svc: socket %p TCP (listen) state change %d\n", sk, sk->sk_state); @@ -761,8 +764,9 @@ static void svc_tcp_listen_data_ready(struct sock *sk, int count_unused) printk("svc: socket %p: no user data\n", sk); } - if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) - wake_up_interruptible_all(sk_sleep(sk)); + wq = sk_sleep(sk); + if (wq && waitqueue_active(wq)) + wake_up_interruptible_all(wq); } /* @@ -771,6 +775,7 @@ static void svc_tcp_listen_data_ready(struct sock *sk, int count_unused) static void svc_tcp_state_change(struct sock *sk) { struct svc_sock *svsk = (struct svc_sock *)sk->sk_user_data; + wait_queue_head_t *wq = sk_sleep(sk); dprintk("svc: socket %p TCP (connected) state change %d (svsk %p)\n", sk, sk->sk_state, sk->sk_user_data); @@ -781,13 +786,14 @@ static void svc_tcp_state_change(struct sock *sk) set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags); svc_xprt_enqueue(&svsk->sk_xprt); } - if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) - wake_up_interruptible_all(sk_sleep(sk)); + if (wq && waitqueue_active(wq)) + wake_up_interruptible_all(wq); } static void svc_tcp_data_ready(struct sock *sk, int count) { struct svc_sock *svsk = (struct svc_sock *)sk->sk_user_data; + wait_queue_head_t *wq = sk_sleep(sk); dprintk("svc: socket %p TCP data ready (svsk %p)\n", sk, sk->sk_user_data); @@ -795,8 +801,8 @@ static void svc_tcp_data_ready(struct sock *sk, int count) set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); svc_xprt_enqueue(&svsk->sk_xprt); } - if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) - wake_up_interruptible(sk_sleep(sk)); + if (wq && waitqueue_active(wq)) + wake_up_interruptible(wq); } /* @@ -1531,6 +1537,7 @@ static void svc_sock_detach(struct svc_xprt *xprt) { struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt); struct sock *sk = svsk->sk_sk; + wait_queue_head_t *wq; dprintk("svc: svc_sock_detach(%p)\n", svsk); @@ -1539,8 +1546,9 @@ static void svc_sock_detach(struct svc_xprt *xprt) sk->sk_data_ready = svsk->sk_odata; sk->sk_write_space = svsk->sk_owspace; - if (sk_sleep(sk) && waitqueue_active(sk_sleep(sk))) - wake_up_interruptible(sk_sleep(sk)); + wq = sk_sleep(sk); + if (wq && waitqueue_active(wq)) + wake_up_interruptible(wq); } /* diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index d8d98d5b508c..217fb7f34d52 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1171,7 +1171,7 @@ restart: newsk->sk_type = sk->sk_type; init_peercred(newsk); newu = unix_sk(newsk); - newsk->sk_wq = &newu->peer_wq; + RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq); otheru = unix_sk(other); /* copy address information from listening to new sock*/ -- cgit v1.2.3 From 5a893fc28f0393adb7c885a871b8c59e623fd528 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Tue, 22 Feb 2011 13:24:32 -0500 Subject: ttm: Include the 'struct dev' when using the DMA API. This makes the accounting when using 'debug_dma_dump_mappings()' and CONFIG_DMA_API_DEBUG=y be assigned to the correct device instead of 'fallback'. No functional change - just cosmetic. Signed-off-by: Konrad Rzeszutek Wilk --- drivers/gpu/drm/nouveau/nouveau_mem.c | 1 + drivers/gpu/drm/radeon/radeon_ttm.c | 1 + drivers/gpu/drm/ttm/ttm_page_alloc.c | 11 ++++++----- drivers/gpu/drm/ttm/ttm_tt.c | 4 ++-- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 2 +- include/drm/ttm/ttm_bo_driver.h | 1 + include/drm/ttm/ttm_page_alloc.h | 8 ++++++-- 7 files changed, 18 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index a163c7c612e7..931b22142ed2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -559,6 +559,7 @@ nouveau_mem_vram_init(struct drm_device *dev) if (ret) return ret; + dev_priv->ttm.bdev.dev = dev->dev; ret = ttm_bo_device_init(&dev_priv->ttm.bdev, dev_priv->ttm.bo_global_ref.ref.object, &nouveau_bo_driver, DRM_FILE_PAGE_OFFSET, diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index ca045058e498..cfe223f22394 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -513,6 +513,7 @@ int radeon_ttm_init(struct radeon_device *rdev) if (r) { return r; } + rdev->mman.bdev.dev = rdev->dev; /* No others user of address space so set it to 0 */ r = ttm_bo_device_init(&rdev->mman.bdev, rdev->mman.bo_global_ref.ref.object, diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c index 737a2a2e46a5..35849dbf3ab5 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c @@ -664,7 +664,7 @@ out: */ int ttm_get_pages(struct list_head *pages, int flags, enum ttm_caching_state cstate, unsigned count, - dma_addr_t *dma_address) + dma_addr_t *dma_address, struct device *dev) { struct ttm_page_pool *pool = ttm_get_pool(flags, cstate); struct page *p = NULL; @@ -685,7 +685,7 @@ int ttm_get_pages(struct list_head *pages, int flags, for (r = 0; r < count; ++r) { if ((flags & TTM_PAGE_FLAG_DMA32) && dma_address) { void *addr; - addr = dma_alloc_coherent(NULL, PAGE_SIZE, + addr = dma_alloc_coherent(dev, PAGE_SIZE, &dma_address[r], gfp_flags); if (addr == NULL) @@ -730,7 +730,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, NULL); + ttm_put_pages(pages, 0, flags, cstate, NULL, NULL); return r; } } @@ -741,7 +741,8 @@ 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, dma_addr_t *dma_address) + enum ttm_caching_state cstate, dma_addr_t *dma_address, + struct device *dev) { unsigned long irq_flags; struct ttm_page_pool *pool = ttm_get_pool(flags, cstate); @@ -757,7 +758,7 @@ void ttm_put_pages(struct list_head *pages, unsigned page_count, int flags, void *addr = page_address(p); WARN_ON(!addr || !dma_address[r]); if (addr) - dma_free_coherent(NULL, PAGE_SIZE, + dma_free_coherent(dev, PAGE_SIZE, addr, dma_address[r]); dma_address[r] = 0; diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index 86d5b1745a45..0f8fc9ff0c53 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -110,7 +110,7 @@ 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, - &ttm->dma_address[index]); + &ttm->dma_address[index], ttm->be->bdev->dev); if (ret != 0) return NULL; @@ -304,7 +304,7 @@ static void ttm_tt_free_alloced_pages(struct ttm_tt *ttm) } } ttm_put_pages(&h, count, ttm->page_flags, ttm->caching_state, - ttm->dma_address); + ttm->dma_address, ttm->be->bdev->dev); ttm->state = tt_unpopulated; ttm->first_himem_page = ttm->num_pages; ttm->last_lomem_page = -1; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 10ca97ee0206..4a8c7893e8ff 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -322,7 +322,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM); dev_priv->active_master = &dev_priv->fbdev_master; - + dev_priv->bdev.dev = dev->dev; ret = ttm_bo_device_init(&dev_priv->bdev, dev_priv->bo_global_ref.ref.object, &vmw_bo_driver, VMWGFX_FILE_PAGE_OFFSET, diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index ebcd3dd7203b..4d97014e8c8d 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -533,6 +533,7 @@ struct ttm_bo_device { struct list_head device_list; struct ttm_bo_global *glob; struct ttm_bo_driver *driver; + struct device *dev; rwlock_t vm_lock; struct ttm_mem_type_manager man[TTM_NUM_MEM_TYPES]; /* diff --git a/include/drm/ttm/ttm_page_alloc.h b/include/drm/ttm/ttm_page_alloc.h index 8062890f725e..ccb6b7a240e2 100644 --- a/include/drm/ttm/ttm_page_alloc.h +++ b/include/drm/ttm/ttm_page_alloc.h @@ -37,12 +37,14 @@ * @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). + * @dev: struct device for appropiate DMA accounting. */ int ttm_get_pages(struct list_head *pages, int flags, enum ttm_caching_state cstate, unsigned count, - dma_addr_t *dma_address); + dma_addr_t *dma_address, + struct device *dev); /** * Put linked list of pages to pool. * @@ -52,12 +54,14 @@ int ttm_get_pages(struct list_head *pages, * @flags: ttm flags for page allocation. * @cstate: ttm caching state. * @dma_address: The DMA (bus) address of pages (if TTM_PAGE_FLAG_DMA32 set). + * @dev: struct device for appropiate DMA accounting. */ void ttm_put_pages(struct list_head *pages, unsigned page_count, int flags, enum ttm_caching_state cstate, - dma_addr_t *dma_address); + dma_addr_t *dma_address, + struct device *dev); /** * Initialize pool allocator. */ -- cgit v1.2.3 From 9b7c525dfaa9a1b5f01db1f3a1edc50bbb6eb739 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Feb 2011 20:05:44 -0800 Subject: ASoC: Support WM8958 direct microphone detection IRQ Allow direct routing of the WM8958 microphone detection signal to a GPIO to be used, saving the need to demux the interrupt. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/linux/mfd/wm8994/pdata.h | 5 ++++ sound/soc/codecs/wm8994.c | 57 ++++++++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index 9eab263658be..06869466b7f0 100644 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -103,6 +103,11 @@ struct wm8994_pdata { unsigned int lineout1fb:1; unsigned int lineout2fb:1; + /* IRQ for microphone detection if brought out directly as a + * signal. + */ + int micdet_irq; + /* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */ unsigned int micbias1_lvl:1; unsigned int micbias2_lvl:1; diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index b23e91027d64..1ad6e3db7804 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -104,6 +104,7 @@ struct wm8994_priv { void *jack_cb_data; bool jack_is_mic; bool jack_is_video; + int micdet_irq; int revision; struct wm8994_pdata *pdata; @@ -3102,6 +3103,12 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994->pdata = dev_get_platdata(codec->dev->parent); wm8994->codec = codec; + if (wm8994->pdata && wm8994->pdata->micdet_irq) + wm8994->micdet_irq = wm8994->pdata->micdet_irq; + else if (wm8994->pdata && wm8994->pdata->irq_base) + wm8994->micdet_irq = wm8994->pdata->irq_base + + WM8994_IRQ_MIC1_DET; + pm_runtime_enable(codec->dev); pm_runtime_resume(codec->dev); @@ -3150,14 +3157,17 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) switch (control->type) { case WM8994: - ret = wm8994_request_irq(codec->control_data, - WM8994_IRQ_MIC1_DET, - wm8994_mic_irq, "Mic 1 detect", - wm8994); - if (ret != 0) - dev_warn(codec->dev, - "Failed to request Mic1 detect IRQ: %d\n", - ret); + if (wm8994->micdet_irq) { + ret = request_threaded_irq(wm8994->micdet_irq, NULL, + wm8994_mic_irq, + IRQF_TRIGGER_RISING, + "Mic1 detect", + wm8994); + if (ret != 0) + dev_warn(codec->dev, + "Failed to request Mic1 detect IRQ: %d\n", + ret); + } ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, @@ -3188,15 +3198,17 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) break; case WM8958: - ret = wm8994_request_irq(codec->control_data, - WM8994_IRQ_MIC1_DET, - wm8958_mic_irq, "Mic detect", - wm8994); - if (ret != 0) - dev_warn(codec->dev, - "Failed to request Mic detect IRQ: %d\n", - ret); - break; + if (wm8994->micdet_irq) { + ret = request_threaded_irq(wm8994->micdet_irq, NULL, + wm8958_mic_irq, + IRQF_TRIGGER_RISING, + "Mic detect", + wm8994); + if (ret != 0) + dev_warn(codec->dev, + "Failed to request Mic detect IRQ: %d\n", + ret); + } } /* Remember if AIFnLRCLK is configured as a GPIO. This should be @@ -3328,7 +3340,8 @@ err_irq: wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, wm8994); - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, wm8994); + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); err: kfree(wm8994); return ret; @@ -3345,8 +3358,8 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) switch (control->type) { case WM8994: - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, - wm8994); + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, @@ -3356,8 +3369,8 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) break; case WM8958: - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, - wm8994); + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); break; } kfree(wm8994->retune_mobile_texts); -- cgit v1.2.3 From 48e028eccabc9c246bfad175262582a1ce34a316 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 21 Feb 2011 17:11:59 -0800 Subject: ASoC: Support configuration of WM8958 microphone bias analogue parameters The WM8958 has a different microphone bias architecture to WM8994 so needs different configuration to WM8994. Support this in platform data. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/linux/mfd/wm8994/pdata.h | 7 +++++-- include/linux/mfd/wm8994/registers.h | 2 ++ sound/soc/codecs/wm8994.c | 7 +++++++ 3 files changed, 14 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index 06869466b7f0..466b1c777aff 100644 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -108,13 +108,16 @@ struct wm8994_pdata { */ int micdet_irq; - /* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */ + /* WM8994 microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */ unsigned int micbias1_lvl:1; unsigned int micbias2_lvl:1; - /* Jack detect threashold levels, see datasheet for values */ + /* WM8994 jack detect threashold levels, see datasheet for values */ unsigned int jd_scthr:2; unsigned int jd_thr:2; + + /* WM8958 microphone bias configuration */ + int micbias[2]; }; #endif diff --git a/include/linux/mfd/wm8994/registers.h b/include/linux/mfd/wm8994/registers.h index be072faec6f0..f3ee84284670 100644 --- a/include/linux/mfd/wm8994/registers.h +++ b/include/linux/mfd/wm8994/registers.h @@ -63,6 +63,8 @@ #define WM8994_MICBIAS 0x3A #define WM8994_LDO_1 0x3B #define WM8994_LDO_2 0x3C +#define WM8958_MICBIAS1 0x3D +#define WM8958_MICBIAS2 0x3E #define WM8994_CHARGE_PUMP_1 0x4C #define WM8958_CHARGE_PUMP_2 0x4D #define WM8994_CLASS_W_1 0x51 diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 1ad6e3db7804..9b9c15ffb7d2 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2855,6 +2855,13 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994) else snd_soc_add_controls(wm8994->codec, wm8994_eq_controls, ARRAY_SIZE(wm8994_eq_controls)); + + for (i = 0; i < ARRAY_SIZE(pdata->micbias); i++) { + if (pdata->micbias[i]) { + snd_soc_write(codec, WM8958_MICBIAS1 + i, + pdata->micbias[i] & 0xffff); + } + } } /** -- cgit v1.2.3 From ce792580ea2ce6f7259b45124e9ccc4574c31606 Mon Sep 17 00:00:00 2001 From: Thomas Chou Date: Mon, 14 Feb 2011 10:20:39 +0800 Subject: spi: add OpenCores tiny SPI driver This patch adds support of OpenCores tiny SPI driver. http://opencores.org/project,tiny_spi Signed-off-by: Thomas Chou Signed-off-by: Grant Likely --- .../devicetree/bindings/spi/spi_oc_tiny.txt | 12 + drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/spi_oc_tiny.c | 425 +++++++++++++++++++++ include/linux/spi/spi_oc_tiny.h | 20 + 5 files changed, 465 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi_oc_tiny.txt create mode 100644 drivers/spi/spi_oc_tiny.c create mode 100644 include/linux/spi/spi_oc_tiny.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/spi/spi_oc_tiny.txt b/Documentation/devicetree/bindings/spi/spi_oc_tiny.txt new file mode 100644 index 000000000000..d95c0b367a04 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi_oc_tiny.txt @@ -0,0 +1,12 @@ +OpenCores tiny SPI + +Required properties: +- compatible : should be "opencores,tiny-spi-rtlsvn2". +- gpios : should specify GPIOs used for chipselect. +Optional properties: +- clock-frequency : input clock frequency to the core. +- baud-width: width, in bits, of the programmable divider used to scale + the input clock to SCLK. + +The clock-frequency and baud-width properties are needed only if the divider +is programmable. They are not needed if the divider is fixed. diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index bb233a9cbad2..8faa57a58dd1 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -231,6 +231,13 @@ config SPI_FSL_ESPI From MPC8536, 85xx platform uses the controller, and all P10xx, P20xx, P30xx,P40xx, P50xx uses this controller. +config SPI_OC_TINY + tristate "OpenCores tiny SPI" + depends on GENERIC_GPIO + select SPI_BITBANG + help + This is the driver for OpenCores tiny SPI master controller. + config SPI_OMAP_UWIRE tristate "OMAP1 MicroWire" depends on ARCH_OMAP1 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 86d1b5f9bbd9..c7e4c23c6f36 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_SPI_IMX) += spi_imx.o obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o obj-$(CONFIG_SPI_PXA2XX_PCI) += pxa2xx_spi_pci.o +obj-$(CONFIG_SPI_OC_TINY) += spi_oc_tiny.o obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o obj-$(CONFIG_SPI_OMAP_100K) += omap_spi_100k.o diff --git a/drivers/spi/spi_oc_tiny.c b/drivers/spi/spi_oc_tiny.c new file mode 100644 index 000000000000..f1bde66cea19 --- /dev/null +++ b/drivers/spi/spi_oc_tiny.c @@ -0,0 +1,425 @@ +/* + * OpenCores tiny SPI master driver + * + * http://opencores.org/project,tiny_spi + * + * Copyright (C) 2011 Thomas Chou + * + * Based on spi_s3c24xx.c, which is: + * Copyright (c) 2006 Ben Dooks + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks + * + * 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 +#include + +#define DRV_NAME "spi_oc_tiny" + +#define TINY_SPI_RXDATA 0 +#define TINY_SPI_TXDATA 4 +#define TINY_SPI_STATUS 8 +#define TINY_SPI_CONTROL 12 +#define TINY_SPI_BAUD 16 + +#define TINY_SPI_STATUS_TXE 0x1 +#define TINY_SPI_STATUS_TXR 0x2 + +struct tiny_spi { + /* bitbang has to be first */ + struct spi_bitbang bitbang; + struct completion done; + + void __iomem *base; + int irq; + unsigned int freq; + unsigned int baudwidth; + unsigned int baud; + unsigned int speed_hz; + unsigned int mode; + unsigned int len; + unsigned int txc, rxc; + const u8 *txp; + u8 *rxp; + unsigned int gpio_cs_count; + int *gpio_cs; +}; + +static inline struct tiny_spi *tiny_spi_to_hw(struct spi_device *sdev) +{ + return spi_master_get_devdata(sdev->master); +} + +static unsigned int tiny_spi_baud(struct spi_device *spi, unsigned int hz) +{ + struct tiny_spi *hw = tiny_spi_to_hw(spi); + + return min(DIV_ROUND_UP(hw->freq, hz * 2), (1U << hw->baudwidth)) - 1; +} + +static void tiny_spi_chipselect(struct spi_device *spi, int is_active) +{ + struct tiny_spi *hw = tiny_spi_to_hw(spi); + + if (hw->gpio_cs_count) { + gpio_set_value(hw->gpio_cs[spi->chip_select], + (spi->mode & SPI_CS_HIGH) ? is_active : !is_active); + } +} + +static int tiny_spi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct tiny_spi *hw = tiny_spi_to_hw(spi); + unsigned int baud = hw->baud; + + if (t) { + if (t->speed_hz && t->speed_hz != hw->speed_hz) + baud = tiny_spi_baud(spi, t->speed_hz); + } + writel(baud, hw->base + TINY_SPI_BAUD); + writel(hw->mode, hw->base + TINY_SPI_CONTROL); + return 0; +} + +static int tiny_spi_setup(struct spi_device *spi) +{ + struct tiny_spi *hw = tiny_spi_to_hw(spi); + + if (spi->max_speed_hz != hw->speed_hz) { + hw->speed_hz = spi->max_speed_hz; + hw->baud = tiny_spi_baud(spi, hw->speed_hz); + } + hw->mode = spi->mode & (SPI_CPOL | SPI_CPHA); + return 0; +} + +static inline void tiny_spi_wait_txr(struct tiny_spi *hw) +{ + while (!(readb(hw->base + TINY_SPI_STATUS) & + TINY_SPI_STATUS_TXR)) + cpu_relax(); +} + +static inline void tiny_spi_wait_txe(struct tiny_spi *hw) +{ + while (!(readb(hw->base + TINY_SPI_STATUS) & + TINY_SPI_STATUS_TXE)) + cpu_relax(); +} + +static int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) +{ + struct tiny_spi *hw = tiny_spi_to_hw(spi); + const u8 *txp = t->tx_buf; + u8 *rxp = t->rx_buf; + unsigned int i; + + if (hw->irq >= 0) { + /* use intrrupt driven data transfer */ + hw->len = t->len; + hw->txp = t->tx_buf; + hw->rxp = t->rx_buf; + hw->txc = 0; + hw->rxc = 0; + + /* send the first byte */ + if (t->len > 1) { + writeb(hw->txp ? *hw->txp++ : 0, + hw->base + TINY_SPI_TXDATA); + hw->txc++; + writeb(hw->txp ? *hw->txp++ : 0, + hw->base + TINY_SPI_TXDATA); + hw->txc++; + writeb(TINY_SPI_STATUS_TXR, hw->base + TINY_SPI_STATUS); + } else { + writeb(hw->txp ? *hw->txp++ : 0, + hw->base + TINY_SPI_TXDATA); + hw->txc++; + writeb(TINY_SPI_STATUS_TXE, hw->base + TINY_SPI_STATUS); + } + + wait_for_completion(&hw->done); + } else if (txp && rxp) { + /* we need to tighten the transfer loop */ + writeb(*txp++, hw->base + TINY_SPI_TXDATA); + if (t->len > 1) { + writeb(*txp++, hw->base + TINY_SPI_TXDATA); + for (i = 2; i < t->len; i++) { + u8 rx, tx = *txp++; + tiny_spi_wait_txr(hw); + rx = readb(hw->base + TINY_SPI_TXDATA); + writeb(tx, hw->base + TINY_SPI_TXDATA); + *rxp++ = rx; + } + tiny_spi_wait_txr(hw); + *rxp++ = readb(hw->base + TINY_SPI_TXDATA); + } + tiny_spi_wait_txe(hw); + *rxp++ = readb(hw->base + TINY_SPI_RXDATA); + } else if (rxp) { + writeb(0, hw->base + TINY_SPI_TXDATA); + if (t->len > 1) { + writeb(0, + hw->base + TINY_SPI_TXDATA); + for (i = 2; i < t->len; i++) { + u8 rx; + tiny_spi_wait_txr(hw); + rx = readb(hw->base + TINY_SPI_TXDATA); + writeb(0, hw->base + TINY_SPI_TXDATA); + *rxp++ = rx; + } + tiny_spi_wait_txr(hw); + *rxp++ = readb(hw->base + TINY_SPI_TXDATA); + } + tiny_spi_wait_txe(hw); + *rxp++ = readb(hw->base + TINY_SPI_RXDATA); + } else if (txp) { + writeb(*txp++, hw->base + TINY_SPI_TXDATA); + if (t->len > 1) { + writeb(*txp++, hw->base + TINY_SPI_TXDATA); + for (i = 2; i < t->len; i++) { + u8 tx = *txp++; + tiny_spi_wait_txr(hw); + writeb(tx, hw->base + TINY_SPI_TXDATA); + } + } + tiny_spi_wait_txe(hw); + } else { + writeb(0, hw->base + TINY_SPI_TXDATA); + if (t->len > 1) { + writeb(0, hw->base + TINY_SPI_TXDATA); + for (i = 2; i < t->len; i++) { + tiny_spi_wait_txr(hw); + writeb(0, hw->base + TINY_SPI_TXDATA); + } + } + tiny_spi_wait_txe(hw); + } + return t->len; +} + +static irqreturn_t tiny_spi_irq(int irq, void *dev) +{ + struct tiny_spi *hw = dev; + + writeb(0, hw->base + TINY_SPI_STATUS); + if (hw->rxc + 1 == hw->len) { + if (hw->rxp) + *hw->rxp++ = readb(hw->base + TINY_SPI_RXDATA); + hw->rxc++; + complete(&hw->done); + } else { + if (hw->rxp) + *hw->rxp++ = readb(hw->base + TINY_SPI_TXDATA); + hw->rxc++; + if (hw->txc < hw->len) { + writeb(hw->txp ? *hw->txp++ : 0, + hw->base + TINY_SPI_TXDATA); + hw->txc++; + writeb(TINY_SPI_STATUS_TXR, + hw->base + TINY_SPI_STATUS); + } else { + writeb(TINY_SPI_STATUS_TXE, + hw->base + TINY_SPI_STATUS); + } + } + return IRQ_HANDLED; +} + +#ifdef CONFIG_OF +#include + +static int __devinit tiny_spi_of_probe(struct platform_device *pdev) +{ + struct tiny_spi *hw = platform_get_drvdata(pdev); + struct device_node *np = pdev->dev.of_node; + unsigned int i; + const __be32 *val; + int len; + + if (!np) + return 0; + hw->gpio_cs_count = of_gpio_count(np); + if (hw->gpio_cs_count) { + hw->gpio_cs = devm_kzalloc(&pdev->dev, + hw->gpio_cs_count * sizeof(unsigned int), + GFP_KERNEL); + if (!hw->gpio_cs) + return -ENOMEM; + } + for (i = 0; i < hw->gpio_cs_count; i++) { + hw->gpio_cs[i] = of_get_gpio_flags(np, i, NULL); + if (hw->gpio_cs[i] < 0) + return -ENODEV; + } + hw->bitbang.master->dev.of_node = pdev->dev.of_node; + val = of_get_property(pdev->dev.of_node, + "clock-frequency", &len); + if (val && len >= sizeof(__be32)) + hw->freq = be32_to_cpup(val); + val = of_get_property(pdev->dev.of_node, "baud-width", &len); + if (val && len >= sizeof(__be32)) + hw->baudwidth = be32_to_cpup(val); + return 0; +} +#else /* !CONFIG_OF */ +static int __devinit tiny_spi_of_probe(struct platform_device *pdev) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static int __devinit tiny_spi_probe(struct platform_device *pdev) +{ + struct tiny_spi_platform_data *platp = pdev->dev.platform_data; + struct tiny_spi *hw; + struct spi_master *master; + struct resource *res; + unsigned int i; + int err = -ENODEV; + + master = spi_alloc_master(&pdev->dev, sizeof(struct tiny_spi)); + if (!master) + return err; + + /* setup the master state. */ + master->bus_num = pdev->id; + master->num_chipselect = 255; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->setup = tiny_spi_setup; + + hw = spi_master_get_devdata(master); + platform_set_drvdata(pdev, hw); + + /* setup the state for the bitbang driver */ + hw->bitbang.master = spi_master_get(master); + if (!hw->bitbang.master) + return err; + hw->bitbang.setup_transfer = tiny_spi_setup_transfer; + hw->bitbang.chipselect = tiny_spi_chipselect; + hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs; + + /* find and map our resources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + goto exit_busy; + if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), + pdev->name)) + goto exit_busy; + hw->base = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (!hw->base) + goto exit_busy; + /* irq is optional */ + hw->irq = platform_get_irq(pdev, 0); + if (hw->irq >= 0) { + init_completion(&hw->done); + err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0, + pdev->name, hw); + if (err) + goto exit; + } + /* find platform data */ + if (platp) { + hw->gpio_cs_count = platp->gpio_cs_count; + hw->gpio_cs = platp->gpio_cs; + if (platp->gpio_cs_count && !platp->gpio_cs) + goto exit_busy; + hw->freq = platp->freq; + hw->baudwidth = platp->baudwidth; + } else { + err = tiny_spi_of_probe(pdev); + if (err) + goto exit; + } + for (i = 0; i < hw->gpio_cs_count; i++) { + err = gpio_request(hw->gpio_cs[i], dev_name(&pdev->dev)); + if (err) + goto exit_gpio; + gpio_direction_output(hw->gpio_cs[i], 1); + } + hw->bitbang.master->num_chipselect = max(1U, hw->gpio_cs_count); + + /* register our spi controller */ + err = spi_bitbang_start(&hw->bitbang); + if (err) + goto exit; + dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq); + + return 0; + +exit_gpio: + while (i-- > 0) + gpio_free(hw->gpio_cs[i]); +exit_busy: + err = -EBUSY; +exit: + platform_set_drvdata(pdev, NULL); + spi_master_put(master); + return err; +} + +static int __devexit tiny_spi_remove(struct platform_device *pdev) +{ + struct tiny_spi *hw = platform_get_drvdata(pdev); + struct spi_master *master = hw->bitbang.master; + unsigned int i; + + spi_bitbang_stop(&hw->bitbang); + for (i = 0; i < hw->gpio_cs_count; i++) + gpio_free(hw->gpio_cs[i]); + platform_set_drvdata(pdev, NULL); + spi_master_put(master); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id tiny_spi_match[] = { + { .compatible = "opencores,tiny-spi-rtlsvn2", }, + {}, +}; +MODULE_DEVICE_TABLE(of, tiny_spi_match); +#else /* CONFIG_OF */ +#define tiny_spi_match NULL +#endif /* CONFIG_OF */ + +static struct platform_driver tiny_spi_driver = { + .probe = tiny_spi_probe, + .remove = __devexit_p(tiny_spi_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = NULL, + .of_match_table = tiny_spi_match, + }, +}; + +static int __init tiny_spi_init(void) +{ + return platform_driver_register(&tiny_spi_driver); +} +module_init(tiny_spi_init); + +static void __exit tiny_spi_exit(void) +{ + platform_driver_unregister(&tiny_spi_driver); +} +module_exit(tiny_spi_exit); + +MODULE_DESCRIPTION("OpenCores tiny SPI driver"); +MODULE_AUTHOR("Thomas Chou "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/include/linux/spi/spi_oc_tiny.h b/include/linux/spi/spi_oc_tiny.h new file mode 100644 index 000000000000..1ac529cf4f06 --- /dev/null +++ b/include/linux/spi/spi_oc_tiny.h @@ -0,0 +1,20 @@ +#ifndef _LINUX_SPI_SPI_OC_TINY_H +#define _LINUX_SPI_SPI_OC_TINY_H + +/** + * struct tiny_spi_platform_data - platform data of the OpenCores tiny SPI + * @freq: input clock freq to the core. + * @baudwidth: baud rate divider width of the core. + * @gpio_cs_count: number of gpio pins used for chipselect. + * @gpio_cs: array of gpio pins used for chipselect. + * + * freq and baudwidth are used only if the divider is programmable. + */ +struct tiny_spi_platform_data { + unsigned int freq; + unsigned int baudwidth; + unsigned int gpio_cs_count; + int *gpio_cs; +}; + +#endif /* _LINUX_SPI_SPI_OC_TINY_H */ -- cgit v1.2.3 From 31a5b8ce8f3bf20799eb68da9602de2bee58fdd3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 18 Feb 2011 17:59:11 +0100 Subject: drm/nouveau: don't munge in drm_mm internals Nouveau was checking drm_mm internals on teardown to see whether the memory manager was initialized. Hide these internals in a small inline helper function. Acked-by: Ben Skeggs Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/nouveau/nouveau_object.c | 2 +- drivers/gpu/drm/nouveau/nv50_instmem.c | 4 ++-- drivers/gpu/drm/nouveau/nvc0_instmem.c | 2 +- include/drm/drm_mm.h | 5 +++++ 4 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c index 30b6544467ca..03adfe4c7665 100644 --- a/drivers/gpu/drm/nouveau/nouveau_object.c +++ b/drivers/gpu/drm/nouveau/nouveau_object.c @@ -909,7 +909,7 @@ nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan) nouveau_vm_ref(NULL, &chan->vm, chan->vm_pd); nouveau_gpuobj_ref(NULL, &chan->vm_pd); - if (chan->ramin_heap.free_stack.next) + if (drm_mm_initialized(&chan->ramin_heap)) drm_mm_takedown(&chan->ramin_heap); nouveau_gpuobj_ref(NULL, &chan->ramin); } diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c index ea0041810ae3..300285ae8e9e 100644 --- a/drivers/gpu/drm/nouveau/nv50_instmem.c +++ b/drivers/gpu/drm/nouveau/nv50_instmem.c @@ -56,7 +56,7 @@ nv50_channel_del(struct nouveau_channel **pchan) nouveau_gpuobj_ref(NULL, &chan->ramfc); nouveau_vm_ref(NULL, &chan->vm, chan->vm_pd); nouveau_gpuobj_ref(NULL, &chan->vm_pd); - if (chan->ramin_heap.free_stack.next) + if (drm_mm_initialized(&chan->ramin_heap)) drm_mm_takedown(&chan->ramin_heap); nouveau_gpuobj_ref(NULL, &chan->ramin); kfree(chan); @@ -259,7 +259,7 @@ nv50_instmem_takedown(struct drm_device *dev) nouveau_gpuobj_ref(NULL, &dev_priv->bar3_vm->pgt[0].obj[0]); nouveau_vm_ref(NULL, &dev_priv->bar3_vm, NULL); - if (dev_priv->ramin_heap.free_stack.next) + if (drm_mm_initialized(&dev_priv->ramin_heap)) drm_mm_takedown(&dev_priv->ramin_heap); dev_priv->engine.instmem.priv = NULL; diff --git a/drivers/gpu/drm/nouveau/nvc0_instmem.c b/drivers/gpu/drm/nouveau/nvc0_instmem.c index c09091749054..82357d2df1f4 100644 --- a/drivers/gpu/drm/nouveau/nvc0_instmem.c +++ b/drivers/gpu/drm/nouveau/nvc0_instmem.c @@ -67,7 +67,7 @@ nvc0_channel_del(struct nouveau_channel **pchan) return; nouveau_vm_ref(NULL, &chan->vm, NULL); - if (chan->ramin_heap.free_stack.next) + if (drm_mm_initialized(&chan->ramin_heap)) drm_mm_takedown(&chan->ramin_heap); nouveau_gpuobj_ref(NULL, &chan->ramin); kfree(chan); diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index e39177778601..0d791462f7b2 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -72,6 +72,11 @@ struct drm_mm { unsigned long scan_end; }; +static inline bool drm_mm_initialized(struct drm_mm *mm) +{ + return mm->free_stack.next; +} + /* * Basic range manager support (drm_mm.c) */ -- cgit v1.2.3 From ea7b1dd44867e9cd6bac67e7c9fc3f128b5b255c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 18 Feb 2011 17:59:12 +0100 Subject: drm: mm: track free areas implicitly The idea is to track free holes implicitly by marking the allocation immediatly preceeding a hole. To avoid an ugly corner case add a dummy head_node to struct drm_mm to track the hole that spans to complete allocation area when the memory manager is empty. To guarantee that there's always a preceeding/following node (that might be marked as hole_follows == 1), move the mm->node_list list_head to the head_node. The main allocator and fair-lru scan code actually becomes simpler. Only the debug code slightly suffers because free areas are no longer explicit. Also add drm_mm_for_each_node (which will be much more useful when struct drm_mm_node is embeddable). Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_mm.c | 464 ++++++++++++++++++++++------------------------- include/drm/drm_mm.h | 21 ++- 2 files changed, 225 insertions(+), 260 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index c59515ba7e69..4fa33e1283af 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -64,8 +64,8 @@ static struct drm_mm_node *drm_mm_kmalloc(struct drm_mm *mm, int atomic) else { child = list_entry(mm->unused_nodes.next, - struct drm_mm_node, free_stack); - list_del(&child->free_stack); + struct drm_mm_node, node_list); + list_del(&child->node_list); --mm->num_unused; } spin_unlock(&mm->unused_lock); @@ -94,126 +94,123 @@ int drm_mm_pre_get(struct drm_mm *mm) return ret; } ++mm->num_unused; - list_add_tail(&node->free_stack, &mm->unused_nodes); + list_add_tail(&node->node_list, &mm->unused_nodes); } spin_unlock(&mm->unused_lock); return 0; } EXPORT_SYMBOL(drm_mm_pre_get); -static int drm_mm_create_tail_node(struct drm_mm *mm, - unsigned long start, - unsigned long size, int atomic) +static inline unsigned long drm_mm_hole_node_start(struct drm_mm_node *hole_node) { - struct drm_mm_node *child; - - child = drm_mm_kmalloc(mm, atomic); - if (unlikely(child == NULL)) - return -ENOMEM; - - child->free = 1; - child->size = size; - child->start = start; - child->mm = mm; - - list_add_tail(&child->node_list, &mm->node_list); - list_add_tail(&child->free_stack, &mm->free_stack); - - return 0; + return hole_node->start + hole_node->size; } -static struct drm_mm_node *drm_mm_split_at_start(struct drm_mm_node *parent, - unsigned long size, - int atomic) +static inline unsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node) { - struct drm_mm_node *child; - - child = drm_mm_kmalloc(parent->mm, atomic); - if (unlikely(child == NULL)) - return NULL; - - INIT_LIST_HEAD(&child->free_stack); + struct drm_mm_node *next_node = + list_entry(hole_node->node_list.next, struct drm_mm_node, + node_list); - child->size = size; - child->start = parent->start; - child->mm = parent->mm; - - list_add_tail(&child->node_list, &parent->node_list); - INIT_LIST_HEAD(&child->free_stack); - - parent->size -= size; - parent->start += size; - return child; + return next_node->start; } - -struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node, +struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *hole_node, unsigned long size, unsigned alignment, int atomic) { - struct drm_mm_node *align_splitoff = NULL; - unsigned tmp = 0; + struct drm_mm_node *node; + struct drm_mm *mm = hole_node->mm; + unsigned long tmp = 0, wasted = 0; + unsigned long hole_start = drm_mm_hole_node_start(hole_node); + unsigned long hole_end = drm_mm_hole_node_end(hole_node); + + BUG_ON(!hole_node->hole_follows); + + node = drm_mm_kmalloc(mm, atomic); + if (unlikely(node == NULL)) + return NULL; if (alignment) - tmp = node->start % alignment; + tmp = hole_start % alignment; - if (tmp) { - align_splitoff = - drm_mm_split_at_start(node, alignment - tmp, atomic); - if (unlikely(align_splitoff == NULL)) - return NULL; - } + if (!tmp) { + hole_node->hole_follows = 0; + list_del_init(&hole_node->hole_stack); + } else + wasted = alignment - tmp; + + node->start = hole_start + wasted; + node->size = size; + node->mm = mm; - if (node->size == size) { - list_del_init(&node->free_stack); - node->free = 0; + INIT_LIST_HEAD(&node->hole_stack); + list_add(&node->node_list, &hole_node->node_list); + + BUG_ON(node->start + node->size > hole_end); + + if (node->start + node->size < hole_end) { + list_add(&node->hole_stack, &mm->hole_stack); + node->hole_follows = 1; } else { - node = drm_mm_split_at_start(node, size, atomic); + node->hole_follows = 0; } - if (align_splitoff) - drm_mm_put_block(align_splitoff); - return node; } EXPORT_SYMBOL(drm_mm_get_block_generic); -struct drm_mm_node *drm_mm_get_block_range_generic(struct drm_mm_node *node, +struct drm_mm_node *drm_mm_get_block_range_generic(struct drm_mm_node *hole_node, unsigned long size, unsigned alignment, unsigned long start, unsigned long end, int atomic) { - struct drm_mm_node *align_splitoff = NULL; - unsigned tmp = 0; - unsigned wasted = 0; + struct drm_mm_node *node; + struct drm_mm *mm = hole_node->mm; + unsigned long tmp = 0, wasted = 0; + unsigned long hole_start = drm_mm_hole_node_start(hole_node); + unsigned long hole_end = drm_mm_hole_node_end(hole_node); - if (node->start < start) - wasted += start - node->start; + BUG_ON(!hole_node->hole_follows); + + node = drm_mm_kmalloc(mm, atomic); + if (unlikely(node == NULL)) + return NULL; + + if (hole_start < start) + wasted += start - hole_start; if (alignment) - tmp = ((node->start + wasted) % alignment); + tmp = (hole_start + wasted) % alignment; if (tmp) wasted += alignment - tmp; - if (wasted) { - align_splitoff = drm_mm_split_at_start(node, wasted, atomic); - if (unlikely(align_splitoff == NULL)) - return NULL; + + if (!wasted) { + hole_node->hole_follows = 0; + list_del_init(&hole_node->hole_stack); } - if (node->size == size) { - list_del_init(&node->free_stack); - node->free = 0; + node->start = hole_start + wasted; + node->size = size; + node->mm = mm; + + INIT_LIST_HEAD(&node->hole_stack); + list_add(&node->node_list, &hole_node->node_list); + + BUG_ON(node->start + node->size > hole_end); + BUG_ON(node->start + node->size > end); + + if (node->start + node->size < hole_end) { + list_add(&node->hole_stack, &mm->hole_stack); + node->hole_follows = 1; } else { - node = drm_mm_split_at_start(node, size, atomic); + node->hole_follows = 0; } - if (align_splitoff) - drm_mm_put_block(align_splitoff); - return node; } EXPORT_SYMBOL(drm_mm_get_block_range_generic); @@ -223,66 +220,41 @@ EXPORT_SYMBOL(drm_mm_get_block_range_generic); * Otherwise add to the free stack. */ -void drm_mm_put_block(struct drm_mm_node *cur) +void drm_mm_put_block(struct drm_mm_node *node) { - struct drm_mm *mm = cur->mm; - struct list_head *cur_head = &cur->node_list; - struct list_head *root_head = &mm->node_list; - struct drm_mm_node *prev_node = NULL; - struct drm_mm_node *next_node; + struct drm_mm *mm = node->mm; + struct drm_mm_node *prev_node; - int merged = 0; + BUG_ON(node->scanned_block || node->scanned_prev_free + || node->scanned_next_free); - BUG_ON(cur->scanned_block || cur->scanned_prev_free - || cur->scanned_next_free); + prev_node = + list_entry(node->node_list.prev, struct drm_mm_node, node_list); - if (cur_head->prev != root_head) { - prev_node = - list_entry(cur_head->prev, struct drm_mm_node, node_list); - if (prev_node->free) { - prev_node->size += cur->size; - merged = 1; - } - } - if (cur_head->next != root_head) { - next_node = - list_entry(cur_head->next, struct drm_mm_node, node_list); - if (next_node->free) { - if (merged) { - prev_node->size += next_node->size; - list_del(&next_node->node_list); - list_del(&next_node->free_stack); - spin_lock(&mm->unused_lock); - if (mm->num_unused < MM_UNUSED_TARGET) { - list_add(&next_node->free_stack, - &mm->unused_nodes); - ++mm->num_unused; - } else - kfree(next_node); - spin_unlock(&mm->unused_lock); - } else { - next_node->size += cur->size; - next_node->start = cur->start; - merged = 1; - } - } - } - if (!merged) { - cur->free = 1; - list_add(&cur->free_stack, &mm->free_stack); - } else { - list_del(&cur->node_list); - spin_lock(&mm->unused_lock); - if (mm->num_unused < MM_UNUSED_TARGET) { - list_add(&cur->free_stack, &mm->unused_nodes); - ++mm->num_unused; - } else - kfree(cur); - spin_unlock(&mm->unused_lock); - } -} + if (node->hole_follows) { + BUG_ON(drm_mm_hole_node_start(node) + == drm_mm_hole_node_end(node)); + list_del(&node->hole_stack); + } else + BUG_ON(drm_mm_hole_node_start(node) + != drm_mm_hole_node_end(node)); + if (!prev_node->hole_follows) { + prev_node->hole_follows = 1; + list_add(&prev_node->hole_stack, &mm->hole_stack); + } else + list_move(&prev_node->hole_stack, &mm->hole_stack); + + list_del(&node->node_list); + spin_lock(&mm->unused_lock); + if (mm->num_unused < MM_UNUSED_TARGET) { + list_add(&node->node_list, &mm->unused_nodes); + ++mm->num_unused; + } else + kfree(node); + spin_unlock(&mm->unused_lock); +} EXPORT_SYMBOL(drm_mm_put_block); static int check_free_hole(unsigned long start, unsigned long end, @@ -319,8 +291,10 @@ struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm, best = NULL; best_size = ~0UL; - list_for_each_entry(entry, &mm->free_stack, free_stack) { - if (!check_free_hole(entry->start, entry->start + entry->size, + list_for_each_entry(entry, &mm->hole_stack, hole_stack) { + BUG_ON(!entry->hole_follows); + if (!check_free_hole(drm_mm_hole_node_start(entry), + drm_mm_hole_node_end(entry), size, alignment)) continue; @@ -353,12 +327,13 @@ struct drm_mm_node *drm_mm_search_free_in_range(const struct drm_mm *mm, best = NULL; best_size = ~0UL; - list_for_each_entry(entry, &mm->free_stack, free_stack) { - unsigned long adj_start = entry->start < start ? - start : entry->start; - unsigned long adj_end = entry->start + entry->size > end ? - end : entry->start + entry->size; + list_for_each_entry(entry, &mm->hole_stack, hole_stack) { + unsigned long adj_start = drm_mm_hole_node_start(entry) < start ? + start : drm_mm_hole_node_start(entry); + unsigned long adj_end = drm_mm_hole_node_end(entry) > end ? + end : drm_mm_hole_node_end(entry); + BUG_ON(!entry->hole_follows); if (!check_free_hole(adj_start, adj_end, size, alignment)) continue; @@ -430,70 +405,40 @@ EXPORT_SYMBOL(drm_mm_init_scan_with_range); int drm_mm_scan_add_block(struct drm_mm_node *node) { struct drm_mm *mm = node->mm; - struct list_head *prev_free, *next_free; - struct drm_mm_node *prev_node, *next_node; + struct drm_mm_node *prev_node; + unsigned long hole_start, hole_end; unsigned long adj_start; unsigned long adj_end; mm->scanned_blocks++; - prev_free = next_free = NULL; - - BUG_ON(node->free); + BUG_ON(node->scanned_block); node->scanned_block = 1; - node->free = 1; - - if (node->node_list.prev != &mm->node_list) { - prev_node = list_entry(node->node_list.prev, struct drm_mm_node, - node_list); - - if (prev_node->free) { - list_del(&prev_node->node_list); - - node->start = prev_node->start; - node->size += prev_node->size; - - prev_node->scanned_prev_free = 1; - - prev_free = &prev_node->free_stack; - } - } - - if (node->node_list.next != &mm->node_list) { - next_node = list_entry(node->node_list.next, struct drm_mm_node, - node_list); - - if (next_node->free) { - list_del(&next_node->node_list); - - node->size += next_node->size; - - next_node->scanned_next_free = 1; - next_free = &next_node->free_stack; - } - } + prev_node = list_entry(node->node_list.prev, struct drm_mm_node, + node_list); - /* The free_stack list is not used for allocated objects, so these two - * pointers can be abused (as long as no allocations in this memory - * manager happens). */ - node->free_stack.prev = prev_free; - node->free_stack.next = next_free; + node->scanned_preceeds_hole = prev_node->hole_follows; + prev_node->hole_follows = 1; + list_del(&node->node_list); + node->node_list.prev = &prev_node->node_list; + hole_start = drm_mm_hole_node_start(prev_node); + hole_end = drm_mm_hole_node_end(prev_node); if (mm->scan_check_range) { - adj_start = node->start < mm->scan_start ? - mm->scan_start : node->start; - adj_end = node->start + node->size > mm->scan_end ? - mm->scan_end : node->start + node->size; + adj_start = hole_start < mm->scan_start ? + mm->scan_start : hole_start; + adj_end = hole_end > mm->scan_end ? + mm->scan_end : hole_end; } else { - adj_start = node->start; - adj_end = node->start + node->size; + adj_start = hole_start; + adj_end = hole_end; } if (check_free_hole(adj_start , adj_end, mm->scan_size, mm->scan_alignment)) { - mm->scan_hit_start = node->start; - mm->scan_hit_size = node->size; + mm->scan_hit_start = hole_start; + mm->scan_hit_size = hole_end; return 1; } @@ -519,39 +464,19 @@ EXPORT_SYMBOL(drm_mm_scan_add_block); int drm_mm_scan_remove_block(struct drm_mm_node *node) { struct drm_mm *mm = node->mm; - struct drm_mm_node *prev_node, *next_node; + struct drm_mm_node *prev_node; mm->scanned_blocks--; BUG_ON(!node->scanned_block); node->scanned_block = 0; - node->free = 0; - - prev_node = list_entry(node->free_stack.prev, struct drm_mm_node, - free_stack); - next_node = list_entry(node->free_stack.next, struct drm_mm_node, - free_stack); - - if (prev_node) { - BUG_ON(!prev_node->scanned_prev_free); - prev_node->scanned_prev_free = 0; - - list_add_tail(&prev_node->node_list, &node->node_list); - node->start = prev_node->start + prev_node->size; - node->size -= prev_node->size; - } - - if (next_node) { - BUG_ON(!next_node->scanned_next_free); - next_node->scanned_next_free = 0; - - list_add(&next_node->node_list, &node->node_list); - - node->size -= next_node->size; - } + prev_node = list_entry(node->node_list.prev, struct drm_mm_node, + node_list); - INIT_LIST_HEAD(&node->free_stack); + prev_node->hole_follows = node->scanned_preceeds_hole; + INIT_LIST_HEAD(&node->node_list); + list_add(&node->node_list, &prev_node->node_list); /* Only need to check for containement because start&size for the * complete resulting free block (not just the desired part) is @@ -568,7 +493,7 @@ EXPORT_SYMBOL(drm_mm_scan_remove_block); int drm_mm_clean(struct drm_mm * mm) { - struct list_head *head = &mm->node_list; + struct list_head *head = &mm->head_node.node_list; return (head->next->next == head); } @@ -576,38 +501,40 @@ EXPORT_SYMBOL(drm_mm_clean); int drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size) { - INIT_LIST_HEAD(&mm->node_list); - INIT_LIST_HEAD(&mm->free_stack); + INIT_LIST_HEAD(&mm->hole_stack); INIT_LIST_HEAD(&mm->unused_nodes); mm->num_unused = 0; mm->scanned_blocks = 0; spin_lock_init(&mm->unused_lock); - return drm_mm_create_tail_node(mm, start, size, 0); + /* Clever trick to avoid a special case in the free hole tracking. */ + INIT_LIST_HEAD(&mm->head_node.node_list); + INIT_LIST_HEAD(&mm->head_node.hole_stack); + mm->head_node.hole_follows = 1; + mm->head_node.scanned_block = 0; + mm->head_node.scanned_prev_free = 0; + mm->head_node.scanned_next_free = 0; + mm->head_node.mm = mm; + mm->head_node.start = start + size; + mm->head_node.size = start - mm->head_node.start; + list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack); + + return 0; } EXPORT_SYMBOL(drm_mm_init); void drm_mm_takedown(struct drm_mm * mm) { - struct list_head *bnode = mm->free_stack.next; - struct drm_mm_node *entry; - struct drm_mm_node *next; + struct drm_mm_node *entry, *next; - entry = list_entry(bnode, struct drm_mm_node, free_stack); - - if (entry->node_list.next != &mm->node_list || - entry->free_stack.next != &mm->free_stack) { + if (!list_empty(&mm->head_node.node_list)) { DRM_ERROR("Memory manager not clean. Delaying takedown\n"); return; } - list_del(&entry->free_stack); - list_del(&entry->node_list); - kfree(entry); - spin_lock(&mm->unused_lock); - list_for_each_entry_safe(entry, next, &mm->unused_nodes, free_stack) { - list_del(&entry->free_stack); + list_for_each_entry_safe(entry, next, &mm->unused_nodes, node_list) { + list_del(&entry->node_list); kfree(entry); --mm->num_unused; } @@ -620,19 +547,37 @@ EXPORT_SYMBOL(drm_mm_takedown); void drm_mm_debug_table(struct drm_mm *mm, const char *prefix) { struct drm_mm_node *entry; - int total_used = 0, total_free = 0, total = 0; - - list_for_each_entry(entry, &mm->node_list, node_list) { - printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8ld: %s\n", + unsigned long total_used = 0, total_free = 0, total = 0; + unsigned long hole_start, hole_end, hole_size; + + hole_start = drm_mm_hole_node_start(&mm->head_node); + hole_end = drm_mm_hole_node_end(&mm->head_node); + hole_size = hole_end - hole_start; + if (hole_size) + printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n", + prefix, hole_start, hole_end, + hole_size); + total_free += hole_size; + + drm_mm_for_each_node(entry, mm) { + printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: used\n", prefix, entry->start, entry->start + entry->size, - entry->size, entry->free ? "free" : "used"); - total += entry->size; - if (entry->free) - total_free += entry->size; - else - total_used += entry->size; + entry->size); + total_used += entry->size; + + if (entry->hole_follows) { + hole_start = drm_mm_hole_node_start(entry); + hole_end = drm_mm_hole_node_end(entry); + hole_size = hole_end - hole_start; + printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n", + prefix, hole_start, hole_end, + hole_size); + total_free += hole_size; + } } - printk(KERN_DEBUG "%s total: %d, used %d free %d\n", prefix, total, + total = total_free + total_used; + + printk(KERN_DEBUG "%s total: %lu, used %lu free %lu\n", prefix, total, total_used, total_free); } EXPORT_SYMBOL(drm_mm_debug_table); @@ -641,17 +586,34 @@ EXPORT_SYMBOL(drm_mm_debug_table); int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm) { struct drm_mm_node *entry; - int total_used = 0, total_free = 0, total = 0; - - list_for_each_entry(entry, &mm->node_list, node_list) { - seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: %s\n", entry->start, entry->start + entry->size, entry->size, entry->free ? "free" : "used"); - total += entry->size; - if (entry->free) - total_free += entry->size; - else - total_used += entry->size; + unsigned long total_used = 0, total_free = 0, total = 0; + unsigned long hole_start, hole_end, hole_size; + + hole_start = drm_mm_hole_node_start(&mm->head_node); + hole_end = drm_mm_hole_node_end(&mm->head_node); + hole_size = hole_end - hole_start; + if (hole_size) + seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: free\n", + hole_start, hole_end, hole_size); + total_free += hole_size; + + drm_mm_for_each_node(entry, mm) { + seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: used\n", + entry->start, entry->start + entry->size, + entry->size); + total_used += entry->size; + if (entry->hole_follows) { + hole_start = drm_mm_hole_node_start(&mm->head_node); + hole_end = drm_mm_hole_node_end(&mm->head_node); + hole_size = hole_end - hole_start; + seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: free\n", + hole_start, hole_end, hole_size); + total_free += hole_size; + } } - seq_printf(m, "total: %d, used %d free %d\n", total, total_used, total_free); + total = total_free + total_used; + + seq_printf(m, "total: %lu, used %lu free %lu\n", total, total_used, total_free); return 0; } EXPORT_SYMBOL(drm_mm_dump_table); diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index 0d791462f7b2..34fa36f2de70 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -42,23 +42,24 @@ #endif struct drm_mm_node { - struct list_head free_stack; struct list_head node_list; - unsigned free : 1; + struct list_head hole_stack; + unsigned hole_follows : 1; unsigned scanned_block : 1; unsigned scanned_prev_free : 1; unsigned scanned_next_free : 1; + unsigned scanned_preceeds_hole : 1; unsigned long start; unsigned long size; struct drm_mm *mm; }; struct drm_mm { - /* List of free memory blocks, most recently freed ordered. */ - struct list_head free_stack; - /* List of all memory nodes, ordered according to the (increasing) start - * address of the memory node. */ - struct list_head node_list; + /* List of all memory nodes that immediatly preceed a free hole. */ + struct list_head hole_stack; + /* head_node.node_list is the list of all memory nodes, ordered + * according to the (increasing) start address of the memory node. */ + struct drm_mm_node head_node; struct list_head unused_nodes; int num_unused; spinlock_t unused_lock; @@ -74,9 +75,11 @@ struct drm_mm { static inline bool drm_mm_initialized(struct drm_mm *mm) { - return mm->free_stack.next; + return mm->hole_stack.next; } - +#define drm_mm_for_each_node(entry, mm) list_for_each_entry(entry, \ + &(mm)->head_node.node_list, \ + node_list); /* * Basic range manager support (drm_mm.c) */ -- cgit v1.2.3 From b0b7af1884b7d807a3504804f9825d472de78708 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 18 Feb 2011 17:59:14 +0100 Subject: drm: mm: add api for embedding struct drm_mm_node The old api has a two-step process: First search for a suitable free hole, then allocate from that specific hole. No user used this to do anything clever. So drop it for the embeddable variant of the drm_mm api (the old one retains this ability, for the time being). With struct drm_mm_node embedded, we cannot track allocations anymore by checking for a NULL pointer. So keep track of this and add a small helper drm_mm_node_allocated. Also add a function to move allocations between different struct drm_mm_node. v2: Implement suggestions by Chris Wilson. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_mm.c | 93 +++++++++++++++++++++++++++++++++++++++++++----- include/drm/drm_mm.h | 19 +++++++--- 2 files changed, 98 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index fecb4063c018..d6432f9e49c1 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -124,6 +124,8 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, unsigned long hole_start = drm_mm_hole_node_start(hole_node); unsigned long hole_end = drm_mm_hole_node_end(hole_node); + BUG_ON(!hole_node->hole_follows || node->allocated); + if (alignment) tmp = hole_start % alignment; @@ -136,6 +138,7 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, node->start = hole_start + wasted; node->size = size; node->mm = mm; + node->allocated = 1; INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list); @@ -157,8 +160,6 @@ struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *hole_node, { struct drm_mm_node *node; - BUG_ON(!hole_node->hole_follows); - node = drm_mm_kmalloc(hole_node->mm, atomic); if (unlikely(node == NULL)) return NULL; @@ -169,6 +170,26 @@ struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *hole_node, } EXPORT_SYMBOL(drm_mm_get_block_generic); +/** + * Search for free space and insert a preallocated memory node. Returns + * -ENOSPC if no suitable free area is available. The preallocated memory node + * must be cleared. + */ +int drm_mm_insert_node(struct drm_mm *mm, struct drm_mm_node *node, + unsigned long size, unsigned alignment) +{ + struct drm_mm_node *hole_node; + + hole_node = drm_mm_search_free(mm, size, alignment, 0); + if (!hole_node) + return -ENOSPC; + + drm_mm_insert_helper(hole_node, node, size, alignment); + + return 0; +} +EXPORT_SYMBOL(drm_mm_insert_node); + static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, struct drm_mm_node *node, unsigned long size, unsigned alignment, @@ -179,6 +200,8 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, unsigned long hole_start = drm_mm_hole_node_start(hole_node); unsigned long hole_end = drm_mm_hole_node_end(hole_node); + BUG_ON(!hole_node->hole_follows || node->allocated); + if (hole_start < start) wasted += start - hole_start; if (alignment) @@ -195,6 +218,7 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, node->start = hole_start + wasted; node->size = size; node->mm = mm; + node->allocated = 1; INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list); @@ -219,8 +243,6 @@ struct drm_mm_node *drm_mm_get_block_range_generic(struct drm_mm_node *hole_node { struct drm_mm_node *node; - BUG_ON(!hole_node->hole_follows); - node = drm_mm_kmalloc(hole_node->mm, atomic); if (unlikely(node == NULL)) return NULL; @@ -232,14 +254,34 @@ struct drm_mm_node *drm_mm_get_block_range_generic(struct drm_mm_node *hole_node } EXPORT_SYMBOL(drm_mm_get_block_range_generic); -/* - * Put a block. Merge with the previous and / or next block if they are free. - * Otherwise add to the free stack. +/** + * Search for free space and insert a preallocated memory node. Returns + * -ENOSPC if no suitable free area is available. This is for range + * restricted allocations. The preallocated memory node must be cleared. */ - -void drm_mm_put_block(struct drm_mm_node *node) +int drm_mm_insert_node_in_range(struct drm_mm *mm, struct drm_mm_node *node, + unsigned long size, unsigned alignment, + unsigned long start, unsigned long end) { + struct drm_mm_node *hole_node; + + hole_node = drm_mm_search_free_in_range(mm, size, alignment, + start, end, 0); + if (!hole_node) + return -ENOSPC; + + drm_mm_insert_helper_range(hole_node, node, size, alignment, + start, end); + return 0; +} +EXPORT_SYMBOL(drm_mm_insert_node_in_range); + +/** + * Remove a memory node from the allocator. + */ +void drm_mm_remove_node(struct drm_mm_node *node) +{ struct drm_mm *mm = node->mm; struct drm_mm_node *prev_node; @@ -264,6 +306,22 @@ void drm_mm_put_block(struct drm_mm_node *node) list_move(&prev_node->hole_stack, &mm->hole_stack); list_del(&node->node_list); + node->allocated = 0; +} +EXPORT_SYMBOL(drm_mm_remove_node); + +/* + * Remove a memory node from the allocator and free the allocated struct + * drm_mm_node. Only to be used on a struct drm_mm_node obtained by one of the + * drm_mm_get_block functions. + */ +void drm_mm_put_block(struct drm_mm_node *node) +{ + + struct drm_mm *mm = node->mm; + + drm_mm_remove_node(node); + spin_lock(&mm->unused_lock); if (mm->num_unused < MM_UNUSED_TARGET) { list_add(&node->node_list, &mm->unused_nodes); @@ -367,6 +425,23 @@ struct drm_mm_node *drm_mm_search_free_in_range(const struct drm_mm *mm, } EXPORT_SYMBOL(drm_mm_search_free_in_range); +/** + * Moves an allocation. To be used with embedded struct drm_mm_node. + */ +void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) +{ + list_replace(&old->node_list, &new->node_list); + list_replace(&old->node_list, &new->hole_stack); + new->hole_follows = old->hole_follows; + new->mm = old->mm; + new->start = old->start; + new->size = old->size; + + old->allocated = 0; + new->allocated = 1; +} +EXPORT_SYMBOL(drm_mm_replace_node); + /** * Initializa lru scanning. * diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index 34fa36f2de70..17a070e11d3c 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -49,6 +49,7 @@ struct drm_mm_node { unsigned scanned_prev_free : 1; unsigned scanned_next_free : 1; unsigned scanned_preceeds_hole : 1; + unsigned allocated : 1; unsigned long start; unsigned long size; struct drm_mm *mm; @@ -73,6 +74,11 @@ struct drm_mm { unsigned long scan_end; }; +static inline bool drm_mm_node_allocated(struct drm_mm_node *node) +{ + return node->allocated; +} + static inline bool drm_mm_initialized(struct drm_mm *mm) { return mm->hole_stack.next; @@ -126,7 +132,15 @@ static inline struct drm_mm_node *drm_mm_get_block_atomic_range( return drm_mm_get_block_range_generic(parent, size, alignment, start, end, 1); } +extern int drm_mm_insert_node(struct drm_mm *mm, struct drm_mm_node *node, + unsigned long size, unsigned alignment); +extern int drm_mm_insert_node_in_range(struct drm_mm *mm, + struct drm_mm_node *node, + unsigned long size, unsigned alignment, + unsigned long start, unsigned long end); extern void drm_mm_put_block(struct drm_mm_node *cur); +extern void drm_mm_remove_node(struct drm_mm_node *node); +extern void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new); extern struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm, unsigned long size, unsigned alignment, @@ -142,11 +156,6 @@ extern int drm_mm_init(struct drm_mm *mm, unsigned long start, unsigned long size); extern void drm_mm_takedown(struct drm_mm *mm); extern int drm_mm_clean(struct drm_mm *mm); -extern unsigned long drm_mm_tail_space(struct drm_mm *mm); -extern int drm_mm_remove_space_from_tail(struct drm_mm *mm, - unsigned long size); -extern int drm_mm_add_space_to_tail(struct drm_mm *mm, - unsigned long size, int atomic); extern int drm_mm_pre_get(struct drm_mm *mm); static inline struct drm_mm *drm_get_mm(struct drm_mm_node *block) -- cgit v1.2.3 From ae0cec2880a4dc6d90c7f8392bdc6705988389ca Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 18 Feb 2011 17:59:15 +0100 Subject: drm: mm: add helper to unwind scan state With the switch to implicit free space accounting one pointer got unused when scanning. Use it to create a single-linked list to ensure correct unwinding of the scan state. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_mm.c | 4 ++++ include/drm/drm_mm.h | 8 ++++++++ 2 files changed, 12 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index d6432f9e49c1..add1737dae0d 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -460,6 +460,7 @@ void drm_mm_init_scan(struct drm_mm *mm, unsigned long size, mm->scan_hit_start = 0; mm->scan_hit_size = 0; mm->scan_check_range = 0; + mm->prev_scanned_node = NULL; } EXPORT_SYMBOL(drm_mm_init_scan); @@ -485,6 +486,7 @@ void drm_mm_init_scan_with_range(struct drm_mm *mm, unsigned long size, mm->scan_start = start; mm->scan_end = end; mm->scan_check_range = 1; + mm->prev_scanned_node = NULL; } EXPORT_SYMBOL(drm_mm_init_scan_with_range); @@ -514,6 +516,8 @@ int drm_mm_scan_add_block(struct drm_mm_node *node) prev_node->hole_follows = 1; list_del(&node->node_list); node->node_list.prev = &prev_node->node_list; + node->node_list.next = &mm->prev_scanned_node->node_list; + mm->prev_scanned_node = node; hole_start = drm_mm_hole_node_start(prev_node); hole_end = drm_mm_hole_node_end(prev_node); diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index 17a070e11d3c..b1e7809e5e15 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -72,6 +72,7 @@ struct drm_mm { unsigned scanned_blocks; unsigned long scan_start; unsigned long scan_end; + struct drm_mm_node *prev_scanned_node; }; static inline bool drm_mm_node_allocated(struct drm_mm_node *node) @@ -86,6 +87,13 @@ static inline bool drm_mm_initialized(struct drm_mm *mm) #define drm_mm_for_each_node(entry, mm) list_for_each_entry(entry, \ &(mm)->head_node.node_list, \ node_list); +#define drm_mm_for_each_scanned_node_reverse(entry, n, mm) \ + for (entry = (mm)->prev_scanned_node, \ + next = entry ? list_entry(entry->node_list.next, \ + struct drm_mm_node, node_list) : NULL; \ + entry != NULL; entry = next, \ + next = entry ? list_entry(entry->node_list.next, \ + struct drm_mm_node, node_list) : NULL) \ /* * Basic range manager support (drm_mm.c) */ -- cgit v1.2.3 From db811ca0f48578f9940f49f284ac81e336b264ad Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Sun, 20 Feb 2011 17:14:21 -0800 Subject: i2c: tegra: Add i2c support Adds I2C bus driver for nVidia Tegra SoCs. Tegra includes 4 I2C controllers, one of which is inside the Dynamic Voltage Controller and has a slightly different register map. Signed-off-by: Colin Cross Signed-off-by: Ben Dooks --- drivers/i2c/busses/Kconfig | 7 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-tegra.c | 700 +++++++++++++++++++++++++++++++++++++++++ include/linux/i2c-tegra.h | 25 ++ 4 files changed, 733 insertions(+) create mode 100644 drivers/i2c/busses/i2c-tegra.c create mode 100644 include/linux/i2c-tegra.h (limited to 'include') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 113505a6434e..a5f48dc3b81f 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -607,6 +607,13 @@ config I2C_STU300 This driver can also be built as a module. If so, the module will be called i2c-stu300. +config I2C_TEGRA + tristate "NVIDIA Tegra internal I2C controller" + depends on ARCH_TEGRA + help + If you say yes to this option, support will be included for the + I2C controller embedded in NVIDIA Tegra SOCs + config I2C_VERSATILE tristate "ARM Versatile/Realview I2C bus support" depends on ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 9d2d0ec7fb23..a0d4afe05ea2 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o obj-$(CONFIG_I2C_STU300) += i2c-stu300.o +obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c new file mode 100644 index 000000000000..3921f664c9c3 --- /dev/null +++ b/drivers/i2c/busses/i2c-tegra.c @@ -0,0 +1,700 @@ +/* + * drivers/i2c/busses/i2c-tegra.c + * + * Copyright (C) 2010 Google, Inc. + * Author: Colin Cross + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000)) +#define BYTES_PER_FIFO_WORD 4 + +#define I2C_CNFG 0x000 +#define I2C_CNFG_PACKET_MODE_EN (1<<10) +#define I2C_CNFG_NEW_MASTER_FSM (1<<11) +#define I2C_SL_CNFG 0x020 +#define I2C_SL_CNFG_NEWSL (1<<2) +#define I2C_SL_ADDR1 0x02c +#define I2C_TX_FIFO 0x050 +#define I2C_RX_FIFO 0x054 +#define I2C_PACKET_TRANSFER_STATUS 0x058 +#define I2C_FIFO_CONTROL 0x05c +#define I2C_FIFO_CONTROL_TX_FLUSH (1<<1) +#define I2C_FIFO_CONTROL_RX_FLUSH (1<<0) +#define I2C_FIFO_CONTROL_TX_TRIG_SHIFT 5 +#define I2C_FIFO_CONTROL_RX_TRIG_SHIFT 2 +#define I2C_FIFO_STATUS 0x060 +#define I2C_FIFO_STATUS_TX_MASK 0xF0 +#define I2C_FIFO_STATUS_TX_SHIFT 4 +#define I2C_FIFO_STATUS_RX_MASK 0x0F +#define I2C_FIFO_STATUS_RX_SHIFT 0 +#define I2C_INT_MASK 0x064 +#define I2C_INT_STATUS 0x068 +#define I2C_INT_PACKET_XFER_COMPLETE (1<<7) +#define I2C_INT_ALL_PACKETS_XFER_COMPLETE (1<<6) +#define I2C_INT_TX_FIFO_OVERFLOW (1<<5) +#define I2C_INT_RX_FIFO_UNDERFLOW (1<<4) +#define I2C_INT_NO_ACK (1<<3) +#define I2C_INT_ARBITRATION_LOST (1<<2) +#define I2C_INT_TX_FIFO_DATA_REQ (1<<1) +#define I2C_INT_RX_FIFO_DATA_REQ (1<<0) +#define I2C_CLK_DIVISOR 0x06c + +#define DVC_CTRL_REG1 0x000 +#define DVC_CTRL_REG1_INTR_EN (1<<10) +#define DVC_CTRL_REG2 0x004 +#define DVC_CTRL_REG3 0x008 +#define DVC_CTRL_REG3_SW_PROG (1<<26) +#define DVC_CTRL_REG3_I2C_DONE_INTR_EN (1<<30) +#define DVC_STATUS 0x00c +#define DVC_STATUS_I2C_DONE_INTR (1<<30) + +#define I2C_ERR_NONE 0x00 +#define I2C_ERR_NO_ACK 0x01 +#define I2C_ERR_ARBITRATION_LOST 0x02 + +#define PACKET_HEADER0_HEADER_SIZE_SHIFT 28 +#define PACKET_HEADER0_PACKET_ID_SHIFT 16 +#define PACKET_HEADER0_CONT_ID_SHIFT 12 +#define PACKET_HEADER0_PROTOCOL_I2C (1<<4) + +#define I2C_HEADER_HIGHSPEED_MODE (1<<22) +#define I2C_HEADER_CONT_ON_NAK (1<<21) +#define I2C_HEADER_SEND_START_BYTE (1<<20) +#define I2C_HEADER_READ (1<<19) +#define I2C_HEADER_10BIT_ADDR (1<<18) +#define I2C_HEADER_IE_ENABLE (1<<17) +#define I2C_HEADER_REPEAT_START (1<<16) +#define I2C_HEADER_MASTER_ADDR_SHIFT 12 +#define I2C_HEADER_SLAVE_ADDR_SHIFT 1 + +/** + * struct tegra_i2c_dev - per device i2c context + * @dev: device reference for power management + * @adapter: core i2c layer adapter information + * @clk: clock reference for i2c controller + * @i2c_clk: clock reference for i2c bus + * @iomem: memory resource for registers + * @base: ioremapped registers cookie + * @cont_id: i2c controller id, used for for packet header + * @irq: irq number of transfer complete interrupt + * @is_dvc: identifies the DVC i2c controller, has a different register layout + * @msg_complete: transfer completion notifier + * @msg_err: error code for completed message + * @msg_buf: pointer to current message data + * @msg_buf_remaining: size of unsent data in the message buffer + * @msg_read: identifies read transfers + * @bus_clk_rate: current i2c bus clock rate + * @is_suspended: prevents i2c controller accesses after suspend is called + */ +struct tegra_i2c_dev { + struct device *dev; + struct i2c_adapter adapter; + struct clk *clk; + struct clk *i2c_clk; + struct resource *iomem; + void __iomem *base; + int cont_id; + int irq; + int is_dvc; + struct completion msg_complete; + int msg_err; + u8 *msg_buf; + size_t msg_buf_remaining; + int msg_read; + unsigned long bus_clk_rate; + bool is_suspended; +}; + +static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned long reg) +{ + writel(val, i2c_dev->base + reg); +} + +static u32 dvc_readl(struct tegra_i2c_dev *i2c_dev, unsigned long reg) +{ + return readl(i2c_dev->base + reg); +} + +/* + * i2c_writel and i2c_readl will offset the register if necessary to talk + * to the I2C block inside the DVC block + */ +static unsigned long tegra_i2c_reg_addr(struct tegra_i2c_dev *i2c_dev, + unsigned long reg) +{ + if (i2c_dev->is_dvc) + reg += (reg >= I2C_TX_FIFO) ? 0x10 : 0x40; + return reg; +} + +static void i2c_writel(struct tegra_i2c_dev *i2c_dev, u32 val, + unsigned long reg) +{ + writel(val, i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg)); +} + +static u32 i2c_readl(struct tegra_i2c_dev *i2c_dev, unsigned long reg) +{ + return readl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg)); +} + +static void i2c_writesl(struct tegra_i2c_dev *i2c_dev, void *data, + unsigned long reg, int len) +{ + writesl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len); +} + +static void i2c_readsl(struct tegra_i2c_dev *i2c_dev, void *data, + unsigned long reg, int len) +{ + readsl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len); +} + +static void tegra_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask) +{ + u32 int_mask = i2c_readl(i2c_dev, I2C_INT_MASK); + int_mask &= ~mask; + i2c_writel(i2c_dev, int_mask, I2C_INT_MASK); +} + +static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask) +{ + u32 int_mask = i2c_readl(i2c_dev, I2C_INT_MASK); + int_mask |= mask; + i2c_writel(i2c_dev, int_mask, I2C_INT_MASK); +} + +static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev) +{ + unsigned long timeout = jiffies + HZ; + u32 val = i2c_readl(i2c_dev, I2C_FIFO_CONTROL); + val |= I2C_FIFO_CONTROL_TX_FLUSH | I2C_FIFO_CONTROL_RX_FLUSH; + i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL); + + while (i2c_readl(i2c_dev, I2C_FIFO_CONTROL) & + (I2C_FIFO_CONTROL_TX_FLUSH | I2C_FIFO_CONTROL_RX_FLUSH)) { + if (time_after(jiffies, timeout)) { + dev_warn(i2c_dev->dev, "timeout waiting for fifo flush\n"); + return -ETIMEDOUT; + } + msleep(1); + } + return 0; +} + +static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev) +{ + u32 val; + int rx_fifo_avail; + u8 *buf = i2c_dev->msg_buf; + size_t buf_remaining = i2c_dev->msg_buf_remaining; + int words_to_transfer; + + val = i2c_readl(i2c_dev, I2C_FIFO_STATUS); + rx_fifo_avail = (val & I2C_FIFO_STATUS_RX_MASK) >> + I2C_FIFO_STATUS_RX_SHIFT; + + /* Rounds down to not include partial word at the end of buf */ + words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD; + if (words_to_transfer > rx_fifo_avail) + words_to_transfer = rx_fifo_avail; + + i2c_readsl(i2c_dev, buf, I2C_RX_FIFO, words_to_transfer); + + buf += words_to_transfer * BYTES_PER_FIFO_WORD; + buf_remaining -= words_to_transfer * BYTES_PER_FIFO_WORD; + rx_fifo_avail -= words_to_transfer; + + /* + * If there is a partial word at the end of buf, handle it manually to + * prevent overwriting past the end of buf + */ + if (rx_fifo_avail > 0 && buf_remaining > 0) { + BUG_ON(buf_remaining > 3); + val = i2c_readl(i2c_dev, I2C_RX_FIFO); + memcpy(buf, &val, buf_remaining); + buf_remaining = 0; + rx_fifo_avail--; + } + + BUG_ON(rx_fifo_avail > 0 && buf_remaining > 0); + i2c_dev->msg_buf_remaining = buf_remaining; + i2c_dev->msg_buf = buf; + return 0; +} + +static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev) +{ + u32 val; + int tx_fifo_avail; + u8 *buf = i2c_dev->msg_buf; + size_t buf_remaining = i2c_dev->msg_buf_remaining; + int words_to_transfer; + + val = i2c_readl(i2c_dev, I2C_FIFO_STATUS); + tx_fifo_avail = (val & I2C_FIFO_STATUS_TX_MASK) >> + I2C_FIFO_STATUS_TX_SHIFT; + + /* Rounds down to not include partial word at the end of buf */ + words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD; + if (words_to_transfer > tx_fifo_avail) + words_to_transfer = tx_fifo_avail; + + i2c_writesl(i2c_dev, buf, I2C_TX_FIFO, words_to_transfer); + + buf += words_to_transfer * BYTES_PER_FIFO_WORD; + buf_remaining -= words_to_transfer * BYTES_PER_FIFO_WORD; + tx_fifo_avail -= words_to_transfer; + + /* + * If there is a partial word at the end of buf, handle it manually to + * prevent reading past the end of buf, which could cross a page + * boundary and fault. + */ + if (tx_fifo_avail > 0 && buf_remaining > 0) { + BUG_ON(buf_remaining > 3); + memcpy(&val, buf, buf_remaining); + i2c_writel(i2c_dev, val, I2C_TX_FIFO); + buf_remaining = 0; + tx_fifo_avail--; + } + + BUG_ON(tx_fifo_avail > 0 && buf_remaining > 0); + i2c_dev->msg_buf_remaining = buf_remaining; + i2c_dev->msg_buf = buf; + return 0; +} + +/* + * One of the Tegra I2C blocks is inside the DVC (Digital Voltage Controller) + * block. This block is identical to the rest of the I2C blocks, except that + * it only supports master mode, it has registers moved around, and it needs + * some extra init to get it into I2C mode. The register moves are handled + * by i2c_readl and i2c_writel + */ +static void tegra_dvc_init(struct tegra_i2c_dev *i2c_dev) +{ + u32 val = 0; + val = dvc_readl(i2c_dev, DVC_CTRL_REG3); + val |= DVC_CTRL_REG3_SW_PROG; + val |= DVC_CTRL_REG3_I2C_DONE_INTR_EN; + dvc_writel(i2c_dev, val, DVC_CTRL_REG3); + + val = dvc_readl(i2c_dev, DVC_CTRL_REG1); + val |= DVC_CTRL_REG1_INTR_EN; + dvc_writel(i2c_dev, val, DVC_CTRL_REG1); +} + +static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) +{ + u32 val; + int err = 0; + + clk_enable(i2c_dev->clk); + + tegra_periph_reset_assert(i2c_dev->clk); + udelay(2); + tegra_periph_reset_deassert(i2c_dev->clk); + + if (i2c_dev->is_dvc) + tegra_dvc_init(i2c_dev); + + val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN; + i2c_writel(i2c_dev, val, I2C_CNFG); + i2c_writel(i2c_dev, 0, I2C_INT_MASK); + clk_set_rate(i2c_dev->clk, i2c_dev->bus_clk_rate * 8); + + val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT | + 0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT; + i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL); + + if (tegra_i2c_flush_fifos(i2c_dev)) + err = -ETIMEDOUT; + + clk_disable(i2c_dev->clk); + return err; +} + +static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) +{ + u32 status; + const u32 status_err = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; + struct tegra_i2c_dev *i2c_dev = dev_id; + + status = i2c_readl(i2c_dev, I2C_INT_STATUS); + + if (status == 0) { + dev_warn(i2c_dev->dev, "interrupt with no status\n"); + return IRQ_NONE; + } + + if (unlikely(status & status_err)) { + if (status & I2C_INT_NO_ACK) + i2c_dev->msg_err |= I2C_ERR_NO_ACK; + if (status & I2C_INT_ARBITRATION_LOST) + i2c_dev->msg_err |= I2C_ERR_ARBITRATION_LOST; + complete(&i2c_dev->msg_complete); + goto err; + } + + if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) { + if (i2c_dev->msg_buf_remaining) + tegra_i2c_empty_rx_fifo(i2c_dev); + else + BUG(); + } + + if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) { + if (i2c_dev->msg_buf_remaining) + tegra_i2c_fill_tx_fifo(i2c_dev); + else + tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ); + } + + if ((status & I2C_INT_PACKET_XFER_COMPLETE) && + !i2c_dev->msg_buf_remaining) + complete(&i2c_dev->msg_complete); + + i2c_writel(i2c_dev, status, I2C_INT_STATUS); + if (i2c_dev->is_dvc) + dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); + return IRQ_HANDLED; +err: + /* An error occured, mask all interrupts */ + tegra_i2c_mask_irq(i2c_dev, I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST | + I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ | + I2C_INT_RX_FIFO_DATA_REQ); + i2c_writel(i2c_dev, status, I2C_INT_STATUS); + return IRQ_HANDLED; +} + +static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, + struct i2c_msg *msg, int stop) +{ + u32 packet_header; + u32 int_mask; + int ret; + + tegra_i2c_flush_fifos(i2c_dev); + i2c_writel(i2c_dev, 0xFF, I2C_INT_STATUS); + + if (msg->len == 0) + return -EINVAL; + + i2c_dev->msg_buf = msg->buf; + i2c_dev->msg_buf_remaining = msg->len; + i2c_dev->msg_err = I2C_ERR_NONE; + i2c_dev->msg_read = (msg->flags & I2C_M_RD); + INIT_COMPLETION(i2c_dev->msg_complete); + + packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) | + PACKET_HEADER0_PROTOCOL_I2C | + (i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) | + (1 << PACKET_HEADER0_PACKET_ID_SHIFT); + i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + + packet_header = msg->len - 1; + i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + + packet_header = msg->addr << I2C_HEADER_SLAVE_ADDR_SHIFT; + packet_header |= I2C_HEADER_IE_ENABLE; + if (msg->flags & I2C_M_TEN) + packet_header |= I2C_HEADER_10BIT_ADDR; + if (msg->flags & I2C_M_IGNORE_NAK) + packet_header |= I2C_HEADER_CONT_ON_NAK; + if (msg->flags & I2C_M_NOSTART) + packet_header |= I2C_HEADER_REPEAT_START; + if (msg->flags & I2C_M_RD) + packet_header |= I2C_HEADER_READ; + i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + + if (!(msg->flags & I2C_M_RD)) + tegra_i2c_fill_tx_fifo(i2c_dev); + + int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; + if (msg->flags & I2C_M_RD) + int_mask |= I2C_INT_RX_FIFO_DATA_REQ; + else if (i2c_dev->msg_buf_remaining) + int_mask |= I2C_INT_TX_FIFO_DATA_REQ; + tegra_i2c_unmask_irq(i2c_dev, int_mask); + dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n", + i2c_readl(i2c_dev, I2C_INT_MASK)); + + ret = wait_for_completion_timeout(&i2c_dev->msg_complete, TEGRA_I2C_TIMEOUT); + tegra_i2c_mask_irq(i2c_dev, int_mask); + + if (WARN_ON(ret == 0)) { + dev_err(i2c_dev->dev, "i2c transfer timed out\n"); + + tegra_i2c_init(i2c_dev); + return -ETIMEDOUT; + } + + dev_dbg(i2c_dev->dev, "transfer complete: %d %d %d\n", + ret, completion_done(&i2c_dev->msg_complete), i2c_dev->msg_err); + + if (likely(i2c_dev->msg_err == I2C_ERR_NONE)) + return 0; + + tegra_i2c_init(i2c_dev); + if (i2c_dev->msg_err == I2C_ERR_NO_ACK) { + if (msg->flags & I2C_M_IGNORE_NAK) + return 0; + return -EREMOTEIO; + } + + return -EIO; +} + +static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], + int num) +{ + struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap); + int i; + int ret = 0; + + if (i2c_dev->is_suspended) + return -EBUSY; + + clk_enable(i2c_dev->clk); + for (i = 0; i < num; i++) { + int stop = (i == (num - 1)) ? 1 : 0; + ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], stop); + if (ret) + break; + } + clk_disable(i2c_dev->clk); + return ret ?: i; +} + +static u32 tegra_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm tegra_i2c_algo = { + .master_xfer = tegra_i2c_xfer, + .functionality = tegra_i2c_func, +}; + +static int tegra_i2c_probe(struct platform_device *pdev) +{ + struct tegra_i2c_dev *i2c_dev; + struct tegra_i2c_platform_data *pdata = pdev->dev.platform_data; + struct resource *res; + struct resource *iomem; + struct clk *clk; + struct clk *i2c_clk; + void *base; + int irq; + int ret = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no mem resource\n"); + return -EINVAL; + } + iomem = request_mem_region(res->start, resource_size(res), pdev->name); + if (!iomem) { + dev_err(&pdev->dev, "I2C region already claimed\n"); + return -EBUSY; + } + + base = ioremap(iomem->start, resource_size(iomem)); + if (!base) { + dev_err(&pdev->dev, "Cannot ioremap I2C region\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, "no irq resource\n"); + ret = -EINVAL; + goto err_iounmap; + } + irq = res->start; + + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "missing controller clock"); + ret = PTR_ERR(clk); + goto err_release_region; + } + + i2c_clk = clk_get(&pdev->dev, "i2c"); + if (IS_ERR(i2c_clk)) { + dev_err(&pdev->dev, "missing bus clock"); + ret = PTR_ERR(i2c_clk); + goto err_clk_put; + } + + i2c_dev = kzalloc(sizeof(struct tegra_i2c_dev), GFP_KERNEL); + if (!i2c_dev) { + ret = -ENOMEM; + goto err_i2c_clk_put; + } + + i2c_dev->base = base; + i2c_dev->clk = clk; + i2c_dev->i2c_clk = i2c_clk; + i2c_dev->iomem = iomem; + i2c_dev->adapter.algo = &tegra_i2c_algo; + i2c_dev->irq = irq; + i2c_dev->cont_id = pdev->id; + i2c_dev->dev = &pdev->dev; + i2c_dev->bus_clk_rate = pdata ? pdata->bus_clk_rate : 100000; + + if (pdev->id == 3) + i2c_dev->is_dvc = 1; + init_completion(&i2c_dev->msg_complete); + + platform_set_drvdata(pdev, i2c_dev); + + ret = tegra_i2c_init(i2c_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize i2c controller"); + goto err_free; + } + + ret = request_irq(i2c_dev->irq, tegra_i2c_isr, 0, pdev->name, i2c_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq); + goto err_free; + } + + clk_enable(i2c_dev->i2c_clk); + + i2c_set_adapdata(&i2c_dev->adapter, i2c_dev); + i2c_dev->adapter.owner = THIS_MODULE; + i2c_dev->adapter.class = I2C_CLASS_HWMON; + strlcpy(i2c_dev->adapter.name, "Tegra I2C adapter", + sizeof(i2c_dev->adapter.name)); + i2c_dev->adapter.algo = &tegra_i2c_algo; + i2c_dev->adapter.dev.parent = &pdev->dev; + i2c_dev->adapter.nr = pdev->id; + + ret = i2c_add_numbered_adapter(&i2c_dev->adapter); + if (ret) { + dev_err(&pdev->dev, "Failed to add I2C adapter\n"); + goto err_free_irq; + } + + return 0; +err_free_irq: + free_irq(i2c_dev->irq, i2c_dev); +err_free: + kfree(i2c_dev); +err_i2c_clk_put: + clk_put(i2c_clk); +err_clk_put: + clk_put(clk); +err_release_region: + release_mem_region(iomem->start, resource_size(iomem)); +err_iounmap: + iounmap(base); + return ret; +} + +static int tegra_i2c_remove(struct platform_device *pdev) +{ + struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + i2c_del_adapter(&i2c_dev->adapter); + free_irq(i2c_dev->irq, i2c_dev); + clk_put(i2c_dev->i2c_clk); + clk_put(i2c_dev->clk); + release_mem_region(i2c_dev->iomem->start, + resource_size(i2c_dev->iomem)); + iounmap(i2c_dev->base); + kfree(i2c_dev); + return 0; +} + +#ifdef CONFIG_PM +static int tegra_i2c_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + + i2c_lock_adapter(&i2c_dev->adapter); + i2c_dev->is_suspended = true; + i2c_unlock_adapter(&i2c_dev->adapter); + + return 0; +} + +static int tegra_i2c_resume(struct platform_device *pdev) +{ + struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + int ret; + + i2c_lock_adapter(&i2c_dev->adapter); + + ret = tegra_i2c_init(i2c_dev); + + if (ret) { + i2c_unlock_adapter(&i2c_dev->adapter); + return ret; + } + + i2c_dev->is_suspended = false; + + i2c_unlock_adapter(&i2c_dev->adapter); + + return 0; +} +#endif + +static struct platform_driver tegra_i2c_driver = { + .probe = tegra_i2c_probe, + .remove = tegra_i2c_remove, +#ifdef CONFIG_PM + .suspend = tegra_i2c_suspend, + .resume = tegra_i2c_resume, +#endif + .driver = { + .name = "tegra-i2c", + .owner = THIS_MODULE, + }, +}; + +static int __init tegra_i2c_init_driver(void) +{ + return platform_driver_register(&tegra_i2c_driver); +} + +static void __exit tegra_i2c_exit_driver(void) +{ + platform_driver_unregister(&tegra_i2c_driver); +} + +subsys_initcall(tegra_i2c_init_driver); +module_exit(tegra_i2c_exit_driver); + +MODULE_DESCRIPTION("nVidia Tegra2 I2C Bus Controller driver"); +MODULE_AUTHOR("Colin Cross"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/i2c-tegra.h b/include/linux/i2c-tegra.h new file mode 100644 index 000000000000..9c85da49857a --- /dev/null +++ b/include/linux/i2c-tegra.h @@ -0,0 +1,25 @@ +/* + * drivers/i2c/busses/i2c-tegra.c + * + * Copyright (C) 2010 Google, Inc. + * Author: Colin Cross + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 _LINUX_I2C_TEGRA_H +#define _LINUX_I2C_TEGRA_H + +struct tegra_i2c_platform_data { + unsigned long bus_clk_rate; +}; + +#endif /* _LINUX_I2C_TEGRA_H */ -- cgit v1.2.3 From b1f559ecdc6026ef783ccadc62a61e7da906fcb4 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 26 Jan 2011 09:49:47 +0000 Subject: drm: Mark constant arrays of drm_display_mode const ... and fixup some methods to accept the constant argument. Now that constant module arrays are loaded into read-only memory, using const appropriately has some benefits beyond warning the programmer about likely mistakes. Signed-off-by: Chris Wilson Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_edid.c | 19 ++++++++++--------- drivers/gpu/drm/drm_edid_modes.h | 4 ++-- drivers/gpu/drm/drm_modes.c | 6 +++--- drivers/gpu/drm/i915/intel_sdvo.c | 2 +- drivers/gpu/drm/nouveau/nv17_tv.c | 4 +++- drivers/gpu/drm/nouveau/nv17_tv.h | 2 +- drivers/gpu/drm/nouveau/nv17_tv_modes.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 5 +++-- include/drm/drm_crtc.h | 6 +++--- 9 files changed, 27 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index a245d17165ae..af60d9be9632 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -449,12 +449,11 @@ static void edid_fixup_preferred(struct drm_connector *connector, struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, int hsize, int vsize, int fresh) { + struct drm_display_mode *mode = NULL; int i; - struct drm_display_mode *ptr, *mode; - mode = NULL; for (i = 0; i < drm_num_dmt_modes; i++) { - ptr = &drm_dmt_modes[i]; + const struct drm_display_mode *ptr = &drm_dmt_modes[i]; if (hsize == ptr->hdisplay && vsize == ptr->vdisplay && fresh == drm_mode_vrefresh(ptr)) { @@ -885,7 +884,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, } static bool -mode_is_rb(struct drm_display_mode *mode) +mode_is_rb(const struct drm_display_mode *mode) { return (mode->htotal - mode->hdisplay == 160) && (mode->hsync_end - mode->hdisplay == 80) && @@ -894,7 +893,8 @@ mode_is_rb(struct drm_display_mode *mode) } static bool -mode_in_hsync_range(struct drm_display_mode *mode, struct edid *edid, u8 *t) +mode_in_hsync_range(const struct drm_display_mode *mode, + struct edid *edid, u8 *t) { int hsync, hmin, hmax; @@ -910,7 +910,8 @@ mode_in_hsync_range(struct drm_display_mode *mode, struct edid *edid, u8 *t) } static bool -mode_in_vsync_range(struct drm_display_mode *mode, struct edid *edid, u8 *t) +mode_in_vsync_range(const struct drm_display_mode *mode, + struct edid *edid, u8 *t) { int vsync, vmin, vmax; @@ -941,7 +942,7 @@ range_pixel_clock(struct edid *edid, u8 *t) } static bool -mode_in_range(struct drm_display_mode *mode, struct edid *edid, +mode_in_range(const struct drm_display_mode *mode, struct edid *edid, struct detailed_timing *timing) { u32 max_clock; @@ -1472,7 +1473,7 @@ int drm_add_modes_noedid(struct drm_connector *connector, int hdisplay, int vdisplay) { int i, count, num_modes = 0; - struct drm_display_mode *mode, *ptr; + struct drm_display_mode *mode; struct drm_device *dev = connector->dev; count = sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode); @@ -1482,7 +1483,7 @@ int drm_add_modes_noedid(struct drm_connector *connector, vdisplay = 0; for (i = 0; i < count; i++) { - ptr = &drm_dmt_modes[i]; + const struct drm_display_mode *ptr = &drm_dmt_modes[i]; if (hdisplay && vdisplay) { /* * Only when two are valid, they will be used to check diff --git a/drivers/gpu/drm/drm_edid_modes.h b/drivers/gpu/drm/drm_edid_modes.h index 6eb7592e152f..5f2064489fd5 100644 --- a/drivers/gpu/drm/drm_edid_modes.h +++ b/drivers/gpu/drm/drm_edid_modes.h @@ -32,7 +32,7 @@ * This table is copied from xfree86/modes/xf86EdidModes.c. * But the mode with Reduced blank feature is deleted. */ -static struct drm_display_mode drm_dmt_modes[] = { +static const struct drm_display_mode drm_dmt_modes[] = { /* 640x350@85Hz */ { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 31500, 640, 672, 736, 832, 0, 350, 382, 385, 445, 0, @@ -266,7 +266,7 @@ static struct drm_display_mode drm_dmt_modes[] = { static const int drm_num_dmt_modes = sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode); -static struct drm_display_mode edid_est_modes[] = { +static const struct drm_display_mode edid_est_modes[] = { { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, 968, 1056, 0, 600, 601, 605, 628, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */ diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 58e65f92c232..25bf87390f53 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -593,7 +593,7 @@ EXPORT_SYMBOL(drm_mode_height); * * Return @modes's hsync rate in kHz, rounded to the nearest int. */ -int drm_mode_hsync(struct drm_display_mode *mode) +int drm_mode_hsync(const struct drm_display_mode *mode) { unsigned int calc_val; @@ -627,7 +627,7 @@ EXPORT_SYMBOL(drm_mode_hsync); * If it is 70.288, it will return 70Hz. * If it is 59.6, it will return 60Hz. */ -int drm_mode_vrefresh(struct drm_display_mode *mode) +int drm_mode_vrefresh(const struct drm_display_mode *mode) { int refresh = 0; unsigned int calc_val; @@ -725,7 +725,7 @@ EXPORT_SYMBOL(drm_mode_set_crtcinfo); * a pointer to it. Used to create new instances of established modes. */ struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, - struct drm_display_mode *mode) + const struct drm_display_mode *mode) { struct drm_display_mode *nmode; int new_id; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 6a09c1413d60..318f398e6b2e 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1460,7 +1460,7 @@ static void intel_sdvo_get_ddc_modes(struct drm_connector *connector) * Note! This is in reply order (see loop in get_tv_modes). * XXX: all 60Hz refresh? */ -struct drm_display_mode sdvo_tv_modes[] = { +static const struct drm_display_mode sdvo_tv_modes[] = { { DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 5815, 320, 321, 384, 416, 0, 200, 201, 232, 233, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c index 28119fd19d03..3900cebba560 100644 --- a/drivers/gpu/drm/nouveau/nv17_tv.c +++ b/drivers/gpu/drm/nouveau/nv17_tv.c @@ -197,10 +197,12 @@ static int nv17_tv_get_ld_modes(struct drm_encoder *encoder, struct drm_connector *connector) { struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); - struct drm_display_mode *mode, *tv_mode; + const struct drm_display_mode *tv_mode; int n = 0; for (tv_mode = nv17_tv_modes; tv_mode->hdisplay; tv_mode++) { + struct drm_display_mode *mode; + mode = drm_mode_duplicate(encoder->dev, tv_mode); mode->clock = tv_norm->tv_enc_mode.vrefresh * diff --git a/drivers/gpu/drm/nouveau/nv17_tv.h b/drivers/gpu/drm/nouveau/nv17_tv.h index 6bf03840f9eb..622e72221682 100644 --- a/drivers/gpu/drm/nouveau/nv17_tv.h +++ b/drivers/gpu/drm/nouveau/nv17_tv.h @@ -112,7 +112,7 @@ extern struct nv17_tv_norm_params { } nv17_tv_norms[NUM_TV_NORMS]; #define get_tv_norm(enc) (&nv17_tv_norms[to_tv_enc(enc)->tv_norm]) -extern struct drm_display_mode nv17_tv_modes[]; +extern const struct drm_display_mode nv17_tv_modes[]; static inline int interpolate(int y0, int y1, int y2, int x) { diff --git a/drivers/gpu/drm/nouveau/nv17_tv_modes.c b/drivers/gpu/drm/nouveau/nv17_tv_modes.c index 9d3893c50a41..4d1d29f60307 100644 --- a/drivers/gpu/drm/nouveau/nv17_tv_modes.c +++ b/drivers/gpu/drm/nouveau/nv17_tv_modes.c @@ -438,7 +438,7 @@ void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state) /* Timings similar to the ones the blob sets */ -struct drm_display_mode nv17_tv_modes[] = { +const struct drm_display_mode nv17_tv_modes[] = { { DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 0, 320, 344, 392, 560, 0, 200, 200, 202, 220, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 29113c9b26a8..b3a2cd5118d7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -345,7 +345,7 @@ static enum drm_connector_status return connector_status_disconnected; } -static struct drm_display_mode vmw_ldu_connector_builtin[] = { +static const struct drm_display_mode vmw_ldu_connector_builtin[] = { /* 640x480@60Hz */ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, 752, 800, 0, 480, 489, 492, 525, 0, @@ -429,7 +429,6 @@ static int vmw_ldu_connector_fill_modes(struct drm_connector *connector, struct drm_device *dev = connector->dev; struct vmw_private *dev_priv = vmw_priv(dev); struct drm_display_mode *mode = NULL; - struct drm_display_mode *bmode; struct drm_display_mode prefmode = { DRM_MODE("preferred", DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -459,6 +458,8 @@ static int vmw_ldu_connector_fill_modes(struct drm_connector *connector, } for (i = 0; vmw_ldu_connector_builtin[i].type != 0; i++) { + const struct drm_display_mode *bmode; + bmode = &vmw_ldu_connector_builtin[i]; if (bmode->hdisplay > max_width || bmode->vdisplay > max_height) diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 080a6e33470e..60edf9be31e5 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -659,7 +659,7 @@ extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid extern void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode); extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode); extern struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, - struct drm_display_mode *mode); + const 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); @@ -685,8 +685,8 @@ extern void drm_mode_validate_size(struct drm_device *dev, extern void drm_mode_prune_invalid(struct drm_device *dev, struct list_head *mode_list, bool verbose); extern void drm_mode_sort(struct list_head *mode_list); -extern int drm_mode_hsync(struct drm_display_mode *mode); -extern int drm_mode_vrefresh(struct drm_display_mode *mode); +extern int drm_mode_hsync(const struct drm_display_mode *mode); +extern int drm_mode_vrefresh(const struct drm_display_mode *mode); extern void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags); extern void drm_mode_connector_list_update(struct drm_connector *connector); -- cgit v1.2.3 From 7811bddb6654337fd85837ef14c1a96a0c264745 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 26 Jan 2011 18:33:25 +0000 Subject: drm: Remove unused members from struct drm_open_hash Signed-off-by: Chris Wilson Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_hashtab.c | 27 ++++++++------------------- include/drm/drm_hashtab.h | 6 +----- 2 files changed, 9 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_hashtab.c b/drivers/gpu/drm/drm_hashtab.c index a93d7b4ddaa6..e3a75688f3cd 100644 --- a/drivers/gpu/drm/drm_hashtab.c +++ b/drivers/gpu/drm/drm_hashtab.c @@ -39,27 +39,18 @@ int drm_ht_create(struct drm_open_hash *ht, unsigned int order) { - unsigned int i; + unsigned int size = 1 << order; - ht->size = 1 << order; ht->order = order; - ht->fill = 0; ht->table = NULL; - ht->use_vmalloc = ((ht->size * sizeof(*ht->table)) > PAGE_SIZE); - if (!ht->use_vmalloc) { - ht->table = kcalloc(ht->size, sizeof(*ht->table), GFP_KERNEL); - } - if (!ht->table) { - ht->use_vmalloc = 1; - ht->table = vmalloc(ht->size*sizeof(*ht->table)); - } + if (size <= PAGE_SIZE / sizeof(*ht->table)) + ht->table = kcalloc(size, sizeof(*ht->table), GFP_KERNEL); + else + ht->table = vzalloc(size*sizeof(*ht->table)); if (!ht->table) { DRM_ERROR("Out of memory for hash table\n"); return -ENOMEM; } - for (i=0; i< ht->size; ++i) { - INIT_HLIST_HEAD(&ht->table[i]); - } return 0; } EXPORT_SYMBOL(drm_ht_create); @@ -180,7 +171,6 @@ int drm_ht_remove_key(struct drm_open_hash *ht, unsigned long key) list = drm_ht_find_key(ht, key); if (list) { hlist_del_init(list); - ht->fill--; return 0; } return -EINVAL; @@ -189,7 +179,6 @@ int drm_ht_remove_key(struct drm_open_hash *ht, unsigned long key) int drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item) { hlist_del_init(&item->head); - ht->fill--; return 0; } EXPORT_SYMBOL(drm_ht_remove_item); @@ -197,10 +186,10 @@ EXPORT_SYMBOL(drm_ht_remove_item); void drm_ht_remove(struct drm_open_hash *ht) { if (ht->table) { - if (ht->use_vmalloc) - vfree(ht->table); - else + if ((PAGE_SIZE / sizeof(*ht->table)) >> ht->order) kfree(ht->table); + else + vfree(ht->table); ht->table = NULL; } } diff --git a/include/drm/drm_hashtab.h b/include/drm/drm_hashtab.h index 0af087a4d3b3..3650d5d011ee 100644 --- a/include/drm/drm_hashtab.h +++ b/include/drm/drm_hashtab.h @@ -45,14 +45,10 @@ struct drm_hash_item { }; struct drm_open_hash { - unsigned int size; - unsigned int order; - unsigned int fill; struct hlist_head *table; - int use_vmalloc; + u8 order; }; - extern int drm_ht_create(struct drm_open_hash *ht, unsigned int order); extern int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item); extern int drm_ht_just_insert_please(struct drm_open_hash *ht, struct drm_hash_item *item, -- cgit v1.2.3 From e8a4e37716dbc964e1cd18bca1a62fbd11805c1d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 22 Feb 2011 17:42:56 -0800 Subject: xfrm: Mark flowi arg const in key extraction helpers. Signed-off-by: David S. Miller --- include/net/xfrm.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 1f6e8a0eb544..2de3dae3d297 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -790,7 +790,7 @@ static __inline__ int addr_match(void *token1, void *token2, int prefixlen) } static __inline__ -__be16 xfrm_flowi_sport(struct flowi *fl) +__be16 xfrm_flowi_sport(const struct flowi *fl) { __be16 port; switch(fl->proto) { @@ -817,7 +817,7 @@ __be16 xfrm_flowi_sport(struct flowi *fl) } static __inline__ -__be16 xfrm_flowi_dport(struct flowi *fl) +__be16 xfrm_flowi_dport(const struct flowi *fl) { __be16 port; switch(fl->proto) { @@ -1127,7 +1127,7 @@ static inline int xfrm6_policy_check_reverse(struct sock *sk, int dir, #endif static __inline__ -xfrm_address_t *xfrm_flowi_daddr(struct flowi *fl, unsigned short family) +xfrm_address_t *xfrm_flowi_daddr(const struct flowi *fl, unsigned short family) { switch (family){ case AF_INET: @@ -1139,7 +1139,7 @@ xfrm_address_t *xfrm_flowi_daddr(struct flowi *fl, unsigned short family) } static __inline__ -xfrm_address_t *xfrm_flowi_saddr(struct flowi *fl, unsigned short family) +xfrm_address_t *xfrm_flowi_saddr(const struct flowi *fl, unsigned short family) { switch (family){ case AF_INET: @@ -1151,7 +1151,7 @@ xfrm_address_t *xfrm_flowi_saddr(struct flowi *fl, unsigned short family) } static __inline__ -void xfrm_flowi_addr_get(struct flowi *fl, +void xfrm_flowi_addr_get(const struct flowi *fl, xfrm_address_t *saddr, xfrm_address_t *daddr, unsigned short family) { @@ -1204,7 +1204,7 @@ xfrm_state_addr_check(struct xfrm_state *x, } static __inline__ int -xfrm_state_addr_flow_check(struct xfrm_state *x, struct flowi *fl, +xfrm_state_addr_flow_check(struct xfrm_state *x, const struct flowi *fl, unsigned short family) { switch (family) { -- cgit v1.2.3 From 05d8402576c9c1b85bfc9e4f9d6a21c27ccbd5b1 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 22 Feb 2011 17:47:10 -0800 Subject: xfrm: Mark flowi arg to ->get_tos() const. Signed-off-by: David S. Miller --- include/net/xfrm.h | 2 +- net/ipv4/xfrm4_policy.c | 2 +- net/ipv6/xfrm6_policy.c | 2 +- net/xfrm/xfrm_policy.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 2de3dae3d297..2c0927b04436 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -273,7 +273,7 @@ struct xfrm_policy_afinfo { void (*decode_session)(struct sk_buff *skb, struct flowi *fl, int reverse); - int (*get_tos)(struct flowi *fl); + int (*get_tos)(const struct flowi *fl); int (*init_path)(struct xfrm_dst *path, struct dst_entry *dst, int nfheader_len); diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 19fbdec6baaa..ef12e6830468 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -56,7 +56,7 @@ static int xfrm4_get_saddr(struct net *net, return 0; } -static int xfrm4_get_tos(struct flowi *fl) +static int xfrm4_get_tos(const struct flowi *fl) { return IPTOS_RT_MASK & fl->fl4_tos; /* Strip ECN bits */ } diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 834dc02f1d4f..753e9a1db379 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -67,7 +67,7 @@ static int xfrm6_get_saddr(struct net *net, return 0; } -static int xfrm6_get_tos(struct flowi *fl) +static int xfrm6_get_tos(const struct flowi *fl) { return 0; } diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 7a8e2c77d08f..f8ccb97b234e 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1256,7 +1256,7 @@ xfrm_tmpl_resolve(struct xfrm_policy **pols, int npols, struct flowi *fl, * still valid. */ -static inline int xfrm_get_tos(struct flowi *fl, int family) +static inline int xfrm_get_tos(const struct flowi *fl, int family) { struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); int tos; -- cgit v1.2.3 From 0c7b3eefb4ab8df245e94feb0d83c1c3450a3d87 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 22 Feb 2011 17:48:57 -0800 Subject: xfrm: Mark flowi arg to ->fill_dst() const. Signed-off-by: David S. Miller --- include/net/xfrm.h | 2 +- net/ipv4/xfrm4_policy.c | 2 +- net/ipv6/xfrm6_policy.c | 2 +- net/xfrm/xfrm_policy.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 2c0927b04436..c77407fdfa87 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -279,7 +279,7 @@ struct xfrm_policy_afinfo { int nfheader_len); int (*fill_dst)(struct xfrm_dst *xdst, struct net_device *dev, - struct flowi *fl); + const struct flowi *fl); }; extern int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo); diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index ef12e6830468..1e9844d1f8c5 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -68,7 +68,7 @@ static int xfrm4_init_path(struct xfrm_dst *path, struct dst_entry *dst, } static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, - struct flowi *fl) + const struct flowi *fl) { struct rtable *rt = (struct rtable *)xdst->route; diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 753e9a1db379..f2fa904b8a91 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -87,7 +87,7 @@ static int xfrm6_init_path(struct xfrm_dst *path, struct dst_entry *dst, } static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, - struct flowi *fl) + const struct flowi *fl) { struct rt6_info *rt = (struct rt6_info*)xdst->route; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index f8ccb97b234e..fa0b7f33874b 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1369,7 +1369,7 @@ static inline int xfrm_init_path(struct xfrm_dst *path, struct dst_entry *dst, } static inline int xfrm_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, - struct flowi *fl) + const struct flowi *fl) { struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(xdst->u.dst.ops->family); -- cgit v1.2.3 From 73e5ebb20f2809e2eb0b904448481e010c2599d7 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 22 Feb 2011 17:51:44 -0800 Subject: xfrm: Mark flowi arg to ->init_tempsel() const. Signed-off-by: David S. Miller --- include/net/xfrm.h | 3 ++- net/ipv4/xfrm4_state.c | 2 +- net/ipv6/xfrm6_state.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index c77407fdfa87..614c296c4532 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -300,7 +300,8 @@ struct xfrm_state_afinfo { const struct xfrm_type *type_map[IPPROTO_MAX]; struct xfrm_mode *mode_map[XFRM_MODE_MAX]; int (*init_flags)(struct xfrm_state *x); - void (*init_tempsel)(struct xfrm_selector *sel, struct flowi *fl); + void (*init_tempsel)(struct xfrm_selector *sel, + const struct flowi *fl); void (*init_temprop)(struct xfrm_state *x, struct xfrm_tmpl *tmpl, xfrm_address_t *daddr, xfrm_address_t *saddr); int (*tmpl_sort)(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n); diff --git a/net/ipv4/xfrm4_state.c b/net/ipv4/xfrm4_state.c index 47947624eccc..19eb56067727 100644 --- a/net/ipv4/xfrm4_state.c +++ b/net/ipv4/xfrm4_state.c @@ -21,7 +21,7 @@ static int xfrm4_init_flags(struct xfrm_state *x) } static void -__xfrm4_init_tempsel(struct xfrm_selector *sel, struct flowi *fl) +__xfrm4_init_tempsel(struct xfrm_selector *sel, const struct flowi *fl) { sel->daddr.a4 = fl->fl4_dst; sel->saddr.a4 = fl->fl4_src; diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c index a67575d472a3..68a14c0339db 100644 --- a/net/ipv6/xfrm6_state.c +++ b/net/ipv6/xfrm6_state.c @@ -20,7 +20,7 @@ #include static void -__xfrm6_init_tempsel(struct xfrm_selector *sel, struct flowi *fl) +__xfrm6_init_tempsel(struct xfrm_selector *sel, const struct flowi *fl) { /* Initialize temporary selector matching only * to current session. */ -- cgit v1.2.3 From 8f029de281b26ec9fd5cd77294db1d35d9876f1a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 22 Feb 2011 17:59:59 -0800 Subject: xfrm: Mark flowi arg to xfrm_type->reject() const. Signed-off-by: David S. Miller --- include/net/xfrm.h | 3 ++- net/ipv6/mip6.c | 3 ++- net/xfrm/xfrm_policy.c | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 614c296c4532..cbe00035416d 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -334,7 +334,8 @@ struct xfrm_type { void (*destructor)(struct xfrm_state *); int (*input)(struct xfrm_state *, struct sk_buff *skb); int (*output)(struct xfrm_state *, struct sk_buff *pskb); - int (*reject)(struct xfrm_state *, struct sk_buff *, struct flowi *); + int (*reject)(struct xfrm_state *, struct sk_buff *, + const struct flowi *); int (*hdr_offset)(struct xfrm_state *, struct sk_buff *, u8 **); /* Estimate maximal size of result of transformation of a dgram */ u32 (*get_mtu)(struct xfrm_state *, int size); diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c index d6e9599d0705..f3e3ca938a54 100644 --- a/net/ipv6/mip6.c +++ b/net/ipv6/mip6.c @@ -203,7 +203,8 @@ static inline int mip6_report_rl_allow(struct timeval *stamp, return allow; } -static int mip6_destopt_reject(struct xfrm_state *x, struct sk_buff *skb, struct flowi *fl) +static int mip6_destopt_reject(struct xfrm_state *x, struct sk_buff *skb, + const struct flowi *fl) { struct net *net = xs_net(x); struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index fa0b7f33874b..ccd47cf1765c 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1907,7 +1907,7 @@ int xfrm_lookup(struct net *net, struct dst_entry **dst_p, struct flowi *fl, EXPORT_SYMBOL(xfrm_lookup); static inline int -xfrm_secpath_reject(int idx, struct sk_buff *skb, struct flowi *fl) +xfrm_secpath_reject(int idx, struct sk_buff *skb, const struct flowi *fl) { struct xfrm_state *x; -- cgit v1.2.3 From 1744a8fe09e5db7315a57da52fa7c1afa779cfa0 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 22 Feb 2011 18:02:12 -0800 Subject: xfrm: Mark token args to addr_match() const. Also, make it return a real bool. Signed-off-by: David S. Miller --- include/net/xfrm.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index cbe00035416d..2328532f0076 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -765,10 +765,11 @@ static inline void xfrm_state_hold(struct xfrm_state *x) atomic_inc(&x->refcnt); } -static __inline__ int addr_match(void *token1, void *token2, int prefixlen) +static inline bool addr_match(const void *token1, const void *token2, + int prefixlen) { - __be32 *a1 = token1; - __be32 *a2 = token2; + const __be32 *a1 = token1; + const __be32 *a2 = token2; int pdw; int pbi; @@ -777,7 +778,7 @@ static __inline__ int addr_match(void *token1, void *token2, int prefixlen) if (pdw) if (memcmp(a1, a2, pdw << 2)) - return 0; + return false; if (pbi) { __be32 mask; @@ -785,10 +786,10 @@ static __inline__ int addr_match(void *token1, void *token2, int prefixlen) mask = htonl((0xffffffff) << (32 - pbi)); if ((a1[pdw] ^ a2[pdw]) & mask) - return 0; + return false; } - return 1; + return true; } static __inline__ -- cgit v1.2.3 From e1ad2ab2cf0cabcd81861e2c61870fc27bb27ded Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 22 Feb 2011 18:07:39 -0800 Subject: xfrm: Mark flowi arg to xfrm_selector_match() const. Signed-off-by: David S. Miller --- include/net/xfrm.h | 3 ++- net/xfrm/xfrm_policy.c | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 2328532f0076..b965ad795b60 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -843,7 +843,8 @@ __be16 xfrm_flowi_dport(const struct flowi *fl) return port; } -extern int xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl, +extern int xfrm_selector_match(struct xfrm_selector *sel, + const struct flowi *fl, unsigned short family); #ifdef CONFIG_SECURITY_NETWORK_XFRM diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index ccd47cf1765c..71e6dc25bc5c 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -58,7 +58,7 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, int dir); static inline int -__xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl) +__xfrm4_selector_match(struct xfrm_selector *sel, const struct flowi *fl) { return addr_match(&fl->fl4_dst, &sel->daddr, sel->prefixlen_d) && addr_match(&fl->fl4_src, &sel->saddr, sel->prefixlen_s) && @@ -69,7 +69,7 @@ __xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl) } static inline int -__xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl) +__xfrm6_selector_match(struct xfrm_selector *sel, const struct flowi *fl) { return addr_match(&fl->fl6_dst, &sel->daddr, sel->prefixlen_d) && addr_match(&fl->fl6_src, &sel->saddr, sel->prefixlen_s) && @@ -79,8 +79,8 @@ __xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl) (fl->oif == sel->ifindex || !sel->ifindex); } -int xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl, - unsigned short family) +int xfrm_selector_match(struct xfrm_selector *sel, const struct flowi *fl, + unsigned short family) { switch (family) { case AF_INET: -- cgit v1.2.3 From e33f770426674a565a188042caf3f974f8b3722d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 22 Feb 2011 18:13:15 -0800 Subject: xfrm: Mark flowi arg to security_xfrm_state_pol_flow_match() const. Signed-off-by: David S. Miller --- include/linux/security.h | 7 ++++--- security/capability.c | 2 +- security/security.c | 3 ++- security/selinux/include/xfrm.h | 2 +- security/selinux/xfrm.c | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/security.h b/include/linux/security.h index b2b7f9749f5e..9b5f184a7f65 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1623,7 +1623,7 @@ struct security_operations { int (*xfrm_policy_lookup) (struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir); int (*xfrm_state_pol_flow_match) (struct xfrm_state *x, struct xfrm_policy *xp, - struct flowi *fl); + const struct flowi *fl); int (*xfrm_decode_session) (struct sk_buff *skb, u32 *secid, int ckall); #endif /* CONFIG_SECURITY_NETWORK_XFRM */ @@ -2761,7 +2761,8 @@ int security_xfrm_state_delete(struct xfrm_state *x); void security_xfrm_state_free(struct xfrm_state *x); int security_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir); int security_xfrm_state_pol_flow_match(struct xfrm_state *x, - struct xfrm_policy *xp, struct flowi *fl); + struct xfrm_policy *xp, + const struct flowi *fl); int security_xfrm_decode_session(struct sk_buff *skb, u32 *secid); void security_skb_classify_flow(struct sk_buff *skb, struct flowi *fl); @@ -2813,7 +2814,7 @@ static inline int security_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_s } static inline int security_xfrm_state_pol_flow_match(struct xfrm_state *x, - struct xfrm_policy *xp, struct flowi *fl) + struct xfrm_policy *xp, const struct flowi *fl) { return 1; } diff --git a/security/capability.c b/security/capability.c index 2a5df2b7da83..b8eeaee5c99e 100644 --- a/security/capability.c +++ b/security/capability.c @@ -760,7 +760,7 @@ static int cap_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 sk_sid, u8 dir) static int cap_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp, - struct flowi *fl) + const struct flowi *fl) { return 1; } diff --git a/security/security.c b/security/security.c index 7b7308ace8c5..8ef1f7dff277 100644 --- a/security/security.c +++ b/security/security.c @@ -1233,7 +1233,8 @@ int security_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir) } int security_xfrm_state_pol_flow_match(struct xfrm_state *x, - struct xfrm_policy *xp, struct flowi *fl) + struct xfrm_policy *xp, + const struct flowi *fl) { return security_ops->xfrm_state_pol_flow_match(x, xp, fl); } diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 13128f9a3e5a..b43813c9e049 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -19,7 +19,7 @@ void selinux_xfrm_state_free(struct xfrm_state *x); int selinux_xfrm_state_delete(struct xfrm_state *x); int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir); int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, - struct xfrm_policy *xp, struct flowi *fl); + struct xfrm_policy *xp, const struct flowi *fl); /* * Extract the security blob from the sock (it's actually on the socket) diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index fff78d3b51a2..c43ab542246c 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -112,7 +112,7 @@ int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir) */ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp, - struct flowi *fl) + const struct flowi *fl) { u32 state_sid; int rc; -- cgit v1.2.3 From b520e9f616f4f29c8d2557ba704b74ce6d79ff07 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 22 Feb 2011 18:24:19 -0800 Subject: xfrm: Mark flowi arg to xfrm_state_find() const. Signed-off-by: David S. Miller --- include/net/xfrm.h | 6 ++++-- net/xfrm/xfrm_state.c | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index b965ad795b60..bb824a5d71bf 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1328,8 +1328,10 @@ extern int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk, int (*func)(struct xfrm_state *, int, void*), void *); extern void xfrm_state_walk_done(struct xfrm_state_walk *walk); extern struct xfrm_state *xfrm_state_alloc(struct net *net); -extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, - struct flowi *fl, struct xfrm_tmpl *tmpl, +extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, + xfrm_address_t *saddr, + const struct flowi *fl, + struct xfrm_tmpl *tmpl, struct xfrm_policy *pol, int *err, unsigned short family); extern struct xfrm_state *xfrm_stateonly_find(struct net *net, u32 mark, diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 674f2780cedd..30a0f178819c 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -785,7 +785,7 @@ static void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x, struct xfrm_state * xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, - struct flowi *fl, struct xfrm_tmpl *tmpl, + const struct flowi *fl, struct xfrm_tmpl *tmpl, struct xfrm_policy *pol, int *err, unsigned short family) { -- cgit v1.2.3 From 0730b9a1504cb76f80c97d90ff82f8daeb1243a3 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 22 Feb 2011 18:27:22 -0800 Subject: net: Mark flowi arg to flow_cache_uli_match() const. Signed-off-by: David S. Miller --- include/net/flow.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/flow.h b/include/net/flow.h index 1ae901f24436..f4270d4b22c3 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -112,7 +112,8 @@ extern struct flow_cache_object *flow_cache_lookup( extern void flow_cache_flush(void); extern atomic_t flow_cache_genid; -static inline int flow_cache_uli_match(struct flowi *fl1, struct flowi *fl2) +static inline int flow_cache_uli_match(const struct flowi *fl1, + const struct flowi *fl2) { return (fl1->proto == fl2->proto && !memcmp(&fl1->uli_u, &fl2->uli_u, sizeof(fl1->uli_u))); -- cgit v1.2.3 From dee9f4bceb5fd9dbfcc1567148fccdbf16d6a38a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 22 Feb 2011 18:44:31 -0800 Subject: net: Make flow cache paths use a const struct flowi. Signed-off-by: David S. Miller --- include/net/dst.h | 10 ++++++---- include/net/flow.h | 4 ++-- net/core/flow.c | 14 +++++++------- net/xfrm/xfrm_policy.c | 13 ++++++++----- 4 files changed, 23 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index 23b564d3e110..4fedffd7c56f 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -428,20 +428,22 @@ enum { struct flowi; #ifndef CONFIG_XFRM static inline int xfrm_lookup(struct net *net, struct dst_entry **dst_p, - struct flowi *fl, struct sock *sk, int flags) + const struct flowi *fl, struct sock *sk, + int flags) { return 0; } static inline int __xfrm_lookup(struct net *net, struct dst_entry **dst_p, - struct flowi *fl, struct sock *sk, int flags) + const struct flowi *fl, struct sock *sk, + int flags) { return 0; } #else extern int xfrm_lookup(struct net *net, struct dst_entry **dst_p, - struct flowi *fl, struct sock *sk, int flags); + const struct flowi *fl, struct sock *sk, int flags); extern int __xfrm_lookup(struct net *net, struct dst_entry **dst_p, - struct flowi *fl, struct sock *sk, int flags); + const struct flowi *fl, struct sock *sk, int flags); #endif #endif diff --git a/include/net/flow.h b/include/net/flow.h index f4270d4b22c3..f2080e65276d 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -102,11 +102,11 @@ struct flow_cache_ops { }; typedef struct flow_cache_object *(*flow_resolve_t)( - struct net *net, struct flowi *key, u16 family, + struct net *net, const struct flowi *key, u16 family, u8 dir, struct flow_cache_object *oldobj, void *ctx); extern struct flow_cache_object *flow_cache_lookup( - struct net *net, struct flowi *key, u16 family, + struct net *net, const struct flowi *key, u16 family, u8 dir, flow_resolve_t resolver, void *ctx); extern void flow_cache_flush(void); diff --git a/net/core/flow.c b/net/core/flow.c index 127c8a7ffd61..990703b8863b 100644 --- a/net/core/flow.c +++ b/net/core/flow.c @@ -172,9 +172,9 @@ static void flow_new_hash_rnd(struct flow_cache *fc, static u32 flow_hash_code(struct flow_cache *fc, struct flow_cache_percpu *fcp, - struct flowi *key) + const struct flowi *key) { - u32 *k = (u32 *) key; + const u32 *k = (const u32 *) key; return jhash2(k, (sizeof(*key) / sizeof(u32)), fcp->hash_rnd) & (flow_cache_hash_size(fc) - 1); @@ -186,17 +186,17 @@ typedef unsigned long flow_compare_t; * important assumptions that we can here, such as alignment and * constant size. */ -static int flow_key_compare(struct flowi *key1, struct flowi *key2) +static int flow_key_compare(const struct flowi *key1, const struct flowi *key2) { - flow_compare_t *k1, *k1_lim, *k2; + const flow_compare_t *k1, *k1_lim, *k2; const int n_elem = sizeof(struct flowi) / sizeof(flow_compare_t); BUILD_BUG_ON(sizeof(struct flowi) % sizeof(flow_compare_t)); - k1 = (flow_compare_t *) key1; + k1 = (const flow_compare_t *) key1; k1_lim = k1 + n_elem; - k2 = (flow_compare_t *) key2; + k2 = (const flow_compare_t *) key2; do { if (*k1++ != *k2++) @@ -207,7 +207,7 @@ static int flow_key_compare(struct flowi *key1, struct flowi *key2) } struct flow_cache_object * -flow_cache_lookup(struct net *net, struct flowi *key, u16 family, u8 dir, +flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, flow_resolve_t resolver, void *ctx) { struct flow_cache *fc = &flow_cache_global; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index ef899a8e33ce..28c865adf609 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -954,7 +954,7 @@ __xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir } static struct flow_cache_object * -xfrm_policy_lookup(struct net *net, struct flowi *fl, u16 family, +xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, struct flow_cache_object *old_obj, void *ctx) { struct xfrm_policy *pol; @@ -990,7 +990,8 @@ static inline int policy_to_flow_dir(int dir) } } -static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl) +static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, + const struct flowi *fl) { struct xfrm_policy *pol; @@ -1629,7 +1630,7 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols, } static struct flow_cache_object * -xfrm_bundle_lookup(struct net *net, struct flowi *fl, u16 family, u8 dir, +xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, struct flow_cache_object *oldflo, void *ctx) { struct dst_entry *dst_orig = (struct dst_entry *)ctx; @@ -1733,7 +1734,8 @@ error: * At the moment we eat a raw IP route. Mostly to speed up lookups * on interfaces with disabled IPsec. */ -int __xfrm_lookup(struct net *net, struct dst_entry **dst_p, struct flowi *fl, +int __xfrm_lookup(struct net *net, struct dst_entry **dst_p, + const struct flowi *fl, struct sock *sk, int flags) { struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX]; @@ -1889,7 +1891,8 @@ dropdst: } EXPORT_SYMBOL(__xfrm_lookup); -int xfrm_lookup(struct net *net, struct dst_entry **dst_p, struct flowi *fl, +int xfrm_lookup(struct net *net, struct dst_entry **dst_p, + const struct flowi *fl, struct sock *sk, int flags) { int err = __xfrm_lookup(net, dst_p, fl, sk, flags); -- cgit v1.2.3 From a2c06ee2fe5b48a71e697bae00c6e7195fc016b6 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 23 Feb 2011 14:24:01 +1000 Subject: Revert "ttm: Include the 'struct dev' when using the DMA API." This reverts commit 5a893fc28f0393adb7c885a871b8c59e623fd528. This causes a use after free in the ttm free alloc pages path, when it tries to get the be after the be has been destroyed. Signed-off-by: Dave Airlie --- drivers/gpu/drm/nouveau/nouveau_mem.c | 1 - drivers/gpu/drm/radeon/radeon_ttm.c | 1 - drivers/gpu/drm/ttm/ttm_page_alloc.c | 11 +++++------ drivers/gpu/drm/ttm/ttm_tt.c | 4 ++-- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 2 +- include/drm/ttm/ttm_bo_driver.h | 1 - include/drm/ttm/ttm_page_alloc.h | 8 ++------ 7 files changed, 10 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 2b4e5e912110..123969dd4f56 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -409,7 +409,6 @@ nouveau_mem_vram_init(struct drm_device *dev) if (ret) return ret; - dev_priv->ttm.bdev.dev = dev->dev; ret = ttm_bo_device_init(&dev_priv->ttm.bdev, dev_priv->ttm.bo_global_ref.ref.object, &nouveau_bo_driver, DRM_FILE_PAGE_OFFSET, diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 177adc884b74..df5734d0c4af 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -513,7 +513,6 @@ int radeon_ttm_init(struct radeon_device *rdev) if (r) { return r; } - rdev->mman.bdev.dev = rdev->dev; /* No others user of address space so set it to 0 */ r = ttm_bo_device_init(&rdev->mman.bdev, rdev->mman.bo_global_ref.ref.object, diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c index 35849dbf3ab5..737a2a2e46a5 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c @@ -664,7 +664,7 @@ out: */ int ttm_get_pages(struct list_head *pages, int flags, enum ttm_caching_state cstate, unsigned count, - dma_addr_t *dma_address, struct device *dev) + dma_addr_t *dma_address) { struct ttm_page_pool *pool = ttm_get_pool(flags, cstate); struct page *p = NULL; @@ -685,7 +685,7 @@ int ttm_get_pages(struct list_head *pages, int flags, for (r = 0; r < count; ++r) { if ((flags & TTM_PAGE_FLAG_DMA32) && dma_address) { void *addr; - addr = dma_alloc_coherent(dev, PAGE_SIZE, + addr = dma_alloc_coherent(NULL, PAGE_SIZE, &dma_address[r], gfp_flags); if (addr == NULL) @@ -730,7 +730,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, NULL, NULL); + ttm_put_pages(pages, 0, flags, cstate, NULL); return r; } } @@ -741,8 +741,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, dma_addr_t *dma_address, - struct device *dev) + enum ttm_caching_state cstate, dma_addr_t *dma_address) { unsigned long irq_flags; struct ttm_page_pool *pool = ttm_get_pool(flags, cstate); @@ -758,7 +757,7 @@ void ttm_put_pages(struct list_head *pages, unsigned page_count, int flags, void *addr = page_address(p); WARN_ON(!addr || !dma_address[r]); if (addr) - dma_free_coherent(dev, PAGE_SIZE, + dma_free_coherent(NULL, PAGE_SIZE, addr, dma_address[r]); dma_address[r] = 0; diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index 0f8fc9ff0c53..86d5b1745a45 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -110,7 +110,7 @@ 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, - &ttm->dma_address[index], ttm->be->bdev->dev); + &ttm->dma_address[index]); if (ret != 0) return NULL; @@ -304,7 +304,7 @@ static void ttm_tt_free_alloced_pages(struct ttm_tt *ttm) } } ttm_put_pages(&h, count, ttm->page_flags, ttm->caching_state, - ttm->dma_address, ttm->be->bdev->dev); + ttm->dma_address); ttm->state = tt_unpopulated; ttm->first_himem_page = ttm->num_pages; ttm->last_lomem_page = -1; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index df04661e2b93..96949b93d920 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -322,7 +322,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM); dev_priv->active_master = &dev_priv->fbdev_master; - dev_priv->bdev.dev = dev->dev; + ret = ttm_bo_device_init(&dev_priv->bdev, dev_priv->bo_global_ref.ref.object, &vmw_bo_driver, VMWGFX_FILE_PAGE_OFFSET, diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 38ff06822609..efed0820d9fa 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -551,7 +551,6 @@ struct ttm_bo_device { struct list_head device_list; struct ttm_bo_global *glob; struct ttm_bo_driver *driver; - struct device *dev; rwlock_t vm_lock; struct ttm_mem_type_manager man[TTM_NUM_MEM_TYPES]; spinlock_t fence_lock; diff --git a/include/drm/ttm/ttm_page_alloc.h b/include/drm/ttm/ttm_page_alloc.h index ccb6b7a240e2..8062890f725e 100644 --- a/include/drm/ttm/ttm_page_alloc.h +++ b/include/drm/ttm/ttm_page_alloc.h @@ -37,14 +37,12 @@ * @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). - * @dev: struct device for appropiate DMA accounting. */ int ttm_get_pages(struct list_head *pages, int flags, enum ttm_caching_state cstate, unsigned count, - dma_addr_t *dma_address, - struct device *dev); + dma_addr_t *dma_address); /** * Put linked list of pages to pool. * @@ -54,14 +52,12 @@ int ttm_get_pages(struct list_head *pages, * @flags: ttm flags for page allocation. * @cstate: ttm caching state. * @dma_address: The DMA (bus) address of pages (if TTM_PAGE_FLAG_DMA32 set). - * @dev: struct device for appropiate DMA accounting. */ void ttm_put_pages(struct list_head *pages, unsigned page_count, int flags, enum ttm_caching_state cstate, - dma_addr_t *dma_address, - struct device *dev); + dma_addr_t *dma_address); /** * Initialize pool allocator. */ -- cgit v1.2.3 From 394d5aefcdecb51bbf7d6df757e73559c9692a08 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sat, 12 Feb 2011 15:58:25 +0100 Subject: ARM: 6662/1: amba: make amba_bustype non-static Export amba_bustype struct so it can be used for things like registering bus notifiers. Signed-off-by: Rob Herring Signed-off-by: Russell King --- drivers/amba/bus.c | 2 +- include/linux/amba/bus.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index e7df019d29d4..ca96b0a26303 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -106,7 +106,7 @@ static struct device_attribute amba_dev_attrs[] = { * Primecells are part of the Advanced Microcontroller Bus Architecture, * so we call the bus "amba". */ -static struct bus_type amba_bustype = { +struct bus_type amba_bustype = { .name = "amba", .dev_attrs = amba_dev_attrs, .match = amba_match, diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index 9e7f259346e1..a0ccf28e9741 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -56,6 +56,8 @@ enum amba_vendor { AMBA_VENDOR_ST = 0x80, }; +extern struct bus_type amba_bustype; + #define amba_get_drvdata(d) dev_get_drvdata(&d->dev) #define amba_set_drvdata(d,p) dev_set_drvdata(&d->dev, p) -- cgit v1.2.3 From aa25afad2ca60d19457849ea75e9c31236f4e174 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 19 Feb 2011 15:55:00 +0000 Subject: ARM: amba: make probe() functions take const id tables Make Primecell driver probe functions take a const pointer to their ID tables. Drivers should never modify their ID tables in their probe handler. Signed-off-by: Russell King --- arch/arm/kernel/etm.c | 4 ++-- drivers/char/hw_random/nomadik-rng.c | 2 +- drivers/dma/amba-pl08x.c | 2 +- drivers/dma/pl330.c | 2 +- drivers/gpio/pl061.c | 2 +- drivers/input/serio/ambakmi.c | 3 ++- drivers/mmc/host/mmci.c | 3 ++- drivers/rtc/rtc-pl030.c | 2 +- drivers/rtc/rtc-pl031.c | 2 +- drivers/spi/amba-pl022.c | 2 +- drivers/tty/serial/amba-pl010.c | 2 +- drivers/tty/serial/amba-pl011.c | 2 +- drivers/video/amba-clcd.c | 2 +- drivers/watchdog/sp805_wdt.c | 2 +- include/linux/amba/bus.h | 2 +- sound/arm/aaci.c | 3 ++- 16 files changed, 20 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 11db62806a1a..052b509e2d5f 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -338,7 +338,7 @@ static struct miscdevice etb_miscdev = { .fops = &etb_fops, }; -static int __init etb_probe(struct amba_device *dev, struct amba_id *id) +static int __init etb_probe(struct amba_device *dev, const struct amba_id *id) { struct tracectx *t = &tracer; int ret = 0; @@ -530,7 +530,7 @@ static ssize_t trace_mode_store(struct kobject *kobj, static struct kobj_attribute trace_mode_attr = __ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store); -static int __init etm_probe(struct amba_device *dev, struct amba_id *id) +static int __init etm_probe(struct amba_device *dev, const struct amba_id *id) { struct tracectx *t = &tracer; int ret = 0; diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c index a348c7e9aa0b..dd1d143eb8ea 100644 --- a/drivers/char/hw_random/nomadik-rng.c +++ b/drivers/char/hw_random/nomadik-rng.c @@ -39,7 +39,7 @@ static struct hwrng nmk_rng = { .read = nmk_rng_read, }; -static int nmk_rng_probe(struct amba_device *dev, struct amba_id *id) +static int nmk_rng_probe(struct amba_device *dev, const struct amba_id *id) { void __iomem *base; int ret; diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 07bca4970e50..e6d7228b1479 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1845,7 +1845,7 @@ static inline void init_pl08x_debugfs(struct pl08x_driver_data *pl08x) } #endif -static int pl08x_probe(struct amba_device *adev, struct amba_id *id) +static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) { struct pl08x_driver_data *pl08x; const struct vendor_data *vd = id->data; diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 7c50f6dfd3f4..6abe1ec1f2ce 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -657,7 +657,7 @@ static irqreturn_t pl330_irq_handler(int irq, void *data) } static int __devinit -pl330_probe(struct amba_device *adev, struct amba_id *id) +pl330_probe(struct amba_device *adev, const struct amba_id *id) { struct dma_pl330_platdata *pdat; struct dma_pl330_dmac *pdmac; diff --git a/drivers/gpio/pl061.c b/drivers/gpio/pl061.c index 2975d22daffe..838ddbdf90cc 100644 --- a/drivers/gpio/pl061.c +++ b/drivers/gpio/pl061.c @@ -232,7 +232,7 @@ static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) desc->irq_data.chip->irq_unmask(&desc->irq_data); } -static int pl061_probe(struct amba_device *dev, struct amba_id *id) +static int pl061_probe(struct amba_device *dev, const struct amba_id *id) { struct pl061_platform_data *pdata; struct pl061_gpio *chip; diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c index 92563a681d65..12abc50508e5 100644 --- a/drivers/input/serio/ambakmi.c +++ b/drivers/input/serio/ambakmi.c @@ -107,7 +107,8 @@ static void amba_kmi_close(struct serio *io) clk_disable(kmi->clk); } -static int __devinit amba_kmi_probe(struct amba_device *dev, struct amba_id *id) +static int __devinit amba_kmi_probe(struct amba_device *dev, + const struct amba_id *id) { struct amba_kmi_port *kmi; struct serio *io; diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 2d6de3e03e2d..67f17d61b978 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -713,7 +713,8 @@ static const struct mmc_host_ops mmci_ops = { .get_cd = mmci_get_cd, }; -static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id) +static int __devinit mmci_probe(struct amba_device *dev, + const struct amba_id *id) { struct mmci_platform_data *plat = dev->dev.platform_data; struct variant_data *variant = id->data; diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c index bbdb2f02798a..baff724cd40f 100644 --- a/drivers/rtc/rtc-pl030.c +++ b/drivers/rtc/rtc-pl030.c @@ -103,7 +103,7 @@ static const struct rtc_class_ops pl030_ops = { .set_alarm = pl030_set_alarm, }; -static int pl030_probe(struct amba_device *dev, struct amba_id *id) +static int pl030_probe(struct amba_device *dev, const struct amba_id *id) { struct pl030_rtc *rtc; int ret; diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index b7a6690e5b35..6568f4b37e19 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -358,7 +358,7 @@ static int pl031_remove(struct amba_device *adev) return 0; } -static int pl031_probe(struct amba_device *adev, struct amba_id *id) +static int pl031_probe(struct amba_device *adev, const struct amba_id *id) { int ret; struct pl031_local *ldata; diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 71a1219a995d..95e58c70a2c9 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -2021,7 +2021,7 @@ static void pl022_cleanup(struct spi_device *spi) static int __devinit -pl022_probe(struct amba_device *adev, struct amba_id *id) +pl022_probe(struct amba_device *adev, const struct amba_id *id) { struct device *dev = &adev->dev; struct pl022_ssp_controller *platform_info = adev->dev.platform_data; diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index 2904aa044126..d742dd2c525c 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -676,7 +676,7 @@ static struct uart_driver amba_reg = { .cons = AMBA_CONSOLE, }; -static int pl010_probe(struct amba_device *dev, struct amba_id *id) +static int pl010_probe(struct amba_device *dev, const struct amba_id *id) { struct uart_amba_port *uap; void __iomem *base; diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index e76d7d000128..0dae46f91021 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1349,7 +1349,7 @@ static struct uart_driver amba_reg = { .cons = AMBA_CONSOLE, }; -static int pl011_probe(struct amba_device *dev, struct amba_id *id) +static int pl011_probe(struct amba_device *dev, const struct amba_id *id) { struct uart_amba_port *uap; struct vendor_data *vendor = id->data; diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c index 1c2c68356ea7..013c8ce57205 100644 --- a/drivers/video/amba-clcd.c +++ b/drivers/video/amba-clcd.c @@ -461,7 +461,7 @@ static int clcdfb_register(struct clcd_fb *fb) return ret; } -static int clcdfb_probe(struct amba_device *dev, struct amba_id *id) +static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id) { struct clcd_board *board = dev->dev.platform_data; struct clcd_fb *fb; diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c index 9127eda2145b..0a0efe713bc8 100644 --- a/drivers/watchdog/sp805_wdt.c +++ b/drivers/watchdog/sp805_wdt.c @@ -278,7 +278,7 @@ static struct miscdevice sp805_wdt_miscdev = { }; static int __devinit -sp805_wdt_probe(struct amba_device *adev, struct amba_id *id) +sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id) { int ret = 0; diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index a0ccf28e9741..07b6374652dc 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -43,7 +43,7 @@ struct amba_id { struct amba_driver { struct device_driver drv; - int (*probe)(struct amba_device *, struct amba_id *); + int (*probe)(struct amba_device *, const struct amba_id *); int (*remove)(struct amba_device *); void (*shutdown)(struct amba_device *); int (*suspend)(struct amba_device *, pm_message_t); diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index 7c1fc64cb53d..d0821f8974a4 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c @@ -1011,7 +1011,8 @@ static unsigned int __devinit aaci_size_fifo(struct aaci *aaci) return i; } -static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id) +static int __devinit aaci_probe(struct amba_device *dev, + const struct amba_id *id) { struct aaci *aaci; int ret, i; -- cgit v1.2.3 From 2d00880fa842e7dd34f5bb5eb3b5f8337b068be5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 19 Feb 2011 15:55:48 +0000 Subject: ARM: amba: make amba_driver id_table const Now that the bus level code deals with a const id table, we can also make the ID table in the amba_driver structure also const. Signed-off-by: Russell King --- include/linux/amba/bus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index 07b6374652dc..227d590cd8d5 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -48,7 +48,7 @@ struct amba_driver { void (*shutdown)(struct amba_device *); int (*suspend)(struct amba_device *, pm_message_t); int (*resume)(struct amba_device *); - struct amba_id *id_table; + const struct amba_id *id_table; }; enum amba_vendor { -- cgit v1.2.3 From ba74ec7f6b2bf9e1b5d0f2c5cef08766944cb2c8 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 23 Feb 2011 04:33:17 +0100 Subject: ARM: 6758/1: amba: support pm ops Support pm_ops in the AMBA bus, required to allow drivers to use runtime pm. The implementation of AMBA bus pm ops is based on the platform bus implementation. Acked-by: Rafael J. Wysocki Acked-by: Linus Walleij Signed-off-by: Rabin Vincent Signed-off-by: Russell King --- drivers/amba/bus.c | 340 +++++++++++++++++++++++++++++++++++++++++++---- include/linux/amba/bus.h | 2 + 2 files changed, 319 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 43088996a07a..6d2bb2524b6e 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -13,12 +13,13 @@ #include #include #include +#include +#include #include #include #include -#define to_amba_device(d) container_of(d, struct amba_device, dev) #define to_amba_driver(d) container_of(d, struct amba_driver, drv) static const struct amba_id * @@ -57,26 +58,6 @@ static int amba_uevent(struct device *dev, struct kobj_uevent_env *env) #define amba_uevent NULL #endif -static int amba_suspend(struct device *dev, pm_message_t state) -{ - struct amba_driver *drv = to_amba_driver(dev->driver); - int ret = 0; - - if (dev->driver && drv->suspend) - ret = drv->suspend(to_amba_device(dev), state); - return ret; -} - -static int amba_resume(struct device *dev) -{ - struct amba_driver *drv = to_amba_driver(dev->driver); - int ret = 0; - - if (dev->driver && drv->resume) - ret = drv->resume(to_amba_device(dev)); - return ret; -} - #define amba_attr_func(name,fmt,arg...) \ static ssize_t name##_show(struct device *_dev, \ struct device_attribute *attr, char *buf) \ @@ -102,6 +83,320 @@ static struct device_attribute amba_dev_attrs[] = { __ATTR_NULL, }; +#ifdef CONFIG_PM_SLEEP + +static int amba_legacy_suspend(struct device *dev, pm_message_t mesg) +{ + struct amba_driver *adrv = to_amba_driver(dev->driver); + struct amba_device *adev = to_amba_device(dev); + int ret = 0; + + if (dev->driver && adrv->suspend) + ret = adrv->suspend(adev, mesg); + + return ret; +} + +static int amba_legacy_resume(struct device *dev) +{ + struct amba_driver *adrv = to_amba_driver(dev->driver); + struct amba_device *adev = to_amba_device(dev); + int ret = 0; + + if (dev->driver && adrv->resume) + ret = adrv->resume(adev); + + return ret; +} + +static int amba_pm_prepare(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (drv && drv->pm && drv->pm->prepare) + ret = drv->pm->prepare(dev); + + return ret; +} + +static void amba_pm_complete(struct device *dev) +{ + struct device_driver *drv = dev->driver; + + if (drv && drv->pm && drv->pm->complete) + drv->pm->complete(dev); +} + +#else /* !CONFIG_PM_SLEEP */ + +#define amba_pm_prepare NULL +#define amba_pm_complete NULL + +#endif /* !CONFIG_PM_SLEEP */ + +#ifdef CONFIG_SUSPEND + +static int amba_pm_suspend(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->suspend) + ret = drv->pm->suspend(dev); + } else { + ret = amba_legacy_suspend(dev, PMSG_SUSPEND); + } + + return ret; +} + +static int amba_pm_suspend_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->suspend_noirq) + ret = drv->pm->suspend_noirq(dev); + } + + return ret; +} + +static int amba_pm_resume(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->resume) + ret = drv->pm->resume(dev); + } else { + ret = amba_legacy_resume(dev); + } + + return ret; +} + +static int amba_pm_resume_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->resume_noirq) + ret = drv->pm->resume_noirq(dev); + } + + return ret; +} + +#else /* !CONFIG_SUSPEND */ + +#define amba_pm_suspend NULL +#define amba_pm_resume NULL +#define amba_pm_suspend_noirq NULL +#define amba_pm_resume_noirq NULL + +#endif /* !CONFIG_SUSPEND */ + +#ifdef CONFIG_HIBERNATION + +static int amba_pm_freeze(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->freeze) + ret = drv->pm->freeze(dev); + } else { + ret = amba_legacy_suspend(dev, PMSG_FREEZE); + } + + return ret; +} + +static int amba_pm_freeze_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->freeze_noirq) + ret = drv->pm->freeze_noirq(dev); + } + + return ret; +} + +static int amba_pm_thaw(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->thaw) + ret = drv->pm->thaw(dev); + } else { + ret = amba_legacy_resume(dev); + } + + return ret; +} + +static int amba_pm_thaw_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->thaw_noirq) + ret = drv->pm->thaw_noirq(dev); + } + + return ret; +} + +static int amba_pm_poweroff(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->poweroff) + ret = drv->pm->poweroff(dev); + } else { + ret = amba_legacy_suspend(dev, PMSG_HIBERNATE); + } + + return ret; +} + +static int amba_pm_poweroff_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->poweroff_noirq) + ret = drv->pm->poweroff_noirq(dev); + } + + return ret; +} + +static int amba_pm_restore(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->restore) + ret = drv->pm->restore(dev); + } else { + ret = amba_legacy_resume(dev); + } + + return ret; +} + +static int amba_pm_restore_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->restore_noirq) + ret = drv->pm->restore_noirq(dev); + } + + return ret; +} + +#else /* !CONFIG_HIBERNATION */ + +#define amba_pm_freeze NULL +#define amba_pm_thaw NULL +#define amba_pm_poweroff NULL +#define amba_pm_restore NULL +#define amba_pm_freeze_noirq NULL +#define amba_pm_thaw_noirq NULL +#define amba_pm_poweroff_noirq NULL +#define amba_pm_restore_noirq NULL + +#endif /* !CONFIG_HIBERNATION */ + +#ifdef CONFIG_PM + +static const struct dev_pm_ops amba_pm = { + .prepare = amba_pm_prepare, + .complete = amba_pm_complete, + .suspend = amba_pm_suspend, + .resume = amba_pm_resume, + .freeze = amba_pm_freeze, + .thaw = amba_pm_thaw, + .poweroff = amba_pm_poweroff, + .restore = amba_pm_restore, + .suspend_noirq = amba_pm_suspend_noirq, + .resume_noirq = amba_pm_resume_noirq, + .freeze_noirq = amba_pm_freeze_noirq, + .thaw_noirq = amba_pm_thaw_noirq, + .poweroff_noirq = amba_pm_poweroff_noirq, + .restore_noirq = amba_pm_restore_noirq, + SET_RUNTIME_PM_OPS( + pm_generic_runtime_suspend, + pm_generic_runtime_resume, + pm_generic_runtime_idle + ) +}; + +#define AMBA_PM (&amba_pm) + +#else /* !CONFIG_PM */ + +#define AMBA_PM NULL + +#endif /* !CONFIG_PM */ + /* * Primecells are part of the Advanced Microcontroller Bus Architecture, * so we call the bus "amba". @@ -111,8 +406,7 @@ struct bus_type amba_bustype = { .dev_attrs = amba_dev_attrs, .match = amba_match, .uevent = amba_uevent, - .suspend = amba_suspend, - .resume = amba_resume, + .pm = AMBA_PM, }; static int __init amba_init(void) diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index 227d590cd8d5..fcbbe71a3cc1 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -58,6 +58,8 @@ enum amba_vendor { extern struct bus_type amba_bustype; +#define to_amba_device(d) container_of(d, struct amba_device, dev) + #define amba_get_drvdata(d) dev_get_drvdata(&d->dev) #define amba_set_drvdata(d,p) dev_set_drvdata(&d->dev, p) -- cgit v1.2.3 From 008536e8458613ce569595e43b0e71afa8b48ae8 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 19 Feb 2011 15:45:29 -0800 Subject: connector: Convert char *name to const char *name Allow more const declarations. Signed-off-by: Joe Perches Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/connector/cn_queue.c | 7 ++++--- drivers/connector/connector.c | 2 +- include/linux/connector.h | 9 ++++++--- 3 files changed, 11 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/connector/cn_queue.c b/drivers/connector/cn_queue.c index 81270d221e5a..55653aba6735 100644 --- a/drivers/connector/cn_queue.c +++ b/drivers/connector/cn_queue.c @@ -48,7 +48,7 @@ void cn_queue_wrapper(struct work_struct *work) } static struct cn_callback_entry * -cn_queue_alloc_callback_entry(char *name, struct cb_id *id, +cn_queue_alloc_callback_entry(const char *name, struct cb_id *id, void (*callback)(struct cn_msg *, struct netlink_skb_parms *)) { struct cn_callback_entry *cbq; @@ -78,7 +78,8 @@ int cn_cb_equal(struct cb_id *i1, struct cb_id *i2) return ((i1->idx == i2->idx) && (i1->val == i2->val)); } -int cn_queue_add_callback(struct cn_queue_dev *dev, char *name, struct cb_id *id, +int cn_queue_add_callback(struct cn_queue_dev *dev, const char *name, + struct cb_id *id, void (*callback)(struct cn_msg *, struct netlink_skb_parms *)) { struct cn_callback_entry *cbq, *__cbq; @@ -135,7 +136,7 @@ void cn_queue_del_callback(struct cn_queue_dev *dev, struct cb_id *id) } } -struct cn_queue_dev *cn_queue_alloc_dev(char *name, struct sock *nls) +struct cn_queue_dev *cn_queue_alloc_dev(const char *name, struct sock *nls) { struct cn_queue_dev *dev; diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index 05117f1ad867..f7554de3be5e 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -205,7 +205,7 @@ static void cn_rx_skb(struct sk_buff *__skb) * * May sleep. */ -int cn_add_callback(struct cb_id *id, char *name, +int cn_add_callback(struct cb_id *id, const char *name, void (*callback)(struct cn_msg *, struct netlink_skb_parms *)) { int err; diff --git a/include/linux/connector.h b/include/linux/connector.h index 2e9759cdd275..bcafc942e5e4 100644 --- a/include/linux/connector.h +++ b/include/linux/connector.h @@ -129,14 +129,17 @@ struct cn_dev { struct cn_queue_dev *cbdev; }; -int cn_add_callback(struct cb_id *, char *, void (*callback) (struct cn_msg *, struct netlink_skb_parms *)); +int cn_add_callback(struct cb_id *id, const char *name, + void (*callback)(struct cn_msg *, struct netlink_skb_parms *)); void cn_del_callback(struct cb_id *); int cn_netlink_send(struct cn_msg *, u32, gfp_t); -int cn_queue_add_callback(struct cn_queue_dev *dev, char *name, struct cb_id *id, void (*callback)(struct cn_msg *, struct netlink_skb_parms *)); +int cn_queue_add_callback(struct cn_queue_dev *dev, const char *name, + struct cb_id *id, + void (*callback)(struct cn_msg *, struct netlink_skb_parms *)); void cn_queue_del_callback(struct cn_queue_dev *dev, struct cb_id *id); -struct cn_queue_dev *cn_queue_alloc_dev(char *name, struct sock *); +struct cn_queue_dev *cn_queue_alloc_dev(const char *name, struct sock *); void cn_queue_free_dev(struct cn_queue_dev *dev); int cn_cb_equal(struct cb_id *, struct cb_id *); -- cgit v1.2.3 From 6ebacbb79d2d05978ba50a24d8cbe2a76ff2014c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 23 Feb 2011 15:06:08 +0100 Subject: mac80211: rename RX_FLAG_TSFT The flag isn't very descriptive -- the intention is that the driver provides a TSF timestamp at the beginning of the MPDU -- make that clearer by renaming the flag to RX_FLAG_MACTIME_MPDU. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 2 +- drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 2 +- drivers/net/wireless/ath/ath9k/recv.c | 2 +- drivers/net/wireless/b43/xmit.c | 2 +- drivers/net/wireless/b43legacy/xmit.c | 2 +- drivers/net/wireless/iwlegacy/iwl-4965-lib.c | 2 +- drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 2 +- drivers/net/wireless/p54/txrx.c | 2 +- drivers/net/wireless/rtl818x/rtl8180/dev.c | 2 +- drivers/net/wireless/rtl818x/rtl8187/dev.c | 2 +- drivers/net/wireless/rtlwifi/rtl8192ce/trx.c | 2 +- drivers/net/wireless/rtlwifi/rtl8192cu/trx.c | 2 +- drivers/net/wireless/wl1251/rx.c | 2 +- drivers/staging/brcm80211/sys/wlc_mac80211.c | 5 ++++- include/net/mac80211.h | 9 +++++---- net/mac80211/ibss.c | 2 +- net/mac80211/rx.c | 4 ++-- 17 files changed, 25 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index dbc45e085434..80d9cf0c4cd2 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1361,7 +1361,7 @@ ath5k_receive_frame(struct ath5k_softc *sc, struct sk_buff *skb, * right now, so it's not too bad... */ rxs->mactime = ath5k_extend_tsf(sc->ah, rs->rs_tstamp); - rxs->flag |= RX_FLAG_TSFT; + rxs->flag |= RX_FLAG_MACTIME_MPDU; rxs->freq = sc->curchan->center_freq; rxs->band = sc->curchan->band; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 564ac13596f1..4a4f27ba96af 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -616,7 +616,7 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, rx_status->freq = hw->conf.channel->center_freq; rx_status->signal = rxbuf->rxstatus.rs_rssi + ATH_DEFAULT_NOISE_FLOOR; rx_status->antenna = rxbuf->rxstatus.rs_antenna; - rx_status->flag |= RX_FLAG_TSFT; + rx_status->flag |= RX_FLAG_MACTIME_MPDU; return true; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index daf171d2f610..cb559e345b86 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -983,7 +983,7 @@ static int ath9k_rx_skb_preprocess(struct ath_common *common, rx_status->freq = hw->conf.channel->center_freq; rx_status->signal = ATH_DEFAULT_NOISE_FLOOR + rx_stats->rs_rssi; rx_status->antenna = rx_stats->rs_antenna; - rx_status->flag |= RX_FLAG_TSFT; + rx_status->flag |= RX_FLAG_MACTIME_MPDU; return 0; } diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index e6b0528f3b52..ad605bcdd40e 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -652,7 +652,7 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) status.mactime += mactime; if (low_mactime_now <= mactime) status.mactime -= 0x10000; - status.flag |= RX_FLAG_TSFT; + status.flag |= RX_FLAG_MACTIME_MPDU; } chanid = (chanstat & B43_RX_CHAN_ID) >> B43_RX_CHAN_ID_SHIFT; diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c index 7d177d97f1f7..3a95541708a6 100644 --- a/drivers/net/wireless/b43legacy/xmit.c +++ b/drivers/net/wireless/b43legacy/xmit.c @@ -572,7 +572,7 @@ void b43legacy_rx(struct b43legacy_wldev *dev, status.mactime += mactime; if (low_mactime_now <= mactime) status.mactime -= 0x10000; - status.flag |= RX_FLAG_TSFT; + status.flag |= RX_FLAG_MACTIME_MPDU; } chanid = (chanstat & B43legacy_RX_CHAN_ID) >> diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-lib.c b/drivers/net/wireless/iwlegacy/iwl-4965-lib.c index c1a24946715e..bd9618a4269d 100644 --- a/drivers/net/wireless/iwlegacy/iwl-4965-lib.c +++ b/drivers/net/wireless/iwlegacy/iwl-4965-lib.c @@ -639,7 +639,7 @@ void iwl4965_rx_reply_rx(struct iwl_priv *priv, /* TSF isn't reliable. In order to allow smooth user experience, * this W/A doesn't propagate it to the mac80211 */ - /*rx_status.flag |= RX_FLAG_TSFT;*/ + /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/ priv->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index 325ff5c89ee8..0d0572ca7e77 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c @@ -1173,7 +1173,7 @@ void iwlagn_rx_reply_rx(struct iwl_priv *priv, /* TSF isn't reliable. In order to allow smooth user experience, * this W/A doesn't propagate it to the mac80211 */ - /*rx_status.flag |= RX_FLAG_TSFT;*/ + /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/ priv->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 917d5d948e3c..a408ff333920 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -367,7 +367,7 @@ static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb) rx_status->mactime = ((u64)priv->tsf_high32) << 32 | tsf32; priv->tsf_low32 = tsf32; - rx_status->flag |= RX_FLAG_TSFT; + rx_status->flag |= RX_FLAG_MACTIME_MPDU; if (hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN)) header_len += hdr->align[0]; diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 5851cbc1e957..b85debb4f7b1 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -146,7 +146,7 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev) rx_status.freq = dev->conf.channel->center_freq; rx_status.band = dev->conf.channel->band; rx_status.mactime = le64_to_cpu(entry->tsft); - rx_status.flag |= RX_FLAG_TSFT; + rx_status.flag |= RX_FLAG_MACTIME_MPDU; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c index 6b82cac37ee3..1f5df12cb156 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -373,7 +373,7 @@ static void rtl8187_rx_cb(struct urb *urb) rx_status.rate_idx = rate; rx_status.freq = dev->conf.channel->center_freq; rx_status.band = dev->conf.channel->band; - rx_status.flag |= RX_FLAG_TSFT; + rx_status.flag |= RX_FLAG_MACTIME_MPDU; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c index 01b95427fee0..8a67372f71fb 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c @@ -691,7 +691,7 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw, if (GET_RX_DESC_RXHT(pdesc)) rx_status->flag |= RX_FLAG_HT; - rx_status->flag |= RX_FLAG_TSFT; + rx_status->flag |= RX_FLAG_MACTIME_MPDU; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c index 9855c3e0a4b2..659e0ca95c64 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c @@ -334,7 +334,7 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_40MHZ; if (GET_RX_DESC_RX_HT(pdesc)) rx_status->flag |= RX_FLAG_HT; - rx_status->flag |= RX_FLAG_TSFT; + rx_status->flag |= RX_FLAG_MACTIME_MPDU; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; rx_status->rate_idx = _rtl92c_rate_mapping(hw, diff --git a/drivers/net/wireless/wl1251/rx.c b/drivers/net/wireless/wl1251/rx.c index b659e15c78df..c1b3b3f03da2 100644 --- a/drivers/net/wireless/wl1251/rx.c +++ b/drivers/net/wireless/wl1251/rx.c @@ -81,7 +81,7 @@ static void wl1251_rx_status(struct wl1251 *wl, status->freq = ieee80211_channel_to_frequency(desc->channel, status->band); - status->flag |= RX_FLAG_TSFT; + status->flag |= RX_FLAG_MACTIME_MPDU; if (desc->flags & RX_DESC_ENCRYPTION_MASK) { status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; diff --git a/drivers/staging/brcm80211/sys/wlc_mac80211.c b/drivers/staging/brcm80211/sys/wlc_mac80211.c index 1d5d01ac0a9b..f305bf948617 100644 --- a/drivers/staging/brcm80211/sys/wlc_mac80211.c +++ b/drivers/staging/brcm80211/sys/wlc_mac80211.c @@ -6819,11 +6819,14 @@ prep_mac80211_status(struct wlc_info *wlc, d11rxhdr_t *rxh, struct sk_buff *p, ratespec_t rspec; unsigned char *plcp; +#if 0 + /* Clearly, this is bogus -- reading the TSF now is wrong */ wlc_read_tsf(wlc, &tsf_l, &tsf_h); /* mactime */ rx_status->mactime = tsf_h; rx_status->mactime <<= 32; rx_status->mactime |= tsf_l; - rx_status->flag |= RX_FLAG_TSFT; + rx_status->flag |= RX_FLAG_MACTIME_MPDU; /* clearly wrong */ +#endif channel = WLC_CHAN_CHANNEL(rxh->RxChan); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8fcd1691cfb7..a13c8d8fca5c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -599,9 +599,10 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * the frame. * @RX_FLAG_FAILED_PLCP_CRC: Set this flag if the PCLP check failed on * the frame. - * @RX_FLAG_TSFT: The timestamp passed in the RX status (@mactime field) - * is valid. This is useful in monitor mode and necessary for beacon frames - * to enable IBSS merging. + * @RX_FLAG_MACTIME_MPDU: The timestamp passed in the RX status (@mactime + * field) is valid and contains the time the first symbol of the MPDU + * was received. This is useful in monitor mode and for proper IBSS + * merging. * @RX_FLAG_SHORTPRE: Short preamble was used for this frame * @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index * @RX_FLAG_40MHZ: HT40 (40 MHz) was used @@ -614,7 +615,7 @@ enum mac80211_rx_flags { RX_FLAG_IV_STRIPPED = 1<<4, RX_FLAG_FAILED_FCS_CRC = 1<<5, RX_FLAG_FAILED_PLCP_CRC = 1<<6, - RX_FLAG_TSFT = 1<<7, + RX_FLAG_MACTIME_MPDU = 1<<7, RX_FLAG_SHORTPRE = 1<<8, RX_FLAG_HT = 1<<9, RX_FLAG_40MHZ = 1<<10, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index a42aa61269ea..463271f9492e 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -355,7 +355,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, if (memcmp(cbss->bssid, sdata->u.ibss.bssid, ETH_ALEN) == 0) goto put_bss; - if (rx_status->flag & RX_FLAG_TSFT) { + if (rx_status->flag & RX_FLAG_MACTIME_MPDU) { /* * For correct IBSS merging we need mactime; since mactime is * defined as the time the first data symbol of the frame hits diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index f502634d43af..5b534235d6be 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -77,7 +77,7 @@ ieee80211_rx_radiotap_len(struct ieee80211_local *local, /* always present fields */ len = sizeof(struct ieee80211_radiotap_header) + 9; - if (status->flag & RX_FLAG_TSFT) + if (status->flag & RX_FLAG_MACTIME_MPDU) len += 8; if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) len += 1; @@ -123,7 +123,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, /* the order of the following fields is important */ /* IEEE80211_RADIOTAP_TSFT */ - if (status->flag & RX_FLAG_TSFT) { + if (status->flag & RX_FLAG_MACTIME_MPDU) { put_unaligned_le64(status->mactime, pos); rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT); -- cgit v1.2.3 From 3bcbaf6e08d8d82cde781997bd2c56dda87049b5 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 22 Feb 2011 21:07:46 +0100 Subject: rtc: cmos: Add OF bindings This allows to load the OF driver based informations from the device tree. Systems without BIOS may need to perform some initialization. PowerPC creates a PNP device from the OF information and performs this kind of initialization in their private PCI quirk. This looks more generic. This patch also avoids registering the platform RTC driver on X86 if we have a device tree blob. Otherwise we would setup the device based on the hardcoded information in arch/x86 rather than the device tree based one. [ tglx: Changed "int of_have_populated_dt()" to bool as recommended by Grant ] Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie Acked-by: Grant Likely Cc: sodaville@linutronix.de Cc: devicetree-discuss@lists.ozlabs.org Cc: rtc-linux@googlegroups.com Cc: Alessandro Zummo LKML-Reference: <1298405266-1624-12-git-send-email-bigeasy@linutronix.de> Signed-off-by: Thomas Gleixner --- Documentation/devicetree/bindings/rtc/rtc-cmos.txt | 28 ++++++++++++++ arch/x86/kernel/rtc.c | 3 ++ drivers/rtc/rtc-cmos.c | 45 ++++++++++++++++++++++ include/linux/of.h | 12 ++++++ 4 files changed, 88 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/rtc-cmos.txt (limited to 'include') diff --git a/Documentation/devicetree/bindings/rtc/rtc-cmos.txt b/Documentation/devicetree/bindings/rtc/rtc-cmos.txt new file mode 100644 index 000000000000..7382989b3052 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/rtc-cmos.txt @@ -0,0 +1,28 @@ + Motorola mc146818 compatible RTC +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Required properties: + - compatible : "motorola,mc146818" + - reg : should contain registers location and length. + +Optional properties: + - interrupts : should contain interrupt. + - interrupt-parent : interrupt source phandle. + - ctrl-reg : Contains the initial value of the control register also + called "Register B". + - freq-reg : Contains the initial value of the frequency register also + called "Regsiter A". + +"Register A" and "B" are usually initialized by the firmware (BIOS for +instance). If this is not done, it can be performed by the driver. + +ISA Example: + + rtc@70 { + compatible = "motorola,mc146818"; + interrupts = <8 3>; + interrupt-parent = <&ioapic1>; + ctrl-reg = <2>; + freq-reg = <0x26>; + reg = <1 0x70 2>; + }; diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c index 6f39cab052d5..3f2ad2640d85 100644 --- a/arch/x86/kernel/rtc.c +++ b/arch/x86/kernel/rtc.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -236,6 +237,8 @@ static __init int add_rtc_cmos(void) } } #endif + if (of_have_populated_dt()) + return 0; platform_device_register(&rtc_device); dev_info(&rtc_device.dev, diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index c7ff8df347e7..159b95e4b420 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include /* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */ #include @@ -1123,6 +1125,47 @@ static struct pnp_driver cmos_pnp_driver = { #endif /* CONFIG_PNP */ +#ifdef CONFIG_OF +static const struct of_device_id of_cmos_match[] = { + { + .compatible = "motorola,mc146818", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, of_cmos_match); + +static __init void cmos_of_init(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct rtc_time time; + int ret; + const __be32 *val; + + if (!node) + return; + + val = of_get_property(node, "ctrl-reg", NULL); + if (val) + CMOS_WRITE(be32_to_cpup(val), RTC_CONTROL); + + val = of_get_property(node, "freq-reg", NULL); + if (val) + CMOS_WRITE(be32_to_cpup(val), RTC_FREQ_SELECT); + + get_rtc_time(&time); + ret = rtc_valid_tm(&time); + if (ret) { + struct rtc_time def_time = { + .tm_year = 1, + .tm_mday = 1, + }; + set_rtc_time(&def_time); + } +} +#else +static inline void cmos_of_init(struct platform_device *pdev) {} +#define of_cmos_match NULL +#endif /*----------------------------------------------------------------*/ /* Platform setup should have set up an RTC device, when PNP is @@ -1131,6 +1174,7 @@ static struct pnp_driver cmos_pnp_driver = { static int __init cmos_platform_probe(struct platform_device *pdev) { + cmos_of_init(pdev); cmos_wake_setup(&pdev->dev); return cmos_do_probe(&pdev->dev, platform_get_resource(pdev, IORESOURCE_IO, 0), @@ -1162,6 +1206,7 @@ static struct platform_driver cmos_platform_driver = { #ifdef CONFIG_PM .pm = &cmos_pm_ops, #endif + .of_match_table = of_cmos_match, } }; diff --git a/include/linux/of.h b/include/linux/of.h index d9dd664a6a9c..266db1d0baa9 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -70,6 +70,11 @@ extern struct device_node *allnodes; extern struct device_node *of_chosen; extern rwlock_t devtree_lock; +static inline bool of_have_populated_dt(void) +{ + return allnodes != NULL; +} + static inline bool of_node_is_root(const struct device_node *node) { return node && (node->parent == NULL); @@ -222,5 +227,12 @@ extern void of_attach_node(struct device_node *); extern void of_detach_node(struct device_node *); #endif +#else + +static inline bool of_have_populated_dt(void) +{ + return false; +} + #endif /* CONFIG_OF */ #endif /* _LINUX_OF_H */ -- cgit v1.2.3 From e13e02a3c68d899169c78d9a18689bd73491d59a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 23 Feb 2011 10:56:17 +0000 Subject: net_sched: SFB flow scheduler This is the Stochastic Fair Blue scheduler, based on work from : W. Feng, D. Kandlur, D. Saha, K. Shin. Blue: A New Class of Active Queue Management Algorithms. U. Michigan CSE-TR-387-99, April 1999. http://www.thefengs.com/wuchang/blue/CSE-TR-387-99.pdf This implementation is based on work done by Juliusz Chroboczek General SFB algorithm can be found in figure 14, page 15: B[l][n] : L x N array of bins (L levels, N bins per level) enqueue() Calculate hash function values h{0}, h{1}, .. h{L-1} Update bins at each level for i = 0 to L - 1 if (B[i][h{i}].qlen > bin_size) B[i][h{i}].p_mark += p_increment; else if (B[i][h{i}].qlen == 0) B[i][h{i}].p_mark -= p_decrement; p_min = min(B[0][h{0}].p_mark ... B[L-1][h{L-1}].p_mark); if (p_min == 1.0) ratelimit(); else mark/drop with probabilty p_min; I did the adaptation of Juliusz code to meet current kernel standards, and various changes to address previous comments : http://thread.gmane.org/gmane.linux.network/90225 http://thread.gmane.org/gmane.linux.network/90375 Default flow classifier is the rxhash introduced by RPS in 2.6.35, but we can use an external flow classifier if wanted. tc qdisc add dev $DEV parent 1:11 handle 11: \ est 0.5sec 2sec sfb limit 128 tc filter add dev $DEV protocol ip parent 11: handle 3 \ flow hash keys dst divisor 1024 Notes: 1) SFB default child qdisc is pfifo_fast. It can be changed by another qdisc but a child qdisc MUST not drop a packet previously queued. This is because SFB needs to handle a dequeued packet in order to maintain its virtual queue states. pfifo_head_drop or CHOKe should not be used. 2) ECN is enabled by default, unlike RED/CHOKe/GRED With help from Patrick McHardy & Andi Kleen Signed-off-by: Eric Dumazet CC: Juliusz Chroboczek CC: Stephen Hemminger CC: Patrick McHardy CC: Andi Kleen CC: John W. Linville Signed-off-by: David S. Miller --- include/linux/pkt_sched.h | 39 +++ net/sched/Kconfig | 11 + net/sched/Makefile | 1 + net/sched/sch_sfb.c | 709 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 760 insertions(+) create mode 100644 net/sched/sch_sfb.c (limited to 'include') diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h index d4bb6f58c90c..5afee2b238bd 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -522,4 +522,43 @@ struct tc_mqprio_qopt { __u16 offset[TC_QOPT_MAX_QUEUE]; }; +/* SFB */ + +enum { + TCA_SFB_UNSPEC, + TCA_SFB_PARMS, + __TCA_SFB_MAX, +}; + +#define TCA_SFB_MAX (__TCA_SFB_MAX - 1) + +/* + * Note: increment, decrement are Q0.16 fixed-point values. + */ +struct tc_sfb_qopt { + __u32 rehash_interval; /* delay between hash move, in ms */ + __u32 warmup_time; /* double buffering warmup time in ms (warmup_time < rehash_interval) */ + __u32 max; /* max len of qlen_min */ + __u32 bin_size; /* maximum queue length per bin */ + __u32 increment; /* probability increment, (d1 in Blue) */ + __u32 decrement; /* probability decrement, (d2 in Blue) */ + __u32 limit; /* max SFB queue length */ + __u32 penalty_rate; /* inelastic flows are rate limited to 'rate' pps */ + __u32 penalty_burst; +}; + +struct tc_sfb_xstats { + __u32 earlydrop; + __u32 penaltydrop; + __u32 bucketdrop; + __u32 queuedrop; + __u32 childdrop; /* drops in child qdisc */ + __u32 marked; + __u32 maxqlen; + __u32 maxprob; + __u32 avgprob; +}; + +#define SFB_MAX_PROB 0xFFFF + #endif diff --git a/net/sched/Kconfig b/net/sched/Kconfig index 8c19b6e3201e..a7a5583d4f68 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -126,6 +126,17 @@ config NET_SCH_RED To compile this code as a module, choose M here: the module will be called sch_red. +config NET_SCH_SFB + tristate "Stochastic Fair Blue (SFB)" + ---help--- + Say Y here if you want to use the Stochastic Fair Blue (SFB) + packet scheduling algorithm. + + See the top of for more details. + + To compile this code as a module, choose M here: the + module will be called sch_sfb. + config NET_SCH_SFQ tristate "Stochastic Fairness Queueing (SFQ)" ---help--- diff --git a/net/sched/Makefile b/net/sched/Makefile index 06c6cdfd1948..2e77b8dba22e 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_NET_SCH_RED) += sch_red.o obj-$(CONFIG_NET_SCH_GRED) += sch_gred.o obj-$(CONFIG_NET_SCH_INGRESS) += sch_ingress.o obj-$(CONFIG_NET_SCH_DSMARK) += sch_dsmark.o +obj-$(CONFIG_NET_SCH_SFB) += sch_sfb.o obj-$(CONFIG_NET_SCH_SFQ) += sch_sfq.o obj-$(CONFIG_NET_SCH_TBF) += sch_tbf.o obj-$(CONFIG_NET_SCH_TEQL) += sch_teql.o diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c new file mode 100644 index 000000000000..0a833d0c1f61 --- /dev/null +++ b/net/sched/sch_sfb.c @@ -0,0 +1,709 @@ +/* + * net/sched/sch_sfb.c Stochastic Fair Blue + * + * Copyright (c) 2008-2011 Juliusz Chroboczek + * Copyright (c) 2011 Eric Dumazet + * + * 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. + * + * W. Feng, D. Kandlur, D. Saha, K. Shin. Blue: + * A New Class of Active Queue Management Algorithms. + * U. Michigan CSE-TR-387-99, April 1999. + * + * http://www.thefengs.com/wuchang/blue/CSE-TR-387-99.pdf + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * SFB uses two B[l][n] : L x N arrays of bins (L levels, N bins per level) + * This implementation uses L = 8 and N = 16 + * This permits us to split one 32bit hash (provided per packet by rxhash or + * external classifier) into 8 subhashes of 4 bits. + */ +#define SFB_BUCKET_SHIFT 4 +#define SFB_NUMBUCKETS (1 << SFB_BUCKET_SHIFT) /* N bins per Level */ +#define SFB_BUCKET_MASK (SFB_NUMBUCKETS - 1) +#define SFB_LEVELS (32 / SFB_BUCKET_SHIFT) /* L */ + +/* SFB algo uses a virtual queue, named "bin" */ +struct sfb_bucket { + u16 qlen; /* length of virtual queue */ + u16 p_mark; /* marking probability */ +}; + +/* We use a double buffering right before hash change + * (Section 4.4 of SFB reference : moving hash functions) + */ +struct sfb_bins { + u32 perturbation; /* jhash perturbation */ + struct sfb_bucket bins[SFB_LEVELS][SFB_NUMBUCKETS]; +}; + +struct sfb_sched_data { + struct Qdisc *qdisc; + struct tcf_proto *filter_list; + unsigned long rehash_interval; + unsigned long warmup_time; /* double buffering warmup time in jiffies */ + u32 max; + u32 bin_size; /* maximum queue length per bin */ + u32 increment; /* d1 */ + u32 decrement; /* d2 */ + u32 limit; /* HARD maximal queue length */ + u32 penalty_rate; + u32 penalty_burst; + u32 tokens_avail; + unsigned long rehash_time; + unsigned long token_time; + + u8 slot; /* current active bins (0 or 1) */ + bool double_buffering; + struct sfb_bins bins[2]; + + struct { + u32 earlydrop; + u32 penaltydrop; + u32 bucketdrop; + u32 queuedrop; + u32 childdrop; /* drops in child qdisc */ + u32 marked; /* ECN mark */ + } stats; +}; + +/* + * Each queued skb might be hashed on one or two bins + * We store in skb_cb the two hash values. + * (A zero value means double buffering was not used) + */ +struct sfb_skb_cb { + u32 hashes[2]; +}; + +static inline struct sfb_skb_cb *sfb_skb_cb(const struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(skb->cb) < + sizeof(struct qdisc_skb_cb) + sizeof(struct sfb_skb_cb)); + return (struct sfb_skb_cb *)qdisc_skb_cb(skb)->data; +} + +/* + * If using 'internal' SFB flow classifier, hash comes from skb rxhash + * If using external classifier, hash comes from the classid. + */ +static u32 sfb_hash(const struct sk_buff *skb, u32 slot) +{ + return sfb_skb_cb(skb)->hashes[slot]; +} + +/* Probabilities are coded as Q0.16 fixed-point values, + * with 0xFFFF representing 65535/65536 (almost 1.0) + * Addition and subtraction are saturating in [0, 65535] + */ +static u32 prob_plus(u32 p1, u32 p2) +{ + u32 res = p1 + p2; + + return min_t(u32, res, SFB_MAX_PROB); +} + +static u32 prob_minus(u32 p1, u32 p2) +{ + return p1 > p2 ? p1 - p2 : 0; +} + +static void increment_one_qlen(u32 sfbhash, u32 slot, struct sfb_sched_data *q) +{ + int i; + struct sfb_bucket *b = &q->bins[slot].bins[0][0]; + + for (i = 0; i < SFB_LEVELS; i++) { + u32 hash = sfbhash & SFB_BUCKET_MASK; + + sfbhash >>= SFB_BUCKET_SHIFT; + if (b[hash].qlen < 0xFFFF) + b[hash].qlen++; + b += SFB_NUMBUCKETS; /* next level */ + } +} + +static void increment_qlen(const struct sk_buff *skb, struct sfb_sched_data *q) +{ + u32 sfbhash; + + sfbhash = sfb_hash(skb, 0); + if (sfbhash) + increment_one_qlen(sfbhash, 0, q); + + sfbhash = sfb_hash(skb, 1); + if (sfbhash) + increment_one_qlen(sfbhash, 1, q); +} + +static void decrement_one_qlen(u32 sfbhash, u32 slot, + struct sfb_sched_data *q) +{ + int i; + struct sfb_bucket *b = &q->bins[slot].bins[0][0]; + + for (i = 0; i < SFB_LEVELS; i++) { + u32 hash = sfbhash & SFB_BUCKET_MASK; + + sfbhash >>= SFB_BUCKET_SHIFT; + if (b[hash].qlen > 0) + b[hash].qlen--; + b += SFB_NUMBUCKETS; /* next level */ + } +} + +static void decrement_qlen(const struct sk_buff *skb, struct sfb_sched_data *q) +{ + u32 sfbhash; + + sfbhash = sfb_hash(skb, 0); + if (sfbhash) + decrement_one_qlen(sfbhash, 0, q); + + sfbhash = sfb_hash(skb, 1); + if (sfbhash) + decrement_one_qlen(sfbhash, 1, q); +} + +static void decrement_prob(struct sfb_bucket *b, struct sfb_sched_data *q) +{ + b->p_mark = prob_minus(b->p_mark, q->decrement); +} + +static void increment_prob(struct sfb_bucket *b, struct sfb_sched_data *q) +{ + b->p_mark = prob_plus(b->p_mark, q->increment); +} + +static void sfb_zero_all_buckets(struct sfb_sched_data *q) +{ + memset(&q->bins, 0, sizeof(q->bins)); +} + +/* + * compute max qlen, max p_mark, and avg p_mark + */ +static u32 sfb_compute_qlen(u32 *prob_r, u32 *avgpm_r, const struct sfb_sched_data *q) +{ + int i; + u32 qlen = 0, prob = 0, totalpm = 0; + const struct sfb_bucket *b = &q->bins[q->slot].bins[0][0]; + + for (i = 0; i < SFB_LEVELS * SFB_NUMBUCKETS; i++) { + if (qlen < b->qlen) + qlen = b->qlen; + totalpm += b->p_mark; + if (prob < b->p_mark) + prob = b->p_mark; + b++; + } + *prob_r = prob; + *avgpm_r = totalpm / (SFB_LEVELS * SFB_NUMBUCKETS); + return qlen; +} + + +static void sfb_init_perturbation(u32 slot, struct sfb_sched_data *q) +{ + q->bins[slot].perturbation = net_random(); +} + +static void sfb_swap_slot(struct sfb_sched_data *q) +{ + sfb_init_perturbation(q->slot, q); + q->slot ^= 1; + q->double_buffering = false; +} + +/* Non elastic flows are allowed to use part of the bandwidth, expressed + * in "penalty_rate" packets per second, with "penalty_burst" burst + */ +static bool sfb_rate_limit(struct sk_buff *skb, struct sfb_sched_data *q) +{ + if (q->penalty_rate == 0 || q->penalty_burst == 0) + return true; + + if (q->tokens_avail < 1) { + unsigned long age = min(10UL * HZ, jiffies - q->token_time); + + q->tokens_avail = (age * q->penalty_rate) / HZ; + if (q->tokens_avail > q->penalty_burst) + q->tokens_avail = q->penalty_burst; + q->token_time = jiffies; + if (q->tokens_avail < 1) + return true; + } + + q->tokens_avail--; + return false; +} + +static bool sfb_classify(struct sk_buff *skb, struct sfb_sched_data *q, + int *qerr, u32 *salt) +{ + struct tcf_result res; + int result; + + result = tc_classify(skb, q->filter_list, &res); + if (result >= 0) { +#ifdef CONFIG_NET_CLS_ACT + switch (result) { + case TC_ACT_STOLEN: + case TC_ACT_QUEUED: + *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; + case TC_ACT_SHOT: + return false; + } +#endif + *salt = TC_H_MIN(res.classid); + return true; + } + return false; +} + +static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch) +{ + + struct sfb_sched_data *q = qdisc_priv(sch); + struct Qdisc *child = q->qdisc; + int i; + u32 p_min = ~0; + u32 minqlen = ~0; + u32 r, slot, salt, sfbhash; + int ret = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; + + if (q->rehash_interval > 0) { + unsigned long limit = q->rehash_time + q->rehash_interval; + + if (unlikely(time_after(jiffies, limit))) { + sfb_swap_slot(q); + q->rehash_time = jiffies; + } else if (unlikely(!q->double_buffering && q->warmup_time > 0 && + time_after(jiffies, limit - q->warmup_time))) { + q->double_buffering = true; + } + } + + if (q->filter_list) { + /* If using external classifiers, get result and record it. */ + if (!sfb_classify(skb, q, &ret, &salt)) + goto other_drop; + } else { + salt = skb_get_rxhash(skb); + } + + slot = q->slot; + + sfbhash = jhash_1word(salt, q->bins[slot].perturbation); + if (!sfbhash) + sfbhash = 1; + sfb_skb_cb(skb)->hashes[slot] = sfbhash; + + for (i = 0; i < SFB_LEVELS; i++) { + u32 hash = sfbhash & SFB_BUCKET_MASK; + struct sfb_bucket *b = &q->bins[slot].bins[i][hash]; + + sfbhash >>= SFB_BUCKET_SHIFT; + if (b->qlen == 0) + decrement_prob(b, q); + else if (b->qlen >= q->bin_size) + increment_prob(b, q); + if (minqlen > b->qlen) + minqlen = b->qlen; + if (p_min > b->p_mark) + p_min = b->p_mark; + } + + slot ^= 1; + sfb_skb_cb(skb)->hashes[slot] = 0; + + if (unlikely(minqlen >= q->max || sch->q.qlen >= q->limit)) { + sch->qstats.overlimits++; + if (minqlen >= q->max) + q->stats.bucketdrop++; + else + q->stats.queuedrop++; + goto drop; + } + + if (unlikely(p_min >= SFB_MAX_PROB)) { + /* Inelastic flow */ + if (q->double_buffering) { + sfbhash = jhash_1word(salt, q->bins[slot].perturbation); + if (!sfbhash) + sfbhash = 1; + sfb_skb_cb(skb)->hashes[slot] = sfbhash; + + for (i = 0; i < SFB_LEVELS; i++) { + u32 hash = sfbhash & SFB_BUCKET_MASK; + struct sfb_bucket *b = &q->bins[slot].bins[i][hash]; + + sfbhash >>= SFB_BUCKET_SHIFT; + if (b->qlen == 0) + decrement_prob(b, q); + else if (b->qlen >= q->bin_size) + increment_prob(b, q); + } + } + if (sfb_rate_limit(skb, q)) { + sch->qstats.overlimits++; + q->stats.penaltydrop++; + goto drop; + } + goto enqueue; + } + + r = net_random() & SFB_MAX_PROB; + + if (unlikely(r < p_min)) { + if (unlikely(p_min > SFB_MAX_PROB / 2)) { + /* If we're marking that many packets, then either + * this flow is unresponsive, or we're badly congested. + * In either case, we want to start dropping packets. + */ + if (r < (p_min - SFB_MAX_PROB / 2) * 2) { + q->stats.earlydrop++; + goto drop; + } + } + if (INET_ECN_set_ce(skb)) { + q->stats.marked++; + } else { + q->stats.earlydrop++; + goto drop; + } + } + +enqueue: + ret = qdisc_enqueue(skb, child); + if (likely(ret == NET_XMIT_SUCCESS)) { + sch->q.qlen++; + increment_qlen(skb, q); + } else if (net_xmit_drop_count(ret)) { + q->stats.childdrop++; + sch->qstats.drops++; + } + return ret; + +drop: + qdisc_drop(skb, sch); + return NET_XMIT_CN; +other_drop: + if (ret & __NET_XMIT_BYPASS) + sch->qstats.drops++; + kfree_skb(skb); + return ret; +} + +static struct sk_buff *sfb_dequeue(struct Qdisc *sch) +{ + struct sfb_sched_data *q = qdisc_priv(sch); + struct Qdisc *child = q->qdisc; + struct sk_buff *skb; + + skb = child->dequeue(q->qdisc); + + if (skb) { + qdisc_bstats_update(sch, skb); + sch->q.qlen--; + decrement_qlen(skb, q); + } + + return skb; +} + +static struct sk_buff *sfb_peek(struct Qdisc *sch) +{ + struct sfb_sched_data *q = qdisc_priv(sch); + struct Qdisc *child = q->qdisc; + + return child->ops->peek(child); +} + +/* No sfb_drop -- impossible since the child doesn't return the dropped skb. */ + +static void sfb_reset(struct Qdisc *sch) +{ + struct sfb_sched_data *q = qdisc_priv(sch); + + qdisc_reset(q->qdisc); + sch->q.qlen = 0; + q->slot = 0; + q->double_buffering = false; + sfb_zero_all_buckets(q); + sfb_init_perturbation(0, q); +} + +static void sfb_destroy(struct Qdisc *sch) +{ + struct sfb_sched_data *q = qdisc_priv(sch); + + tcf_destroy_chain(&q->filter_list); + qdisc_destroy(q->qdisc); +} + +static const struct nla_policy sfb_policy[TCA_SFB_MAX + 1] = { + [TCA_SFB_PARMS] = { .len = sizeof(struct tc_sfb_qopt) }, +}; + +static const struct tc_sfb_qopt sfb_default_ops = { + .rehash_interval = 600 * MSEC_PER_SEC, + .warmup_time = 60 * MSEC_PER_SEC, + .limit = 0, + .max = 25, + .bin_size = 20, + .increment = (SFB_MAX_PROB + 500) / 1000, /* 0.1 % */ + .decrement = (SFB_MAX_PROB + 3000) / 6000, + .penalty_rate = 10, + .penalty_burst = 20, +}; + +static int sfb_change(struct Qdisc *sch, struct nlattr *opt) +{ + struct sfb_sched_data *q = qdisc_priv(sch); + struct Qdisc *child; + struct nlattr *tb[TCA_SFB_MAX + 1]; + const struct tc_sfb_qopt *ctl = &sfb_default_ops; + u32 limit; + int err; + + if (opt) { + err = nla_parse_nested(tb, TCA_SFB_MAX, opt, sfb_policy); + if (err < 0) + return -EINVAL; + + if (tb[TCA_SFB_PARMS] == NULL) + return -EINVAL; + + ctl = nla_data(tb[TCA_SFB_PARMS]); + } + + limit = ctl->limit; + if (limit == 0) + limit = max_t(u32, qdisc_dev(sch)->tx_queue_len, 1); + + child = fifo_create_dflt(sch, &pfifo_qdisc_ops, limit); + if (IS_ERR(child)) + return PTR_ERR(child); + + sch_tree_lock(sch); + + qdisc_tree_decrease_qlen(q->qdisc, q->qdisc->q.qlen); + qdisc_destroy(q->qdisc); + q->qdisc = child; + + q->rehash_interval = msecs_to_jiffies(ctl->rehash_interval); + q->warmup_time = msecs_to_jiffies(ctl->warmup_time); + q->rehash_time = jiffies; + q->limit = limit; + q->increment = ctl->increment; + q->decrement = ctl->decrement; + q->max = ctl->max; + q->bin_size = ctl->bin_size; + q->penalty_rate = ctl->penalty_rate; + q->penalty_burst = ctl->penalty_burst; + q->tokens_avail = ctl->penalty_burst; + q->token_time = jiffies; + + q->slot = 0; + q->double_buffering = false; + sfb_zero_all_buckets(q); + sfb_init_perturbation(0, q); + sfb_init_perturbation(1, q); + + sch_tree_unlock(sch); + + return 0; +} + +static int sfb_init(struct Qdisc *sch, struct nlattr *opt) +{ + struct sfb_sched_data *q = qdisc_priv(sch); + + q->qdisc = &noop_qdisc; + return sfb_change(sch, opt); +} + +static int sfb_dump(struct Qdisc *sch, struct sk_buff *skb) +{ + struct sfb_sched_data *q = qdisc_priv(sch); + struct nlattr *opts; + struct tc_sfb_qopt opt = { + .rehash_interval = jiffies_to_msecs(q->rehash_interval), + .warmup_time = jiffies_to_msecs(q->warmup_time), + .limit = q->limit, + .max = q->max, + .bin_size = q->bin_size, + .increment = q->increment, + .decrement = q->decrement, + .penalty_rate = q->penalty_rate, + .penalty_burst = q->penalty_burst, + }; + + sch->qstats.backlog = q->qdisc->qstats.backlog; + opts = nla_nest_start(skb, TCA_OPTIONS); + NLA_PUT(skb, TCA_SFB_PARMS, sizeof(opt), &opt); + return nla_nest_end(skb, opts); + +nla_put_failure: + nla_nest_cancel(skb, opts); + return -EMSGSIZE; +} + +static int sfb_dump_stats(struct Qdisc *sch, struct gnet_dump *d) +{ + struct sfb_sched_data *q = qdisc_priv(sch); + struct tc_sfb_xstats st = { + .earlydrop = q->stats.earlydrop, + .penaltydrop = q->stats.penaltydrop, + .bucketdrop = q->stats.bucketdrop, + .queuedrop = q->stats.queuedrop, + .childdrop = q->stats.childdrop, + .marked = q->stats.marked, + }; + + st.maxqlen = sfb_compute_qlen(&st.maxprob, &st.avgprob, q); + + return gnet_stats_copy_app(d, &st, sizeof(st)); +} + +static int sfb_dump_class(struct Qdisc *sch, unsigned long cl, + struct sk_buff *skb, struct tcmsg *tcm) +{ + return -ENOSYS; +} + +static int sfb_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, + struct Qdisc **old) +{ + struct sfb_sched_data *q = qdisc_priv(sch); + + if (new == NULL) + new = &noop_qdisc; + + sch_tree_lock(sch); + *old = q->qdisc; + q->qdisc = new; + qdisc_tree_decrease_qlen(*old, (*old)->q.qlen); + qdisc_reset(*old); + sch_tree_unlock(sch); + return 0; +} + +static struct Qdisc *sfb_leaf(struct Qdisc *sch, unsigned long arg) +{ + struct sfb_sched_data *q = qdisc_priv(sch); + + return q->qdisc; +} + +static unsigned long sfb_get(struct Qdisc *sch, u32 classid) +{ + return 1; +} + +static void sfb_put(struct Qdisc *sch, unsigned long arg) +{ +} + +static int sfb_change_class(struct Qdisc *sch, u32 classid, u32 parentid, + struct nlattr **tca, unsigned long *arg) +{ + return -ENOSYS; +} + +static int sfb_delete(struct Qdisc *sch, unsigned long cl) +{ + return -ENOSYS; +} + +static void sfb_walk(struct Qdisc *sch, struct qdisc_walker *walker) +{ + if (!walker->stop) { + if (walker->count >= walker->skip) + if (walker->fn(sch, 1, walker) < 0) { + walker->stop = 1; + return; + } + walker->count++; + } +} + +static struct tcf_proto **sfb_find_tcf(struct Qdisc *sch, unsigned long cl) +{ + struct sfb_sched_data *q = qdisc_priv(sch); + + if (cl) + return NULL; + return &q->filter_list; +} + +static unsigned long sfb_bind(struct Qdisc *sch, unsigned long parent, + u32 classid) +{ + return 0; +} + + +static const struct Qdisc_class_ops sfb_class_ops = { + .graft = sfb_graft, + .leaf = sfb_leaf, + .get = sfb_get, + .put = sfb_put, + .change = sfb_change_class, + .delete = sfb_delete, + .walk = sfb_walk, + .tcf_chain = sfb_find_tcf, + .bind_tcf = sfb_bind, + .unbind_tcf = sfb_put, + .dump = sfb_dump_class, +}; + +static struct Qdisc_ops sfb_qdisc_ops __read_mostly = { + .id = "sfb", + .priv_size = sizeof(struct sfb_sched_data), + .cl_ops = &sfb_class_ops, + .enqueue = sfb_enqueue, + .dequeue = sfb_dequeue, + .peek = sfb_peek, + .init = sfb_init, + .reset = sfb_reset, + .destroy = sfb_destroy, + .change = sfb_change, + .dump = sfb_dump, + .dump_stats = sfb_dump_stats, + .owner = THIS_MODULE, +}; + +static int __init sfb_module_init(void) +{ + return register_qdisc(&sfb_qdisc_ops); +} + +static void __exit sfb_module_exit(void) +{ + unregister_qdisc(&sfb_qdisc_ops); +} + +module_init(sfb_module_init) +module_exit(sfb_module_exit) + +MODULE_DESCRIPTION("Stochastic Fair Blue queue discipline"); +MODULE_AUTHOR("Juliusz Chroboczek"); +MODULE_AUTHOR("Eric Dumazet"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ecc1058aecd986b9f00ba0b4b6ff3681dfb3ab47 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 23 Feb 2011 14:12:25 -0800 Subject: Revert "staging: iio: ak8975: add platform data." This reverts commit f2f1794835f1d8900d2b15d114c54e70c849809b. It should not be putting code into the include/input/ directory, and lots of other people have complained about it. Cc: Tony SIM Cc: Andrew Chew Cc: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/magnetometer/ak8975.c | 8 +------- include/linux/input/ak8975.h | 20 -------------------- 2 files changed, 1 insertion(+), 27 deletions(-) delete mode 100644 include/linux/input/ak8975.h (limited to 'include') diff --git a/drivers/staging/iio/magnetometer/ak8975.c b/drivers/staging/iio/magnetometer/ak8975.c index 80c0f4137076..420f206cf517 100644 --- a/drivers/staging/iio/magnetometer/ak8975.c +++ b/drivers/staging/iio/magnetometer/ak8975.c @@ -29,7 +29,6 @@ #include #include -#include #include "../iio.h" #include "magnet.h" @@ -436,7 +435,6 @@ static int ak8975_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ak8975_data *data; - struct ak8975_platform_data *pdata; int err; /* Allocate our device context. */ @@ -454,11 +452,7 @@ static int ak8975_probe(struct i2c_client *client, /* Grab and set up the supplied GPIO. */ data->eoc_irq = client->irq; - pdata = client->dev.platform_data; - if (pdata) - data->eoc_gpio = pdata->gpio; - else - data->eoc_gpio = irq_to_gpio(client->irq); + data->eoc_gpio = irq_to_gpio(client->irq); if (!data->eoc_gpio) { dev_err(&client->dev, "failed, no valid GPIO\n"); diff --git a/include/linux/input/ak8975.h b/include/linux/input/ak8975.h deleted file mode 100644 index 25d41eb10c3e..000000000000 --- a/include/linux/input/ak8975.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * ak8975 platform support - * - * Copyright (C) 2010 Renesas Solutions Corp. - * - * Author: Tony SIM - * - * 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 _AK8975_H -#define _AK8975_H - -struct ak8975_platform_data { - int gpio; -}; - -#endif -- cgit v1.2.3 From 39fc0ce5710c53bad14aaba1a789eec810c556f9 Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Tue, 22 Feb 2011 16:52:29 +0000 Subject: net: Implement SFEATURES compatibility for not updated drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use discrete setting ops for not updated drivers. This will not make them conform to full G/SFEATURES semantics, though. Signed-off-by: Michał Mirosław Signed-off-by: David S. Miller --- include/linux/ethtool.h | 5 ++++ net/core/ethtool.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) (limited to 'include') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 54d776c2c1b5..aac3e2eeb4fd 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -591,6 +591,9 @@ struct ethtool_sfeatures { * Probably there are other device-specific constraints on some features * in the set. When %ETHTOOL_F_UNSUPPORTED is set, .valid is considered * here as though ignored bits were cleared. + * %ETHTOOL_F_COMPAT - some or all changes requested were made by calling + * compatibility functions. Requested offload state cannot be properly + * managed by kernel. * * Meaning of bits in the masks are obtained by %ETHTOOL_GSSET_INFO (number of * bits in the arrays - always multiple of 32) and %ETHTOOL_GSTRINGS commands @@ -600,10 +603,12 @@ struct ethtool_sfeatures { enum ethtool_sfeatures_retval_bits { ETHTOOL_F_UNSUPPORTED__BIT, ETHTOOL_F_WISH__BIT, + ETHTOOL_F_COMPAT__BIT, }; #define ETHTOOL_F_UNSUPPORTED (1 << ETHTOOL_F_UNSUPPORTED__BIT) #define ETHTOOL_F_WISH (1 << ETHTOOL_F_WISH__BIT) +#define ETHTOOL_F_COMPAT (1 << ETHTOOL_F_COMPAT__BIT) #ifdef __KERNEL__ diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 69a3edc182f9..c1a71bb738da 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -178,6 +178,64 @@ static void ethtool_get_features_compat(struct net_device *dev, if (dev->ethtool_ops->get_rx_csum) if (dev->ethtool_ops->get_rx_csum(dev)) features[0].active |= NETIF_F_RXCSUM; + + /* mark legacy-changeable features */ + if (dev->ethtool_ops->set_sg) + features[0].available |= NETIF_F_SG; + if (dev->ethtool_ops->set_tx_csum) + features[0].available |= NETIF_F_ALL_CSUM; + if (dev->ethtool_ops->set_tso) + features[0].available |= NETIF_F_ALL_TSO; + if (dev->ethtool_ops->set_rx_csum) + features[0].available |= NETIF_F_RXCSUM; + if (dev->ethtool_ops->set_flags) + features[0].available |= flags_dup_features; +} + +static int ethtool_set_feature_compat(struct net_device *dev, + int (*legacy_set)(struct net_device *, u32), + struct ethtool_set_features_block *features, u32 mask) +{ + u32 do_set; + + if (!legacy_set) + return 0; + + if (!(features[0].valid & mask)) + return 0; + + features[0].valid &= ~mask; + + do_set = !!(features[0].requested & mask); + + if (legacy_set(dev, do_set) < 0) + netdev_info(dev, + "Legacy feature change (%s) failed for 0x%08x\n", + do_set ? "set" : "clear", mask); + + return 1; +} + +static int ethtool_set_features_compat(struct net_device *dev, + struct ethtool_set_features_block *features) +{ + int compat; + + if (!dev->ethtool_ops) + return 0; + + compat = ethtool_set_feature_compat(dev, dev->ethtool_ops->set_sg, + features, NETIF_F_SG); + compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_tx_csum, + features, NETIF_F_ALL_CSUM); + compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_tso, + features, NETIF_F_ALL_TSO); + compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_rx_csum, + features, NETIF_F_RXCSUM); + compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_flags, + features, flags_dup_features); + + return compat; } static int ethtool_get_features(struct net_device *dev, void __user *useraddr) @@ -234,6 +292,9 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr) if (features[0].valid & ~NETIF_F_ETHTOOL_BITS) return -EINVAL; + if (ethtool_set_features_compat(dev, features)) + ret |= ETHTOOL_F_COMPAT; + if (features[0].valid & ~dev->hw_features) { features[0].valid &= dev->hw_features; ret |= ETHTOOL_F_UNSUPPORTED; -- cgit v1.2.3 From 5413b4c6c07b659e52c84a4e40d897b32b89834f Mon Sep 17 00:00:00 2001 From: Allan Stephens Date: Tue, 18 Jan 2011 13:24:55 -0500 Subject: tipc: Improve handling of invalid link tolerance values Enhances TIPC link code to ignore an invalid link tolerance value contained in an incoming LINK_PROTOCOL message, rather than processing the value and potentially causing a divide-by-zero error. Also add a compile-time check that catches attempts to redefine TIPC's minimum link tolerance value in a manner that might result in the same divide-by-zero error at run-time. Signed-off-by: Allan Stephens Signed-off-by: Paul Gortmaker --- include/linux/tipc_config.h | 4 ++++ net/tipc/link.c | 3 +++ 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/include/linux/tipc_config.h b/include/linux/tipc_config.h index 7d42460a5e3c..c14102dee22e 100644 --- a/include/linux/tipc_config.h +++ b/include/linux/tipc_config.h @@ -193,6 +193,10 @@ #define TIPC_DEF_LINK_TOL 1500 #define TIPC_MAX_LINK_TOL 30000 +#if (TIPC_MIN_LINK_TOL < 16) +#error "TIPC_MIN_LINK_TOL is too small (abort limit may be NaN)" +#endif + /* * Link window limits (min, default, max), in packets */ diff --git a/net/tipc/link.c b/net/tipc/link.c index 1c5c53a81531..3c1c28cdbaa4 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -2617,6 +2617,9 @@ static void link_check_defragm_bufs(struct link *l_ptr) static void link_set_supervision_props(struct link *l_ptr, u32 tolerance) { + if ((tolerance < TIPC_MIN_LINK_TOL) || (tolerance > TIPC_MAX_LINK_TOL)) + return; + l_ptr->tolerance = tolerance; l_ptr->continuity_interval = ((tolerance / 4) > 500) ? 500 : tolerance / 4; -- cgit v1.2.3 From 77c81e0bb8af3f1a0e5d84dd0346fe57dfe3da27 Mon Sep 17 00:00:00 2001 From: Allan Stephens Date: Tue, 18 Jan 2011 13:37:09 -0500 Subject: tipc: Clean out all remaining instances of #if 0'd unused code Remove all instances of legacy or proposed-but-not-implemented code that lives within an #if 0 ... #endif block. If some of it is needed in the future it can recovered out of history, but there is no need for it to clutter up the active code base. Signed-off-by: Allan Stephens Signed-off-by: Paul Gortmaker --- include/linux/tipc.h | 8 +------- include/linux/tipc_config.h | 28 +--------------------------- 2 files changed, 2 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/linux/tipc.h b/include/linux/tipc.h index 1eefa3f6d1f4..a5b994a204d2 100644 --- a/include/linux/tipc.h +++ b/include/linux/tipc.h @@ -2,7 +2,7 @@ * include/linux/tipc.h: Include file for TIPC socket interface * * Copyright (c) 2003-2006, Ericsson AB - * Copyright (c) 2005, Wind River Systems + * Copyright (c) 2005, 2010-2011, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -130,12 +130,6 @@ static inline unsigned int tipc_node(__u32 addr) #define TIPC_SUB_PORTS 0x01 /* filter for port availability */ #define TIPC_SUB_SERVICE 0x02 /* filter for service availability */ #define TIPC_SUB_CANCEL 0x04 /* cancel a subscription */ -#if 0 -/* The following filter options are not currently implemented */ -#define TIPC_SUB_NO_BIND_EVTS 0x04 /* filter out "publish" events */ -#define TIPC_SUB_NO_UNBIND_EVTS 0x08 /* filter out "withdraw" events */ -#define TIPC_SUB_SINGLE_EVT 0x10 /* expire after first event */ -#endif #define TIPC_WAIT_FOREVER (~0) /* timeout for permanent subscription */ diff --git a/include/linux/tipc_config.h b/include/linux/tipc_config.h index c14102dee22e..011556fcef04 100644 --- a/include/linux/tipc_config.h +++ b/include/linux/tipc_config.h @@ -2,7 +2,7 @@ * include/linux/tipc_config.h: Include file for TIPC configuration interface * * Copyright (c) 2003-2006, Ericsson AB - * Copyright (c) 2005-2007, Wind River Systems + * Copyright (c) 2005-2007, 2010-2011, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -76,13 +76,6 @@ #define TIPC_CMD_SHOW_LINK_STATS 0x000B /* tx link_name, rx ultra_string */ #define TIPC_CMD_SHOW_STATS 0x000F /* tx unsigned, rx ultra_string */ -#if 0 -#define TIPC_CMD_SHOW_PORT_STATS 0x0008 /* tx port_ref, rx ultra_string */ -#define TIPC_CMD_RESET_PORT_STATS 0x0009 /* tx port_ref, rx none */ -#define TIPC_CMD_GET_ROUTES 0x000A /* tx ?, rx ? */ -#define TIPC_CMD_GET_LINK_PEER 0x000D /* tx link_name, rx ? */ -#endif - /* * Protected commands: * May only be issued by "network administration capable" process. @@ -109,13 +102,6 @@ #define TIPC_CMD_DUMP_LOG 0x410B /* tx none, rx ultra_string */ #define TIPC_CMD_RESET_LINK_STATS 0x410C /* tx link_name, rx none */ -#if 0 -#define TIPC_CMD_CREATE_LINK 0x4103 /* tx link_create, rx none */ -#define TIPC_CMD_REMOVE_LINK 0x4104 /* tx link_name, rx none */ -#define TIPC_CMD_BLOCK_LINK 0x4105 /* tx link_name, rx none */ -#define TIPC_CMD_UNBLOCK_LINK 0x4106 /* tx link_name, rx none */ -#endif - /* * Private commands: * May only be issued by "network administration capable" process. @@ -123,9 +109,6 @@ */ #define TIPC_CMD_SET_NODE_ADDR 0x8001 /* tx net_addr, rx none */ -#if 0 -#define TIPC_CMD_SET_ZONE_MASTER 0x8002 /* tx none, rx none */ -#endif #define TIPC_CMD_SET_REMOTE_MNG 0x8003 /* tx unsigned, rx none */ #define TIPC_CMD_SET_MAX_PORTS 0x8004 /* tx unsigned, rx none */ #define TIPC_CMD_SET_MAX_PUBL 0x8005 /* tx unsigned, rx none */ @@ -251,15 +234,6 @@ struct tipc_name_table_query { #define TIPC_CFG_NOT_SUPPORTED "\x84" /* request is not supported by TIPC */ #define TIPC_CFG_INVALID_VALUE "\x85" /* request has invalid argument value */ -#if 0 -/* prototypes TLV structures for proposed commands */ -struct tipc_link_create { - __u32 domain; - struct tipc_media_addr peer_addr; - char bearer_name[TIPC_MAX_BEARER_NAME]; -}; -#endif - /* * A TLV consists of a descriptor, followed by the TLV value. * TLV descriptor fields are stored in network byte order; -- cgit v1.2.3 From adf779c1ee1d5556ebd83e39a7189022d4ebce3a Mon Sep 17 00:00:00 2001 From: Heungjun Kim Date: Wed, 23 Feb 2011 21:40:11 -0800 Subject: Input: mcs_touchkey - add support for suspend/resume This adds support for system-level suspend/resume to the driver. Signed-off-by: Heungjun Kim Signed-off-by: Kyungmin Park Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/mcs_touchkey.c | 48 ++++++++++++++++++++++++++++++++++- include/linux/i2c/mcs.h | 1 + 2 files changed, 48 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c index 63b849d7e90b..03fa59a0b220 100644 --- a/drivers/input/keyboard/mcs_touchkey.c +++ b/drivers/input/keyboard/mcs_touchkey.c @@ -1,5 +1,5 @@ /* - * mcs_touchkey.c - Touchkey driver for MELFAS MCS5000/5080 controller + * Touchkey driver for MELFAS MCS5000/5080 controller * * Copyright (C) 2010 Samsung Electronics Co.Ltd * Author: HeungJun Kim @@ -19,6 +19,7 @@ #include #include #include +#include /* MCS5000 Touchkey */ #define MCS5000_TOUCHKEY_STATUS 0x04 @@ -45,6 +46,8 @@ struct mcs_touchkey_chip { }; struct mcs_touchkey_data { + void (*poweron)(bool); + struct i2c_client *client; struct input_dev *input_dev; struct mcs_touchkey_chip chip; @@ -169,6 +172,11 @@ static int __devinit mcs_touchkey_probe(struct i2c_client *client, if (pdata->cfg_pin) pdata->cfg_pin(); + if (pdata->poweron) { + data->poweron = pdata->poweron; + data->poweron(true); + } + error = request_threaded_irq(client->irq, NULL, mcs_touchkey_interrupt, IRQF_TRIGGER_FALLING, client->dev.driver->name, data); if (error) { @@ -196,12 +204,49 @@ static int __devexit mcs_touchkey_remove(struct i2c_client *client) struct mcs_touchkey_data *data = i2c_get_clientdata(client); free_irq(client->irq, data); + if (data->poweron) + data->poweron(false); input_unregister_device(data->input_dev); kfree(data); return 0; } +#ifdef CONFIG_PM_SLEEP +static int mcs_touchkey_suspend(struct device *dev) +{ + struct mcs_touchkey_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + /* Disable the work */ + disable_irq(client->irq); + + /* Finally turn off the power */ + if (data->poweron) + data->poweron(false); + + return 0; +} + +static int mcs_touchkey_resume(struct device *dev) +{ + struct mcs_touchkey_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + /* Enable the device first */ + if (data->poweron) + data->poweron(true); + + /* Enable irq again */ + enable_irq(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mcs_touchkey_pm_ops, + mcs_touchkey_suspend, mcs_touchkey_resume); + static const struct i2c_device_id mcs_touchkey_id[] = { { "mcs5000_touchkey", MCS5000_TOUCHKEY }, { "mcs5080_touchkey", MCS5080_TOUCHKEY }, @@ -213,6 +258,7 @@ static struct i2c_driver mcs_touchkey_driver = { .driver = { .name = "mcs_touchkey", .owner = THIS_MODULE, + .pm = &mcs_touchkey_pm_ops, }, .probe = mcs_touchkey_probe, .remove = __devexit_p(mcs_touchkey_remove), diff --git a/include/linux/i2c/mcs.h b/include/linux/i2c/mcs.h index 725ae7c313ff..61bb18a4fd3c 100644 --- a/include/linux/i2c/mcs.h +++ b/include/linux/i2c/mcs.h @@ -18,6 +18,7 @@ #define MCS_KEY_CODE(v) ((v) & 0xffff) struct mcs_platform_data { + void (*poweron)(bool); void (*cfg_pin)(void); /* touchscreen */ -- cgit v1.2.3 From 214e005bc32c7045b8554f9f0fb07b3fcce2cd42 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 24 Feb 2011 00:02:38 -0500 Subject: xfrm: Pass km_event pointers around as const when possible. Signed-off-by: David S. Miller --- include/net/xfrm.h | 8 ++++---- net/key/af_key.c | 16 ++++++++-------- net/xfrm/xfrm_state.c | 4 ++-- net/xfrm/xfrm_user.c | 24 ++++++++++++------------ 4 files changed, 26 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index bb824a5d71bf..6ef5c374ef8a 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -284,8 +284,8 @@ struct xfrm_policy_afinfo { extern int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo); extern int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo); -extern void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c); -extern void km_state_notify(struct xfrm_state *x, struct km_event *c); +extern void km_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c); +extern void km_state_notify(struct xfrm_state *x, const struct km_event *c); struct xfrm_tmpl; extern int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol); @@ -548,11 +548,11 @@ struct xfrm_migrate { struct xfrm_mgr { struct list_head list; char *id; - int (*notify)(struct xfrm_state *x, struct km_event *c); + int (*notify)(struct xfrm_state *x, const struct km_event *c); int (*acquire)(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *xp, int dir); struct xfrm_policy *(*compile_policy)(struct sock *sk, int opt, u8 *data, int len, int *dir); int (*new_mapping)(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport); - int (*notify_policy)(struct xfrm_policy *x, int dir, struct km_event *c); + int (*notify_policy)(struct xfrm_policy *x, int dir, const struct km_event *c); int (*report)(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr); int (*migrate)(struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_bundles, struct xfrm_kmaddress *k); }; diff --git a/net/key/af_key.c b/net/key/af_key.c index 60fd2f13f427..7c5e101e7c28 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1429,7 +1429,7 @@ static inline int event2keytype(int event) } /* ADD/UPD/DEL */ -static int key_notify_sa(struct xfrm_state *x, struct km_event *c) +static int key_notify_sa(struct xfrm_state *x, const struct km_event *c) { struct sk_buff *skb; struct sadb_msg *hdr; @@ -1688,7 +1688,7 @@ static int unicast_flush_resp(struct sock *sk, struct sadb_msg *ihdr) return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ONE, sk, sock_net(sk)); } -static int key_notify_sa_flush(struct km_event *c) +static int key_notify_sa_flush(const struct km_event *c) { struct sk_buff *skb; struct sadb_msg *hdr; @@ -2123,7 +2123,7 @@ static int pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, in return 0; } -static int key_notify_policy(struct xfrm_policy *xp, int dir, struct km_event *c) +static int key_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c) { struct sk_buff *out_skb; struct sadb_msg *out_hdr; @@ -2660,7 +2660,7 @@ static int pfkey_spddump(struct sock *sk, struct sk_buff *skb, struct sadb_msg * return pfkey_do_dump(pfk); } -static int key_notify_policy_flush(struct km_event *c) +static int key_notify_policy_flush(const struct km_event *c) { struct sk_buff *skb_out; struct sadb_msg *hdr; @@ -2914,12 +2914,12 @@ static void dump_esp_combs(struct sk_buff *skb, struct xfrm_tmpl *t) } } -static int key_notify_policy_expire(struct xfrm_policy *xp, struct km_event *c) +static int key_notify_policy_expire(struct xfrm_policy *xp, const struct km_event *c) { return 0; } -static int key_notify_sa_expire(struct xfrm_state *x, struct km_event *c) +static int key_notify_sa_expire(struct xfrm_state *x, const struct km_event *c) { struct sk_buff *out_skb; struct sadb_msg *out_hdr; @@ -2949,7 +2949,7 @@ static int key_notify_sa_expire(struct xfrm_state *x, struct km_event *c) return 0; } -static int pfkey_send_notify(struct xfrm_state *x, struct km_event *c) +static int pfkey_send_notify(struct xfrm_state *x, const struct km_event *c) { struct net *net = x ? xs_net(x) : c->net; struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id); @@ -2976,7 +2976,7 @@ static int pfkey_send_notify(struct xfrm_state *x, struct km_event *c) return 0; } -static int pfkey_send_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) +static int pfkey_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) { if (xp && xp->type != XFRM_POLICY_TYPE_MAIN) return 0; diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 30a0f178819c..7028f063f093 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1727,7 +1727,7 @@ void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq) static LIST_HEAD(xfrm_km_list); static DEFINE_RWLOCK(xfrm_km_lock); -void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) +void km_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) { struct xfrm_mgr *km; @@ -1738,7 +1738,7 @@ void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) read_unlock(&xfrm_km_lock); } -void km_state_notify(struct xfrm_state *x, struct km_event *c) +void km_state_notify(struct xfrm_state *x, const struct km_event *c) { struct xfrm_mgr *km; read_lock(&xfrm_km_lock); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 61291965c5f6..2cc9dab29887 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1582,7 +1582,7 @@ static inline size_t xfrm_aevent_msgsize(void) + nla_total_size(4); /* XFRM_AE_ETHR */ } -static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, struct km_event *c) +static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c) { struct xfrm_aevent_id *id; struct nlmsghdr *nlh; @@ -2220,7 +2220,7 @@ static inline size_t xfrm_expire_msgsize(void) + nla_total_size(sizeof(struct xfrm_mark)); } -static int build_expire(struct sk_buff *skb, struct xfrm_state *x, struct km_event *c) +static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c) { struct xfrm_user_expire *ue; struct nlmsghdr *nlh; @@ -2242,7 +2242,7 @@ nla_put_failure: return -EMSGSIZE; } -static int xfrm_exp_state_notify(struct xfrm_state *x, struct km_event *c) +static int xfrm_exp_state_notify(struct xfrm_state *x, const struct km_event *c) { struct net *net = xs_net(x); struct sk_buff *skb; @@ -2259,7 +2259,7 @@ static int xfrm_exp_state_notify(struct xfrm_state *x, struct km_event *c) return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); } -static int xfrm_aevent_state_notify(struct xfrm_state *x, struct km_event *c) +static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event *c) { struct net *net = xs_net(x); struct sk_buff *skb; @@ -2274,7 +2274,7 @@ static int xfrm_aevent_state_notify(struct xfrm_state *x, struct km_event *c) return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC); } -static int xfrm_notify_sa_flush(struct km_event *c) +static int xfrm_notify_sa_flush(const struct km_event *c) { struct net *net = c->net; struct xfrm_usersa_flush *p; @@ -2330,7 +2330,7 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x) return l; } -static int xfrm_notify_sa(struct xfrm_state *x, struct km_event *c) +static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c) { struct net *net = xs_net(x); struct xfrm_usersa_info *p; @@ -2387,7 +2387,7 @@ nla_put_failure: return -1; } -static int xfrm_send_state_notify(struct xfrm_state *x, struct km_event *c) +static int xfrm_send_state_notify(struct xfrm_state *x, const struct km_event *c) { switch (c->event) { @@ -2546,7 +2546,7 @@ static inline size_t xfrm_polexpire_msgsize(struct xfrm_policy *xp) } static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp, - int dir, struct km_event *c) + int dir, const struct km_event *c) { struct xfrm_user_polexpire *upe; struct nlmsghdr *nlh; @@ -2576,7 +2576,7 @@ nlmsg_failure: return -EMSGSIZE; } -static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) +static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) { struct net *net = xp_net(xp); struct sk_buff *skb; @@ -2591,7 +2591,7 @@ static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, struct km_eve return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC); } -static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, struct km_event *c) +static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c) { struct net *net = xp_net(xp); struct xfrm_userpolicy_info *p; @@ -2656,7 +2656,7 @@ nlmsg_failure: return -1; } -static int xfrm_notify_policy_flush(struct km_event *c) +static int xfrm_notify_policy_flush(const struct km_event *c) { struct net *net = c->net; struct nlmsghdr *nlh; @@ -2681,7 +2681,7 @@ nlmsg_failure: return -1; } -static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) +static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) { switch (c->event) { -- cgit v1.2.3 From 19bd62441c36279ab33e311faebd357ef04ba344 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 24 Feb 2011 00:07:20 -0500 Subject: xfrm: Const'ify tmpl and address arguments to ->init_temprop() Signed-off-by: David S. Miller --- include/net/xfrm.h | 6 ++++-- net/ipv4/xfrm4_state.c | 4 ++-- net/ipv6/xfrm6_state.c | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 6ef5c374ef8a..46f44703b611 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -302,8 +302,10 @@ struct xfrm_state_afinfo { int (*init_flags)(struct xfrm_state *x); void (*init_tempsel)(struct xfrm_selector *sel, const struct flowi *fl); - void (*init_temprop)(struct xfrm_state *x, struct xfrm_tmpl *tmpl, - xfrm_address_t *daddr, xfrm_address_t *saddr); + void (*init_temprop)(struct xfrm_state *x, + const struct xfrm_tmpl *tmpl, + const xfrm_address_t *daddr, + const xfrm_address_t *saddr); int (*tmpl_sort)(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n); int (*state_sort)(struct xfrm_state **dst, struct xfrm_state **src, int n); int (*output)(struct sk_buff *skb); diff --git a/net/ipv4/xfrm4_state.c b/net/ipv4/xfrm4_state.c index 19eb56067727..983eff248988 100644 --- a/net/ipv4/xfrm4_state.c +++ b/net/ipv4/xfrm4_state.c @@ -37,8 +37,8 @@ __xfrm4_init_tempsel(struct xfrm_selector *sel, const struct flowi *fl) } static void -xfrm4_init_temprop(struct xfrm_state *x, struct xfrm_tmpl *tmpl, - xfrm_address_t *daddr, xfrm_address_t *saddr) +xfrm4_init_temprop(struct xfrm_state *x, const struct xfrm_tmpl *tmpl, + const xfrm_address_t *daddr, const xfrm_address_t *saddr) { x->id = tmpl->id; if (x->id.daddr.a4 == 0) diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c index 68a14c0339db..a02598e0079a 100644 --- a/net/ipv6/xfrm6_state.c +++ b/net/ipv6/xfrm6_state.c @@ -38,8 +38,8 @@ __xfrm6_init_tempsel(struct xfrm_selector *sel, const struct flowi *fl) } static void -xfrm6_init_temprop(struct xfrm_state *x, struct xfrm_tmpl *tmpl, - xfrm_address_t *daddr, xfrm_address_t *saddr) +xfrm6_init_temprop(struct xfrm_state *x, const struct xfrm_tmpl *tmpl, + const xfrm_address_t *daddr, const xfrm_address_t *saddr) { x->id = tmpl->id; if (ipv6_addr_any((struct in6_addr*)&x->id.daddr)) -- cgit v1.2.3 From 200ce96e5601391a6d97c87067edf21fa94fb74e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 24 Feb 2011 00:12:25 -0500 Subject: xfrm: Const'ify selector argument to xfrm_selector_match() Signed-off-by: David S. Miller --- include/net/xfrm.h | 2 +- net/xfrm/xfrm_policy.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 46f44703b611..567f08b559a1 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -845,7 +845,7 @@ __be16 xfrm_flowi_dport(const struct flowi *fl) return port; } -extern int xfrm_selector_match(struct xfrm_selector *sel, +extern int xfrm_selector_match(const struct xfrm_selector *sel, const struct flowi *fl, unsigned short family); diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 28c865adf609..4827c8db864d 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -58,7 +58,7 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, int dir); static inline int -__xfrm4_selector_match(struct xfrm_selector *sel, const struct flowi *fl) +__xfrm4_selector_match(const struct xfrm_selector *sel, const struct flowi *fl) { return addr_match(&fl->fl4_dst, &sel->daddr, sel->prefixlen_d) && addr_match(&fl->fl4_src, &sel->saddr, sel->prefixlen_s) && @@ -69,7 +69,7 @@ __xfrm4_selector_match(struct xfrm_selector *sel, const struct flowi *fl) } static inline int -__xfrm6_selector_match(struct xfrm_selector *sel, const struct flowi *fl) +__xfrm6_selector_match(const struct xfrm_selector *sel, const struct flowi *fl) { return addr_match(&fl->fl6_dst, &sel->daddr, sel->prefixlen_d) && addr_match(&fl->fl6_src, &sel->saddr, sel->prefixlen_s) && @@ -79,7 +79,7 @@ __xfrm6_selector_match(struct xfrm_selector *sel, const struct flowi *fl) (fl->oif == sel->ifindex || !sel->ifindex); } -int xfrm_selector_match(struct xfrm_selector *sel, const struct flowi *fl, +int xfrm_selector_match(const struct xfrm_selector *sel, const struct flowi *fl, unsigned short family) { switch (family) { -- cgit v1.2.3 From 5e6b930f21b0a442f9d5db97c8314b4d91be1c27 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 24 Feb 2011 00:14:45 -0500 Subject: xfrm: Const'ify address arguments to ->dst_lookup() Signed-off-by: David S. Miller --- include/net/xfrm.h | 4 ++-- net/ipv4/xfrm4_policy.c | 4 ++-- net/ipv6/xfrm6_policy.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 567f08b559a1..18f115a6fb19 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -267,8 +267,8 @@ struct xfrm_policy_afinfo { struct dst_ops *dst_ops; void (*garbage_collect)(struct net *net); struct dst_entry *(*dst_lookup)(struct net *net, int tos, - xfrm_address_t *saddr, - xfrm_address_t *daddr); + const xfrm_address_t *saddr, + const xfrm_address_t *daddr); int (*get_saddr)(struct net *net, xfrm_address_t *saddr, xfrm_address_t *daddr); void (*decode_session)(struct sk_buff *skb, struct flowi *fl, diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 1e9844d1f8c5..63aa88efdcef 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -19,8 +19,8 @@ static struct xfrm_policy_afinfo xfrm4_policy_afinfo; static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, - xfrm_address_t *saddr, - xfrm_address_t *daddr) + const xfrm_address_t *saddr, + const xfrm_address_t *daddr) { struct flowi fl = { .fl4_dst = daddr->a4, diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index f2fa904b8a91..c128ca1affe3 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -27,8 +27,8 @@ static struct xfrm_policy_afinfo xfrm6_policy_afinfo; static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, - xfrm_address_t *saddr, - xfrm_address_t *daddr) + const xfrm_address_t *saddr, + const xfrm_address_t *daddr) { struct flowi fl = {}; struct dst_entry *dst; -- cgit v1.2.3 From ff6acd16825d59de3964b036183a5d214213b9a6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 24 Feb 2011 00:19:13 -0500 Subject: xfrm: Const'ify address arguments to xfrm_addr_cmp() Signed-off-by: David S. Miller --- include/net/xfrm.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 18f115a6fb19..1c82b944803a 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1520,7 +1520,8 @@ struct scatterlist; typedef int (icv_update_fn_t)(struct hash_desc *, struct scatterlist *, unsigned int); -static inline int xfrm_addr_cmp(xfrm_address_t *a, xfrm_address_t *b, +static inline int xfrm_addr_cmp(const xfrm_address_t *a, + const xfrm_address_t *b, int family) { switch (family) { -- cgit v1.2.3 From 6cc329610f2a1698576a2a8a94dbad8f82a66363 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 24 Feb 2011 00:19:59 -0500 Subject: xfrm: Const'ify address argument to xfrm_addr_any() Signed-off-by: David S. Miller --- include/net/xfrm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 1c82b944803a..b60f9564fb8d 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -954,7 +954,7 @@ secpath_reset(struct sk_buff *skb) } static inline int -xfrm_addr_any(xfrm_address_t *addr, unsigned short family) +xfrm_addr_any(const xfrm_address_t *addr, unsigned short family) { switch (family) { case AF_INET: -- cgit v1.2.3 From 183cad12785ffc036571c4b789dc084ec61a1bad Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 24 Feb 2011 00:28:01 -0500 Subject: xfrm: Const'ify pointer args to km_migrate() and implementations. Signed-off-by: David S. Miller --- include/net/xfrm.h | 12 ++++++++---- net/key/af_key.c | 22 +++++++++++----------- net/xfrm/xfrm_state.c | 6 +++--- net/xfrm/xfrm_user.c | 24 ++++++++++++------------ 4 files changed, 34 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index b60f9564fb8d..17b296b19982 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -556,7 +556,11 @@ struct xfrm_mgr { int (*new_mapping)(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport); int (*notify_policy)(struct xfrm_policy *x, int dir, const struct km_event *c); int (*report)(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr); - int (*migrate)(struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_bundles, struct xfrm_kmaddress *k); + int (*migrate)(const struct xfrm_selector *sel, + u8 dir, u8 type, + const struct xfrm_migrate *m, + int num_bundles, + const struct xfrm_kmaddress *k); }; extern int xfrm_register_km(struct xfrm_mgr *km); @@ -1483,9 +1487,9 @@ struct xfrm_state *xfrm_find_acq(struct net *net, struct xfrm_mark *mark, extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol); #ifdef CONFIG_XFRM_MIGRATE -extern int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type, - struct xfrm_migrate *m, int num_bundles, - struct xfrm_kmaddress *k); +extern int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, + const struct xfrm_migrate *m, int num_bundles, + const struct xfrm_kmaddress *k); extern struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m); extern struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x, struct xfrm_migrate *m); diff --git a/net/key/af_key.c b/net/key/af_key.c index 7c5e101e7c28..56372853142a 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -690,7 +690,7 @@ static inline int pfkey_mode_to_xfrm(int mode) } } -static unsigned int pfkey_sockaddr_fill(xfrm_address_t *xaddr, __be16 port, +static unsigned int pfkey_sockaddr_fill(const xfrm_address_t *xaddr, __be16 port, struct sockaddr *sa, unsigned short family) { @@ -3318,7 +3318,7 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, #ifdef CONFIG_NET_KEY_MIGRATE static int set_sadb_address(struct sk_buff *skb, int sasize, int type, - struct xfrm_selector *sel) + const struct xfrm_selector *sel) { struct sadb_address *addr; addr = (struct sadb_address *)skb_put(skb, sizeof(struct sadb_address) + sasize); @@ -3348,7 +3348,7 @@ static int set_sadb_address(struct sk_buff *skb, int sasize, int type, } -static int set_sadb_kmaddress(struct sk_buff *skb, struct xfrm_kmaddress *k) +static int set_sadb_kmaddress(struct sk_buff *skb, const struct xfrm_kmaddress *k) { struct sadb_x_kmaddress *kma; u8 *sa; @@ -3376,7 +3376,7 @@ static int set_sadb_kmaddress(struct sk_buff *skb, struct xfrm_kmaddress *k) static int set_ipsecrequest(struct sk_buff *skb, uint8_t proto, uint8_t mode, int level, uint32_t reqid, uint8_t family, - xfrm_address_t *src, xfrm_address_t *dst) + const xfrm_address_t *src, const xfrm_address_t *dst) { struct sadb_x_ipsecrequest *rq; u8 *sa; @@ -3404,9 +3404,9 @@ static int set_ipsecrequest(struct sk_buff *skb, #endif #ifdef CONFIG_NET_KEY_MIGRATE -static int pfkey_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, - struct xfrm_migrate *m, int num_bundles, - struct xfrm_kmaddress *k) +static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, + const struct xfrm_migrate *m, int num_bundles, + const struct xfrm_kmaddress *k) { int i; int sasize_sel; @@ -3415,7 +3415,7 @@ static int pfkey_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, struct sk_buff *skb; struct sadb_msg *hdr; struct sadb_x_policy *pol; - struct xfrm_migrate *mp; + const struct xfrm_migrate *mp; if (type != XFRM_POLICY_TYPE_MAIN) return 0; @@ -3513,9 +3513,9 @@ err: return -EINVAL; } #else -static int pfkey_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, - struct xfrm_migrate *m, int num_bundles, - struct xfrm_kmaddress *k) +static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, + const struct xfrm_migrate *m, int num_bundles, + const struct xfrm_kmaddress *k) { return -ENOPROTOOPT; } diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 7028f063f093..555beddb63f0 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1819,9 +1819,9 @@ void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid) EXPORT_SYMBOL(km_policy_expired); #ifdef CONFIG_XFRM_MIGRATE -int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type, - struct xfrm_migrate *m, int num_migrate, - struct xfrm_kmaddress *k) +int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, + const struct xfrm_migrate *m, int num_migrate, + const struct xfrm_kmaddress *k) { int err = -EINVAL; int ret; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 2cc9dab29887..b43c1b1240d4 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1986,7 +1986,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, #endif #ifdef CONFIG_XFRM_MIGRATE -static int copy_to_user_migrate(struct xfrm_migrate *m, struct sk_buff *skb) +static int copy_to_user_migrate(const struct xfrm_migrate *m, struct sk_buff *skb) { struct xfrm_user_migrate um; @@ -2004,7 +2004,7 @@ static int copy_to_user_migrate(struct xfrm_migrate *m, struct sk_buff *skb) return nla_put(skb, XFRMA_MIGRATE, sizeof(um), &um); } -static int copy_to_user_kmaddress(struct xfrm_kmaddress *k, struct sk_buff *skb) +static int copy_to_user_kmaddress(const struct xfrm_kmaddress *k, struct sk_buff *skb) { struct xfrm_user_kmaddress uk; @@ -2025,11 +2025,11 @@ static inline size_t xfrm_migrate_msgsize(int num_migrate, int with_kma) + userpolicy_type_attrsize(); } -static int build_migrate(struct sk_buff *skb, struct xfrm_migrate *m, - int num_migrate, struct xfrm_kmaddress *k, - struct xfrm_selector *sel, u8 dir, u8 type) +static int build_migrate(struct sk_buff *skb, const struct xfrm_migrate *m, + int num_migrate, const struct xfrm_kmaddress *k, + const struct xfrm_selector *sel, u8 dir, u8 type) { - struct xfrm_migrate *mp; + const struct xfrm_migrate *mp; struct xfrm_userpolicy_id *pol_id; struct nlmsghdr *nlh; int i; @@ -2061,9 +2061,9 @@ nlmsg_failure: return -EMSGSIZE; } -static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, - struct xfrm_migrate *m, int num_migrate, - struct xfrm_kmaddress *k) +static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, + const struct xfrm_migrate *m, int num_migrate, + const struct xfrm_kmaddress *k) { struct net *net = &init_net; struct sk_buff *skb; @@ -2079,9 +2079,9 @@ static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MIGRATE, GFP_ATOMIC); } #else -static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, - struct xfrm_migrate *m, int num_migrate, - struct xfrm_kmaddress *k) +static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, + const struct xfrm_migrate *m, int num_migrate, + const struct xfrm_kmaddress *k) { return -ENOPROTOOPT; } -- cgit v1.2.3 From b4b7c0b389131c34b6c3a6bf3f3c4d17fe59155f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 24 Feb 2011 00:35:06 -0500 Subject: xfrm: Const'ify selector args in xfrm_migrate paths. Signed-off-by: David S. Miller --- include/net/xfrm.h | 2 +- net/xfrm/xfrm_policy.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 17b296b19982..1806c918a15a 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1493,7 +1493,7 @@ extern int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, extern struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m); extern struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x, struct xfrm_migrate *m); -extern int xfrm_migrate(struct xfrm_selector *sel, u8 dir, u8 type, +extern int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_bundles, struct xfrm_kmaddress *k); #endif diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 0770b3ae5ccb..0c503be3260c 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -2736,8 +2736,8 @@ EXPORT_SYMBOL_GPL(xfrm_audit_policy_delete); #endif #ifdef CONFIG_XFRM_MIGRATE -static int xfrm_migrate_selector_match(struct xfrm_selector *sel_cmp, - struct xfrm_selector *sel_tgt) +static int xfrm_migrate_selector_match(const struct xfrm_selector *sel_cmp, + const struct xfrm_selector *sel_tgt) { if (sel_cmp->proto == IPSEC_ULPROTO_ANY) { if (sel_tgt->family == sel_cmp->family && @@ -2757,7 +2757,7 @@ static int xfrm_migrate_selector_match(struct xfrm_selector *sel_cmp, return 0; } -static struct xfrm_policy * xfrm_migrate_policy_find(struct xfrm_selector *sel, +static struct xfrm_policy * xfrm_migrate_policy_find(const struct xfrm_selector *sel, u8 dir, u8 type) { struct xfrm_policy *pol, *ret = NULL; @@ -2897,7 +2897,7 @@ static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate) return 0; } -int xfrm_migrate(struct xfrm_selector *sel, u8 dir, u8 type, +int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_migrate, struct xfrm_kmaddress *k) { -- cgit v1.2.3 From 63eb23f5d80d7158fa575aaca240cb8497e2c06f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 24 Feb 2011 01:25:19 -0500 Subject: xfrm: Const'ify policy arg to xp_net. Signed-off-by: David S. Miller --- include/net/xfrm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 1806c918a15a..5402a1e2c0de 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -506,7 +506,7 @@ struct xfrm_policy { struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH]; }; -static inline struct net *xp_net(struct xfrm_policy *xp) +static inline struct net *xp_net(const struct xfrm_policy *xp) { return read_pnet(&xp->xp_net); } -- cgit v1.2.3 From 21eddb5c1e972727fadec57d8c340dcf814d7902 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 24 Feb 2011 01:35:16 -0500 Subject: xfrm: Const'ify xfrm_tmpl and xfrm_state args to xfrm_state_addr_cmp. Signed-off-by: David S. Miller --- include/net/xfrm.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 5402a1e2c0de..f6d2f635c81d 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -970,21 +970,21 @@ xfrm_addr_any(const xfrm_address_t *addr, unsigned short family) } static inline int -__xfrm4_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x) +__xfrm4_state_addr_cmp(const struct xfrm_tmpl *tmpl, const struct xfrm_state *x) { return (tmpl->saddr.a4 && tmpl->saddr.a4 != x->props.saddr.a4); } static inline int -__xfrm6_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x) +__xfrm6_state_addr_cmp(const struct xfrm_tmpl *tmpl, const struct xfrm_state *x) { return (!ipv6_addr_any((struct in6_addr*)&tmpl->saddr) && ipv6_addr_cmp((struct in6_addr *)&tmpl->saddr, (struct in6_addr*)&x->props.saddr)); } static inline int -xfrm_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x, unsigned short family) +xfrm_state_addr_cmp(const struct xfrm_tmpl *tmpl, const struct xfrm_state *x, unsigned short family) { switch (family) { case AF_INET: -- cgit v1.2.3 From f8848067caff97ce03ee9beef8b6dd5c70f7e736 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 24 Feb 2011 01:42:28 -0500 Subject: xfrm: Const'ify ptr args to xfrm_state_*_check and xfrm_state_kern. Signed-off-by: David S. Miller --- include/net/xfrm.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index f6d2f635c81d..3205e5e4379f 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1178,8 +1178,8 @@ void xfrm_flowi_addr_get(const struct flowi *fl, } static __inline__ int -__xfrm4_state_addr_check(struct xfrm_state *x, - xfrm_address_t *daddr, xfrm_address_t *saddr) +__xfrm4_state_addr_check(const struct xfrm_state *x, + const xfrm_address_t *daddr, const xfrm_address_t *saddr) { if (daddr->a4 == x->id.daddr.a4 && (saddr->a4 == x->props.saddr.a4 || !saddr->a4 || !x->props.saddr.a4)) @@ -1188,8 +1188,8 @@ __xfrm4_state_addr_check(struct xfrm_state *x, } static __inline__ int -__xfrm6_state_addr_check(struct xfrm_state *x, - xfrm_address_t *daddr, xfrm_address_t *saddr) +__xfrm6_state_addr_check(const struct xfrm_state *x, + const xfrm_address_t *daddr, const xfrm_address_t *saddr) { if (!ipv6_addr_cmp((struct in6_addr *)daddr, (struct in6_addr *)&x->id.daddr) && (!ipv6_addr_cmp((struct in6_addr *)saddr, (struct in6_addr *)&x->props.saddr)|| @@ -1200,8 +1200,8 @@ __xfrm6_state_addr_check(struct xfrm_state *x, } static __inline__ int -xfrm_state_addr_check(struct xfrm_state *x, - xfrm_address_t *daddr, xfrm_address_t *saddr, +xfrm_state_addr_check(const struct xfrm_state *x, + const xfrm_address_t *daddr, const xfrm_address_t *saddr, unsigned short family) { switch (family) { @@ -1214,23 +1214,23 @@ xfrm_state_addr_check(struct xfrm_state *x, } static __inline__ int -xfrm_state_addr_flow_check(struct xfrm_state *x, const struct flowi *fl, +xfrm_state_addr_flow_check(const struct xfrm_state *x, const struct flowi *fl, unsigned short family) { switch (family) { case AF_INET: return __xfrm4_state_addr_check(x, - (xfrm_address_t *)&fl->fl4_dst, - (xfrm_address_t *)&fl->fl4_src); + (const xfrm_address_t *)&fl->fl4_dst, + (const xfrm_address_t *)&fl->fl4_src); case AF_INET6: return __xfrm6_state_addr_check(x, - (xfrm_address_t *)&fl->fl6_dst, - (xfrm_address_t *)&fl->fl6_src); + (const xfrm_address_t *)&fl->fl6_dst, + (const xfrm_address_t *)&fl->fl6_src); } return 0; } -static inline int xfrm_state_kern(struct xfrm_state *x) +static inline int xfrm_state_kern(const struct xfrm_state *x) { return atomic_read(&x->tunnel_users); } -- cgit v1.2.3 From 33765d06033cc4ba4d9ae6d3d606ef3f28773c1b Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 24 Feb 2011 01:55:45 -0500 Subject: xfrm: Const'ify xfrm_address_t args to xfrm_state_find. This required a const'ification in xfrm_init_tempstate() too. Signed-off-by: David S. Miller --- include/net/xfrm.h | 4 ++-- net/xfrm/xfrm_state.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 3205e5e4379f..44dccfcf9204 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1334,8 +1334,8 @@ extern int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk, int (*func)(struct xfrm_state *, int, void*), void *); extern void xfrm_state_walk_done(struct xfrm_state_walk *walk); extern struct xfrm_state *xfrm_state_alloc(struct net *net); -extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, - xfrm_address_t *saddr, +extern struct xfrm_state *xfrm_state_find(const xfrm_address_t *daddr, + const xfrm_address_t *saddr, const struct flowi *fl, struct xfrm_tmpl *tmpl, struct xfrm_policy *pol, int *err, diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 9d9ac7ca3dd4..8496b3d3e85b 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -659,7 +659,7 @@ EXPORT_SYMBOL(xfrm_sad_getinfo); static int xfrm_init_tempstate(struct xfrm_state *x, const struct flowi *fl, const struct xfrm_tmpl *tmpl, - xfrm_address_t *daddr, xfrm_address_t *saddr, + const xfrm_address_t *daddr, const xfrm_address_t *saddr, unsigned short family) { struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); @@ -790,7 +790,7 @@ static void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x, } struct xfrm_state * -xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, +xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, const struct flowi *fl, struct xfrm_tmpl *tmpl, struct xfrm_policy *pol, int *err, unsigned short family) -- cgit v1.2.3 From 8bc1f91e1f0e977fb95b11d8fa686f5091888110 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 24 Feb 2011 14:43:06 +0100 Subject: bootmem: Move __alloc_memory_core_early() to nobootmem.c Now that bootmem.c and nobootmem.c are separate, there's no reason to define __alloc_memory_core_early(), which is used only by nobootmem, inside #ifdef in page_alloc.c. Move it to nobootmem.c and make it static. This patch doesn't introduce any behavior change. -tj: Updated commit description. Signed-off-by: Yinghai Lu Acked-by: Andrew Morton Signed-off-by: Tejun Heo --- include/linux/mm.h | 2 -- mm/nobootmem.c | 25 +++++++++++++++++++++++++ mm/page_alloc.c | 28 ---------------------------- 3 files changed, 25 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/linux/mm.h b/include/linux/mm.h index f6385fc17ad4..679300c050f5 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1309,8 +1309,6 @@ int add_from_early_node_map(struct range *range, int az, int nr_range, int nid); u64 __init find_memory_core_early(int nid, u64 size, u64 align, u64 goal, u64 limit); -void *__alloc_memory_core_early(int nodeid, u64 size, u64 align, - u64 goal, u64 limit); typedef int (*work_fn_t)(unsigned long, unsigned long, void *); extern void work_with_active_regions(int nid, work_fn_t work_fn, void *data); extern void sparse_memory_present_with_active_regions(int nid); diff --git a/mm/nobootmem.c b/mm/nobootmem.c index 6a018e49b7be..e2bdb07079ce 100644 --- a/mm/nobootmem.c +++ b/mm/nobootmem.c @@ -40,6 +40,31 @@ unsigned long max_pfn; unsigned long saved_max_pfn; #endif +static void * __init __alloc_memory_core_early(int nid, u64 size, u64 align, + u64 goal, u64 limit) +{ + void *ptr; + u64 addr; + + if (limit > memblock.current_limit) + limit = memblock.current_limit; + + addr = find_memory_core_early(nid, size, align, goal, limit); + + if (addr == MEMBLOCK_ERROR) + return NULL; + + ptr = phys_to_virt(addr); + memset(ptr, 0, size); + memblock_x86_reserve_range(addr, addr + size, "BOOTMEM"); + /* + * The min_count is set to 0 so that bootmem allocated blocks + * are never reported as leaks. + */ + kmemleak_alloc(ptr, size, 0, 0); + return ptr; +} + /* * free_bootmem_late - free bootmem pages directly to page allocator * @addr: starting address of the range diff --git a/mm/page_alloc.c b/mm/page_alloc.c index a243a7fd6922..603513609ba5 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3780,34 +3780,6 @@ int __init add_from_early_node_map(struct range *range, int az, return nr_range; } -#ifdef CONFIG_NO_BOOTMEM -void * __init __alloc_memory_core_early(int nid, u64 size, u64 align, - u64 goal, u64 limit) -{ - void *ptr; - u64 addr; - - if (limit > memblock.current_limit) - limit = memblock.current_limit; - - addr = find_memory_core_early(nid, size, align, goal, limit); - - if (addr == MEMBLOCK_ERROR) - return NULL; - - ptr = phys_to_virt(addr); - memset(ptr, 0, size); - memblock_x86_reserve_range(addr, addr + size, "BOOTMEM"); - /* - * The min_count is set to 0 so that bootmem allocated blocks - * are never reported as leaks. - */ - kmemleak_alloc(ptr, size, 0, 0); - return ptr; -} -#endif - - void __init work_with_active_regions(int nid, work_fn_t work_fn, void *data) { int i; -- cgit v1.2.3 From 8a4a0f3ad071e258a9637c5491c34005a9a97903 Mon Sep 17 00:00:00 2001 From: Eddie Wai Date: Wed, 16 Feb 2011 15:04:28 -0600 Subject: [SCSI] bnx2i: Added support for the 57712(E) devices Moved all PCI_DEVICE_ID_NX2_57712(E) definitions to pci_ids.h Signed-off-by: Eddie Wai Acked-by: Michael Chan Acked-by: Eilon Greenstein Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/net/bnx2x/bnx2x_main.c | 7 ------- drivers/scsi/bnx2i/bnx2i_init.c | 10 ++++++---- include/linux/pci_ids.h | 2 ++ 3 files changed, 8 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/net/bnx2x/bnx2x_main.c b/drivers/net/bnx2x/bnx2x_main.c index 8cdcf5b39d1e..9791dc53375d 100644 --- a/drivers/net/bnx2x/bnx2x_main.c +++ b/drivers/net/bnx2x/bnx2x_main.c @@ -145,13 +145,6 @@ static struct { { "Broadcom NetXtreme II BCM57712E XGb" } }; -#ifndef PCI_DEVICE_ID_NX2_57712 -#define PCI_DEVICE_ID_NX2_57712 0x1662 -#endif -#ifndef PCI_DEVICE_ID_NX2_57712E -#define PCI_DEVICE_ID_NX2_57712E 0x1663 -#endif - static DEFINE_PCI_DEVICE_TABLE(bnx2x_pci_tbl) = { { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57710), BCM57710 }, { PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57711), BCM57711 }, diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c index 6afaa34222af..5ef01c98f48e 100644 --- a/drivers/scsi/bnx2i/bnx2i_init.c +++ b/drivers/scsi/bnx2i/bnx2i_init.c @@ -29,7 +29,7 @@ static char version[] __devinitdata = MODULE_AUTHOR("Anil Veerabhadrappa and " "Eddie Wai "); -MODULE_DESCRIPTION("Broadcom NetXtreme II BCM5706/5708/5709/57710/57711" +MODULE_DESCRIPTION("Broadcom NetXtreme II BCM5706/5708/5709/57710/57711/57712" " iSCSI Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); @@ -88,9 +88,11 @@ void bnx2i_identify_device(struct bnx2i_hba *hba) (hba->pci_did == PCI_DEVICE_ID_NX2_5709S)) { set_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type); hba->mail_queue_access = BNX2I_MQ_BIN_MODE; - } else if (hba->pci_did == PCI_DEVICE_ID_NX2_57710 || - hba->pci_did == PCI_DEVICE_ID_NX2_57711 || - hba->pci_did == PCI_DEVICE_ID_NX2_57711E) + } else if (hba->pci_did == PCI_DEVICE_ID_NX2_57710 || + hba->pci_did == PCI_DEVICE_ID_NX2_57711 || + hba->pci_did == PCI_DEVICE_ID_NX2_57711E || + hba->pci_did == PCI_DEVICE_ID_NX2_57712 || + hba->pci_did == PCI_DEVICE_ID_NX2_57712E) set_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type); else printk(KERN_ALERT "bnx2i: unknown device, 0x%x\n", diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 3adb06ebf841..f82986b4f44a 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2078,6 +2078,8 @@ #define PCI_DEVICE_ID_TIGON3_5723 0x165b #define PCI_DEVICE_ID_TIGON3_5705M 0x165d #define PCI_DEVICE_ID_TIGON3_5705M_2 0x165e +#define PCI_DEVICE_ID_NX2_57712 0x1662 +#define PCI_DEVICE_ID_NX2_57712E 0x1663 #define PCI_DEVICE_ID_TIGON3_5714 0x1668 #define PCI_DEVICE_ID_TIGON3_5714S 0x1669 #define PCI_DEVICE_ID_TIGON3_5780 0x166a -- cgit v1.2.3 From 22a39fbbfecfea703b686a4626a631d706ccb3ee Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Wed, 16 Feb 2011 15:04:33 -0600 Subject: [SCSI] iscsi: fix iscsi_endpoint leak When iscsid restarts it does not know the connection's endpoint, so it is getting leaked. This fixes the problem by having the iscsi class force a disconnect before a new connection is bound. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_iscsi.c | 63 +++++++++++++++++++++++++++++-------- include/scsi/scsi_transport_iscsi.h | 3 ++ 2 files changed, 53 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index f905ecb5704d..4e09b68a0789 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -954,6 +954,7 @@ iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid) if (dd_size) conn->dd_data = &conn[1]; + mutex_init(&conn->ep_mutex); INIT_LIST_HEAD(&conn->conn_list); conn->transport = transport; conn->cid = cid; @@ -1430,6 +1431,29 @@ release_host: return err; } +static int iscsi_if_ep_disconnect(struct iscsi_transport *transport, + u64 ep_handle) +{ + struct iscsi_cls_conn *conn; + struct iscsi_endpoint *ep; + + if (!transport->ep_disconnect) + return -EINVAL; + + ep = iscsi_lookup_endpoint(ep_handle); + if (!ep) + return -EINVAL; + conn = ep->conn; + if (conn) { + mutex_lock(&conn->ep_mutex); + conn->ep = NULL; + mutex_unlock(&conn->ep_mutex); + } + + transport->ep_disconnect(ep); + return 0; +} + static int iscsi_if_transport_ep(struct iscsi_transport *transport, struct iscsi_uevent *ev, int msg_type) @@ -1454,14 +1478,8 @@ iscsi_if_transport_ep(struct iscsi_transport *transport, ev->u.ep_poll.timeout_ms); break; case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT: - if (!transport->ep_disconnect) - return -EINVAL; - - ep = iscsi_lookup_endpoint(ev->u.ep_disconnect.ep_handle); - if (!ep) - return -EINVAL; - - transport->ep_disconnect(ep); + rc = iscsi_if_ep_disconnect(transport, + ev->u.ep_disconnect.ep_handle); break; } return rc; @@ -1609,12 +1627,31 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) session = iscsi_session_lookup(ev->u.b_conn.sid); conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid); - if (session && conn) - ev->r.retcode = transport->bind_conn(session, conn, - ev->u.b_conn.transport_eph, - ev->u.b_conn.is_leading); - else + if (conn && conn->ep) + iscsi_if_ep_disconnect(transport, conn->ep->id); + + if (!session || !conn) { err = -EINVAL; + break; + } + + ev->r.retcode = transport->bind_conn(session, conn, + ev->u.b_conn.transport_eph, + ev->u.b_conn.is_leading); + if (ev->r.retcode || !transport->ep_connect) + break; + + ep = iscsi_lookup_endpoint(ev->u.b_conn.transport_eph); + if (ep) { + ep->conn = conn; + + mutex_lock(&conn->ep_mutex); + conn->ep = ep; + mutex_unlock(&conn->ep_mutex); + } else + iscsi_cls_conn_printk(KERN_ERR, conn, + "Could not set ep conn " + "binding\n"); break; case ISCSI_UEVENT_SET_PARAM: err = iscsi_set_param(transport, ev); diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index 7fff94b3b2a8..b9ba349ef257 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -160,6 +160,8 @@ struct iscsi_cls_conn { void *dd_data; /* LLD private data */ struct iscsi_transport *transport; uint32_t cid; /* connection id */ + struct mutex ep_mutex; + struct iscsi_endpoint *ep; int active; /* must be accessed with the connlock */ struct device dev; /* sysfs transport/container device */ @@ -222,6 +224,7 @@ struct iscsi_endpoint { void *dd_data; /* LLD private data */ struct device dev; uint64_t id; + struct iscsi_cls_conn *conn; }; /* -- cgit v1.2.3 From bbc5261b2cb5e69754c935ea2466fb22775f0e48 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Wed, 16 Feb 2011 15:04:34 -0600 Subject: [SCSI] iscsi class: remove unused active variable The active variable on the iscsi_cls_conn is not used so this patch removes it. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_iscsi.c | 2 -- include/scsi/scsi_transport_iscsi.h | 1 - 2 files changed, 3 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 4e09b68a0789..a631e58894f1 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -976,7 +976,6 @@ iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid) spin_lock_irqsave(&connlock, flags); list_add(&conn->conn_list, &connlist); - conn->active = 1; spin_unlock_irqrestore(&connlock, flags); ISCSI_DBG_TRANS_CONN(conn, "Completed conn creation\n"); @@ -1002,7 +1001,6 @@ int iscsi_destroy_conn(struct iscsi_cls_conn *conn) unsigned long flags; spin_lock_irqsave(&connlock, flags); - conn->active = 0; list_del(&conn->conn_list); spin_unlock_irqrestore(&connlock, flags); diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index b9ba349ef257..00e5bf7c9de6 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -163,7 +163,6 @@ struct iscsi_cls_conn { struct mutex ep_mutex; struct iscsi_endpoint *ep; - int active; /* must be accessed with the connlock */ struct device dev; /* sysfs transport/container device */ }; -- cgit v1.2.3 From 00f3708e6ed1698d6aee3901ea991197e31a8007 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Wed, 16 Feb 2011 15:04:35 -0600 Subject: [SCSI] libiscsi: add helper to convert addr to string This adds a helper to convert a addr struct to a string. This will be used by the drivers in the next patches. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/libiscsi.c | 44 +++++++++++++++++++++++++++++++++++++++++--- include/scsi/libiscsi.h | 2 ++ 2 files changed, 43 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index da8b61543ee4..0c550d5b9133 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -3352,6 +3352,47 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session, } EXPORT_SYMBOL_GPL(iscsi_session_get_param); +int iscsi_conn_get_addr_param(struct sockaddr_storage *addr, + enum iscsi_param param, char *buf) +{ + struct sockaddr_in6 *sin6 = NULL; + struct sockaddr_in *sin = NULL; + int len; + + switch (addr->ss_family) { + case AF_INET: + sin = (struct sockaddr_in *)addr; + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)addr; + break; + default: + return -EINVAL; + } + + switch (param) { + case ISCSI_PARAM_CONN_ADDRESS: + case ISCSI_HOST_PARAM_IPADDRESS: + if (sin) + len = sprintf(buf, "%pI4\n", &sin->sin_addr.s_addr); + else + len = sprintf(buf, "%pI6\n", &sin6->sin6_addr); + break; + case ISCSI_PARAM_CONN_PORT: + if (sin) + len = sprintf(buf, "%hu\n", be16_to_cpu(sin->sin_port)); + else + len = sprintf(buf, "%hu\n", + be16_to_cpu(sin6->sin6_port)); + break; + default: + return -EINVAL; + } + + return len; +} +EXPORT_SYMBOL_GPL(iscsi_conn_get_addr_param); + int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, char *buf) { @@ -3416,9 +3457,6 @@ int iscsi_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param, case ISCSI_HOST_PARAM_INITIATOR_NAME: len = sprintf(buf, "%s\n", ihost->initiatorname); break; - case ISCSI_HOST_PARAM_IPADDRESS: - len = sprintf(buf, "%s\n", ihost->local_address); - break; default: return -ENOSYS; } diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index 748382b32b52..4bef19fb3a65 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -394,6 +394,8 @@ extern void iscsi_session_failure(struct iscsi_session *session, enum iscsi_err err); extern int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, char *buf); +extern int iscsi_conn_get_addr_param(struct sockaddr_storage *addr, + enum iscsi_param param, char *buf); extern void iscsi_suspend_tx(struct iscsi_conn *conn); extern void iscsi_suspend_queue(struct iscsi_conn *conn); extern void iscsi_conn_queue_work(struct iscsi_conn *conn); -- cgit v1.2.3 From 289324b0c6007171d67bf1ab0827355ae3374773 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Wed, 16 Feb 2011 15:04:37 -0600 Subject: [SCSI] iscsi class: add callout to get iscsi_endpoint values For drivers using the ep callbacks the addr and port are attached to the endpoint instead of the conn. This adds a callout to the iscsi_transport to get ep values. It also adds locking around the get param call to make sure that ep_disconnect does not free the LLD's ep interconnect structs from under us (the ep has a refcount so it will not go away but the LLD may have structs from other subsystems that are not allocated in the ep so we need to protect them from getting freed). Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_iscsi.c | 39 +++++++++++++++++++++++++++++++++++-- include/scsi/scsi_transport_iscsi.h | 2 ++ 2 files changed, 39 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index a631e58894f1..b4218390941e 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -1782,13 +1782,48 @@ iscsi_conn_attr(data_digest, ISCSI_PARAM_DATADGST_EN); iscsi_conn_attr(ifmarker, ISCSI_PARAM_IFMARKER_EN); iscsi_conn_attr(ofmarker, ISCSI_PARAM_OFMARKER_EN); iscsi_conn_attr(persistent_port, ISCSI_PARAM_PERSISTENT_PORT); -iscsi_conn_attr(port, ISCSI_PARAM_CONN_PORT); iscsi_conn_attr(exp_statsn, ISCSI_PARAM_EXP_STATSN); iscsi_conn_attr(persistent_address, ISCSI_PARAM_PERSISTENT_ADDRESS); -iscsi_conn_attr(address, ISCSI_PARAM_CONN_ADDRESS); iscsi_conn_attr(ping_tmo, ISCSI_PARAM_PING_TMO); iscsi_conn_attr(recv_tmo, ISCSI_PARAM_RECV_TMO); +#define iscsi_conn_ep_attr_show(param) \ +static ssize_t show_conn_ep_param_##param(struct device *dev, \ + struct device_attribute *attr,\ + char *buf) \ +{ \ + struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent); \ + struct iscsi_transport *t = conn->transport; \ + struct iscsi_endpoint *ep; \ + ssize_t rc; \ + \ + /* \ + * Need to make sure ep_disconnect does not free the LLD's \ + * interconnect resources while we are trying to read them. \ + */ \ + mutex_lock(&conn->ep_mutex); \ + ep = conn->ep; \ + if (!ep && t->ep_connect) { \ + mutex_unlock(&conn->ep_mutex); \ + return -ENOTCONN; \ + } \ + \ + if (ep) \ + rc = t->get_ep_param(ep, param, buf); \ + else \ + rc = t->get_conn_param(conn, param, buf); \ + mutex_unlock(&conn->ep_mutex); \ + return rc; \ +} + +#define iscsi_conn_ep_attr(field, param) \ + iscsi_conn_ep_attr_show(param) \ +static ISCSI_CLASS_ATTR(conn, field, S_IRUGO, \ + show_conn_ep_param_##param, NULL); + +iscsi_conn_ep_attr(address, ISCSI_PARAM_CONN_ADDRESS); +iscsi_conn_ep_attr(port, ISCSI_PARAM_CONN_PORT); + /* * iSCSI session attrs */ diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index 00e5bf7c9de6..bf8f52965675 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -101,6 +101,8 @@ struct iscsi_transport { void (*destroy_conn) (struct iscsi_cls_conn *conn); int (*set_param) (struct iscsi_cls_conn *conn, enum iscsi_param param, char *buf, int buflen); + int (*get_ep_param) (struct iscsi_endpoint *ep, enum iscsi_param param, + char *buf); int (*get_conn_param) (struct iscsi_cls_conn *conn, enum iscsi_param param, char *buf); int (*get_session_param) (struct iscsi_cls_session *session, -- cgit v1.2.3 From c71b9b669e1243623f7ed4332877d3f2beafc6ab Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Wed, 16 Feb 2011 15:04:38 -0600 Subject: [SCSI] cxgbi: convert to use iscsi_conn_get_addr_param This has cxgbi use the iscsi_conn_get_addr_param helper and the get ep callback. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/cxgbi/cxgb3i/cxgb3i.c | 3 ++- drivers/scsi/cxgbi/cxgb4i/cxgb4i.c | 3 ++- drivers/scsi/cxgbi/libcxgbi.c | 36 +++++++++++++++++------------------- drivers/scsi/cxgbi/libcxgbi.h | 2 +- include/scsi/libiscsi.h | 6 ------ 5 files changed, 22 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c index 69a6769c633e..fc2cdb62f53b 100644 --- a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c +++ b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c @@ -137,7 +137,7 @@ static struct iscsi_transport cxgb3i_iscsi_transport = { .destroy_conn = iscsi_tcp_conn_teardown, .start_conn = iscsi_conn_start, .stop_conn = iscsi_conn_stop, - .get_conn_param = cxgbi_get_conn_param, + .get_conn_param = iscsi_conn_get_param, .set_param = cxgbi_set_conn_param, .get_stats = cxgbi_get_conn_stats, /* pdu xmit req from user space */ @@ -152,6 +152,7 @@ static struct iscsi_transport cxgb3i_iscsi_transport = { .xmit_pdu = cxgbi_conn_xmit_pdu, .parse_pdu_itt = cxgbi_parse_pdu_itt, /* TCP connect/disconnect */ + .get_ep_param = cxgbi_get_ep_param, .ep_connect = cxgbi_ep_connect, .ep_poll = cxgbi_ep_poll, .ep_disconnect = cxgbi_ep_disconnect, diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c index 719aa71f5b10..f3a4cd7cf782 100644 --- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c +++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c @@ -138,7 +138,7 @@ static struct iscsi_transport cxgb4i_iscsi_transport = { .destroy_conn = iscsi_tcp_conn_teardown, .start_conn = iscsi_conn_start, .stop_conn = iscsi_conn_stop, - .get_conn_param = cxgbi_get_conn_param, + .get_conn_param = iscsi_conn_get_param, .set_param = cxgbi_set_conn_param, .get_stats = cxgbi_get_conn_stats, /* pdu xmit req from user space */ @@ -153,6 +153,7 @@ static struct iscsi_transport cxgb4i_iscsi_transport = { .xmit_pdu = cxgbi_conn_xmit_pdu, .parse_pdu_itt = cxgbi_parse_pdu_itt, /* TCP connect/disconnect */ + .get_ep_param = cxgbi_get_ep_param, .ep_connect = cxgbi_ep_connect, .ep_poll = cxgbi_ep_poll, .ep_disconnect = cxgbi_ep_disconnect, diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index b2acdef3dcb7..fedf1be278ff 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -543,6 +543,7 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr) csk->dst = dst; csk->daddr.sin_addr.s_addr = daddr->sin_addr.s_addr; csk->daddr.sin_port = daddr->sin_port; + csk->daddr.sin_family = daddr->sin_family; csk->saddr.sin_addr.s_addr = rt->rt_src; return csk; @@ -2200,32 +2201,34 @@ int cxgbi_set_conn_param(struct iscsi_cls_conn *cls_conn, } EXPORT_SYMBOL_GPL(cxgbi_set_conn_param); -int cxgbi_get_conn_param(struct iscsi_cls_conn *cls_conn, - enum iscsi_param param, char *buf) +int cxgbi_get_ep_param(struct iscsi_endpoint *ep, enum iscsi_param param, + char *buf) { - struct iscsi_conn *iconn = cls_conn->dd_data; + struct cxgbi_endpoint *cep = ep->dd_data; + struct cxgbi_sock *csk; int len; log_debug(1 << CXGBI_DBG_ISCSI, - "cls_conn 0x%p, param %d.\n", cls_conn, param); + "cls_conn 0x%p, param %d.\n", ep, param); switch (param) { case ISCSI_PARAM_CONN_PORT: - spin_lock_bh(&iconn->session->lock); - len = sprintf(buf, "%hu\n", iconn->portal_port); - spin_unlock_bh(&iconn->session->lock); - break; case ISCSI_PARAM_CONN_ADDRESS: - spin_lock_bh(&iconn->session->lock); - len = sprintf(buf, "%s\n", iconn->portal_address); - spin_unlock_bh(&iconn->session->lock); - break; + if (!cep) + return -ENOTCONN; + + csk = cep->csk; + if (!csk) + return -ENOTCONN; + + return iscsi_conn_get_addr_param((struct sockaddr_storage *) + &csk->daddr, param, buf); default: - return iscsi_conn_get_param(cls_conn, param, buf); + return -ENOSYS; } return len; } -EXPORT_SYMBOL_GPL(cxgbi_get_conn_param); +EXPORT_SYMBOL_GPL(cxgbi_get_ep_param); struct iscsi_cls_conn * cxgbi_create_conn(struct iscsi_cls_session *cls_session, u32 cid) @@ -2292,11 +2295,6 @@ int cxgbi_bind_conn(struct iscsi_cls_session *cls_session, cxgbi_conn_max_xmit_dlength(conn); cxgbi_conn_max_recv_dlength(conn); - spin_lock_bh(&conn->session->lock); - sprintf(conn->portal_address, "%pI4", &csk->daddr.sin_addr.s_addr); - conn->portal_port = ntohs(csk->daddr.sin_port); - spin_unlock_bh(&conn->session->lock); - log_debug(1 << CXGBI_DBG_ISCSI, "cls 0x%p,0x%p, ep 0x%p, cconn 0x%p, csk 0x%p.\n", cls_session, cls_conn, ep, cconn, csk); diff --git a/drivers/scsi/cxgbi/libcxgbi.h b/drivers/scsi/cxgbi/libcxgbi.h index 23cbc5854503..0a20fd5f7102 100644 --- a/drivers/scsi/cxgbi/libcxgbi.h +++ b/drivers/scsi/cxgbi/libcxgbi.h @@ -712,7 +712,7 @@ void cxgbi_cleanup_task(struct iscsi_task *task); void cxgbi_get_conn_stats(struct iscsi_cls_conn *, struct iscsi_stats *); int cxgbi_set_conn_param(struct iscsi_cls_conn *, enum iscsi_param, char *, int); -int cxgbi_get_conn_param(struct iscsi_cls_conn *, enum iscsi_param, char *); +int cxgbi_get_ep_param(struct iscsi_endpoint *ep, enum iscsi_param, char *); struct iscsi_cls_conn *cxgbi_create_conn(struct iscsi_cls_session *, u32); int cxgbi_bind_conn(struct iscsi_cls_session *, struct iscsi_cls_conn *, u64, int); diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index 4bef19fb3a65..0f4367751b71 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -212,9 +212,6 @@ struct iscsi_conn { /* values userspace uses to id a conn */ int persistent_port; char *persistent_address; - /* remote portal currently connected to */ - int portal_port; - char portal_address[ISCSI_ADDRESS_BUF_LEN]; /* MIB-statistics */ uint64_t txdata_octets; @@ -319,9 +316,6 @@ struct iscsi_host { /* hw address or netdev iscsi connection is bound to */ char *hwaddress; char *netdev; - /* local address */ - int local_port; - char local_address[ISCSI_ADDRESS_BUF_LEN]; wait_queue_head_t session_removal_wq; /* protects sessions and state */ -- cgit v1.2.3 From 073b4964b3b75fd9e19bf3933b26d9c23591c9db Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Feb 2011 23:37:31 +0100 Subject: ACPI: Do not export functions that are only used in osl.c The functions acpi_os_map_generic_address() and acpi_os_unmap_generic_address() are only used in drivers/acpi/osl.c, so make them static and remove the extern definitions of them from include/linux/acpi_io.h. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 6 ++---- include/linux/acpi_io.h | 3 --- 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index c90c76aa7f8b..ff2189d3fa06 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -397,7 +397,7 @@ void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size) __acpi_unmap_table(virt, size); } -int acpi_os_map_generic_address(struct acpi_generic_address *addr) +static int acpi_os_map_generic_address(struct acpi_generic_address *addr) { void __iomem *virt; @@ -413,9 +413,8 @@ int acpi_os_map_generic_address(struct acpi_generic_address *addr) return 0; } -EXPORT_SYMBOL_GPL(acpi_os_map_generic_address); -void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) +static void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) { void __iomem *virt; unsigned long flags; @@ -433,7 +432,6 @@ void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) acpi_os_unmap_memory(virt, size); } -EXPORT_SYMBOL_GPL(acpi_os_unmap_generic_address); #ifdef ACPI_FUTURE_USAGE acpi_status diff --git a/include/linux/acpi_io.h b/include/linux/acpi_io.h index 7180013a4a3a..28a3ae279d7a 100644 --- a/include/linux/acpi_io.h +++ b/include/linux/acpi_io.h @@ -10,7 +10,4 @@ static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys, return ioremap_cache(phys, size); } -int acpi_os_map_generic_address(struct acpi_generic_address *addr); -void acpi_os_unmap_generic_address(struct acpi_generic_address *addr); - #endif -- cgit v1.2.3 From 13606a2de1996f8d83a9ce296f74022bdbadf712 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Feb 2011 23:38:25 +0100 Subject: ACPI: Introduce acpi_os_get_iomem() Introduce function acpi_os_get_iomem() that may be used by its callers to get a reference to an ACPI iomap. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 16 ++++++++++++++++ include/linux/acpi_io.h | 2 ++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 5389f9f2e2ff..52ca8721bc9c 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -285,6 +285,22 @@ acpi_map_vaddr_lookup(acpi_physical_address phys, unsigned int size) return NULL; } +void __iomem *acpi_os_get_iomem(acpi_physical_address phys, unsigned int size) +{ + struct acpi_ioremap *map; + void __iomem *virt = NULL; + + mutex_lock(&acpi_ioremap_lock); + map = acpi_map_lookup(phys, size); + if (map) { + virt = map->virt + (phys - map->phys); + map->refcount++; + } + mutex_unlock(&acpi_ioremap_lock); + return virt; +} +EXPORT_SYMBOL_GPL(acpi_os_get_iomem); + /* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */ static struct acpi_ioremap * acpi_map_lookup_virt(void __iomem *virt, acpi_size size) diff --git a/include/linux/acpi_io.h b/include/linux/acpi_io.h index 28a3ae279d7a..4afd7102459d 100644 --- a/include/linux/acpi_io.h +++ b/include/linux/acpi_io.h @@ -10,4 +10,6 @@ static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys, return ioremap_cache(phys, size); } +void __iomem *acpi_os_get_iomem(acpi_physical_address phys, unsigned int size); + #endif -- cgit v1.2.3 From 5190726765b40774c069e187a958e10ccd970e65 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Feb 2011 23:40:37 +0100 Subject: ACPI: Remove the wakeup.run_wake_count device field The wakeup.run_wake_count ACPI device field is only used by the PCI runtime PM code to "protect" devices from being prepared for generating wakeup signals more than once in a row. However, it really doesn't provide any protection, because (1) all of the functions it is supposed to protect use their own reference counters effectively ensuring that the device will be set up for generating wakeup signals just once and (2) the PCI runtime PM code uses wakeup.run_wake_count in a racy way, since nothing prevents acpi_dev_run_wake() from being called concurrently from two different threads for the same device. Remove the wakeup.run_wake_count ACPI device field which is unnecessary, confusing and used in a wrong way. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/button.c | 2 -- drivers/acpi/scan.c | 1 - drivers/pci/pci-acpi.c | 16 ++++------------ include/acpi/acpi_bus.h | 1 - 4 files changed, 4 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 76bbb78a5ad9..e643a0936dc4 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -430,7 +430,6 @@ static int acpi_button_add(struct acpi_device *device) /* Button's GPE is run-wake GPE */ acpi_enable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number); - device->wakeup.run_wake_count++; device_set_wakeup_enable(&device->dev, true); } @@ -453,7 +452,6 @@ static int acpi_button_remove(struct acpi_device *device, int type) if (device->wakeup.flags.valid) { acpi_disable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number); - device->wakeup.run_wake_count--; device_set_wakeup_enable(&device->dev, false); } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index b99e62494607..b136c9c1e531 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -797,7 +797,6 @@ static void acpi_bus_set_run_wake_flags(struct acpi_device *device) acpi_status status; acpi_event_status event_status; - device->wakeup.run_wake_count = 0; device->wakeup.flags.notifier_present = 0; /* Power button, Lid switch always enable wakeup */ diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 6fe0772e0e7d..7c3b18e78cee 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -293,19 +293,11 @@ static int acpi_dev_run_wake(struct device *phys_dev, bool enable) } if (enable) { - if (!dev->wakeup.run_wake_count++) { - acpi_enable_wakeup_device_power(dev, ACPI_STATE_S0); - acpi_enable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number); - } - } else if (dev->wakeup.run_wake_count > 0) { - if (!--dev->wakeup.run_wake_count) { - acpi_disable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number); - acpi_disable_wakeup_device_power(dev); - } + acpi_enable_wakeup_device_power(dev, ACPI_STATE_S0); + acpi_enable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number); } else { - error = -EALREADY; + acpi_disable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number); + acpi_disable_wakeup_device_power(dev); } return error; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 78ca429929f7..f50ebb9bc53b 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -250,7 +250,6 @@ struct acpi_device_wakeup { struct acpi_handle_list resources; struct acpi_device_wakeup_flags flags; int prepare_count; - int run_wake_count; }; /* Device */ -- cgit v1.2.3 From 8f7286f8e4e80f7b868ba3d117ae900f0d207cbe Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 14 Feb 2011 09:57:35 +1000 Subject: drm/nv50: support for compression Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + drivers/gpu/drm/nouveau/nouveau_mem.c | 2 +- drivers/gpu/drm/nouveau/nouveau_vm.c | 5 +++- drivers/gpu/drm/nouveau/nouveau_vm.h | 7 ++--- drivers/gpu/drm/nouveau/nv50_fb.c | 51 ++++++++++++++++++++++++----------- drivers/gpu/drm/nouveau/nv50_vm.c | 8 +++++- drivers/gpu/drm/nouveau/nv50_vram.c | 27 ++++++++++++++++--- drivers/gpu/drm/nouveau/nvc0_vm.c | 2 +- drivers/gpu/drm/nouveau/nvc0_vram.c | 2 +- include/drm/nouveau_drm.h | 1 + 10 files changed, 79 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 4bdc726a072b..00aff226397d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -72,6 +72,7 @@ struct nouveau_mem { struct nouveau_vma tmp_vma; u8 page_shift; + struct drm_mm_node *tag; struct list_head regions; dma_addr_t *pages; u32 memtype; diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 73f37bd0adfa..63b9040b5f30 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -740,7 +740,7 @@ nouveau_vram_manager_new(struct ttm_mem_type_manager *man, ret = vram->get(dev, mem->num_pages << PAGE_SHIFT, mem->page_alignment << PAGE_SHIFT, size_nc, - (nvbo->tile_flags >> 8) & 0xff, &node); + (nvbo->tile_flags >> 8) & 0x3ff, &node); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.c b/drivers/gpu/drm/nouveau/nouveau_vm.c index 3d9c1595eaa8..62824c80bcb8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vm.c +++ b/drivers/gpu/drm/nouveau/nouveau_vm.c @@ -40,6 +40,7 @@ nouveau_vm_map_at(struct nouveau_vma *vma, u64 delta, struct nouveau_mem *node) u32 max = 1 << (vm->pgt_bits - bits); u32 end, len; + delta = 0; list_for_each_entry(r, &node->regions, rl_entry) { u64 phys = (u64)r->offset << 12; u32 num = r->length >> bits; @@ -52,7 +53,7 @@ nouveau_vm_map_at(struct nouveau_vma *vma, u64 delta, struct nouveau_mem *node) end = max; len = end - pte; - vm->map(vma, pgt, node, pte, len, phys); + vm->map(vma, pgt, node, pte, len, phys, delta); num -= len; pte += len; @@ -60,6 +61,8 @@ nouveau_vm_map_at(struct nouveau_vma *vma, u64 delta, struct nouveau_mem *node) pde++; pte = 0; } + + delta += (u64)len << vma->node->type; } } diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.h b/drivers/gpu/drm/nouveau/nouveau_vm.h index 0e00264b8f49..2e06b55cfdc1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vm.h +++ b/drivers/gpu/drm/nouveau/nouveau_vm.h @@ -67,7 +67,8 @@ struct nouveau_vm { void (*map_pgt)(struct nouveau_gpuobj *pgd, u32 pde, struct nouveau_gpuobj *pgt[2]); void (*map)(struct nouveau_vma *, struct nouveau_gpuobj *, - struct nouveau_mem *, u32 pte, u32 cnt, u64 phys); + struct nouveau_mem *, u32 pte, u32 cnt, + u64 phys, u64 delta); void (*map_sg)(struct nouveau_vma *, struct nouveau_gpuobj *, struct nouveau_mem *, u32 pte, u32 cnt, dma_addr_t *); void (*unmap)(struct nouveau_gpuobj *pgt, u32 pte, u32 cnt); @@ -93,7 +94,7 @@ void nouveau_vm_map_sg(struct nouveau_vma *, u64 offset, u64 length, void nv50_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 pde, struct nouveau_gpuobj *pgt[2]); void nv50_vm_map(struct nouveau_vma *, struct nouveau_gpuobj *, - struct nouveau_mem *, u32 pte, u32 cnt, u64 phys); + struct nouveau_mem *, u32 pte, u32 cnt, u64 phys, u64 delta); void nv50_vm_map_sg(struct nouveau_vma *, struct nouveau_gpuobj *, struct nouveau_mem *, u32 pte, u32 cnt, dma_addr_t *); void nv50_vm_unmap(struct nouveau_gpuobj *, u32 pte, u32 cnt); @@ -104,7 +105,7 @@ void nv50_vm_flush_engine(struct drm_device *, int engine); void nvc0_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 pde, struct nouveau_gpuobj *pgt[2]); void nvc0_vm_map(struct nouveau_vma *, struct nouveau_gpuobj *, - struct nouveau_mem *, u32 pte, u32 cnt, u64 phys); + struct nouveau_mem *, u32 pte, u32 cnt, u64 phys, u64 delta); void nvc0_vm_map_sg(struct nouveau_vma *, struct nouveau_gpuobj *, struct nouveau_mem *, u32 pte, u32 cnt, dma_addr_t *); void nvc0_vm_unmap(struct nouveau_gpuobj *, u32 pte, u32 cnt); diff --git a/drivers/gpu/drm/nouveau/nv50_fb.c b/drivers/gpu/drm/nouveau/nv50_fb.c index 50290dea0ac4..ed411d88451d 100644 --- a/drivers/gpu/drm/nouveau/nv50_fb.c +++ b/drivers/gpu/drm/nouveau/nv50_fb.c @@ -8,31 +8,61 @@ struct nv50_fb_priv { dma_addr_t r100c08; }; +static void +nv50_fb_destroy(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; + struct nv50_fb_priv *priv = pfb->priv; + + if (drm_mm_initialized(&pfb->tag_heap)) + drm_mm_takedown(&pfb->tag_heap); + + if (priv->r100c08_page) { + pci_unmap_page(dev->pdev, priv->r100c08, PAGE_SIZE, + PCI_DMA_BIDIRECTIONAL); + __free_page(priv->r100c08_page); + } + + kfree(priv); + pfb->priv = NULL; +} + static int nv50_fb_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; struct nv50_fb_priv *priv; + u32 tagmem; + int ret; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + pfb->priv = priv; priv->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO); if (!priv->r100c08_page) { - kfree(priv); + nv50_fb_destroy(dev); return -ENOMEM; } priv->r100c08 = pci_map_page(dev->pdev, priv->r100c08_page, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); if (pci_dma_mapping_error(dev->pdev, priv->r100c08)) { - __free_page(priv->r100c08_page); - kfree(priv); + nv50_fb_destroy(dev); return -EFAULT; } - dev_priv->engine.fb.priv = priv; + tagmem = nv_rd32(dev, 0x100320); + NV_DEBUG(dev, "%d tags available\n", tagmem); + ret = drm_mm_init(&pfb->tag_heap, 0, tagmem); + if (ret) { + nv50_fb_destroy(dev); + return ret; + } + return 0; } @@ -81,18 +111,7 @@ nv50_fb_init(struct drm_device *dev) void nv50_fb_takedown(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nv50_fb_priv *priv; - - priv = dev_priv->engine.fb.priv; - if (!priv) - return; - dev_priv->engine.fb.priv = NULL; - - pci_unmap_page(dev->pdev, priv->r100c08, PAGE_SIZE, - PCI_DMA_BIDIRECTIONAL); - __free_page(priv->r100c08_page); - kfree(priv); + nv50_fb_destroy(dev); } void diff --git a/drivers/gpu/drm/nouveau/nv50_vm.c b/drivers/gpu/drm/nouveau/nv50_vm.c index 37f941bf4a0b..b23794c8859b 100644 --- a/drivers/gpu/drm/nouveau/nv50_vm.c +++ b/drivers/gpu/drm/nouveau/nv50_vm.c @@ -83,8 +83,9 @@ nv50_vm_addr(struct nouveau_vma *vma, u64 phys, u32 memtype, u32 target) void nv50_vm_map(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt, - struct nouveau_mem *mem, u32 pte, u32 cnt, u64 phys) + struct nouveau_mem *mem, u32 pte, u32 cnt, u64 phys, u64 delta) { + u32 comp = (mem->memtype & 0x180) >> 7; u32 block; int i; @@ -105,6 +106,11 @@ nv50_vm_map(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt, phys += block << (vma->node->type - 3); cnt -= block; + if (comp) { + u32 tag = mem->tag->start + ((delta >> 16) * comp); + offset_h |= (tag << 17); + delta += block << (vma->node->type - 3); + } while (block) { nv_wo32(pgt, pte + 0, offset_l); diff --git a/drivers/gpu/drm/nouveau/nv50_vram.c b/drivers/gpu/drm/nouveau/nv50_vram.c index ff6cbae40e58..ffbc3d8cf5be 100644 --- a/drivers/gpu/drm/nouveau/nv50_vram.c +++ b/drivers/gpu/drm/nouveau/nv50_vram.c @@ -69,6 +69,11 @@ nv50_vram_del(struct drm_device *dev, struct nouveau_mem **pmem) list_del(&this->rl_entry); nouveau_mm_put(mm, this); } + + if (mem->tag) { + drm_mm_put_block(mem->tag); + mem->tag = NULL; + } mutex_unlock(&mm->mutex); kfree(mem); @@ -76,7 +81,7 @@ nv50_vram_del(struct drm_device *dev, struct nouveau_mem **pmem) int nv50_vram_new(struct drm_device *dev, u64 size, u32 align, u32 size_nc, - u32 type, struct nouveau_mem **pmem) + u32 memtype, struct nouveau_mem **pmem) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct ttm_bo_device *bdev = &dev_priv->ttm.bdev; @@ -84,6 +89,8 @@ nv50_vram_new(struct drm_device *dev, u64 size, u32 align, u32 size_nc, struct nouveau_mm *mm = man->priv; struct nouveau_mm_node *r; struct nouveau_mem *mem; + int comp = (memtype & 0x300) >> 8; + int type = (memtype & 0x07f); int ret; if (!types[type]) @@ -96,12 +103,26 @@ nv50_vram_new(struct drm_device *dev, u64 size, u32 align, u32 size_nc, if (!mem) return -ENOMEM; + mutex_lock(&mm->mutex); + if (comp) { + if (align == 16) { + struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; + int n = (size >> 4) * comp; + + mem->tag = drm_mm_search_free(&pfb->tag_heap, n, 0, 0); + if (mem->tag) + mem->tag = drm_mm_get_block(mem->tag, n, 0); + } + + if (unlikely(!mem->tag)) + comp = 0; + } + INIT_LIST_HEAD(&mem->regions); mem->dev = dev_priv->dev; - mem->memtype = type; + mem->memtype = (comp << 7) | type; mem->size = size; - mutex_lock(&mm->mutex); do { ret = nouveau_mm_get(mm, types[type], size, size_nc, align, &r); if (ret) { diff --git a/drivers/gpu/drm/nouveau/nvc0_vm.c b/drivers/gpu/drm/nouveau/nvc0_vm.c index 2b984b25f27e..69af0ba7edd3 100644 --- a/drivers/gpu/drm/nouveau/nvc0_vm.c +++ b/drivers/gpu/drm/nouveau/nvc0_vm.c @@ -59,7 +59,7 @@ nvc0_vm_addr(struct nouveau_vma *vma, u64 phys, u32 memtype, u32 target) void nvc0_vm_map(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt, - struct nouveau_mem *mem, u32 pte, u32 cnt, u64 phys) + struct nouveau_mem *mem, u32 pte, u32 cnt, u64 phys, u64 delta) { u32 next = 1 << (vma->node->type - 8); diff --git a/drivers/gpu/drm/nouveau/nvc0_vram.c b/drivers/gpu/drm/nouveau/nvc0_vram.c index 6d777a2a04dd..67c6ec6f34ea 100644 --- a/drivers/gpu/drm/nouveau/nvc0_vram.c +++ b/drivers/gpu/drm/nouveau/nvc0_vram.c @@ -78,7 +78,7 @@ nvc0_vram_new(struct drm_device *dev, u64 size, u32 align, u32 ncmin, INIT_LIST_HEAD(&mem->regions); mem->dev = dev_priv->dev; - mem->memtype = type; + mem->memtype = (type & 0xff); mem->size = size; mutex_lock(&mm->mutex); diff --git a/include/drm/nouveau_drm.h b/include/drm/nouveau_drm.h index e2cfe80f6fca..5edd3a76fffa 100644 --- a/include/drm/nouveau_drm.h +++ b/include/drm/nouveau_drm.h @@ -94,6 +94,7 @@ struct drm_nouveau_setparam { #define NOUVEAU_GEM_DOMAIN_GART (1 << 2) #define NOUVEAU_GEM_DOMAIN_MAPPABLE (1 << 3) +#define NOUVEAU_GEM_TILE_COMP 0x00030000 /* nv50-only */ #define NOUVEAU_GEM_TILE_LAYOUT_MASK 0x0000ff00 #define NOUVEAU_GEM_TILE_16BPP 0x00000001 #define NOUVEAU_GEM_TILE_32BPP 0x00000002 -- cgit v1.2.3 From dca8b089c95d94afa1d715df257de0286350e99d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 24 Feb 2011 13:38:12 -0800 Subject: ipv4: Rearrange how ip_route_newports() gets port keys. ip_route_newports() is the only place in the entire kernel that cares about the port members in the routing cache entry's lookup flow key. Therefore the only reason we store an entire flow inside of the struct rtentry is for this one special case. Rewrite ip_route_newports() such that: 1) The caller passes in the original port values, so we don't need to use the rth->fl.fl_ip_{s,d}port values to remember them. 2) The lookup flow is constructed by hand instead of being copied from the routing cache entry's flow. Signed-off-by: David S. Miller --- include/net/route.h | 19 +++++++++++-------- net/dccp/ipv4.c | 10 +++++++--- net/ipv4/tcp_ipv4.c | 6 +++++- 3 files changed, 23 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/route.h b/include/net/route.h index bf790c1c6ac8..b3f89ad04e0b 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -200,16 +200,19 @@ static inline int ip_route_connect(struct rtable **rp, __be32 dst, } static inline int ip_route_newports(struct rtable **rp, u8 protocol, + __be16 orig_sport, __be16 orig_dport, __be16 sport, __be16 dport, struct sock *sk) { - if (sport != (*rp)->fl.fl_ip_sport || - dport != (*rp)->fl.fl_ip_dport) { - struct flowi fl; - - memcpy(&fl, &(*rp)->fl, sizeof(fl)); - fl.fl_ip_sport = sport; - fl.fl_ip_dport = dport; - fl.proto = protocol; + if (sport != orig_sport || dport != orig_dport) { + struct flowi fl = { .oif = (*rp)->fl.oif, + .mark = (*rp)->fl.mark, + .fl4_dst = (*rp)->fl.fl4_dst, + .fl4_src = (*rp)->fl.fl4_src, + .fl4_tos = (*rp)->fl.fl4_tos, + .proto = (*rp)->fl.proto, + .fl_ip_sport = sport, + .fl_ip_dport = dport }; + if (inet_sk(sk)->transparent) fl.flags |= FLOWI_FLAG_ANYSRC; if (protocol == IPPROTO_TCP) diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 45a434f94169..937989199c80 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -43,6 +43,7 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) struct inet_sock *inet = inet_sk(sk); struct dccp_sock *dp = dccp_sk(sk); const struct sockaddr_in *usin = (struct sockaddr_in *)uaddr; + __be16 orig_sport, orig_dport; struct rtable *rt; __be32 daddr, nexthop; int tmp; @@ -63,10 +64,12 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) nexthop = inet->opt->faddr; } + orig_sport = inet->inet_sport; + orig_dport = usin->sin_port; tmp = ip_route_connect(&rt, nexthop, inet->inet_saddr, RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, IPPROTO_DCCP, - inet->inet_sport, usin->sin_port, sk, 1); + orig_sport, orig_dport, sk, 1); if (tmp < 0) return tmp; @@ -99,8 +102,9 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (err != 0) goto failure; - err = ip_route_newports(&rt, IPPROTO_DCCP, inet->inet_sport, - inet->inet_dport, sk); + err = ip_route_newports(&rt, IPPROTO_DCCP, + orig_sport, orig_dport, + inet->inet_sport, inet->inet_dport, sk); if (err != 0) goto failure; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index ef5a90beb9b0..27a0cc8cc888 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -149,6 +149,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) struct inet_sock *inet = inet_sk(sk); struct tcp_sock *tp = tcp_sk(sk); struct sockaddr_in *usin = (struct sockaddr_in *)uaddr; + __be16 orig_sport, orig_dport; struct rtable *rt; __be32 daddr, nexthop; int tmp; @@ -167,10 +168,12 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) nexthop = inet->opt->faddr; } + orig_sport = inet->inet_sport; + orig_dport = usin->sin_port; tmp = ip_route_connect(&rt, nexthop, inet->inet_saddr, RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, IPPROTO_TCP, - inet->inet_sport, usin->sin_port, sk, 1); + orig_sport, orig_dport, sk, 1); if (tmp < 0) { if (tmp == -ENETUNREACH) IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); @@ -234,6 +237,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) goto failure; err = ip_route_newports(&rt, IPPROTO_TCP, + orig_sport, orig_dport, inet->inet_sport, inet->inet_dport, sk); if (err) goto failure; -- cgit v1.2.3 From b552f7e3a9524abcbcdf86f0a99b2be58e55a9c6 Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Sat, 19 Feb 2011 17:32:28 +0800 Subject: ipvs: unify the formula to estimate the overhead of processing connections lc and wlc use the same formula, but lblc and lblcr use another one. There is no reason for using two different formulas for the lc variants. The formula used by lc is used by all the lc variants in this patch. Signed-off-by: Changli Gao Acked-by: Wensong Zhang Signed-off-by: Simon Horman --- include/net/ip_vs.h | 14 ++++++++++++++ net/netfilter/ipvs/ip_vs_lblc.c | 13 +++---------- net/netfilter/ipvs/ip_vs_lblcr.c | 25 +++++++------------------ net/netfilter/ipvs/ip_vs_lc.c | 18 +----------------- net/netfilter/ipvs/ip_vs_wlc.c | 20 ++------------------ 5 files changed, 27 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 17b01b2d48f9..e74da41ebd1b 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -1243,6 +1243,20 @@ static inline void ip_vs_conn_drop_conntrack(struct ip_vs_conn *cp) /* CONFIG_IP_VS_NFCT */ #endif +static inline unsigned int +ip_vs_dest_conn_overhead(struct ip_vs_dest *dest) +{ + /* + * We think the overhead of processing active connections is 256 + * times higher than that of inactive connections in average. (This + * 256 times might not be accurate, we will change it later) We + * use the following formula to estimate the overhead now: + * dest->activeconns*256 + dest->inactconns + */ + return (atomic_read(&dest->activeconns) << 8) + + atomic_read(&dest->inactconns); +} + #endif /* __KERNEL__ */ #endif /* _NET_IP_VS_H */ diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 4a9c8cd19690..6bf7a807649c 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -389,12 +389,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc) int loh, doh; /* - * We think the overhead of processing active connections is fifty - * times higher than that of inactive connections in average. (This - * fifty times might not be accurate, we will change it later.) We - * use the following formula to estimate the overhead: - * dest->activeconns*50 + dest->inactconns - * and the load: + * We use the following formula to estimate the load: * (dest overhead) / dest->weight * * Remember -- no floats in kernel mode!!! @@ -410,8 +405,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc) continue; if (atomic_read(&dest->weight) > 0) { least = dest; - loh = atomic_read(&least->activeconns) * 50 - + atomic_read(&least->inactconns); + loh = ip_vs_dest_conn_overhead(least); goto nextstage; } } @@ -425,8 +419,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc) if (dest->flags & IP_VS_DEST_F_OVERLOAD) continue; - doh = atomic_read(&dest->activeconns) * 50 - + atomic_read(&dest->inactconns); + doh = ip_vs_dest_conn_overhead(dest); if (loh * atomic_read(&dest->weight) > doh * atomic_read(&least->weight)) { least = dest; diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index bd329b1e9589..00631765b92a 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -178,8 +178,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) if ((atomic_read(&least->weight) > 0) && (least->flags & IP_VS_DEST_F_AVAILABLE)) { - loh = atomic_read(&least->activeconns) * 50 - + atomic_read(&least->inactconns); + loh = ip_vs_dest_conn_overhead(least); goto nextstage; } } @@ -192,8 +191,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) if (dest->flags & IP_VS_DEST_F_OVERLOAD) continue; - doh = atomic_read(&dest->activeconns) * 50 - + atomic_read(&dest->inactconns); + doh = ip_vs_dest_conn_overhead(dest); if ((loh * atomic_read(&dest->weight) > doh * atomic_read(&least->weight)) && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { @@ -228,8 +226,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set) list_for_each_entry(e, &set->list, list) { most = e->dest; if (atomic_read(&most->weight) > 0) { - moh = atomic_read(&most->activeconns) * 50 - + atomic_read(&most->inactconns); + moh = ip_vs_dest_conn_overhead(most); goto nextstage; } } @@ -239,8 +236,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set) nextstage: list_for_each_entry(e, &set->list, list) { dest = e->dest; - doh = atomic_read(&dest->activeconns) * 50 - + atomic_read(&dest->inactconns); + doh = ip_vs_dest_conn_overhead(dest); /* moh/mw < doh/dw ==> moh*dw < doh*mw, where mw,dw>0 */ if ((moh * atomic_read(&dest->weight) < doh * atomic_read(&most->weight)) @@ -563,12 +559,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc) int loh, doh; /* - * We think the overhead of processing active connections is fifty - * times higher than that of inactive connections in average. (This - * fifty times might not be accurate, we will change it later.) We - * use the following formula to estimate the overhead: - * dest->activeconns*50 + dest->inactconns - * and the load: + * We use the following formula to estimate the load: * (dest overhead) / dest->weight * * Remember -- no floats in kernel mode!!! @@ -585,8 +576,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc) if (atomic_read(&dest->weight) > 0) { least = dest; - loh = atomic_read(&least->activeconns) * 50 - + atomic_read(&least->inactconns); + loh = ip_vs_dest_conn_overhead(least); goto nextstage; } } @@ -600,8 +590,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc) if (dest->flags & IP_VS_DEST_F_OVERLOAD) continue; - doh = atomic_read(&dest->activeconns) * 50 - + atomic_read(&dest->inactconns); + doh = ip_vs_dest_conn_overhead(dest); if (loh * atomic_read(&dest->weight) > doh * atomic_read(&least->weight)) { least = dest; diff --git a/net/netfilter/ipvs/ip_vs_lc.c b/net/netfilter/ipvs/ip_vs_lc.c index 60638007c6c7..f391819c0cca 100644 --- a/net/netfilter/ipvs/ip_vs_lc.c +++ b/net/netfilter/ipvs/ip_vs_lc.c @@ -22,22 +22,6 @@ #include - -static inline unsigned int -ip_vs_lc_dest_overhead(struct ip_vs_dest *dest) -{ - /* - * We think the overhead of processing active connections is 256 - * times higher than that of inactive connections in average. (This - * 256 times might not be accurate, we will change it later) We - * use the following formula to estimate the overhead now: - * dest->activeconns*256 + dest->inactconns - */ - return (atomic_read(&dest->activeconns) << 8) + - atomic_read(&dest->inactconns); -} - - /* * Least Connection scheduling */ @@ -62,7 +46,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) if ((dest->flags & IP_VS_DEST_F_OVERLOAD) || atomic_read(&dest->weight) == 0) continue; - doh = ip_vs_lc_dest_overhead(dest); + doh = ip_vs_dest_conn_overhead(dest); if (!least || doh < loh) { least = dest; loh = doh; diff --git a/net/netfilter/ipvs/ip_vs_wlc.c b/net/netfilter/ipvs/ip_vs_wlc.c index fdf0f58962a4..bc1bfc48a17f 100644 --- a/net/netfilter/ipvs/ip_vs_wlc.c +++ b/net/netfilter/ipvs/ip_vs_wlc.c @@ -27,22 +27,6 @@ #include - -static inline unsigned int -ip_vs_wlc_dest_overhead(struct ip_vs_dest *dest) -{ - /* - * We think the overhead of processing active connections is 256 - * times higher than that of inactive connections in average. (This - * 256 times might not be accurate, we will change it later) We - * use the following formula to estimate the overhead now: - * dest->activeconns*256 + dest->inactconns - */ - return (atomic_read(&dest->activeconns) << 8) + - atomic_read(&dest->inactconns); -} - - /* * Weighted Least Connection scheduling */ @@ -71,7 +55,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) && atomic_read(&dest->weight) > 0) { least = dest; - loh = ip_vs_wlc_dest_overhead(least); + loh = ip_vs_dest_conn_overhead(least); goto nextstage; } } @@ -85,7 +69,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) list_for_each_entry_continue(dest, &svc->destinations, n_list) { if (dest->flags & IP_VS_DEST_F_OVERLOAD) continue; - doh = ip_vs_wlc_dest_overhead(dest); + doh = ip_vs_dest_conn_overhead(dest); if (loh * atomic_read(&dest->weight) > doh * atomic_read(&least->weight)) { least = dest; -- cgit v1.2.3 From df173bda2639ac744ccf596ec1f8f7e66fe4c343 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 23 Feb 2011 13:04:19 +0000 Subject: netem: define NETEM_DIST_MAX Rather than magic constant in code, expose the maximum size of packet distribution table in API. In iproute2, q_netem defines MAX_DIST as 16K already. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/pkt_sched.h | 1 + net/sched/sch_netem.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h index 5afee2b238bd..891382268fe1 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -495,6 +495,7 @@ struct tc_netem_corrupt { }; #define NETEM_DIST_SCALE 8192 +#define NETEM_DIST_MAX 16384 /* DRR */ diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 86dad1eee549..289febd3ccac 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -332,7 +332,7 @@ static int get_dist_table(struct Qdisc *sch, const struct nlattr *attr) int i; size_t s; - if (n > 65536) + if (n > NETEM_DIST_MAX) return -EINVAL; s = sizeof(struct disttable) + n * sizeof(s16); -- cgit v1.2.3 From 661b79725fea030803a89a16cda506bac8eeca78 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 23 Feb 2011 13:04:21 +0000 Subject: netem: revised correlated loss generator This is a patch originated with Stefano Salsano and Fabio Ludovici. It provides several alternative loss models for use with netem. This patch adds two state machine based loss models. See: http://netgroup.uniroma2.it/twiki/bin/view.cgi/Main/NetemCLG Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/pkt_sched.h | 26 +++++ net/sched/sch_netem.c | 274 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 296 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h index 891382268fe1..b1032a3fafdc 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -464,6 +464,7 @@ enum { TCA_NETEM_DELAY_DIST, TCA_NETEM_REORDER, TCA_NETEM_CORRUPT, + TCA_NETEM_LOSS, __TCA_NETEM_MAX, }; @@ -494,6 +495,31 @@ struct tc_netem_corrupt { __u32 correlation; }; +enum { + NETEM_LOSS_UNSPEC, + NETEM_LOSS_GI, /* General Intuitive - 4 state model */ + NETEM_LOSS_GE, /* Gilbert Elliot models */ + __NETEM_LOSS_MAX +}; +#define NETEM_LOSS_MAX (__NETEM_LOSS_MAX - 1) + +/* State transition probablities for 4 state model */ +struct tc_netem_gimodel { + __u32 p13; + __u32 p31; + __u32 p32; + __u32 p14; + __u32 p23; +}; + +/* Gilbert-Elliot models */ +struct tc_netem_gemodel { + __u32 p; + __u32 r; + __u32 h; + __u32 k1; +}; + #define NETEM_DIST_SCALE 8192 #define NETEM_DIST_MAX 16384 diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index f176890eeef0..5bbcccc353d0 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -47,6 +47,20 @@ layering other disciplines. It does not need to do bandwidth control either since that can be handled by using token bucket or other rate control. + + Correlated Loss Generator models + + Added generation of correlated loss according to the + "Gilbert-Elliot" model, a 4-state markov model. + + References: + [1] NetemCLG Home http://netgroup.uniroma2.it/NetemCLG + [2] S. Salsano, F. Ludovici, A. Ordine, "Definition of a general + and intuitive loss model for packet networks and its implementation + in the Netem module in the Linux kernel", available in [1] + + Authors: Stefano Salsano */ struct netem_sched_data { @@ -73,6 +87,26 @@ struct netem_sched_data { u32 size; s16 table[0]; } *delay_dist; + + enum { + CLG_RANDOM, + CLG_4_STATES, + CLG_GILB_ELL, + } loss_model; + + /* Correlated Loss Generation models */ + struct clgstate { + /* state of the Markov chain */ + u8 state; + + /* 4-states and Gilbert-Elliot models */ + u32 a1; /* p13 for 4-states or p for GE */ + u32 a2; /* p31 for 4-states or r for GE */ + u32 a3; /* p32 for 4-states or h for GE */ + u32 a4; /* p14 for 4-states or 1-k for GE */ + u32 a5; /* p23 used only in 4-states */ + } clg; + }; /* Time stamp put into socket buffer control block */ @@ -115,6 +149,122 @@ static u32 get_crandom(struct crndstate *state) return answer; } +/* loss_4state - 4-state model loss generator + * Generates losses according to the 4-state Markov chain adopted in + * the GI (General and Intuitive) loss model. + */ +static bool loss_4state(struct netem_sched_data *q) +{ + struct clgstate *clg = &q->clg; + u32 rnd = net_random(); + + /* + * Makes a comparision between rnd and the transition + * probabilities outgoing from the current state, then decides the + * next state and if the next packet has to be transmitted or lost. + * The four states correspond to: + * 1 => successfully transmitted packets within a gap period + * 4 => isolated losses within a gap period + * 3 => lost packets within a burst period + * 2 => successfully transmitted packets within a burst period + */ + switch (clg->state) { + case 1: + if (rnd < clg->a4) { + clg->state = 4; + return true; + } else if (clg->a4 < rnd && rnd < clg->a1) { + clg->state = 3; + return true; + } else if (clg->a1 < rnd) + clg->state = 1; + + break; + case 2: + if (rnd < clg->a5) { + clg->state = 3; + return true; + } else + clg->state = 2; + + break; + case 3: + if (rnd < clg->a3) + clg->state = 2; + else if (clg->a3 < rnd && rnd < clg->a2 + clg->a3) { + clg->state = 1; + return true; + } else if (clg->a2 + clg->a3 < rnd) { + clg->state = 3; + return true; + } + break; + case 4: + clg->state = 1; + break; + } + + return false; +} + +/* loss_gilb_ell - Gilbert-Elliot model loss generator + * Generates losses according to the Gilbert-Elliot loss model or + * its special cases (Gilbert or Simple Gilbert) + * + * Makes a comparision between random number and the transition + * probabilities outgoing from the current state, then decides the + * next state. A second random number is extracted and the comparision + * with the loss probability of the current state decides if the next + * packet will be transmitted or lost. + */ +static bool loss_gilb_ell(struct netem_sched_data *q) +{ + struct clgstate *clg = &q->clg; + + switch (clg->state) { + case 1: + if (net_random() < clg->a1) + clg->state = 2; + if (net_random() < clg->a4) + return true; + case 2: + if (net_random() < clg->a2) + clg->state = 1; + if (clg->a3 > net_random()) + return true; + } + + return false; +} + +static bool loss_event(struct netem_sched_data *q) +{ + switch (q->loss_model) { + case CLG_RANDOM: + /* Random packet drop 0 => none, ~0 => all */ + return q->loss && q->loss >= get_crandom(&q->loss_cor); + + case CLG_4_STATES: + /* 4state loss model algorithm (used also for GI model) + * Extracts a value from the markov 4 state loss generator, + * if it is 1 drops a packet and if needed writes the event in + * the kernel logs + */ + return loss_4state(q); + + case CLG_GILB_ELL: + /* Gilbert-Elliot loss model algorithm + * Extracts a value from the Gilbert-Elliot loss generator, + * if it is 1 drops a packet and if needed writes the event in + * the kernel logs + */ + return loss_gilb_ell(q); + } + + return false; /* not reached */ +} + + /* tabledist - return a pseudo-randomly distributed value with mean mu and * std deviation sigma. Uses table lookup to approximate the desired * distribution, and a uniformly-distributed pseudo-random source. @@ -167,8 +317,8 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor)) ++count; - /* Random packet drop 0 => none, ~0 => all */ - if (q->loss && q->loss >= get_crandom(&q->loss_cor)) + /* Drop packet? */ + if (loss_event(q)) --count; if (count == 0) { @@ -385,10 +535,66 @@ static void get_corrupt(struct Qdisc *sch, const struct nlattr *attr) init_crandom(&q->corrupt_cor, r->correlation); } +static int get_loss_clg(struct Qdisc *sch, const struct nlattr *attr) +{ + struct netem_sched_data *q = qdisc_priv(sch); + const struct nlattr *la; + int rem; + + nla_for_each_nested(la, attr, rem) { + u16 type = nla_type(la); + + switch(type) { + case NETEM_LOSS_GI: { + const struct tc_netem_gimodel *gi = nla_data(la); + + if (nla_len(la) != sizeof(struct tc_netem_gimodel)) { + pr_info("netem: incorrect gi model size\n"); + return -EINVAL; + } + + q->loss_model = CLG_4_STATES; + + q->clg.state = 1; + q->clg.a1 = gi->p13; + q->clg.a2 = gi->p31; + q->clg.a3 = gi->p32; + q->clg.a4 = gi->p14; + q->clg.a5 = gi->p23; + break; + } + + case NETEM_LOSS_GE: { + const struct tc_netem_gemodel *ge = nla_data(la); + + if (nla_len(la) != sizeof(struct tc_netem_gemodel)) { + pr_info("netem: incorrect gi model size\n"); + return -EINVAL; + } + + q->loss_model = CLG_GILB_ELL; + q->clg.state = 1; + q->clg.a1 = ge->p; + q->clg.a2 = ge->r; + q->clg.a3 = ge->h; + q->clg.a4 = ge->k1; + break; + } + + default: + pr_info("netem: unknown loss type %u\n", type); + return -EINVAL; + } + } + + return 0; +} + static const struct nla_policy netem_policy[TCA_NETEM_MAX + 1] = { [TCA_NETEM_CORR] = { .len = sizeof(struct tc_netem_corr) }, [TCA_NETEM_REORDER] = { .len = sizeof(struct tc_netem_reorder) }, [TCA_NETEM_CORRUPT] = { .len = sizeof(struct tc_netem_corrupt) }, + [TCA_NETEM_LOSS] = { .type = NLA_NESTED }, }; static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla, @@ -396,11 +602,15 @@ static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla, { int nested_len = nla_len(nla) - NLA_ALIGN(len); - if (nested_len < 0) + if (nested_len < 0) { + pr_info("netem: invalid attributes len %d\n", nested_len); return -EINVAL; + } + if (nested_len >= nla_attr_size(0)) return nla_parse(tb, maxtype, nla_data(nla) + NLA_ALIGN(len), nested_len, policy); + memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); return 0; } @@ -456,7 +666,11 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt) if (tb[TCA_NETEM_CORRUPT]) get_corrupt(sch, tb[TCA_NETEM_CORRUPT]); - return 0; + q->loss_model = CLG_RANDOM; + if (tb[TCA_NETEM_LOSS]) + ret = get_loss_clg(sch, tb[TCA_NETEM_LOSS]); + + return ret; } /* @@ -551,6 +765,7 @@ static int netem_init(struct Qdisc *sch, struct nlattr *opt) qdisc_watchdog_init(&q->watchdog, sch); + q->loss_model = CLG_RANDOM; q->qdisc = qdisc_create_dflt(sch->dev_queue, &tfifo_qdisc_ops, TC_H_MAKE(sch->handle, 1)); if (!q->qdisc) { @@ -575,6 +790,54 @@ static void netem_destroy(struct Qdisc *sch) dist_free(q->delay_dist); } +static int dump_loss_model(const struct netem_sched_data *q, + struct sk_buff *skb) +{ + struct nlattr *nest; + + nest = nla_nest_start(skb, TCA_NETEM_LOSS); + if (nest == NULL) + goto nla_put_failure; + + switch (q->loss_model) { + case CLG_RANDOM: + /* legacy loss model */ + nla_nest_cancel(skb, nest); + return 0; /* no data */ + + case CLG_4_STATES: { + struct tc_netem_gimodel gi = { + .p13 = q->clg.a1, + .p31 = q->clg.a2, + .p32 = q->clg.a3, + .p14 = q->clg.a4, + .p23 = q->clg.a5, + }; + + NLA_PUT(skb, NETEM_LOSS_GI, sizeof(gi), &gi); + break; + } + case CLG_GILB_ELL: { + struct tc_netem_gemodel ge = { + .p = q->clg.a1, + .r = q->clg.a2, + .h = q->clg.a3, + .k1 = q->clg.a4, + }; + + NLA_PUT(skb, NETEM_LOSS_GE, sizeof(ge), &ge); + break; + } + } + + nla_nest_end(skb, nest); + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nest); + return -1; +} + static int netem_dump(struct Qdisc *sch, struct sk_buff *skb) { const struct netem_sched_data *q = qdisc_priv(sch); @@ -605,6 +868,9 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb) corrupt.correlation = q->corrupt_cor.rho; NLA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt); + if (dump_loss_model(q, skb) != 0) + goto nla_put_failure; + return nla_nest_end(skb, nla); nla_put_failure: -- cgit v1.2.3 From c80a420995e721099906607b07c09a24543b31d9 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Thu, 2 Dec 2010 17:55:00 +0000 Subject: xen-blkfront: handle Xen major numbers other than XENVBD This patch makes sure blkfront handles correctly virtual device numbers corresponding to Xen emulated IDE and SCSI disks: in those cases blkfront translates the major number to XENVBD and the minor number to a low xvd minor. Note: this behaviour is different from what old xenlinux PV guests used to do: they used to steal an IDE or SCSI major number and use it instead. Signed-off-by: Stefano Stabellini Acked-by: Jeremy Fitzhardinge --- drivers/block/xen-blkfront.c | 79 +++++++++++++++++++++++++++++++++++++--- include/xen/interface/io/blkif.h | 21 +++++++++++ 2 files changed, 95 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index d7aa39e349a6..64d9c6dfc634 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -120,6 +120,10 @@ static DEFINE_SPINLOCK(minor_lock); #define EXTENDED (1<feature_flush ? "enabled" : "disabled"); } +static int xen_translate_vdev(int vdevice, int *minor, unsigned int *offset) +{ + int major; + major = BLKIF_MAJOR(vdevice); + *minor = BLKIF_MINOR(vdevice); + switch (major) { + case XEN_IDE0_MAJOR: + *offset = (*minor / 64) + EMULATED_HD_DISK_NAME_OFFSET; + *minor = ((*minor / 64) * PARTS_PER_DISK) + + EMULATED_HD_DISK_MINOR_OFFSET; + break; + case XEN_IDE1_MAJOR: + *offset = (*minor / 64) + 2 + EMULATED_HD_DISK_NAME_OFFSET; + *minor = (((*minor / 64) + 2) * PARTS_PER_DISK) + + EMULATED_HD_DISK_MINOR_OFFSET; + break; + case XEN_SCSI_DISK0_MAJOR: + *offset = (*minor / PARTS_PER_DISK) + EMULATED_SD_DISK_NAME_OFFSET; + *minor = *minor + EMULATED_SD_DISK_MINOR_OFFSET; + break; + case XEN_SCSI_DISK1_MAJOR: + case XEN_SCSI_DISK2_MAJOR: + case XEN_SCSI_DISK3_MAJOR: + case XEN_SCSI_DISK4_MAJOR: + case XEN_SCSI_DISK5_MAJOR: + case XEN_SCSI_DISK6_MAJOR: + case XEN_SCSI_DISK7_MAJOR: + *offset = (*minor / PARTS_PER_DISK) + + ((major - XEN_SCSI_DISK1_MAJOR + 1) * 16) + + EMULATED_SD_DISK_NAME_OFFSET; + *minor = *minor + + ((major - XEN_SCSI_DISK1_MAJOR + 1) * 16 * PARTS_PER_DISK) + + EMULATED_SD_DISK_MINOR_OFFSET; + break; + case XEN_SCSI_DISK8_MAJOR: + case XEN_SCSI_DISK9_MAJOR: + case XEN_SCSI_DISK10_MAJOR: + case XEN_SCSI_DISK11_MAJOR: + case XEN_SCSI_DISK12_MAJOR: + case XEN_SCSI_DISK13_MAJOR: + case XEN_SCSI_DISK14_MAJOR: + case XEN_SCSI_DISK15_MAJOR: + *offset = (*minor / PARTS_PER_DISK) + + ((major - XEN_SCSI_DISK8_MAJOR + 8) * 16) + + EMULATED_SD_DISK_NAME_OFFSET; + *minor = *minor + + ((major - XEN_SCSI_DISK8_MAJOR + 8) * 16 * PARTS_PER_DISK) + + EMULATED_SD_DISK_MINOR_OFFSET; + break; + case XENVBD_MAJOR: + *offset = *minor / PARTS_PER_DISK; + break; + default: + printk(KERN_WARNING "blkfront: your disk configuration is " + "incorrect, please use an xvd device instead\n"); + return -ENODEV; + } + return 0; +} static int xlvbd_alloc_gendisk(blkif_sector_t capacity, struct blkfront_info *info, @@ -441,7 +504,7 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity, { struct gendisk *gd; int nr_minors = 1; - int err = -ENODEV; + int err; unsigned int offset; int minor; int nr_parts; @@ -456,12 +519,20 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity, } if (!VDEV_IS_EXTENDED(info->vdevice)) { - minor = BLKIF_MINOR(info->vdevice); - nr_parts = PARTS_PER_DISK; + err = xen_translate_vdev(info->vdevice, &minor, &offset); + if (err) + return err; + nr_parts = PARTS_PER_DISK; } else { minor = BLKIF_MINOR_EXT(info->vdevice); nr_parts = PARTS_PER_EXT_DISK; + offset = minor / nr_parts; + if (xen_hvm_domain() && offset <= EMULATED_HD_DISK_NAME_OFFSET + 4) + printk(KERN_WARNING "blkfront: vdevice 0x%x might conflict with " + "emulated IDE disks,\n\t choose an xvd device name" + "from xvde on\n", info->vdevice); } + err = -ENODEV; if ((minor % nr_parts) == 0) nr_minors = nr_parts; @@ -475,8 +546,6 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity, if (gd == NULL) goto release; - offset = minor / nr_parts; - if (nr_minors > 1) { if (offset < 26) sprintf(gd->disk_name, "%s%c", DEV_NAME, 'a' + offset); diff --git a/include/xen/interface/io/blkif.h b/include/xen/interface/io/blkif.h index c2d1fa4dc1ee..68dd2b49635c 100644 --- a/include/xen/interface/io/blkif.h +++ b/include/xen/interface/io/blkif.h @@ -91,4 +91,25 @@ DEFINE_RING_TYPES(blkif, struct blkif_request, struct blkif_response); #define VDISK_REMOVABLE 0x2 #define VDISK_READONLY 0x4 +/* Xen-defined major numbers for virtual disks, they look strangely + * familiar */ +#define XEN_IDE0_MAJOR 3 +#define XEN_IDE1_MAJOR 22 +#define XEN_SCSI_DISK0_MAJOR 8 +#define XEN_SCSI_DISK1_MAJOR 65 +#define XEN_SCSI_DISK2_MAJOR 66 +#define XEN_SCSI_DISK3_MAJOR 67 +#define XEN_SCSI_DISK4_MAJOR 68 +#define XEN_SCSI_DISK5_MAJOR 69 +#define XEN_SCSI_DISK6_MAJOR 70 +#define XEN_SCSI_DISK7_MAJOR 71 +#define XEN_SCSI_DISK8_MAJOR 128 +#define XEN_SCSI_DISK9_MAJOR 129 +#define XEN_SCSI_DISK10_MAJOR 130 +#define XEN_SCSI_DISK11_MAJOR 131 +#define XEN_SCSI_DISK12_MAJOR 132 +#define XEN_SCSI_DISK13_MAJOR 133 +#define XEN_SCSI_DISK14_MAJOR 134 +#define XEN_SCSI_DISK15_MAJOR 135 + #endif /* __XEN_PUBLIC_IO_BLKIF_H__ */ -- cgit v1.2.3 From a8b7458363b9174f3c2196ca6085630b4b30b7a1 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Thu, 17 Feb 2011 11:04:20 +0000 Subject: xen: switch to new schedop hypercall by default. Rename old interface to sched_op_compat and rename sched_op_new to simply sched_op. Signed-off-by: Ian Campbell Reviewed-by: Konrad Rzeszutek Wilk --- arch/x86/include/asm/xen/hypercall.h | 4 ++-- include/xen/interface/xen.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/xen/hypercall.h b/arch/x86/include/asm/xen/hypercall.h index a7d5823862b4..8508bfe52296 100644 --- a/arch/x86/include/asm/xen/hypercall.h +++ b/arch/x86/include/asm/xen/hypercall.h @@ -287,7 +287,7 @@ HYPERVISOR_fpu_taskswitch(int set) static inline int HYPERVISOR_sched_op(int cmd, void *arg) { - return _hypercall2(int, sched_op_new, cmd, arg); + return _hypercall2(int, sched_op, cmd, arg); } static inline long @@ -432,7 +432,7 @@ HYPERVISOR_suspend(unsigned long start_info_mfn) * hypercall calling convention this is the third hypercall * argument, which is start_info_mfn here. */ - return _hypercall3(int, sched_op_new, SCHEDOP_shutdown, &r, start_info_mfn); + return _hypercall3(int, sched_op, SCHEDOP_shutdown, &r, start_info_mfn); } static inline int diff --git a/include/xen/interface/xen.h b/include/xen/interface/xen.h index 2befa3e2f1bc..b33257bc7e83 100644 --- a/include/xen/interface/xen.h +++ b/include/xen/interface/xen.h @@ -30,7 +30,7 @@ #define __HYPERVISOR_stack_switch 3 #define __HYPERVISOR_set_callbacks 4 #define __HYPERVISOR_fpu_taskswitch 5 -#define __HYPERVISOR_sched_op 6 +#define __HYPERVISOR_sched_op_compat 6 #define __HYPERVISOR_dom0_op 7 #define __HYPERVISOR_set_debugreg 8 #define __HYPERVISOR_get_debugreg 9 @@ -52,7 +52,7 @@ #define __HYPERVISOR_mmuext_op 26 #define __HYPERVISOR_acm_op 27 #define __HYPERVISOR_nmi_op 28 -#define __HYPERVISOR_sched_op_new 29 +#define __HYPERVISOR_sched_op 29 #define __HYPERVISOR_callback_op 30 #define __HYPERVISOR_xenoprof_op 31 #define __HYPERVISOR_event_channel_op 32 -- cgit v1.2.3 From 03c8142bd2fb3b87effa6ecb2f8957be588bc85f Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Thu, 17 Feb 2011 11:04:20 +0000 Subject: xen: suspend: add "arch" to pre/post suspend hooks xen_pre_device_suspend is unused on ia64. Signed-off-by: Ian Campbell Reviewed-by: Konrad Rzeszutek Wilk --- arch/ia64/xen/suspend.c | 9 ++------- arch/x86/xen/suspend.c | 6 +++--- drivers/xen/manage.c | 6 +++--- include/xen/xen-ops.h | 6 +++--- 4 files changed, 11 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/arch/ia64/xen/suspend.c b/arch/ia64/xen/suspend.c index fd66b048c6fa..419c8620945a 100644 --- a/arch/ia64/xen/suspend.c +++ b/arch/ia64/xen/suspend.c @@ -37,19 +37,14 @@ xen_mm_unpin_all(void) /* nothing */ } -void xen_pre_device_suspend(void) -{ - /* nothing */ -} - void -xen_pre_suspend() +xen_arch_pre_suspend() { /* nothing */ } void -xen_post_suspend(int suspend_cancelled) +xen_arch_post_suspend(int suspend_cancelled) { if (suspend_cancelled) return; diff --git a/arch/x86/xen/suspend.c b/arch/x86/xen/suspend.c index 4a3d3dd3dd30..45329c8c226e 100644 --- a/arch/x86/xen/suspend.c +++ b/arch/x86/xen/suspend.c @@ -12,7 +12,7 @@ #include "xen-ops.h" #include "mmu.h" -void xen_pre_suspend(void) +void xen_arch_pre_suspend(void) { xen_start_info->store_mfn = mfn_to_pfn(xen_start_info->store_mfn); xen_start_info->console.domU.mfn = @@ -26,7 +26,7 @@ void xen_pre_suspend(void) BUG(); } -void xen_hvm_post_suspend(int suspend_cancelled) +void xen_arch_hvm_post_suspend(int suspend_cancelled) { #ifdef CONFIG_XEN_PVHVM int cpu; @@ -41,7 +41,7 @@ void xen_hvm_post_suspend(int suspend_cancelled) #endif } -void xen_post_suspend(int suspend_cancelled) +void xen_arch_post_suspend(int suspend_cancelled) { xen_build_mfn_list_list(); diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index 6ce6b91e7645..134eb73ca596 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -61,7 +61,7 @@ static int xen_hvm_suspend(void *data) */ si->cancelled = HYPERVISOR_suspend(si->arg); - xen_hvm_post_suspend(si->cancelled); + xen_arch_hvm_post_suspend(si->cancelled); gnttab_resume(); if (!si->cancelled) { @@ -91,7 +91,7 @@ static int xen_suspend(void *data) xen_mm_pin_all(); gnttab_suspend(); - xen_pre_suspend(); + xen_arch_pre_suspend(); /* * This hypercall returns 1 if suspend was cancelled @@ -100,7 +100,7 @@ static int xen_suspend(void *data) */ si->cancelled = HYPERVISOR_suspend(si->arg); - xen_post_suspend(si->cancelled); + xen_arch_post_suspend(si->cancelled); gnttab_resume(); xen_mm_unpin_all(); diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h index 98b92154a264..03c85d7387fb 100644 --- a/include/xen/xen-ops.h +++ b/include/xen/xen-ops.h @@ -5,9 +5,9 @@ DECLARE_PER_CPU(struct vcpu_info *, xen_vcpu); -void xen_pre_suspend(void); -void xen_post_suspend(int suspend_cancelled); -void xen_hvm_post_suspend(int suspend_cancelled); +void xen_arch_pre_suspend(void); +void xen_arch_post_suspend(int suspend_cancelled); +void xen_arch_hvm_post_suspend(int suspend_cancelled); void xen_mm_pin_all(void); void xen_mm_unpin_all(void); -- cgit v1.2.3 From 22fd411ac9853f4becb3db9860f6d0b8398cac44 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Tue, 22 Feb 2011 19:54:47 +0100 Subject: [SCSI] fc: Add GSPN_ID request to header file Add request code and corresponding structs for "Get Symbolic Port Name" (GSPN_ID) request. Signed-off-by: Christof Schmitt Signed-off-by: Steffen Maier Signed-off-by: James Bottomley --- include/scsi/fc/fc_ns.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/scsi/fc/fc_ns.h b/include/scsi/fc/fc_ns.h index 185015dd1166..f7751d53f1d3 100644 --- a/include/scsi/fc/fc_ns.h +++ b/include/scsi/fc/fc_ns.h @@ -41,6 +41,7 @@ enum fc_ns_req { FC_NS_GI_A = 0x0101, /* get identifiers - scope */ FC_NS_GPN_ID = 0x0112, /* get port name by ID */ FC_NS_GNN_ID = 0x0113, /* get node name by ID */ + FC_NS_GSPN_ID = 0x0118, /* get symbolic port name */ FC_NS_GID_PN = 0x0121, /* get ID for port name */ FC_NS_GID_NN = 0x0131, /* get IDs for node name */ FC_NS_GID_FT = 0x0171, /* get IDs by FC4 type */ @@ -144,13 +145,21 @@ struct fc_ns_gid_pn { }; /* - * GID_PN response + * GID_PN response or GSPN_ID request */ struct fc_gid_pn_resp { __u8 fp_resvd; __u8 fp_fid[3]; /* port ID */ }; +/* + * GSPN_ID response + */ +struct fc_gspn_resp { + __u8 fp_name_len; + char fp_name[]; +}; + /* * RFT_ID request - register FC-4 types for ID. */ -- cgit v1.2.3 From a8059512b120362b15424f152b2548fe8b11bd0c Mon Sep 17 00:00:00 2001 From: Rémi Denis-Courmont Date: Thu, 24 Feb 2011 23:14:57 +0000 Subject: Phonet: implement per-socket destination/peer address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- include/net/phonet/phonet.h | 1 + net/phonet/af_phonet.c | 19 ++++++++++++++----- net/phonet/socket.c | 4 ++-- 3 files changed, 17 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/phonet/phonet.h b/include/net/phonet/phonet.h index 5395e09187df..68e509750caa 100644 --- a/include/net/phonet/phonet.h +++ b/include/net/phonet/phonet.h @@ -36,6 +36,7 @@ struct pn_sock { struct sock sk; u16 sobject; + u16 dobject; u8 resource; }; diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c index 1072b2c19d31..30cc676c35fd 100644 --- a/net/phonet/af_phonet.c +++ b/net/phonet/af_phonet.c @@ -110,6 +110,7 @@ static int pn_socket_create(struct net *net, struct socket *sock, int protocol, sk->sk_protocol = protocol; pn = pn_sk(sk); pn->sobject = 0; + pn->dobject = 0; pn->resource = 0; sk->sk_prot->init(sk); err = 0; @@ -242,8 +243,18 @@ int pn_skb_send(struct sock *sk, struct sk_buff *skb, struct net_device *dev; struct pn_sock *pn = pn_sk(sk); int err; - u16 src; - u8 daddr = pn_sockaddr_get_addr(target), saddr = PN_NO_ADDR; + u16 src, dst; + u8 daddr, saddr, res; + + src = pn->sobject; + if (target != NULL) { + dst = pn_sockaddr_get_object(target); + res = pn_sockaddr_get_resource(target); + } else { + dst = pn->dobject; + res = pn->resource; + } + daddr = pn_addr(dst); err = -EHOSTUNREACH; if (sk->sk_bound_dev_if) @@ -271,12 +282,10 @@ int pn_skb_send(struct sock *sk, struct sk_buff *skb, if (saddr == PN_NO_ADDR) goto drop; - src = pn->sobject; if (!pn_addr(src)) src = pn_object(saddr, pn_obj(src)); - err = pn_send(skb, dev, pn_sockaddr_get_object(target), - src, pn_sockaddr_get_resource(target), 0); + err = pn_send(skb, dev, dst, src, res, 0); dev_put(dev); return err; diff --git a/net/phonet/socket.c b/net/phonet/socket.c index ceb5143f5ef9..65a03338769e 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -633,8 +633,8 @@ static int pn_sock_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "%2d %04X:%04X:%02X %02X %08X:%08X %5d %lu " "%d %p %d%n", - sk->sk_protocol, pn->sobject, 0, pn->resource, - sk->sk_state, + sk->sk_protocol, pn->sobject, pn->dobject, + pn->resource, sk->sk_state, sk_wmem_alloc_get(sk), sk_rmem_alloc_get(sk), sock_i_uid(sk), sock_i_ino(sk), atomic_read(&sk->sk_refcnt), sk, -- cgit v1.2.3 From 14ba8faebcc241e4d60a4ef4a7d3fdef1c2e846f Mon Sep 17 00:00:00 2001 From: Rémi Denis-Courmont Date: Thu, 24 Feb 2011 23:14:58 +0000 Subject: Phonet: use socket destination in pipe protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- include/net/phonet/pep.h | 1 - net/phonet/pep.c | 41 ++++++++++++++++------------------------- 2 files changed, 16 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h index b60b28c99e87..788ccf353582 100644 --- a/include/net/phonet/pep.h +++ b/include/net/phonet/pep.h @@ -47,7 +47,6 @@ struct pep_sock { u8 aligned; #ifdef CONFIG_PHONET_PIPECTRLR u8 pipe_state; - struct sockaddr_pn remote_pep; #endif }; diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 3e60f2e4e6c2..4fce882d001a 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -50,11 +50,6 @@ #define CREDITS_MAX 10 #define CREDITS_THR 7 -static const struct sockaddr_pn pipe_srv = { - .spn_family = AF_PHONET, - .spn_resource = 0xD9, /* pipe service */ -}; - #define pep_sb_size(s) (((s) + 5) & ~3) /* 2-bytes head, 32-bits aligned */ /* Get the next TLV sub-block. */ @@ -88,6 +83,7 @@ static int pep_reply(struct sock *sk, struct sk_buff *oskb, const struct pnpipehdr *oph = pnp_hdr(oskb); struct pnpipehdr *ph; struct sk_buff *skb; + struct sockaddr_pn peer; skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority); if (!skb) @@ -105,7 +101,8 @@ static int pep_reply(struct sock *sk, struct sk_buff *oskb, ph->pipe_handle = oph->pipe_handle; ph->error_code = code; - return pn_skb_send(sk, skb, &pipe_srv); + pn_skb_get_src_sockaddr(oskb, &peer); + return pn_skb_send(sk, skb, &peer); } #define PAD 0x00 @@ -220,7 +217,7 @@ static int pipe_handler_send_req(struct sock *sk, u8 utid, ph->pipe_handle = pn->pipe_handle; ph->error_code = PN_PIPE_NO_ERROR; - return pn_skb_send(sk, skb, &pn->remote_pep); + return pn_skb_send(sk, skb, NULL); } static int pipe_handler_send_created_ind(struct sock *sk, @@ -262,7 +259,7 @@ static int pipe_handler_send_created_ind(struct sock *sk, ph->pipe_handle = pn->pipe_handle; ph->error_code = err_code; - return pn_skb_send(sk, skb, &pn->remote_pep); + return pn_skb_send(sk, skb, NULL); } static int pipe_handler_send_ind(struct sock *sk, u8 utid, u8 msg_id) @@ -295,7 +292,7 @@ static int pipe_handler_send_ind(struct sock *sk, u8 utid, u8 msg_id) ph->pipe_handle = pn->pipe_handle; ph->error_code = err_code; - return pn_skb_send(sk, skb, &pn->remote_pep); + return pn_skb_send(sk, skb, NULL); } static int pipe_handler_enable_pipe(struct sock *sk, int enable) @@ -396,11 +393,7 @@ static int pipe_snd_status(struct sock *sk, u8 type, u8 status, gfp_t priority) ph->data[3] = PAD; ph->data[4] = status; -#ifdef CONFIG_PHONET_PIPECTRLR - return pn_skb_send(sk, skb, &pn->remote_pep); -#else - return pn_skb_send(sk, skb, &pipe_srv); -#endif + return pn_skb_send(sk, skb, NULL); } /* Send our RX flow control information to the sender. @@ -722,7 +715,7 @@ static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb) struct sock *newsk; struct pep_sock *newpn, *pn = pep_sk(sk); struct pnpipehdr *hdr; - struct sockaddr_pn dst; + struct sockaddr_pn dst, src; u16 peer_type; u8 pipe_handle, enabled, n_sb; u8 aligned = 0; @@ -789,8 +782,10 @@ static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb) newpn = pep_sk(newsk); pn_skb_get_dst_sockaddr(skb, &dst); + pn_skb_get_src_sockaddr(skb, &src); newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst); - newpn->pn_sk.resource = pn->pn_sk.resource; + newpn->pn_sk.dobject = pn_sockaddr_get_object(&src); + newpn->pn_sk.resource = pn_sockaddr_get_resource(&dst); skb_queue_head_init(&newpn->ctrlreq_queue); newpn->pipe_handle = pipe_handle; atomic_set(&newpn->tx_credits, 0); @@ -925,7 +920,7 @@ static int pipe_do_remove(struct sock *sk) ph->pipe_handle = pn->pipe_handle; ph->data[0] = PAD; - return pn_skb_send(sk, skb, &pipe_srv); + return pn_skb_send(sk, skb, NULL); } /* associated socket ceases to exist */ @@ -1042,10 +1037,10 @@ out: static int pep_sock_connect(struct sock *sk, struct sockaddr *addr, int len) { struct pep_sock *pn = pep_sk(sk); - struct sockaddr_pn *spn = (struct sockaddr_pn *)addr; - - memcpy(&pn->remote_pep, spn, sizeof(struct sockaddr_pn)); + const struct sockaddr_pn *spn = (struct sockaddr_pn *)addr; + pn->pn_sk.dobject = pn_sockaddr_get_object(spn); + pn->pn_sk.resource = pn_sockaddr_get_resource(spn); return pipe_handler_send_req(sk, PNS_PEP_CONNECT_UTID, PNS_PEP_CONNECT_REQ, GFP_ATOMIC); @@ -1222,11 +1217,7 @@ static int pipe_skb_send(struct sock *sk, struct sk_buff *skb) } else ph->message_id = PNS_PIPE_DATA; ph->pipe_handle = pn->pipe_handle; -#ifdef CONFIG_PHONET_PIPECTRLR - err = pn_skb_send(sk, skb, &pn->remote_pep); -#else - err = pn_skb_send(sk, skb, &pipe_srv); -#endif + err = pn_skb_send(sk, skb, NULL); if (err && pn_flow_safe(pn->tx_fc)) atomic_inc(&pn->tx_credits); -- cgit v1.2.3 From 2feb61816f7f0be57f4bc61137555e9a8cb4f322 Mon Sep 17 00:00:00 2001 From: Rémi Denis-Courmont Date: Thu, 24 Feb 2011 23:14:59 +0000 Subject: Phonet: remove redumdant pep->pipe_state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sk->sk_state already contains the pipe state. Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- include/net/phonet/pep.h | 9 --------- net/phonet/pep.c | 25 ++++++------------------- 2 files changed, 6 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h index 788ccf353582..4c48ed80bc07 100644 --- a/include/net/phonet/pep.h +++ b/include/net/phonet/pep.h @@ -45,9 +45,6 @@ struct pep_sock { u8 tx_fc; /* TX flow control */ u8 init_enable; /* auto-enable at creation */ u8 aligned; -#ifdef CONFIG_PHONET_PIPECTRLR - u8 pipe_state; -#endif }; static inline struct pep_sock *pep_sk(struct sock *sk) @@ -177,12 +174,6 @@ enum { #define PNS_PIPE_DISABLED_IND_UTID 0x11 #define PNS_PEP_DISCONNECT_UTID 0x06 -/* Used for tracking state of a pipe */ -enum { - PIPE_IDLE, - PIPE_DISABLED, - PIPE_ENABLED, -}; #endif /* CONFIG_PHONET_PIPECTRLR */ #endif diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 4fce882d001a..15775a74b6f6 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -527,7 +527,6 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) #ifdef CONFIG_PHONET_PIPECTRLR case PNS_PEP_DISCONNECT_RESP: - pn->pipe_state = PIPE_IDLE; sk->sk_state = TCP_CLOSE; break; #endif @@ -539,7 +538,6 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) #ifdef CONFIG_PHONET_PIPECTRLR case PNS_PEP_ENABLE_RESP: - pn->pipe_state = PIPE_ENABLED; pipe_handler_send_ind(sk, PNS_PIPE_ENABLED_IND_UTID, PNS_PIPE_ENABLED_IND); @@ -574,7 +572,6 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) #ifdef CONFIG_PHONET_PIPECTRLR case PNS_PEP_DISABLE_RESP: - pn->pipe_state = PIPE_DISABLED; atomic_set(&pn->tx_credits, 0); pipe_handler_send_ind(sk, PNS_PIPE_DISABLED_IND_UTID, PNS_PIPE_DISABLED_IND); @@ -692,7 +689,6 @@ static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb) remote_pref_rx_fc, sizeof(host_pref_rx_fc)); - pn->pipe_state = PIPE_DISABLED; sk->sk_state = TCP_SYN_RECV; sk->sk_backlog_rcv = pipe_do_rcv; sk->sk_destruct = pipe_destruct; @@ -941,21 +937,18 @@ static void pep_sock_close(struct sock *sk, long timeout) sk_for_each_safe(sknode, p, n, &pn->ackq) sk_del_node_init(sknode); sk->sk_state = TCP_CLOSE; - } else if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) + } else if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) { +#ifndef CONFIG_PHONET_PIPECTRLR /* Forcefully remove dangling Phonet pipe */ pipe_do_remove(sk); - -#ifdef CONFIG_PHONET_PIPECTRLR - if (pn->pipe_state != PIPE_IDLE) { +#else /* send pep disconnect request */ pipe_handler_send_req(sk, PNS_PEP_DISCONNECT_UTID, PNS_PEP_DISCONNECT_REQ, GFP_KERNEL); - - pn->pipe_state = PIPE_IDLE; sk->sk_state = TCP_CLOSE; - } #endif + } ifindex = pn->ifindex; pn->ifindex = 0; @@ -1101,10 +1094,6 @@ static int pep_setsockopt(struct sock *sk, int level, int optname, #ifdef CONFIG_PHONET_PIPECTRLR case PNPIPE_PIPE_HANDLE: if (val) { - if (pn->pipe_state > PIPE_IDLE) { - err = -EFAULT; - break; - } pn->pipe_handle = val; break; } @@ -1138,7 +1127,7 @@ static int pep_setsockopt(struct sock *sk, int level, int optname, #ifdef CONFIG_PHONET_PIPECTRLR case PNPIPE_ENABLE: - if (pn->pipe_state <= PIPE_IDLE) { + if ((1 << sk->sk_state) & ~(TCPF_SYN_RECV|TCPF_ESTABLISHED)) { err = -ENOTCONN; break; } @@ -1177,9 +1166,7 @@ static int pep_getsockopt(struct sock *sk, int level, int optname, #ifdef CONFIG_PHONET_PIPECTRLR case PNPIPE_ENABLE: - if (pn->pipe_state <= PIPE_IDLE) - return -ENOTCONN; - val = pn->pipe_state != PIPE_DISABLED; + val = sk->sk_state == TCP_ESTABLISHED; break; #endif -- cgit v1.2.3 From 0165d69bcb18c5aa220538389c872852243f9725 Mon Sep 17 00:00:00 2001 From: Rémi Denis-Courmont Date: Thu, 24 Feb 2011 23:15:00 +0000 Subject: Phonet: don't bother with transaction IDs (especially for indications) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- include/net/phonet/pep.h | 11 ----------- net/phonet/pep.c | 49 ++++++++++++++---------------------------------- 2 files changed, 14 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h index 4c48ed80bc07..37f23dc05de8 100644 --- a/include/net/phonet/pep.h +++ b/include/net/phonet/pep.h @@ -165,15 +165,4 @@ enum { PEP_IND_READY, }; -#ifdef CONFIG_PHONET_PIPECTRLR -#define PNS_PEP_CONNECT_UTID 0x02 -#define PNS_PIPE_CREATED_IND_UTID 0x04 -#define PNS_PIPE_ENABLE_UTID 0x0A -#define PNS_PIPE_ENABLED_IND_UTID 0x0C -#define PNS_PIPE_DISABLE_UTID 0x0F -#define PNS_PIPE_DISABLED_IND_UTID 0x11 -#define PNS_PEP_DISCONNECT_UTID 0x06 - -#endif /* CONFIG_PHONET_PIPECTRLR */ - #endif diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 15775a74b6f6..0ecab59963e0 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -172,8 +172,7 @@ static int pipe_get_flow_info(struct sock *sk, struct sk_buff *skb, return 0; } -static int pipe_handler_send_req(struct sock *sk, u8 utid, - u8 msg_id, gfp_t priority) +static int pipe_handler_send_req(struct sock *sk, u8 msg_id, gfp_t priority) { int len; struct pnpipehdr *ph; @@ -212,7 +211,7 @@ static int pipe_handler_send_req(struct sock *sk, u8 utid, __skb_push(skb, sizeof(*ph)); skb_reset_transport_header(skb); ph = pnp_hdr(skb); - ph->utid = utid; + ph->utid = msg_id; /* whatever */ ph->message_id = msg_id; ph->pipe_handle = pn->pipe_handle; ph->error_code = PN_PIPE_NO_ERROR; @@ -220,8 +219,7 @@ static int pipe_handler_send_req(struct sock *sk, u8 utid, return pn_skb_send(sk, skb, NULL); } -static int pipe_handler_send_created_ind(struct sock *sk, - u8 utid, u8 msg_id) +static int pipe_handler_send_created_ind(struct sock *sk, u8 msg_id) { int err_code; struct pnpipehdr *ph; @@ -254,7 +252,7 @@ static int pipe_handler_send_created_ind(struct sock *sk, __skb_push(skb, sizeof(*ph)); skb_reset_transport_header(skb); ph = pnp_hdr(skb); - ph->utid = utid; + ph->utid = 0; ph->message_id = msg_id; ph->pipe_handle = pn->pipe_handle; ph->error_code = err_code; @@ -262,7 +260,7 @@ static int pipe_handler_send_created_ind(struct sock *sk, return pn_skb_send(sk, skb, NULL); } -static int pipe_handler_send_ind(struct sock *sk, u8 utid, u8 msg_id) +static int pipe_handler_send_ind(struct sock *sk, u8 msg_id) { int err_code; struct pnpipehdr *ph; @@ -287,7 +285,7 @@ static int pipe_handler_send_ind(struct sock *sk, u8 utid, u8 msg_id) __skb_push(skb, sizeof(*ph)); skb_reset_transport_header(skb); ph = pnp_hdr(skb); - ph->utid = utid; + ph->utid = 0; ph->message_id = msg_id; ph->pipe_handle = pn->pipe_handle; ph->error_code = err_code; @@ -297,16 +295,9 @@ static int pipe_handler_send_ind(struct sock *sk, u8 utid, u8 msg_id) static int pipe_handler_enable_pipe(struct sock *sk, int enable) { - int utid, req; - - if (enable) { - utid = PNS_PIPE_ENABLE_UTID; - req = PNS_PEP_ENABLE_REQ; - } else { - utid = PNS_PIPE_DISABLE_UTID; - req = PNS_PEP_DISABLE_REQ; - } - return pipe_handler_send_req(sk, utid, req, GFP_ATOMIC); + u8 id = enable ? PNS_PEP_ENABLE_REQ : PNS_PEP_DISABLE_REQ; + + return pipe_handler_send_req(sk, id, GFP_KERNEL); } #endif @@ -538,8 +529,7 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) #ifdef CONFIG_PHONET_PIPECTRLR case PNS_PEP_ENABLE_RESP: - pipe_handler_send_ind(sk, PNS_PIPE_ENABLED_IND_UTID, - PNS_PIPE_ENABLED_IND); + pipe_handler_send_ind(sk, PNS_PIPE_ENABLED_IND); if (!pn_flow_safe(pn->tx_fc)) { atomic_set(&pn->tx_credits, 1); @@ -573,8 +563,7 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) #ifdef CONFIG_PHONET_PIPECTRLR case PNS_PEP_DISABLE_RESP: atomic_set(&pn->tx_credits, 0); - pipe_handler_send_ind(sk, PNS_PIPE_DISABLED_IND_UTID, - PNS_PIPE_DISABLED_IND); + pipe_handler_send_ind(sk, PNS_PIPE_DISABLED_IND); sk->sk_state = TCP_SYN_RECV; pn->rx_credits = 0; break; @@ -678,7 +667,6 @@ static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb) u8 host_pref_rx_fc[3] = {3, 2, 1}, host_req_tx_fc[3] = {3, 2, 1}; u8 remote_pref_rx_fc[3], remote_req_tx_fc[3]; u8 negotiated_rx_fc, negotiated_tx_fc; - int ret; pipe_get_flow_info(sk, skb, remote_pref_rx_fc, remote_req_tx_fc); @@ -697,12 +685,7 @@ static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb) pn->tx_fc = negotiated_tx_fc; sk->sk_state_change(sk); - ret = pipe_handler_send_created_ind(sk, - PNS_PIPE_CREATED_IND_UTID, - PNS_PIPE_CREATED_IND - ); - - return ret; + return pipe_handler_send_created_ind(sk, PNS_PIPE_CREATED_IND); } #endif @@ -943,9 +926,7 @@ static void pep_sock_close(struct sock *sk, long timeout) pipe_do_remove(sk); #else /* send pep disconnect request */ - pipe_handler_send_req(sk, - PNS_PEP_DISCONNECT_UTID, PNS_PEP_DISCONNECT_REQ, - GFP_KERNEL); + pipe_handler_send_req(sk, PNS_PEP_DISCONNECT_REQ, GFP_KERNEL); sk->sk_state = TCP_CLOSE; #endif } @@ -1034,9 +1015,7 @@ static int pep_sock_connect(struct sock *sk, struct sockaddr *addr, int len) pn->pn_sk.dobject = pn_sockaddr_get_object(spn); pn->pn_sk.resource = pn_sockaddr_get_resource(spn); - return pipe_handler_send_req(sk, - PNS_PEP_CONNECT_UTID, PNS_PEP_CONNECT_REQ, - GFP_ATOMIC); + return pipe_handler_send_req(sk, PNS_PEP_CONNECT_REQ, GFP_KERNEL); } #endif -- cgit v1.2.3 From 8f44fcc72a454c5eb7cbc138bd53f0963f23e87f Mon Sep 17 00:00:00 2001 From: Rémi Denis-Courmont Date: Thu, 24 Feb 2011 23:15:01 +0000 Subject: Phonet: fix flawed "SYN/ACK" logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Do not fail if the peer supports more or less than 3 algorithms. * Ignore unknown congestion control algorithms instead of failing. * Simplify congestion algorithm negotiation (largest is best). * Do not use a static buffer. * Fix off-by-two read overflow. * Avoid extra memory copy (in addition to skb_copy_bits()). The previous code really made no sense. Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- include/net/phonet/pep.h | 1 + net/phonet/pep.c | 125 ++++++++++++++++++----------------------------- 2 files changed, 48 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h index 37f23dc05de8..38eed1be6ffd 100644 --- a/include/net/phonet/pep.h +++ b/include/net/phonet/pep.h @@ -154,6 +154,7 @@ enum { PN_LEGACY_FLOW_CONTROL, PN_ONE_CREDIT_FLOW_CONTROL, PN_MULTI_CREDIT_FLOW_CONTROL, + PN_MAX_FLOW_CONTROL, }; #define pn_flow_safe(fc) ((fc) >> 1) diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 0ecab59963e0..b8c31fc928e1 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -108,70 +108,6 @@ static int pep_reply(struct sock *sk, struct sk_buff *oskb, #define PAD 0x00 #ifdef CONFIG_PHONET_PIPECTRLR -static u8 pipe_negotiate_fc(u8 *host_fc, u8 *remote_fc, int len) -{ - int i, j; - u8 base_fc, final_fc; - - for (i = 0; i < len; i++) { - base_fc = host_fc[i]; - for (j = 0; j < len; j++) { - if (remote_fc[j] == base_fc) { - final_fc = base_fc; - goto done; - } - } - } - return -EINVAL; - -done: - return final_fc; - -} - -static int pipe_get_flow_info(struct sock *sk, struct sk_buff *skb, - u8 *pref_rx_fc, u8 *req_tx_fc) -{ - struct pnpipehdr *hdr; - u8 n_sb; - - if (!pskb_may_pull(skb, sizeof(*hdr) + 4)) - return -EINVAL; - - hdr = pnp_hdr(skb); - n_sb = hdr->data[4]; - - __skb_pull(skb, sizeof(*hdr) + 4); - while (n_sb > 0) { - u8 type, buf[3], len = sizeof(buf); - u8 *data = pep_get_sb(skb, &type, &len, buf); - - if (data == NULL) - return -EINVAL; - - switch (type) { - case PN_PIPE_SB_REQUIRED_FC_TX: - if (len < 3 || (data[2] | data[3] | data[4]) > 3) - break; - req_tx_fc[0] = data[2]; - req_tx_fc[1] = data[3]; - req_tx_fc[2] = data[4]; - break; - - case PN_PIPE_SB_PREFERRED_FC_RX: - if (len < 3 || (data[2] | data[3] | data[4]) > 3) - break; - pref_rx_fc[0] = data[2]; - pref_rx_fc[1] = data[3]; - pref_rx_fc[2] = data[4]; - break; - - } - n_sb--; - } - return 0; -} - static int pipe_handler_send_req(struct sock *sk, u8 msg_id, gfp_t priority) { int len; @@ -661,28 +597,61 @@ static void pipe_destruct(struct sock *sk) } #ifdef CONFIG_PHONET_PIPECTRLR +static u8 pipe_negotiate_fc(const u8 *fcs, unsigned n) +{ + unsigned i; + u8 final_fc = PN_NO_FLOW_CONTROL; + + for (i = 0; i < n; i++) { + u8 fc = fcs[i]; + + if (fc > final_fc && fc < PN_MAX_FLOW_CONTROL) + final_fc = fc; + } + return final_fc; +} + static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb) { struct pep_sock *pn = pep_sk(sk); - u8 host_pref_rx_fc[3] = {3, 2, 1}, host_req_tx_fc[3] = {3, 2, 1}; - u8 remote_pref_rx_fc[3], remote_req_tx_fc[3]; - u8 negotiated_rx_fc, negotiated_tx_fc; - - pipe_get_flow_info(sk, skb, remote_pref_rx_fc, - remote_req_tx_fc); - negotiated_tx_fc = pipe_negotiate_fc(remote_req_tx_fc, - host_pref_rx_fc, - sizeof(host_pref_rx_fc)); - negotiated_rx_fc = pipe_negotiate_fc(host_req_tx_fc, - remote_pref_rx_fc, - sizeof(host_pref_rx_fc)); + struct pnpipehdr *hdr; + u8 n_sb; + + if (!pskb_pull(skb, sizeof(*hdr) + 4)) + return -EINVAL; + + hdr = pnp_hdr(skb); + + /* Parse sub-blocks */ + n_sb = hdr->data[4]; + while (n_sb > 0) { + u8 type, buf[6], len = sizeof(buf); + const u8 *data = pep_get_sb(skb, &type, &len, buf); + + if (data == NULL) + return -EINVAL; + + switch (type) { + case PN_PIPE_SB_REQUIRED_FC_TX: + if (len < 2 || len < data[0]) + break; + pn->tx_fc = pipe_negotiate_fc(data + 2, len - 2); + break; + + case PN_PIPE_SB_PREFERRED_FC_RX: + if (len < 2 || len < data[0]) + break; + pn->rx_fc = pipe_negotiate_fc(data + 2, len - 2); + break; + + } + n_sb--; + } sk->sk_state = TCP_SYN_RECV; sk->sk_backlog_rcv = pipe_do_rcv; sk->sk_destruct = pipe_destruct; pn->rx_credits = 0; - pn->rx_fc = negotiated_rx_fc; - pn->tx_fc = negotiated_tx_fc; sk->sk_state_change(sk); return pipe_handler_send_created_ind(sk, PNS_PIPE_CREATED_IND); -- cgit v1.2.3 From b5faba21a6805c33b40e258d36f57997ee1de131 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 23 Feb 2011 23:52:13 +0000 Subject: genirq: Prepare the handling of shared oneshot interrupts For level type interrupts we need to track how many threads are on flight to avoid useless interrupt storms when not all thread handlers have finished yet. Keep track of the woken threads and only unmask when there are no more threads in flight. Yes, I'm lazy and using a bitfield. But not only because I'm lazy, the main reason is that it's way simpler than using a refcount. A refcount based solution would need to keep track of various things like crashing the irq thread, spurious interrupts coming in, disables/enables, free_irq() and some more. The bitfield keeps the tracking simple and makes things just work. It's also nicely confined to the thread code pathes and does not require additional checks all over the place. Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra LKML-Reference: <20110223234956.388095876@linutronix.de> --- include/linux/interrupt.h | 2 ++ include/linux/irqdesc.h | 2 ++ kernel/irq/handle.c | 76 +++++++++++++++++++++++++++++++++++++++-------- kernel/irq/manage.c | 54 +++++++++++++++++++++++++++++---- 4 files changed, 115 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 8da6643e39a6..e116fef274cd 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -99,6 +99,7 @@ typedef irqreturn_t (*irq_handler_t)(int, void *); * @thread_fn: interupt handler function for threaded interrupts * @thread: thread pointer for threaded interrupts * @thread_flags: flags related to @thread + * @thread_mask: bitmask for keeping track of @thread activity */ struct irqaction { irq_handler_t handler; @@ -109,6 +110,7 @@ struct irqaction { irq_handler_t thread_fn; struct task_struct *thread; unsigned long thread_flags; + unsigned long thread_mask; const char *name; struct proc_dir_entry *dir; } ____cacheline_internodealigned_in_smp; diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index 2f87d6441302..9eb9cd313052 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h @@ -28,6 +28,7 @@ struct timer_rand_state; * @lock: locking for SMP * @affinity_notify: context for notification of affinity changes * @pending_mask: pending rebalanced interrupts + * @threads_oneshot: bitfield to handle shared oneshot threads * @threads_active: number of irqaction threads currently running * @wait_for_threads: wait queue for sync_irq to wait for threaded handlers * @dir: /proc/irq/ procfs entry @@ -86,6 +87,7 @@ struct irq_desc { cpumask_var_t pending_mask; #endif #endif + unsigned long threads_oneshot; atomic_t threads_active; wait_queue_head_t wait_for_threads; #ifdef CONFIG_PROC_FS diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c index b110c835e070..517561fc7317 100644 --- a/kernel/irq/handle.c +++ b/kernel/irq/handle.c @@ -51,6 +51,68 @@ static void warn_no_thread(unsigned int irq, struct irqaction *action) "but no thread function available.", irq, action->name); } +static void irq_wake_thread(struct irq_desc *desc, struct irqaction *action) +{ + /* + * Wake up the handler thread for this action. In case the + * thread crashed and was killed we just pretend that we + * handled the interrupt. The hardirq handler has disabled the + * device interrupt, so no irq storm is lurking. If the + * RUNTHREAD bit is already set, nothing to do. + */ + if (test_bit(IRQTF_DIED, &action->thread_flags) || + test_and_set_bit(IRQTF_RUNTHREAD, &action->thread_flags)) + return; + + /* + * It's safe to OR the mask lockless here. We have only two + * places which write to threads_oneshot: This code and the + * irq thread. + * + * This code is the hard irq context and can never run on two + * cpus in parallel. If it ever does we have more serious + * problems than this bitmask. + * + * The irq threads of this irq which clear their "running" bit + * in threads_oneshot are serialized via desc->lock against + * each other and they are serialized against this code by + * IRQS_INPROGRESS. + * + * Hard irq handler: + * + * spin_lock(desc->lock); + * desc->state |= IRQS_INPROGRESS; + * spin_unlock(desc->lock); + * set_bit(IRQTF_RUNTHREAD, &action->thread_flags); + * desc->threads_oneshot |= mask; + * spin_lock(desc->lock); + * desc->state &= ~IRQS_INPROGRESS; + * spin_unlock(desc->lock); + * + * irq thread: + * + * again: + * spin_lock(desc->lock); + * if (desc->state & IRQS_INPROGRESS) { + * spin_unlock(desc->lock); + * while(desc->state & IRQS_INPROGRESS) + * cpu_relax(); + * goto again; + * } + * if (!test_bit(IRQTF_RUNTHREAD, &action->thread_flags)) + * desc->threads_oneshot &= ~mask; + * spin_unlock(desc->lock); + * + * So either the thread waits for us to clear IRQS_INPROGRESS + * or we are waiting in the flow handler for desc->lock to be + * released before we reach this point. The thread also checks + * IRQTF_RUNTHREAD under desc->lock. If set it leaves + * threads_oneshot untouched and runs the thread another time. + */ + desc->threads_oneshot |= action->thread_mask; + wake_up_process(action->thread); +} + irqreturn_t handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action) { @@ -85,19 +147,7 @@ handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action) break; } - /* - * Wake up the handler thread for this - * action. In case the thread crashed and was - * killed we just pretend that we handled the - * interrupt. The hardirq handler above has - * disabled the device interrupt, so no irq - * storm is lurking. - */ - if (likely(!test_bit(IRQTF_DIED, - &action->thread_flags))) { - set_bit(IRQTF_RUNTHREAD, &action->thread_flags); - wake_up_process(action->thread); - } + irq_wake_thread(desc, action); /* Fall through to add to randomness */ case IRQ_HANDLED: diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 01f8a9519e63..2301de19ac7d 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -617,8 +617,11 @@ static int irq_wait_for_interrupt(struct irqaction *action) * handler finished. unmask if the interrupt has not been disabled and * is marked MASKED. */ -static void irq_finalize_oneshot(unsigned int irq, struct irq_desc *desc) +static void irq_finalize_oneshot(struct irq_desc *desc, + struct irqaction *action, bool force) { + if (!(desc->istate & IRQS_ONESHOT)) + return; again: chip_bus_lock(desc); raw_spin_lock_irq(&desc->lock); @@ -631,6 +634,11 @@ again: * on the other CPU. If we unmask the irq line then the * interrupt can come in again and masks the line, leaves due * to IRQS_INPROGRESS and the irq line is masked forever. + * + * This also serializes the state of shared oneshot handlers + * versus "desc->threads_onehsot |= action->thread_mask;" in + * irq_wake_thread(). See the comment there which explains the + * serialization. */ if (unlikely(desc->istate & IRQS_INPROGRESS)) { raw_spin_unlock_irq(&desc->lock); @@ -639,11 +647,23 @@ again: goto again; } - if (!(desc->istate & IRQS_DISABLED) && (desc->istate & IRQS_MASKED)) { + /* + * Now check again, whether the thread should run. Otherwise + * we would clear the threads_oneshot bit of this thread which + * was just set. + */ + if (!force && test_bit(IRQTF_RUNTHREAD, &action->thread_flags)) + goto out_unlock; + + desc->threads_oneshot &= ~action->thread_mask; + + if (!desc->threads_oneshot && !(desc->istate & IRQS_DISABLED) && + (desc->istate & IRQS_MASKED)) { irq_compat_clr_masked(desc); desc->istate &= ~IRQS_MASKED; desc->irq_data.chip->irq_unmask(&desc->irq_data); } +out_unlock: raw_spin_unlock_irq(&desc->lock); chip_bus_sync_unlock(desc); } @@ -691,7 +711,7 @@ static int irq_thread(void *data) }; struct irqaction *action = data; struct irq_desc *desc = irq_to_desc(action->irq); - int wake, oneshot = desc->istate & IRQS_ONESHOT; + int wake; sched_setscheduler(current, SCHED_FIFO, ¶m); current->irqaction = action; @@ -719,8 +739,7 @@ static int irq_thread(void *data) action->thread_fn(action->irq, action->dev_id); - if (oneshot) - irq_finalize_oneshot(action->irq, desc); + irq_finalize_oneshot(desc, action, false); } wake = atomic_dec_and_test(&desc->threads_active); @@ -729,6 +748,9 @@ static int irq_thread(void *data) wake_up(&desc->wait_for_threads); } + /* Prevent a stale desc->threads_oneshot */ + irq_finalize_oneshot(desc, action, true); + /* * Clear irqaction. Otherwise exit_irq_thread() would make * fuzz about an active irq thread going into nirvana. @@ -743,6 +765,7 @@ static int irq_thread(void *data) void exit_irq_thread(void) { struct task_struct *tsk = current; + struct irq_desc *desc; if (!tsk->irqaction) return; @@ -751,6 +774,14 @@ void exit_irq_thread(void) "exiting task \"%s\" (%d) is an active IRQ thread (irq %d)\n", tsk->comm ? tsk->comm : "", tsk->pid, tsk->irqaction->irq); + desc = irq_to_desc(tsk->irqaction->irq); + + /* + * Prevent a stale desc->threads_oneshot. Must be called + * before setting the IRQTF_DIED flag. + */ + irq_finalize_oneshot(desc, tsk->irqaction, true); + /* * Set the THREAD DIED flag to prevent further wakeups of the * soon to be gone threaded handler. @@ -767,7 +798,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) { struct irqaction *old, **old_ptr; const char *old_name = NULL; - unsigned long flags; + unsigned long flags, thread_mask = 0; int ret, nested, shared = 0; cpumask_var_t mask; @@ -865,12 +896,23 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) /* add new interrupt at end of irq queue */ do { + thread_mask |= old->thread_mask; old_ptr = &old->next; old = *old_ptr; } while (old); shared = 1; } + /* + * Setup the thread mask for this irqaction. Unlikely to have + * 32 resp 64 irqs sharing one line, but who knows. + */ + if (new->flags & IRQF_ONESHOT && thread_mask == ~0UL) { + ret = -EBUSY; + goto out_mask; + } + new->thread_mask = 1 << ffz(thread_mask); + if (!shared) { irq_chip_set_defaults(desc->irq_data.chip); -- cgit v1.2.3 From 0c4602ff88d6d6ef0ee6d228ee9acaa6448ff6f5 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 23 Feb 2011 23:52:18 +0000 Subject: genirq: Add IRQF_NO_THREAD Some low level interrupts cannot be threaded even when we force thread all interrupt handlers. Add a flag to annotate such interrupts. Add all timer interrupts to this category by default. Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra LKML-Reference: <20110223234956.578893460@linutronix.de> --- include/linux/interrupt.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index e116fef274cd..0fc3eb9397b4 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -58,6 +58,7 @@ * irq line disabled until the threaded handler has been run. * IRQF_NO_SUSPEND - Do not disable this IRQ during suspend * IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set + * IRQF_NO_THREAD - Interrupt cannot be threaded */ #define IRQF_DISABLED 0x00000020 #define IRQF_SAMPLE_RANDOM 0x00000040 @@ -70,8 +71,9 @@ #define IRQF_ONESHOT 0x00002000 #define IRQF_NO_SUSPEND 0x00004000 #define IRQF_FORCE_RESUME 0x00008000 +#define IRQF_NO_THREAD 0x00010000 -#define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND) +#define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD) /* * These values can be returned by request_any_context_irq() and -- cgit v1.2.3 From 969e3033ae7733a0af8f7742ca74cd16c0857e71 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 23 Feb 2011 15:28:18 -0500 Subject: USB: serial drivers need to use larger bulk-in buffers When a driver doesn't know how much data a device is going to send, the buffer size should be at least as big as the endpoint's maxpacket value. The serial drivers don't follow this rule; many of them request only 256-byte bulk-in buffers. As a result, they suffer overflow errors if a high-speed device wants to send a lot of data, because high-speed bulk endpoints are required to have a maxpacket size of 512. This patch (as1450) fixes the problem by using the driver's bulk_in_size value as a minimum, always allocating buffers no smaller than the endpoint's maxpacket size. Signed-off-by: Alan Stern Tested-by: Flynn Marquardt CC: [after .39-rc1 is out] Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 5 ++--- include/linux/usb/serial.h | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 546a52179bec..2ff90a9c8f47 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -911,9 +911,8 @@ int usb_serial_probe(struct usb_interface *interface, dev_err(&interface->dev, "No free urbs available\n"); goto probe_error; } - buffer_size = serial->type->bulk_in_size; - if (!buffer_size) - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = max_t(int, serial->type->bulk_in_size, + le16_to_cpu(endpoint->wMaxPacketSize)); port->bulk_in_size = buffer_size; port->bulk_in_endpointAddress = endpoint->bEndpointAddress; port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index c9049139a7a5..45f3b9db4258 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -191,7 +191,8 @@ static inline void usb_set_serial_data(struct usb_serial *serial, void *data) * @id_table: pointer to a list of usb_device_id structures that define all * of the devices this structure can support. * @num_ports: the number of different ports this device will have. - * @bulk_in_size: bytes to allocate for bulk-in buffer (0 = end-point size) + * @bulk_in_size: minimum number of bytes to allocate for bulk-in buffer + * (0 = end-point size) * @bulk_out_size: bytes to allocate for bulk-out buffer (0 = end-point size) * @calc_num_ports: pointer to a function to determine how many ports this * device has dynamically. It will be called after the probe() -- cgit v1.2.3 From c9642374d0e969e8c17f4f31cd1a2bd111634227 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 23 Feb 2011 14:38:20 -0800 Subject: USB: fix unsafe USB_SS_MAX_STREAMS() definition Macro arguments used in expressions need to be enclosed in parenthesis to avoid unpleasant surprises. Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/ch9.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index ab461948b579..34316ba05f29 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -584,7 +584,7 @@ struct usb_ss_ep_comp_descriptor { #define USB_DT_SS_EP_COMP_SIZE 6 /* Bits 4:0 of bmAttributes if this is a bulk endpoint */ -#define USB_SS_MAX_STREAMS(p) (1 << (p & 0x1f)) +#define USB_SS_MAX_STREAMS(p) (1 << ((p) & 0x1f)) /*-------------------------------------------------------------------------*/ -- cgit v1.2.3 From 93c890dbe5287d146007083021148e7318058e37 Mon Sep 17 00:00:00 2001 From: Mike Waychison Date: Tue, 22 Feb 2011 17:53:15 -0800 Subject: firmware: Add DMI entry types to the headers In preparation for the upcoming commits, introduce the DMI entry types to the headers. These type names are based on those specified in the DMTF SMBIOS specification version 2.7.1. Signed-off-by: Mike Waychison Signed-off-by: Greg Kroah-Hartman --- include/linux/dmi.h | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'include') diff --git a/include/linux/dmi.h b/include/linux/dmi.h index 90e087f8d951..f156cca25ad0 100644 --- a/include/linux/dmi.h +++ b/include/linux/dmi.h @@ -23,6 +23,53 @@ enum dmi_device_type { DMI_DEV_TYPE_DEV_ONBOARD = -3, }; +enum dmi_entry_type { + DMI_ENTRY_BIOS = 0, + DMI_ENTRY_SYSTEM, + DMI_ENTRY_BASEBOARD, + DMI_ENTRY_CHASSIS, + DMI_ENTRY_PROCESSOR, + DMI_ENTRY_MEM_CONTROLLER, + DMI_ENTRY_MEM_MODULE, + DMI_ENTRY_CACHE, + DMI_ENTRY_PORT_CONNECTOR, + DMI_ENTRY_SYSTEM_SLOT, + DMI_ENTRY_ONBOARD_DEVICE, + DMI_ENTRY_OEMSTRINGS, + DMI_ENTRY_SYSCONF, + DMI_ENTRY_BIOS_LANG, + DMI_ENTRY_GROUP_ASSOC, + DMI_ENTRY_SYSTEM_EVENT_LOG, + DMI_ENTRY_PHYS_MEM_ARRAY, + DMI_ENTRY_MEM_DEVICE, + DMI_ENTRY_32_MEM_ERROR, + DMI_ENTRY_MEM_ARRAY_MAPPED_ADDR, + DMI_ENTRY_MEM_DEV_MAPPED_ADDR, + DMI_ENTRY_BUILTIN_POINTING_DEV, + DMI_ENTRY_PORTABLE_BATTERY, + DMI_ENTRY_SYSTEM_RESET, + DMI_ENTRY_HW_SECURITY, + DMI_ENTRY_SYSTEM_POWER_CONTROLS, + DMI_ENTRY_VOLTAGE_PROBE, + DMI_ENTRY_COOLING_DEV, + DMI_ENTRY_TEMP_PROBE, + DMI_ENTRY_ELECTRICAL_CURRENT_PROBE, + DMI_ENTRY_OOB_REMOTE_ACCESS, + DMI_ENTRY_BIS_ENTRY, + DMI_ENTRY_SYSTEM_BOOT, + DMI_ENTRY_MGMT_DEV, + DMI_ENTRY_MGMT_DEV_COMPONENT, + DMI_ENTRY_MGMT_DEV_THRES, + DMI_ENTRY_MEM_CHANNEL, + DMI_ENTRY_IPMI_DEV, + DMI_ENTRY_SYS_POWER_SUPPLY, + DMI_ENTRY_ADDITIONAL, + DMI_ENTRY_ONBOARD_DEV_EXT, + DMI_ENTRY_MGMT_CONTROLLER_HOST, + DMI_ENTRY_INACTIVE = 126, + DMI_ENTRY_END_OF_TABLE = 127, +}; + struct dmi_header { u8 type; u8 length; -- cgit v1.2.3 From 7bb4568372856688bc070917265bce0b88bb7d4d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 24 Feb 2011 14:42:06 +0100 Subject: mac80211: make tx() operation return void The return value of the tx operation is commonly misused by drivers, leading to errors. All drivers will drop frames if they fail to TX the frame, and they must also properly manage the queues (if they didn't, mac80211 would already warn). Removing the ability for drivers to return a BUSY value also allows significant cleanups of the TX TX handling code in mac80211. Note that this also fixes a bug in ath9k_htc, the old "return -1" there was wrong. Signed-off-by: Johannes Berg Tested-by: Sedat Dilek [ath5k] Acked-by: Gertjan van Wingerde [rt2x00] Acked-by: Larry Finger [b43, rtl8187, rtlwifi] Acked-by: Luciano Coelho [wl12xx] Signed-off-by: John W. Linville --- drivers/net/wireless/adm8211.c | 4 +- drivers/net/wireless/at76c50x-usb.c | 7 +- drivers/net/wireless/ath/ar9170/ar9170.h | 2 +- drivers/net/wireless/ath/ar9170/main.c | 5 +- drivers/net/wireless/ath/ath5k/ath5k.h | 4 +- drivers/net/wireless/ath/ath5k/base.c | 5 +- drivers/net/wireless/ath/ath5k/mac80211-ops.c | 6 +- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 7 +- drivers/net/wireless/ath/ath9k/main.c | 6 +- drivers/net/wireless/ath/carl9170/carl9170.h | 2 +- drivers/net/wireless/ath/carl9170/tx.c | 5 +- drivers/net/wireless/b43/main.c | 6 +- drivers/net/wireless/b43legacy/main.c | 5 +- drivers/net/wireless/iwlegacy/iwl-4965.h | 2 +- drivers/net/wireless/iwlegacy/iwl3945-base.c | 3 +- drivers/net/wireless/iwlegacy/iwl4965-base.c | 3 +- drivers/net/wireless/iwlwifi/iwl-agn.c | 3 +- drivers/net/wireless/iwlwifi/iwl-agn.h | 2 +- drivers/net/wireless/libertas_tf/main.c | 3 +- drivers/net/wireless/mac80211_hwsim.c | 5 +- drivers/net/wireless/mwl8k.c | 15 +-- drivers/net/wireless/p54/lmac.h | 2 +- drivers/net/wireless/p54/main.c | 2 +- drivers/net/wireless/p54/txrx.c | 11 +- drivers/net/wireless/rt2x00/rt2x00.h | 2 +- drivers/net/wireless/rt2x00/rt2x00mac.c | 5 +- drivers/net/wireless/rtl818x/rtl8180/dev.c | 8 +- drivers/net/wireless/rtl818x/rtl8187/dev.c | 6 +- drivers/net/wireless/rtlwifi/core.c | 5 +- drivers/net/wireless/wl1251/main.c | 4 +- drivers/net/wireless/wl12xx/main.c | 4 +- drivers/net/wireless/zd1211rw/zd_mac.c | 5 +- drivers/staging/brcm80211/sys/wl_mac80211.c | 28 +---- drivers/staging/winbond/wbusb.c | 7 +- include/net/mac80211.h | 2 +- net/mac80211/driver-ops.h | 4 +- net/mac80211/tx.c | 164 +++++++++----------------- 37 files changed, 122 insertions(+), 237 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index f9aa1bc0a947..afe2cbc6cb24 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1658,7 +1658,7 @@ static void adm8211_tx_raw(struct ieee80211_hw *dev, struct sk_buff *skb, } /* Put adm8211_tx_hdr on skb and transmit */ -static int adm8211_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +static void adm8211_tx(struct ieee80211_hw *dev, struct sk_buff *skb) { struct adm8211_tx_hdr *txhdr; size_t payload_len, hdrlen; @@ -1707,8 +1707,6 @@ static int adm8211_tx(struct ieee80211_hw *dev, struct sk_buff *skb) txhdr->retry_limit = info->control.rates[0].count; adm8211_tx_raw(dev, skb, plcp_signal, hdrlen); - - return NETDEV_TX_OK; } static int adm8211_alloc_rings(struct ieee80211_hw *dev) diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index 1476314afa8a..10b4393d7fe0 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -1728,7 +1728,7 @@ static void at76_mac80211_tx_callback(struct urb *urb) ieee80211_wake_queues(priv->hw); } -static int at76_mac80211_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void at76_mac80211_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct at76_priv *priv = hw->priv; struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer; @@ -1741,7 +1741,8 @@ static int at76_mac80211_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (priv->tx_urb->status == -EINPROGRESS) { wiphy_err(priv->hw->wiphy, "%s called while tx urb is pending\n", __func__); - return NETDEV_TX_BUSY; + dev_kfree_skb_any(skb); + return; } /* The following code lines are important when the device is going to @@ -1795,8 +1796,6 @@ static int at76_mac80211_tx(struct ieee80211_hw *hw, struct sk_buff *skb) priv->tx_urb, priv->tx_urb->hcpriv, priv->tx_urb->complete); } - - return 0; } static int at76_mac80211_start(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/ath/ar9170/ar9170.h b/drivers/net/wireless/ath/ar9170/ar9170.h index 4f845f80c098..371e4ce49528 100644 --- a/drivers/net/wireless/ath/ar9170/ar9170.h +++ b/drivers/net/wireless/ath/ar9170/ar9170.h @@ -224,7 +224,7 @@ void ar9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len); int ar9170_nag_limiter(struct ar9170 *ar); /* MAC */ -int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +void ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb); int ar9170_init_mac(struct ar9170 *ar); int ar9170_set_qos(struct ar9170 *ar); int ar9170_update_multicast(struct ar9170 *ar, const u64 mc_hast); diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c index a9111e1161fd..b761fec0d721 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -1475,7 +1475,7 @@ static void ar9170_tx(struct ar9170 *ar) msecs_to_jiffies(AR9170_JANITOR_DELAY)); } -int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +void ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ar9170 *ar = hw->priv; struct ieee80211_tx_info *info; @@ -1493,11 +1493,10 @@ int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) skb_queue_tail(&ar->tx_pending[queue], skb); ar9170_tx(ar); - return NETDEV_TX_OK; + return; err_free: dev_kfree_skb_any(skb); - return NETDEV_TX_OK; } static int ar9170_op_add_interface(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 70abb61e9eff..0ee54eb333de 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1164,8 +1164,8 @@ struct ath5k_txq; void set_beacon_filter(struct ieee80211_hw *hw, bool enable); bool ath_any_vif_assoc(struct ath5k_softc *sc); -int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ath5k_txq *txq); +void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ath5k_txq *txq); int ath5k_init_hw(struct ath5k_softc *sc); int ath5k_stop_hw(struct ath5k_softc *sc); void ath5k_mode_setup(struct ath5k_softc *sc, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 80d9cf0c4cd2..91411e9b4b68 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1518,7 +1518,7 @@ unlock: * TX Handling * \*************/ -int +void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath5k_txq *txq) { @@ -1567,11 +1567,10 @@ ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, spin_unlock_irqrestore(&sc->txbuflock, flags); goto drop_packet; } - return NETDEV_TX_OK; + return; drop_packet: dev_kfree_skb_any(skb); - return NETDEV_TX_OK; } static void diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index a60a726a140c..1fbe3c0b9f08 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -52,7 +52,7 @@ extern int ath5k_modparam_nohwcrypt; * Mac80211 functions * \********************/ -static int +static void ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ath5k_softc *sc = hw->priv; @@ -60,10 +60,10 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (WARN_ON(qnum >= sc->ah->ah_capabilities.cap_queues.q_tx_num)) { dev_kfree_skb_any(skb); - return 0; + return; } - return ath5k_tx_queue(hw, skb, &sc->txqs[qnum]); + ath5k_tx_queue(hw, skb, &sc->txqs[qnum]); } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 7367d6c1c649..71adab34006c 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1036,7 +1036,7 @@ set_timer: /* mac80211 Callbacks */ /**********************/ -static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_hdr *hdr; struct ath9k_htc_priv *priv = hw->priv; @@ -1049,7 +1049,7 @@ static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) padsize = padpos & 3; if (padsize && skb->len > padpos) { if (skb_headroom(skb) < padsize) - return -1; + goto fail_tx; skb_push(skb, padsize); memmove(skb->data, skb->data + padsize, padpos); } @@ -1070,11 +1070,10 @@ static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) goto fail_tx; } - return 0; + return; fail_tx: dev_kfree_skb_any(skb); - return 0; } static int ath9k_htc_start(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index a71550049d84..39a72ae80970 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1142,8 +1142,7 @@ mutex_unlock: return r; } -static int ath9k_tx(struct ieee80211_hw *hw, - struct sk_buff *skb) +static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); @@ -1200,10 +1199,9 @@ static int ath9k_tx(struct ieee80211_hw *hw, goto exit; } - return 0; + return; exit: dev_kfree_skb_any(skb); - return 0; } static void ath9k_stop(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h index 420d437f9580..c6a5fae634a0 100644 --- a/drivers/net/wireless/ath/carl9170/carl9170.h +++ b/drivers/net/wireless/ath/carl9170/carl9170.h @@ -534,7 +534,7 @@ void carl9170_rx(struct ar9170 *ar, void *buf, unsigned int len); void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len); /* TX */ -int carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +void carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb); void carl9170_tx_janitor(struct work_struct *work); void carl9170_tx_process_status(struct ar9170 *ar, const struct carl9170_rsp *cmd); diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index 6f41e21d3a1c..0ef70b6fc512 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -1339,7 +1339,7 @@ err_unlock_rcu: return false; } -int carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +void carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ar9170 *ar = hw->priv; struct ieee80211_tx_info *info; @@ -1373,12 +1373,11 @@ int carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) } carl9170_tx(ar); - return NETDEV_TX_OK; + return; err_free: ar->tx_dropped++; dev_kfree_skb_any(skb); - return NETDEV_TX_OK; } void carl9170_tx_scheduler(struct ar9170 *ar) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 22bc9f17f634..57eb5b649730 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3203,7 +3203,7 @@ static void b43_tx_work(struct work_struct *work) mutex_unlock(&wl->mutex); } -static int b43_op_tx(struct ieee80211_hw *hw, +static void b43_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct b43_wl *wl = hw_to_b43_wl(hw); @@ -3211,14 +3211,12 @@ static int b43_op_tx(struct ieee80211_hw *hw, if (unlikely(skb->len < 2 + 2 + 6)) { /* Too short, this can't be a valid frame. */ dev_kfree_skb_any(skb); - return NETDEV_TX_OK; + return; } B43_WARN_ON(skb_shinfo(skb)->nr_frags); skb_queue_tail(&wl->tx_queue, skb); ieee80211_queue_work(wl->hw, &wl->tx_work); - - return NETDEV_TX_OK; } static void b43_qos_params_upload(struct b43_wldev *dev, diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 1f11e1670bf0..c7fd73e3ad76 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -2442,8 +2442,8 @@ static int b43legacy_rng_init(struct b43legacy_wl *wl) return err; } -static int b43legacy_op_tx(struct ieee80211_hw *hw, - struct sk_buff *skb) +static void b43legacy_op_tx(struct ieee80211_hw *hw, + struct sk_buff *skb) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); struct b43legacy_wldev *dev = wl->current_dev; @@ -2466,7 +2466,6 @@ out: /* Drop the packet. */ dev_kfree_skb_any(skb); } - return NETDEV_TX_OK; } static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, u16 queue, diff --git a/drivers/net/wireless/iwlegacy/iwl-4965.h b/drivers/net/wireless/iwlegacy/iwl-4965.h index 79e206770f71..01f8163daf16 100644 --- a/drivers/net/wireless/iwlegacy/iwl-4965.h +++ b/drivers/net/wireless/iwlegacy/iwl-4965.h @@ -253,7 +253,7 @@ void iwl4965_eeprom_release_semaphore(struct iwl_priv *priv); int iwl4965_eeprom_check_version(struct iwl_priv *priv); /* mac80211 handlers (for 4965) */ -int iwl4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +void iwl4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); int iwl4965_mac_start(struct ieee80211_hw *hw); void iwl4965_mac_stop(struct ieee80211_hw *hw); void iwl4965_configure_filter(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/iwlegacy/iwl3945-base.c b/drivers/net/wireless/iwlegacy/iwl3945-base.c index ef94d161b783..a6af9817efce 100644 --- a/drivers/net/wireless/iwlegacy/iwl3945-base.c +++ b/drivers/net/wireless/iwlegacy/iwl3945-base.c @@ -3170,7 +3170,7 @@ static void iwl3945_mac_stop(struct ieee80211_hw *hw) IWL_DEBUG_MAC80211(priv, "leave\n"); } -static int iwl3945_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void iwl3945_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct iwl_priv *priv = hw->priv; @@ -3183,7 +3183,6 @@ static int iwl3945_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) dev_kfree_skb_any(skb); IWL_DEBUG_MAC80211(priv, "leave\n"); - return NETDEV_TX_OK; } void iwl3945_config_ap(struct iwl_priv *priv) diff --git a/drivers/net/wireless/iwlegacy/iwl4965-base.c b/drivers/net/wireless/iwlegacy/iwl4965-base.c index c0e07685059a..4d53d0ff5fc7 100644 --- a/drivers/net/wireless/iwlegacy/iwl4965-base.c +++ b/drivers/net/wireless/iwlegacy/iwl4965-base.c @@ -2631,7 +2631,7 @@ void iwl4965_mac_stop(struct ieee80211_hw *hw) IWL_DEBUG_MAC80211(priv, "leave\n"); } -int iwl4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +void iwl4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct iwl_priv *priv = hw->priv; @@ -2644,7 +2644,6 @@ int iwl4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) dev_kfree_skb_any(skb); IWL_DEBUG_MACDUMP(priv, "leave\n"); - return NETDEV_TX_OK; } void iwl4965_mac_update_tkip_key(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index d08fa938501a..8cdbd8c4027f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -3330,7 +3330,7 @@ void iwlagn_mac_stop(struct ieee80211_hw *hw) IWL_DEBUG_MAC80211(priv, "leave\n"); } -int iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct iwl_priv *priv = hw->priv; @@ -3343,7 +3343,6 @@ int iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) dev_kfree_skb_any(skb); IWL_DEBUG_MACDUMP(priv, "leave\n"); - return NETDEV_TX_OK; } void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h index d00e1ea50a8d..88c7210dfb91 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn.h @@ -356,7 +356,7 @@ iwlagn_remove_notification(struct iwl_priv *priv, struct iwl_notification_wait *wait_entry); /* mac80211 handlers (for 4965) */ -int iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); int iwlagn_mac_start(struct ieee80211_hw *hw); void iwlagn_mac_stop(struct ieee80211_hw *hw); void iwlagn_configure_filter(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c index 9278b3c8ee30..d4005081f1df 100644 --- a/drivers/net/wireless/libertas_tf/main.c +++ b/drivers/net/wireless/libertas_tf/main.c @@ -225,7 +225,7 @@ static void lbtf_free_adapter(struct lbtf_private *priv) lbtf_deb_leave(LBTF_DEB_MAIN); } -static int lbtf_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void lbtf_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct lbtf_private *priv = hw->priv; @@ -236,7 +236,6 @@ static int lbtf_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) * there are no buffered multicast frames to send */ ieee80211_stop_queues(priv->hw); - return NETDEV_TX_OK; } static void lbtf_tx_work(struct work_struct *work) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 5d39b2840584..56f439d58013 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -541,7 +541,7 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, } -static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { bool ack; struct ieee80211_tx_info *txi; @@ -551,7 +551,7 @@ static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (skb->len < 10) { /* Should not happen; just a sanity check for addr1 use */ dev_kfree_skb(skb); - return NETDEV_TX_OK; + return; } ack = mac80211_hwsim_tx_frame(hw, skb); @@ -571,7 +571,6 @@ static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) && ack) txi->flags |= IEEE80211_TX_STAT_ACK; ieee80211_tx_status_irqsafe(hw, skb); - return NETDEV_TX_OK; } diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 03f2584aed12..df5959f36d0b 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1573,7 +1573,7 @@ static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index) txq->txd = NULL; } -static int +static void mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) { struct mwl8k_priv *priv = hw->priv; @@ -1635,7 +1635,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) wiphy_debug(hw->wiphy, "failed to dma map skb, dropping TX frame.\n"); dev_kfree_skb(skb); - return NETDEV_TX_OK; + return; } spin_lock_bh(&priv->tx_lock); @@ -1672,8 +1672,6 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) mwl8k_tx_start(priv); spin_unlock_bh(&priv->tx_lock); - - return NETDEV_TX_OK; } @@ -3742,22 +3740,19 @@ static void mwl8k_rx_poll(unsigned long data) /* * Core driver operations. */ -static int mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct mwl8k_priv *priv = hw->priv; int index = skb_get_queue_mapping(skb); - int rc; if (!priv->radio_on) { wiphy_debug(hw->wiphy, "dropped TX frame since radio disabled\n"); dev_kfree_skb(skb); - return NETDEV_TX_OK; + return; } - rc = mwl8k_txq_xmit(hw, index, skb); - - return rc; + mwl8k_txq_xmit(hw, index, skb); } static int mwl8k_start(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/p54/lmac.h b/drivers/net/wireless/p54/lmac.h index 5ca117e6f95b..eb581abc1079 100644 --- a/drivers/net/wireless/p54/lmac.h +++ b/drivers/net/wireless/p54/lmac.h @@ -526,7 +526,7 @@ int p54_init_leds(struct p54_common *priv); void p54_unregister_leds(struct p54_common *priv); /* xmit functions */ -int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb); +void p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb); int p54_tx_cancel(struct p54_common *priv, __le32 req_id); void p54_tx(struct p54_common *priv, struct sk_buff *skb); diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index d7a92af24dd5..356e6bb443a6 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -157,7 +157,7 @@ static int p54_beacon_update(struct p54_common *priv, * to cancel the old beacon template by hand, instead the firmware * will release the previous one through the feedback mechanism. */ - WARN_ON(p54_tx_80211(priv->hw, beacon)); + p54_tx_80211(priv->hw, beacon); priv->tsf_high32 = 0; priv->tsf_low32 = 0; diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index a408ff333920..7834c26c2954 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -696,7 +696,7 @@ static u8 p54_convert_algo(u32 cipher) } } -int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) +void p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) { struct p54_common *priv = dev->priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -717,12 +717,8 @@ int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) &hdr_flags, &aid, &burst_allowed); if (p54_tx_qos_accounting_alloc(priv, skb, queue)) { - if (!IS_QOS_QUEUE(queue)) { - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } else { - return NETDEV_TX_BUSY; - } + dev_kfree_skb_any(skb); + return; } padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3; @@ -865,5 +861,4 @@ int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) p54info->extra_len = extra_len; p54_tx(priv, skb); - return NETDEV_TX_OK; } diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 1df432c1f2c7..19453d23e90d 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -1185,7 +1185,7 @@ void rt2x00lib_rxdone(struct queue_entry *entry); /* * mac80211 handlers. */ -int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +void rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); int rt2x00mac_start(struct ieee80211_hw *hw); void rt2x00mac_stop(struct ieee80211_hw *hw); int rt2x00mac_add_interface(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 1b3edef9e3d2..c2c35838c2f3 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -99,7 +99,7 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, return retval; } -int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +void rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct rt2x00_dev *rt2x00dev = hw->priv; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); @@ -155,12 +155,11 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (rt2x00queue_threshold(queue)) rt2x00queue_pause_queue(queue); - return NETDEV_TX_OK; + return; exit_fail: ieee80211_stop_queue(rt2x00dev->hw, qid); dev_kfree_skb_any(skb); - return NETDEV_TX_OK; } EXPORT_SYMBOL_GPL(rt2x00mac_tx); diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index b85debb4f7b1..80db5cabc9b9 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -240,7 +240,7 @@ static irqreturn_t rtl8180_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int rtl8180_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +static void rtl8180_tx(struct ieee80211_hw *dev, struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; @@ -321,8 +321,6 @@ static int rtl8180_tx(struct ieee80211_hw *dev, struct sk_buff *skb) spin_unlock_irqrestore(&priv->lock, flags); rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << (prio + 4))); - - return 0; } void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam) @@ -687,7 +685,6 @@ static void rtl8180_beacon_work(struct work_struct *work) struct ieee80211_hw *dev = vif_priv->dev; struct ieee80211_mgmt *mgmt; struct sk_buff *skb; - int err = 0; /* don't overflow the tx ring */ if (ieee80211_queue_stopped(dev, 0)) @@ -708,8 +705,7 @@ static void rtl8180_beacon_work(struct work_struct *work) /* TODO: use actual beacon queue */ skb_set_queue_mapping(skb, 0); - err = rtl8180_tx(dev, skb); - WARN_ON(err); + rtl8180_tx(dev, skb); resched: /* diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c index 1f5df12cb156..c5a5e788f25f 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -227,7 +227,7 @@ static void rtl8187_tx_cb(struct urb *urb) } } -static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +static void rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) { struct rtl8187_priv *priv = dev->priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -241,7 +241,7 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { kfree_skb(skb); - return NETDEV_TX_OK; + return; } flags = skb->len; @@ -309,8 +309,6 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) kfree_skb(skb); } usb_free_urb(urb); - - return NETDEV_TX_OK; } static void rtl8187_rx_cb(struct urb *urb) diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index b0996bf8a214..059ab036b01d 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -82,7 +82,7 @@ static void rtl_op_stop(struct ieee80211_hw *hw) mutex_unlock(&rtlpriv->locks.conf_mutex); } -static int rtl_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void rtl_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); @@ -97,11 +97,10 @@ static int rtl_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) rtlpriv->intf_ops->adapter_tx(hw, skb); - return NETDEV_TX_OK; + return; err_free: dev_kfree_skb_any(skb); - return NETDEV_TX_OK; } static int rtl_op_add_interface(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/wl1251/main.c b/drivers/net/wireless/wl1251/main.c index 5a1c13878eaf..12c9e635a6d6 100644 --- a/drivers/net/wireless/wl1251/main.c +++ b/drivers/net/wireless/wl1251/main.c @@ -375,7 +375,7 @@ out: mutex_unlock(&wl->mutex); } -static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct wl1251 *wl = hw->priv; unsigned long flags; @@ -401,8 +401,6 @@ static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) wl->tx_queue_stopped = true; spin_unlock_irqrestore(&wl->wl_lock, flags); } - - return NETDEV_TX_OK; } static int wl1251_op_start(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 95aa19ae84e5..947491a1d9cc 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1034,7 +1034,7 @@ int wl1271_plt_stop(struct wl1271 *wl) return ret; } -static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct wl1271 *wl = hw->priv; unsigned long flags; @@ -1073,8 +1073,6 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags)) ieee80211_queue_work(wl->hw, &wl->tx_work); - - return NETDEV_TX_OK; } static struct notifier_block wl1271_dev_notifier = { diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 74a269ebbeb9..5037c8b2b415 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -850,7 +850,7 @@ static int fill_ctrlset(struct zd_mac *mac, * control block of the skbuff will be initialized. If necessary the incoming * mac80211 queues will be stopped. */ -static int zd_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void zd_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct zd_mac *mac = zd_hw_mac(hw); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -865,11 +865,10 @@ static int zd_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) r = zd_usb_tx(&mac->chip.usb, skb); if (r) goto fail; - return 0; + return; fail: dev_kfree_skb(skb); - return 0; } /** diff --git a/drivers/staging/brcm80211/sys/wl_mac80211.c b/drivers/staging/brcm80211/sys/wl_mac80211.c index bdd629d72a75..c83bdcc640a5 100644 --- a/drivers/staging/brcm80211/sys/wl_mac80211.c +++ b/drivers/staging/brcm80211/sys/wl_mac80211.c @@ -104,9 +104,6 @@ static int wl_request_fw(struct wl_info *wl, struct pci_dev *pdev); static void wl_release_fw(struct wl_info *wl); /* local prototypes */ -static int wl_start(struct sk_buff *skb, struct wl_info *wl); -static int wl_start_int(struct wl_info *wl, struct ieee80211_hw *hw, - struct sk_buff *skb); static void wl_dpc(unsigned long data); MODULE_AUTHOR("Broadcom Corporation"); @@ -135,7 +132,6 @@ module_param(phymsglevel, int, 0); #define HW_TO_WL(hw) (hw->priv) #define WL_TO_HW(wl) (wl->pub->ieee_hw) -static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb); static int wl_ops_start(struct ieee80211_hw *hw); static void wl_ops_stop(struct ieee80211_hw *hw); static int wl_ops_add_interface(struct ieee80211_hw *hw, @@ -173,20 +169,18 @@ static int wl_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn); -static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { - int status; struct wl_info *wl = hw->priv; WL_LOCK(wl); if (!wl->pub->up) { WL_ERROR("ops->tx called while down\n"); - status = -ENETDOWN; + kfree_skb(skb); goto done; } - status = wl_start(skb, wl); + wlc_sendpkt_mac80211(wl->wlc, skb, hw); done: WL_UNLOCK(wl); - return status; } static int wl_ops_start(struct ieee80211_hw *hw) @@ -1316,22 +1310,6 @@ void wl_free(struct wl_info *wl) osl_detach(osh); } -/* transmit a packet */ -static int BCMFASTPATH wl_start(struct sk_buff *skb, struct wl_info *wl) -{ - if (!wl) - return -ENETDOWN; - - return wl_start_int(wl, WL_TO_HW(wl), skb); -} - -static int BCMFASTPATH -wl_start_int(struct wl_info *wl, struct ieee80211_hw *hw, struct sk_buff *skb) -{ - wlc_sendpkt_mac80211(wl->wlc, skb, hw); - return NETDEV_TX_OK; -} - void wl_txflowcontrol(struct wl_info *wl, struct wl_if *wlif, bool state, int prio) { diff --git a/drivers/staging/winbond/wbusb.c b/drivers/staging/winbond/wbusb.c index 2163d60c2eaf..3724e1e67ec2 100644 --- a/drivers/staging/winbond/wbusb.c +++ b/drivers/staging/winbond/wbusb.c @@ -118,13 +118,14 @@ static void wbsoft_configure_filter(struct ieee80211_hw *dev, *total_flags = new_flags; } -static int wbsoft_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +static void wbsoft_tx(struct ieee80211_hw *dev, struct sk_buff *skb) { struct wbsoft_priv *priv = dev->priv; if (priv->sMlmeFrame.IsInUsed != PACKET_FREE_TO_USE) { priv->sMlmeFrame.wNumTxMMPDUDiscarded++; - return NETDEV_TX_BUSY; + kfree_skb(skb); + return; } priv->sMlmeFrame.IsInUsed = PACKET_COME_FROM_MLME; @@ -140,8 +141,6 @@ static int wbsoft_tx(struct ieee80211_hw *dev, struct sk_buff *skb) */ Mds_Tx(priv); - - return NETDEV_TX_OK; } static int wbsoft_start(struct ieee80211_hw *dev) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a13c8d8fca5c..96cc7ed35169 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1801,7 +1801,7 @@ enum ieee80211_ampdu_mlme_action { * aborted before it expires. This callback may sleep. */ struct ieee80211_ops { - int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); + void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); int (*start)(struct ieee80211_hw *hw); void (*stop)(struct ieee80211_hw *hw); int (*add_interface)(struct ieee80211_hw *hw, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 78af32d4bc58..32f05c1abbaf 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -5,9 +5,9 @@ #include "ieee80211_i.h" #include "driver-trace.h" -static inline int drv_tx(struct ieee80211_local *local, struct sk_buff *skb) +static inline void drv_tx(struct ieee80211_local *local, struct sk_buff *skb) { - return local->ops->tx(&local->hw, skb); + local->ops->tx(&local->hw, skb); } static inline int drv_start(struct ieee80211_local *local) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 34edf7f22b0e..081dcaf6577b 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -33,10 +33,6 @@ #include "wme.h" #include "rate.h" -#define IEEE80211_TX_OK 0 -#define IEEE80211_TX_AGAIN 1 -#define IEEE80211_TX_PENDING 2 - /* misc utils */ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr, @@ -1285,16 +1281,17 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, return TX_CONTINUE; } -static int __ieee80211_tx(struct ieee80211_local *local, - struct sk_buff **skbp, - struct sta_info *sta, - bool txpending) +/* + * Returns false if the frame couldn't be transmitted but was queued instead. + */ +static bool __ieee80211_tx(struct ieee80211_local *local, struct sk_buff **skbp, + struct sta_info *sta, bool txpending) { struct sk_buff *skb = *skbp, *next; struct ieee80211_tx_info *info; struct ieee80211_sub_if_data *sdata; unsigned long flags; - int ret, len; + int len; bool fragm = false; while (skb) { @@ -1302,13 +1299,37 @@ static int __ieee80211_tx(struct ieee80211_local *local, __le16 fc; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - ret = IEEE80211_TX_OK; if (local->queue_stop_reasons[q] || - (!txpending && !skb_queue_empty(&local->pending[q]))) - ret = IEEE80211_TX_PENDING; + (!txpending && !skb_queue_empty(&local->pending[q]))) { + /* + * Since queue is stopped, queue up frames for later + * transmission from the tx-pending tasklet when the + * queue is woken again. + */ + + do { + next = skb->next; + skb->next = NULL; + /* + * NB: If txpending is true, next must already + * be NULL since we must've gone through this + * loop before already; therefore we can just + * queue the frame to the head without worrying + * about reordering of fragments. + */ + if (unlikely(txpending)) + __skb_queue_head(&local->pending[q], + skb); + else + __skb_queue_tail(&local->pending[q], + skb); + } while ((skb = next)); + + spin_unlock_irqrestore(&local->queue_stop_reason_lock, + flags); + return false; + } spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); - if (ret != IEEE80211_TX_OK) - return ret; info = IEEE80211_SKB_CB(skb); @@ -1343,15 +1364,7 @@ static int __ieee80211_tx(struct ieee80211_local *local, info->control.sta = NULL; fc = ((struct ieee80211_hdr *)skb->data)->frame_control; - ret = drv_tx(local, skb); - if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) { - dev_kfree_skb(skb); - ret = NETDEV_TX_OK; - } - if (ret != NETDEV_TX_OK) { - info->control.vif = &sdata->vif; - return IEEE80211_TX_AGAIN; - } + drv_tx(local, skb); ieee80211_tpt_led_trig_tx(local, fc, len); *skbp = skb = next; @@ -1359,7 +1372,7 @@ static int __ieee80211_tx(struct ieee80211_local *local, fragm = true; } - return IEEE80211_TX_OK; + return true; } /* @@ -1419,23 +1432,24 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) return 0; } -static void ieee80211_tx(struct ieee80211_sub_if_data *sdata, +/* + * Returns false if the frame couldn't be transmitted but was queued instead. + */ +static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, bool txpending) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_data tx; ieee80211_tx_result res_prepare; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct sk_buff *next; - unsigned long flags; - int ret, retries; u16 queue; + bool result = true; queue = skb_get_queue_mapping(skb); if (unlikely(skb->len < 10)) { dev_kfree_skb(skb); - return; + return true; } rcu_read_lock(); @@ -1445,85 +1459,19 @@ static void ieee80211_tx(struct ieee80211_sub_if_data *sdata, if (unlikely(res_prepare == TX_DROP)) { dev_kfree_skb(skb); - rcu_read_unlock(); - return; + goto out; } else if (unlikely(res_prepare == TX_QUEUED)) { - rcu_read_unlock(); - return; + goto out; } tx.channel = local->hw.conf.channel; info->band = tx.channel->band; - if (invoke_tx_handlers(&tx)) - goto out; - - retries = 0; - retry: - ret = __ieee80211_tx(local, &tx.skb, tx.sta, txpending); - switch (ret) { - case IEEE80211_TX_OK: - break; - case IEEE80211_TX_AGAIN: - /* - * Since there are no fragmented frames on A-MPDU - * queues, there's no reason for a driver to reject - * a frame there, warn and drop it. - */ - if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU)) - goto drop; - /* fall through */ - case IEEE80211_TX_PENDING: - skb = tx.skb; - - spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - - if (local->queue_stop_reasons[queue] || - !skb_queue_empty(&local->pending[queue])) { - /* - * if queue is stopped, queue up frames for later - * transmission from the tasklet - */ - do { - next = skb->next; - skb->next = NULL; - if (unlikely(txpending)) - __skb_queue_head(&local->pending[queue], - skb); - else - __skb_queue_tail(&local->pending[queue], - skb); - } while ((skb = next)); - - spin_unlock_irqrestore(&local->queue_stop_reason_lock, - flags); - } else { - /* - * otherwise retry, but this is a race condition or - * a driver bug (which we warn about if it persists) - */ - spin_unlock_irqrestore(&local->queue_stop_reason_lock, - flags); - - retries++; - if (WARN(retries > 10, "tx refused but queue active\n")) - goto drop; - goto retry; - } - } + if (!invoke_tx_handlers(&tx)) + result = __ieee80211_tx(local, &tx.skb, tx.sta, txpending); out: rcu_read_unlock(); - return; - - drop: - rcu_read_unlock(); - - skb = tx.skb; - while (skb) { - next = skb->next; - dev_kfree_skb(skb); - skb = next; - } + return result; } /* device xmit handlers */ @@ -2070,6 +2018,11 @@ void ieee80211_clear_tx_pending(struct ieee80211_local *local) skb_queue_purge(&local->pending[i]); } +/* + * Returns false if the frame couldn't be transmitted but was queued instead, + * which in this case means re-queued -- take as an indication to stop sending + * more pending frames. + */ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, struct sk_buff *skb) { @@ -2077,20 +2030,17 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata; struct sta_info *sta; struct ieee80211_hdr *hdr; - int ret; - bool result = true; + bool result; sdata = vif_to_sdata(info->control.vif); if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) { - ieee80211_tx(sdata, skb, true); + result = ieee80211_tx(sdata, skb, true); } else { hdr = (struct ieee80211_hdr *)skb->data; sta = sta_info_get(sdata, hdr->addr1); - ret = __ieee80211_tx(local, &skb, sta, true); - if (ret != IEEE80211_TX_OK) - result = false; + result = __ieee80211_tx(local, &skb, sta, true); } return result; @@ -2132,8 +2082,6 @@ void ieee80211_tx_pending(unsigned long data) flags); txok = ieee80211_tx_pending_skb(local, skb); - if (!txok) - __skb_queue_head(&local->pending[i], skb); spin_lock_irqsave(&local->queue_stop_reason_lock, flags); if (!txok) -- cgit v1.2.3 From 5f16a43617d46cf255a66f4dc193a7f5b2540aaf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 25 Feb 2011 15:36:57 +0100 Subject: mac80211: support direct offchannel TX offload For devices supported by iwlwifi sometimes off-channel transmissions need to be handled by the device completely. To support this mac80211 needs to pass the frame directly to the driver and not through the TX path as the driver needs the frame and channel information at the same time. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 10 ++++++++++ net/mac80211/cfg.c | 39 +++++++++++++++++++++++++++++++++++++++ net/mac80211/driver-ops.h | 31 +++++++++++++++++++++++++++++++ net/mac80211/driver-trace.h | 33 +++++++++++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 1 + net/mac80211/status.c | 4 ++++ 6 files changed, 118 insertions(+) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 96cc7ed35169..2b072fa99399 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1799,6 +1799,11 @@ enum ieee80211_ampdu_mlme_action { * ieee80211_remain_on_channel_expired(). This callback may sleep. * @cancel_remain_on_channel: Requests that an ongoing off-channel period is * aborted before it expires. This callback may sleep. + * @offchannel_tx: Transmit frame on another channel, wait for a response + * and return. Reliable TX status must be reported for the frame. If the + * return value is 1, then the @remain_on_channel will be used with a + * regular transmission (if supported.) + * @offchannel_tx_cancel_wait: cancel wait associated with offchannel TX */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); @@ -1878,6 +1883,11 @@ struct ieee80211_ops { enum nl80211_channel_type channel_type, int duration); int (*cancel_remain_on_channel)(struct ieee80211_hw *hw); + int (*offchannel_tx)(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int wait); + int (*offchannel_tx_cancel_wait)(struct ieee80211_hw *hw); }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 140503d4c97a..8b436c768c4e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1800,6 +1800,33 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, *cookie = (unsigned long) skb; + if (is_offchan && local->ops->offchannel_tx) { + int ret; + + IEEE80211_SKB_CB(skb)->band = chan->band; + + mutex_lock(&local->mtx); + + if (local->hw_offchan_tx_cookie) { + mutex_unlock(&local->mtx); + return -EBUSY; + } + + /* TODO: bitrate control, TX processing? */ + ret = drv_offchannel_tx(local, skb, chan, channel_type, wait); + + if (ret == 0) + local->hw_offchan_tx_cookie = *cookie; + mutex_unlock(&local->mtx); + + /* + * Allow driver to return 1 to indicate it wants to have the + * frame transmitted with a remain_on_channel + regular TX. + */ + if (ret != 1) + return ret; + } + if (is_offchan && local->ops->remain_on_channel) { unsigned int duration; int ret; @@ -1886,6 +1913,18 @@ static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, mutex_lock(&local->mtx); + if (local->ops->offchannel_tx_cancel_wait && + local->hw_offchan_tx_cookie == cookie) { + ret = drv_offchannel_tx_cancel_wait(local); + + if (!ret) + local->hw_offchan_tx_cookie = 0; + + mutex_unlock(&local->mtx); + + return ret; + } + if (local->ops->cancel_remain_on_channel) { cookie ^= 2; ret = ieee80211_cancel_remain_on_channel_hw(local, cookie); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 32f05c1abbaf..3729296f6f95 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -495,4 +495,35 @@ static inline int drv_cancel_remain_on_channel(struct ieee80211_local *local) return ret; } +static inline int drv_offchannel_tx(struct ieee80211_local *local, + struct sk_buff *skb, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int wait) +{ + int ret; + + might_sleep(); + + trace_drv_offchannel_tx(local, skb, chan, channel_type, wait); + ret = local->ops->offchannel_tx(&local->hw, skb, chan, + channel_type, wait); + trace_drv_return_int(local, ret); + + return ret; +} + +static inline int drv_offchannel_tx_cancel_wait(struct ieee80211_local *local) +{ + int ret; + + might_sleep(); + + trace_drv_offchannel_tx_cancel_wait(local); + ret = local->ops->offchannel_tx_cancel_wait(&local->hw); + trace_drv_return_int(local, ret); + + return ret; +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index e5cce19a7d65..520fe2444893 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -884,6 +884,39 @@ DEFINE_EVENT(local_only_evt, drv_cancel_remain_on_channel, TP_ARGS(local) ); +TRACE_EVENT(drv_offchannel_tx, + TP_PROTO(struct ieee80211_local *local, struct sk_buff *skb, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int wait), + + TP_ARGS(local, skb, chan, channel_type, wait), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(int, center_freq) + __field(int, channel_type) + __field(unsigned int, wait) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->center_freq = chan->center_freq; + __entry->channel_type = channel_type; + __entry->wait = wait; + ), + + TP_printk( + LOCAL_PR_FMT " freq:%dMHz, wait:%dms", + LOCAL_PR_ARG, __entry->center_freq, __entry->wait + ) +); + +DEFINE_EVENT(local_only_evt, drv_offchannel_tx_cancel_wait, + TP_PROTO(struct ieee80211_local *local), + TP_ARGS(local) +); + /* * Tracing for API calls that drivers call. */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 0a570a111a84..a40401701424 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -957,6 +957,7 @@ struct ieee80211_local { unsigned int hw_roc_duration; u32 hw_roc_cookie; bool hw_roc_for_tx; + unsigned long hw_offchan_tx_cookie; /* dummy netdev for use w/ NAPI */ struct net_device napi_dev; diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 865185127f51..b936dd29e92b 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -341,6 +341,10 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) cookie = local->hw_roc_cookie ^ 2; local->hw_roc_skb_for_status = NULL; } + + if (cookie == local->hw_offchan_tx_cookie) + local->hw_offchan_tx_cookie = 0; + cfg80211_mgmt_tx_status( skb->dev, cookie, skb->data, skb->len, !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC); -- cgit v1.2.3 From 8d32a307e4faa8b123dc8a9cd56d1a7525f69ad3 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 23 Feb 2011 23:52:23 +0000 Subject: genirq: Provide forced interrupt threading Add a commandline parameter "threadirqs" which forces all interrupts except those marked IRQF_NO_THREAD to run threaded. That's mostly a debug option to allow retrieving better debug data from crashing interrupt handlers. If "threadirqs" is not enabled on the kernel command line, then there is no impact in the interrupt hotpath. Architecture code needs to select CONFIG_IRQ_FORCED_THREADING after marking the interrupts which cant be threaded IRQF_NO_THREAD. All interrupts which have IRQF_TIMER set are implict marked IRQF_NO_THREAD. Also all PER_CPU interrupts are excluded. Forced threading hard interrupts also forces all soft interrupt handling into thread context. When enabled it might slow down things a bit, but for debugging problems in interrupt code it's a reasonable penalty as it does not immediately crash and burn the machine when an interrupt handler is buggy. Some test results on a Core2Duo machine: Cache cold run of: # time git grep irq_desc non-threaded threaded real 1m18.741s 1m19.061s user 0m1.874s 0m1.757s sys 0m5.843s 0m5.427s # iperf -c server non-threaded [ 3] 0.0-10.0 sec 1.09 GBytes 933 Mbits/sec [ 3] 0.0-10.0 sec 1.09 GBytes 934 Mbits/sec [ 3] 0.0-10.0 sec 1.09 GBytes 933 Mbits/sec threaded [ 3] 0.0-10.0 sec 1.09 GBytes 939 Mbits/sec [ 3] 0.0-10.0 sec 1.09 GBytes 934 Mbits/sec [ 3] 0.0-10.0 sec 1.09 GBytes 937 Mbits/sec Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra LKML-Reference: <20110223234956.772668648@linutronix.de> --- Documentation/kernel-parameters.txt | 4 +++ include/linux/interrupt.h | 7 ++++ kernel/irq/Kconfig | 3 ++ kernel/irq/internals.h | 2 ++ kernel/irq/manage.c | 67 ++++++++++++++++++++++++++++++++++--- kernel/softirq.c | 16 +++++++-- 6 files changed, 93 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 89835a4766a6..cac6cf9a588c 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2436,6 +2436,10 @@ and is between 256 and 4096 characters. It is defined in the file : poll all this frequency 0: no polling (default) + threadirqs [KNL] + Force threading of all interrupt handlers except those + marked explicitely IRQF_NO_THREAD. + topology= [S390] Format: {off | on} Specify if the kernel should make use of the cpu diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 0fc3eb9397b4..f8a8af108e0c 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -383,6 +383,13 @@ static inline int disable_irq_wake(unsigned int irq) } #endif /* CONFIG_GENERIC_HARDIRQS */ + +#ifdef CONFIG_IRQ_FORCED_THREADING +extern bool force_irqthreads; +#else +#define force_irqthreads (0) +#endif + #ifndef __ARCH_SET_SOFTIRQ_PENDING #define set_softirq_pending(x) (local_softirq_pending() = (x)) #define or_softirq_pending(x) (local_softirq_pending() |= (x)) diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index 355b8c7957f5..144db9dcfcde 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig @@ -38,6 +38,9 @@ config HARDIRQS_SW_RESEND config IRQ_PREFLOW_FASTEOI bool +config IRQ_FORCED_THREADING + bool + config SPARSE_IRQ bool "Support sparse irq numbering" depends on HAVE_SPARSE_IRQ diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 935bec4bfa87..6c6ec9a49027 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -27,12 +27,14 @@ extern int noirqdebug; * IRQTF_DIED - handler thread died * IRQTF_WARNED - warning "IRQ_WAKE_THREAD w/o thread_fn" has been printed * IRQTF_AFFINITY - irq thread is requested to adjust affinity + * IRQTF_FORCED_THREAD - irq action is force threaded */ enum { IRQTF_RUNTHREAD, IRQTF_DIED, IRQTF_WARNED, IRQTF_AFFINITY, + IRQTF_FORCED_THREAD, }; /* diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 58c861367300..acd599a43bfb 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -17,6 +17,17 @@ #include "internals.h" +#ifdef CONFIG_IRQ_FORCED_THREADING +__read_mostly bool force_irqthreads; + +static int __init setup_forced_irqthreads(char *arg) +{ + force_irqthreads = true; + return 0; +} +early_param("threadirqs", setup_forced_irqthreads); +#endif + /** * synchronize_irq - wait for pending IRQ handlers (on other CPUs) * @irq: interrupt number to wait for @@ -701,6 +712,32 @@ static inline void irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action) { } #endif +/* + * Interrupts which are not explicitely requested as threaded + * interrupts rely on the implicit bh/preempt disable of the hard irq + * context. So we need to disable bh here to avoid deadlocks and other + * side effects. + */ +static void +irq_forced_thread_fn(struct irq_desc *desc, struct irqaction *action) +{ + local_bh_disable(); + action->thread_fn(action->irq, action->dev_id); + irq_finalize_oneshot(desc, action, false); + local_bh_enable(); +} + +/* + * Interrupts explicitely requested as threaded interupts want to be + * preemtible - many of them need to sleep and wait for slow busses to + * complete. + */ +static void irq_thread_fn(struct irq_desc *desc, struct irqaction *action) +{ + action->thread_fn(action->irq, action->dev_id); + irq_finalize_oneshot(desc, action, false); +} + /* * Interrupt handler thread */ @@ -711,8 +748,15 @@ static int irq_thread(void *data) }; struct irqaction *action = data; struct irq_desc *desc = irq_to_desc(action->irq); + void (*handler_fn)(struct irq_desc *desc, struct irqaction *action); int wake; + if (force_irqthreads & test_bit(IRQTF_FORCED_THREAD, + &action->thread_flags)) + handler_fn = irq_forced_thread_fn; + else + handler_fn = irq_thread_fn; + sched_setscheduler(current, SCHED_FIFO, ¶m); current->irqaction = action; @@ -736,10 +780,7 @@ static int irq_thread(void *data) raw_spin_unlock_irq(&desc->lock); } else { raw_spin_unlock_irq(&desc->lock); - - action->thread_fn(action->irq, action->dev_id); - - irq_finalize_oneshot(desc, action, false); + handler_fn(desc, action); } wake = atomic_dec_and_test(&desc->threads_active); @@ -789,6 +830,22 @@ void exit_irq_thread(void) set_bit(IRQTF_DIED, &tsk->irqaction->flags); } +static void irq_setup_forced_threading(struct irqaction *new) +{ + if (!force_irqthreads) + return; + if (new->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT)) + return; + + new->flags |= IRQF_ONESHOT; + + if (!new->thread_fn) { + set_bit(IRQTF_FORCED_THREAD, &new->thread_flags); + new->thread_fn = new->handler; + new->handler = irq_default_primary_handler; + } +} + /* * Internal function to register an irqaction - typically used to * allocate special interrupts that are part of the architecture. @@ -838,6 +895,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) * dummy function which warns when called. */ new->handler = irq_nested_primary_handler; + } else { + irq_setup_forced_threading(new); } /* diff --git a/kernel/softirq.c b/kernel/softirq.c index c0490464e92f..a33fb2911248 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -311,9 +311,21 @@ void irq_enter(void) } #ifdef __ARCH_IRQ_EXIT_IRQS_DISABLED -# define invoke_softirq() __do_softirq() +static inline void invoke_softirq(void) +{ + if (!force_irqthreads) + __do_softirq(); + else + wakeup_softirqd(); +} #else -# define invoke_softirq() do_softirq() +static inline void invoke_softirq(void) +{ + if (!force_irqthreads) + do_softirq(); + else + wakeup_softirqd(); +} #endif /* -- cgit v1.2.3 From 4e51eae9cdda4bf096e73a4ebe23f8f96a17596a Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Fri, 25 Feb 2011 19:05:48 +0100 Subject: Bluetooth: Move index to common header in management interface Most mgmt commands and event are related to hci adapter. Moving index to common header allow to easily use it in command status while reporting errors. For those not related to adapter use MGMT_INDEX_NONE (0xFFFF) as index. Signed-off-by: Szymon Janc Acked-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/mgmt.h | 43 +---- net/bluetooth/mgmt.c | 407 +++++++++++++++++++------------------------ 2 files changed, 183 insertions(+), 267 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 1e63c3141a78..5fabfa886b3e 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -21,11 +21,13 @@ SOFTWARE IS DISCLAIMED. */ +#define MGMT_INDEX_NONE 0xFFFF + struct mgmt_hdr { __le16 opcode; + __le16 index; __le16 len; } __packed; -#define MGMT_HDR_SIZE 4 #define MGMT_OP_READ_VERSION 0x0001 struct mgmt_rp_read_version { @@ -40,11 +42,7 @@ struct mgmt_rp_read_index_list { } __packed; #define MGMT_OP_READ_INFO 0x0004 -struct mgmt_cp_read_info { - __le16 index; -} __packed; struct mgmt_rp_read_info { - __le16 index; __u8 type; __u8 powered; __u8 connectable; @@ -60,7 +58,6 @@ struct mgmt_rp_read_info { } __packed; struct mgmt_mode { - __le16 index; __u8 val; } __packed; @@ -74,27 +71,23 @@ struct mgmt_mode { #define MGMT_OP_ADD_UUID 0x0009 struct mgmt_cp_add_uuid { - __le16 index; __u8 uuid[16]; __u8 svc_hint; } __packed; #define MGMT_OP_REMOVE_UUID 0x000A struct mgmt_cp_remove_uuid { - __le16 index; __u8 uuid[16]; } __packed; #define MGMT_OP_SET_DEV_CLASS 0x000B struct mgmt_cp_set_dev_class { - __le16 index; __u8 major; __u8 minor; } __packed; #define MGMT_OP_SET_SERVICE_CACHE 0x000C struct mgmt_cp_set_service_cache { - __le16 index; __u8 enable; } __packed; @@ -107,7 +100,6 @@ struct mgmt_key_info { #define MGMT_OP_LOAD_KEYS 0x000D struct mgmt_cp_load_keys { - __le16 index; __u8 debug_keys; __le16 key_count; struct mgmt_key_info keys[0]; @@ -115,75 +107,60 @@ struct mgmt_cp_load_keys { #define MGMT_OP_REMOVE_KEY 0x000E struct mgmt_cp_remove_key { - __le16 index; bdaddr_t bdaddr; __u8 disconnect; } __packed; #define MGMT_OP_DISCONNECT 0x000F struct mgmt_cp_disconnect { - __le16 index; bdaddr_t bdaddr; } __packed; struct mgmt_rp_disconnect { - __le16 index; bdaddr_t bdaddr; } __packed; #define MGMT_OP_GET_CONNECTIONS 0x0010 -struct mgmt_cp_get_connections { - __le16 index; -} __packed; struct mgmt_rp_get_connections { - __le16 index; __le16 conn_count; bdaddr_t conn[0]; } __packed; #define MGMT_OP_PIN_CODE_REPLY 0x0011 struct mgmt_cp_pin_code_reply { - __le16 index; bdaddr_t bdaddr; __u8 pin_len; __u8 pin_code[16]; } __packed; struct mgmt_rp_pin_code_reply { - __le16 index; bdaddr_t bdaddr; uint8_t status; } __packed; #define MGMT_OP_PIN_CODE_NEG_REPLY 0x0012 struct mgmt_cp_pin_code_neg_reply { - __le16 index; bdaddr_t bdaddr; } __packed; #define MGMT_OP_SET_IO_CAPABILITY 0x0013 struct mgmt_cp_set_io_capability { - __le16 index; __u8 io_capability; } __packed; #define MGMT_OP_PAIR_DEVICE 0x0014 struct mgmt_cp_pair_device { - __le16 index; bdaddr_t bdaddr; __u8 io_cap; } __packed; struct mgmt_rp_pair_device { - __le16 index; bdaddr_t bdaddr; __u8 status; } __packed; #define MGMT_OP_USER_CONFIRM_REPLY 0x0015 struct mgmt_cp_user_confirm_reply { - __le16 index; bdaddr_t bdaddr; } __packed; struct mgmt_rp_user_confirm_reply { - __le16 index; bdaddr_t bdaddr; __u8 status; } __packed; @@ -204,19 +181,12 @@ struct mgmt_ev_cmd_status { #define MGMT_EV_CONTROLLER_ERROR 0x0003 struct mgmt_ev_controller_error { - __le16 index; __u8 error_code; } __packed; #define MGMT_EV_INDEX_ADDED 0x0004 -struct mgmt_ev_index_added { - __le16 index; -} __packed; #define MGMT_EV_INDEX_REMOVED 0x0005 -struct mgmt_ev_index_removed { - __le16 index; -} __packed; #define MGMT_EV_POWERED 0x0006 @@ -228,46 +198,39 @@ struct mgmt_ev_index_removed { #define MGMT_EV_NEW_KEY 0x000A struct mgmt_ev_new_key { - __le16 index; struct mgmt_key_info key; __u8 old_key_type; } __packed; #define MGMT_EV_CONNECTED 0x000B struct mgmt_ev_connected { - __le16 index; bdaddr_t bdaddr; } __packed; #define MGMT_EV_DISCONNECTED 0x000C struct mgmt_ev_disconnected { - __le16 index; bdaddr_t bdaddr; } __packed; #define MGMT_EV_CONNECT_FAILED 0x000D struct mgmt_ev_connect_failed { - __le16 index; bdaddr_t bdaddr; __u8 status; } __packed; #define MGMT_EV_PIN_CODE_REQUEST 0x000E struct mgmt_ev_pin_code_request { - __le16 index; bdaddr_t bdaddr; } __packed; #define MGMT_EV_USER_CONFIRM_REQUEST 0x000F struct mgmt_ev_user_confirm_request { - __le16 index; bdaddr_t bdaddr; __le32 value; } __packed; #define MGMT_EV_AUTH_FAILED 0x0010 struct mgmt_ev_auth_failed { - __le16 index; bdaddr_t bdaddr; __u8 status; } __packed; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 4543ede4ddf3..98c92aee6239 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -43,7 +43,7 @@ struct pending_cmd { LIST_HEAD(cmd_list); -static int cmd_status(struct sock *sk, u16 cmd, u8 status) +static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) { struct sk_buff *skb; struct mgmt_hdr *hdr; @@ -58,6 +58,7 @@ static int cmd_status(struct sock *sk, u16 cmd, u8 status) hdr = (void *) skb_put(skb, sizeof(*hdr)); hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS); + hdr->index = cpu_to_le16(index); hdr->len = cpu_to_le16(sizeof(*ev)); ev = (void *) skb_put(skb, sizeof(*ev)); @@ -70,7 +71,8 @@ static int cmd_status(struct sock *sk, u16 cmd, u8 status) return 0; } -static int cmd_complete(struct sock *sk, u16 cmd, void *rp, size_t rp_len) +static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp, + size_t rp_len) { struct sk_buff *skb; struct mgmt_hdr *hdr; @@ -85,6 +87,7 @@ static int cmd_complete(struct sock *sk, u16 cmd, void *rp, size_t rp_len) hdr = (void *) skb_put(skb, sizeof(*hdr)); hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); + hdr->index = cpu_to_le16(index); hdr->len = cpu_to_le16(sizeof(*ev) + rp_len); ev = (void *) skb_put(skb, sizeof(*ev) + rp_len); @@ -106,7 +109,8 @@ static int read_version(struct sock *sk) rp.version = MGMT_VERSION; put_unaligned_le16(MGMT_REVISION, &rp.revision); - return cmd_complete(sk, MGMT_OP_READ_VERSION, &rp, sizeof(rp)); + return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, &rp, + sizeof(rp)); } static int read_index_list(struct sock *sk) @@ -152,32 +156,24 @@ static int read_index_list(struct sock *sk) read_unlock(&hci_dev_list_lock); - err = cmd_complete(sk, MGMT_OP_READ_INDEX_LIST, rp, rp_len); + err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, rp, + rp_len); kfree(rp); return err; } -static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) +static int read_controller_info(struct sock *sk, u16 index) { struct mgmt_rp_read_info rp; - struct mgmt_cp_read_info *cp = (void *) data; struct hci_dev *hdev; - u16 dev_id; - BT_DBG("sock %p", sk); - - if (len != 2) - return cmd_status(sk, MGMT_OP_READ_INFO, EINVAL); - - dev_id = get_unaligned_le16(&cp->index); + BT_DBG("sock %p hci%u", sk, index); - BT_DBG("request for hci%u", dev_id); - - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV); + return cmd_status(sk, index, MGMT_OP_READ_INFO, ENODEV); hci_del_off_timer(hdev); @@ -185,7 +181,6 @@ static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) set_bit(HCI_MGMT, &hdev->flags); - put_unaligned_le16(hdev->id, &rp.index); rp.type = hdev->dev_type; rp.powered = test_bit(HCI_UP, &hdev->flags); @@ -210,7 +205,7 @@ static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) hci_dev_unlock_bh(hdev); hci_dev_put(hdev); - return cmd_complete(sk, MGMT_OP_READ_INFO, &rp, sizeof(rp)); + return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp)); } static void mgmt_pending_free(struct pending_cmd *cmd) @@ -296,37 +291,35 @@ static void mgmt_pending_remove(struct pending_cmd *cmd) mgmt_pending_free(cmd); } -static int set_powered(struct sock *sk, unsigned char *data, u16 len) +static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct mgmt_mode *cp; struct hci_dev *hdev; struct pending_cmd *cmd; - u16 dev_id; int err, up; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - BT_DBG("request for hci%u", dev_id); + BT_DBG("request for hci%u", index); - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_SET_POWERED, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_POWERED, ENODEV); hci_dev_lock_bh(hdev); up = test_bit(HCI_UP, &hdev->flags); if ((cp->val && up) || (!cp->val && !up)) { - err = cmd_status(sk, MGMT_OP_SET_POWERED, EALREADY); + err = cmd_status(sk, index, MGMT_OP_SET_POWERED, EALREADY); goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_POWERED, dev_id)) { - err = cmd_status(sk, MGMT_OP_SET_POWERED, EBUSY); + if (mgmt_pending_find(MGMT_OP_SET_POWERED, index)) { + err = cmd_status(sk, index, MGMT_OP_SET_POWERED, EBUSY); goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, dev_id, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, index, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -345,44 +338,43 @@ failed: return err; } -static int set_discoverable(struct sock *sk, unsigned char *data, u16 len) +static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct mgmt_mode *cp; struct hci_dev *hdev; struct pending_cmd *cmd; - u16 dev_id; u8 scan; int err; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - BT_DBG("request for hci%u", dev_id); + BT_DBG("request for hci%u", index); - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENODEV); hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, ENETDOWN); + err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENETDOWN); goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id)) { - err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EBUSY); + if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, index) || + mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, index)) { + err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EBUSY); goto failed; } if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) && test_bit(HCI_PSCAN, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EALREADY); + err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EALREADY); goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, dev_id, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, index, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -404,43 +396,42 @@ failed: return err; } -static int set_connectable(struct sock *sk, unsigned char *data, u16 len) +static int set_connectable(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct mgmt_mode *cp; struct hci_dev *hdev; struct pending_cmd *cmd; - u16 dev_id; u8 scan; int err; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - BT_DBG("request for hci%u", dev_id); + BT_DBG("request for hci%u", index); - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_SET_CONNECTABLE, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENODEV); hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, ENETDOWN); + err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENETDOWN); goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id)) { - err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, EBUSY); + if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, index) || + mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, index)) { + err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EBUSY); goto failed; } if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, EALREADY); + err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EALREADY); goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, dev_id, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, index, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -462,7 +453,8 @@ failed: return err; } -static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk) +static int mgmt_event(u16 event, u16 index, void *data, u16 data_len, + struct sock *skip_sk) { struct sk_buff *skb; struct mgmt_hdr *hdr; @@ -475,9 +467,11 @@ static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk) hdr = (void *) skb_put(skb, sizeof(*hdr)); hdr->opcode = cpu_to_le16(event); + hdr->index = cpu_to_le16(index); hdr->len = cpu_to_le16(data_len); - memcpy(skb_put(skb, data_len), data, data_len); + if (data) + memcpy(skb_put(skb, data_len), data, data_len); hci_send_to_sock(NULL, skb, skip_sk); kfree_skb(skb); @@ -489,27 +483,25 @@ static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val) { struct mgmt_mode rp; - put_unaligned_le16(index, &rp.index); rp.val = val; - return cmd_complete(sk, opcode, &rp, sizeof(rp)); + return cmd_complete(sk, index, opcode, &rp, sizeof(rp)); } -static int set_pairable(struct sock *sk, unsigned char *data, u16 len) +static int set_pairable(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct mgmt_mode *cp, ev; struct hci_dev *hdev; - u16 dev_id; int err; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - BT_DBG("request for hci%u", dev_id); + BT_DBG("request for hci%u", index); - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_SET_PAIRABLE, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, ENODEV); hci_dev_lock_bh(hdev); @@ -518,14 +510,13 @@ static int set_pairable(struct sock *sk, unsigned char *data, u16 len) else clear_bit(HCI_PAIRABLE, &hdev->flags); - err = send_mode_rsp(sk, MGMT_OP_SET_PAIRABLE, dev_id, cp->val); + err = send_mode_rsp(sk, MGMT_OP_SET_PAIRABLE, index, cp->val); if (err < 0) goto failed; - put_unaligned_le16(dev_id, &ev.index); ev.val = cp->val; - err = mgmt_event(MGMT_EV_PAIRABLE, &ev, sizeof(ev), sk); + err = mgmt_event(MGMT_EV_PAIRABLE, index, &ev, sizeof(ev), sk); failed: hci_dev_unlock_bh(hdev); @@ -567,22 +558,20 @@ static int update_class(struct hci_dev *hdev) return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); } -static int add_uuid(struct sock *sk, unsigned char *data, u16 len) +static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct mgmt_cp_add_uuid *cp; struct hci_dev *hdev; struct bt_uuid *uuid; - u16 dev_id; int err; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - BT_DBG("request for hci%u", dev_id); + BT_DBG("request for hci%u", index); - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_ADD_UUID, ENODEV); + return cmd_status(sk, index, MGMT_OP_ADD_UUID, ENODEV); hci_dev_lock_bh(hdev); @@ -601,7 +590,7 @@ static int add_uuid(struct sock *sk, unsigned char *data, u16 len) if (err < 0) goto failed; - err = cmd_complete(sk, MGMT_OP_ADD_UUID, &dev_id, sizeof(dev_id)); + err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0); failed: hci_dev_unlock_bh(hdev); @@ -610,23 +599,21 @@ failed: return err; } -static int remove_uuid(struct sock *sk, unsigned char *data, u16 len) +static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct list_head *p, *n; struct mgmt_cp_remove_uuid *cp; struct hci_dev *hdev; u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - u16 dev_id; int err, found; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - BT_DBG("request for hci%u", dev_id); + BT_DBG("request for hci%u", index); - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_REMOVE_UUID, ENODEV); + return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, ENODEV); hci_dev_lock_bh(hdev); @@ -648,7 +635,7 @@ static int remove_uuid(struct sock *sk, unsigned char *data, u16 len) } if (found == 0) { - err = cmd_status(sk, MGMT_OP_REMOVE_UUID, ENOENT); + err = cmd_status(sk, index, MGMT_OP_REMOVE_UUID, ENOENT); goto unlock; } @@ -656,7 +643,7 @@ static int remove_uuid(struct sock *sk, unsigned char *data, u16 len) if (err < 0) goto unlock; - err = cmd_complete(sk, MGMT_OP_REMOVE_UUID, &dev_id, sizeof(dev_id)); + err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0); unlock: hci_dev_unlock_bh(hdev); @@ -665,21 +652,20 @@ unlock: return err; } -static int set_dev_class(struct sock *sk, unsigned char *data, u16 len) +static int set_dev_class(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct hci_dev *hdev; struct mgmt_cp_set_dev_class *cp; - u16 dev_id; int err; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - BT_DBG("request for hci%u", dev_id); + BT_DBG("request for hci%u", index); - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_SET_DEV_CLASS, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, ENODEV); hci_dev_lock_bh(hdev); @@ -689,8 +675,7 @@ static int set_dev_class(struct sock *sk, unsigned char *data, u16 len) err = update_class(hdev); if (err == 0) - err = cmd_complete(sk, MGMT_OP_SET_DEV_CLASS, &dev_id, - sizeof(dev_id)); + err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, NULL, 0); hci_dev_unlock_bh(hdev); hci_dev_put(hdev); @@ -698,23 +683,22 @@ static int set_dev_class(struct sock *sk, unsigned char *data, u16 len) return err; } -static int set_service_cache(struct sock *sk, unsigned char *data, u16 len) +static int set_service_cache(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct hci_dev *hdev; struct mgmt_cp_set_service_cache *cp; - u16 dev_id; int err; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_SET_SERVICE_CACHE, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, ENODEV); hci_dev_lock_bh(hdev); - BT_DBG("hci%u enable %d", dev_id, cp->enable); + BT_DBG("hci%u enable %d", index, cp->enable); if (cp->enable) { set_bit(HCI_SERVICE_CACHE, &hdev->flags); @@ -725,8 +709,8 @@ static int set_service_cache(struct sock *sk, unsigned char *data, u16 len) } if (err == 0) - err = cmd_complete(sk, MGMT_OP_SET_SERVICE_CACHE, &dev_id, - sizeof(dev_id)); + err = cmd_complete(sk, index, MGMT_OP_SET_SERVICE_CACHE, NULL, + 0); hci_dev_unlock_bh(hdev); hci_dev_put(hdev); @@ -734,15 +718,14 @@ static int set_service_cache(struct sock *sk, unsigned char *data, u16 len) return err; } -static int load_keys(struct sock *sk, unsigned char *data, u16 len) +static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; struct mgmt_cp_load_keys *cp; - u16 dev_id, key_count, expected_len; + u16 key_count, expected_len; int i; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); key_count = get_unaligned_le16(&cp->key_count); expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info); @@ -752,11 +735,11 @@ static int load_keys(struct sock *sk, unsigned char *data, u16 len) return -EINVAL; } - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_LOAD_KEYS, ENODEV); + return cmd_status(sk, index, MGMT_OP_LOAD_KEYS, ENODEV); - BT_DBG("hci%u debug_keys %u key_count %u", dev_id, cp->debug_keys, + BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys, key_count); hci_dev_lock_bh(hdev); @@ -783,26 +766,24 @@ static int load_keys(struct sock *sk, unsigned char *data, u16 len) return 0; } -static int remove_key(struct sock *sk, unsigned char *data, u16 len) +static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; struct mgmt_cp_remove_key *cp; struct hci_conn *conn; - u16 dev_id; int err; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_REMOVE_KEY, ENODEV); + return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, ENODEV); hci_dev_lock_bh(hdev); err = hci_remove_link_key(hdev, &cp->bdaddr); if (err < 0) { - err = cmd_status(sk, MGMT_OP_REMOVE_KEY, -err); + err = cmd_status(sk, index, MGMT_OP_REMOVE_KEY, -err); goto unlock; } @@ -827,44 +808,42 @@ unlock: return err; } -static int disconnect(struct sock *sk, unsigned char *data, u16 len) +static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; struct mgmt_cp_disconnect *cp; struct hci_cp_disconnect dc; struct pending_cmd *cmd; struct hci_conn *conn; - u16 dev_id; int err; BT_DBG(""); cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_DISCONNECT, ENODEV); + return cmd_status(sk, index, MGMT_OP_DISCONNECT, ENODEV); hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_DISCONNECT, ENETDOWN); + err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENETDOWN); goto failed; } - if (mgmt_pending_find(MGMT_OP_DISCONNECT, dev_id)) { - err = cmd_status(sk, MGMT_OP_DISCONNECT, EBUSY); + if (mgmt_pending_find(MGMT_OP_DISCONNECT, index)) { + err = cmd_status(sk, index, MGMT_OP_DISCONNECT, EBUSY); goto failed; } conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); if (!conn) { - err = cmd_status(sk, MGMT_OP_DISCONNECT, ENOTCONN); + err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENOTCONN); goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, dev_id, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, index, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -884,24 +863,24 @@ failed: return err; } -static int get_connections(struct sock *sk, unsigned char *data, u16 len) +static int get_connections(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct mgmt_cp_get_connections *cp; struct mgmt_rp_get_connections *rp; struct hci_dev *hdev; struct list_head *p; size_t rp_len; - u16 dev_id, count; + u16 count; int i, err; BT_DBG(""); cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_GET_CONNECTIONS, ENODEV); + return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS, ENODEV); hci_dev_lock_bh(hdev); @@ -917,7 +896,6 @@ static int get_connections(struct sock *sk, unsigned char *data, u16 len) goto unlock; } - put_unaligned_le16(dev_id, &rp->index); put_unaligned_le16(count, &rp->conn_count); read_lock(&hci_dev_list_lock); @@ -931,7 +909,7 @@ static int get_connections(struct sock *sk, unsigned char *data, u16 len) read_unlock(&hci_dev_list_lock); - err = cmd_complete(sk, MGMT_OP_GET_CONNECTIONS, rp, rp_len); + err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, rp, rp_len); unlock: kfree(rp); @@ -940,32 +918,31 @@ unlock: return err; } -static int pin_code_reply(struct sock *sk, unsigned char *data, u16 len) +static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct hci_dev *hdev; struct mgmt_cp_pin_code_reply *cp; struct hci_cp_pin_code_reply reply; struct pending_cmd *cmd; - u16 dev_id; int err; BT_DBG(""); cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_PIN_CODE_REPLY, ENODEV); + return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENODEV); hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_PIN_CODE_REPLY, ENETDOWN); + err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENETDOWN); goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, dev_id, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, index, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -986,31 +963,32 @@ failed: return err; } -static int pin_code_neg_reply(struct sock *sk, unsigned char *data, u16 len) +static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct hci_dev *hdev; struct mgmt_cp_pin_code_neg_reply *cp; struct pending_cmd *cmd; - u16 dev_id; int err; BT_DBG(""); cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_PIN_CODE_NEG_REPLY, ENODEV); + return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, + ENODEV); hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_PIN_CODE_NEG_REPLY, ENETDOWN); + err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, + ENETDOWN); goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, dev_id, + cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, index, data, len); if (!cmd) { err = -ENOMEM; @@ -1029,20 +1007,19 @@ failed: return err; } -static int set_io_capability(struct sock *sk, unsigned char *data, u16 len) +static int set_io_capability(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct hci_dev *hdev; struct mgmt_cp_set_io_capability *cp; - u16 dev_id; BT_DBG(""); cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_SET_IO_CAPABILITY, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, ENODEV); hci_dev_lock_bh(hdev); @@ -1054,8 +1031,7 @@ static int set_io_capability(struct sock *sk, unsigned char *data, u16 len) hci_dev_unlock_bh(hdev); hci_dev_put(hdev); - return cmd_complete(sk, MGMT_OP_SET_IO_CAPABILITY, - &dev_id, sizeof(dev_id)); + return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, NULL, 0); } static inline struct pending_cmd *find_pairing(struct hci_conn *conn) @@ -1088,11 +1064,10 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status) struct mgmt_rp_pair_device rp; struct hci_conn *conn = cmd->user_data; - rp.index = cmd->index; bacpy(&rp.bdaddr, &conn->dst); rp.status = status; - cmd_complete(cmd->sk, MGMT_OP_PAIR_DEVICE, &rp, sizeof(rp)); + cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, &rp, sizeof(rp)); /* So we don't get further callbacks for this connection */ conn->connect_cfm_cb = NULL; @@ -1119,24 +1094,22 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status) pairing_complete(cmd, status); } -static int pair_device(struct sock *sk, unsigned char *data, u16 len) +static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; struct mgmt_cp_pair_device *cp; struct pending_cmd *cmd; u8 sec_level, auth_type; struct hci_conn *conn; - u16 dev_id; int err; BT_DBG(""); cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_PAIR_DEVICE, ENODEV); + return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, ENODEV); hci_dev_lock_bh(hdev); @@ -1156,11 +1129,11 @@ static int pair_device(struct sock *sk, unsigned char *data, u16 len) if (conn->connect_cfm_cb) { hci_conn_put(conn); - err = cmd_status(sk, MGMT_OP_PAIR_DEVICE, EBUSY); + err = cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, EBUSY); goto unlock; } - cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, dev_id, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, index, data, len); if (!cmd) { err = -ENOMEM; hci_conn_put(conn); @@ -1186,19 +1159,17 @@ unlock: return err; } -static int user_confirm_reply(struct sock *sk, unsigned char *data, u16 len, - int success) +static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data, + u16 len, int success) { struct mgmt_cp_user_confirm_reply *cp = (void *) data; - u16 dev_id, mgmt_op, hci_op; + u16 mgmt_op, hci_op; struct pending_cmd *cmd; struct hci_dev *hdev; int err; BT_DBG(""); - dev_id = get_unaligned_le16(&cp->index); - if (success) { mgmt_op = MGMT_OP_USER_CONFIRM_REPLY; hci_op = HCI_OP_USER_CONFIRM_REPLY; @@ -1207,16 +1178,16 @@ static int user_confirm_reply(struct sock *sk, unsigned char *data, u16 len, hci_op = HCI_OP_USER_CONFIRM_NEG_REPLY; } - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, mgmt_op, ENODEV); + return cmd_status(sk, index, mgmt_op, ENODEV); if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, mgmt_op, ENETDOWN); + err = cmd_status(sk, index, mgmt_op, ENETDOWN); goto failed; } - cmd = mgmt_pending_add(sk, mgmt_op, dev_id, data, len); + cmd = mgmt_pending_add(sk, mgmt_op, index, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -1237,7 +1208,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; struct mgmt_hdr *hdr; - u16 opcode, len; + u16 opcode, index, len; int err; BT_DBG("got %zu bytes", msglen); @@ -1256,6 +1227,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) hdr = (struct mgmt_hdr *) buf; opcode = get_unaligned_le16(&hdr->opcode); + index = get_unaligned_le16(&hdr->index); len = get_unaligned_le16(&hdr->len); if (len != msglen - sizeof(*hdr)) { @@ -1271,65 +1243,65 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) err = read_index_list(sk); break; case MGMT_OP_READ_INFO: - err = read_controller_info(sk, buf + sizeof(*hdr), len); + err = read_controller_info(sk, index); break; case MGMT_OP_SET_POWERED: - err = set_powered(sk, buf + sizeof(*hdr), len); + err = set_powered(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_SET_DISCOVERABLE: - err = set_discoverable(sk, buf + sizeof(*hdr), len); + err = set_discoverable(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_SET_CONNECTABLE: - err = set_connectable(sk, buf + sizeof(*hdr), len); + err = set_connectable(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_SET_PAIRABLE: - err = set_pairable(sk, buf + sizeof(*hdr), len); + err = set_pairable(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_ADD_UUID: - err = add_uuid(sk, buf + sizeof(*hdr), len); + err = add_uuid(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_REMOVE_UUID: - err = remove_uuid(sk, buf + sizeof(*hdr), len); + err = remove_uuid(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_SET_DEV_CLASS: - err = set_dev_class(sk, buf + sizeof(*hdr), len); + err = set_dev_class(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_SET_SERVICE_CACHE: - err = set_service_cache(sk, buf + sizeof(*hdr), len); + err = set_service_cache(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_LOAD_KEYS: - err = load_keys(sk, buf + sizeof(*hdr), len); + err = load_keys(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_REMOVE_KEY: - err = remove_key(sk, buf + sizeof(*hdr), len); + err = remove_key(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_DISCONNECT: - err = disconnect(sk, buf + sizeof(*hdr), len); + err = disconnect(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_GET_CONNECTIONS: - err = get_connections(sk, buf + sizeof(*hdr), len); + err = get_connections(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_PIN_CODE_REPLY: - err = pin_code_reply(sk, buf + sizeof(*hdr), len); + err = pin_code_reply(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_PIN_CODE_NEG_REPLY: - err = pin_code_neg_reply(sk, buf + sizeof(*hdr), len); + err = pin_code_neg_reply(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_SET_IO_CAPABILITY: - err = set_io_capability(sk, buf + sizeof(*hdr), len); + err = set_io_capability(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_PAIR_DEVICE: - err = pair_device(sk, buf + sizeof(*hdr), len); + err = pair_device(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_USER_CONFIRM_REPLY: - err = user_confirm_reply(sk, buf + sizeof(*hdr), len, 1); + err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 1); break; case MGMT_OP_USER_CONFIRM_NEG_REPLY: - err = user_confirm_reply(sk, buf + sizeof(*hdr), len, 0); + err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 0); break; default: BT_DBG("Unknown op %u", opcode); - err = cmd_status(sk, opcode, 0x01); + err = cmd_status(sk, index, opcode, 0x01); break; } @@ -1345,20 +1317,12 @@ done: int mgmt_index_added(u16 index) { - struct mgmt_ev_index_added ev; - - put_unaligned_le16(index, &ev.index); - - return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_INDEX_ADDED, index, NULL, 0, NULL); } int mgmt_index_removed(u16 index) { - struct mgmt_ev_index_added ev; - - put_unaligned_le16(index, &ev.index); - - return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_INDEX_REMOVED, index, NULL, 0, NULL); } struct cmd_lookup { @@ -1394,10 +1358,9 @@ int mgmt_powered(u16 index, u8 powered) mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match); - put_unaligned_le16(index, &ev.index); ev.val = powered; - ret = mgmt_event(MGMT_EV_POWERED, &ev, sizeof(ev), match.sk); + ret = mgmt_event(MGMT_EV_POWERED, index, &ev, sizeof(ev), match.sk); if (match.sk) sock_put(match.sk); @@ -1414,10 +1377,10 @@ int mgmt_discoverable(u16 index, u8 discoverable) mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index, mode_rsp, &match); - put_unaligned_le16(index, &ev.index); ev.val = discoverable; - ret = mgmt_event(MGMT_EV_DISCOVERABLE, &ev, sizeof(ev), match.sk); + ret = mgmt_event(MGMT_EV_DISCOVERABLE, index, &ev, sizeof(ev), + match.sk); if (match.sk) sock_put(match.sk); @@ -1433,10 +1396,9 @@ int mgmt_connectable(u16 index, u8 connectable) mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, mode_rsp, &match); - put_unaligned_le16(index, &ev.index); ev.val = connectable; - ret = mgmt_event(MGMT_EV_CONNECTABLE, &ev, sizeof(ev), match.sk); + ret = mgmt_event(MGMT_EV_CONNECTABLE, index, &ev, sizeof(ev), match.sk); if (match.sk) sock_put(match.sk); @@ -1450,25 +1412,22 @@ int mgmt_new_key(u16 index, struct link_key *key, u8 old_key_type) memset(&ev, 0, sizeof(ev)); - put_unaligned_le16(index, &ev.index); - bacpy(&ev.key.bdaddr, &key->bdaddr); ev.key.type = key->type; memcpy(ev.key.val, key->val, 16); ev.key.pin_len = key->pin_len; ev.old_key_type = old_key_type; - return mgmt_event(MGMT_EV_NEW_KEY, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL); } int mgmt_connected(u16 index, bdaddr_t *bdaddr) { struct mgmt_ev_connected ev; - put_unaligned_le16(index, &ev.index); bacpy(&ev.bdaddr, bdaddr); - return mgmt_event(MGMT_EV_CONNECTED, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_CONNECTED, index, &ev, sizeof(ev), NULL); } static void disconnect_rsp(struct pending_cmd *cmd, void *data) @@ -1477,10 +1436,9 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data) struct sock **sk = data; struct mgmt_rp_disconnect rp; - put_unaligned_le16(cmd->index, &rp.index); bacpy(&rp.bdaddr, &cp->bdaddr); - cmd_complete(cmd->sk, MGMT_OP_DISCONNECT, &rp, sizeof(rp)); + cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, &rp, sizeof(rp)); *sk = cmd->sk; sock_hold(*sk); @@ -1496,10 +1454,9 @@ int mgmt_disconnected(u16 index, bdaddr_t *bdaddr) mgmt_pending_foreach(MGMT_OP_DISCONNECT, index, disconnect_rsp, &sk); - put_unaligned_le16(index, &ev.index); bacpy(&ev.bdaddr, bdaddr); - err = mgmt_event(MGMT_EV_DISCONNECTED, &ev, sizeof(ev), sk); + err = mgmt_event(MGMT_EV_DISCONNECTED, index, &ev, sizeof(ev), sk); if (sk) sock_put(sk); @@ -1516,7 +1473,7 @@ int mgmt_disconnect_failed(u16 index) if (!cmd) return -ENOENT; - err = cmd_status(cmd->sk, MGMT_OP_DISCONNECT, EIO); + err = cmd_status(cmd->sk, index, MGMT_OP_DISCONNECT, EIO); mgmt_pending_remove(cmd); @@ -1527,21 +1484,20 @@ int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status) { struct mgmt_ev_connect_failed ev; - put_unaligned_le16(index, &ev.index); bacpy(&ev.bdaddr, bdaddr); ev.status = status; - return mgmt_event(MGMT_EV_CONNECT_FAILED, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_CONNECT_FAILED, index, &ev, sizeof(ev), NULL); } int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr) { struct mgmt_ev_pin_code_request ev; - put_unaligned_le16(index, &ev.index); bacpy(&ev.bdaddr, bdaddr); - return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, index, &ev, sizeof(ev), + NULL); } int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) @@ -1554,11 +1510,11 @@ int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) if (!cmd) return -ENOENT; - put_unaligned_le16(index, &rp.index); bacpy(&rp.bdaddr, bdaddr); rp.status = status; - err = cmd_complete(cmd->sk, MGMT_OP_PIN_CODE_REPLY, &rp, sizeof(rp)); + err = cmd_complete(cmd->sk, index, MGMT_OP_PIN_CODE_REPLY, &rp, + sizeof(rp)); mgmt_pending_remove(cmd); @@ -1575,12 +1531,11 @@ int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) if (!cmd) return -ENOENT; - put_unaligned_le16(index, &rp.index); bacpy(&rp.bdaddr, bdaddr); rp.status = status; - err = cmd_complete(cmd->sk, MGMT_OP_PIN_CODE_NEG_REPLY, - &rp, sizeof(rp)); + err = cmd_complete(cmd->sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, &rp, + sizeof(rp)); mgmt_pending_remove(cmd); @@ -1593,11 +1548,11 @@ int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value) BT_DBG("hci%u", index); - put_unaligned_le16(index, &ev.index); bacpy(&ev.bdaddr, bdaddr); put_unaligned_le32(value, &ev.value); - return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, index, &ev, sizeof(ev), + NULL); } static int confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status, @@ -1611,10 +1566,9 @@ static int confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status, if (!cmd) return -ENOENT; - put_unaligned_le16(index, &rp.index); bacpy(&rp.bdaddr, bdaddr); rp.status = status; - err = cmd_complete(cmd->sk, opcode, &rp, sizeof(rp)); + err = cmd_complete(cmd->sk, index, opcode, &rp, sizeof(rp)); mgmt_pending_remove(cmd); @@ -1638,9 +1592,8 @@ int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status) { struct mgmt_ev_auth_failed ev; - put_unaligned_le16(index, &ev.index); bacpy(&ev.bdaddr, bdaddr); ev.status = status; - return mgmt_event(MGMT_EV_AUTH_FAILED, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_AUTH_FAILED, index, &ev, sizeof(ev), NULL); } -- cgit v1.2.3 From 080e4130b1fb6a02e75149a1cccc8192e734713d Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Thu, 17 Feb 2011 23:43:33 +0000 Subject: netpoll: remove IFF_IN_NETPOLL flag V4: rebase to net-next-2.6 This patch removes the flag IFF_IN_NETPOLL, we don't need it any more since we have netpoll_tx_running() now. Signed-off-by: WANG Cong Acked-by: Neil Horman Cc: Herbert Xu Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 6 ++---- drivers/net/bonding/bonding.h | 2 +- include/linux/if.h | 9 ++++----- net/core/netpoll.c | 2 -- 4 files changed, 7 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 2ed662464cac..c75126ddc646 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -423,11 +423,9 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, { skb->dev = slave_dev; skb->priority = 1; - if (unlikely(netpoll_tx_running(slave_dev))) { - slave_dev->priv_flags |= IFF_IN_NETPOLL; + if (unlikely(netpoll_tx_running(slave_dev))) bond_netpoll_send_skb(bond_get_slave_by_dev(bond, slave_dev), skb); - slave_dev->priv_flags &= ~IFF_IN_NETPOLL; - } else + else dev_queue_xmit(skb); return 0; diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 0a3e00b220b7..a401b8df84f0 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -133,7 +133,7 @@ static inline void unblock_netpoll_tx(void) static inline int is_netpoll_tx_blocked(struct net_device *dev) { - if (unlikely(dev->priv_flags & IFF_IN_NETPOLL)) + if (unlikely(netpoll_tx_running(dev))) return atomic_read(&netpoll_block_tx); return 0; } diff --git a/include/linux/if.h b/include/linux/if.h index 123959927745..3bc63e6a02f7 100644 --- a/include/linux/if.h +++ b/include/linux/if.h @@ -71,11 +71,10 @@ * release skb->dst */ #define IFF_DONT_BRIDGE 0x800 /* disallow bridging this ether dev */ -#define IFF_IN_NETPOLL 0x1000 /* whether we are processing netpoll */ -#define IFF_DISABLE_NETPOLL 0x2000 /* disable netpoll at run-time */ -#define IFF_MACVLAN_PORT 0x4000 /* device used as macvlan port */ -#define IFF_BRIDGE_PORT 0x8000 /* device used as bridge port */ -#define IFF_OVS_DATAPATH 0x10000 /* device used as Open vSwitch +#define IFF_DISABLE_NETPOLL 0x1000 /* disable netpoll at run-time */ +#define IFF_MACVLAN_PORT 0x2000 /* device used as macvlan port */ +#define IFF_BRIDGE_PORT 0x4000 /* device used as bridge port */ +#define IFF_OVS_DATAPATH 0x8000 /* device used as Open vSwitch * datapath port */ #define IF_GET_IFACE 0x0001 /* for querying only */ diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 02dc2cbcbe86..f68e6949294e 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -313,9 +313,7 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, tries > 0; --tries) { if (__netif_tx_trylock(txq)) { if (!netif_tx_queue_stopped(txq)) { - dev->priv_flags |= IFF_IN_NETPOLL; status = ops->ndo_start_xmit(skb, dev); - dev->priv_flags &= ~IFF_IN_NETPOLL; if (status == NETDEV_TX_OK) txq_trans_update(txq); } -- cgit v1.2.3 From 6f2f19ed955e62a6789495da512d510f26ad4885 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 27 Feb 2011 23:04:45 -0800 Subject: xfrm: Pass name as const to xfrm_*_get_byname(). Signed-off-by: David S. Miller --- include/net/xfrm.h | 8 ++++---- net/xfrm/xfrm_algo.c | 8 ++++---- net/xfrm/xfrm_user.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 44dccfcf9204..86ecfc1004b4 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1513,10 +1513,10 @@ extern struct xfrm_algo_desc *xfrm_ealg_get_byidx(unsigned int idx); extern struct xfrm_algo_desc *xfrm_aalg_get_byid(int alg_id); extern struct xfrm_algo_desc *xfrm_ealg_get_byid(int alg_id); extern struct xfrm_algo_desc *xfrm_calg_get_byid(int alg_id); -extern struct xfrm_algo_desc *xfrm_aalg_get_byname(char *name, int probe); -extern struct xfrm_algo_desc *xfrm_ealg_get_byname(char *name, int probe); -extern struct xfrm_algo_desc *xfrm_calg_get_byname(char *name, int probe); -extern struct xfrm_algo_desc *xfrm_aead_get_byname(char *name, int icv_len, +extern struct xfrm_algo_desc *xfrm_aalg_get_byname(const char *name, int probe); +extern struct xfrm_algo_desc *xfrm_ealg_get_byname(const char *name, int probe); +extern struct xfrm_algo_desc *xfrm_calg_get_byname(const char *name, int probe); +extern struct xfrm_algo_desc *xfrm_aead_get_byname(const char *name, int icv_len, int probe); struct hash_desc; diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c index 8b4d6e3246e5..58064d9e565d 100644 --- a/net/xfrm/xfrm_algo.c +++ b/net/xfrm/xfrm_algo.c @@ -618,21 +618,21 @@ static int xfrm_alg_name_match(const struct xfrm_algo_desc *entry, (entry->compat && !strcmp(name, entry->compat))); } -struct xfrm_algo_desc *xfrm_aalg_get_byname(char *name, int probe) +struct xfrm_algo_desc *xfrm_aalg_get_byname(const char *name, int probe) { return xfrm_find_algo(&xfrm_aalg_list, xfrm_alg_name_match, name, probe); } EXPORT_SYMBOL_GPL(xfrm_aalg_get_byname); -struct xfrm_algo_desc *xfrm_ealg_get_byname(char *name, int probe) +struct xfrm_algo_desc *xfrm_ealg_get_byname(const char *name, int probe) { return xfrm_find_algo(&xfrm_ealg_list, xfrm_alg_name_match, name, probe); } EXPORT_SYMBOL_GPL(xfrm_ealg_get_byname); -struct xfrm_algo_desc *xfrm_calg_get_byname(char *name, int probe) +struct xfrm_algo_desc *xfrm_calg_get_byname(const char *name, int probe) { return xfrm_find_algo(&xfrm_calg_list, xfrm_alg_name_match, name, probe); @@ -654,7 +654,7 @@ static int xfrm_aead_name_match(const struct xfrm_algo_desc *entry, !strcmp(name, entry->name); } -struct xfrm_algo_desc *xfrm_aead_get_byname(char *name, int icv_len, int probe) +struct xfrm_algo_desc *xfrm_aead_get_byname(const char *name, int icv_len, int probe) { struct xfrm_aead_name data = { .name = name, diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index b43c1b1240d4..673698d380d7 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -234,7 +234,7 @@ out: } static int attach_one_algo(struct xfrm_algo **algpp, u8 *props, - struct xfrm_algo_desc *(*get_byname)(char *, int), + struct xfrm_algo_desc *(*get_byname)(const char *, int), struct nlattr *rta) { struct xfrm_algo *p, *ualg; -- cgit v1.2.3 From 851586218f5d463bbd62af40dfa264c5e3539572 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 27 Feb 2011 23:07:02 -0800 Subject: xfrm: Pass const arg to xfrm_alg_len and xfrm_alg_auth_len. Signed-off-by: David S. Miller --- include/net/xfrm.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 86ecfc1004b4..15e310fae282 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1558,12 +1558,12 @@ static inline int xfrm_aevent_is_on(struct net *net) } #endif -static inline int xfrm_alg_len(struct xfrm_algo *alg) +static inline int xfrm_alg_len(const struct xfrm_algo *alg) { return sizeof(*alg) + ((alg->alg_key_len + 7) / 8); } -static inline int xfrm_alg_auth_len(struct xfrm_algo_auth *alg) +static inline int xfrm_alg_auth_len(const struct xfrm_algo_auth *alg) { return sizeof(*alg) + ((alg->alg_key_len + 7) / 8); } -- cgit v1.2.3 From a70486f0e669730bad6713063e3f59e2e870044f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 27 Feb 2011 23:17:24 -0800 Subject: xfrm: Pass const xfrm_address_t objects to xfrm_state_lookup* and xfrm_find_acq. Signed-off-by: David S. Miller --- include/net/xfrm.h | 10 +++++----- net/xfrm/xfrm_state.c | 12 ++++++++---- 2 files changed, 13 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 15e310fae282..437c289649ca 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1350,11 +1350,11 @@ extern void xfrm_state_insert(struct xfrm_state *x); extern int xfrm_state_add(struct xfrm_state *x); extern int xfrm_state_update(struct xfrm_state *x); extern struct xfrm_state *xfrm_state_lookup(struct net *net, u32 mark, - xfrm_address_t *daddr, __be32 spi, + const xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family); extern struct xfrm_state *xfrm_state_lookup_byaddr(struct net *net, u32 mark, - xfrm_address_t *daddr, - xfrm_address_t *saddr, + const xfrm_address_t *daddr, + const xfrm_address_t *saddr, u8 proto, unsigned short family); #ifdef CONFIG_XFRM_SUB_POLICY @@ -1481,8 +1481,8 @@ u32 xfrm_get_acqseq(void); extern int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi); struct xfrm_state *xfrm_find_acq(struct net *net, struct xfrm_mark *mark, u8 mode, u32 reqid, u8 proto, - xfrm_address_t *daddr, - xfrm_address_t *saddr, int create, + const xfrm_address_t *daddr, + const xfrm_address_t *saddr, int create, unsigned short family); extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 8496b3d3e85b..81221d9cbf06 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -997,7 +997,11 @@ void xfrm_state_insert(struct xfrm_state *x) EXPORT_SYMBOL(xfrm_state_insert); /* xfrm_state_lock is held */ -static struct xfrm_state *__find_acq_core(struct net *net, struct xfrm_mark *m, unsigned short family, u8 mode, u32 reqid, u8 proto, xfrm_address_t *daddr, xfrm_address_t *saddr, int create) +static struct xfrm_state *__find_acq_core(struct net *net, struct xfrm_mark *m, + unsigned short family, u8 mode, + u32 reqid, u8 proto, + const xfrm_address_t *daddr, + const xfrm_address_t *saddr, int create) { unsigned int h = xfrm_dst_hash(net, daddr, saddr, reqid, family); struct hlist_node *entry; @@ -1375,7 +1379,7 @@ int xfrm_state_check_expire(struct xfrm_state *x) EXPORT_SYMBOL(xfrm_state_check_expire); struct xfrm_state * -xfrm_state_lookup(struct net *net, u32 mark, xfrm_address_t *daddr, __be32 spi, +xfrm_state_lookup(struct net *net, u32 mark, const xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family) { struct xfrm_state *x; @@ -1389,7 +1393,7 @@ EXPORT_SYMBOL(xfrm_state_lookup); struct xfrm_state * xfrm_state_lookup_byaddr(struct net *net, u32 mark, - xfrm_address_t *daddr, xfrm_address_t *saddr, + const xfrm_address_t *daddr, const xfrm_address_t *saddr, u8 proto, unsigned short family) { struct xfrm_state *x; @@ -1403,7 +1407,7 @@ EXPORT_SYMBOL(xfrm_state_lookup_byaddr); struct xfrm_state * xfrm_find_acq(struct net *net, struct xfrm_mark *mark, u8 mode, u32 reqid, u8 proto, - xfrm_address_t *daddr, xfrm_address_t *saddr, + const xfrm_address_t *daddr, const xfrm_address_t *saddr, int create, unsigned short family) { struct xfrm_state *x; -- cgit v1.2.3 From e3dfa389fd2c79526b4bbf295726b66d21001664 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 27 Feb 2011 23:20:19 -0800 Subject: xfrm: Pass const xfrm_mark to xfrm_mark_put(). Signed-off-by: David S. Miller --- include/net/xfrm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 437c289649ca..efded23dc4ae 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1611,7 +1611,7 @@ static inline int xfrm_mark_get(struct nlattr **attrs, struct xfrm_mark *m) return m->v & m->m; } -static inline int xfrm_mark_put(struct sk_buff *skb, struct xfrm_mark *m) +static inline int xfrm_mark_put(struct sk_buff *skb, const struct xfrm_mark *m) { if (m->m | m->v) NLA_PUT(skb, XFRMA_MARK, sizeof(struct xfrm_mark), m); -- cgit v1.2.3 From 3d6b88282751a3329d7b041a8d18db87641db9e8 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 22 Feb 2011 18:18:51 +0100 Subject: dt: Typo fix. Signed-off-by: Lennert Buytenhek Signed-off-by: Grant Likely --- include/linux/of.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/of.h b/include/linux/of.h index d9dd664a6a9c..c004753d4c1b 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -103,7 +103,7 @@ extern void of_node_put(struct device_node *node); #endif /* - * OF address retreival & translation + * OF address retrieval & translation */ /* Helper to read a big number; size is in cells (not bytes) */ -- cgit v1.2.3 From b826291c14c396e7aa5d84523aafac117f430902 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 17 Feb 2011 02:37:15 -0700 Subject: drivercore/dt: add a match table pointer to struct device Add a new .of_match field to struct device which points at the matching device driver .of_match_table entry when a device is probed via the device tree Signed-off-by: Grant Likely Acked-by: Greg Kroah-Hartman --- include/linux/device.h | 1 + include/linux/of_device.h | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/device.h b/include/linux/device.h index ca5d25225aab..8d8e2675d07f 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -441,6 +441,7 @@ struct device { struct dev_archdata archdata; struct device_node *of_node; /* associated device tree node */ + const struct of_device_id *of_match; /* matching of_device_id from driver */ dev_t devt; /* dev_t, creates the sysfs "dev" */ diff --git a/include/linux/of_device.h b/include/linux/of_device.h index 975d347079d9..8bfe6c1d4365 100644 --- a/include/linux/of_device.h +++ b/include/linux/of_device.h @@ -18,10 +18,11 @@ extern void of_device_make_bus_id(struct device *dev); * @drv: the device_driver structure to test * @dev: the device structure to match against */ -static inline int of_driver_match_device(const struct device *dev, +static inline int of_driver_match_device(struct device *dev, const struct device_driver *drv) { - return of_match_device(drv->of_match_table, dev) != NULL; + dev->of_match = of_match_device(drv->of_match_table, dev); + return dev->of_match != NULL; } extern struct platform_device *of_dev_get(struct platform_device *dev); -- cgit v1.2.3 From 710ac54be44e0cc53f5bf29b03d12c8706e7077a Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 17 Feb 2011 00:26:57 -0700 Subject: dt/powerpc: move of_bus_type infrastructure to ibmebus arch/powerpc/kernel/ibmebus.c is the only remaining user of the of_bus_type support code for initializing the bus and registering drivers. All others have either been switched to the vanilla platform bus or already have their own infrastructure. This patch moves the functionality that ibmebus is using out of drivers/of/{platform,device}.c and into ibmebus.c where it is actually used. Also renames the moved symbols from of_platform_* to ibmebus_bus_* to reflect the actual usage. This patch is part of moving all of the of_platform_bus_type users over to the platform_bus_type. Signed-off-by: Grant Likely --- arch/powerpc/kernel/ibmebus.c | 404 +++++++++++++++++++++++++++++++++++++++++- drivers/of/device.c | 34 ---- drivers/of/platform.c | 393 ---------------------------------------- include/linux/of_platform.h | 6 - 4 files changed, 400 insertions(+), 437 deletions(-) (limited to 'include') diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c index f62efdfd1769..c00d4ca1ee15 100644 --- a/arch/powerpc/kernel/ibmebus.c +++ b/arch/powerpc/kernel/ibmebus.c @@ -201,13 +201,14 @@ int ibmebus_register_driver(struct of_platform_driver *drv) /* If the driver uses devices that ibmebus doesn't know, add them */ ibmebus_create_devices(drv->driver.of_match_table); - return of_register_driver(drv, &ibmebus_bus_type); + drv->driver.bus = &ibmebus_bus_type; + return driver_register(&drv->driver); } EXPORT_SYMBOL(ibmebus_register_driver); void ibmebus_unregister_driver(struct of_platform_driver *drv) { - of_unregister_driver(drv); + driver_unregister(&drv->driver); } EXPORT_SYMBOL(ibmebus_unregister_driver); @@ -308,15 +309,410 @@ static ssize_t ibmebus_store_remove(struct bus_type *bus, } } + static struct bus_attribute ibmebus_bus_attrs[] = { __ATTR(probe, S_IWUSR, NULL, ibmebus_store_probe), __ATTR(remove, S_IWUSR, NULL, ibmebus_store_remove), __ATTR_NULL }; +static int ibmebus_bus_bus_match(struct device *dev, struct device_driver *drv) +{ + const struct of_device_id *matches = drv->of_match_table; + + if (!matches) + return 0; + + return of_match_device(matches, dev) != NULL; +} + +static int ibmebus_bus_device_probe(struct device *dev) +{ + int error = -ENODEV; + struct of_platform_driver *drv; + struct platform_device *of_dev; + const struct of_device_id *match; + + drv = to_of_platform_driver(dev->driver); + of_dev = to_platform_device(dev); + + if (!drv->probe) + return error; + + of_dev_get(of_dev); + + match = of_match_device(drv->driver.of_match_table, dev); + if (match) + error = drv->probe(of_dev, match); + if (error) + of_dev_put(of_dev); + + return error; +} + +static int ibmebus_bus_device_remove(struct device *dev) +{ + struct platform_device *of_dev = to_platform_device(dev); + struct of_platform_driver *drv = to_of_platform_driver(dev->driver); + + if (dev->driver && drv->remove) + drv->remove(of_dev); + return 0; +} + +static void ibmebus_bus_device_shutdown(struct device *dev) +{ + struct platform_device *of_dev = to_platform_device(dev); + struct of_platform_driver *drv = to_of_platform_driver(dev->driver); + + if (dev->driver && drv->shutdown) + drv->shutdown(of_dev); +} + +/* + * ibmebus_bus_device_attrs + */ +static ssize_t devspec_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *ofdev; + + ofdev = to_platform_device(dev); + return sprintf(buf, "%s\n", ofdev->dev.of_node->full_name); +} + +static ssize_t name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *ofdev; + + ofdev = to_platform_device(dev); + return sprintf(buf, "%s\n", ofdev->dev.of_node->name); +} + +static ssize_t modalias_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = of_device_get_modalias(dev, buf, PAGE_SIZE - 2); + buf[len] = '\n'; + buf[len+1] = 0; + return len+1; +} + +struct device_attribute ibmebus_bus_device_attrs[] = { + __ATTR_RO(devspec), + __ATTR_RO(name), + __ATTR_RO(modalias), + __ATTR_NULL +}; + +#ifdef CONFIG_PM_SLEEP +static int ibmebus_bus_legacy_suspend(struct device *dev, pm_message_t mesg) +{ + struct platform_device *of_dev = to_platform_device(dev); + struct of_platform_driver *drv = to_of_platform_driver(dev->driver); + int ret = 0; + + if (dev->driver && drv->suspend) + ret = drv->suspend(of_dev, mesg); + return ret; +} + +static int ibmebus_bus_legacy_resume(struct device *dev) +{ + struct platform_device *of_dev = to_platform_device(dev); + struct of_platform_driver *drv = to_of_platform_driver(dev->driver); + int ret = 0; + + if (dev->driver && drv->resume) + ret = drv->resume(of_dev); + return ret; +} + +static int ibmebus_bus_pm_prepare(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (drv && drv->pm && drv->pm->prepare) + ret = drv->pm->prepare(dev); + + return ret; +} + +static void ibmebus_bus_pm_complete(struct device *dev) +{ + struct device_driver *drv = dev->driver; + + if (drv && drv->pm && drv->pm->complete) + drv->pm->complete(dev); +} + +#ifdef CONFIG_SUSPEND + +static int ibmebus_bus_pm_suspend(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->suspend) + ret = drv->pm->suspend(dev); + } else { + ret = ibmebus_bus_legacy_suspend(dev, PMSG_SUSPEND); + } + + return ret; +} + +static int ibmebus_bus_pm_suspend_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->suspend_noirq) + ret = drv->pm->suspend_noirq(dev); + } + + return ret; +} + +static int ibmebus_bus_pm_resume(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->resume) + ret = drv->pm->resume(dev); + } else { + ret = ibmebus_bus_legacy_resume(dev); + } + + return ret; +} + +static int ibmebus_bus_pm_resume_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->resume_noirq) + ret = drv->pm->resume_noirq(dev); + } + + return ret; +} + +#else /* !CONFIG_SUSPEND */ + +#define ibmebus_bus_pm_suspend NULL +#define ibmebus_bus_pm_resume NULL +#define ibmebus_bus_pm_suspend_noirq NULL +#define ibmebus_bus_pm_resume_noirq NULL + +#endif /* !CONFIG_SUSPEND */ + +#ifdef CONFIG_HIBERNATION + +static int ibmebus_bus_pm_freeze(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->freeze) + ret = drv->pm->freeze(dev); + } else { + ret = ibmebus_bus_legacy_suspend(dev, PMSG_FREEZE); + } + + return ret; +} + +static int ibmebus_bus_pm_freeze_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->freeze_noirq) + ret = drv->pm->freeze_noirq(dev); + } + + return ret; +} + +static int ibmebus_bus_pm_thaw(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->thaw) + ret = drv->pm->thaw(dev); + } else { + ret = ibmebus_bus_legacy_resume(dev); + } + + return ret; +} + +static int ibmebus_bus_pm_thaw_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->thaw_noirq) + ret = drv->pm->thaw_noirq(dev); + } + + return ret; +} + +static int ibmebus_bus_pm_poweroff(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->poweroff) + ret = drv->pm->poweroff(dev); + } else { + ret = ibmebus_bus_legacy_suspend(dev, PMSG_HIBERNATE); + } + + return ret; +} + +static int ibmebus_bus_pm_poweroff_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->poweroff_noirq) + ret = drv->pm->poweroff_noirq(dev); + } + + return ret; +} + +static int ibmebus_bus_pm_restore(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->restore) + ret = drv->pm->restore(dev); + } else { + ret = ibmebus_bus_legacy_resume(dev); + } + + return ret; +} + +static int ibmebus_bus_pm_restore_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->restore_noirq) + ret = drv->pm->restore_noirq(dev); + } + + return ret; +} + +#else /* !CONFIG_HIBERNATION */ + +#define ibmebus_bus_pm_freeze NULL +#define ibmebus_bus_pm_thaw NULL +#define ibmebus_bus_pm_poweroff NULL +#define ibmebus_bus_pm_restore NULL +#define ibmebus_bus_pm_freeze_noirq NULL +#define ibmebus_bus_pm_thaw_noirq NULL +#define ibmebus_bus_pm_poweroff_noirq NULL +#define ibmebus_bus_pm_restore_noirq NULL + +#endif /* !CONFIG_HIBERNATION */ + +static struct dev_pm_ops ibmebus_bus_dev_pm_ops = { + .prepare = ibmebus_bus_pm_prepare, + .complete = ibmebus_bus_pm_complete, + .suspend = ibmebus_bus_pm_suspend, + .resume = ibmebus_bus_pm_resume, + .freeze = ibmebus_bus_pm_freeze, + .thaw = ibmebus_bus_pm_thaw, + .poweroff = ibmebus_bus_pm_poweroff, + .restore = ibmebus_bus_pm_restore, + .suspend_noirq = ibmebus_bus_pm_suspend_noirq, + .resume_noirq = ibmebus_bus_pm_resume_noirq, + .freeze_noirq = ibmebus_bus_pm_freeze_noirq, + .thaw_noirq = ibmebus_bus_pm_thaw_noirq, + .poweroff_noirq = ibmebus_bus_pm_poweroff_noirq, + .restore_noirq = ibmebus_bus_pm_restore_noirq, +}; + +#define IBMEBUS_BUS_PM_OPS_PTR (&ibmebus_bus_dev_pm_ops) + +#else /* !CONFIG_PM_SLEEP */ + +#define IBMEBUS_BUS_PM_OPS_PTR NULL + +#endif /* !CONFIG_PM_SLEEP */ + struct bus_type ibmebus_bus_type = { + .name = "ibmebus", .uevent = of_device_uevent, - .bus_attrs = ibmebus_bus_attrs + .bus_attrs = ibmebus_bus_attrs, + .match = ibmebus_bus_bus_match, + .probe = ibmebus_bus_device_probe, + .remove = ibmebus_bus_device_remove, + .shutdown = ibmebus_bus_device_shutdown, + .dev_attrs = ibmebus_bus_device_attrs, + .pm = IBMEBUS_BUS_PM_OPS_PTR, }; EXPORT_SYMBOL(ibmebus_bus_type); @@ -326,7 +722,7 @@ static int __init ibmebus_bus_init(void) printk(KERN_INFO "IBM eBus Device Driver\n"); - err = of_bus_type_init(&ibmebus_bus_type, "ibmebus"); + err = bus_register(&ibmebus_bus_type); if (err) { printk(KERN_ERR "%s: failed to register IBM eBus.\n", __func__); diff --git a/drivers/of/device.c b/drivers/of/device.c index 45d86530799f..62b4b32ac887 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -47,40 +47,6 @@ void of_dev_put(struct platform_device *dev) } EXPORT_SYMBOL(of_dev_put); -static ssize_t devspec_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *ofdev; - - ofdev = to_platform_device(dev); - return sprintf(buf, "%s\n", ofdev->dev.of_node->full_name); -} - -static ssize_t name_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *ofdev; - - ofdev = to_platform_device(dev); - return sprintf(buf, "%s\n", ofdev->dev.of_node->name); -} - -static ssize_t modalias_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - ssize_t len = of_device_get_modalias(dev, buf, PAGE_SIZE - 2); - buf[len] = '\n'; - buf[len+1] = 0; - return len+1; -} - -struct device_attribute of_platform_device_attrs[] = { - __ATTR_RO(devspec), - __ATTR_RO(name), - __ATTR_RO(modalias), - __ATTR_NULL -}; - int of_device_add(struct platform_device *ofdev) { BUG_ON(ofdev->dev.of_node == NULL); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index c01cd1ac7617..b71d0cdc4209 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -114,399 +114,6 @@ EXPORT_SYMBOL(of_unregister_platform_driver); #include #endif -extern struct device_attribute of_platform_device_attrs[]; - -static int of_platform_bus_match(struct device *dev, struct device_driver *drv) -{ - const struct of_device_id *matches = drv->of_match_table; - - if (!matches) - return 0; - - return of_match_device(matches, dev) != NULL; -} - -static int of_platform_device_probe(struct device *dev) -{ - int error = -ENODEV; - struct of_platform_driver *drv; - struct platform_device *of_dev; - const struct of_device_id *match; - - drv = to_of_platform_driver(dev->driver); - of_dev = to_platform_device(dev); - - if (!drv->probe) - return error; - - of_dev_get(of_dev); - - match = of_match_device(drv->driver.of_match_table, dev); - if (match) - error = drv->probe(of_dev, match); - if (error) - of_dev_put(of_dev); - - return error; -} - -static int of_platform_device_remove(struct device *dev) -{ - struct platform_device *of_dev = to_platform_device(dev); - struct of_platform_driver *drv = to_of_platform_driver(dev->driver); - - if (dev->driver && drv->remove) - drv->remove(of_dev); - return 0; -} - -static void of_platform_device_shutdown(struct device *dev) -{ - struct platform_device *of_dev = to_platform_device(dev); - struct of_platform_driver *drv = to_of_platform_driver(dev->driver); - - if (dev->driver && drv->shutdown) - drv->shutdown(of_dev); -} - -#ifdef CONFIG_PM_SLEEP - -static int of_platform_legacy_suspend(struct device *dev, pm_message_t mesg) -{ - struct platform_device *of_dev = to_platform_device(dev); - struct of_platform_driver *drv = to_of_platform_driver(dev->driver); - int ret = 0; - - if (dev->driver && drv->suspend) - ret = drv->suspend(of_dev, mesg); - return ret; -} - -static int of_platform_legacy_resume(struct device *dev) -{ - struct platform_device *of_dev = to_platform_device(dev); - struct of_platform_driver *drv = to_of_platform_driver(dev->driver); - int ret = 0; - - if (dev->driver && drv->resume) - ret = drv->resume(of_dev); - return ret; -} - -static int of_platform_pm_prepare(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (drv && drv->pm && drv->pm->prepare) - ret = drv->pm->prepare(dev); - - return ret; -} - -static void of_platform_pm_complete(struct device *dev) -{ - struct device_driver *drv = dev->driver; - - if (drv && drv->pm && drv->pm->complete) - drv->pm->complete(dev); -} - -#ifdef CONFIG_SUSPEND - -static int of_platform_pm_suspend(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->suspend) - ret = drv->pm->suspend(dev); - } else { - ret = of_platform_legacy_suspend(dev, PMSG_SUSPEND); - } - - return ret; -} - -static int of_platform_pm_suspend_noirq(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->suspend_noirq) - ret = drv->pm->suspend_noirq(dev); - } - - return ret; -} - -static int of_platform_pm_resume(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->resume) - ret = drv->pm->resume(dev); - } else { - ret = of_platform_legacy_resume(dev); - } - - return ret; -} - -static int of_platform_pm_resume_noirq(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->resume_noirq) - ret = drv->pm->resume_noirq(dev); - } - - return ret; -} - -#else /* !CONFIG_SUSPEND */ - -#define of_platform_pm_suspend NULL -#define of_platform_pm_resume NULL -#define of_platform_pm_suspend_noirq NULL -#define of_platform_pm_resume_noirq NULL - -#endif /* !CONFIG_SUSPEND */ - -#ifdef CONFIG_HIBERNATION - -static int of_platform_pm_freeze(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->freeze) - ret = drv->pm->freeze(dev); - } else { - ret = of_platform_legacy_suspend(dev, PMSG_FREEZE); - } - - return ret; -} - -static int of_platform_pm_freeze_noirq(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->freeze_noirq) - ret = drv->pm->freeze_noirq(dev); - } - - return ret; -} - -static int of_platform_pm_thaw(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->thaw) - ret = drv->pm->thaw(dev); - } else { - ret = of_platform_legacy_resume(dev); - } - - return ret; -} - -static int of_platform_pm_thaw_noirq(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->thaw_noirq) - ret = drv->pm->thaw_noirq(dev); - } - - return ret; -} - -static int of_platform_pm_poweroff(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->poweroff) - ret = drv->pm->poweroff(dev); - } else { - ret = of_platform_legacy_suspend(dev, PMSG_HIBERNATE); - } - - return ret; -} - -static int of_platform_pm_poweroff_noirq(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->poweroff_noirq) - ret = drv->pm->poweroff_noirq(dev); - } - - return ret; -} - -static int of_platform_pm_restore(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->restore) - ret = drv->pm->restore(dev); - } else { - ret = of_platform_legacy_resume(dev); - } - - return ret; -} - -static int of_platform_pm_restore_noirq(struct device *dev) -{ - struct device_driver *drv = dev->driver; - int ret = 0; - - if (!drv) - return 0; - - if (drv->pm) { - if (drv->pm->restore_noirq) - ret = drv->pm->restore_noirq(dev); - } - - return ret; -} - -#else /* !CONFIG_HIBERNATION */ - -#define of_platform_pm_freeze NULL -#define of_platform_pm_thaw NULL -#define of_platform_pm_poweroff NULL -#define of_platform_pm_restore NULL -#define of_platform_pm_freeze_noirq NULL -#define of_platform_pm_thaw_noirq NULL -#define of_platform_pm_poweroff_noirq NULL -#define of_platform_pm_restore_noirq NULL - -#endif /* !CONFIG_HIBERNATION */ - -static struct dev_pm_ops of_platform_dev_pm_ops = { - .prepare = of_platform_pm_prepare, - .complete = of_platform_pm_complete, - .suspend = of_platform_pm_suspend, - .resume = of_platform_pm_resume, - .freeze = of_platform_pm_freeze, - .thaw = of_platform_pm_thaw, - .poweroff = of_platform_pm_poweroff, - .restore = of_platform_pm_restore, - .suspend_noirq = of_platform_pm_suspend_noirq, - .resume_noirq = of_platform_pm_resume_noirq, - .freeze_noirq = of_platform_pm_freeze_noirq, - .thaw_noirq = of_platform_pm_thaw_noirq, - .poweroff_noirq = of_platform_pm_poweroff_noirq, - .restore_noirq = of_platform_pm_restore_noirq, -}; - -#define OF_PLATFORM_PM_OPS_PTR (&of_platform_dev_pm_ops) - -#else /* !CONFIG_PM_SLEEP */ - -#define OF_PLATFORM_PM_OPS_PTR NULL - -#endif /* !CONFIG_PM_SLEEP */ - -int of_bus_type_init(struct bus_type *bus, const char *name) -{ - bus->name = name; - bus->match = of_platform_bus_match; - bus->probe = of_platform_device_probe; - bus->remove = of_platform_device_remove; - bus->shutdown = of_platform_device_shutdown; - bus->dev_attrs = of_platform_device_attrs; - bus->pm = OF_PLATFORM_PM_OPS_PTR; - return bus_register(bus); -} - -int of_register_driver(struct of_platform_driver *drv, struct bus_type *bus) -{ - /* - * Temporary: of_platform_bus used to be distinct from the platform - * bus. It isn't anymore, and so drivers on the platform bus need - * to be registered in a special way. - * - * After all of_platform_bus_type drivers are converted to - * platform_drivers, this exception can be removed. - */ - if (bus == &platform_bus_type) - return of_register_platform_driver(drv); - - /* register with core */ - drv->driver.bus = bus; - return driver_register(&drv->driver); -} -EXPORT_SYMBOL(of_register_driver); - -void of_unregister_driver(struct of_platform_driver *drv) -{ - if (drv->driver.bus == &platform_bus_type) - of_unregister_platform_driver(drv); - else - driver_unregister(&drv->driver); -} -EXPORT_SYMBOL(of_unregister_driver); - #if !defined(CONFIG_SPARC) /* * The following routines scan a subtree and registers a device for diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h index a68716ad38ce..048949fa1d10 100644 --- a/include/linux/of_platform.h +++ b/include/linux/of_platform.h @@ -47,10 +47,6 @@ struct of_platform_driver #define to_of_platform_driver(drv) \ container_of(drv,struct of_platform_driver, driver) -extern int of_register_driver(struct of_platform_driver *drv, - struct bus_type *bus); -extern void of_unregister_driver(struct of_platform_driver *drv); - /* Platform drivers register/unregister */ extern int of_register_platform_driver(struct of_platform_driver *drv); extern void of_unregister_platform_driver(struct of_platform_driver *drv); @@ -60,8 +56,6 @@ extern struct platform_device *of_device_alloc(struct device_node *np, struct device *parent); extern struct platform_device *of_find_device_by_node(struct device_node *np); -extern int of_bus_type_init(struct bus_type *bus, const char *name); - #if !defined(CONFIG_SPARC) /* SPARC has its own device registration method */ /* Platform devices and busses creation */ extern struct platform_device *of_platform_device_create(struct device_node *np, -- cgit v1.2.3 From 7c3343392172ba98d9d90a83edcc4c2e80897009 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Mon, 28 Feb 2011 11:02:24 +0100 Subject: percpu: Generic support for this_cpu_cmpxchg_double() Introduce this_cpu_cmpxchg_double(). this_cpu_cmpxchg_double() allows the comparison between two consecutive words and replaces them if there is a match. bool this_cpu_cmpxchg_double(pcp1, pcp2, old_word1, old_word2, new_word1, new_word2) this_cpu_cmpxchg_double does not return the old value (difficult since there are two words) but a boolean indicating if the operation was successful. The first percpu variable must be double word aligned! -tj: Updated to return bool instead of int, converted size check to BUILD_BUG_ON() instead of VM_BUG_ON() and other cosmetic changes. Signed-off-by: Christoph Lameter Signed-off-by: Tejun Heo --- include/linux/percpu.h | 128 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) (limited to 'include') diff --git a/include/linux/percpu.h b/include/linux/percpu.h index 27c3c6fcfad3..3a5c4449fd36 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h @@ -255,6 +255,30 @@ extern void __bad_size_call_parameter(void); pscr2_ret__; \ }) +/* + * Special handling for cmpxchg_double. cmpxchg_double is passed two + * percpu variables. The first has to be aligned to a double word + * boundary and the second has to follow directly thereafter. + */ +#define __pcpu_double_call_return_bool(stem, pcp1, pcp2, ...) \ +({ \ + bool pdcrb_ret__; \ + __verify_pcpu_ptr(&pcp1); \ + BUILD_BUG_ON(sizeof(pcp1) != sizeof(pcp2)); \ + VM_BUG_ON((unsigned long)(&pcp1) % (2 * sizeof(pcp1))); \ + VM_BUG_ON((unsigned long)(&pcp2) != \ + (unsigned long)(&pcp1) + sizeof(pcp1)); \ + switch(sizeof(pcp1)) { \ + case 1: pdcrb_ret__ = stem##1(pcp1, pcp2, __VA_ARGS__); break; \ + case 2: pdcrb_ret__ = stem##2(pcp1, pcp2, __VA_ARGS__); break; \ + case 4: pdcrb_ret__ = stem##4(pcp1, pcp2, __VA_ARGS__); break; \ + case 8: pdcrb_ret__ = stem##8(pcp1, pcp2, __VA_ARGS__); break; \ + default: \ + __bad_size_call_parameter(); break; \ + } \ + pdcrb_ret__; \ +}) + #define __pcpu_size_call(stem, variable, ...) \ do { \ __verify_pcpu_ptr(&(variable)); \ @@ -500,6 +524,45 @@ do { \ __pcpu_size_call_return2(this_cpu_cmpxchg_, pcp, oval, nval) #endif +/* + * cmpxchg_double replaces two adjacent scalars at once. The first + * two parameters are per cpu variables which have to be of the same + * size. A truth value is returned to indicate success or failure + * (since a double register result is difficult to handle). There is + * very limited hardware support for these operations, so only certain + * sizes may work. + */ +#define _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ +({ \ + int ret__; \ + preempt_disable(); \ + ret__ = __this_cpu_generic_cmpxchg_double(pcp1, pcp2, \ + oval1, oval2, nval1, nval2); \ + preempt_enable(); \ + ret__; \ +}) + +#ifndef this_cpu_cmpxchg_double +# ifndef this_cpu_cmpxchg_double_1 +# define this_cpu_cmpxchg_double_1(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# ifndef this_cpu_cmpxchg_double_2 +# define this_cpu_cmpxchg_double_2(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# ifndef this_cpu_cmpxchg_double_4 +# define this_cpu_cmpxchg_double_4(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# ifndef this_cpu_cmpxchg_double_8 +# define this_cpu_cmpxchg_double_8(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# define this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + __pcpu_double_call_return_bool(this_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2)) +#endif + /* * Generic percpu operations that do not require preemption handling. * Either we do not care about races or the caller has the @@ -703,6 +766,39 @@ do { \ __pcpu_size_call_return2(__this_cpu_cmpxchg_, pcp, oval, nval) #endif +#define __this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ +({ \ + int __ret = 0; \ + if (__this_cpu_read(pcp1) == (oval1) && \ + __this_cpu_read(pcp2) == (oval2)) { \ + __this_cpu_write(pcp1, (nval1)); \ + __this_cpu_write(pcp2, (nval2)); \ + __ret = 1; \ + } \ + (__ret); \ +}) + +#ifndef __this_cpu_cmpxchg_double +# ifndef __this_cpu_cmpxchg_double_1 +# define __this_cpu_cmpxchg_double_1(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + __this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# ifndef __this_cpu_cmpxchg_double_2 +# define __this_cpu_cmpxchg_double_2(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + __this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# ifndef __this_cpu_cmpxchg_double_4 +# define __this_cpu_cmpxchg_double_4(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + __this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# ifndef __this_cpu_cmpxchg_double_8 +# define __this_cpu_cmpxchg_double_8(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + __this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# define __this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + __pcpu_double_call_return_bool(__this_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2)) +#endif + /* * IRQ safe versions of the per cpu RMW operations. Note that these operations * are *not* safe against modification of the same variable from another @@ -823,4 +919,36 @@ do { \ __pcpu_size_call_return2(irqsafe_cpu_cmpxchg_, (pcp), oval, nval) #endif +#define irqsafe_generic_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ +({ \ + int ret__; \ + unsigned long flags; \ + local_irq_save(flags); \ + ret__ = __this_cpu_generic_cmpxchg_double(pcp1, pcp2, \ + oval1, oval2, nval1, nval2); \ + local_irq_restore(flags); \ + ret__; \ +}) + +#ifndef irqsafe_cpu_cmpxchg_double +# ifndef irqsafe_cpu_cmpxchg_double_1 +# define irqsafe_cpu_cmpxchg_double_1(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + irqsafe_generic_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# ifndef irqsafe_cpu_cmpxchg_double_2 +# define irqsafe_cpu_cmpxchg_double_2(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + irqsafe_generic_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# ifndef irqsafe_cpu_cmpxchg_double_4 +# define irqsafe_cpu_cmpxchg_double_4(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + irqsafe_generic_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# ifndef irqsafe_cpu_cmpxchg_double_8 +# define irqsafe_cpu_cmpxchg_double_8(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + irqsafe_generic_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# define irqsafe_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + __pcpu_double_call_return_int(irqsafe_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2)) +#endif + #endif /* __LINUX_PERCPU_H */ -- cgit v1.2.3 From 2e820f58f7ad8eaca2f194ccdfea0de63e9c6d78 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Mon, 9 Feb 2009 12:05:50 -0800 Subject: xen/irq: implement bind_interdomain_evtchn_to_irqhandler for backend drivers Impact: new Xen-internal API Signed-off-by: Ian Campbell Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Konrad Rzeszutek Wilk --- drivers/xen/events.c | 38 ++++++++++++++++++++++++++++++++++++++ include/xen/events.h | 6 ++++++ 2 files changed, 44 insertions(+) (limited to 'include') diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 74681478100a..a41601675633 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -864,6 +864,21 @@ static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu) return irq; } +static int bind_interdomain_evtchn_to_irq(unsigned int remote_domain, + unsigned int remote_port) +{ + struct evtchn_bind_interdomain bind_interdomain; + int err; + + bind_interdomain.remote_dom = remote_domain; + bind_interdomain.remote_port = remote_port; + + err = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain, + &bind_interdomain); + + return err ? : bind_evtchn_to_irq(bind_interdomain.local_port); +} + int bind_virq_to_irq(unsigned int virq, unsigned int cpu) { @@ -959,6 +974,29 @@ int bind_evtchn_to_irqhandler(unsigned int evtchn, } EXPORT_SYMBOL_GPL(bind_evtchn_to_irqhandler); +int bind_interdomain_evtchn_to_irqhandler(unsigned int remote_domain, + unsigned int remote_port, + irq_handler_t handler, + unsigned long irqflags, + const char *devname, + void *dev_id) +{ + int irq, retval; + + irq = bind_interdomain_evtchn_to_irq(remote_domain, remote_port); + if (irq < 0) + return irq; + + retval = request_irq(irq, handler, irqflags, devname, dev_id); + if (retval != 0) { + unbind_from_irq(irq); + return retval; + } + + return irq; +} +EXPORT_SYMBOL_GPL(bind_interdomain_evtchn_to_irqhandler); + int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) diff --git a/include/xen/events.h b/include/xen/events.h index 00f53ddcc062..bd03b1e4a2f4 100644 --- a/include/xen/events.h +++ b/include/xen/events.h @@ -23,6 +23,12 @@ int bind_ipi_to_irqhandler(enum ipi_vector ipi, unsigned long irqflags, const char *devname, void *dev_id); +int bind_interdomain_evtchn_to_irqhandler(unsigned int remote_domain, + unsigned int remote_port, + irq_handler_t handler, + unsigned long irqflags, + const char *devname, + void *dev_id); /* * Common unbind function for all event sources. Takes IRQ to unbind from. -- cgit v1.2.3 From 52208ae3fc60cbcb214c10fb8b82304199e2cc3a Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Thu, 24 Feb 2011 16:58:20 -0800 Subject: [SCSI] target: Fix t_transport_aborted handling in LUN_RESET + active I/O shutdown This patch addresses two outstanding bugs related to T_TASK(cmd)->t_transport_aborted handling during TMR LUN_RESET and active I/O shutdown. This first involves adding two explict t_transport_aborted=1 assignments in core_tmr_lun_reset() in order to signal the task has been aborted, and updating transport_generic_wait_for_tasks() to skip sleeping when t_transport_aborted=1 has been set. This fixes an issue where transport_generic_wait_for_tasks() would end up sleeping indefinately when called from fabric module context while TMR LUN_RESET was happening with long outstanding backend struct se_task not yet being completed. The second adds a missing call to transport_remove_task_from_execute_queue() when task->task_execute_queue=1 is set in order to fix an OOPs when task->t_execute_list has not been dropped. It also fixes the same case in transport_processing_shutdown() to prevent the issue from happening during active I/O struct se_device shutdown. Signed-off-by: Nicholas A. Bellinger Signed-off-by: James Bottomley --- drivers/target/target_core_tmr.c | 5 +++++ drivers/target/target_core_transport.c | 8 ++++++-- include/target/target_core_transport.h | 2 ++ 3 files changed, 13 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 158cecbec718..4a109835e420 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -282,6 +282,9 @@ int core_tmr_lun_reset( atomic_set(&task->task_active, 0); atomic_set(&task->task_stop, 0); + } else { + if (atomic_read(&task->task_execute_queue) != 0) + transport_remove_task_from_execute_queue(task, dev); } __transport_stop_task_timer(task, &flags); @@ -301,6 +304,7 @@ int core_tmr_lun_reset( DEBUG_LR("LUN_RESET: got t_transport_active = 1 for" " task: %p, t_fe_count: %d dev: %p\n", task, fe_count, dev); + atomic_set(&T_TASK(cmd)->t_transport_aborted, 1); spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags); core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, fe_count); @@ -310,6 +314,7 @@ int core_tmr_lun_reset( } DEBUG_LR("LUN_RESET: Got t_transport_active = 0 for task: %p," " t_fe_count: %d dev: %p\n", task, fe_count, dev); + atomic_set(&T_TASK(cmd)->t_transport_aborted, 1); spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags); core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, fe_count); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 236e22d8cfae..4bbf6c147f89 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1207,7 +1207,7 @@ transport_get_task_from_execute_queue(struct se_device *dev) * * */ -static void transport_remove_task_from_execute_queue( +void transport_remove_task_from_execute_queue( struct se_task *task, struct se_device *dev) { @@ -5549,7 +5549,8 @@ static void transport_generic_wait_for_tasks( atomic_set(&T_TASK(cmd)->transport_lun_stop, 0); } - if (!atomic_read(&T_TASK(cmd)->t_transport_active)) + if (!atomic_read(&T_TASK(cmd)->t_transport_active) || + atomic_read(&T_TASK(cmd)->t_transport_aborted)) goto remove; atomic_set(&T_TASK(cmd)->t_transport_stop, 1); @@ -5956,6 +5957,9 @@ static void transport_processing_shutdown(struct se_device *dev) atomic_set(&task->task_active, 0); atomic_set(&task->task_stop, 0); + } else { + if (atomic_read(&task->task_execute_queue) != 0) + transport_remove_task_from_execute_queue(task, dev); } __transport_stop_task_timer(task, &flags); diff --git a/include/target/target_core_transport.h b/include/target/target_core_transport.h index 246940511579..2e8ec51f0615 100644 --- a/include/target/target_core_transport.h +++ b/include/target/target_core_transport.h @@ -135,6 +135,8 @@ extern void transport_complete_task(struct se_task *, int); extern void transport_add_task_to_execute_queue(struct se_task *, struct se_task *, struct se_device *); +extern void transport_remove_task_from_execute_queue(struct se_task *, + struct se_device *); unsigned char *transport_dump_cmd_direction(struct se_cmd *); extern void transport_dump_dev_state(struct se_device *, char *, int *); extern void transport_dump_dev_info(struct se_device *, struct se_lun *, -- cgit v1.2.3 From 059f04d4aa60f89b7ad6ca118856f4cb59d9257f Mon Sep 17 00:00:00 2001 From: Bhanu Prakash Gollapudi Date: Fri, 25 Feb 2011 15:03:07 -0800 Subject: [SCSI] libfc: introduce __fc_fill_fc_hdr that accepts fc_hdr as an argument fc_fill_fc_hdr() expects fc_frame as an argument. Introduce __fc_fill_fc_hdr to accept fc_frame_header as an argument. Signed-off-by: Bhanu Prakash Gollapudi Signed-off-by: Robert Love Signed-off-by: James Bottomley --- include/scsi/fc_encode.h | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/scsi/fc_encode.h b/include/scsi/fc_encode.h index 6d293c846a46..be418d8448a5 100644 --- a/include/scsi/fc_encode.h +++ b/include/scsi/fc_encode.h @@ -46,16 +46,11 @@ struct fc_ct_req { } payload; }; -/** - * fill FC header fields in specified fc_frame - */ -static inline void fc_fill_fc_hdr(struct fc_frame *fp, enum fc_rctl r_ctl, - u32 did, u32 sid, enum fc_fh_type type, - u32 f_ctl, u32 parm_offset) +static inline void __fc_fill_fc_hdr(struct fc_frame_header *fh, + enum fc_rctl r_ctl, + u32 did, u32 sid, enum fc_fh_type type, + u32 f_ctl, u32 parm_offset) { - struct fc_frame_header *fh; - - fh = fc_frame_header_get(fp); WARN_ON(r_ctl == 0); fh->fh_r_ctl = r_ctl; hton24(fh->fh_d_id, did); @@ -67,6 +62,19 @@ static inline void fc_fill_fc_hdr(struct fc_frame *fp, enum fc_rctl r_ctl, fh->fh_parm_offset = htonl(parm_offset); } +/** + * fill FC header fields in specified fc_frame + */ +static inline void fc_fill_fc_hdr(struct fc_frame *fp, enum fc_rctl r_ctl, + u32 did, u32 sid, enum fc_fh_type type, + u32 f_ctl, u32 parm_offset) +{ + struct fc_frame_header *fh; + + fh = fc_frame_header_get(fp); + __fc_fill_fc_hdr(fh, r_ctl, did, sid, type, f_ctl, parm_offset); +} + /** * fc_adisc_fill() - Fill in adisc request frame * @lport: local port. -- cgit v1.2.3 From f4d2b2b6ea8abd0df72a31b4724522a277af6a6c Mon Sep 17 00:00:00 2001 From: Bhanu Prakash Gollapudi Date: Fri, 25 Feb 2011 15:03:12 -0800 Subject: [SCSI] libfcoe: Move FCOE_MTU definition from fcoe.h to libfcoe.h both fcoe and bnx2fc drivers can access the common definition of FCOE_MTU. Signed-off-by: Bhanu Prakash Gollapudi Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.h | 6 ------ include/scsi/libfcoe.h | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index d775128398e9..408a6fd78fb4 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -40,12 +40,6 @@ #define FCOE_MIN_XID 0x0000 /* the min xid supported by fcoe_sw */ #define FCOE_MAX_XID 0x0FFF /* the max xid supported by fcoe_sw */ -/* - * Max MTU for FCoE: 14 (FCoE header) + 24 (FC header) + 2112 (max FC payload) - * + 4 (FC CRC) + 4 (FCoE trailer) = 2158 bytes - */ -#define FCOE_MTU 2158 - unsigned int fcoe_debug_logging; module_param_named(debug_logging, fcoe_debug_logging, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index e5024634bfab..8c1638b8c28e 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -32,6 +32,12 @@ #define FCOE_MAX_CMD_LEN 16 /* Supported CDB length */ +/* + * Max MTU for FCoE: 14 (FCoE header) + 24 (FC header) + 2112 (max FC payload) + * + 4 (FC CRC) + 4 (FCoE trailer) = 2158 bytes + */ +#define FCOE_MTU 2158 + /* * FIP tunable parameters. */ -- cgit v1.2.3 From 486af1896f3a4a388410215c5a2014b9d09a79f5 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 1 Mar 2011 14:32:27 +1000 Subject: drm/radeon: add new getparam for number of backends. This allows userspace to work out how many DBs there are for conditional rendering to work. Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/radeon_drv.c | 2 +- drivers/gpu/drm/radeon/radeon_kms.c | 11 +++++++++++ include/drm/radeon_drm.h | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 6c1161505582..63d2de8771dc 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -49,7 +49,7 @@ * - 2.6.0 - add tiling config query (r6xx+), add initial HiZ support (r300->r500) * 2.7.0 - fixups for r600 2D tiling support. (no external ABI change), add eg dyn gpr regs * 2.8.0 - pageflip support, r500 US_FORMAT regs. r500 ARGB2101010 colorbuf, r300->r500 CMASK, clock crystal query - * 2.9.0 - r600 tiling (s3tc,rgtc) working, SET_PREDICATION packet 3 on r600 + eg + * 2.9.0 - r600 tiling (s3tc,rgtc) working, SET_PREDICATION packet 3 on r600 + eg, backend query */ #define KMS_DRIVER_MAJOR 2 #define KMS_DRIVER_MINOR 9 diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 4057ebf5195d..68a7b22c1fe9 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -205,6 +205,17 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) /* return clock value in KHz */ value = rdev->clock.spll.reference_freq * 10; break; + case RADEON_INFO_NUM_BACKENDS: + if (rdev->family >= CHIP_CEDAR) + value = rdev->config.evergreen.max_backends; + else if (rdev->family >= CHIP_RV770) + value = rdev->config.rv770.max_backends; + else if (rdev->family >= CHIP_R600) + value = rdev->config.r600.max_backends; + else { + return -EINVAL; + } + break; default: DRM_DEBUG_KMS("Invalid request %d\n", info->request); return -EINVAL; diff --git a/include/drm/radeon_drm.h b/include/drm/radeon_drm.h index e5c607a02d57..3dec41cf8342 100644 --- a/include/drm/radeon_drm.h +++ b/include/drm/radeon_drm.h @@ -908,6 +908,7 @@ struct drm_radeon_cs { #define RADEON_INFO_WANT_HYPERZ 0x07 #define RADEON_INFO_WANT_CMASK 0x08 /* get access to CMASK on r300 */ #define RADEON_INFO_CLOCK_CRYSTAL_FREQ 0x09 /* clock crystal frequency */ +#define RADEON_INFO_NUM_BACKENDS 0x0a /* DB/backends for r600+ - need for OQ */ struct drm_radeon_info { uint32_t request; -- cgit v1.2.3 From 9b4f06fae48765650f3ac9c876bc629d804bd763 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Mon, 28 Feb 2011 10:32:22 +0100 Subject: asm-generic/user.h: Fix spelling in comment Fix two misspellings. Signed-off-by: Tobias Klauser Signed-off-by: Jiri Kosina --- include/asm-generic/user.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-generic/user.h b/include/asm-generic/user.h index 8b9c3c960aeb..35638c34700f 100644 --- a/include/asm-generic/user.h +++ b/include/asm-generic/user.h @@ -1,8 +1,8 @@ #ifndef __ASM_GENERIC_USER_H #define __ASM_GENERIC_USER_H /* - * This file may define a 'struct user' structure. However, it it only - * used for a.out file, which are not supported on new architectures. + * This file may define a 'struct user' structure. However, it is only + * used for a.out files, which are not supported on new architectures. */ #endif /* __ASM_GENERIC_USER_H */ -- cgit v1.2.3 From 271d81b84171d84723357ae6d172ec16b0d8139c Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 1 Mar 2011 15:24:41 +0000 Subject: drm/i915: Allow relocation deltas outside of target bo Userspace has a legitimate requirement to use a delta that points to outside of the target bo, and so we need to enable this. (As this is an abi break, albeit a relaxation of the current restrictions, mark the change with a new flag.) Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/i915_dma.c | 3 +++ drivers/gpu/drm/i915/i915_gem_execbuffer.c | 10 ---------- include/drm/i915_drm.h | 1 + 3 files changed, 4 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index ffa2196eb3b9..51150692af2d 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -772,6 +772,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_EXEC_CONSTANTS: value = INTEL_INFO(dev)->gen >= 4; break; + case I915_PARAM_HAS_RELAXED_DELTA: + value = 1; + break; default: DRM_DEBUG_DRIVER("Unknown parameter %d\n", param->param); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 71a4a3b69158..1c3b76a8a6fb 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -350,16 +350,6 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, return ret; } - /* and points to somewhere within the target object. */ - if (unlikely(reloc->delta >= target_obj->size)) { - DRM_ERROR("Relocation beyond target object bounds: " - "obj %p target %d delta %d size %d.\n", - obj, reloc->target_handle, - (int) reloc->delta, - (int) target_obj->size); - return ret; - } - reloc->delta += target_offset; if (obj->base.write_domain == I915_GEM_DOMAIN_CPU) { uint32_t page_offset = reloc->offset & ~PAGE_MASK; diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index 0039f1f97ad8..c4d6dbfa3ff4 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -290,6 +290,7 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_HAS_RELAXED_FENCING 12 #define I915_PARAM_HAS_COHERENT_RINGS 13 #define I915_PARAM_HAS_EXEC_CONSTANTS 14 +#define I915_PARAM_HAS_RELAXED_DELTA 15 typedef struct drm_i915_getparam { int param; -- cgit v1.2.3 From f635bd11c8d332d917fb9a4cad3071b2357d5b2a Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Thu, 24 Feb 2011 19:30:59 +0100 Subject: HID: Do not create input devices for feature reports When the multi input quirk is set, there is a new input device created for every feature report. Since the idea is to present features per hid device, not per input device, revert back to the original report loop and change the feature_mapping() callback to not take the input device as argument. Signed-off-by: Henrik Rydberg Tested-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 30 +++++++++++++++++++++--------- drivers/hid/hid-multitouch.c | 2 +- include/linux/hid.h | 2 +- 3 files changed, 23 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 7f552bfad32c..ebcc02a1c1c9 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -290,14 +290,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel goto ignore; } - if (field->report_type == HID_FEATURE_REPORT) { - if (device->driver->feature_mapping) { - device->driver->feature_mapping(device, hidinput, field, - usage); - } - goto ignore; - } - if (device->driver->input_mapping) { int ret = device->driver->input_mapping(device, hidinput, field, usage, &bit, &max); @@ -835,6 +827,24 @@ static void hidinput_close(struct input_dev *dev) hid_hw_close(hid); } +static void report_features(struct hid_device *hid) +{ + struct hid_driver *drv = hid->driver; + struct hid_report_enum *rep_enum; + struct hid_report *rep; + int i, j; + + if (!drv->feature_mapping) + return; + + rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) + for (i = 0; i < rep->maxfield; i++) + for (j = 0; j < rep->field[i]->maxusage; j++) + drv->feature_mapping(hid, rep->field[i], + rep->field[i]->usage + j); +} + /* * Register the input device; print a message. * Configure the input layer interface @@ -863,7 +873,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) return -1; } - for (k = HID_INPUT_REPORT; k <= HID_FEATURE_REPORT; k++) { + report_features(hid); + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { if (k == HID_OUTPUT_REPORT && hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) continue; diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 07d3183fdde5..2bbc9545f5cc 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -122,7 +122,7 @@ struct mt_class mt_classes[] = { { } }; -static void mt_feature_mapping(struct hid_device *hdev, struct hid_input *hi, +static void mt_feature_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { if (usage->hid == HID_DG_INPUTMODE) { diff --git a/include/linux/hid.h b/include/linux/hid.h index d91c25e253c8..fc5faf60f6df 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -638,7 +638,7 @@ struct hid_driver { struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max); void (*feature_mapping)(struct hid_device *hdev, - struct hid_input *hidinput, struct hid_field *field, + struct hid_field *field, struct hid_usage *usage); #ifdef CONFIG_PM int (*suspend)(struct hid_device *hdev, pm_message_t message); -- cgit v1.2.3 From d714d1979d7b4df7e2c127407f4014ce71f73cd0 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Tue, 22 Feb 2011 20:22:44 -0700 Subject: dt: eliminate of_platform_driver shim code Commit eca393016, "of: Merge of_platform_bus_type with platform_bus_type" added a shim to allow of_platform_drivers to get registers onto the platform bus so that there was time to migrate the existing drivers to the platform_bus_type. This patch removes the shim since there are no more users of the old interface. Signed-off-by: Grant Likely --- drivers/of/platform.c | 68 --------------------------------------------- include/linux/of_platform.h | 12 +------- 2 files changed, 1 insertion(+), 79 deletions(-) (limited to 'include') diff --git a/drivers/of/platform.c b/drivers/of/platform.c index b71d0cdc4209..1ce4c45c4ab2 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -42,74 +42,6 @@ struct platform_device *of_find_device_by_node(struct device_node *np) } EXPORT_SYMBOL(of_find_device_by_node); -static int platform_driver_probe_shim(struct platform_device *pdev) -{ - struct platform_driver *pdrv; - struct of_platform_driver *ofpdrv; - const struct of_device_id *match; - - pdrv = container_of(pdev->dev.driver, struct platform_driver, driver); - ofpdrv = container_of(pdrv, struct of_platform_driver, platform_driver); - - /* There is an unlikely chance that an of_platform driver might match - * on a non-OF platform device. If so, then of_match_device() will - * come up empty. Return -EINVAL in this case so other drivers get - * the chance to bind. */ - match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); - return match ? ofpdrv->probe(pdev, match) : -EINVAL; -} - -static void platform_driver_shutdown_shim(struct platform_device *pdev) -{ - struct platform_driver *pdrv; - struct of_platform_driver *ofpdrv; - - pdrv = container_of(pdev->dev.driver, struct platform_driver, driver); - ofpdrv = container_of(pdrv, struct of_platform_driver, platform_driver); - ofpdrv->shutdown(pdev); -} - -/** - * of_register_platform_driver - */ -int of_register_platform_driver(struct of_platform_driver *drv) -{ - char *of_name; - - /* setup of_platform_driver to platform_driver adaptors */ - drv->platform_driver.driver = drv->driver; - - /* Prefix the driver name with 'of:' to avoid namespace collisions - * and bogus matches. There are some drivers in the tree that - * register both an of_platform_driver and a platform_driver with - * the same name. This is a temporary measure until they are all - * cleaned up --gcl July 29, 2010 */ - of_name = kmalloc(strlen(drv->driver.name) + 5, GFP_KERNEL); - if (!of_name) - return -ENOMEM; - sprintf(of_name, "of:%s", drv->driver.name); - drv->platform_driver.driver.name = of_name; - - if (drv->probe) - drv->platform_driver.probe = platform_driver_probe_shim; - drv->platform_driver.remove = drv->remove; - if (drv->shutdown) - drv->platform_driver.shutdown = platform_driver_shutdown_shim; - drv->platform_driver.suspend = drv->suspend; - drv->platform_driver.resume = drv->resume; - - return platform_driver_register(&drv->platform_driver); -} -EXPORT_SYMBOL(of_register_platform_driver); - -void of_unregister_platform_driver(struct of_platform_driver *drv) -{ - platform_driver_unregister(&drv->platform_driver); - kfree(drv->platform_driver.driver.name); - drv->platform_driver.driver.name = NULL; -} -EXPORT_SYMBOL(of_unregister_platform_driver); - #if defined(CONFIG_PPC_DCR) #include #endif diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h index 048949fa1d10..17c7e21c0bd7 100644 --- a/include/linux/of_platform.h +++ b/include/linux/of_platform.h @@ -23,13 +23,7 @@ * of_platform_driver - Legacy of-aware driver for platform devices. * * An of_platform_driver driver is attached to a basic platform_device on - * ether the "platform bus" (platform_bus_type), or the ibm ebus - * (ibmebus_bus_type). - * - * of_platform_driver is being phased out when used with the platform_bus_type, - * and regular platform_drivers should be used instead. When the transition - * is complete, only ibmebus will be using this structure, and the - * platform_driver member of this structure will be removed. + * the ibm ebus (ibmebus_bus_type). */ struct of_platform_driver { @@ -42,15 +36,11 @@ struct of_platform_driver int (*shutdown)(struct platform_device* dev); struct device_driver driver; - struct platform_driver platform_driver; }; #define to_of_platform_driver(drv) \ container_of(drv,struct of_platform_driver, driver) /* Platform drivers register/unregister */ -extern int of_register_platform_driver(struct of_platform_driver *drv); -extern void of_unregister_platform_driver(struct of_platform_driver *drv); - extern struct platform_device *of_device_alloc(struct device_node *np, const char *bus_id, struct device *parent); -- cgit v1.2.3 From 450adcbe518ab3a3953d8475309525d22de77cba Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Tue, 1 Mar 2011 13:40:54 -0500 Subject: blk-throttle: Do not use kblockd workqueue for throtl work o Dominik Klein reported a system hang issue while doing some blkio throttling testing. https://lkml.org/lkml/2011/2/24/173 o Some tracing revealed that CFQ was not dispatching any more jobs as queue unplug was not happening. And queue unplug was not happening because unplug work was not being called as there was one throttling work on same cpu which as not finished yet. And throttling work had not finished as it was tyring to dispatch a bio to CFQ but all the request descriptors were consume to it was put to sleep. o So basically it is a cyclic dependecny between CFQ unplug work and throtl dispatch work. Tejun suggested that use separate workqueue for such cases. o This patch uses a separate workqueue for throttle related work and does not rely on kblockd workqueue anymore. Cc: stable@kernel.org Reported-by: Dominik Klein Signed-off-by: Vivek Goyal Acked-by: Tejun Heo Signed-off-by: Jens Axboe --- block/blk-core.c | 7 ------- block/blk-throttle.c | 29 ++++++++++++++++++----------- include/linux/blkdev.h | 3 --- 3 files changed, 18 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 2f4002f79a24..792ece276160 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2610,13 +2610,6 @@ int kblockd_schedule_work(struct request_queue *q, struct work_struct *work) } EXPORT_SYMBOL(kblockd_schedule_work); -int kblockd_schedule_delayed_work(struct request_queue *q, - struct delayed_work *dwork, unsigned long delay) -{ - return queue_delayed_work(kblockd_workqueue, dwork, delay); -} -EXPORT_SYMBOL(kblockd_schedule_delayed_work); - int __init blk_dev_init(void) { BUILD_BUG_ON(__REQ_NR_BITS > 8 * diff --git a/block/blk-throttle.c b/block/blk-throttle.c index a89043a3caa4..e36cc10a346c 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -20,6 +20,11 @@ static int throtl_quantum = 32; /* Throttling is performed over 100ms slice and after that slice is renewed */ static unsigned long throtl_slice = HZ/10; /* 100 ms */ +/* A workqueue to queue throttle related work */ +static struct workqueue_struct *kthrotld_workqueue; +static void throtl_schedule_delayed_work(struct throtl_data *td, + unsigned long delay); + struct throtl_rb_root { struct rb_root rb; struct rb_node *left; @@ -345,10 +350,9 @@ static void throtl_schedule_next_dispatch(struct throtl_data *td) update_min_dispatch_time(st); if (time_before_eq(st->min_disptime, jiffies)) - throtl_schedule_delayed_work(td->queue, 0); + throtl_schedule_delayed_work(td, 0); else - throtl_schedule_delayed_work(td->queue, - (st->min_disptime - jiffies)); + throtl_schedule_delayed_work(td, (st->min_disptime - jiffies)); } static inline void @@ -815,10 +819,10 @@ void blk_throtl_work(struct work_struct *work) } /* Call with queue lock held */ -void throtl_schedule_delayed_work(struct request_queue *q, unsigned long delay) +static void +throtl_schedule_delayed_work(struct throtl_data *td, unsigned long delay) { - struct throtl_data *td = q->td; struct delayed_work *dwork = &td->throtl_work; if (total_nr_queued(td) > 0) { @@ -827,12 +831,11 @@ void throtl_schedule_delayed_work(struct request_queue *q, unsigned long delay) * Cancel that and schedule a new one. */ __cancel_delayed_work(dwork); - kblockd_schedule_delayed_work(q, dwork, delay); + queue_delayed_work(kthrotld_workqueue, dwork, delay); throtl_log(td, "schedule work. delay=%lu jiffies=%lu", delay, jiffies); } } -EXPORT_SYMBOL(throtl_schedule_delayed_work); static void throtl_destroy_tg(struct throtl_data *td, struct throtl_grp *tg) @@ -920,7 +923,7 @@ static void throtl_update_blkio_group_read_bps(void *key, smp_mb__after_atomic_inc(); /* Schedule a work now to process the limit change */ - throtl_schedule_delayed_work(td->queue, 0); + throtl_schedule_delayed_work(td, 0); } static void throtl_update_blkio_group_write_bps(void *key, @@ -934,7 +937,7 @@ static void throtl_update_blkio_group_write_bps(void *key, smp_mb__before_atomic_inc(); atomic_inc(&td->limits_changed); smp_mb__after_atomic_inc(); - throtl_schedule_delayed_work(td->queue, 0); + throtl_schedule_delayed_work(td, 0); } static void throtl_update_blkio_group_read_iops(void *key, @@ -948,7 +951,7 @@ static void throtl_update_blkio_group_read_iops(void *key, smp_mb__before_atomic_inc(); atomic_inc(&td->limits_changed); smp_mb__after_atomic_inc(); - throtl_schedule_delayed_work(td->queue, 0); + throtl_schedule_delayed_work(td, 0); } static void throtl_update_blkio_group_write_iops(void *key, @@ -962,7 +965,7 @@ static void throtl_update_blkio_group_write_iops(void *key, smp_mb__before_atomic_inc(); atomic_inc(&td->limits_changed); smp_mb__after_atomic_inc(); - throtl_schedule_delayed_work(td->queue, 0); + throtl_schedule_delayed_work(td, 0); } void throtl_shutdown_timer_wq(struct request_queue *q) @@ -1135,6 +1138,10 @@ void blk_throtl_exit(struct request_queue *q) static int __init throtl_init(void) { + kthrotld_workqueue = alloc_workqueue("kthrotld", WQ_MEM_RECLAIM, 0); + if (!kthrotld_workqueue) + panic("Failed to create kthrotld\n"); + blkio_policy_register(&blkio_policy_throtl); return 0; } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 4d18ff34670a..dd8cd0f47e3a 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1088,7 +1088,6 @@ static inline void put_dev_sector(Sector p) struct work_struct; int kblockd_schedule_work(struct request_queue *q, struct work_struct *work); -int kblockd_schedule_delayed_work(struct request_queue *q, struct delayed_work *dwork, unsigned long delay); #ifdef CONFIG_BLK_CGROUP /* @@ -1136,7 +1135,6 @@ static inline uint64_t rq_io_start_time_ns(struct request *req) extern int blk_throtl_init(struct request_queue *q); extern void blk_throtl_exit(struct request_queue *q); extern int blk_throtl_bio(struct request_queue *q, struct bio **bio); -extern void throtl_schedule_delayed_work(struct request_queue *q, unsigned long delay); extern void throtl_shutdown_timer_wq(struct request_queue *q); #else /* CONFIG_BLK_DEV_THROTTLING */ static inline int blk_throtl_bio(struct request_queue *q, struct bio **bio) @@ -1146,7 +1144,6 @@ static inline int blk_throtl_bio(struct request_queue *q, struct bio **bio) static inline int blk_throtl_init(struct request_queue *q) { return 0; } static inline int blk_throtl_exit(struct request_queue *q) { return 0; } -static inline void throtl_schedule_delayed_work(struct request_queue *q, unsigned long delay) {} static inline void throtl_shutdown_timer_wq(struct request_queue *q) {} #endif /* CONFIG_BLK_DEV_THROTTLING */ -- cgit v1.2.3 From c8dcfd8a046c1f49af0c15726761af17b957962d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 27 Feb 2011 22:08:00 +0100 Subject: cfg80211: add a field for the bitrate of the last rx data packet from a station Also fix a typo in the STATION_INFO_TX_BITRATE description Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- include/linux/nl80211.h | 3 +++ include/net/cfg80211.h | 5 ++++- net/wireless/nl80211.c | 56 ++++++++++++++++++++++++++++++++----------------- 3 files changed, 44 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 821ffb954f14..30022189104d 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1243,6 +1243,8 @@ enum nl80211_rate_info { * @NL80211_STA_INFO_LLID: the station's mesh LLID * @NL80211_STA_INFO_PLID: the station's mesh PLID * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station + * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested + * attribute, like NL80211_STA_INFO_TX_BITRATE. * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -1261,6 +1263,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_TX_RETRIES, NL80211_STA_INFO_TX_FAILED, NL80211_STA_INFO_SIGNAL_AVG, + NL80211_STA_INFO_RX_BITRATE, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 679a0494b5f2..1ac5786da14b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -413,7 +413,7 @@ struct station_parameters { * @STATION_INFO_PLID: @plid filled * @STATION_INFO_PLINK_STATE: @plink_state filled * @STATION_INFO_SIGNAL: @signal filled - * @STATION_INFO_TX_BITRATE: @tx_bitrate fields are filled + * @STATION_INFO_TX_BITRATE: @txrate fields are filled * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs) * @STATION_INFO_RX_PACKETS: @rx_packets filled * @STATION_INFO_TX_PACKETS: @tx_packets filled @@ -421,6 +421,7 @@ struct station_parameters { * @STATION_INFO_TX_FAILED: @tx_failed filled * @STATION_INFO_RX_DROP_MISC: @rx_dropped_misc filled * @STATION_INFO_SIGNAL_AVG: @signal_avg filled + * @STATION_INFO_RX_BITRATE: @rxrate fields are filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -437,6 +438,7 @@ enum station_info_flags { STATION_INFO_TX_FAILED = 1<<11, STATION_INFO_RX_DROP_MISC = 1<<12, STATION_INFO_SIGNAL_AVG = 1<<13, + STATION_INFO_RX_BITRATE = 1<<14, }; /** @@ -506,6 +508,7 @@ struct station_info { s8 signal; s8 signal_avg; struct rate_info txrate; + struct rate_info rxrate; u32 rx_packets; u32 tx_packets; u32 tx_retries; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 864ddfbeff2f..4ebce4284e9d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1968,13 +1968,41 @@ static int parse_station_flags(struct genl_info *info, return 0; } +static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, + int attr) +{ + struct nlattr *rate; + u16 bitrate; + + rate = nla_nest_start(msg, attr); + if (!rate) + goto nla_put_failure; + + /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ + bitrate = cfg80211_calculate_bitrate(info); + if (bitrate > 0) + NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); + + if (info->flags & RATE_INFO_FLAGS_MCS) + NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, info->mcs); + if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) + NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH); + if (info->flags & RATE_INFO_FLAGS_SHORT_GI) + NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI); + + nla_nest_end(msg, rate); + return true; + +nla_put_failure: + return false; +} + static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo) { void *hdr; - struct nlattr *sinfoattr, *txrate; - u16 bitrate; + struct nlattr *sinfoattr; hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); if (!hdr) @@ -2013,24 +2041,14 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG, sinfo->signal_avg); if (sinfo->filled & STATION_INFO_TX_BITRATE) { - txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE); - if (!txrate) + if (!nl80211_put_sta_rate(msg, &sinfo->txrate, + NL80211_STA_INFO_TX_BITRATE)) + goto nla_put_failure; + } + if (sinfo->filled & STATION_INFO_RX_BITRATE) { + if (!nl80211_put_sta_rate(msg, &sinfo->rxrate, + NL80211_STA_INFO_RX_BITRATE)) goto nla_put_failure; - - /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ - bitrate = cfg80211_calculate_bitrate(&sinfo->txrate); - if (bitrate > 0) - NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); - - if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS) - NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, - sinfo->txrate.mcs); - if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) - NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH); - if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI) - NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI); - - nla_nest_end(msg, txrate); } if (sinfo->filled & STATION_INFO_RX_PACKETS) NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS, -- cgit v1.2.3 From bb879101606dd7235d8f4ecd0f707b63281d0838 Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Mon, 28 Feb 2011 16:55:28 -0800 Subject: bq20z75: Add optional battery detect gpio Adding support for an optional gpio for battery detection. This is passed in through the i2c platform data. It also accepts another field, battery_detect_present to signify the gpio state which means the battery is present, either 0 (low) or 1 (high). Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/bq20z75.c | 160 ++++++++++++++++++++++++++++++++++-------- include/linux/power/bq20z75.h | 37 ++++++++++ 2 files changed, 166 insertions(+), 31 deletions(-) create mode 100644 include/linux/power/bq20z75.h (limited to 'include') diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index 4141775e5ff6..a51e98d74d34 100644 --- a/drivers/power/bq20z75.c +++ b/drivers/power/bq20z75.c @@ -25,6 +25,10 @@ #include #include #include +#include +#include + +#include enum { REG_MANUFACTURER_DATA, @@ -141,8 +145,13 @@ static enum power_supply_property bq20z75_properties[] = { }; struct bq20z75_info { - struct i2c_client *client; - struct power_supply power_supply; + struct i2c_client *client; + struct power_supply power_supply; + struct bq20z75_platform_data *pdata; + bool is_present; + bool gpio_detect; + bool enable_detection; + int irq; }; static int bq20z75_read_word_data(struct i2c_client *client, u8 address) @@ -179,6 +188,18 @@ static int bq20z75_get_battery_presence_and_health( union power_supply_propval *val) { s32 ret; + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + + if (psp == POWER_SUPPLY_PROP_PRESENT && + bq20z75_device->gpio_detect) { + ret = gpio_get_value( + bq20z75_device->pdata->battery_detect); + if (ret == bq20z75_device->pdata->battery_detect_present) + val->intval = 1; + else + val->intval = 0; + return ret; + } /* Write to ManufacturerAccess with * ManufacturerAccess command and then @@ -192,8 +213,11 @@ static int bq20z75_get_battery_presence_and_health( ret = bq20z75_read_word_data(client, bq20z75_data[REG_MANUFACTURER_DATA].addr); - if (ret < 0) + if (ret < 0) { + if (psp == POWER_SUPPLY_PROP_PRESENT) + val->intval = 0; /* battery removed */ return ret; + } if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value || ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) { @@ -397,8 +421,7 @@ static int bq20z75_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - int ps_index; - int ret; + int ret = 0; struct bq20z75_info *bq20z75_device = container_of(psy, struct bq20z75_info, power_supply); struct i2c_client *client = bq20z75_device->client; @@ -407,8 +430,6 @@ static int bq20z75_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_HEALTH: ret = bq20z75_get_battery_presence_and_health(client, psp, val); - if (ret) - return ret; break; case POWER_SUPPLY_PROP_TECHNOLOGY: @@ -422,20 +443,15 @@ static int bq20z75_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_FULL: case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: case POWER_SUPPLY_PROP_CAPACITY: - ps_index = bq20z75_get_property_index(client, psp); - if (ps_index < 0) - return ps_index; - - ret = bq20z75_get_battery_capacity(client, ps_index, psp, val); - if (ret) - return ret; + ret = bq20z75_get_property_index(client, psp); + if (ret < 0) + break; + ret = bq20z75_get_battery_capacity(client, ret, psp, val); break; case POWER_SUPPLY_PROP_SERIAL_NUMBER: ret = bq20z75_get_battery_serial_number(client, val); - if (ret) - return ret; break; case POWER_SUPPLY_PROP_STATUS: @@ -446,14 +462,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: - ps_index = bq20z75_get_property_index(client, psp); - if (ps_index < 0) - return ps_index; - - ret = bq20z75_get_battery_property(client, ps_index, psp, val); - if (ret) - return ret; + ret = bq20z75_get_property_index(client, psp); + if (ret < 0) + break; + ret = bq20z75_get_battery_property(client, ret, psp, val); break; default: @@ -462,26 +475,51 @@ static int bq20z75_get_property(struct power_supply *psy, return -EINVAL; } - /* Convert units to match requirements for power supply class */ - bq20z75_unit_adjustment(client, psp, val); + if (!bq20z75_device->enable_detection) + goto done; + + if (!bq20z75_device->gpio_detect && + bq20z75_device->is_present != (ret >= 0)) { + bq20z75_device->is_present = (ret >= 0); + power_supply_changed(&bq20z75_device->power_supply); + } + +done: + if (!ret) { + /* Convert units to match requirements for power supply class */ + bq20z75_unit_adjustment(client, psp, val); + } dev_dbg(&client->dev, "%s: property = %d, value = %d\n", __func__, psp, val->intval); - return 0; + return ret; } -static int bq20z75_probe(struct i2c_client *client, +static irqreturn_t bq20z75_irq(int irq, void *devid) +{ + struct power_supply *battery = devid; + + power_supply_changed(battery); + + return IRQ_HANDLED; +} + +static int __devinit bq20z75_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct bq20z75_info *bq20z75_device; + struct bq20z75_platform_data *pdata = client->dev.platform_data; int rc; + int irq; bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL); if (!bq20z75_device) return -ENOMEM; bq20z75_device->client = client; + bq20z75_device->enable_detection = false; + bq20z75_device->gpio_detect = false; bq20z75_device->power_supply.name = "battery"; bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY; bq20z75_device->power_supply.properties = bq20z75_properties; @@ -489,26 +527,86 @@ static int bq20z75_probe(struct i2c_client *client, ARRAY_SIZE(bq20z75_properties); bq20z75_device->power_supply.get_property = bq20z75_get_property; + if (pdata) { + bq20z75_device->gpio_detect = + gpio_is_valid(pdata->battery_detect); + bq20z75_device->pdata = pdata; + } + i2c_set_clientdata(client, bq20z75_device); + if (!bq20z75_device->gpio_detect) + goto skip_gpio; + + rc = gpio_request(pdata->battery_detect, dev_name(&client->dev)); + if (rc) { + dev_warn(&client->dev, "Failed to request gpio: %d\n", rc); + bq20z75_device->gpio_detect = false; + goto skip_gpio; + } + + rc = gpio_direction_input(pdata->battery_detect); + if (rc) { + dev_warn(&client->dev, "Failed to get gpio as input: %d\n", rc); + gpio_free(pdata->battery_detect); + bq20z75_device->gpio_detect = false; + goto skip_gpio; + } + + irq = gpio_to_irq(pdata->battery_detect); + if (irq <= 0) { + dev_warn(&client->dev, "Failed to get gpio as irq: %d\n", irq); + gpio_free(pdata->battery_detect); + bq20z75_device->gpio_detect = false; + goto skip_gpio; + } + + rc = request_irq(irq, bq20z75_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + dev_name(&client->dev), &bq20z75_device->power_supply); + if (rc) { + dev_warn(&client->dev, "Failed to request irq: %d\n", rc); + gpio_free(pdata->battery_detect); + bq20z75_device->gpio_detect = false; + goto skip_gpio; + } + + bq20z75_device->irq = irq; + +skip_gpio: + rc = power_supply_register(&client->dev, &bq20z75_device->power_supply); if (rc) { dev_err(&client->dev, "%s: Failed to register power supply\n", __func__); - kfree(bq20z75_device); - return rc; + goto exit_psupply; } dev_info(&client->dev, "%s: battery gas gauge device registered\n", client->name); return 0; + +exit_psupply: + if (bq20z75_device->irq) + free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); + if (bq20z75_device->gpio_detect) + gpio_free(pdata->battery_detect); + + kfree(bq20z75_device); + + return rc; } -static int bq20z75_remove(struct i2c_client *client) +static int __devexit bq20z75_remove(struct i2c_client *client) { struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + if (bq20z75_device->irq) + free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); + if (bq20z75_device->gpio_detect) + gpio_free(bq20z75_device->pdata->battery_detect); + power_supply_unregister(&bq20z75_device->power_supply); kfree(bq20z75_device); bq20z75_device = NULL; @@ -544,7 +642,7 @@ static const struct i2c_device_id bq20z75_id[] = { static struct i2c_driver bq20z75_battery_driver = { .probe = bq20z75_probe, - .remove = bq20z75_remove, + .remove = __devexit_p(bq20z75_remove), .suspend = bq20z75_suspend, .resume = bq20z75_resume, .id_table = bq20z75_id, diff --git a/include/linux/power/bq20z75.h b/include/linux/power/bq20z75.h new file mode 100644 index 000000000000..0e1b8a26a9b1 --- /dev/null +++ b/include/linux/power/bq20z75.h @@ -0,0 +1,37 @@ +/* + * Gas Gauge driver for TI's BQ20Z75 + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __LINUX_POWER_BQ20Z75_H_ +#define __LINUX_POWER_BQ20Z75_H_ + +#include +#include + +/** + * struct bq20z75_platform_data - platform data for bq20z75 devices + * @battery_detect: GPIO which is used to detect battery presence + * @battery_detect_present: gpio state when battery is present (0 / 1) + */ +struct bq20z75_platform_data { + int battery_detect; + int battery_detect_present; +}; + +#endif -- cgit v1.2.3 From ff28fcef1bedcfbdf49500fee1573dc2f3eedb22 Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Mon, 28 Feb 2011 16:55:29 -0800 Subject: bq20z75: Add i2c retry mechanism With the support of platform data, now adding support for option i2c retries on read/write failures. Ths is specified through the optional platform data. Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/bq20z75.c | 37 +++++++++++++++++++++++++++++++------ include/linux/power/bq20z75.h | 2 ++ 2 files changed, 33 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index a51e98d74d34..e82d10e25e8c 100644 --- a/drivers/power/bq20z75.c +++ b/drivers/power/bq20z75.c @@ -156,30 +156,55 @@ struct bq20z75_info { static int bq20z75_read_word_data(struct i2c_client *client, u8 address) { - s32 ret; + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + s32 ret = 0; + int retries = 1; + + if (bq20z75_device->pdata) + retries = max(bq20z75_device->pdata->i2c_retry_count + 1, 1); + + while (retries > 0) { + ret = i2c_smbus_read_word_data(client, address); + if (ret >= 0) + break; + retries--; + } - ret = i2c_smbus_read_word_data(client, address); if (ret < 0) { - dev_err(&client->dev, + dev_warn(&client->dev, "%s: i2c read at address 0x%x failed\n", __func__, address); return ret; } + return le16_to_cpu(ret); } static int bq20z75_write_word_data(struct i2c_client *client, u8 address, u16 value) { - s32 ret; + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + s32 ret = 0; + int retries = 1; + + if (bq20z75_device->pdata) + retries = max(bq20z75_device->pdata->i2c_retry_count + 1, 1); + + while (retries > 0) { + ret = i2c_smbus_write_word_data(client, address, + le16_to_cpu(value)); + if (ret >= 0) + break; + retries--; + } - ret = i2c_smbus_write_word_data(client, address, le16_to_cpu(value)); if (ret < 0) { - dev_err(&client->dev, + dev_warn(&client->dev, "%s: i2c write to address 0x%x failed\n", __func__, address); return ret; } + return 0; } diff --git a/include/linux/power/bq20z75.h b/include/linux/power/bq20z75.h index 0e1b8a26a9b1..b0843b68af92 100644 --- a/include/linux/power/bq20z75.h +++ b/include/linux/power/bq20z75.h @@ -28,10 +28,12 @@ * struct bq20z75_platform_data - platform data for bq20z75 devices * @battery_detect: GPIO which is used to detect battery presence * @battery_detect_present: gpio state when battery is present (0 / 1) + * @i2c_retry_count: # of times to retry on i2c IO failure */ struct bq20z75_platform_data { int battery_detect; int battery_detect_present; + int i2c_retry_count; }; #endif -- cgit v1.2.3 From 35c9d267665230cf44445be616d491d3763a5cd3 Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Mon, 28 Feb 2011 16:55:31 -0800 Subject: power_supply: Update power_supply_is_watt_property Update the power_supply_is_watt_property function to include POWER_NOW. Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- include/linux/power_supply.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 20f23fef63cc..204c18dfdc9e 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -251,6 +251,7 @@ static inline bool power_supply_is_watt_property(enum power_supply_property psp) case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_VOLTAGE_AVG: + case POWER_SUPPLY_PROP_POWER_NOW: return 1; default: break; -- cgit v1.2.3 From 1470ddf7f8cecf776921e5ccee72e3d2b3d60cbc Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 1 Mar 2011 02:36:47 +0000 Subject: inet: Remove explicit write references to sk/inet in ip_append_data In order to allow simultaneous calls to ip_append_data on the same socket, it must not modify any shared state in sk or inet (other than those that are designed to allow that such as atomic counters). This patch abstracts out write references to sk and inet_sk in ip_append_data and its friends so that we may use the underlying code in parallel. Signed-off-by: Herbert Xu Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inet_sock.h | 23 +++-- net/ipv4/ip_output.c | 238 ++++++++++++++++++++++++++++-------------------- 2 files changed, 154 insertions(+), 107 deletions(-) (limited to 'include') diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 6e6dfd757682..7a37369f8ea3 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -86,6 +86,19 @@ static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk) return (struct inet_request_sock *)sk; } +struct inet_cork { + unsigned int flags; + unsigned int fragsize; + struct ip_options *opt; + struct dst_entry *dst; + int length; /* Total length of all frames */ + __be32 addr; + struct flowi fl; + struct page *page; + u32 off; + u8 tx_flags; +}; + struct ip_mc_socklist; struct ipv6_pinfo; struct rtable; @@ -143,15 +156,7 @@ struct inet_sock { int mc_index; __be32 mc_addr; struct ip_mc_socklist __rcu *mc_list; - struct { - unsigned int flags; - unsigned int fragsize; - struct ip_options *opt; - struct dst_entry *dst; - int length; /* Total length of all frames */ - __be32 addr; - struct flowi fl; - } cork; + struct inet_cork cork; }; #define IPCORK_OPT 1 /* ip-options has been held in ipcork.opt */ diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index d3a4540cd308..1dd5ecc9a27e 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -733,6 +733,7 @@ csum_page(struct page *page, int offset, int copy) } static inline int ip_ufo_append_data(struct sock *sk, + struct sk_buff_head *queue, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb), void *from, int length, int hh_len, int fragheaderlen, @@ -745,7 +746,7 @@ static inline int ip_ufo_append_data(struct sock *sk, * device, so create one single skb packet containing complete * udp datagram */ - if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) { + if ((skb = skb_peek_tail(queue)) == NULL) { skb = sock_alloc_send_skb(sk, hh_len + fragheaderlen + transhdrlen + 20, (flags & MSG_DONTWAIT), &err); @@ -771,35 +772,24 @@ static inline int ip_ufo_append_data(struct sock *sk, /* specify the length of each IP datagram fragment */ skb_shinfo(skb)->gso_size = mtu - fragheaderlen; skb_shinfo(skb)->gso_type = SKB_GSO_UDP; - __skb_queue_tail(&sk->sk_write_queue, skb); + __skb_queue_tail(queue, skb); } return skb_append_datato_frags(sk, skb, getfrag, from, (length - transhdrlen)); } -/* - * ip_append_data() and ip_append_page() can make one large IP datagram - * from many pieces of data. Each pieces will be holded on the socket - * until ip_push_pending_frames() is called. Each piece can be a page - * or non-page data. - * - * Not only UDP, other transport protocols - e.g. raw sockets - can use - * this interface potentially. - * - * LATER: length must be adjusted by pad at tail, when it is required. - */ -int ip_append_data(struct sock *sk, - int getfrag(void *from, char *to, int offset, int len, - int odd, struct sk_buff *skb), - void *from, int length, int transhdrlen, - struct ipcm_cookie *ipc, struct rtable **rtp, - unsigned int flags) +static int __ip_append_data(struct sock *sk, struct sk_buff_head *queue, + struct inet_cork *cork, + int getfrag(void *from, char *to, int offset, + int len, int odd, struct sk_buff *skb), + void *from, int length, int transhdrlen, + unsigned int flags) { struct inet_sock *inet = inet_sk(sk); struct sk_buff *skb; - struct ip_options *opt = NULL; + struct ip_options *opt = inet->cork.opt; int hh_len; int exthdrlen; int mtu; @@ -808,58 +798,19 @@ int ip_append_data(struct sock *sk, int offset = 0; unsigned int maxfraglen, fragheaderlen; int csummode = CHECKSUM_NONE; - struct rtable *rt; - - if (flags&MSG_PROBE) - return 0; + struct rtable *rt = (struct rtable *)cork->dst; - if (skb_queue_empty(&sk->sk_write_queue)) { - /* - * setup for corking. - */ - opt = ipc->opt; - if (opt) { - if (inet->cork.opt == NULL) { - inet->cork.opt = kmalloc(sizeof(struct ip_options) + 40, sk->sk_allocation); - if (unlikely(inet->cork.opt == NULL)) - return -ENOBUFS; - } - memcpy(inet->cork.opt, opt, sizeof(struct ip_options)+opt->optlen); - inet->cork.flags |= IPCORK_OPT; - inet->cork.addr = ipc->addr; - } - rt = *rtp; - if (unlikely(!rt)) - return -EFAULT; - /* - * We steal reference to this route, caller should not release it - */ - *rtp = NULL; - inet->cork.fragsize = mtu = inet->pmtudisc == IP_PMTUDISC_PROBE ? - rt->dst.dev->mtu : - dst_mtu(rt->dst.path); - inet->cork.dst = &rt->dst; - inet->cork.length = 0; - sk->sk_sndmsg_page = NULL; - sk->sk_sndmsg_off = 0; - exthdrlen = rt->dst.header_len; - length += exthdrlen; - transhdrlen += exthdrlen; - } else { - rt = (struct rtable *)inet->cork.dst; - if (inet->cork.flags & IPCORK_OPT) - opt = inet->cork.opt; + exthdrlen = transhdrlen ? rt->dst.header_len : 0; + length += exthdrlen; + transhdrlen += exthdrlen; + mtu = inet->cork.fragsize; - transhdrlen = 0; - exthdrlen = 0; - mtu = inet->cork.fragsize; - } hh_len = LL_RESERVED_SPACE(rt->dst.dev); fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0); maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; - if (inet->cork.length + length > 0xFFFF - fragheaderlen) { + if (cork->length + length > 0xFFFF - fragheaderlen) { ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->inet_dport, mtu-exthdrlen); return -EMSGSIZE; @@ -875,15 +826,15 @@ int ip_append_data(struct sock *sk, !exthdrlen) csummode = CHECKSUM_PARTIAL; - skb = skb_peek_tail(&sk->sk_write_queue); + skb = skb_peek_tail(queue); - inet->cork.length += length; + cork->length += length; if (((length > mtu) || (skb && skb_is_gso(skb))) && (sk->sk_protocol == IPPROTO_UDP) && (rt->dst.dev->features & NETIF_F_UFO)) { - err = ip_ufo_append_data(sk, getfrag, from, length, hh_len, - fragheaderlen, transhdrlen, mtu, - flags); + err = ip_ufo_append_data(sk, queue, getfrag, from, length, + hh_len, fragheaderlen, transhdrlen, + mtu, flags); if (err) goto error; return 0; @@ -960,7 +911,7 @@ alloc_new_skb: else /* only the initial fragment is time stamped */ - ipc->tx_flags = 0; + cork->tx_flags = 0; } if (skb == NULL) goto error; @@ -971,7 +922,7 @@ alloc_new_skb: skb->ip_summed = csummode; skb->csum = 0; skb_reserve(skb, hh_len); - skb_shinfo(skb)->tx_flags = ipc->tx_flags; + skb_shinfo(skb)->tx_flags = cork->tx_flags; /* * Find where to start putting bytes. @@ -1008,7 +959,7 @@ alloc_new_skb: /* * Put the packet on the pending queue. */ - __skb_queue_tail(&sk->sk_write_queue, skb); + __skb_queue_tail(queue, skb); continue; } @@ -1028,8 +979,8 @@ alloc_new_skb: } else { int i = skb_shinfo(skb)->nr_frags; skb_frag_t *frag = &skb_shinfo(skb)->frags[i-1]; - struct page *page = sk->sk_sndmsg_page; - int off = sk->sk_sndmsg_off; + struct page *page = cork->page; + int off = cork->off; unsigned int left; if (page && (left = PAGE_SIZE - off) > 0) { @@ -1041,7 +992,7 @@ alloc_new_skb: goto error; } get_page(page); - skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0); + skb_fill_page_desc(skb, i, page, off, 0); frag = &skb_shinfo(skb)->frags[i]; } } else if (i < MAX_SKB_FRAGS) { @@ -1052,8 +1003,8 @@ alloc_new_skb: err = -ENOMEM; goto error; } - sk->sk_sndmsg_page = page; - sk->sk_sndmsg_off = 0; + cork->page = page; + cork->off = 0; skb_fill_page_desc(skb, i, page, 0, 0); frag = &skb_shinfo(skb)->frags[i]; @@ -1065,7 +1016,7 @@ alloc_new_skb: err = -EFAULT; goto error; } - sk->sk_sndmsg_off += copy; + cork->off += copy; frag->size += copy; skb->len += copy; skb->data_len += copy; @@ -1079,11 +1030,87 @@ alloc_new_skb: return 0; error: - inet->cork.length -= length; + cork->length -= length; IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS); return err; } +static int ip_setup_cork(struct sock *sk, struct inet_cork *cork, + struct ipcm_cookie *ipc, struct rtable **rtp) +{ + struct inet_sock *inet = inet_sk(sk); + struct ip_options *opt; + struct rtable *rt; + + /* + * setup for corking. + */ + opt = ipc->opt; + if (opt) { + if (cork->opt == NULL) { + cork->opt = kmalloc(sizeof(struct ip_options) + 40, + sk->sk_allocation); + if (unlikely(cork->opt == NULL)) + return -ENOBUFS; + } + memcpy(cork->opt, opt, sizeof(struct ip_options) + opt->optlen); + cork->flags |= IPCORK_OPT; + cork->addr = ipc->addr; + } + rt = *rtp; + if (unlikely(!rt)) + return -EFAULT; + /* + * We steal reference to this route, caller should not release it + */ + *rtp = NULL; + cork->fragsize = inet->pmtudisc == IP_PMTUDISC_PROBE ? + rt->dst.dev->mtu : dst_mtu(rt->dst.path); + cork->dst = &rt->dst; + cork->length = 0; + cork->tx_flags = ipc->tx_flags; + cork->page = NULL; + cork->off = 0; + + return 0; +} + +/* + * ip_append_data() and ip_append_page() can make one large IP datagram + * from many pieces of data. Each pieces will be holded on the socket + * until ip_push_pending_frames() is called. Each piece can be a page + * or non-page data. + * + * Not only UDP, other transport protocols - e.g. raw sockets - can use + * this interface potentially. + * + * LATER: length must be adjusted by pad at tail, when it is required. + */ +int ip_append_data(struct sock *sk, + int getfrag(void *from, char *to, int offset, int len, + int odd, struct sk_buff *skb), + void *from, int length, int transhdrlen, + struct ipcm_cookie *ipc, struct rtable **rtp, + unsigned int flags) +{ + struct inet_sock *inet = inet_sk(sk); + int err; + + if (flags&MSG_PROBE) + return 0; + + if (skb_queue_empty(&sk->sk_write_queue)) { + err = ip_setup_cork(sk, &inet->cork, ipc, rtp); + if (err) + return err; + } else { + transhdrlen = 0; + } + + return __ip_append_data(sk, &sk->sk_write_queue, &inet->cork, getfrag, + from, length, transhdrlen, flags); +} + ssize_t ip_append_page(struct sock *sk, struct page *page, int offset, size_t size, int flags) { @@ -1227,40 +1254,42 @@ error: return err; } -static void ip_cork_release(struct inet_sock *inet) +static void ip_cork_release(struct inet_cork *cork) { - inet->cork.flags &= ~IPCORK_OPT; - kfree(inet->cork.opt); - inet->cork.opt = NULL; - dst_release(inet->cork.dst); - inet->cork.dst = NULL; + cork->flags &= ~IPCORK_OPT; + kfree(cork->opt); + cork->opt = NULL; + dst_release(cork->dst); + cork->dst = NULL; } /* * Combined all pending IP fragments on the socket as one IP datagram * and push them out. */ -int ip_push_pending_frames(struct sock *sk) +static int __ip_push_pending_frames(struct sock *sk, + struct sk_buff_head *queue, + struct inet_cork *cork) { struct sk_buff *skb, *tmp_skb; struct sk_buff **tail_skb; struct inet_sock *inet = inet_sk(sk); struct net *net = sock_net(sk); struct ip_options *opt = NULL; - struct rtable *rt = (struct rtable *)inet->cork.dst; + struct rtable *rt = (struct rtable *)cork->dst; struct iphdr *iph; __be16 df = 0; __u8 ttl; int err = 0; - if ((skb = __skb_dequeue(&sk->sk_write_queue)) == NULL) + if ((skb = __skb_dequeue(queue)) == NULL) goto out; tail_skb = &(skb_shinfo(skb)->frag_list); /* move skb->data to ip header from ext header */ if (skb->data < skb_network_header(skb)) __skb_pull(skb, skb_network_offset(skb)); - while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) { + while ((tmp_skb = __skb_dequeue(queue)) != NULL) { __skb_pull(tmp_skb, skb_network_header_len(skb)); *tail_skb = tmp_skb; tail_skb = &(tmp_skb->next); @@ -1286,8 +1315,8 @@ int ip_push_pending_frames(struct sock *sk) ip_dont_fragment(sk, &rt->dst))) df = htons(IP_DF); - if (inet->cork.flags & IPCORK_OPT) - opt = inet->cork.opt; + if (cork->flags & IPCORK_OPT) + opt = cork->opt; if (rt->rt_type == RTN_MULTICAST) ttl = inet->mc_ttl; @@ -1299,7 +1328,7 @@ int ip_push_pending_frames(struct sock *sk) iph->ihl = 5; if (opt) { iph->ihl += opt->optlen>>2; - ip_options_build(skb, opt, inet->cork.addr, rt, 0); + ip_options_build(skb, opt, cork->addr, rt, 0); } iph->tos = inet->tos; iph->frag_off = df; @@ -1315,7 +1344,7 @@ int ip_push_pending_frames(struct sock *sk) * Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec * on dst refcount */ - inet->cork.dst = NULL; + cork->dst = NULL; skb_dst_set(skb, &rt->dst); if (iph->protocol == IPPROTO_ICMP) @@ -1332,7 +1361,7 @@ int ip_push_pending_frames(struct sock *sk) } out: - ip_cork_release(inet); + ip_cork_release(cork); return err; error: @@ -1340,17 +1369,30 @@ error: goto out; } +int ip_push_pending_frames(struct sock *sk) +{ + return __ip_push_pending_frames(sk, &sk->sk_write_queue, + &inet_sk(sk)->cork); +} + /* * Throw away all pending data on the socket. */ -void ip_flush_pending_frames(struct sock *sk) +static void __ip_flush_pending_frames(struct sock *sk, + struct sk_buff_head *queue, + struct inet_cork *cork) { struct sk_buff *skb; - while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL) + while ((skb = __skb_dequeue_tail(queue)) != NULL) kfree_skb(skb); - ip_cork_release(inet_sk(sk)); + ip_cork_release(cork); +} + +void ip_flush_pending_frames(struct sock *sk) +{ + __ip_flush_pending_frames(sk, &sk->sk_write_queue, &inet_sk(sk)->cork); } -- cgit v1.2.3 From 1c32c5ad6fac8cee1a77449f5abf211e911ff830 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 1 Mar 2011 02:36:47 +0000 Subject: inet: Add ip_make_skb and ip_finish_skb This patch adds the helper ip_make_skb which is like ip_append_data and ip_push_pending_frames all rolled into one, except that it does not send the skb produced. The sending part is carried out by ip_send_skb, which the transport protocol can call after it has tweaked the skb. It is meant to be called in cases where corking is not used should have a one-to-one correspondence to sendmsg. This patch also adds the helper ip_finish_skb which is meant to be replace ip_push_pending_frames when corking is required. Previously the protocol stack would peek at the socket write queue and add its header to the first packet. With ip_finish_skb, the protocol stack can directly operate on the final skb instead, just like the non-corking case with ip_make_skb. Signed-off-by: Herbert Xu Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/ip.h | 16 +++++++++++++ net/ipv4/ip_output.c | 65 +++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 67 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/ip.h b/include/net/ip.h index 67fac78a186b..a4f631108c54 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -116,8 +116,24 @@ extern int ip_append_data(struct sock *sk, extern int ip_generic_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb); extern ssize_t ip_append_page(struct sock *sk, struct page *page, int offset, size_t size, int flags); +extern struct sk_buff *__ip_make_skb(struct sock *sk, + struct sk_buff_head *queue, + struct inet_cork *cork); +extern int ip_send_skb(struct sk_buff *skb); extern int ip_push_pending_frames(struct sock *sk); extern void ip_flush_pending_frames(struct sock *sk); +extern struct sk_buff *ip_make_skb(struct sock *sk, + int getfrag(void *from, char *to, int offset, int len, + int odd, struct sk_buff *skb), + void *from, int length, int transhdrlen, + struct ipcm_cookie *ipc, + struct rtable **rtp, + unsigned int flags); + +static inline struct sk_buff *ip_finish_skb(struct sock *sk) +{ + return __ip_make_skb(sk, &sk->sk_write_queue, &inet_sk(sk)->cork); +} /* datagram.c */ extern int ip4_datagram_connect(struct sock *sk, diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 1dd5ecc9a27e..460308c35028 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1267,9 +1267,9 @@ static void ip_cork_release(struct inet_cork *cork) * Combined all pending IP fragments on the socket as one IP datagram * and push them out. */ -static int __ip_push_pending_frames(struct sock *sk, - struct sk_buff_head *queue, - struct inet_cork *cork) +struct sk_buff *__ip_make_skb(struct sock *sk, + struct sk_buff_head *queue, + struct inet_cork *cork) { struct sk_buff *skb, *tmp_skb; struct sk_buff **tail_skb; @@ -1280,7 +1280,6 @@ static int __ip_push_pending_frames(struct sock *sk, struct iphdr *iph; __be16 df = 0; __u8 ttl; - int err = 0; if ((skb = __skb_dequeue(queue)) == NULL) goto out; @@ -1351,28 +1350,37 @@ static int __ip_push_pending_frames(struct sock *sk, icmp_out_count(net, ((struct icmphdr *) skb_transport_header(skb))->type); - /* Netfilter gets whole the not fragmented skb. */ + ip_cork_release(cork); +out: + return skb; +} + +int ip_send_skb(struct sk_buff *skb) +{ + struct net *net = sock_net(skb->sk); + int err; + err = ip_local_out(skb); if (err) { if (err > 0) err = net_xmit_errno(err); if (err) - goto error; + IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS); } -out: - ip_cork_release(cork); return err; - -error: - IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS); - goto out; } int ip_push_pending_frames(struct sock *sk) { - return __ip_push_pending_frames(sk, &sk->sk_write_queue, - &inet_sk(sk)->cork); + struct sk_buff *skb; + + skb = ip_finish_skb(sk); + if (!skb) + return 0; + + /* Netfilter gets whole the not fragmented skb. */ + return ip_send_skb(skb); } /* @@ -1395,6 +1403,35 @@ void ip_flush_pending_frames(struct sock *sk) __ip_flush_pending_frames(sk, &sk->sk_write_queue, &inet_sk(sk)->cork); } +struct sk_buff *ip_make_skb(struct sock *sk, + int getfrag(void *from, char *to, int offset, + int len, int odd, struct sk_buff *skb), + void *from, int length, int transhdrlen, + struct ipcm_cookie *ipc, struct rtable **rtp, + unsigned int flags) +{ + struct inet_cork cork = {}; + struct sk_buff_head queue; + int err; + + if (flags & MSG_PROBE) + return NULL; + + __skb_queue_head_init(&queue); + + err = ip_setup_cork(sk, &cork, ipc, rtp); + if (err) + return ERR_PTR(err); + + err = __ip_append_data(sk, &queue, &cork, getfrag, + from, length, transhdrlen, flags); + if (err) { + __ip_flush_pending_frames(sk, &queue, &cork); + return ERR_PTR(err); + } + + return __ip_make_skb(sk, &queue, &cork); +} /* * Fetch data from kernel space and fill in checksum if needed. -- cgit v1.2.3 From f6b9664f8b711cf4fd53e70aa0d21f72d5bf806c Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 1 Mar 2011 02:36:48 +0000 Subject: udp: Switch to ip_finish_skb This patch converts UDP to use the new ip_finish_skb API. This would then allows us to more easily use ip_make_skb which allows UDP to run without a socket lock. Signed-off-by: Herbert Xu Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/udp.h | 11 +++++++ include/net/udplite.h | 12 ++++++++ net/ipv4/udp.c | 83 +++++++++++++++++++++++++++++++-------------------- 3 files changed, 73 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/net/udp.h b/include/net/udp.h index e82f3a8c0f8f..67ea6fcb3ec0 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -144,6 +144,17 @@ static inline __wsum udp_csum_outgoing(struct sock *sk, struct sk_buff *skb) return csum; } +static inline __wsum udp_csum(struct sk_buff *skb) +{ + __wsum csum = csum_partial(skb_transport_header(skb), + sizeof(struct udphdr), skb->csum); + + for (skb = skb_shinfo(skb)->frag_list; skb; skb = skb->next) { + csum = csum_add(csum, skb->csum); + } + return csum; +} + /* hash routines shared between UDPv4/6 and UDP-Litev4/6 */ static inline void udp_lib_hash(struct sock *sk) { diff --git a/include/net/udplite.h b/include/net/udplite.h index afdffe607b24..673a024c6b2a 100644 --- a/include/net/udplite.h +++ b/include/net/udplite.h @@ -115,6 +115,18 @@ static inline __wsum udplite_csum_outgoing(struct sock *sk, struct sk_buff *skb) return csum; } +static inline __wsum udplite_csum(struct sk_buff *skb) +{ + struct sock *sk = skb->sk; + int cscov = udplite_sender_cscov(udp_sk(sk), udp_hdr(skb)); + const int off = skb_transport_offset(skb); + const int len = skb->len - off; + + skb->ip_summed = CHECKSUM_NONE; /* no HW support for checksumming */ + + return skb_checksum(skb, off, min(cscov, len), 0); +} + extern void udplite4_register(void); extern int udplite_get_port(struct sock *sk, unsigned short snum, int (*scmp)(const struct sock *, const struct sock *)); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index d37baaa1dbe3..61c22ee7d4ba 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -663,75 +663,72 @@ void udp_flush_pending_frames(struct sock *sk) EXPORT_SYMBOL(udp_flush_pending_frames); /** - * udp4_hwcsum_outgoing - handle outgoing HW checksumming - * @sk: socket we are sending on + * udp4_hwcsum - handle outgoing HW checksumming * @skb: sk_buff containing the filled-in UDP header * (checksum field must be zeroed out) + * @src: source IP address + * @dst: destination IP address */ -static void udp4_hwcsum_outgoing(struct sock *sk, struct sk_buff *skb, - __be32 src, __be32 dst, int len) +static void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst) { - unsigned int offset; struct udphdr *uh = udp_hdr(skb); + struct sk_buff *frags = skb_shinfo(skb)->frag_list; + int offset = skb_transport_offset(skb); + int len = skb->len - offset; + int hlen = len; __wsum csum = 0; - if (skb_queue_len(&sk->sk_write_queue) == 1) { + if (!frags) { /* * Only one fragment on the socket. */ skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct udphdr, check); - uh->check = ~csum_tcpudp_magic(src, dst, len, IPPROTO_UDP, 0); + uh->check = ~csum_tcpudp_magic(src, dst, len, + IPPROTO_UDP, 0); } else { /* * HW-checksum won't work as there are two or more * fragments on the socket so that all csums of sk_buffs * should be together */ - offset = skb_transport_offset(skb); - skb->csum = skb_checksum(skb, offset, skb->len - offset, 0); + do { + csum = csum_add(csum, frags->csum); + hlen -= frags->len; + } while ((frags = frags->next)); + csum = skb_checksum(skb, offset, hlen, csum); skb->ip_summed = CHECKSUM_NONE; - skb_queue_walk(&sk->sk_write_queue, skb) { - csum = csum_add(csum, skb->csum); - } - uh->check = csum_tcpudp_magic(src, dst, len, IPPROTO_UDP, csum); if (uh->check == 0) uh->check = CSUM_MANGLED_0; } } -/* - * Push out all pending data as one UDP datagram. Socket is locked. - */ -static int udp_push_pending_frames(struct sock *sk) +static int udp_send_skb(struct sk_buff *skb, __be32 daddr, __be32 dport) { - struct udp_sock *up = udp_sk(sk); + struct sock *sk = skb->sk; struct inet_sock *inet = inet_sk(sk); - struct flowi *fl = &inet->cork.fl; - struct sk_buff *skb; struct udphdr *uh; + struct rtable *rt = (struct rtable *)skb_dst(skb); int err = 0; int is_udplite = IS_UDPLITE(sk); + int offset = skb_transport_offset(skb); + int len = skb->len - offset; __wsum csum = 0; - /* Grab the skbuff where UDP header space exists. */ - if ((skb = skb_peek(&sk->sk_write_queue)) == NULL) - goto out; - /* * Create a UDP header */ uh = udp_hdr(skb); - uh->source = fl->fl_ip_sport; - uh->dest = fl->fl_ip_dport; - uh->len = htons(up->len); + uh->source = inet->inet_sport; + uh->dest = dport; + uh->len = htons(len); uh->check = 0; if (is_udplite) /* UDP-Lite */ - csum = udplite_csum_outgoing(sk, skb); + csum = udplite_csum(skb); else if (sk->sk_no_check == UDP_CSUM_NOXMIT) { /* UDP csum disabled */ @@ -740,20 +737,20 @@ static int udp_push_pending_frames(struct sock *sk) } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ - udp4_hwcsum_outgoing(sk, skb, fl->fl4_src, fl->fl4_dst, up->len); + udp4_hwcsum(skb, rt->rt_src, daddr); goto send; - } else /* `normal' UDP */ - csum = udp_csum_outgoing(sk, skb); + } else + csum = udp_csum(skb); /* add protocol-dependent pseudo-header */ - uh->check = csum_tcpudp_magic(fl->fl4_src, fl->fl4_dst, up->len, + uh->check = csum_tcpudp_magic(rt->rt_src, daddr, len, sk->sk_protocol, csum); if (uh->check == 0) uh->check = CSUM_MANGLED_0; send: - err = ip_push_pending_frames(sk); + err = ip_send_skb(skb); if (err) { if (err == -ENOBUFS && !inet->recverr) { UDP_INC_STATS_USER(sock_net(sk), @@ -763,6 +760,26 @@ send: } else UDP_INC_STATS_USER(sock_net(sk), UDP_MIB_OUTDATAGRAMS, is_udplite); + return err; +} + +/* + * Push out all pending data as one UDP datagram. Socket is locked. + */ +static int udp_push_pending_frames(struct sock *sk) +{ + struct udp_sock *up = udp_sk(sk); + struct inet_sock *inet = inet_sk(sk); + struct flowi *fl = &inet->cork.fl; + struct sk_buff *skb; + int err = 0; + + skb = ip_finish_skb(sk); + if (!skb) + goto out; + + err = udp_send_skb(skb, fl->fl4_dst, fl->fl_ip_dport); + out: up->len = 0; up->pending = 0; -- cgit v1.2.3 From 68d0c6d34d586a893292d4fb633a3bf8c547b222 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 1 Mar 2011 13:19:07 -0800 Subject: ipv6: Consolidate route lookup sequences. Route lookups follow a general pattern in the ipv6 code wherein we first find the non-IPSEC route, potentially override the flow destination address due to ipv6 options settings, and then finally make an IPSEC search using either xfrm_lookup() or __xfrm_lookup(). __xfrm_lookup() is used when we want to generate a blackhole route if the key manager needs to resolve the IPSEC rules (in this case -EREMOTE is returned and the original 'dst' is left unchanged). Otherwise plain xfrm_lookup() is used and when asynchronous IPSEC resolution is necessary, we simply fail the lookup completely. All of these cases are encapsulated into two routines, ip6_dst_lookup_flow and ip6_sk_dst_lookup_flow. The latter of which handles unconnected UDP datagram sockets. Signed-off-by: David S. Miller --- include/net/ipv6.h | 11 ++++-- net/dccp/ipv6.c | 65 ++++++++++---------------------- net/ipv6/af_inet6.c | 17 +++------ net/ipv6/datagram.c | 15 ++------ net/ipv6/inet6_connection_sock.c | 25 +++---------- net/ipv6/ip6_output.c | 80 ++++++++++++++++++++++++++++++++++------ net/ipv6/raw.c | 15 ++------ net/ipv6/syncookies.c | 7 +--- net/ipv6/tcp_ipv6.c | 57 +++++++++++----------------- net/ipv6/udp.c | 15 ++------ 10 files changed, 142 insertions(+), 165 deletions(-) (limited to 'include') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 4a3cd2cd2f5e..1fc5631cf1a2 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -512,12 +512,17 @@ extern void ip6_flush_pending_frames(struct sock *sk); extern int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl); +extern struct dst_entry * ip6_dst_lookup_flow(struct sock *sk, + struct flowi *fl, + const struct in6_addr *final_dst, + bool want_blackhole); +extern struct dst_entry * ip6_sk_dst_lookup_flow(struct sock *sk, + struct flowi *fl, + const struct in6_addr *final_dst, + bool want_blackhole); extern int ip6_dst_blackhole(struct sock *sk, struct dst_entry **dst, struct flowi *fl); -extern int ip6_sk_dst_lookup(struct sock *sk, - struct dst_entry **dst, - struct flowi *fl); /* * skb processing functions diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 460d545a6509..5efc57f5e605 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -162,15 +162,9 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, fl.fl_ip_sport = inet->inet_sport; security_sk_classify_flow(sk, &fl); - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) { - sk->sk_err_soft = -err; - goto out; - } - - err = xfrm_lookup(net, &dst, &fl, sk, 0); - if (err < 0) { - sk->sk_err_soft = -err; + dst = ip6_dst_lookup_flow(sk, &fl, NULL, false); + if (IS_ERR(dst)) { + sk->sk_err_soft = -PTR_ERR(dst); goto out; } } else @@ -267,16 +261,12 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req, final_p = fl6_update_dst(&fl, opt, &final); - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) - goto done; - - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - err = xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0); - if (err < 0) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, false); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); + dst = NULL; goto done; + } skb = dccp_make_response(sk, dst, req); if (skb != NULL) { @@ -338,14 +328,13 @@ static void dccp_v6_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb) security_skb_classify_flow(rxskb, &fl); /* sk = NULL, but it is safe for now. RST socket required. */ - if (!ip6_dst_lookup(ctl_sk, &dst, &fl)) { - if (xfrm_lookup(net, &dst, &fl, NULL, 0) >= 0) { - skb_dst_set(skb, dst); - ip6_xmit(ctl_sk, skb, &fl, NULL); - DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS); - DCCP_INC_STATS_BH(DCCP_MIB_OUTRSTS); - return; - } + dst = ip6_dst_lookup_flow(ctl_sk, &fl, NULL, false); + if (!IS_ERR(dst)) { + skb_dst_set(skb, dst); + ip6_xmit(ctl_sk, skb, &fl, NULL); + DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS); + DCCP_INC_STATS_BH(DCCP_MIB_OUTRSTS); + return; } kfree_skb(skb); @@ -550,13 +539,8 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, fl.fl_ip_sport = inet_rsk(req)->loc_port; security_sk_classify_flow(sk, &fl); - if (ip6_dst_lookup(sk, &dst, &fl)) - goto out; - - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - if ((xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, false); + if (IS_ERR(dst)) goto out; } @@ -979,19 +963,10 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, final_p = fl6_update_dst(&fl, np->opt, &final); - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, true); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); goto failure; - - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT); - if (err < 0) { - if (err == -EREMOTE) - err = ip6_dst_blackhole(sk, &dst, &fl); - if (err < 0) - goto failure; } if (saddr == NULL) { diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 3194aa909872..a88b2e9d25f1 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -644,9 +644,8 @@ EXPORT_SYMBOL(inet6_unregister_protosw); int inet6_sk_rebuild_header(struct sock *sk) { - int err; - struct dst_entry *dst; struct ipv6_pinfo *np = inet6_sk(sk); + struct dst_entry *dst; dst = __sk_dst_check(sk, np->dst_cookie); @@ -668,17 +667,11 @@ int inet6_sk_rebuild_header(struct sock *sk) final_p = fl6_update_dst(&fl, np->opt, &final); - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) { + dst = ip6_dst_lookup_flow(sk, &fl, final_p, false); + if (IS_ERR(dst)) { sk->sk_route_caps = 0; - return err; - } - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - if ((err = xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0) { - sk->sk_err_soft = -err; - return err; + sk->sk_err_soft = -PTR_ERR(dst); + return PTR_ERR(dst); } __ip6_dst_store(sk, dst, NULL, NULL); diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 320bdb877eed..be3a781c0085 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -162,18 +162,11 @@ ipv4_connected: opt = flowlabel ? flowlabel->opt : np->opt; final_p = fl6_update_dst(&fl, opt, &final); - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, true); + err = 0; + if (IS_ERR(dst)) { + err = PTR_ERR(dst); goto out; - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT); - if (err < 0) { - if (err == -EREMOTE) - err = ip6_dst_blackhole(sk, &dst, &fl); - if (err < 0) - goto out; } /* source address lookup done in ip6_dst_lookup */ diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index d144e629d2b4..d687e1397333 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -74,13 +74,8 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk, fl.fl_ip_sport = inet_rsk(req)->loc_port; security_req_classify_flow(req, &fl); - if (ip6_dst_lookup(sk, &dst, &fl)) - return NULL; - - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - if ((xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, false); + if (IS_ERR(dst)) return NULL; return dst; @@ -234,21 +229,13 @@ int inet6_csk_xmit(struct sk_buff *skb) dst = __inet6_csk_dst_check(sk, np->dst_cookie); if (dst == NULL) { - int err = ip6_dst_lookup(sk, &dst, &fl); - - if (err) { - sk->sk_err_soft = -err; - kfree_skb(skb); - return err; - } - - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); + dst = ip6_dst_lookup_flow(sk, &fl, final_p, false); - if ((err = xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0) { + if (IS_ERR(dst)) { + sk->sk_err_soft = -PTR_ERR(dst); sk->sk_route_caps = 0; kfree_skb(skb); - return err; + return PTR_ERR(dst); } __inet6_csk_dst_store(sk, dst, NULL, NULL); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 5c618f20523e..28209b2d254d 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1002,29 +1002,87 @@ int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl) EXPORT_SYMBOL_GPL(ip6_dst_lookup); /** - * ip6_sk_dst_lookup - perform socket cached route lookup on flow + * ip6_dst_lookup_flow - perform route lookup on flow with ipsec + * @sk: socket which provides route info + * @fl: flow to lookup + * @final_dst: final destination address for ipsec lookup + * @want_blackhole: IPSEC blackhole handling desired + * + * This function performs a route lookup on the given flow. + * + * It returns a valid dst pointer on success, or a pointer encoded + * error code. + */ +struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi *fl, + const struct in6_addr *final_dst, + bool want_blackhole) +{ + struct dst_entry *dst = NULL; + int err; + + err = ip6_dst_lookup_tail(sk, &dst, fl); + if (err) + return ERR_PTR(err); + if (final_dst) + ipv6_addr_copy(&fl->fl6_dst, final_dst); + if (want_blackhole) { + err = __xfrm_lookup(sock_net(sk), &dst, fl, sk, XFRM_LOOKUP_WAIT); + if (err == -EREMOTE) + err = ip6_dst_blackhole(sk, &dst, fl); + if (err) + return ERR_PTR(err); + } else { + err = xfrm_lookup(sock_net(sk), &dst, fl, sk, 0); + if (err) + return ERR_PTR(err); + } + return dst; +} +EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow); + +/** + * ip6_sk_dst_lookup_flow - perform socket cached route lookup on flow * @sk: socket which provides the dst cache and route info - * @dst: pointer to dst_entry * for result * @fl: flow to lookup + * @final_dst: final destination address for ipsec lookup + * @want_blackhole: IPSEC blackhole handling desired * * This function performs a route lookup on the given flow with the * possibility of using the cached route in the socket if it is valid. * It will take the socket dst lock when operating on the dst cache. * As a result, this function can only be used in process context. * - * It returns zero on success, or a standard errno code on error. + * It returns a valid dst pointer on success, or a pointer encoded + * error code. */ -int ip6_sk_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl) +struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi *fl, + const struct in6_addr *final_dst, + bool want_blackhole) { - *dst = NULL; - if (sk) { - *dst = sk_dst_check(sk, inet6_sk(sk)->dst_cookie); - *dst = ip6_sk_dst_check(sk, *dst, fl); - } + struct dst_entry *dst = sk_dst_check(sk, inet6_sk(sk)->dst_cookie); + int err; - return ip6_dst_lookup_tail(sk, dst, fl); + dst = ip6_sk_dst_check(sk, dst, fl); + + err = ip6_dst_lookup_tail(sk, &dst, fl); + if (err) + return ERR_PTR(err); + if (final_dst) + ipv6_addr_copy(&fl->fl6_dst, final_dst); + if (want_blackhole) { + err = __xfrm_lookup(sock_net(sk), &dst, fl, sk, XFRM_LOOKUP_WAIT); + if (err == -EREMOTE) + err = ip6_dst_blackhole(sk, &dst, fl); + if (err) + return ERR_PTR(err); + } else { + err = xfrm_lookup(sock_net(sk), &dst, fl, sk, 0); + if (err) + return ERR_PTR(err); + } + return dst; } -EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup); +EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow); static inline int ip6_ufo_append_data(struct sock *sk, int getfrag(void *from, char *to, int offset, int len, diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 364e86683388..dc29b07caf42 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -856,20 +856,11 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, fl.oif = np->mcast_oif; security_sk_classify_flow(sk, &fl); - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, true); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); goto out; - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT); - if (err < 0) { - if (err == -EREMOTE) - err = ip6_dst_blackhole(sk, &dst, &fl); - if (err < 0) - goto out; } - if (hlimit < 0) { if (ipv6_addr_is_multicast(&fl.fl6_dst)) hlimit = np->mcast_hops; diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 09fd34f0dbf2..0b4cf350631b 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -243,12 +243,9 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) fl.fl_ip_dport = inet_rsk(req)->rmt_port; fl.fl_ip_sport = inet_sk(sk)->inet_sport; security_req_classify_flow(req, &fl); - if (ip6_dst_lookup(sk, &dst, &fl)) - goto out_free; - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - if ((xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, false); + if (IS_ERR(dst)) goto out_free; } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 1d0ab5570904..e59a31c48baf 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -255,18 +255,10 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, security_sk_classify_flow(sk, &fl); - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, true); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); goto failure; - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT); - if (err < 0) { - if (err == -EREMOTE) - err = ip6_dst_blackhole(sk, &dst, &fl); - if (err < 0) - goto failure; } if (saddr == NULL) { @@ -385,7 +377,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, np = inet6_sk(sk); if (type == ICMPV6_PKT_TOOBIG) { - struct dst_entry *dst = NULL; + struct dst_entry *dst; if (sock_owned_by_user(sk)) goto out; @@ -413,13 +405,9 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, fl.fl_ip_sport = inet->inet_sport; security_skb_classify_flow(skb, &fl); - if ((err = ip6_dst_lookup(sk, &dst, &fl))) { - sk->sk_err_soft = -err; - goto out; - } - - if ((err = xfrm_lookup(net, &dst, &fl, sk, 0)) < 0) { - sk->sk_err_soft = -err; + dst = ip6_dst_lookup_flow(sk, &fl, NULL, false); + if (IS_ERR(dst)) { + sk->sk_err_soft = -PTR_ERR(dst); goto out; } @@ -496,7 +484,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req, struct in6_addr * final_p, final; struct flowi fl; struct dst_entry *dst; - int err = -1; + int err; memset(&fl, 0, sizeof(fl)); fl.proto = IPPROTO_TCP; @@ -512,15 +500,13 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req, opt = np->opt; final_p = fl6_update_dst(&fl, opt, &final); - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, false); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); goto done; - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - if ((err = xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0) - goto done; - + } skb = tcp_make_synack(sk, dst, req, rvp); + err = -ENOMEM; if (skb) { __tcp_v6_send_check(skb, &treq->loc_addr, &treq->rmt_addr); @@ -1079,15 +1065,14 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, * Underlying function will use this to retrieve the network * namespace */ - if (!ip6_dst_lookup(ctl_sk, &dst, &fl)) { - if (xfrm_lookup(net, &dst, &fl, NULL, 0) >= 0) { - skb_dst_set(buff, dst); - ip6_xmit(ctl_sk, buff, &fl, NULL); - TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS); - if (rst) - TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS); - return; - } + dst = ip6_dst_lookup_flow(ctl_sk, &fl, NULL, false); + if (!IS_ERR(dst)) { + skb_dst_set(buff, dst); + ip6_xmit(ctl_sk, buff, &fl, NULL); + TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS); + if (rst) + TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS); + return; } kfree_skb(buff); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index a419a787eb69..d86d7f67a597 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1125,18 +1125,11 @@ do_udp_sendmsg: security_sk_classify_flow(sk, &fl); - err = ip6_sk_dst_lookup(sk, &dst, &fl); - if (err) + dst = ip6_sk_dst_lookup_flow(sk, &fl, final_p, true); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); + dst = NULL; goto out; - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT); - if (err < 0) { - if (err == -EREMOTE) - err = ip6_dst_blackhole(sk, &dst, &fl); - if (err < 0) - goto out; } if (hlimit < 0) { -- cgit v1.2.3 From abdf7e7239da270e68262728f125ea94b9b7d42d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 1 Mar 2011 14:15:24 -0800 Subject: ipv4: Can final ip_route_connect() arg to boolean "can_sleep". Since that's what the current vague "flags" thing means. Signed-off-by: David S. Miller --- include/net/route.h | 4 ++-- net/dccp/ipv4.c | 2 +- net/ipv4/af_inet.c | 2 +- net/ipv4/datagram.c | 2 +- net/ipv4/tcp_ipv4.c | 2 +- net/l2tp/l2tp_ip.c | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/route.h b/include/net/route.h index b3f89ad04e0b..5e0826d1c0e0 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -168,7 +168,7 @@ static inline char rt_tos2priority(u8 tos) static inline int ip_route_connect(struct rtable **rp, __be32 dst, __be32 src, u32 tos, int oif, u8 protocol, __be16 sport, __be16 dport, struct sock *sk, - int flags) + bool can_sleep) { struct flowi fl = { .oif = oif, .mark = sk->sk_mark, @@ -196,7 +196,7 @@ static inline int ip_route_connect(struct rtable **rp, __be32 dst, *rp = NULL; } security_sk_classify_flow(sk, &fl); - return ip_route_output_flow(net, rp, &fl, sk, flags); + return ip_route_output_flow(net, rp, &fl, sk, can_sleep ? 1 : 0); } static inline int ip_route_newports(struct rtable **rp, u8 protocol, diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 937989199c80..8372d5c571a0 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -69,7 +69,7 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) tmp = ip_route_connect(&rt, nexthop, inet->inet_saddr, RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, IPPROTO_DCCP, - orig_sport, orig_dport, sk, 1); + orig_sport, orig_dport, sk, true); if (tmp < 0) return tmp; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 7ceb80447631..d16687db9713 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1115,7 +1115,7 @@ static int inet_sk_reselect_saddr(struct sock *sk) RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, sk->sk_protocol, - inet->inet_sport, inet->inet_dport, sk, 0); + inet->inet_sport, inet->inet_dport, sk, false); if (err) return err; diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c index 174be6caa5c8..eaee1edd2dd7 100644 --- a/net/ipv4/datagram.c +++ b/net/ipv4/datagram.c @@ -49,7 +49,7 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) err = ip_route_connect(&rt, usin->sin_addr.s_addr, saddr, RT_CONN_FLAGS(sk), oif, sk->sk_protocol, - inet->inet_sport, usin->sin_port, sk, 1); + inet->inet_sport, usin->sin_port, sk, true); if (err) { if (err == -ENETUNREACH) IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 27a0cc8cc888..05bc6d9455fc 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -173,7 +173,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) tmp = ip_route_connect(&rt, nexthop, inet->inet_saddr, RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, IPPROTO_TCP, - orig_sport, orig_dport, sk, 1); + orig_sport, orig_dport, sk, true); if (tmp < 0) { if (tmp == -ENETUNREACH) IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 110efb704c9b..28e876a6b1dd 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -323,7 +323,7 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len rc = ip_route_connect(&rt, lsa->l2tp_addr.s_addr, saddr, RT_CONN_FLAGS(sk), oif, IPPROTO_L2TP, - 0, 0, sk, 1); + 0, 0, sk, true); if (rc) { if (rc == -ENETUNREACH) IP_INC_STATS_BH(&init_net, IPSTATS_MIB_OUTNOROUTES); -- cgit v1.2.3 From 420d44daa7aa1cc847e9e527f0a27a9ce61768ca Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 1 Mar 2011 14:19:23 -0800 Subject: ipv4: Make final arg to ip_route_output_flow to be boolean "can_sleep" Since that is what the current vague "flags" argument means. Signed-off-by: David S. Miller --- drivers/infiniband/hw/cxgb3/iwch_cm.c | 2 +- drivers/infiniband/hw/cxgb4/cm.c | 2 +- drivers/scsi/cxgbi/libcxgbi.c | 2 +- include/net/route.h | 6 +++--- net/dccp/ipv4.c | 2 +- net/ipv4/af_inet.c | 2 +- net/ipv4/inet_connection_sock.c | 2 +- net/ipv4/ip_output.c | 2 +- net/ipv4/raw.c | 2 +- net/ipv4/route.c | 6 +++--- net/ipv4/udp.c | 2 +- net/l2tp/l2tp_ip.c | 2 +- 12 files changed, 16 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c index d02dcc6e5963..c7f776c8b2b8 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cm.c +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c @@ -354,7 +354,7 @@ static struct rtable *find_route(struct t3cdev *dev, __be32 local_ip, } }; - if (ip_route_output_flow(&init_net, &rt, &fl, NULL, 0)) + if (ip_route_output_flow(&init_net, &rt, &fl, NULL, false)) return NULL; return rt; } diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 8b00e6c46f01..5542c994338d 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -331,7 +331,7 @@ static struct rtable *find_route(struct c4iw_dev *dev, __be32 local_ip, } }; - if (ip_route_output_flow(&init_net, &rt, &fl, NULL, 0)) + if (ip_route_output_flow(&init_net, &rt, &fl, NULL, false)) return NULL; return rt; } diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index d2ad3d676724..fabca75ac2f2 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -470,7 +470,7 @@ static struct rtable *find_route_ipv4(__be32 saddr, __be32 daddr, } }; - if (ip_route_output_flow(&init_net, &rt, &fl, NULL, 0)) + if (ip_route_output_flow(&init_net, &rt, &fl, NULL, false)) return NULL; return rt; diff --git a/include/net/route.h b/include/net/route.h index 5e0826d1c0e0..6de4333d6002 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -120,7 +120,7 @@ extern void rt_cache_flush(struct net *net, int how); extern void rt_cache_flush_batch(struct net *net); extern int __ip_route_output_key(struct net *, struct rtable **, const struct flowi *flp); extern int ip_route_output_key(struct net *, struct rtable **, struct flowi *flp); -extern int ip_route_output_flow(struct net *, struct rtable **rp, struct flowi *flp, struct sock *sk, int flags); +extern int ip_route_output_flow(struct net *, struct rtable **rp, struct flowi *flp, struct sock *sk, bool can_sleep); extern int ip_route_input_common(struct sk_buff *skb, __be32 dst, __be32 src, u8 tos, struct net_device *devin, bool noref); @@ -196,7 +196,7 @@ static inline int ip_route_connect(struct rtable **rp, __be32 dst, *rp = NULL; } security_sk_classify_flow(sk, &fl); - return ip_route_output_flow(net, rp, &fl, sk, can_sleep ? 1 : 0); + return ip_route_output_flow(net, rp, &fl, sk, can_sleep); } static inline int ip_route_newports(struct rtable **rp, u8 protocol, @@ -220,7 +220,7 @@ static inline int ip_route_newports(struct rtable **rp, u8 protocol, ip_rt_put(*rp); *rp = NULL; security_sk_classify_flow(sk, &fl); - return ip_route_output_flow(sock_net(sk), rp, &fl, sk, 0); + return ip_route_output_flow(sock_net(sk), rp, &fl, sk, false); } return 0; } diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 8372d5c571a0..3d4b82f6adfd 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -475,7 +475,7 @@ static struct dst_entry* dccp_v4_route_skb(struct net *net, struct sock *sk, }; security_skb_classify_flow(skb, &fl); - if (ip_route_output_flow(net, &rt, &fl, sk, 0)) { + if (ip_route_output_flow(net, &rt, &fl, sk, false)) { IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES); return NULL; } diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index d16687db9713..7d90fe0ee5a6 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1174,7 +1174,7 @@ int inet_sk_rebuild_header(struct sock *sk) }; security_sk_classify_flow(sk, &fl); - err = ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 0); + err = ip_route_output_flow(sock_net(sk), &rt, &fl, sk, false); } if (!err) sk_setup_caps(sk, &rt->dst); diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 97e5fb765265..0caeb69de4b1 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -369,7 +369,7 @@ struct dst_entry *inet_csk_route_req(struct sock *sk, struct net *net = sock_net(sk); security_req_classify_flow(req, &fl); - if (ip_route_output_flow(net, &rt, &fl, sk, 0)) + if (ip_route_output_flow(net, &rt, &fl, sk, false)) goto no_route; if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) goto route_err; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 460308c35028..e6905c562fb7 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -355,7 +355,7 @@ int ip_queue_xmit(struct sk_buff *skb) * itself out. */ security_sk_classify_flow(sk, &fl); - if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 0)) + if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk, false)) goto no_route; } sk_setup_caps(sk, &rt->dst); diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 6390ba299b3d..e1857658964d 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -563,7 +563,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, } security_sk_classify_flow(sk, &fl); - err = ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 1); + err = ip_route_output_flow(sock_net(sk), &rt, &fl, sk, true); } if (err) goto done; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 52b077d45208..1ac3ecaf36e8 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2720,7 +2720,7 @@ static int ipv4_dst_blackhole(struct net *net, struct rtable **rp, struct flowi } int ip_route_output_flow(struct net *net, struct rtable **rp, struct flowi *flp, - struct sock *sk, int flags) + struct sock *sk, bool can_sleep) { int err; @@ -2733,7 +2733,7 @@ int ip_route_output_flow(struct net *net, struct rtable **rp, struct flowi *flp, if (!flp->fl4_dst) flp->fl4_dst = (*rp)->rt_dst; err = __xfrm_lookup(net, (struct dst_entry **)rp, flp, sk, - flags ? XFRM_LOOKUP_WAIT : 0); + can_sleep ? XFRM_LOOKUP_WAIT : 0); if (err == -EREMOTE) err = ipv4_dst_blackhole(net, rp, flp); @@ -2746,7 +2746,7 @@ EXPORT_SYMBOL_GPL(ip_route_output_flow); int ip_route_output_key(struct net *net, struct rtable **rp, struct flowi *flp) { - return ip_route_output_flow(net, rp, flp, NULL, 0); + return ip_route_output_flow(net, rp, flp, NULL, false); } EXPORT_SYMBOL(ip_route_output_key); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 8155d6eda376..790187b5c308 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -920,7 +920,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, struct net *net = sock_net(sk); security_sk_classify_flow(sk, &fl); - err = ip_route_output_flow(net, &rt, &fl, sk, 1); + err = ip_route_output_flow(net, &rt, &fl, sk, true); if (err) { if (err == -ENETUNREACH) IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES); diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 28e876a6b1dd..7744a8e4b4c6 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -489,7 +489,7 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m * itself out. */ security_sk_classify_flow(sk, &fl); - if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 0)) + if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk, false)) goto no_route; } sk_setup_caps(sk, &rt->dst); -- cgit v1.2.3 From 5df65e5567a497a28067019b8ff08f98fb026629 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 1 Mar 2011 14:22:19 -0800 Subject: net: Add FLOWI_FLAG_CAN_SLEEP. And set is in contexts where the route resolution can sleep. Signed-off-by: David S. Miller --- include/net/flow.h | 1 + include/net/route.h | 2 ++ net/ipv4/raw.c | 3 ++- net/ipv4/udp.c | 6 ++++-- net/ipv6/ip6_output.c | 2 ++ 5 files changed, 11 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/flow.h b/include/net/flow.h index f2080e65276d..fd0413873b8e 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -50,6 +50,7 @@ struct flowi { __u8 flags; #define FLOWI_FLAG_ANYSRC 0x01 #define FLOWI_FLAG_PRECOW_METRICS 0x02 +#define FLOWI_FLAG_CAN_SLEEP 0x04 union { struct { __be16 sport; diff --git a/include/net/route.h b/include/net/route.h index 6de4333d6002..1be5c05a0905 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -185,6 +185,8 @@ static inline int ip_route_connect(struct rtable **rp, __be32 dst, fl.flags |= FLOWI_FLAG_ANYSRC; if (protocol == IPPROTO_TCP) fl.flags |= FLOWI_FLAG_PRECOW_METRICS; + if (can_sleep) + fl.flags |= FLOWI_FLAG_CAN_SLEEP; if (!dst || !src) { err = __ip_route_output_key(net, rp, &fl); diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index e1857658964d..e8e8613bcbcc 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -555,7 +555,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, .fl4_tos = tos, .proto = inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, - }; + .flags = FLOWI_FLAG_CAN_SLEEP, + }; if (!inet->hdrincl) { err = raw_probe_proto_opt(&fl, msg); if (err) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 790187b5c308..c6bcc93debd5 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -914,9 +914,11 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, .fl4_src = saddr, .fl4_tos = tos, .proto = sk->sk_protocol, - .flags = inet_sk_flowi_flags(sk), + .flags = (inet_sk_flowi_flags(sk) | + FLOWI_FLAG_CAN_SLEEP), .fl_ip_sport = inet->inet_sport, - .fl_ip_dport = dport }; + .fl_ip_dport = dport + }; struct net *net = sock_net(sk); security_sk_classify_flow(sk, &fl); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 28209b2d254d..77b1942f335b 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1026,6 +1026,7 @@ struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi *fl, if (final_dst) ipv6_addr_copy(&fl->fl6_dst, final_dst); if (want_blackhole) { + fl->flags |= FLOWI_FLAG_CAN_SLEEP; err = __xfrm_lookup(sock_net(sk), &dst, fl, sk, XFRM_LOOKUP_WAIT); if (err == -EREMOTE) err = ip6_dst_blackhole(sk, &dst, fl); @@ -1070,6 +1071,7 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi *fl, if (final_dst) ipv6_addr_copy(&fl->fl6_dst, final_dst); if (want_blackhole) { + fl->flags |= FLOWI_FLAG_CAN_SLEEP; err = __xfrm_lookup(sock_net(sk), &dst, fl, sk, XFRM_LOOKUP_WAIT); if (err == -EREMOTE) err = ip6_dst_blackhole(sk, &dst, fl); -- cgit v1.2.3 From 273447b352e69c327efdecfd6e1d6fe3edbdcd14 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 1 Mar 2011 14:27:04 -0800 Subject: ipv4: Kill can_sleep arg to ip_route_output_flow() This boolean state is now available in the flow flags. Signed-off-by: David S. Miller --- drivers/infiniband/hw/cxgb3/iwch_cm.c | 2 +- drivers/infiniband/hw/cxgb4/cm.c | 2 +- drivers/scsi/cxgbi/libcxgbi.c | 2 +- include/net/route.h | 6 +++--- net/dccp/ipv4.c | 2 +- net/ipv4/af_inet.c | 2 +- net/ipv4/inet_connection_sock.c | 2 +- net/ipv4/ip_output.c | 2 +- net/ipv4/raw.c | 2 +- net/ipv4/route.c | 7 ++++--- net/ipv4/udp.c | 2 +- net/l2tp/l2tp_ip.c | 2 +- 12 files changed, 17 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c index c7f776c8b2b8..e654285aa6ba 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cm.c +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c @@ -354,7 +354,7 @@ static struct rtable *find_route(struct t3cdev *dev, __be32 local_ip, } }; - if (ip_route_output_flow(&init_net, &rt, &fl, NULL, false)) + if (ip_route_output_flow(&init_net, &rt, &fl, NULL)) return NULL; return rt; } diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 5542c994338d..7e0484f18db5 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -331,7 +331,7 @@ static struct rtable *find_route(struct c4iw_dev *dev, __be32 local_ip, } }; - if (ip_route_output_flow(&init_net, &rt, &fl, NULL, false)) + if (ip_route_output_flow(&init_net, &rt, &fl, NULL)) return NULL; return rt; } diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index fabca75ac2f2..261aa817bdd5 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -470,7 +470,7 @@ static struct rtable *find_route_ipv4(__be32 saddr, __be32 daddr, } }; - if (ip_route_output_flow(&init_net, &rt, &fl, NULL, false)) + if (ip_route_output_flow(&init_net, &rt, &fl, NULL)) return NULL; return rt; diff --git a/include/net/route.h b/include/net/route.h index 1be5c05a0905..923e670586d4 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -120,7 +120,7 @@ extern void rt_cache_flush(struct net *net, int how); extern void rt_cache_flush_batch(struct net *net); extern int __ip_route_output_key(struct net *, struct rtable **, const struct flowi *flp); extern int ip_route_output_key(struct net *, struct rtable **, struct flowi *flp); -extern int ip_route_output_flow(struct net *, struct rtable **rp, struct flowi *flp, struct sock *sk, bool can_sleep); +extern int ip_route_output_flow(struct net *, struct rtable **rp, struct flowi *flp, struct sock *sk); extern int ip_route_input_common(struct sk_buff *skb, __be32 dst, __be32 src, u8 tos, struct net_device *devin, bool noref); @@ -198,7 +198,7 @@ static inline int ip_route_connect(struct rtable **rp, __be32 dst, *rp = NULL; } security_sk_classify_flow(sk, &fl); - return ip_route_output_flow(net, rp, &fl, sk, can_sleep); + return ip_route_output_flow(net, rp, &fl, sk); } static inline int ip_route_newports(struct rtable **rp, u8 protocol, @@ -222,7 +222,7 @@ static inline int ip_route_newports(struct rtable **rp, u8 protocol, ip_rt_put(*rp); *rp = NULL; security_sk_classify_flow(sk, &fl); - return ip_route_output_flow(sock_net(sk), rp, &fl, sk, false); + return ip_route_output_flow(sock_net(sk), rp, &fl, sk); } return 0; } diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 3d4b82f6adfd..a8ff95502081 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -475,7 +475,7 @@ static struct dst_entry* dccp_v4_route_skb(struct net *net, struct sock *sk, }; security_skb_classify_flow(skb, &fl); - if (ip_route_output_flow(net, &rt, &fl, sk, false)) { + if (ip_route_output_flow(net, &rt, &fl, sk)) { IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES); return NULL; } diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 7d90fe0ee5a6..44513bb8ac2e 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1174,7 +1174,7 @@ int inet_sk_rebuild_header(struct sock *sk) }; security_sk_classify_flow(sk, &fl); - err = ip_route_output_flow(sock_net(sk), &rt, &fl, sk, false); + err = ip_route_output_flow(sock_net(sk), &rt, &fl, sk); } if (!err) sk_setup_caps(sk, &rt->dst); diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 0caeb69de4b1..7f85d4aec26a 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -369,7 +369,7 @@ struct dst_entry *inet_csk_route_req(struct sock *sk, struct net *net = sock_net(sk); security_req_classify_flow(req, &fl); - if (ip_route_output_flow(net, &rt, &fl, sk, false)) + if (ip_route_output_flow(net, &rt, &fl, sk)) goto no_route; if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) goto route_err; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index e6905c562fb7..68dbe2d93d9d 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -355,7 +355,7 @@ int ip_queue_xmit(struct sk_buff *skb) * itself out. */ security_sk_classify_flow(sk, &fl); - if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk, false)) + if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk)) goto no_route; } sk_setup_caps(sk, &rt->dst); diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index e8e8613bcbcc..d7a2d1eaec09 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -564,7 +564,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, } security_sk_classify_flow(sk, &fl); - err = ip_route_output_flow(sock_net(sk), &rt, &fl, sk, true); + err = ip_route_output_flow(sock_net(sk), &rt, &fl, sk); } if (err) goto done; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 1ac3ecaf36e8..78462658fccb 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2720,7 +2720,7 @@ static int ipv4_dst_blackhole(struct net *net, struct rtable **rp, struct flowi } int ip_route_output_flow(struct net *net, struct rtable **rp, struct flowi *flp, - struct sock *sk, bool can_sleep) + struct sock *sk) { int err; @@ -2733,7 +2733,8 @@ int ip_route_output_flow(struct net *net, struct rtable **rp, struct flowi *flp, if (!flp->fl4_dst) flp->fl4_dst = (*rp)->rt_dst; err = __xfrm_lookup(net, (struct dst_entry **)rp, flp, sk, - can_sleep ? XFRM_LOOKUP_WAIT : 0); + ((flp->flags & FLOWI_FLAG_CAN_SLEEP) ? + XFRM_LOOKUP_WAIT : 0)); if (err == -EREMOTE) err = ipv4_dst_blackhole(net, rp, flp); @@ -2746,7 +2747,7 @@ EXPORT_SYMBOL_GPL(ip_route_output_flow); int ip_route_output_key(struct net *net, struct rtable **rp, struct flowi *flp) { - return ip_route_output_flow(net, rp, flp, NULL, false); + return ip_route_output_flow(net, rp, flp, NULL); } EXPORT_SYMBOL(ip_route_output_key); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index c6bcc93debd5..ed9a5b7bee53 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -922,7 +922,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, struct net *net = sock_net(sk); security_sk_classify_flow(sk, &fl); - err = ip_route_output_flow(net, &rt, &fl, sk, true); + err = ip_route_output_flow(net, &rt, &fl, sk); if (err) { if (err == -ENETUNREACH) IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES); diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 7744a8e4b4c6..5381cebe516d 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -489,7 +489,7 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m * itself out. */ security_sk_classify_flow(sk, &fl); - if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk, false)) + if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk)) goto no_route; } sk_setup_caps(sk, &rt->dst); -- cgit v1.2.3 From a1414715f0ac905fb4b3a158ff6548d37bbe6165 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 1 Mar 2011 14:32:04 -0800 Subject: ipv6: Change final dst lookup arg name to "can_sleep" Since it indicates whether we are invoked from a sleepable context or not. Signed-off-by: David S. Miller --- include/net/ipv6.h | 4 ++-- net/ipv6/ip6_output.c | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 1fc5631cf1a2..8f78aace0a9d 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -515,11 +515,11 @@ extern int ip6_dst_lookup(struct sock *sk, extern struct dst_entry * ip6_dst_lookup_flow(struct sock *sk, struct flowi *fl, const struct in6_addr *final_dst, - bool want_blackhole); + bool can_sleep); extern struct dst_entry * ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi *fl, const struct in6_addr *final_dst, - bool want_blackhole); + bool can_sleep); extern int ip6_dst_blackhole(struct sock *sk, struct dst_entry **dst, struct flowi *fl); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 77b1942f335b..b5f8769dbdf4 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1006,7 +1006,7 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup); * @sk: socket which provides route info * @fl: flow to lookup * @final_dst: final destination address for ipsec lookup - * @want_blackhole: IPSEC blackhole handling desired + * @can_sleep: we are in a sleepable context * * This function performs a route lookup on the given flow. * @@ -1015,7 +1015,7 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup); */ struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi *fl, const struct in6_addr *final_dst, - bool want_blackhole) + bool can_sleep) { struct dst_entry *dst = NULL; int err; @@ -1025,7 +1025,7 @@ struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi *fl, return ERR_PTR(err); if (final_dst) ipv6_addr_copy(&fl->fl6_dst, final_dst); - if (want_blackhole) { + if (can_sleep) { fl->flags |= FLOWI_FLAG_CAN_SLEEP; err = __xfrm_lookup(sock_net(sk), &dst, fl, sk, XFRM_LOOKUP_WAIT); if (err == -EREMOTE) @@ -1046,7 +1046,7 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow); * @sk: socket which provides the dst cache and route info * @fl: flow to lookup * @final_dst: final destination address for ipsec lookup - * @want_blackhole: IPSEC blackhole handling desired + * @can_sleep: we are in a sleepable context * * This function performs a route lookup on the given flow with the * possibility of using the cached route in the socket if it is valid. @@ -1058,7 +1058,7 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow); */ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi *fl, const struct in6_addr *final_dst, - bool want_blackhole) + bool can_sleep) { struct dst_entry *dst = sk_dst_check(sk, inet6_sk(sk)->dst_cookie); int err; @@ -1070,7 +1070,7 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi *fl, return ERR_PTR(err); if (final_dst) ipv6_addr_copy(&fl->fl6_dst, final_dst); - if (want_blackhole) { + if (can_sleep) { fl->flags |= FLOWI_FLAG_CAN_SLEEP; err = __xfrm_lookup(sock_net(sk), &dst, fl, sk, XFRM_LOOKUP_WAIT); if (err == -EREMOTE) -- cgit v1.2.3 From 80c0bc9e37adfc892af82cb6aa8cace79f8a96cb Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 1 Mar 2011 14:36:37 -0800 Subject: xfrm: Kill XFRM_LOOKUP_WAIT flag. This can be determined from the flow flags instead. Signed-off-by: David S. Miller --- include/net/dst.h | 3 +-- net/decnet/dn_route.c | 5 +++-- net/ipv4/route.c | 4 +--- net/ipv6/ip6_output.c | 4 ++-- net/xfrm/xfrm_policy.c | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index 4fedffd7c56f..15d67c8d3ce8 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -421,8 +421,7 @@ extern void dst_init(void); /* Flags for xfrm_lookup flags argument. */ enum { - XFRM_LOOKUP_WAIT = 1 << 0, - XFRM_LOOKUP_ICMP = 1 << 1, + XFRM_LOOKUP_ICMP = 1 << 0, }; struct flowi; diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 06c054d5ccba..0877147d2167 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1233,8 +1233,9 @@ int dn_route_output_sock(struct dst_entry **pprt, struct flowi *fl, struct sock err = __dn_route_output_key(pprt, fl, flags & MSG_TRYHARD); if (err == 0 && fl->proto) { - err = xfrm_lookup(&init_net, pprt, fl, sk, - (flags & MSG_DONTWAIT) ? 0 : XFRM_LOOKUP_WAIT); + if (!(flags & MSG_DONTWAIT)) + fl->flags |= FLOWI_FLAG_CAN_SLEEP; + err = xfrm_lookup(&init_net, pprt, fl, sk, 0); } return err; } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 78462658fccb..23d205043d92 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2732,9 +2732,7 @@ int ip_route_output_flow(struct net *net, struct rtable **rp, struct flowi *flp, flp->fl4_src = (*rp)->rt_src; if (!flp->fl4_dst) flp->fl4_dst = (*rp)->rt_dst; - err = __xfrm_lookup(net, (struct dst_entry **)rp, flp, sk, - ((flp->flags & FLOWI_FLAG_CAN_SLEEP) ? - XFRM_LOOKUP_WAIT : 0)); + err = __xfrm_lookup(net, (struct dst_entry **)rp, flp, sk, 0); if (err == -EREMOTE) err = ipv4_dst_blackhole(net, rp, flp); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index b5f8769dbdf4..faf7b9d1d536 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1027,7 +1027,7 @@ struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi *fl, ipv6_addr_copy(&fl->fl6_dst, final_dst); if (can_sleep) { fl->flags |= FLOWI_FLAG_CAN_SLEEP; - err = __xfrm_lookup(sock_net(sk), &dst, fl, sk, XFRM_LOOKUP_WAIT); + err = __xfrm_lookup(sock_net(sk), &dst, fl, sk, 0); if (err == -EREMOTE) err = ip6_dst_blackhole(sk, &dst, fl); if (err) @@ -1072,7 +1072,7 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi *fl, ipv6_addr_copy(&fl->fl6_dst, final_dst); if (can_sleep) { fl->flags |= FLOWI_FLAG_CAN_SLEEP; - err = __xfrm_lookup(sock_net(sk), &dst, fl, sk, XFRM_LOOKUP_WAIT); + err = __xfrm_lookup(sock_net(sk), &dst, fl, sk, 0); if (err == -EREMOTE) err = ip6_dst_blackhole(sk, &dst, fl); if (err) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 41a91d27d3ea..f4c7467a614e 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1831,7 +1831,7 @@ restart: XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES); return -EREMOTE; } - if (flags & XFRM_LOOKUP_WAIT) { + if (fl->flags & FLOWI_FLAG_CAN_SLEEP) { DECLARE_WAITQUEUE(wait, current); add_wait_queue(&net->xfrm.km_waitq, &wait); -- cgit v1.2.3 From 69ead7afdf6028184f713a77376ee26f8aaafdcd Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 1 Mar 2011 14:45:33 -0800 Subject: ipv6: Normalize arguments to ip6_dst_blackhole(). Return a dst pointer which is potentitally error encoded. Don't pass original dst pointer by reference, pass a struct net instead of a socket, and elide the flow argument since it is unnecessary. Signed-off-by: David S. Miller --- include/net/ipv6.h | 5 ++--- net/ipv6/ip6_output.c | 4 ++-- net/ipv6/route.c | 12 +++++------- 3 files changed, 9 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 8f78aace0a9d..5d125c1293a9 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -520,9 +520,8 @@ extern struct dst_entry * ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi *fl, const struct in6_addr *final_dst, bool can_sleep); -extern int ip6_dst_blackhole(struct sock *sk, - struct dst_entry **dst, - struct flowi *fl); +extern struct dst_entry * ip6_dst_blackhole(struct net *net, + struct dst_entry *orig_dst); /* * skb processing functions diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index faf7b9d1d536..ac16f3b2a029 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1029,7 +1029,7 @@ struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi *fl, fl->flags |= FLOWI_FLAG_CAN_SLEEP; err = __xfrm_lookup(sock_net(sk), &dst, fl, sk, 0); if (err == -EREMOTE) - err = ip6_dst_blackhole(sk, &dst, fl); + return ip6_dst_blackhole(sock_net(sk), dst); if (err) return ERR_PTR(err); } else { @@ -1074,7 +1074,7 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi *fl, fl->flags |= FLOWI_FLAG_CAN_SLEEP; err = __xfrm_lookup(sock_net(sk), &dst, fl, sk, 0); if (err == -EREMOTE) - err = ip6_dst_blackhole(sk, &dst, fl); + return ip6_dst_blackhole(sock_net(sk), dst); if (err) return ERR_PTR(err); } else { diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 7e9443f835f9..cf6fdeabb6f2 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -870,11 +870,10 @@ struct dst_entry * ip6_route_output(struct net *net, struct sock *sk, EXPORT_SYMBOL(ip6_route_output); -int ip6_dst_blackhole(struct sock *sk, struct dst_entry **dstp, struct flowi *fl) +struct dst_entry *ip6_dst_blackhole(struct net *net, struct dst_entry *dst_orig) { - struct rt6_info *ort = (struct rt6_info *) *dstp; - struct rt6_info *rt = (struct rt6_info *) - dst_alloc(&ip6_dst_blackhole_ops, 1); + struct rt6_info *rt = dst_alloc(&ip6_dst_blackhole_ops, 1); + struct rt6_info *ort = (struct rt6_info *) dst_orig; struct dst_entry *new = NULL; if (rt) { @@ -905,9 +904,8 @@ int ip6_dst_blackhole(struct sock *sk, struct dst_entry **dstp, struct flowi *fl dst_free(new); } - dst_release(*dstp); - *dstp = new; - return new ? 0 : -ENOMEM; + dst_release(dst_orig); + return new ? new : ERR_PTR(-ENOMEM); } EXPORT_SYMBOL_GPL(ip6_dst_blackhole); -- cgit v1.2.3 From 2774c131b1d19920b4587db1cfbd6f0750ad1f15 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 1 Mar 2011 14:59:04 -0800 Subject: xfrm: Handle blackhole route creation via afinfo. That way we don't have to potentially do this in every xfrm_lookup() caller. Signed-off-by: David S. Miller --- include/net/dst.h | 8 -------- include/net/ipv6.h | 4 ++-- include/net/route.h | 1 + include/net/xfrm.h | 1 + net/ipv4/route.c | 20 +++++++------------- net/ipv4/xfrm4_policy.c | 1 + net/ipv6/ip6_output.c | 32 ++++++++++---------------------- net/ipv6/route.c | 3 +-- net/ipv6/xfrm6_policy.c | 1 + net/xfrm/xfrm_policy.c | 46 ++++++++++++++++++++++++++-------------------- 10 files changed, 50 insertions(+), 67 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index 15d67c8d3ce8..8948452132ad 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -432,17 +432,9 @@ static inline int xfrm_lookup(struct net *net, struct dst_entry **dst_p, { return 0; } -static inline int __xfrm_lookup(struct net *net, struct dst_entry **dst_p, - const struct flowi *fl, struct sock *sk, - int flags) -{ - return 0; -} #else extern int xfrm_lookup(struct net *net, struct dst_entry **dst_p, const struct flowi *fl, struct sock *sk, int flags); -extern int __xfrm_lookup(struct net *net, struct dst_entry **dst_p, - const struct flowi *fl, struct sock *sk, int flags); #endif #endif diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 5d125c1293a9..d6d077d7f2cf 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -520,8 +520,8 @@ extern struct dst_entry * ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi *fl, const struct in6_addr *final_dst, bool can_sleep); -extern struct dst_entry * ip6_dst_blackhole(struct net *net, - struct dst_entry *orig_dst); +extern struct dst_entry * ip6_blackhole_route(struct net *net, + struct dst_entry *orig_dst); /* * skb processing functions diff --git a/include/net/route.h b/include/net/route.h index 923e670586d4..707cfc8eccdc 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -121,6 +121,7 @@ extern void rt_cache_flush_batch(struct net *net); extern int __ip_route_output_key(struct net *, struct rtable **, const struct flowi *flp); extern int ip_route_output_key(struct net *, struct rtable **, struct flowi *flp); extern int ip_route_output_flow(struct net *, struct rtable **rp, struct flowi *flp, struct sock *sk); +extern struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_orig); extern int ip_route_input_common(struct sk_buff *skb, __be32 dst, __be32 src, u8 tos, struct net_device *devin, bool noref); diff --git a/include/net/xfrm.h b/include/net/xfrm.h index efded23dc4ae..d5dcf3974636 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -280,6 +280,7 @@ struct xfrm_policy_afinfo { int (*fill_dst)(struct xfrm_dst *xdst, struct net_device *dev, const struct flowi *fl); + struct dst_entry *(*blackhole_route)(struct net *net, struct dst_entry *orig); }; extern int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 23d205043d92..e24e4cf2a112 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2675,12 +2675,10 @@ static struct dst_ops ipv4_dst_blackhole_ops = { .update_pmtu = ipv4_rt_blackhole_update_pmtu, }; - -static int ipv4_dst_blackhole(struct net *net, struct rtable **rp, struct flowi *flp) +struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_orig) { - struct rtable *ort = *rp; - struct rtable *rt = (struct rtable *) - dst_alloc(&ipv4_dst_blackhole_ops, 1); + struct rtable *rt = dst_alloc(&ipv4_dst_blackhole_ops, 1); + struct rtable *ort = (struct rtable *) dst_orig; if (rt) { struct dst_entry *new = &rt->dst; @@ -2714,9 +2712,9 @@ static int ipv4_dst_blackhole(struct net *net, struct rtable **rp, struct flowi dst_free(new); } - dst_release(&(*rp)->dst); - *rp = rt; - return rt ? 0 : -ENOMEM; + dst_release(dst_orig); + + return rt ? &rt->dst : ERR_PTR(-ENOMEM); } int ip_route_output_flow(struct net *net, struct rtable **rp, struct flowi *flp, @@ -2732,11 +2730,7 @@ int ip_route_output_flow(struct net *net, struct rtable **rp, struct flowi *flp, flp->fl4_src = (*rp)->rt_src; if (!flp->fl4_dst) flp->fl4_dst = (*rp)->rt_dst; - err = __xfrm_lookup(net, (struct dst_entry **)rp, flp, sk, 0); - if (err == -EREMOTE) - err = ipv4_dst_blackhole(net, rp, flp); - - return err; + return xfrm_lookup(net, (struct dst_entry **)rp, flp, sk, 0); } return 0; diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 63aa88efdcef..5f0f058dc376 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -234,6 +234,7 @@ static struct xfrm_policy_afinfo xfrm4_policy_afinfo = { .get_tos = xfrm4_get_tos, .init_path = xfrm4_init_path, .fill_dst = xfrm4_fill_dst, + .blackhole_route = ipv4_blackhole_route, }; #ifdef CONFIG_SYSCTL diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index ac16f3b2a029..35a4ad90a0f5 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1025,18 +1025,12 @@ struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi *fl, return ERR_PTR(err); if (final_dst) ipv6_addr_copy(&fl->fl6_dst, final_dst); - if (can_sleep) { + if (can_sleep) fl->flags |= FLOWI_FLAG_CAN_SLEEP; - err = __xfrm_lookup(sock_net(sk), &dst, fl, sk, 0); - if (err == -EREMOTE) - return ip6_dst_blackhole(sock_net(sk), dst); - if (err) - return ERR_PTR(err); - } else { - err = xfrm_lookup(sock_net(sk), &dst, fl, sk, 0); - if (err) - return ERR_PTR(err); - } + + err = xfrm_lookup(sock_net(sk), &dst, fl, sk, 0); + if (err) + return ERR_PTR(err); return dst; } EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow); @@ -1070,18 +1064,12 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi *fl, return ERR_PTR(err); if (final_dst) ipv6_addr_copy(&fl->fl6_dst, final_dst); - if (can_sleep) { + if (can_sleep) fl->flags |= FLOWI_FLAG_CAN_SLEEP; - err = __xfrm_lookup(sock_net(sk), &dst, fl, sk, 0); - if (err == -EREMOTE) - return ip6_dst_blackhole(sock_net(sk), dst); - if (err) - return ERR_PTR(err); - } else { - err = xfrm_lookup(sock_net(sk), &dst, fl, sk, 0); - if (err) - return ERR_PTR(err); - } + + err = xfrm_lookup(sock_net(sk), &dst, fl, sk, 0); + if (err) + return ERR_PTR(err); return dst; } EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index cf6fdeabb6f2..053a92ebf2d5 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -870,7 +870,7 @@ struct dst_entry * ip6_route_output(struct net *net, struct sock *sk, EXPORT_SYMBOL(ip6_route_output); -struct dst_entry *ip6_dst_blackhole(struct net *net, struct dst_entry *dst_orig) +struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) { struct rt6_info *rt = dst_alloc(&ip6_dst_blackhole_ops, 1); struct rt6_info *ort = (struct rt6_info *) dst_orig; @@ -907,7 +907,6 @@ struct dst_entry *ip6_dst_blackhole(struct net *net, struct dst_entry *dst_orig) dst_release(dst_orig); return new ? new : ERR_PTR(-ENOMEM); } -EXPORT_SYMBOL_GPL(ip6_dst_blackhole); /* * Destination cache support functions diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index c128ca1affe3..48ce496802fd 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -274,6 +274,7 @@ static struct xfrm_policy_afinfo xfrm6_policy_afinfo = { .get_tos = xfrm6_get_tos, .init_path = xfrm6_init_path, .fill_dst = xfrm6_fill_dst, + .blackhole_route = ip6_blackhole_route, }; static int __init xfrm6_policy_init(void) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index f4c7467a614e..0248afa11cda 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1735,14 +1735,31 @@ error: return ERR_PTR(err); } +static struct dst_entry *make_blackhole(struct net *net, u16 family, + struct dst_entry *dst_orig) +{ + struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); + struct dst_entry *ret; + + if (!afinfo) { + dst_release(dst_orig); + ret = ERR_PTR(-EINVAL); + } else { + ret = afinfo->blackhole_route(net, dst_orig); + } + xfrm_policy_put_afinfo(afinfo); + + return ret; +} + /* Main function: finds/creates a bundle for given flow. * * At the moment we eat a raw IP route. Mostly to speed up lookups * on interfaces with disabled IPsec. */ -int __xfrm_lookup(struct net *net, struct dst_entry **dst_p, - const struct flowi *fl, - struct sock *sk, int flags) +int xfrm_lookup(struct net *net, struct dst_entry **dst_p, + const struct flowi *fl, + struct sock *sk, int flags) { struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX]; struct flow_cache_object *flo; @@ -1829,7 +1846,12 @@ restart: dst_release(dst); xfrm_pols_put(pols, drop_pols); XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES); - return -EREMOTE; + + dst = make_blackhole(net, family, dst_orig); + if (IS_ERR(dst)) + return PTR_ERR(dst); + *dst_p = dst; + return 0; } if (fl->flags & FLOWI_FLAG_CAN_SLEEP) { DECLARE_WAITQUEUE(wait, current); @@ -1895,22 +1917,6 @@ dropdst: xfrm_pols_put(pols, drop_pols); return err; } -EXPORT_SYMBOL(__xfrm_lookup); - -int xfrm_lookup(struct net *net, struct dst_entry **dst_p, - const struct flowi *fl, - struct sock *sk, int flags) -{ - int err = __xfrm_lookup(net, dst_p, fl, sk, flags); - - if (err == -EREMOTE) { - dst_release(*dst_p); - *dst_p = NULL; - err = -EAGAIN; - } - - return err; -} EXPORT_SYMBOL(xfrm_lookup); static inline int -- cgit v1.2.3 From f51b452bed4ae5c20e1f8a790e4ed8663d909a40 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 25 Jan 2011 21:54:50 +0100 Subject: tracing: don't trace the BKL No reason to trace it when the last user is gone. Signed-off-by: Arnd Bergmann Acked-by: Frederic Weisbecker --- include/trace/events/bkl.h | 61 ---------------------------------------------- lib/kernel_lock.c | 7 ------ 2 files changed, 68 deletions(-) delete mode 100644 include/trace/events/bkl.h (limited to 'include') diff --git a/include/trace/events/bkl.h b/include/trace/events/bkl.h deleted file mode 100644 index 1af72dc24278..000000000000 --- a/include/trace/events/bkl.h +++ /dev/null @@ -1,61 +0,0 @@ -#undef TRACE_SYSTEM -#define TRACE_SYSTEM bkl - -#if !defined(_TRACE_BKL_H) || defined(TRACE_HEADER_MULTI_READ) -#define _TRACE_BKL_H - -#include - -TRACE_EVENT(lock_kernel, - - TP_PROTO(const char *func, const char *file, int line), - - TP_ARGS(func, file, line), - - TP_STRUCT__entry( - __field( int, depth ) - __field_ext( const char *, func, FILTER_PTR_STRING ) - __field_ext( const char *, file, FILTER_PTR_STRING ) - __field( int, line ) - ), - - TP_fast_assign( - /* We want to record the lock_depth after lock is acquired */ - __entry->depth = current->lock_depth + 1; - __entry->func = func; - __entry->file = file; - __entry->line = line; - ), - - TP_printk("depth=%d file:line=%s:%d func=%s()", __entry->depth, - __entry->file, __entry->line, __entry->func) -); - -TRACE_EVENT(unlock_kernel, - - TP_PROTO(const char *func, const char *file, int line), - - TP_ARGS(func, file, line), - - TP_STRUCT__entry( - __field(int, depth ) - __field(const char *, func ) - __field(const char *, file ) - __field(int, line ) - ), - - TP_fast_assign( - __entry->depth = current->lock_depth; - __entry->func = func; - __entry->file = file; - __entry->line = line; - ), - - TP_printk("depth=%d file:line=%s:%d func=%s()", __entry->depth, - __entry->file, __entry->line, __entry->func) -); - -#endif /* _TRACE_BKL_H */ - -/* This part must be outside protection */ -#include diff --git a/lib/kernel_lock.c b/lib/kernel_lock.c index b135d04aa48a..d80e12265862 100644 --- a/lib/kernel_lock.c +++ b/lib/kernel_lock.c @@ -10,9 +10,6 @@ #include #include -#define CREATE_TRACE_POINTS -#include - /* * The 'big kernel lock' * @@ -120,8 +117,6 @@ void __lockfunc _lock_kernel(const char *func, const char *file, int line) { int depth = current->lock_depth + 1; - trace_lock_kernel(func, file, line); - if (likely(!depth)) { might_sleep(); __lock_kernel(); @@ -134,8 +129,6 @@ void __lockfunc _unlock_kernel(const char *func, const char *file, int line) BUG_ON(current->lock_depth < 0); if (likely(--current->lock_depth < 0)) __unlock_kernel(); - - trace_unlock_kernel(func, file, line); } EXPORT_SYMBOL(_lock_kernel); -- cgit v1.2.3 From 4a5f7bda8fe9d0ed08ed4c5beb5dc3fa62f09d05 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 1 Mar 2011 20:10:46 +0000 Subject: ASoC: Add platform data for WM9081 IRQ pin configuration The WM9081 IRQ output can be either active high or active low and can support either CMOS or open drain modes. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/wm9081.h | 9 ++++++--- sound/soc/codecs/wm9081.c | 29 +++++++++++++++++++---------- 2 files changed, 25 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/sound/wm9081.h b/include/sound/wm9081.h index e173ddbf6bd4..f34b0b1716d8 100644 --- a/include/sound/wm9081.h +++ b/include/sound/wm9081.h @@ -17,9 +17,12 @@ struct wm9081_retune_mobile_setting { u16 config[20]; }; -struct wm9081_retune_mobile_config { - struct wm9081_retune_mobile_setting *configs; - int num_configs; +struct wm9081_pdata { + bool irq_high; /* IRQ is active high */ + bool irq_cmos; /* IRQ is in CMOS mode */ + + struct wm9081_retune_mobile_setting *retune_configs; + int num_retune_configs; }; #endif diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 2103623a0776..7883f3ed797b 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -167,7 +167,7 @@ struct wm9081_priv { int fll_fref; int fll_fout; int tdm_width; - struct wm9081_retune_mobile_config *retune; + struct wm9081_pdata pdata; }; static int wm9081_volatile_register(struct snd_soc_codec *codec, unsigned int reg) @@ -1082,21 +1082,22 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream, aif4 |= wm9081->bclk / wm9081->fs; /* Apply a ReTune Mobile configuration if it's in use */ - if (wm9081->retune) { - struct wm9081_retune_mobile_config *retune = wm9081->retune; + if (wm9081->pdata.num_retune_configs) { + struct wm9081_pdata *pdata = &wm9081->pdata; struct wm9081_retune_mobile_setting *s; int eq1; best = 0; - best_val = abs(retune->configs[0].rate - wm9081->fs); - for (i = 0; i < retune->num_configs; i++) { - cur_val = abs(retune->configs[i].rate - wm9081->fs); + best_val = abs(pdata->retune_configs[0].rate - wm9081->fs); + for (i = 0; i < pdata->num_retune_configs; i++) { + cur_val = abs(pdata->retune_configs[i].rate - + wm9081->fs); if (cur_val < best_val) { best_val = cur_val; best = i; } } - s = &retune->configs[best]; + s = &pdata->retune_configs[best]; dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n", s->name, s->rate); @@ -1255,6 +1256,14 @@ static int wm9081_probe(struct snd_soc_codec *codec) return ret; } + reg = 0; + if (wm9081->pdata.irq_high) + reg |= WM9081_IRQ_POL; + if (!wm9081->pdata.irq_cmos) + reg |= WM9081_IRQ_OP_CTRL; + snd_soc_update_bits(codec, WM9081_INTERRUPT_CONTROL, + WM9081_IRQ_POL | WM9081_IRQ_OP_CTRL, reg); + wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Enable zero cross by default */ @@ -1266,7 +1275,7 @@ static int wm9081_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, wm9081_snd_controls, ARRAY_SIZE(wm9081_snd_controls)); - if (!wm9081->retune) { + if (!wm9081->pdata.num_retune_configs) { dev_dbg(codec->dev, "No ReTune Mobile data, using normal EQ\n"); snd_soc_add_controls(codec, wm9081_eq_controls, @@ -1343,8 +1352,8 @@ static __devinit int wm9081_i2c_probe(struct i2c_client *i2c, wm9081->control_data = i2c; if (dev_get_platdata(&i2c->dev)) - memcpy(&wm9081->retune, dev_get_platdata(&i2c->dev), - sizeof(wm9081->retune)); + memcpy(&wm9081->pdata, dev_get_platdata(&i2c->dev), + sizeof(wm9081->pdata)); ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm9081, &wm9081_dai, 1); -- cgit v1.2.3 From 0e0b494ca8c54a7297d0cc549405091019b3b77e Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 23 Jan 2011 09:42:50 -0600 Subject: libata: separate error handler into usable components Right at the moment, the libata error handler is incredibly monolithic. This makes it impossible to use from composite drivers like libsas and ipr which have to handle error themselves in the first instance. The essence of the change is to split the monolithic error handler into two components: one which handles a queue of ata commands for processing and the other which handles the back end of readying a port. This allows the upper error handler fine grained control in calling libsas functions (and making sure they only get called for ATA commands whose lower errors have been fixed up). Signed-off-by: James Bottomley Signed-off-by: Jeff Garzik --- drivers/ata/libata-eh.c | 53 ++++++++++++++++++++++++++++++++++++++++--------- include/linux/libata.h | 2 ++ 2 files changed, 46 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 073b88156b3c..df3f3140c9c7 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -587,7 +587,6 @@ static void ata_eh_unload(struct ata_port *ap) void ata_scsi_error(struct Scsi_Host *host) { struct ata_port *ap = ata_shost_to_port(host); - int i; unsigned long flags; LIST_HEAD(eh_work_q); @@ -597,6 +596,34 @@ void ata_scsi_error(struct Scsi_Host *host) list_splice_init(&host->eh_cmd_q, &eh_work_q); spin_unlock_irqrestore(host->host_lock, flags); + ata_scsi_cmd_error_handler(host, ap, &eh_work_q); + + /* If we timed raced normal completion and there is nothing to + recover nr_timedout == 0 why exactly are we doing error recovery ? */ + ata_scsi_port_error_handler(host, ap); + + /* finish or retry handled scmd's and clean up */ + WARN_ON(host->host_failed || !list_empty(&eh_work_q)); + + DPRINTK("EXIT\n"); +} + +/** + * ata_scsi_cmd_error_handler - error callback for a list of commands + * @host: scsi host containing the port + * @ap: ATA port within the host + * @eh_work_q: list of commands to process + * + * process the given list of commands and return those finished to the + * ap->eh_done_q. This function is the first part of the libata error + * handler which processes a given list of failed commands. + */ +void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, + struct list_head *eh_work_q) +{ + int i; + unsigned long flags; + /* make sure sff pio task is not running */ ata_sff_flush_pio_task(ap); @@ -632,7 +659,7 @@ void ata_scsi_error(struct Scsi_Host *host) if (ap->ops->lost_interrupt) ap->ops->lost_interrupt(ap); - list_for_each_entry_safe(scmd, tmp, &eh_work_q, eh_entry) { + list_for_each_entry_safe(scmd, tmp, eh_work_q, eh_entry) { struct ata_queued_cmd *qc; for (i = 0; i < ATA_MAX_QUEUE; i++) { @@ -676,8 +703,20 @@ void ata_scsi_error(struct Scsi_Host *host) } else spin_unlock_wait(ap->lock); - /* If we timed raced normal completion and there is nothing to - recover nr_timedout == 0 why exactly are we doing error recovery ? */ +} +EXPORT_SYMBOL(ata_scsi_cmd_error_handler); + +/** + * ata_scsi_port_error_handler - recover the port after the commands + * @host: SCSI host containing the port + * @ap: the ATA port + * + * Handle the recovery of the port @ap after all the commands + * have been recovered. + */ +void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap) +{ + unsigned long flags; /* invoke error handler */ if (ap->ops->error_handler) { @@ -766,9 +805,6 @@ void ata_scsi_error(struct Scsi_Host *host) ap->ops->eng_timeout(ap); } - /* finish or retry handled scmd's and clean up */ - WARN_ON(host->host_failed || !list_empty(&eh_work_q)); - scsi_eh_flush_done_q(&ap->eh_done_q); /* clean up */ @@ -789,9 +825,8 @@ void ata_scsi_error(struct Scsi_Host *host) wake_up_all(&ap->eh_wait_q); spin_unlock_irqrestore(ap->lock, flags); - - DPRINTK("EXIT\n"); } +EXPORT_SYMBOL_GPL(ata_scsi_port_error_handler); /** * ata_port_wait_eh - Wait for the currently pending EH to complete diff --git a/include/linux/libata.h b/include/linux/libata.h index c9c5d7ad1a2b..9739317c707a 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1050,6 +1050,8 @@ extern int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth, int reason); extern struct ata_device *ata_dev_pair(struct ata_device *adev); extern int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev); +extern void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap); +extern void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, struct list_head *eh_q); extern int ata_cable_40wire(struct ata_port *ap); extern int ata_cable_80wire(struct ata_port *ap); -- cgit v1.2.3 From 00dd4998a60599d98b4d6635820a1fbeafa5b021 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 23 Jan 2011 09:44:12 -0600 Subject: libsas: convert to libata new error handler The conversion is quite complex given that the libata new error handler has to be hooked into the current libsas timeout and error handling. The way this is done is to process all the failed commands via libsas first, but if they have no underlying sas task (and they're on a sata device) assume they are destined for the libata error handler and send them accordingly. Finally, activate the port recovery of the libata error handler for each port known to the host. This is somewhat suboptimal, since that port may not need recovering, but given the current architecture of the libata error handler, it's the only way; and the spurious activation is harmless. Signed-off-by: James Bottomley Signed-off-by: Jeff Garzik --- drivers/scsi/libsas/sas_ata.c | 87 ++++++++++++++++++++++++++++++++++--- drivers/scsi/libsas/sas_scsi_host.c | 14 +++++- include/scsi/sas_ata.h | 22 ++++++++++ 3 files changed, 115 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index e1a395b438ee..8f56d5fbf6ec 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -238,37 +238,43 @@ static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) return true; } -static void sas_ata_phy_reset(struct ata_port *ap) +static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, + unsigned long deadline) { + struct ata_port *ap = link->ap; struct domain_device *dev = ap->private_data; struct sas_internal *i = to_sas_internal(dev->port->ha->core.shost->transportt); int res = TMF_RESP_FUNC_FAILED; + int ret = 0; if (i->dft->lldd_I_T_nexus_reset) res = i->dft->lldd_I_T_nexus_reset(dev); - if (res != TMF_RESP_FUNC_COMPLETE) + if (res != TMF_RESP_FUNC_COMPLETE) { SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __func__); + ret = -EAGAIN; + } switch (dev->sata_dev.command_set) { case ATA_COMMAND_SET: SAS_DPRINTK("%s: Found ATA device.\n", __func__); - ap->link.device[0].class = ATA_DEV_ATA; + *class = ATA_DEV_ATA; break; case ATAPI_COMMAND_SET: SAS_DPRINTK("%s: Found ATAPI device.\n", __func__); - ap->link.device[0].class = ATA_DEV_ATAPI; + *class = ATA_DEV_ATAPI; break; default: SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", __func__, dev->sata_dev.command_set); - ap->link.device[0].class = ATA_DEV_UNKNOWN; + *class = ATA_DEV_UNKNOWN; break; } ap->cbl = ATA_CBL_SATA; + return ret; } static void sas_ata_post_internal(struct ata_queued_cmd *qc) @@ -349,7 +355,11 @@ static int sas_ata_scr_read(struct ata_link *link, unsigned int sc_reg_in, } static struct ata_port_operations sas_sata_ops = { - .phy_reset = sas_ata_phy_reset, + .prereset = ata_std_prereset, + .softreset = NULL, + .hardreset = sas_ata_hard_reset, + .postreset = ata_std_postreset, + .error_handler = ata_std_error_handler, .post_internal_cmd = sas_ata_post_internal, .qc_defer = ata_std_qc_defer, .qc_prep = ata_noop_qc_prep, @@ -781,3 +791,68 @@ int sas_discover_sata(struct domain_device *dev) return res; } + +void sas_ata_strategy_handler(struct Scsi_Host *shost) +{ + struct scsi_device *sdev; + + shost_for_each_device(sdev, shost) { + struct domain_device *ddev = sdev_to_domain_dev(sdev); + struct ata_port *ap = ddev->sata_dev.ap; + + if (!dev_is_sata(ddev)) + continue; + + ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler"); + ata_scsi_port_error_handler(shost, ap); + } +} + +int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task, + enum blk_eh_timer_return *rtn) +{ + struct domain_device *ddev = cmd_to_domain_dev(cmd); + + if (!dev_is_sata(ddev) || task) + return 0; + + /* we're a sata device with no task, so this must be a libata + * eh timeout. Ideally should hook into libata timeout + * handling, but there's no point, it just wants to activate + * the eh thread */ + *rtn = BLK_EH_NOT_HANDLED; + return 1; +} + +int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, + struct list_head *done_q) +{ + int rtn = 0; + struct scsi_cmnd *cmd, *n; + struct ata_port *ap; + + do { + LIST_HEAD(sata_q); + + ap = NULL; + + list_for_each_entry_safe(cmd, n, work_q, eh_entry) { + struct domain_device *ddev = cmd_to_domain_dev(cmd); + + if (!dev_is_sata(ddev) || TO_SAS_TASK(cmd)) + continue; + if(ap && ap != ddev->sata_dev.ap) + continue; + ap = ddev->sata_dev.ap; + rtn = 1; + list_move(&cmd->eh_entry, &sata_q); + } + + if (!list_empty(&sata_q)) { + ata_port_printk(ap, KERN_DEBUG,"sas eh calling libata cmd error handler\n"); + ata_scsi_cmd_error_handler(shost, ap, &sata_q); + } + } while (ap); + + return rtn; +} diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 9a7aaf5f1311..67758ea8eb7f 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -663,11 +663,16 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) * scsi_unjam_host does, but we skip scsi_eh_abort_cmds because any * command we see here has no sas_task and is thus unknown to the HA. */ - if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q)) - scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q); + if (!sas_ata_eh(shost, &eh_work_q, &ha->eh_done_q)) + if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q)) + scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q); out: + /* now link into libata eh --- if we have any ata devices */ + sas_ata_strategy_handler(shost); + scsi_eh_flush_done_q(&ha->eh_done_q); + SAS_DPRINTK("--- Exit %s\n", __func__); return; } @@ -676,6 +681,11 @@ enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) { struct sas_task *task = TO_SAS_TASK(cmd); unsigned long flags; + enum blk_eh_timer_return rtn; + + if (sas_ata_timed_out(cmd, task, &rtn)) + return rtn; + if (!task) { cmd->request->timeout /= 2; diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index c583193ae929..9c159f74c6d0 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -39,6 +39,11 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev, struct scsi_target *starget); void sas_ata_task_abort(struct sas_task *task); +void sas_ata_strategy_handler(struct Scsi_Host *shost); +int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task, + enum blk_eh_timer_return *rtn); +int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, + struct list_head *done_q); #else @@ -55,6 +60,23 @@ static inline int sas_ata_init_host_and_port(struct domain_device *found_dev, static inline void sas_ata_task_abort(struct sas_task *task) { } + +static inline void sas_ata_strategy_handler(struct Scsi_Host *shost) +{ +} + +static inline int sas_ata_timed_out(struct scsi_cmnd *cmd, + struct sas_task *task, + enum blk_eh_timer_return *rtn) +{ + return 0; +} +static inline int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, + struct list_head *done_q) +{ + return 0; +} + #endif #endif /* _SAS_ATA_H_ */ -- cgit v1.2.3 From c10f97b9d8df818e51e6073be1b96454630595c1 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 4 Feb 2011 22:03:34 +0300 Subject: libata: remove ATA_FLAG_{SRST|SATA_RESET} These flags are marked as obsolete and the checks for them have been removed by commit 294440887b32c58d220fb54b73b7a58079b78f20 (libata-sff: kill unused ata_bus_reset()), so I think it's time to finally get rid of them... Signed-off-by: Sergei Shtylyov Signed-off-by: Jeff Garzik --- drivers/ata/pata_acpi.c | 2 +- drivers/ata/pata_sis.c | 2 +- drivers/ata/sata_sx4.c | 4 ++-- drivers/scsi/ipr.c | 4 ++-- drivers/scsi/libsas/sas_ata.c | 4 ++-- include/linux/libata.h | 2 -- 6 files changed, 8 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/ata/pata_acpi.c b/drivers/ata/pata_acpi.c index c8d47034d5e9..91949d997555 100644 --- a/drivers/ata/pata_acpi.c +++ b/drivers/ata/pata_acpi.c @@ -245,7 +245,7 @@ static struct ata_port_operations pacpi_ops = { static int pacpi_init_one (struct pci_dev *pdev, const struct pci_device_id *id) { static const struct ata_port_info info = { - .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c index 60cea13cccce..c04abc393fc5 100644 --- a/drivers/ata/pata_sis.c +++ b/drivers/ata/pata_sis.c @@ -593,7 +593,7 @@ static const struct ata_port_info sis_info133 = { .port_ops = &sis_133_ops, }; const struct ata_port_info sis_info133_for_sata = { - .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, /* No MWDMA */ .udma_mask = ATA_UDMA6, diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c index bedd5188e5b0..76384d077b2d 100644 --- a/drivers/ata/sata_sx4.c +++ b/drivers/ata/sata_sx4.c @@ -274,8 +274,8 @@ static const struct ata_port_info pdc_port_info[] = { /* board_20621 */ { .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_SRST | ATA_FLAG_MMIO | - ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING, + ATA_FLAG_MMIO | ATA_FLAG_NO_ATAPI | + ATA_FLAG_PIO_POLLING, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 081d14a326fe..6443ce7c41ae 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -6219,8 +6219,8 @@ static struct ata_port_operations ipr_sata_ops = { }; static struct ata_port_info sata_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SATA_RESET | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA, + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO | + ATA_FLAG_PIO_DMA, .pio_mask = ATA_PIO4_ONLY, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index fc6bdc51a047..996dbda47141 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -372,8 +372,8 @@ static struct ata_port_operations sas_sata_ops = { }; static struct ata_port_info sata_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SATA_RESET | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ, + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO | + ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/include/linux/libata.h b/include/linux/libata.h index 9739317c707a..51ec439f75ad 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -181,8 +181,6 @@ enum { ATA_FLAG_SATA = (1 << 1), ATA_FLAG_NO_LEGACY = (1 << 2), /* no legacy mode check */ ATA_FLAG_MMIO = (1 << 3), /* use MMIO, not PIO */ - ATA_FLAG_SRST = (1 << 4), /* (obsolete) use ATA SRST, not E.D.D. */ - ATA_FLAG_SATA_RESET = (1 << 5), /* (obsolete) use COMRESET */ ATA_FLAG_NO_ATAPI = (1 << 6), /* No ATAPI support */ ATA_FLAG_PIO_DMA = (1 << 7), /* PIO cmds via DMA */ ATA_FLAG_PIO_LBA48 = (1 << 8), /* Host DMA engine is LBA28 only */ -- cgit v1.2.3 From 3696df309971b3427cb9cb039138a1732a865a0b Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 4 Feb 2011 22:04:17 +0300 Subject: libata: remove ATA_FLAG_MMIO Commit 0d5ff566779f894ca9937231a181eb31e4adff0e (libata: convert to iomap) removed all checks of ATA_FLAG_MMIO but neglected to remove the flag itself. Do it now, at last... Signed-off-by: Sergei Shtylyov Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 2 +- drivers/ata/ahci.h | 2 +- drivers/ata/libata-acpi.c | 3 +-- drivers/ata/pata_at32.c | 2 +- drivers/ata/pata_bf54x.c | 1 - drivers/ata/pata_ixp4xx_cf.c | 2 +- drivers/ata/pata_macio.c | 3 +-- drivers/ata/pata_octeon_cf.c | 4 ++-- drivers/ata/pata_palmld.c | 2 +- drivers/ata/pata_pdc2027x.c | 6 ++---- drivers/ata/pata_pxa.c | 1 - drivers/ata/pata_rb532_cf.c | 2 +- drivers/ata/pata_samsung_cf.c | 1 - drivers/ata/pata_scc.c | 2 +- drivers/ata/pdc_adma.c | 3 +-- drivers/ata/sata_dwc_460ex.c | 2 +- drivers/ata/sata_fsl.c | 4 ++-- drivers/ata/sata_mv.c | 2 +- drivers/ata/sata_nv.c | 2 +- drivers/ata/sata_promise.c | 1 - drivers/ata/sata_qstor.c | 2 +- drivers/ata/sata_sil.c | 3 +-- drivers/ata/sata_sil24.c | 6 +++--- drivers/ata/sata_svw.c | 10 ++++------ drivers/ata/sata_sx4.c | 3 +-- drivers/ata/sata_vsc.c | 3 +-- drivers/scsi/ipr.c | 3 +-- drivers/scsi/libsas/sas_ata.c | 4 ++-- include/linux/libata.h | 1 - 29 files changed, 33 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index b8d96ce37fc9..3a0435181f77 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -176,7 +176,7 @@ static const struct ata_port_info ahci_port_info[] = { AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_MSI | AHCI_HFLAG_MV_PATA | AHCI_HFLAG_NO_PMP), .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA, + ATA_FLAG_PIO_DMA, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 3e606c34f57b..9a323417907e 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -214,7 +214,7 @@ enum { /* ap->flags bits */ AHCI_FLAG_COMMON = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | + ATA_FLAG_PIO_DMA | ATA_FLAG_ACPI_SATA | ATA_FLAG_AN | ATA_FLAG_LPM, diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index 8b5ea399a4f4..a791b8ce6294 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -660,8 +660,7 @@ static int ata_acpi_filter_tf(struct ata_device *dev, * @dev: target ATA device * @gtf: raw ATA taskfile register set (0x1f1 - 0x1f7) * - * Outputs ATA taskfile to standard ATA host controller using MMIO - * or PIO as indicated by the ATA_FLAG_MMIO flag. + * Outputs ATA taskfile to standard ATA host controller. * Writes the control, feature, nsect, lbal, lbam, and lbah registers. * Optionally (ATA_TFLAG_LBA48) writes hob_feature, hob_nsect, * hob_lbal, hob_lbam, and hob_lbah. diff --git a/drivers/ata/pata_at32.c b/drivers/ata/pata_at32.c index 66ce6a526f27..36f189c7ee8c 100644 --- a/drivers/ata/pata_at32.c +++ b/drivers/ata/pata_at32.c @@ -194,7 +194,7 @@ static int __init pata_at32_init_one(struct device *dev, /* Setup ATA bindings */ ap->ops = &at32_port_ops; ap->pio_mask = PIO_MASK; - ap->flags |= ATA_FLAG_MMIO | ATA_FLAG_SLAVE_POSS; + ap->flags |= ATA_FLAG_SLAVE_POSS; /* * Since all 8-bit taskfile transfers has to go on the lower diff --git a/drivers/ata/pata_bf54x.c b/drivers/ata/pata_bf54x.c index 7aed5c792597..1086cacf3cf5 100644 --- a/drivers/ata/pata_bf54x.c +++ b/drivers/ata/pata_bf54x.c @@ -1455,7 +1455,6 @@ static struct ata_port_operations bfin_pata_ops = { static struct ata_port_info bfin_port_info[] = { { .flags = ATA_FLAG_SLAVE_POSS - | ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY, .pio_mask = ATA_PIO4, .mwdma_mask = 0, diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c index ba54b089f98c..783ec4fc1119 100644 --- a/drivers/ata/pata_ixp4xx_cf.c +++ b/drivers/ata/pata_ixp4xx_cf.c @@ -177,7 +177,7 @@ static __devinit int ixp4xx_pata_probe(struct platform_device *pdev) ap->ops = &ixp4xx_port_ops; ap->pio_mask = ATA_PIO4; - ap->flags |= ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY | ATA_FLAG_NO_ATAPI; + ap->flags |= ATA_FLAG_NO_LEGACY | ATA_FLAG_NO_ATAPI; ixp4xx_setup_port(ap, data, cs0->start, cs1->start); diff --git a/drivers/ata/pata_macio.c b/drivers/ata/pata_macio.c index 75b49d01780b..7f7b883c008e 100644 --- a/drivers/ata/pata_macio.c +++ b/drivers/ata/pata_macio.c @@ -1053,8 +1053,7 @@ static int __devinit pata_macio_common_init(struct pata_macio_priv *priv, /* Allocate libata host for 1 port */ memset(&pinfo, 0, sizeof(struct ata_port_info)); pmac_macio_calc_timing_masks(priv, &pinfo); - pinfo.flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_MMIO | - ATA_FLAG_NO_LEGACY; + pinfo.flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_LEGACY; pinfo.port_ops = &pata_macio_ops; pinfo.private_data = priv; diff --git a/drivers/ata/pata_octeon_cf.c b/drivers/ata/pata_octeon_cf.c index fa1b95a9a7ff..18703e2e51c6 100644 --- a/drivers/ata/pata_octeon_cf.c +++ b/drivers/ata/pata_octeon_cf.c @@ -848,8 +848,8 @@ static int __devinit octeon_cf_probe(struct platform_device *pdev) cf_port->ap = ap; ap->ops = &octeon_cf_ops; ap->pio_mask = ATA_PIO6; - ap->flags |= ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY - | ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING; + ap->flags |= ATA_FLAG_NO_LEGACY | ATA_FLAG_NO_ATAPI + | ATA_FLAG_PIO_POLLING; base = cs0 + ocd->base_region_bias; if (!ocd->is16bit) { diff --git a/drivers/ata/pata_palmld.c b/drivers/ata/pata_palmld.c index 11fb4ccc74b4..462a5fcebab9 100644 --- a/drivers/ata/pata_palmld.c +++ b/drivers/ata/pata_palmld.c @@ -85,7 +85,7 @@ static __devinit int palmld_pata_probe(struct platform_device *pdev) ap = host->ports[0]; ap->ops = &palmld_port_ops; ap->pio_mask = ATA_PIO4; - ap->flags |= ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY | ATA_FLAG_PIO_POLLING; + ap->flags |= ATA_FLAG_NO_LEGACY | ATA_FLAG_PIO_POLLING; /* memory mapping voodoo */ ap->ioaddr.cmd_addr = mem + 0x10; diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c index b18351122525..57ef92ce1a5c 100644 --- a/drivers/ata/pata_pdc2027x.c +++ b/drivers/ata/pata_pdc2027x.c @@ -150,8 +150,7 @@ static struct ata_port_operations pdc2027x_pata133_ops = { static struct ata_port_info pdc2027x_port_info[] = { /* PDC_UDMA_100 */ { - .flags = ATA_FLAG_NO_LEGACY | ATA_FLAG_SLAVE_POSS | - ATA_FLAG_MMIO, + .flags = ATA_FLAG_NO_LEGACY | ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA5, @@ -159,8 +158,7 @@ static struct ata_port_info pdc2027x_port_info[] = { }, /* PDC_UDMA_133 */ { - .flags = ATA_FLAG_NO_LEGACY | ATA_FLAG_SLAVE_POSS | - ATA_FLAG_MMIO, + .flags = ATA_FLAG_NO_LEGACY | ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c index 1898c6ed4b4e..b4ede40f8ae1 100644 --- a/drivers/ata/pata_pxa.c +++ b/drivers/ata/pata_pxa.c @@ -292,7 +292,6 @@ static int __devinit pxa_ata_probe(struct platform_device *pdev) ap->ops = &pxa_ata_port_ops; ap->pio_mask = ATA_PIO4; ap->mwdma_mask = ATA_MWDMA2; - ap->flags = ATA_FLAG_MMIO; ap->ioaddr.cmd_addr = devm_ioremap(&pdev->dev, cmd_res->start, resource_size(cmd_res)); diff --git a/drivers/ata/pata_rb532_cf.c b/drivers/ata/pata_rb532_cf.c index 0ffd631000b7..0365a5d714f6 100644 --- a/drivers/ata/pata_rb532_cf.c +++ b/drivers/ata/pata_rb532_cf.c @@ -91,7 +91,7 @@ static void rb532_pata_setup_ports(struct ata_host *ah) ap->ops = &rb532_pata_port_ops; ap->pio_mask = ATA_PIO4; - ap->flags = ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO; + ap->flags = ATA_FLAG_NO_LEGACY; ap->ioaddr.cmd_addr = info->iobase + RB500_CF_REG_BASE; ap->ioaddr.ctl_addr = info->iobase + RB500_CF_REG_CTRL; diff --git a/drivers/ata/pata_samsung_cf.c b/drivers/ata/pata_samsung_cf.c index 8a51d673e5b2..c446ae6055a3 100644 --- a/drivers/ata/pata_samsung_cf.c +++ b/drivers/ata/pata_samsung_cf.c @@ -531,7 +531,6 @@ static int __init pata_s3c_probe(struct platform_device *pdev) } ap = host->ports[0]; - ap->flags |= ATA_FLAG_MMIO; ap->pio_mask = ATA_PIO4; if (cpu_type == TYPE_S3C64XX) { diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c index 093715c3273a..5eb050086f38 100644 --- a/drivers/ata/pata_scc.c +++ b/drivers/ata/pata_scc.c @@ -959,7 +959,7 @@ static struct ata_port_operations scc_pata_ops = { static struct ata_port_info scc_port_info[] = { { - .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY, + .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_LEGACY, .pio_mask = ATA_PIO4, /* No MWDMA */ .udma_mask = ATA_UDMA6, diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c index adbe0426c8f0..384f202fbdec 100644 --- a/drivers/ata/pdc_adma.c +++ b/drivers/ata/pdc_adma.c @@ -167,8 +167,7 @@ static struct ata_port_info adma_port_info[] = { /* board_1841_idx */ { .flags = ATA_FLAG_SLAVE_POSS | - ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO | - ATA_FLAG_PIO_POLLING, + ATA_FLAG_NO_LEGACY | ATA_FLAG_PIO_POLLING, .pio_mask = ATA_PIO4_ONLY, .udma_mask = ATA_UDMA4, .port_ops = &adma_ata_ops, diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 843af13606e1..8c37b0e7fad8 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -1619,7 +1619,7 @@ static struct ata_port_operations sata_dwc_ops = { static const struct ata_port_info sata_dwc_port_info[] = { { .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_NCQ, + ATA_FLAG_NCQ, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &sata_dwc_ops, diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index b0214d00d50b..1d70cf714275 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -34,8 +34,8 @@ enum { SATA_FSL_MAX_PRD_DIRECT = 16, /* Direct PRDT entries */ SATA_FSL_HOST_FLAGS = (ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_PMP | ATA_FLAG_NCQ | ATA_FLAG_AN), + ATA_FLAG_PIO_DMA | ATA_FLAG_PMP | + ATA_FLAG_NCQ | ATA_FLAG_AN), SATA_FSL_MAX_CMDS = SATA_FSL_QUEUE_DEPTH, SATA_FSL_CMD_HDR_SIZE = 16, /* 4 DWORDS */ diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index bf74a36d3cc3..a2193fc52763 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -161,7 +161,7 @@ enum { MV_FLAG_DUAL_HC = (1 << 30), /* two SATA Host Controllers */ MV_COMMON_FLAGS = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_POLLING, + ATA_FLAG_PIO_POLLING, MV_GEN_I_FLAGS = MV_COMMON_FLAGS | ATA_FLAG_NO_ATAPI, diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 7254e255fd78..cca96e89339d 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -567,7 +567,7 @@ static const struct ata_port_info nv_port_info[] = { /* ADMA */ { .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_NCQ, + ATA_FLAG_NCQ, .pio_mask = NV_PIO_MASK, .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index f03ad48273ff..b0cba3cd9361 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c @@ -135,7 +135,6 @@ enum { PDC_RESET = (1 << 11), /* HDMA reset */ PDC_COMMON_FLAGS = ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_POLLING, /* ap->flags bits */ diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c index daeebf19a6a9..5e30a7994391 100644 --- a/drivers/ata/sata_qstor.c +++ b/drivers/ata/sata_qstor.c @@ -156,7 +156,7 @@ static const struct ata_port_info qs_port_info[] = { /* board_2068_idx */ { .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_POLLING, + ATA_FLAG_PIO_POLLING, .pio_mask = ATA_PIO4_ONLY, .udma_mask = ATA_UDMA6, .port_ops = &qs_ata_ops, diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index 3a4f84219719..0742d3d968e0 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -61,8 +61,7 @@ enum { SIL_FLAG_RERR_ON_DMA_ACT = (1 << 29), SIL_FLAG_MOD15WRITE = (1 << 30), - SIL_DFL_PORT_FLAGS = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO, + SIL_DFL_PORT_FLAGS = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, /* * Controller IDs diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index af41c6fd1254..1ad7b94f0b38 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -245,9 +245,9 @@ enum { /* host flags */ SIL24_COMMON_FLAGS = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_NCQ | ATA_FLAG_ACPI_SATA | - ATA_FLAG_AN | ATA_FLAG_PMP, + ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ | + ATA_FLAG_ACPI_SATA | ATA_FLAG_AN | + ATA_FLAG_PMP, SIL24_FLAG_PCIX_IRQ_WOC = (1 << 24), /* IRQ loss errata on PCI-X */ IRQ_STAT_4PORTS = 0xf, diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c index 7d9db4aaf07e..adc913a35189 100644 --- a/drivers/ata/sata_svw.c +++ b/drivers/ata/sata_svw.c @@ -360,7 +360,7 @@ static const struct ata_port_info k2_port_info[] = { /* chip_svw4 */ { .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | K2_FLAG_NO_ATAPI_DMA, + K2_FLAG_NO_ATAPI_DMA, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, @@ -369,8 +369,7 @@ static const struct ata_port_info k2_port_info[] = { /* chip_svw8 */ { .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | K2_FLAG_NO_ATAPI_DMA | - K2_FLAG_SATA_8_PORTS, + K2_FLAG_NO_ATAPI_DMA | K2_FLAG_SATA_8_PORTS, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, @@ -379,7 +378,7 @@ static const struct ata_port_info k2_port_info[] = { /* chip_svw42 */ { .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | K2_FLAG_BAR_POS_3, + K2_FLAG_BAR_POS_3, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, @@ -387,8 +386,7 @@ static const struct ata_port_info k2_port_info[] = { }, /* chip_svw43 */ { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO, + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c index 76384d077b2d..d58e656bef70 100644 --- a/drivers/ata/sata_sx4.c +++ b/drivers/ata/sata_sx4.c @@ -274,8 +274,7 @@ static const struct ata_port_info pdc_port_info[] = { /* board_20621 */ { .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_NO_ATAPI | - ATA_FLAG_PIO_POLLING, + ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c index e079cf29ed5d..192bdc7d8f6e 100644 --- a/drivers/ata/sata_vsc.c +++ b/drivers/ata/sata_vsc.c @@ -340,8 +340,7 @@ static int __devinit vsc_sata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static const struct ata_port_info pi = { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO, + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 6443ce7c41ae..e437c8ae9d41 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -6219,8 +6219,7 @@ static struct ata_port_operations ipr_sata_ops = { }; static struct ata_port_info sata_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO | - ATA_FLAG_PIO_DMA, + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_PIO_DMA, .pio_mask = ATA_PIO4_ONLY, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 996dbda47141..7538b6ffb3fe 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -372,8 +372,8 @@ static struct ata_port_operations sas_sata_ops = { }; static struct ata_port_info sata_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO | - ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ, + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_PIO_DMA | + ATA_FLAG_NCQ, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/include/linux/libata.h b/include/linux/libata.h index 51ec439f75ad..0c3d9e144891 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -180,7 +180,6 @@ enum { /* (doesn't imply presence) */ ATA_FLAG_SATA = (1 << 1), ATA_FLAG_NO_LEGACY = (1 << 2), /* no legacy mode check */ - ATA_FLAG_MMIO = (1 << 3), /* use MMIO, not PIO */ ATA_FLAG_NO_ATAPI = (1 << 6), /* No ATAPI support */ ATA_FLAG_PIO_DMA = (1 << 7), /* PIO cmds via DMA */ ATA_FLAG_PIO_LBA48 = (1 << 8), /* Host DMA engine is LBA28 only */ -- cgit v1.2.3 From 9cbe056f6c467e7395d5aec39aceec47812eb98e Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 4 Feb 2011 22:05:48 +0300 Subject: libata: remove ATA_FLAG_NO_LEGACY All checks of ATA_FLAG_NO_LEGACY have been removed by the commits c791c30670ea61f19eec390124128bf278e854fe ([libata] minor PCI IDE probe fixes and cleanups) and f0d36efdc624beb3d9e29b9ab9e9537bf0f25d5b (libata: update libata core layer to use devres), so I think it's time to finally get rid of this flag... Signed-off-by: Sergei Shtylyov Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 3 +-- drivers/ata/ahci.h | 3 +-- drivers/ata/pata_bf54x.c | 3 +-- drivers/ata/pata_ixp4xx_cf.c | 2 +- drivers/ata/pata_macio.c | 2 +- drivers/ata/pata_octeon_cf.c | 3 +-- drivers/ata/pata_palmld.c | 2 +- drivers/ata/pata_pdc2027x.c | 4 ++-- drivers/ata/pata_rb532_cf.c | 1 - drivers/ata/pata_scc.c | 2 +- drivers/ata/pdc_adma.c | 3 +-- drivers/ata/sata_dwc_460ex.c | 3 +-- drivers/ata/sata_fsl.c | 5 ++--- drivers/ata/sata_mv.c | 3 +-- drivers/ata/sata_nv.c | 14 ++++++-------- drivers/ata/sata_promise.c | 3 +-- drivers/ata/sata_qstor.c | 3 +-- drivers/ata/sata_sil.c | 2 +- drivers/ata/sata_sil24.c | 7 +++---- drivers/ata/sata_sis.c | 2 +- drivers/ata/sata_svw.c | 12 +++++------- drivers/ata/sata_sx4.c | 4 ++-- drivers/ata/sata_uli.c | 3 +-- drivers/ata/sata_via.c | 9 ++++----- drivers/ata/sata_vsc.c | 2 +- drivers/scsi/ipr.c | 2 +- drivers/scsi/libsas/sas_ata.c | 3 +-- include/linux/libata.h | 1 - 28 files changed, 43 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 3a0435181f77..6856d877a69b 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -175,8 +175,7 @@ static const struct ata_port_info ahci_port_info[] = { { AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_MSI | AHCI_HFLAG_MV_PATA | AHCI_HFLAG_NO_PMP), - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_PIO_DMA, + .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 9a323417907e..fd75844fb009 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -213,8 +213,7 @@ enum { /* ap->flags bits */ - AHCI_FLAG_COMMON = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_PIO_DMA | + AHCI_FLAG_COMMON = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_ACPI_SATA | ATA_FLAG_AN | ATA_FLAG_LPM, diff --git a/drivers/ata/pata_bf54x.c b/drivers/ata/pata_bf54x.c index 1086cacf3cf5..e0b58b8dfe6f 100644 --- a/drivers/ata/pata_bf54x.c +++ b/drivers/ata/pata_bf54x.c @@ -1454,8 +1454,7 @@ static struct ata_port_operations bfin_pata_ops = { static struct ata_port_info bfin_port_info[] = { { - .flags = ATA_FLAG_SLAVE_POSS - | ATA_FLAG_NO_LEGACY, + .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, .mwdma_mask = 0, .udma_mask = 0, diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c index 783ec4fc1119..5253b271b3fe 100644 --- a/drivers/ata/pata_ixp4xx_cf.c +++ b/drivers/ata/pata_ixp4xx_cf.c @@ -177,7 +177,7 @@ static __devinit int ixp4xx_pata_probe(struct platform_device *pdev) ap->ops = &ixp4xx_port_ops; ap->pio_mask = ATA_PIO4; - ap->flags |= ATA_FLAG_NO_LEGACY | ATA_FLAG_NO_ATAPI; + ap->flags |= ATA_FLAG_NO_ATAPI; ixp4xx_setup_port(ap, data, cs0->start, cs1->start); diff --git a/drivers/ata/pata_macio.c b/drivers/ata/pata_macio.c index 7f7b883c008e..46f589edccdb 100644 --- a/drivers/ata/pata_macio.c +++ b/drivers/ata/pata_macio.c @@ -1053,7 +1053,7 @@ static int __devinit pata_macio_common_init(struct pata_macio_priv *priv, /* Allocate libata host for 1 port */ memset(&pinfo, 0, sizeof(struct ata_port_info)); pmac_macio_calc_timing_masks(priv, &pinfo); - pinfo.flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_LEGACY; + pinfo.flags = ATA_FLAG_SLAVE_POSS; pinfo.port_ops = &pata_macio_ops; pinfo.private_data = priv; diff --git a/drivers/ata/pata_octeon_cf.c b/drivers/ata/pata_octeon_cf.c index 18703e2e51c6..220ddc90608f 100644 --- a/drivers/ata/pata_octeon_cf.c +++ b/drivers/ata/pata_octeon_cf.c @@ -848,8 +848,7 @@ static int __devinit octeon_cf_probe(struct platform_device *pdev) cf_port->ap = ap; ap->ops = &octeon_cf_ops; ap->pio_mask = ATA_PIO6; - ap->flags |= ATA_FLAG_NO_LEGACY | ATA_FLAG_NO_ATAPI - | ATA_FLAG_PIO_POLLING; + ap->flags |= ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING; base = cs0 + ocd->base_region_bias; if (!ocd->is16bit) { diff --git a/drivers/ata/pata_palmld.c b/drivers/ata/pata_palmld.c index 462a5fcebab9..a2a73d953840 100644 --- a/drivers/ata/pata_palmld.c +++ b/drivers/ata/pata_palmld.c @@ -85,7 +85,7 @@ static __devinit int palmld_pata_probe(struct platform_device *pdev) ap = host->ports[0]; ap->ops = &palmld_port_ops; ap->pio_mask = ATA_PIO4; - ap->flags |= ATA_FLAG_NO_LEGACY | ATA_FLAG_PIO_POLLING; + ap->flags |= ATA_FLAG_PIO_POLLING; /* memory mapping voodoo */ ap->ioaddr.cmd_addr = mem + 0x10; diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c index 57ef92ce1a5c..9765ace16921 100644 --- a/drivers/ata/pata_pdc2027x.c +++ b/drivers/ata/pata_pdc2027x.c @@ -150,7 +150,7 @@ static struct ata_port_operations pdc2027x_pata133_ops = { static struct ata_port_info pdc2027x_port_info[] = { /* PDC_UDMA_100 */ { - .flags = ATA_FLAG_NO_LEGACY | ATA_FLAG_SLAVE_POSS, + .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA5, @@ -158,7 +158,7 @@ static struct ata_port_info pdc2027x_port_info[] = { }, /* PDC_UDMA_133 */ { - .flags = ATA_FLAG_NO_LEGACY | ATA_FLAG_SLAVE_POSS, + .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/ata/pata_rb532_cf.c b/drivers/ata/pata_rb532_cf.c index 0365a5d714f6..baeaf938d55b 100644 --- a/drivers/ata/pata_rb532_cf.c +++ b/drivers/ata/pata_rb532_cf.c @@ -91,7 +91,6 @@ static void rb532_pata_setup_ports(struct ata_host *ah) ap->ops = &rb532_pata_port_ops; ap->pio_mask = ATA_PIO4; - ap->flags = ATA_FLAG_NO_LEGACY; ap->ioaddr.cmd_addr = info->iobase + RB500_CF_REG_BASE; ap->ioaddr.ctl_addr = info->iobase + RB500_CF_REG_CTRL; diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c index 5eb050086f38..88ea9b677b47 100644 --- a/drivers/ata/pata_scc.c +++ b/drivers/ata/pata_scc.c @@ -959,7 +959,7 @@ static struct ata_port_operations scc_pata_ops = { static struct ata_port_info scc_port_info[] = { { - .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_LEGACY, + .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, /* No MWDMA */ .udma_mask = ATA_UDMA6, diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c index 384f202fbdec..1111712b3d7d 100644 --- a/drivers/ata/pdc_adma.c +++ b/drivers/ata/pdc_adma.c @@ -166,8 +166,7 @@ static struct ata_port_operations adma_ata_ops = { static struct ata_port_info adma_port_info[] = { /* board_1841_idx */ { - .flags = ATA_FLAG_SLAVE_POSS | - ATA_FLAG_NO_LEGACY | ATA_FLAG_PIO_POLLING, + .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_POLLING, .pio_mask = ATA_PIO4_ONLY, .udma_mask = ATA_UDMA4, .port_ops = &adma_ata_ops, diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 8c37b0e7fad8..712ab5a4922e 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -1618,8 +1618,7 @@ static struct ata_port_operations sata_dwc_ops = { static const struct ata_port_info sata_dwc_port_info[] = { { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_NCQ, + .flags = ATA_FLAG_SATA | ATA_FLAG_NCQ, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &sata_dwc_ops, diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 1d70cf714275..beef37134a04 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -33,9 +33,8 @@ enum { SATA_FSL_MAX_PRD_USABLE = SATA_FSL_MAX_PRD - 1, SATA_FSL_MAX_PRD_DIRECT = 16, /* Direct PRDT entries */ - SATA_FSL_HOST_FLAGS = (ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_PIO_DMA | ATA_FLAG_PMP | - ATA_FLAG_NCQ | ATA_FLAG_AN), + SATA_FSL_HOST_FLAGS = (ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | + ATA_FLAG_PMP | ATA_FLAG_NCQ | ATA_FLAG_AN), SATA_FSL_MAX_CMDS = SATA_FSL_QUEUE_DEPTH, SATA_FSL_CMD_HDR_SIZE = 16, /* 4 DWORDS */ diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index a2193fc52763..cd40651e9b72 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -160,8 +160,7 @@ enum { /* Host Flags */ MV_FLAG_DUAL_HC = (1 << 30), /* two SATA Host Controllers */ - MV_COMMON_FLAGS = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_PIO_POLLING, + MV_COMMON_FLAGS = ATA_FLAG_SATA | ATA_FLAG_PIO_POLLING, MV_GEN_I_FLAGS = MV_COMMON_FLAGS | ATA_FLAG_NO_ATAPI, diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index cca96e89339d..42344e3c686d 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -539,7 +539,7 @@ struct nv_pi_priv { static const struct ata_port_info nv_port_info[] = { /* generic */ { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, + .flags = ATA_FLAG_SATA, .pio_mask = NV_PIO_MASK, .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, @@ -548,7 +548,7 @@ static const struct ata_port_info nv_port_info[] = { }, /* nforce2/3 */ { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, + .flags = ATA_FLAG_SATA, .pio_mask = NV_PIO_MASK, .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, @@ -557,7 +557,7 @@ static const struct ata_port_info nv_port_info[] = { }, /* ck804 */ { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, + .flags = ATA_FLAG_SATA, .pio_mask = NV_PIO_MASK, .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, @@ -566,8 +566,7 @@ static const struct ata_port_info nv_port_info[] = { }, /* ADMA */ { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_NCQ, + .flags = ATA_FLAG_SATA | ATA_FLAG_NCQ, .pio_mask = NV_PIO_MASK, .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, @@ -576,7 +575,7 @@ static const struct ata_port_info nv_port_info[] = { }, /* MCP5x */ { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, + .flags = ATA_FLAG_SATA, .pio_mask = NV_PIO_MASK, .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, @@ -585,8 +584,7 @@ static const struct ata_port_info nv_port_info[] = { }, /* SWNCQ */ { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_NCQ, + .flags = ATA_FLAG_SATA | ATA_FLAG_NCQ, .pio_mask = NV_PIO_MASK, .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index b0cba3cd9361..a004b1e0ea6d 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c @@ -134,8 +134,7 @@ enum { PDC_IRQ_DISABLE = (1 << 10), PDC_RESET = (1 << 11), /* HDMA reset */ - PDC_COMMON_FLAGS = ATA_FLAG_NO_LEGACY | - ATA_FLAG_PIO_POLLING, + PDC_COMMON_FLAGS = ATA_FLAG_PIO_POLLING, /* ap->flags bits */ PDC_FLAG_GEN_II = (1 << 24), diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c index 5e30a7994391..c5603265fa58 100644 --- a/drivers/ata/sata_qstor.c +++ b/drivers/ata/sata_qstor.c @@ -155,8 +155,7 @@ static struct ata_port_operations qs_ata_ops = { static const struct ata_port_info qs_port_info[] = { /* board_2068_idx */ { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_PIO_POLLING, + .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_POLLING, .pio_mask = ATA_PIO4_ONLY, .udma_mask = ATA_UDMA6, .port_ops = &qs_ata_ops, diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index 0742d3d968e0..b42edaaf3a53 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -61,7 +61,7 @@ enum { SIL_FLAG_RERR_ON_DMA_ACT = (1 << 29), SIL_FLAG_MOD15WRITE = (1 << 30), - SIL_DFL_PORT_FLAGS = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, + SIL_DFL_PORT_FLAGS = ATA_FLAG_SATA, /* * Controller IDs diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index 1ad7b94f0b38..06c564e55051 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -244,10 +244,9 @@ enum { BID_SIL3131 = 2, /* host flags */ - SIL24_COMMON_FLAGS = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ | - ATA_FLAG_ACPI_SATA | ATA_FLAG_AN | - ATA_FLAG_PMP, + SIL24_COMMON_FLAGS = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | + ATA_FLAG_NCQ | ATA_FLAG_ACPI_SATA | + ATA_FLAG_AN | ATA_FLAG_PMP, SIL24_FLAG_PCIX_IRQ_WOC = (1 << 24), /* IRQ loss errata on PCI-X */ IRQ_STAT_4PORTS = 0xf, diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c index 2bfe3ae03976..cdcc13e9cf51 100644 --- a/drivers/ata/sata_sis.c +++ b/drivers/ata/sata_sis.c @@ -96,7 +96,7 @@ static struct ata_port_operations sis_ops = { }; static const struct ata_port_info sis_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, + .flags = ATA_FLAG_SATA, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c index adc913a35189..35eabcf34568 100644 --- a/drivers/ata/sata_svw.c +++ b/drivers/ata/sata_svw.c @@ -359,8 +359,7 @@ static struct ata_port_operations k2_sata_ops = { static const struct ata_port_info k2_port_info[] = { /* chip_svw4 */ { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - K2_FLAG_NO_ATAPI_DMA, + .flags = ATA_FLAG_SATA | K2_FLAG_NO_ATAPI_DMA, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, @@ -368,8 +367,8 @@ static const struct ata_port_info k2_port_info[] = { }, /* chip_svw8 */ { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - K2_FLAG_NO_ATAPI_DMA | K2_FLAG_SATA_8_PORTS, + .flags = ATA_FLAG_SATA | K2_FLAG_NO_ATAPI_DMA | + K2_FLAG_SATA_8_PORTS, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, @@ -377,8 +376,7 @@ static const struct ata_port_info k2_port_info[] = { }, /* chip_svw42 */ { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - K2_FLAG_BAR_POS_3, + .flags = ATA_FLAG_SATA | K2_FLAG_BAR_POS_3, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, @@ -386,7 +384,7 @@ static const struct ata_port_info k2_port_info[] = { }, /* chip_svw43 */ { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, + .flags = ATA_FLAG_SATA, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c index d58e656bef70..8fd3b7252bda 100644 --- a/drivers/ata/sata_sx4.c +++ b/drivers/ata/sata_sx4.c @@ -273,8 +273,8 @@ static struct ata_port_operations pdc_20621_ops = { static const struct ata_port_info pdc_port_info[] = { /* board_20621 */ { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING, + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_ATAPI | + ATA_FLAG_PIO_POLLING, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c index b8578c32d344..235be717a713 100644 --- a/drivers/ata/sata_uli.c +++ b/drivers/ata/sata_uli.c @@ -88,8 +88,7 @@ static struct ata_port_operations uli_ops = { }; static const struct ata_port_info uli_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_IGN_SIMPLEX, + .flags = ATA_FLAG_SATA | ATA_FLAG_IGN_SIMPLEX, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &uli_ops, diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c index 8b677bbf2d37..21242c5709a0 100644 --- a/drivers/ata/sata_via.c +++ b/drivers/ata/sata_via.c @@ -148,7 +148,7 @@ static struct ata_port_operations vt8251_ops = { }; static const struct ata_port_info vt6420_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, + .flags = ATA_FLAG_SATA, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, @@ -156,7 +156,7 @@ static const struct ata_port_info vt6420_port_info = { }; static struct ata_port_info vt6421_sport_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, + .flags = ATA_FLAG_SATA, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, @@ -164,7 +164,7 @@ static struct ata_port_info vt6421_sport_info = { }; static struct ata_port_info vt6421_pport_info = { - .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_LEGACY, + .flags = ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, /* No MWDMA */ .udma_mask = ATA_UDMA6, @@ -172,8 +172,7 @@ static struct ata_port_info vt6421_pport_info = { }; static struct ata_port_info vt8251_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_SLAVE_POSS | - ATA_FLAG_NO_LEGACY, + .flags = ATA_FLAG_SATA | ATA_FLAG_SLAVE_POSS, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c index 192bdc7d8f6e..7c987371136e 100644 --- a/drivers/ata/sata_vsc.c +++ b/drivers/ata/sata_vsc.c @@ -340,7 +340,7 @@ static int __devinit vsc_sata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static const struct ata_port_info pi = { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, + .flags = ATA_FLAG_SATA, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index e437c8ae9d41..d841e98a8bd5 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -6219,7 +6219,7 @@ static struct ata_port_operations ipr_sata_ops = { }; static struct ata_port_info sata_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_PIO_DMA, + .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA, .pio_mask = ATA_PIO4_ONLY, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 7538b6ffb3fe..4d3b704ede1c 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -372,8 +372,7 @@ static struct ata_port_operations sas_sata_ops = { }; static struct ata_port_info sata_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_PIO_DMA | - ATA_FLAG_NCQ, + .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/include/linux/libata.h b/include/linux/libata.h index 0c3d9e144891..26d80479c75f 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -179,7 +179,6 @@ enum { ATA_FLAG_SLAVE_POSS = (1 << 0), /* host supports slave dev */ /* (doesn't imply presence) */ ATA_FLAG_SATA = (1 << 1), - ATA_FLAG_NO_LEGACY = (1 << 2), /* no legacy mode check */ ATA_FLAG_NO_ATAPI = (1 << 6), /* No ATAPI support */ ATA_FLAG_PIO_DMA = (1 << 7), /* PIO cmds via DMA */ ATA_FLAG_PIO_LBA48 = (1 << 8), /* Host DMA engine is LBA28 only */ -- cgit v1.2.3 From 1a0f6b7ecdcd810f8991ea26c95d93ff965e8f41 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 4 Feb 2011 22:08:22 +0300 Subject: libata: remove ATA_FLAG_LPM Commit 6b7ae9545ad9875a289f4191c0216b473e313cb9 (libata: reimplement link power management) removed the check of ATA_FLAG_LPM but neglected to remove the flag itself. Do it now... Signed-off-by: Sergei Shtylyov Signed-off-by: Jeff Garzik --- drivers/ata/ahci.h | 3 +-- include/linux/libata.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index fd75844fb009..ccaf08122058 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -214,8 +214,7 @@ enum { /* ap->flags bits */ AHCI_FLAG_COMMON = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | - ATA_FLAG_ACPI_SATA | ATA_FLAG_AN | - ATA_FLAG_LPM, + ATA_FLAG_ACPI_SATA | ATA_FLAG_AN, ICH_MAP = 0x90, /* ICH MAP register */ diff --git a/include/linux/libata.h b/include/linux/libata.h index 26d80479c75f..71333aa39532 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -194,7 +194,6 @@ enum { ATA_FLAG_ACPI_SATA = (1 << 17), /* need native SATA ACPI layout */ ATA_FLAG_AN = (1 << 18), /* controller supports AN */ ATA_FLAG_PMP = (1 << 19), /* controller supports PMP */ - ATA_FLAG_LPM = (1 << 20), /* driver can handle LPM */ ATA_FLAG_EM = (1 << 21), /* driver supports enclosure * management */ ATA_FLAG_SW_ACTIVITY = (1 << 22), /* driver supports sw activity -- cgit v1.2.3 From 77bd70e9009eab6dbdef3ee08afe87ab26df8dac Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 4 Feb 2011 14:57:43 +0000 Subject: mfd: Don't suspend WM8994 if the CODEC is not suspended ASoC supports keeping the audio subsysetm active over suspend in order to support use cases such as audio passthrough from a cellular modem with the main CPU suspended. Ensure that we don't power down the CODEC when this is happening by checking to see if VMID is up and skipping suspend and resume when it is. If the CODEC has suspended then it'll turn VMID off before the core suspend() gets called. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-core.c | 18 ++++++++++++++++++ include/linux/mfd/wm8994/core.h | 1 + 2 files changed, 19 insertions(+) (limited to 'include') diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 41233c7fa581..f4016a075fd6 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -246,6 +246,16 @@ static int wm8994_suspend(struct device *dev) struct wm8994 *wm8994 = dev_get_drvdata(dev); int ret; + /* Don't actually go through with the suspend if the CODEC is + * still active (eg, for audio passthrough from CP. */ + ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_1); + if (ret < 0) { + dev_err(dev, "Failed to read power status: %d\n", ret); + } else if (ret & WM8994_VMID_SEL_MASK) { + dev_dbg(dev, "CODEC still active, ignoring suspend\n"); + return 0; + } + /* GPIO configuration state is saved here since we may be configuring * the GPIO alternate functions even if we're not using the gpiolib * driver for them. @@ -261,6 +271,8 @@ static int wm8994_suspend(struct device *dev) if (ret < 0) dev_err(dev, "Failed to save LDO registers: %d\n", ret); + wm8994->suspended = true; + ret = regulator_bulk_disable(wm8994->num_supplies, wm8994->supplies); if (ret != 0) { @@ -276,6 +288,10 @@ static int wm8994_resume(struct device *dev) struct wm8994 *wm8994 = dev_get_drvdata(dev); int ret; + /* We may have lied to the PM core about suspending */ + if (!wm8994->suspended) + return 0; + ret = regulator_bulk_enable(wm8994->num_supplies, wm8994->supplies); if (ret != 0) { @@ -298,6 +314,8 @@ static int wm8994_resume(struct device *dev) if (ret < 0) dev_err(dev, "Failed to restore GPIO registers: %d\n", ret); + wm8994->suspended = false; + return 0; } #endif diff --git a/include/linux/mfd/wm8994/core.h b/include/linux/mfd/wm8994/core.h index 3fd36845ca45..ef4f0b6083a3 100644 --- a/include/linux/mfd/wm8994/core.h +++ b/include/linux/mfd/wm8994/core.h @@ -71,6 +71,7 @@ struct wm8994 { u16 irq_masks_cache[WM8994_NUM_IRQ_REGS]; /* Used over suspend/resume */ + bool suspended; u16 ldo_regs[WM8994_NUM_LDO_REGS]; u16 gpio_regs[WM8994_NUM_GPIO_REGS]; -- cgit v1.2.3 From 1654e7411a1ad4999fe7890ef51d2a2bbb1fcf76 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 2 Mar 2011 08:48:05 -0500 Subject: block: add @force_kblockd to __blk_run_queue() __blk_run_queue() automatically either calls q->request_fn() directly or schedules kblockd depending on whether the function is recursed. blk-flush implementation needs to be able to explicitly choose kblockd. Add @force_kblockd. All the current users are converted to specify %false for the parameter and this patch doesn't introduce any behavior change. stable: This is prerequisite for fixing ide oops caused by the new blk-flush implementation. Signed-off-by: Tejun Heo Cc: Jan Beulich Cc: James Bottomley Cc: stable@kernel.org Signed-off-by: Jens Axboe --- block/blk-core.c | 11 ++++++----- block/blk-flush.c | 2 +- block/cfq-iosched.c | 6 +++--- block/elevator.c | 4 ++-- drivers/scsi/scsi_lib.c | 2 +- drivers/scsi/scsi_transport_fc.c | 2 +- include/linux/blkdev.h | 2 +- 7 files changed, 15 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 792ece276160..518dd423a5fe 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -352,7 +352,7 @@ void blk_start_queue(struct request_queue *q) WARN_ON(!irqs_disabled()); queue_flag_clear(QUEUE_FLAG_STOPPED, q); - __blk_run_queue(q); + __blk_run_queue(q, false); } EXPORT_SYMBOL(blk_start_queue); @@ -403,13 +403,14 @@ EXPORT_SYMBOL(blk_sync_queue); /** * __blk_run_queue - run a single device queue * @q: The queue to run + * @force_kblockd: Don't run @q->request_fn directly. Use kblockd. * * Description: * See @blk_run_queue. This variant must be called with the queue lock * held and interrupts disabled. * */ -void __blk_run_queue(struct request_queue *q) +void __blk_run_queue(struct request_queue *q, bool force_kblockd) { blk_remove_plug(q); @@ -423,7 +424,7 @@ void __blk_run_queue(struct request_queue *q) * Only recurse once to avoid overrunning the stack, let the unplug * handling reinvoke the handler shortly if we already got there. */ - if (!queue_flag_test_and_set(QUEUE_FLAG_REENTER, q)) { + if (!force_kblockd && !queue_flag_test_and_set(QUEUE_FLAG_REENTER, q)) { q->request_fn(q); queue_flag_clear(QUEUE_FLAG_REENTER, q); } else { @@ -446,7 +447,7 @@ void blk_run_queue(struct request_queue *q) unsigned long flags; spin_lock_irqsave(q->queue_lock, flags); - __blk_run_queue(q); + __blk_run_queue(q, false); spin_unlock_irqrestore(q->queue_lock, flags); } EXPORT_SYMBOL(blk_run_queue); @@ -1053,7 +1054,7 @@ void blk_insert_request(struct request_queue *q, struct request *rq, drive_stat_acct(rq, 1); __elv_add_request(q, rq, where, 0); - __blk_run_queue(q); + __blk_run_queue(q, false); spin_unlock_irqrestore(q->queue_lock, flags); } EXPORT_SYMBOL(blk_insert_request); diff --git a/block/blk-flush.c b/block/blk-flush.c index 54b123d6563e..56adaa8d55cd 100644 --- a/block/blk-flush.c +++ b/block/blk-flush.c @@ -69,7 +69,7 @@ static void blk_flush_complete_seq_end_io(struct request_queue *q, * queue. Kick the queue in those cases. */ if (was_empty && next_rq) - __blk_run_queue(q); + __blk_run_queue(q, false); } static void pre_flush_end_io(struct request *rq, int error) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 7be4c7959625..ea83a4f0c27d 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -3355,7 +3355,7 @@ cfq_rq_enqueued(struct cfq_data *cfqd, struct cfq_queue *cfqq, cfqd->busy_queues > 1) { cfq_del_timer(cfqd, cfqq); cfq_clear_cfqq_wait_request(cfqq); - __blk_run_queue(cfqd->queue); + __blk_run_queue(cfqd->queue, false); } else { cfq_blkiocg_update_idle_time_stats( &cfqq->cfqg->blkg); @@ -3370,7 +3370,7 @@ cfq_rq_enqueued(struct cfq_data *cfqd, struct cfq_queue *cfqq, * this new queue is RT and the current one is BE */ cfq_preempt_queue(cfqd, cfqq); - __blk_run_queue(cfqd->queue); + __blk_run_queue(cfqd->queue, false); } } @@ -3731,7 +3731,7 @@ static void cfq_kick_queue(struct work_struct *work) struct request_queue *q = cfqd->queue; spin_lock_irq(q->queue_lock); - __blk_run_queue(cfqd->queue); + __blk_run_queue(cfqd->queue, false); spin_unlock_irq(q->queue_lock); } diff --git a/block/elevator.c b/block/elevator.c index 2569512830d3..236e93c1f46c 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -602,7 +602,7 @@ void elv_quiesce_start(struct request_queue *q) */ elv_drain_elevator(q); while (q->rq.elvpriv) { - __blk_run_queue(q); + __blk_run_queue(q, false); spin_unlock_irq(q->queue_lock); msleep(10); spin_lock_irq(q->queue_lock); @@ -651,7 +651,7 @@ void elv_insert(struct request_queue *q, struct request *rq, int where) * with anything. There's no point in delaying queue * processing. */ - __blk_run_queue(q); + __blk_run_queue(q, false); break; case ELEVATOR_INSERT_SORT: diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 9045c52abd25..fb2bb35c62cb 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -443,7 +443,7 @@ static void scsi_run_queue(struct request_queue *q) &sdev->request_queue->queue_flags); if (flagset) queue_flag_set(QUEUE_FLAG_REENTER, sdev->request_queue); - __blk_run_queue(sdev->request_queue); + __blk_run_queue(sdev->request_queue, false); if (flagset) queue_flag_clear(QUEUE_FLAG_REENTER, sdev->request_queue); spin_unlock(sdev->request_queue->queue_lock); diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 998c01be3234..5c3ccfc6b622 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -3829,7 +3829,7 @@ fc_bsg_goose_queue(struct fc_rport *rport) !test_bit(QUEUE_FLAG_REENTER, &rport->rqst_q->queue_flags); if (flagset) queue_flag_set(QUEUE_FLAG_REENTER, rport->rqst_q); - __blk_run_queue(rport->rqst_q); + __blk_run_queue(rport->rqst_q, false); if (flagset) queue_flag_clear(QUEUE_FLAG_REENTER, rport->rqst_q); spin_unlock_irqrestore(rport->rqst_q->queue_lock, flags); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index dd8cd0f47e3a..d5063e1b5555 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -699,7 +699,7 @@ extern void blk_start_queue(struct request_queue *q); extern void blk_stop_queue(struct request_queue *q); extern void blk_sync_queue(struct request_queue *q); extern void __blk_stop_queue(struct request_queue *q); -extern void __blk_run_queue(struct request_queue *); +extern void __blk_run_queue(struct request_queue *q, bool force_kblockd); extern void blk_run_queue(struct request_queue *); extern int blk_rq_map_user(struct request_queue *, struct request *, struct rq_map_data *, void __user *, unsigned long, -- cgit v1.2.3 From 452edd598f60522c11f7f88fdbab27eb36509d1a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 2 Mar 2011 13:27:41 -0800 Subject: xfrm: Return dst directly from xfrm_lookup() Instead of on the stack. Signed-off-by: David S. Miller --- include/net/dst.h | 14 ++++++++------ net/decnet/dn_route.c | 12 ++++++++++-- net/ipv4/icmp.c | 36 ++++++++++++++---------------------- net/ipv4/netfilter.c | 6 ++++-- net/ipv4/route.c | 7 ++++++- net/ipv6/icmp.c | 37 ++++++++++++++++++------------------- net/ipv6/ip6_output.c | 10 ++-------- net/ipv6/ip6_tunnel.c | 8 +++++++- net/ipv6/mcast.c | 13 ++++++++++--- net/ipv6/ndisc.c | 8 ++++---- net/ipv6/netfilter.c | 3 ++- net/ipv6/netfilter/ip6t_REJECT.c | 3 ++- net/netfilter/ipvs/ip_vs_xmit.c | 9 +++++++-- net/xfrm/xfrm_policy.c | 34 +++++++++++++++++----------------- 14 files changed, 111 insertions(+), 89 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index 8948452132ad..2a46cbaef92d 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -426,15 +426,17 @@ enum { struct flowi; #ifndef CONFIG_XFRM -static inline int xfrm_lookup(struct net *net, struct dst_entry **dst_p, - const struct flowi *fl, struct sock *sk, - int flags) +static inline struct dst_entry *xfrm_lookup(struct net *net, + struct dst_entry *dst_orig, + const struct flowi *fl, struct sock *sk, + int flags) { - return 0; + return dst_orig; } #else -extern int xfrm_lookup(struct net *net, struct dst_entry **dst_p, - const struct flowi *fl, struct sock *sk, int flags); +extern struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, + const struct flowi *fl, struct sock *sk, + int flags); #endif #endif diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 0877147d2167..484fdbf92bd8 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1222,7 +1222,11 @@ static int dn_route_output_key(struct dst_entry **pprt, struct flowi *flp, int f err = __dn_route_output_key(pprt, flp, flags); if (err == 0 && flp->proto) { - err = xfrm_lookup(&init_net, pprt, flp, NULL, 0); + *pprt = xfrm_lookup(&init_net, *pprt, flp, NULL, 0); + if (IS_ERR(*pprt)) { + err = PTR_ERR(*pprt); + *pprt = NULL; + } } return err; } @@ -1235,7 +1239,11 @@ int dn_route_output_sock(struct dst_entry **pprt, struct flowi *fl, struct sock if (err == 0 && fl->proto) { if (!(flags & MSG_DONTWAIT)) fl->flags |= FLOWI_FLAG_CAN_SLEEP; - err = xfrm_lookup(&init_net, pprt, fl, sk, 0); + *pprt = xfrm_lookup(&init_net, *pprt, fl, sk, 0); + if (IS_ERR(*pprt)) { + err = PTR_ERR(*pprt); + *pprt = NULL; + } } return err; } diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 2a86c8951dcd..c23bd8cdeee0 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -398,18 +398,14 @@ static struct rtable *icmp_route_lookup(struct net *net, struct sk_buff *skb_in, if (!fl.fl4_src) fl.fl4_src = rt->rt_src; - err = xfrm_lookup(net, (struct dst_entry **)&rt, &fl, NULL, 0); - switch (err) { - case 0: + rt = (struct rtable *) xfrm_lookup(net, &rt->dst, &fl, NULL, 0); + if (!IS_ERR(rt)) { if (rt != rt2) return rt; - break; - case -EPERM: + } else if (PTR_ERR(rt) == -EPERM) { rt = NULL; - break; - default: - return ERR_PTR(err); - } + } else + return rt; err = xfrm_decode_session_reverse(skb_in, &fl, AF_INET); if (err) @@ -438,22 +434,18 @@ static struct rtable *icmp_route_lookup(struct net *net, struct sk_buff *skb_in, if (err) goto relookup_failed; - err = xfrm_lookup(net, (struct dst_entry **)&rt2, &fl, NULL, - XFRM_LOOKUP_ICMP); - switch (err) { - case 0: + rt2 = (struct rtable *) xfrm_lookup(net, &rt2->dst, &fl, NULL, XFRM_LOOKUP_ICMP); + if (!IS_ERR(rt2)) { dst_release(&rt->dst); rt = rt2; - break; - case -EPERM: - return ERR_PTR(err); - default: - if (!rt) - return ERR_PTR(err); - break; + } else if (PTR_ERR(rt2) == -EPERM) { + if (rt) + dst_release(&rt->dst); + return rt2; + } else { + err = PTR_ERR(rt2); + goto relookup_failed; } - - return rt; relookup_failed: diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c index 994a1f29ebbc..9770bb427952 100644 --- a/net/ipv4/netfilter.c +++ b/net/ipv4/netfilter.c @@ -69,7 +69,8 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type) xfrm_decode_session(skb, &fl, AF_INET) == 0) { struct dst_entry *dst = skb_dst(skb); skb_dst_set(skb, NULL); - if (xfrm_lookup(net, &dst, &fl, skb->sk, 0)) + dst = xfrm_lookup(net, dst, &fl, skb->sk, 0); + if (IS_ERR(dst)) return -1; skb_dst_set(skb, dst); } @@ -102,7 +103,8 @@ int ip_xfrm_me_harder(struct sk_buff *skb) dst = ((struct xfrm_dst *)dst)->route; dst_hold(dst); - if (xfrm_lookup(dev_net(dst->dev), &dst, &fl, skb->sk, 0) < 0) + dst = xfrm_lookup(dev_net(dst->dev), dst, &fl, skb->sk, 0); + if (IS_ERR(dst)) return -1; skb_dst_drop(skb); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index e24e4cf2a112..63d37004ee66 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2730,7 +2730,12 @@ int ip_route_output_flow(struct net *net, struct rtable **rp, struct flowi *flp, flp->fl4_src = (*rp)->rt_src; if (!flp->fl4_dst) flp->fl4_dst = (*rp)->rt_dst; - return xfrm_lookup(net, (struct dst_entry **)rp, flp, sk, 0); + *rp = (struct rtable *) xfrm_lookup(net, &(*rp)->dst, flp, sk, 0); + if (IS_ERR(*rp)) { + err = PTR_ERR(*rp); + *rp = NULL; + return err; + } } return 0; diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index e332bae104ee..55665956b3a8 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -324,17 +324,15 @@ static struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *sk /* No need to clone since we're just using its address. */ dst2 = dst; - err = xfrm_lookup(net, &dst, fl, sk, 0); - switch (err) { - case 0: + dst = xfrm_lookup(net, dst, fl, sk, 0); + if (!IS_ERR(dst)) { if (dst != dst2) return dst; - break; - case -EPERM: - dst = NULL; - break; - default: - return ERR_PTR(err); + } else { + if (PTR_ERR(dst) == -EPERM) + dst = NULL; + else + return dst; } err = xfrm_decode_session_reverse(skb, &fl2, AF_INET6); @@ -345,17 +343,17 @@ static struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *sk if (err) goto relookup_failed; - err = xfrm_lookup(net, &dst2, &fl2, sk, XFRM_LOOKUP_ICMP); - switch (err) { - case 0: + dst2 = xfrm_lookup(net, dst2, &fl2, sk, XFRM_LOOKUP_ICMP); + if (!IS_ERR(dst2)) { dst_release(dst); dst = dst2; - break; - case -EPERM: - dst_release(dst); - return ERR_PTR(err); - default: - goto relookup_failed; + } else { + err = PTR_ERR(dst2); + if (err == -EPERM) { + dst_release(dst); + return dst2; + } else + goto relookup_failed; } relookup_failed: @@ -560,7 +558,8 @@ static void icmpv6_echo_reply(struct sk_buff *skb) err = ip6_dst_lookup(sk, &dst, &fl); if (err) goto out; - if ((err = xfrm_lookup(net, &dst, &fl, sk, 0)) < 0) + dst = xfrm_lookup(net, dst, &fl, sk, 0); + if (IS_ERR(dst)) goto out; if (ipv6_addr_is_multicast(&fl.fl6_dst)) diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 35a4ad90a0f5..adaffaf84555 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1028,10 +1028,7 @@ struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi *fl, if (can_sleep) fl->flags |= FLOWI_FLAG_CAN_SLEEP; - err = xfrm_lookup(sock_net(sk), &dst, fl, sk, 0); - if (err) - return ERR_PTR(err); - return dst; + return xfrm_lookup(sock_net(sk), dst, fl, sk, 0); } EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow); @@ -1067,10 +1064,7 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi *fl, if (can_sleep) fl->flags |= FLOWI_FLAG_CAN_SLEEP; - err = xfrm_lookup(sock_net(sk), &dst, fl, sk, 0); - if (err) - return ERR_PTR(err); - return dst; + return xfrm_lookup(sock_net(sk), dst, fl, sk, 0); } EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow); diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 4f4483e697bd..da43038ae18e 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -903,8 +903,14 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, else { dst = ip6_route_output(net, NULL, fl); - if (dst->error || xfrm_lookup(net, &dst, fl, NULL, 0) < 0) + if (dst->error) goto tx_err_link_failure; + dst = xfrm_lookup(net, dst, fl, NULL, 0); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); + dst = NULL; + goto tx_err_link_failure; + } } tdev = dst->dev; diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 49f986d626a0..7b27d08ee281 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1429,7 +1429,12 @@ static void mld_sendpack(struct sk_buff *skb) &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, skb->dev->ifindex); - err = xfrm_lookup(net, &dst, &fl, NULL, 0); + dst = xfrm_lookup(net, dst, &fl, NULL, 0); + err = 0; + if (IS_ERR(dst)) { + err = PTR_ERR(dst); + dst = NULL; + } skb_dst_set(skb, dst); if (err) goto err_out; @@ -1796,9 +1801,11 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, skb->dev->ifindex); - err = xfrm_lookup(net, &dst, &fl, NULL, 0); - if (err) + dst = xfrm_lookup(net, dst, &fl, NULL, 0); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); goto err_out; + } skb_dst_set(skb, dst); err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev, diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 7254ce364006..9360d3be94f0 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -529,8 +529,8 @@ void ndisc_send_skb(struct sk_buff *skb, return; } - err = xfrm_lookup(net, &dst, &fl, NULL, 0); - if (err < 0) { + dst = xfrm_lookup(net, dst, &fl, NULL, 0); + if (IS_ERR(dst)) { kfree_skb(skb); return; } @@ -1542,8 +1542,8 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, if (dst == NULL) return; - err = xfrm_lookup(net, &dst, &fl, NULL, 0); - if (err) + dst = xfrm_lookup(net, dst, &fl, NULL, 0); + if (IS_ERR(dst)) return; rt = (struct rt6_info *) dst; diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index 35915e8617f0..8d74116ae27d 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c @@ -39,7 +39,8 @@ int ip6_route_me_harder(struct sk_buff *skb) if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && xfrm_decode_session(skb, &fl, AF_INET6) == 0) { skb_dst_set(skb, NULL); - if (xfrm_lookup(net, &dst, &fl, skb->sk, 0)) + dst = xfrm_lookup(net, dst, &fl, skb->sk, 0); + if (IS_ERR(dst)) return -1; skb_dst_set(skb, dst); } diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c index bf998feac14e..91f6a61cefab 100644 --- a/net/ipv6/netfilter/ip6t_REJECT.c +++ b/net/ipv6/netfilter/ip6t_REJECT.c @@ -101,7 +101,8 @@ static void send_reset(struct net *net, struct sk_buff *oldskb) dst_release(dst); return; } - if (xfrm_lookup(net, &dst, &fl, NULL, 0)) + dst = xfrm_lookup(net, dst, &fl, NULL, 0); + if (IS_ERR(dst)) return; hh_len = (dst->dev->hard_header_len + 15)&~15; diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index a48239aba33b..6264219f0a42 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -218,8 +218,13 @@ __ip_vs_route_output_v6(struct net *net, struct in6_addr *daddr, ipv6_dev_get_saddr(net, ip6_dst_idev(dst)->dev, &fl.fl6_dst, 0, &fl.fl6_src) < 0) goto out_err; - if (do_xfrm && xfrm_lookup(net, &dst, &fl, NULL, 0) < 0) - goto out_err; + if (do_xfrm) { + dst = xfrm_lookup(net, dst, &fl, NULL, 0); + if (IS_ERR(dst)) { + dst = NULL; + goto out_err; + } + } ipv6_addr_copy(ret_saddr, &fl.fl6_src); return dst; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 0248afa11cda..b1932a629ef8 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1757,14 +1757,14 @@ static struct dst_entry *make_blackhole(struct net *net, u16 family, * At the moment we eat a raw IP route. Mostly to speed up lookups * on interfaces with disabled IPsec. */ -int xfrm_lookup(struct net *net, struct dst_entry **dst_p, - const struct flowi *fl, - struct sock *sk, int flags) +struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, + const struct flowi *fl, + struct sock *sk, int flags) { struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX]; struct flow_cache_object *flo; struct xfrm_dst *xdst; - struct dst_entry *dst, *dst_orig = *dst_p, *route; + struct dst_entry *dst, *route; u16 family = dst_orig->ops->family; u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT); int i, err, num_pols, num_xfrms = 0, drop_pols = 0; @@ -1847,11 +1847,7 @@ restart: xfrm_pols_put(pols, drop_pols); XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES); - dst = make_blackhole(net, family, dst_orig); - if (IS_ERR(dst)) - return PTR_ERR(dst); - *dst_p = dst; - return 0; + return make_blackhole(net, family, dst_orig); } if (fl->flags & FLOWI_FLAG_CAN_SLEEP) { DECLARE_WAITQUEUE(wait, current); @@ -1895,27 +1891,28 @@ no_transform: goto error; } else if (num_xfrms > 0) { /* Flow transformed */ - *dst_p = dst; dst_release(dst_orig); } else { /* Flow passes untransformed */ dst_release(dst); + dst = dst_orig; } ok: xfrm_pols_put(pols, drop_pols); - return 0; + return dst; nopol: - if (!(flags & XFRM_LOOKUP_ICMP)) + if (!(flags & XFRM_LOOKUP_ICMP)) { + dst = dst_orig; goto ok; + } err = -ENOENT; error: dst_release(dst); dropdst: dst_release(dst_orig); - *dst_p = NULL; xfrm_pols_put(pols, drop_pols); - return err; + return ERR_PTR(err); } EXPORT_SYMBOL(xfrm_lookup); @@ -2175,7 +2172,7 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family) struct net *net = dev_net(skb->dev); struct flowi fl; struct dst_entry *dst; - int res; + int res = 0; if (xfrm_decode_session(skb, &fl, family) < 0) { XFRM_INC_STATS(net, LINUX_MIB_XFRMFWDHDRERROR); @@ -2183,9 +2180,12 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family) } skb_dst_force(skb); - dst = skb_dst(skb); - res = xfrm_lookup(net, &dst, &fl, NULL, 0) == 0; + dst = xfrm_lookup(net, skb_dst(skb), &fl, NULL, 0); + if (IS_ERR(dst)) { + res = 1; + dst = NULL; + } skb_dst_set(skb, dst); return res; } -- cgit v1.2.3 From a8d380f30a7d8cc507fd5cc84b2dc5ee2b2144d7 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 2 Mar 2011 13:17:08 -0800 Subject: msm: mdp: Add support for RGBX 8888 image format. Signed-off-by: Dima Zavin Signed-off-by: Carl Vanderlip Signed-off-by: David Brown --- drivers/video/msm/mdp_hw.h | 9 ++++++++- drivers/video/msm/mdp_ppp.c | 1 + include/linux/msm_mdp.h | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h index 4e3deb4e592b..9e1e92ef3edb 100644 --- a/drivers/video/msm/mdp_hw.h +++ b/drivers/video/msm/mdp_hw.h @@ -449,6 +449,7 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, #define PPP_CFG_MDP_XRGB_8888(dir) PPP_CFG_MDP_ARGB_8888(dir) #define PPP_CFG_MDP_RGBA_8888(dir) PPP_CFG_MDP_ARGB_8888(dir) #define PPP_CFG_MDP_BGRA_8888(dir) PPP_CFG_MDP_ARGB_8888(dir) +#define PPP_CFG_MDP_RGBX_8888(dir) PPP_CFG_MDP_ARGB_8888(dir) #define PPP_CFG_MDP_Y_CBCR_H2V2(dir) (PPP_##dir##_C2R_8BIT | \ PPP_##dir##_C0G_8BIT | \ @@ -494,6 +495,8 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R, 8) #define PPP_PACK_PATTERN_MDP_BGRA_8888 \ MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, 8) +#define PPP_PACK_PATTERN_MDP_RGBX_8888 \ + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R, 8) #define PPP_PACK_PATTERN_MDP_Y_CBCR_H2V1 \ MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8) #define PPP_PACK_PATTERN_MDP_Y_CBCR_H2V2 PPP_PACK_PATTERN_MDP_Y_CBCR_H2V1 @@ -509,6 +512,7 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, #define PPP_CHROMA_SAMP_MDP_ARGB_8888(dir) PPP_OP_##dir##_CHROMA_RGB #define PPP_CHROMA_SAMP_MDP_RGBA_8888(dir) PPP_OP_##dir##_CHROMA_RGB #define PPP_CHROMA_SAMP_MDP_BGRA_8888(dir) PPP_OP_##dir##_CHROMA_RGB +#define PPP_CHROMA_SAMP_MDP_RGBX_8888(dir) PPP_OP_##dir##_CHROMA_RGB #define PPP_CHROMA_SAMP_MDP_Y_CBCR_H2V1(dir) PPP_OP_##dir##_CHROMA_H2V1 #define PPP_CHROMA_SAMP_MDP_Y_CBCR_H2V2(dir) PPP_OP_##dir##_CHROMA_420 #define PPP_CHROMA_SAMP_MDP_Y_CRCB_H2V1(dir) PPP_OP_##dir##_CHROMA_H2V1 @@ -523,6 +527,7 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, [MDP_ARGB_8888] = PPP_##name##_MDP_ARGB_8888,\ [MDP_RGBA_8888] = PPP_##name##_MDP_RGBA_8888,\ [MDP_BGRA_8888] = PPP_##name##_MDP_BGRA_8888,\ + [MDP_RGBX_8888] = PPP_##name##_MDP_RGBX_8888,\ [MDP_Y_CBCR_H2V1] = PPP_##name##_MDP_Y_CBCR_H2V1,\ [MDP_Y_CBCR_H2V2] = PPP_##name##_MDP_Y_CBCR_H2V2,\ [MDP_Y_CRCB_H2V1] = PPP_##name##_MDP_Y_CRCB_H2V1,\ @@ -536,6 +541,7 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, [MDP_ARGB_8888] = PPP_##name##_MDP_ARGB_8888(dir),\ [MDP_RGBA_8888] = PPP_##name##_MDP_RGBA_8888(dir),\ [MDP_BGRA_8888] = PPP_##name##_MDP_BGRA_8888(dir),\ + [MDP_RGBX_8888] = PPP_##name##_MDP_RGBX_8888(dir),\ [MDP_Y_CBCR_H2V1] = PPP_##name##_MDP_Y_CBCR_H2V1(dir),\ [MDP_Y_CBCR_H2V2] = PPP_##name##_MDP_Y_CBCR_H2V2(dir),\ [MDP_Y_CRCB_H2V1] = PPP_##name##_MDP_Y_CRCB_H2V1(dir),\ @@ -547,7 +553,8 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, (img == MDP_YCRYCB_H2V1)) #define IS_RGB(img) ((img == MDP_RGB_565) | (img == MDP_RGB_888) | \ (img == MDP_ARGB_8888) | (img == MDP_RGBA_8888) | \ - (img == MDP_XRGB_8888) | (img == MDP_BGRA_8888)) + (img == MDP_XRGB_8888) | (img == MDP_BGRA_8888) | \ + (img == MDP_RGBX_8888)) #define HAS_ALPHA(img) ((img == MDP_ARGB_8888) | (img == MDP_RGBA_8888) | \ (img == MDP_BGRA_8888)) diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index 4ff001f4cbbd..2b6564e8bfea 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -69,6 +69,7 @@ static uint32_t bytes_per_pixel[] = { [MDP_ARGB_8888] = 4, [MDP_RGBA_8888] = 4, [MDP_BGRA_8888] = 4, + [MDP_RGBX_8888] = 4, [MDP_Y_CBCR_H2V1] = 1, [MDP_Y_CBCR_H2V2] = 1, [MDP_Y_CRCB_H2V1] = 1, diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h index d11fe0f2f956..fe722c1fb61d 100644 --- a/include/linux/msm_mdp.h +++ b/include/linux/msm_mdp.h @@ -32,6 +32,7 @@ enum { MDP_Y_CBCR_H2V1, /* Y and CrCb, pseduo planar w/ Cr is in MSB */ MDP_RGBA_8888, /* ARGB 888 */ MDP_BGRA_8888, /* ABGR 888 */ + MDP_RGBX_8888, /* RGBX 888 */ MDP_IMGTYPE_LIMIT /* Non valid image type after this enum */ }; -- cgit v1.2.3 From b23dd4fe42b455af5c6e20966b7d6959fa8352ea Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 2 Mar 2011 14:31:35 -0800 Subject: ipv4: Make output route lookup return rtable directly. Instead of on the stack. Signed-off-by: David S. Miller --- drivers/infiniband/core/addr.c | 7 ++- drivers/infiniband/hw/cxgb3/iwch_cm.c | 3 +- drivers/infiniband/hw/cxgb4/cm.c | 3 +- drivers/infiniband/hw/nes/nes_cm.c | 3 +- drivers/net/bonding/bond_main.c | 6 +- drivers/net/cnic.c | 7 ++- drivers/net/pptp.c | 8 +-- drivers/scsi/cxgbi/libcxgbi.c | 3 +- include/net/route.h | 58 ++++++++++---------- net/atm/clip.c | 6 +- net/bridge/br_netfilter.c | 9 +-- net/dccp/ipv4.c | 27 ++++----- net/ipv4/af_inet.c | 30 +++++----- net/ipv4/arp.c | 19 +++---- net/ipv4/datagram.c | 11 ++-- net/ipv4/icmp.c | 19 ++++--- net/ipv4/igmp.c | 16 ++++-- net/ipv4/inet_connection_sock.c | 3 +- net/ipv4/ip_gre.c | 11 ++-- net/ipv4/ip_output.c | 6 +- net/ipv4/ipip.c | 7 ++- net/ipv4/ipmr.c | 8 +-- net/ipv4/netfilter.c | 12 +++- net/ipv4/raw.c | 8 ++- net/ipv4/route.c | 100 +++++++++++++++++----------------- net/ipv4/syncookies.c | 3 +- net/ipv4/tcp_ipv4.c | 28 +++++----- net/ipv4/udp.c | 5 +- net/ipv4/xfrm4_policy.c | 12 ++-- net/ipv6/ip6_tunnel.c | 11 ++-- net/ipv6/sit.c | 8 ++- net/l2tp/l2tp_ip.c | 8 ++- net/netfilter/ipvs/ip_vs_xmit.c | 9 ++- net/netfilter/xt_TEE.c | 3 +- net/rxrpc/ar-peer.c | 7 +-- net/sctp/protocol.c | 7 ++- 36 files changed, 267 insertions(+), 224 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index 8aba0ba57de5..2d749937a969 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -193,10 +193,11 @@ static int addr4_resolve(struct sockaddr_in *src_in, fl.nl_u.ip4_u.saddr = src_ip; fl.oif = addr->bound_dev_if; - ret = ip_route_output_key(&init_net, &rt, &fl); - if (ret) + rt = ip_route_output_key(&init_net, &fl); + if (IS_ERR(rt)) { + ret = PTR_ERR(rt); goto out; - + } src_in->sin_family = AF_INET; src_in->sin_addr.s_addr = rt->rt_src; diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c index e654285aa6ba..e0ccbc53fbcc 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cm.c +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c @@ -354,7 +354,8 @@ static struct rtable *find_route(struct t3cdev *dev, __be32 local_ip, } }; - if (ip_route_output_flow(&init_net, &rt, &fl, NULL)) + rt = ip_route_output_flow(&init_net, &fl, NULL); + if (IS_ERR(rt)) return NULL; return rt; } diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 7e0484f18db5..77b0eef2aad9 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -331,7 +331,8 @@ static struct rtable *find_route(struct c4iw_dev *dev, __be32 local_ip, } }; - if (ip_route_output_flow(&init_net, &rt, &fl, NULL)) + rt = ip_route_output_flow(&init_net, &fl, NULL); + if (IS_ERR(rt)) return NULL; return rt; } diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index ec3aa11c36cb..e81599cb1fe6 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c @@ -1112,7 +1112,8 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi memset(&fl, 0, sizeof fl); fl.nl_u.ip4_u.daddr = htonl(dst_ip); - if (ip_route_output_key(&init_net, &rt, &fl)) { + rt = ip_route_output_key(&init_net, &fl); + if (IS_ERR(rt)) { printk(KERN_ERR "%s: ip_route_output_key failed for 0x%08X\n", __func__, dst_ip); return rc; diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 584f97b73060..0592e6da15a6 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2681,7 +2681,7 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op, __be32 dest_ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) { - int i, vlan_id, rv; + int i, vlan_id; __be32 *targets = bond->params.arp_targets; struct vlan_entry *vlan; struct net_device *vlan_dev; @@ -2708,8 +2708,8 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) fl.fl4_dst = targets[i]; fl.fl4_tos = RTO_ONLINK; - rv = ip_route_output_key(dev_net(bond->dev), &rt, &fl); - if (rv) { + rt = ip_route_output_key(dev_net(bond->dev), &fl); + if (IS_ERR(rt)) { if (net_ratelimit()) { pr_warning("%s: no route to arp_ip_target %pI4\n", bond->dev->name, &fl.fl4_dst); diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c index 5274de3e1bb9..25f08880ae0f 100644 --- a/drivers/net/cnic.c +++ b/drivers/net/cnic.c @@ -3397,9 +3397,12 @@ static int cnic_get_v4_route(struct sockaddr_in *dst_addr, memset(&fl, 0, sizeof(fl)); fl.nl_u.ip4_u.daddr = dst_addr->sin_addr.s_addr; - err = ip_route_output_key(&init_net, &rt, &fl); - if (!err) + rt = ip_route_output_key(&init_net, &fl); + err = 0; + if (!IS_ERR(rt)) *dst = &rt->dst; + else + err = PTR_ERR(rt); return err; #else return -ENETUNREACH; diff --git a/drivers/net/pptp.c b/drivers/net/pptp.c index 164cfad6ce79..1af549c89d51 100644 --- a/drivers/net/pptp.c +++ b/drivers/net/pptp.c @@ -175,7 +175,6 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) struct pptp_opt *opt = &po->proto.pptp; struct pptp_gre_header *hdr; unsigned int header_len = sizeof(*hdr); - int err = 0; int islcp; int len; unsigned char *data; @@ -198,8 +197,8 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) .saddr = opt->src_addr.sin_addr.s_addr, .tos = RT_TOS(0) } }, .proto = IPPROTO_GRE }; - err = ip_route_output_key(&init_net, &rt, &fl); - if (err) + rt = ip_route_output_key(&init_net, &fl); + if (IS_ERR(rt)) goto tx_error; } tdev = rt->dst.dev; @@ -477,7 +476,8 @@ static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr, .tos = RT_CONN_FLAGS(sk) } }, .proto = IPPROTO_GRE }; security_sk_classify_flow(sk, &fl); - if (ip_route_output_key(&init_net, &rt, &fl)) { + rt = ip_route_output_key(&init_net, &fl); + if (IS_ERR(rt)) { error = -EHOSTUNREACH; goto end; } diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index 261aa817bdd5..889199aa1f5b 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -470,7 +470,8 @@ static struct rtable *find_route_ipv4(__be32 saddr, __be32 daddr, } }; - if (ip_route_output_flow(&init_net, &rt, &fl, NULL)) + rt = ip_route_output_flow(&init_net, &fl, NULL); + if (IS_ERR(rt)) return NULL; return rt; diff --git a/include/net/route.h b/include/net/route.h index 707cfc8eccdc..088a1867348f 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -118,9 +118,10 @@ extern void ip_rt_redirect(__be32 old_gw, __be32 dst, __be32 new_gw, __be32 src, struct net_device *dev); extern void rt_cache_flush(struct net *net, int how); extern void rt_cache_flush_batch(struct net *net); -extern int __ip_route_output_key(struct net *, struct rtable **, const struct flowi *flp); -extern int ip_route_output_key(struct net *, struct rtable **, struct flowi *flp); -extern int ip_route_output_flow(struct net *, struct rtable **rp, struct flowi *flp, struct sock *sk); +extern struct rtable *__ip_route_output_key(struct net *, const struct flowi *flp); +extern struct rtable *ip_route_output_key(struct net *, struct flowi *flp); +extern struct rtable *ip_route_output_flow(struct net *, struct flowi *flp, + struct sock *sk); extern struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_orig); extern int ip_route_input_common(struct sk_buff *skb, __be32 dst, __be32 src, @@ -166,10 +167,10 @@ static inline char rt_tos2priority(u8 tos) return ip_tos2prio[IPTOS_TOS(tos)>>1]; } -static inline int ip_route_connect(struct rtable **rp, __be32 dst, - __be32 src, u32 tos, int oif, u8 protocol, - __be16 sport, __be16 dport, struct sock *sk, - bool can_sleep) +static inline struct rtable *ip_route_connect(__be32 dst, __be32 src, u32 tos, + int oif, u8 protocol, + __be16 sport, __be16 dport, + struct sock *sk, bool can_sleep) { struct flowi fl = { .oif = oif, .mark = sk->sk_mark, @@ -179,8 +180,8 @@ static inline int ip_route_connect(struct rtable **rp, __be32 dst, .proto = protocol, .fl_ip_sport = sport, .fl_ip_dport = dport }; - int err; struct net *net = sock_net(sk); + struct rtable *rt; if (inet_sk(sk)->transparent) fl.flags |= FLOWI_FLAG_ANYSRC; @@ -190,29 +191,29 @@ static inline int ip_route_connect(struct rtable **rp, __be32 dst, fl.flags |= FLOWI_FLAG_CAN_SLEEP; if (!dst || !src) { - err = __ip_route_output_key(net, rp, &fl); - if (err) - return err; - fl.fl4_dst = (*rp)->rt_dst; - fl.fl4_src = (*rp)->rt_src; - ip_rt_put(*rp); - *rp = NULL; + rt = __ip_route_output_key(net, &fl); + if (IS_ERR(rt)) + return rt; + fl.fl4_dst = rt->rt_dst; + fl.fl4_src = rt->rt_src; + ip_rt_put(rt); } security_sk_classify_flow(sk, &fl); - return ip_route_output_flow(net, rp, &fl, sk); + return ip_route_output_flow(net, &fl, sk); } -static inline int ip_route_newports(struct rtable **rp, u8 protocol, - __be16 orig_sport, __be16 orig_dport, - __be16 sport, __be16 dport, struct sock *sk) +static inline struct rtable *ip_route_newports(struct rtable *rt, + u8 protocol, __be16 orig_sport, + __be16 orig_dport, __be16 sport, + __be16 dport, struct sock *sk) { if (sport != orig_sport || dport != orig_dport) { - struct flowi fl = { .oif = (*rp)->fl.oif, - .mark = (*rp)->fl.mark, - .fl4_dst = (*rp)->fl.fl4_dst, - .fl4_src = (*rp)->fl.fl4_src, - .fl4_tos = (*rp)->fl.fl4_tos, - .proto = (*rp)->fl.proto, + struct flowi fl = { .oif = rt->fl.oif, + .mark = rt->fl.mark, + .fl4_dst = rt->fl.fl4_dst, + .fl4_src = rt->fl.fl4_src, + .fl4_tos = rt->fl.fl4_tos, + .proto = rt->fl.proto, .fl_ip_sport = sport, .fl_ip_dport = dport }; @@ -220,12 +221,11 @@ static inline int ip_route_newports(struct rtable **rp, u8 protocol, fl.flags |= FLOWI_FLAG_ANYSRC; if (protocol == IPPROTO_TCP) fl.flags |= FLOWI_FLAG_PRECOW_METRICS; - ip_rt_put(*rp); - *rp = NULL; + ip_rt_put(rt); security_sk_classify_flow(sk, &fl); - return ip_route_output_flow(sock_net(sk), rp, &fl, sk); + return ip_route_output_flow(sock_net(sk), &fl, sk); } - return 0; + return rt; } extern void rt_bind_peer(struct rtable *rt, int create); diff --git a/net/atm/clip.c b/net/atm/clip.c index d257da50fcfb..810a1294eddb 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -520,9 +520,9 @@ static int clip_setentry(struct atm_vcc *vcc, __be32 ip) unlink_clip_vcc(clip_vcc); return 0; } - error = ip_route_output_key(&init_net, &rt, &fl); - if (error) - return error; + rt = ip_route_output_key(&init_net, &fl); + if (IS_ERR(rt)) + return PTR_ERR(rt); neigh = __neigh_lookup(&clip_tbl, &ip, rt->dst.dev, 1); ip_rt_put(rt); if (!neigh) diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 4b5b66d07bba..45b57b173f70 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -428,14 +428,15 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb) if (err != -EHOSTUNREACH || !in_dev || IN_DEV_FORWARD(in_dev)) goto free_skb; - if (!ip_route_output_key(dev_net(dev), &rt, &fl)) { + rt = ip_route_output_key(dev_net(dev), &fl); + if (!IS_ERR(rt)) { /* - Bridged-and-DNAT'ed traffic doesn't * require ip_forwarding. */ - if (((struct dst_entry *)rt)->dev == dev) { - skb_dst_set(skb, (struct dst_entry *)rt); + if (rt->dst.dev == dev) { + skb_dst_set(skb, &rt->dst); goto bridged_dnat; } - dst_release((struct dst_entry *)rt); + ip_rt_put(rt); } free_skb: kfree_skb(skb); diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index a8ff95502081..7882377bc62e 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -46,7 +46,6 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) __be16 orig_sport, orig_dport; struct rtable *rt; __be32 daddr, nexthop; - int tmp; int err; dp->dccps_role = DCCP_ROLE_CLIENT; @@ -66,12 +65,12 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) orig_sport = inet->inet_sport; orig_dport = usin->sin_port; - tmp = ip_route_connect(&rt, nexthop, inet->inet_saddr, - RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, - IPPROTO_DCCP, - orig_sport, orig_dport, sk, true); - if (tmp < 0) - return tmp; + rt = ip_route_connect(nexthop, inet->inet_saddr, + RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, + IPPROTO_DCCP, + orig_sport, orig_dport, sk, true); + if (IS_ERR(rt)) + return PTR_ERR(rt); if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) { ip_rt_put(rt); @@ -102,12 +101,13 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (err != 0) goto failure; - err = ip_route_newports(&rt, IPPROTO_DCCP, - orig_sport, orig_dport, - inet->inet_sport, inet->inet_dport, sk); - if (err != 0) + rt = ip_route_newports(rt, IPPROTO_DCCP, + orig_sport, orig_dport, + inet->inet_sport, inet->inet_dport, sk); + if (IS_ERR(rt)) { + rt = NULL; goto failure; - + } /* OK, now commit destination to socket. */ sk_setup_caps(sk, &rt->dst); @@ -475,7 +475,8 @@ static struct dst_entry* dccp_v4_route_skb(struct net *net, struct sock *sk, }; security_skb_classify_flow(skb, &fl); - if (ip_route_output_flow(net, &rt, &fl, sk)) { + rt = ip_route_output_flow(net, &fl, sk); + if (IS_ERR(rt)) { IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES); return NULL; } diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 44513bb8ac2e..35a502055018 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1101,23 +1101,20 @@ int sysctl_ip_dynaddr __read_mostly; static int inet_sk_reselect_saddr(struct sock *sk) { struct inet_sock *inet = inet_sk(sk); - int err; - struct rtable *rt; __be32 old_saddr = inet->inet_saddr; - __be32 new_saddr; __be32 daddr = inet->inet_daddr; + struct rtable *rt; + __be32 new_saddr; if (inet->opt && inet->opt->srr) daddr = inet->opt->faddr; /* Query new route. */ - err = ip_route_connect(&rt, daddr, 0, - RT_CONN_FLAGS(sk), - sk->sk_bound_dev_if, - sk->sk_protocol, - inet->inet_sport, inet->inet_dport, sk, false); - if (err) - return err; + rt = ip_route_connect(daddr, 0, RT_CONN_FLAGS(sk), + sk->sk_bound_dev_if, sk->sk_protocol, + inet->inet_sport, inet->inet_dport, sk, false); + if (IS_ERR(rt)) + return PTR_ERR(rt); sk_setup_caps(sk, &rt->dst); @@ -1160,7 +1157,7 @@ int inet_sk_rebuild_header(struct sock *sk) daddr = inet->inet_daddr; if (inet->opt && inet->opt->srr) daddr = inet->opt->faddr; -{ + { struct flowi fl = { .oif = sk->sk_bound_dev_if, .mark = sk->sk_mark, @@ -1174,11 +1171,14 @@ int inet_sk_rebuild_header(struct sock *sk) }; security_sk_classify_flow(sk, &fl); - err = ip_route_output_flow(sock_net(sk), &rt, &fl, sk); -} - if (!err) + rt = ip_route_output_flow(sock_net(sk), &fl, sk); + } + if (!IS_ERR(rt)) { + err = 0; sk_setup_caps(sk, &rt->dst); - else { + } else { + err = PTR_ERR(rt); + /* Routing failed... */ sk->sk_route_caps = 0; /* diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 7927589813b5..fa9988da1da4 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -440,7 +440,8 @@ static int arp_filter(__be32 sip, __be32 tip, struct net_device *dev) /*unsigned long now; */ struct net *net = dev_net(dev); - if (ip_route_output_key(net, &rt, &fl) < 0) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) return 1; if (rt->dst.dev != dev) { NET_INC_STATS_BH(net, LINUX_MIB_ARPFILTER); @@ -1063,10 +1064,10 @@ static int arp_req_set(struct net *net, struct arpreq *r, if (dev == NULL) { struct flowi fl = { .fl4_dst = ip, .fl4_tos = RTO_ONLINK }; - struct rtable *rt; - err = ip_route_output_key(net, &rt, &fl); - if (err != 0) - return err; + struct rtable *rt = ip_route_output_key(net, &fl); + + if (IS_ERR(rt)) + return PTR_ERR(rt); dev = rt->dst.dev; ip_rt_put(rt); if (!dev) @@ -1177,7 +1178,6 @@ static int arp_req_delete_public(struct net *net, struct arpreq *r, static int arp_req_delete(struct net *net, struct arpreq *r, struct net_device *dev) { - int err; __be32 ip; if (r->arp_flags & ATF_PUBL) @@ -1187,10 +1187,9 @@ static int arp_req_delete(struct net *net, struct arpreq *r, if (dev == NULL) { struct flowi fl = { .fl4_dst = ip, .fl4_tos = RTO_ONLINK }; - struct rtable *rt; - err = ip_route_output_key(net, &rt, &fl); - if (err != 0) - return err; + struct rtable *rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) + return PTR_ERR(rt); dev = rt->dst.dev; ip_rt_put(rt); if (!dev) diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c index eaee1edd2dd7..85bd24ca4f6d 100644 --- a/net/ipv4/datagram.c +++ b/net/ipv4/datagram.c @@ -46,11 +46,12 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (!saddr) saddr = inet->mc_addr; } - err = ip_route_connect(&rt, usin->sin_addr.s_addr, saddr, - RT_CONN_FLAGS(sk), oif, - sk->sk_protocol, - inet->inet_sport, usin->sin_port, sk, true); - if (err) { + rt = ip_route_connect(usin->sin_addr.s_addr, saddr, + RT_CONN_FLAGS(sk), oif, + sk->sk_protocol, + inet->inet_sport, usin->sin_port, sk, true); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); if (err == -ENETUNREACH) IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); return err; diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index c23bd8cdeee0..994a785d98f9 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -358,7 +358,8 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) .fl4_tos = RT_TOS(ip_hdr(skb)->tos), .proto = IPPROTO_ICMP }; security_skb_classify_flow(skb, &fl); - if (ip_route_output_key(net, &rt, &fl)) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) goto out_unlock; } if (icmpv4_xrlim_allow(net, rt, icmp_param->data.icmph.type, @@ -388,9 +389,9 @@ static struct rtable *icmp_route_lookup(struct net *net, struct sk_buff *skb_in, int err; security_skb_classify_flow(skb_in, &fl); - err = __ip_route_output_key(net, &rt, &fl); - if (err) - return ERR_PTR(err); + rt = __ip_route_output_key(net, &fl); + if (IS_ERR(rt)) + return rt; /* No need to clone since we're just using its address. */ rt2 = rt; @@ -412,15 +413,19 @@ static struct rtable *icmp_route_lookup(struct net *net, struct sk_buff *skb_in, goto relookup_failed; if (inet_addr_type(net, fl.fl4_src) == RTN_LOCAL) { - err = __ip_route_output_key(net, &rt2, &fl); + rt2 = __ip_route_output_key(net, &fl); + if (IS_ERR(rt2)) + err = PTR_ERR(rt2); } else { struct flowi fl2 = {}; unsigned long orefdst; fl2.fl4_dst = fl.fl4_src; - err = ip_route_output_key(net, &rt2, &fl2); - if (err) + rt2 = ip_route_output_key(net, &fl2); + if (IS_ERR(rt2)) { + err = PTR_ERR(rt2); goto relookup_failed; + } /* Ugh! */ orefdst = skb_in->_skb_refdst; /* save old refdst */ err = ip_route_input(skb_in, fl.fl4_dst, fl.fl4_src, diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index e0e77e297de3..44ba9068b72f 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -325,7 +325,8 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) struct flowi fl = { .oif = dev->ifindex, .fl4_dst = IGMPV3_ALL_MCR, .proto = IPPROTO_IGMP }; - if (ip_route_output_key(net, &rt, &fl)) { + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) { kfree_skb(skb); return NULL; } @@ -670,7 +671,8 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, struct flowi fl = { .oif = dev->ifindex, .fl4_dst = dst, .proto = IPPROTO_IGMP }; - if (ip_route_output_key(net, &rt, &fl)) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) return -1; } if (rt->rt_src == 0) { @@ -1440,7 +1442,6 @@ void ip_mc_destroy_dev(struct in_device *in_dev) static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr) { struct flowi fl = { .fl4_dst = imr->imr_multiaddr.s_addr }; - struct rtable *rt; struct net_device *dev = NULL; struct in_device *idev = NULL; @@ -1454,9 +1455,12 @@ static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr) return NULL; } - if (!dev && !ip_route_output_key(net, &rt, &fl)) { - dev = rt->dst.dev; - ip_rt_put(rt); + if (!dev) { + struct rtable *rt = ip_route_output_key(net, &fl); + if (!IS_ERR(rt)) { + dev = rt->dst.dev; + ip_rt_put(rt); + } } if (dev) { imr->imr_ifindex = dev->ifindex; diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 7f85d4aec26a..e4e301a61c5b 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -369,7 +369,8 @@ struct dst_entry *inet_csk_route_req(struct sock *sk, struct net *net = sock_net(sk); security_req_classify_flow(req, &fl); - if (ip_route_output_flow(net, &rt, &fl, sk)) + rt = ip_route_output_flow(net, &fl, sk); + if (IS_ERR(rt)) goto no_route; if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) goto route_err; diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 6613edfac28c..f9af98dd7561 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -778,7 +778,8 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev .proto = IPPROTO_GRE, .fl_gre_key = tunnel->parms.o_key }; - if (ip_route_output_key(dev_net(dev), &rt, &fl)) { + rt = ip_route_output_key(dev_net(dev), &fl); + if (IS_ERR(rt)) { dev->stats.tx_carrier_errors++; goto tx_error; } @@ -953,9 +954,9 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev) .proto = IPPROTO_GRE, .fl_gre_key = tunnel->parms.o_key }; - struct rtable *rt; + struct rtable *rt = ip_route_output_key(dev_net(dev), &fl); - if (!ip_route_output_key(dev_net(dev), &rt, &fl)) { + if (!IS_ERR(rt)) { tdev = rt->dst.dev; ip_rt_put(rt); } @@ -1215,9 +1216,9 @@ static int ipgre_open(struct net_device *dev) .proto = IPPROTO_GRE, .fl_gre_key = t->parms.o_key }; - struct rtable *rt; + struct rtable *rt = ip_route_output_key(dev_net(dev), &fl); - if (ip_route_output_key(dev_net(dev), &rt, &fl)) + if (IS_ERR(rt)) return -EADDRNOTAVAIL; dev = rt->dst.dev; ip_rt_put(rt); diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 33316b3534ca..171f483b21d5 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -355,7 +355,8 @@ int ip_queue_xmit(struct sk_buff *skb) * itself out. */ security_sk_classify_flow(sk, &fl); - if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk)) + rt = ip_route_output_flow(sock_net(sk), &fl, sk); + if (IS_ERR(rt)) goto no_route; } sk_setup_caps(sk, &rt->dst); @@ -1489,7 +1490,8 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar .proto = sk->sk_protocol, .flags = ip_reply_arg_flowi_flags(arg) }; security_skb_classify_flow(skb, &fl); - if (ip_route_output_key(sock_net(sk), &rt, &fl)) + rt = ip_route_output_key(sock_net(sk), &fl); + if (IS_ERR(rt)) return; } diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 988f52fba54a..e1e17576baa6 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -469,7 +469,8 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) .proto = IPPROTO_IPIP }; - if (ip_route_output_key(dev_net(dev), &rt, &fl)) { + rt = ip_route_output_key(dev_net(dev), &fl); + if (IS_ERR(rt)) { dev->stats.tx_carrier_errors++; goto tx_error_icmp; } @@ -590,9 +591,9 @@ static void ipip_tunnel_bind_dev(struct net_device *dev) .fl4_tos = RT_TOS(iph->tos), .proto = IPPROTO_IPIP }; - struct rtable *rt; + struct rtable *rt = ip_route_output_key(dev_net(dev), &fl); - if (!ip_route_output_key(dev_net(dev), &rt, &fl)) { + if (!IS_ERR(rt)) { tdev = rt->dst.dev; ip_rt_put(rt); } diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 8b65a12654e7..26ca2f2d37ce 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1618,8 +1618,8 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, .fl4_tos = RT_TOS(iph->tos), .proto = IPPROTO_IPIP }; - - if (ip_route_output_key(net, &rt, &fl)) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) goto out_free; encap = sizeof(struct iphdr); } else { @@ -1629,8 +1629,8 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, .fl4_tos = RT_TOS(iph->tos), .proto = IPPROTO_IPIP }; - - if (ip_route_output_key(net, &rt, &fl)) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) goto out_free; } diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c index 9770bb427952..67bf709180de 100644 --- a/net/ipv4/netfilter.c +++ b/net/ipv4/netfilter.c @@ -38,7 +38,8 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type) fl.oif = skb->sk ? skb->sk->sk_bound_dev_if : 0; fl.mark = skb->mark; fl.flags = skb->sk ? inet_sk_flowi_flags(skb->sk) : 0; - if (ip_route_output_key(net, &rt, &fl) != 0) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) return -1; /* Drop old route. */ @@ -48,7 +49,8 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type) /* non-local src, find valid iif to satisfy * rp-filter when calling ip_route_input. */ fl.fl4_dst = iph->saddr; - if (ip_route_output_key(net, &rt, &fl) != 0) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) return -1; orefdst = skb->_skb_refdst; @@ -221,7 +223,11 @@ static __sum16 nf_ip_checksum_partial(struct sk_buff *skb, unsigned int hook, static int nf_ip_route(struct dst_entry **dst, struct flowi *fl) { - return ip_route_output_key(&init_net, (struct rtable **)dst, fl); + struct rtable *rt = ip_route_output_key(&init_net, fl); + if (IS_ERR(rt)) + return PTR_ERR(rt); + *dst = &rt->dst; + return 0; } static const struct nf_afinfo nf_ip_afinfo = { diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index d7a2d1eaec09..467d570d087a 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -564,10 +564,12 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, } security_sk_classify_flow(sk, &fl); - err = ip_route_output_flow(sock_net(sk), &rt, &fl, sk); + rt = ip_route_output_flow(sock_net(sk), &fl, sk); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + goto done; + } } - if (err) - goto done; err = -EACCES; if (rt->rt_flags & RTCF_BROADCAST && !sock_flag(sk, SOCK_BROADCAST)) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 63d37004ee66..5090e956f6b8 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1014,8 +1014,8 @@ static int slow_chain_length(const struct rtable *head) return length >> FRACT_BITS; } -static int rt_intern_hash(unsigned hash, struct rtable *rt, - struct rtable **rp, struct sk_buff *skb, int ifindex) +static struct rtable *rt_intern_hash(unsigned hash, struct rtable *rt, + struct sk_buff *skb, int ifindex) { struct rtable *rth, *cand; struct rtable __rcu **rthp, **candp; @@ -1056,7 +1056,7 @@ restart: printk(KERN_WARNING "Neighbour table failure & not caching routes.\n"); ip_rt_put(rt); - return err; + return ERR_PTR(err); } } @@ -1093,11 +1093,9 @@ restart: spin_unlock_bh(rt_hash_lock_addr(hash)); rt_drop(rt); - if (rp) - *rp = rth; - else + if (skb) skb_dst_set(skb, &rth->dst); - return 0; + return rth; } if (!atomic_read(&rth->dst.__refcnt)) { @@ -1154,7 +1152,7 @@ restart: if (err != -ENOBUFS) { rt_drop(rt); - return err; + return ERR_PTR(err); } /* Neighbour tables are full and nothing @@ -1175,7 +1173,7 @@ restart: if (net_ratelimit()) printk(KERN_WARNING "ipv4: Neighbour table overflow.\n"); rt_drop(rt); - return -ENOBUFS; + return ERR_PTR(-ENOBUFS); } } @@ -1201,11 +1199,9 @@ restart: spin_unlock_bh(rt_hash_lock_addr(hash)); skip_hashing: - if (rp) - *rp = rt; - else + if (skb) skb_dst_set(skb, &rt->dst); - return 0; + return rt; } static atomic_t __rt_peer_genid = ATOMIC_INIT(0); @@ -1896,7 +1892,10 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, RT_CACHE_STAT_INC(in_slow_mc); hash = rt_hash(daddr, saddr, dev->ifindex, rt_genid(dev_net(dev))); - return rt_intern_hash(hash, rth, NULL, skb, dev->ifindex); + rth = rt_intern_hash(hash, rth, skb, dev->ifindex); + err = 0; + if (IS_ERR(rth)) + err = PTR_ERR(rth); e_nobufs: return -ENOBUFS; @@ -2051,7 +2050,10 @@ static int ip_mkroute_input(struct sk_buff *skb, /* put it into the cache */ hash = rt_hash(daddr, saddr, fl->iif, rt_genid(dev_net(rth->dst.dev))); - return rt_intern_hash(hash, rth, NULL, skb, fl->iif); + rth = rt_intern_hash(hash, rth, skb, fl->iif); + if (IS_ERR(rth)) + return PTR_ERR(rth); + return 0; } /* @@ -2194,7 +2196,10 @@ local_input: } rth->rt_type = res.type; hash = rt_hash(daddr, saddr, fl.iif, rt_genid(net)); - err = rt_intern_hash(hash, rth, NULL, skb, fl.iif); + rth = rt_intern_hash(hash, rth, skb, fl.iif); + err = 0; + if (IS_ERR(rth)) + err = PTR_ERR(rth); goto out; no_route: @@ -2422,8 +2427,8 @@ static struct rtable *__mkroute_output(const struct fib_result *res, * called with rcu_read_lock(); */ -static int ip_route_output_slow(struct net *net, struct rtable **rp, - const struct flowi *oldflp) +static struct rtable *ip_route_output_slow(struct net *net, + const struct flowi *oldflp) { u32 tos = RT_FL_TOS(oldflp); struct flowi fl = { .fl4_dst = oldflp->fl4_dst, @@ -2438,8 +2443,6 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp, unsigned int flags = 0; struct net_device *dev_out = NULL; struct rtable *rth; - int err; - res.fi = NULL; #ifdef CONFIG_IP_MULTIPLE_TABLES @@ -2448,7 +2451,7 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp, rcu_read_lock(); if (oldflp->fl4_src) { - err = -EINVAL; + rth = ERR_PTR(-EINVAL); if (ipv4_is_multicast(oldflp->fl4_src) || ipv4_is_lbcast(oldflp->fl4_src) || ipv4_is_zeronet(oldflp->fl4_src)) @@ -2499,13 +2502,13 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp, if (oldflp->oif) { dev_out = dev_get_by_index_rcu(net, oldflp->oif); - err = -ENODEV; + rth = ERR_PTR(-ENODEV); if (dev_out == NULL) goto out; /* RACE: Check return value of inet_select_addr instead. */ if (!(dev_out->flags & IFF_UP) || !__in_dev_get_rcu(dev_out)) { - err = -ENETUNREACH; + rth = ERR_PTR(-ENETUNREACH); goto out; } if (ipv4_is_local_multicast(oldflp->fl4_dst) || @@ -2563,7 +2566,7 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp, res.type = RTN_UNICAST; goto make_route; } - err = -ENETUNREACH; + rth = ERR_PTR(-ENETUNREACH); goto out; } @@ -2598,23 +2601,20 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp, make_route: rth = __mkroute_output(&res, &fl, oldflp, dev_out, flags); - if (IS_ERR(rth)) - err = PTR_ERR(rth); - else { + if (!IS_ERR(rth)) { unsigned int hash; hash = rt_hash(oldflp->fl4_dst, oldflp->fl4_src, oldflp->oif, rt_genid(dev_net(dev_out))); - err = rt_intern_hash(hash, rth, rp, NULL, oldflp->oif); + rth = rt_intern_hash(hash, rth, NULL, oldflp->oif); } out: rcu_read_unlock(); - return err; + return rth; } -int __ip_route_output_key(struct net *net, struct rtable **rp, - const struct flowi *flp) +struct rtable *__ip_route_output_key(struct net *net, const struct flowi *flp) { struct rtable *rth; unsigned int hash; @@ -2639,15 +2639,14 @@ int __ip_route_output_key(struct net *net, struct rtable **rp, dst_use(&rth->dst, jiffies); RT_CACHE_STAT_INC(out_hit); rcu_read_unlock_bh(); - *rp = rth; - return 0; + return rth; } RT_CACHE_STAT_INC(out_hlist_search); } rcu_read_unlock_bh(); slow_output: - return ip_route_output_slow(net, rp, flp); + return ip_route_output_slow(net, flp); } EXPORT_SYMBOL_GPL(__ip_route_output_key); @@ -2717,34 +2716,29 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or return rt ? &rt->dst : ERR_PTR(-ENOMEM); } -int ip_route_output_flow(struct net *net, struct rtable **rp, struct flowi *flp, - struct sock *sk) +struct rtable *ip_route_output_flow(struct net *net, struct flowi *flp, + struct sock *sk) { - int err; + struct rtable *rt = __ip_route_output_key(net, flp); - if ((err = __ip_route_output_key(net, rp, flp)) != 0) - return err; + if (IS_ERR(rt)) + return rt; if (flp->proto) { if (!flp->fl4_src) - flp->fl4_src = (*rp)->rt_src; + flp->fl4_src = rt->rt_src; if (!flp->fl4_dst) - flp->fl4_dst = (*rp)->rt_dst; - *rp = (struct rtable *) xfrm_lookup(net, &(*rp)->dst, flp, sk, 0); - if (IS_ERR(*rp)) { - err = PTR_ERR(*rp); - *rp = NULL; - return err; - } + flp->fl4_dst = rt->rt_dst; + rt = (struct rtable *) xfrm_lookup(net, &rt->dst, flp, sk, 0); } - return 0; + return rt; } EXPORT_SYMBOL_GPL(ip_route_output_flow); -int ip_route_output_key(struct net *net, struct rtable **rp, struct flowi *flp) +struct rtable *ip_route_output_key(struct net *net, struct flowi *flp) { - return ip_route_output_flow(net, rp, flp, NULL); + return ip_route_output_flow(net, flp, NULL); } EXPORT_SYMBOL(ip_route_output_key); @@ -2915,7 +2909,11 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void .oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0, .mark = mark, }; - err = ip_route_output_key(net, &rt, &fl); + rt = ip_route_output_key(net, &fl); + + err = 0; + if (IS_ERR(rt)) + err = PTR_ERR(rt); } if (err) diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 47519205a014..0ad6ddf638a7 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -355,7 +355,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, .fl_ip_sport = th->dest, .fl_ip_dport = th->source }; security_req_classify_flow(req, &fl); - if (ip_route_output_key(sock_net(sk), &rt, &fl)) { + rt = ip_route_output_key(sock_net(sk), &fl); + if (IS_ERR(rt)) { reqsk_free(req); goto out; } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 05bc6d9455fc..f7e6c2c2d2bb 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -152,7 +152,6 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) __be16 orig_sport, orig_dport; struct rtable *rt; __be32 daddr, nexthop; - int tmp; int err; if (addr_len < sizeof(struct sockaddr_in)) @@ -170,14 +169,15 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) orig_sport = inet->inet_sport; orig_dport = usin->sin_port; - tmp = ip_route_connect(&rt, nexthop, inet->inet_saddr, - RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, - IPPROTO_TCP, - orig_sport, orig_dport, sk, true); - if (tmp < 0) { - if (tmp == -ENETUNREACH) + rt = ip_route_connect(nexthop, inet->inet_saddr, + RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, + IPPROTO_TCP, + orig_sport, orig_dport, sk, true); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + if (err == -ENETUNREACH) IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); - return tmp; + return err; } if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) { @@ -236,12 +236,14 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (err) goto failure; - err = ip_route_newports(&rt, IPPROTO_TCP, - orig_sport, orig_dport, - inet->inet_sport, inet->inet_dport, sk); - if (err) + rt = ip_route_newports(rt, IPPROTO_TCP, + orig_sport, orig_dport, + inet->inet_sport, inet->inet_dport, sk); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + rt = NULL; goto failure; - + } /* OK, now commit destination to socket. */ sk->sk_gso_type = SKB_GSO_TCPV4; sk_setup_caps(sk, &rt->dst); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index ed9a5b7bee53..95e0c2c194a1 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -922,8 +922,9 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, struct net *net = sock_net(sk); security_sk_classify_flow(sk, &fl); - err = ip_route_output_flow(net, &rt, &fl, sk); - if (err) { + rt = ip_route_output_flow(net, &fl, sk); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); if (err == -ENETUNREACH) IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES); goto out; diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 5f0f058dc376..45b821480427 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -26,18 +26,16 @@ static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, .fl4_dst = daddr->a4, .fl4_tos = tos, }; - struct dst_entry *dst; struct rtable *rt; - int err; if (saddr) fl.fl4_src = saddr->a4; - err = __ip_route_output_key(net, &rt, &fl); - dst = &rt->dst; - if (err) - dst = ERR_PTR(err); - return dst; + rt = __ip_route_output_key(net, &fl); + if (!IS_ERR(rt)) + return &rt->dst; + + return ERR_CAST(rt); } static int xfrm4_get_saddr(struct net *net, diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index da43038ae18e..02730ef26b0f 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -581,7 +581,8 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, fl.fl4_dst = eiph->saddr; fl.fl4_tos = RT_TOS(eiph->tos); fl.proto = IPPROTO_IPIP; - if (ip_route_output_key(dev_net(skb->dev), &rt, &fl)) + rt = ip_route_output_key(dev_net(skb->dev), &fl); + if (IS_ERR(rt)) goto out; skb2->dev = rt->dst.dev; @@ -593,12 +594,14 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, fl.fl4_dst = eiph->daddr; fl.fl4_src = eiph->saddr; fl.fl4_tos = eiph->tos; - if (ip_route_output_key(dev_net(skb->dev), &rt, &fl) || + rt = ip_route_output_key(dev_net(skb->dev), &fl); + if (IS_ERR(rt) || rt->dst.dev->type != ARPHRD_TUNNEL) { - ip_rt_put(rt); + if (!IS_ERR(rt)) + ip_rt_put(rt); goto out; } - skb_dst_set(skb2, (struct dst_entry *)rt); + skb_dst_set(skb2, &rt->dst); } else { ip_rt_put(rt); if (ip_route_input(skb2, eiph->daddr, eiph->saddr, eiph->tos, diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index b1599a345c10..b8c8adbd7cf6 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -738,7 +738,8 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, .fl4_tos = RT_TOS(tos), .oif = tunnel->parms.link, .proto = IPPROTO_IPV6 }; - if (ip_route_output_key(dev_net(dev), &rt, &fl)) { + rt = ip_route_output_key(dev_net(dev), &fl); + if (IS_ERR(rt)) { dev->stats.tx_carrier_errors++; goto tx_error_icmp; } @@ -862,8 +863,9 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev) .fl4_tos = RT_TOS(iph->tos), .oif = tunnel->parms.link, .proto = IPPROTO_IPV6 }; - struct rtable *rt; - if (!ip_route_output_key(dev_net(dev), &rt, &fl)) { + struct rtable *rt = ip_route_output_key(dev_net(dev), &fl); + + if (!IS_ERR(rt)) { tdev = rt->dst.dev; ip_rt_put(rt); } diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 5381cebe516d..2a698ff89db6 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -320,11 +320,12 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len if (ipv4_is_multicast(lsa->l2tp_addr.s_addr)) goto out; - rc = ip_route_connect(&rt, lsa->l2tp_addr.s_addr, saddr, + rt = ip_route_connect(lsa->l2tp_addr.s_addr, saddr, RT_CONN_FLAGS(sk), oif, IPPROTO_L2TP, 0, 0, sk, true); - if (rc) { + if (IS_ERR(rt)) { + rc = PTR_ERR(rt); if (rc == -ENETUNREACH) IP_INC_STATS_BH(&init_net, IPSTATS_MIB_OUTNOROUTES); goto out; @@ -489,7 +490,8 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m * itself out. */ security_sk_classify_flow(sk, &fl); - if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk)) + rt = ip_route_output_flow(sock_net(sk), &fl, sk); + if (IS_ERR(rt)) goto no_route; } sk_setup_caps(sk, &rt->dst); diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 6264219f0a42..878f6dd9dbad 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -103,7 +103,8 @@ __ip_vs_get_out_rt(struct sk_buff *skb, struct ip_vs_dest *dest, .fl4_tos = rtos, }; - if (ip_route_output_key(net, &rt, &fl)) { + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) { spin_unlock(&dest->dst_lock); IP_VS_DBG_RL("ip_route_output error, dest: %pI4\n", &dest->addr.ip); @@ -121,7 +122,8 @@ __ip_vs_get_out_rt(struct sk_buff *skb, struct ip_vs_dest *dest, .fl4_tos = rtos, }; - if (ip_route_output_key(net, &rt, &fl)) { + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) { IP_VS_DBG_RL("ip_route_output error, dest: %pI4\n", &daddr); return NULL; @@ -180,7 +182,8 @@ __ip_vs_reroute_locally(struct sk_buff *skb) .mark = skb->mark, }; - if (ip_route_output_key(net, &rt, &fl)) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) return 0; if (!(rt->rt_flags & RTCF_LOCAL)) { ip_rt_put(rt); diff --git a/net/netfilter/xt_TEE.c b/net/netfilter/xt_TEE.c index 5128a6c4cb2c..624725b5286f 100644 --- a/net/netfilter/xt_TEE.c +++ b/net/netfilter/xt_TEE.c @@ -73,7 +73,8 @@ tee_tg_route4(struct sk_buff *skb, const struct xt_tee_tginfo *info) fl.fl4_dst = info->gw.ip; fl.fl4_tos = RT_TOS(iph->tos); fl.fl4_scope = RT_SCOPE_UNIVERSE; - if (ip_route_output_key(net, &rt, &fl) != 0) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) return false; skb_dst_drop(skb); diff --git a/net/rxrpc/ar-peer.c b/net/rxrpc/ar-peer.c index a53fb25a64ed..3620c569275f 100644 --- a/net/rxrpc/ar-peer.c +++ b/net/rxrpc/ar-peer.c @@ -37,7 +37,6 @@ static void rxrpc_assess_MTU_size(struct rxrpc_peer *peer) { struct rtable *rt; struct flowi fl; - int ret; peer->if_mtu = 1500; @@ -58,9 +57,9 @@ static void rxrpc_assess_MTU_size(struct rxrpc_peer *peer) BUG(); } - ret = ip_route_output_key(&init_net, &rt, &fl); - if (ret < 0) { - _leave(" [route err %d]", ret); + rt = ip_route_output_key(&init_net, &fl); + if (IS_ERR(rt)) { + _leave(" [route err %ld]", PTR_ERR(rt)); return; } diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index e58f9476f29c..4e55e6c49ec9 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -491,9 +491,9 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, SCTP_DEBUG_PRINTK("%s: DST:%pI4, SRC:%pI4 - ", __func__, &fl.fl4_dst, &fl.fl4_src); - if (!ip_route_output_key(&init_net, &rt, &fl)) { + rt = ip_route_output_key(&init_net, &fl); + if (!IS_ERR(rt)) dst = &rt->dst; - } /* If there is no association or if a source address is passed, no * more validation is required. @@ -535,7 +535,8 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, (AF_INET == laddr->a.sa.sa_family)) { fl.fl4_src = laddr->a.v4.sin_addr.s_addr; fl.fl_ip_sport = laddr->a.v4.sin_port; - if (!ip_route_output_key(&init_net, &rt, &fl)) { + rt = ip_route_output_key(&init_net, &fl); + if (!IS_ERR(rt)) { dst = &rt->dst; goto out_unlock; } -- cgit v1.2.3 From 5bfa787fb2c29cce0722500f90df29e049ff07fc Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 2 Mar 2011 14:56:30 -0800 Subject: ipv4: ip_route_output_key() is better as an inline. This avoid a stack frame at zero cost. Signed-off-by: David S. Miller --- include/net/route.h | 6 +++++- net/ipv4/route.c | 6 ------ 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/route.h b/include/net/route.h index 088a1867348f..60daf745216a 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -119,11 +119,15 @@ extern void ip_rt_redirect(__be32 old_gw, __be32 dst, __be32 new_gw, extern void rt_cache_flush(struct net *net, int how); extern void rt_cache_flush_batch(struct net *net); extern struct rtable *__ip_route_output_key(struct net *, const struct flowi *flp); -extern struct rtable *ip_route_output_key(struct net *, struct flowi *flp); extern struct rtable *ip_route_output_flow(struct net *, struct flowi *flp, struct sock *sk); extern struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_orig); +static inline struct rtable *ip_route_output_key(struct net *net, struct flowi *flp) +{ + return ip_route_output_flow(net, flp, NULL); +} + extern int ip_route_input_common(struct sk_buff *skb, __be32 dst, __be32 src, u8 tos, struct net_device *devin, bool noref); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 5090e956f6b8..432eee645648 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2736,12 +2736,6 @@ struct rtable *ip_route_output_flow(struct net *net, struct flowi *flp, } EXPORT_SYMBOL_GPL(ip_route_output_flow); -struct rtable *ip_route_output_key(struct net *net, struct flowi *flp) -{ - return ip_route_output_flow(net, flp, NULL); -} -EXPORT_SYMBOL(ip_route_output_key); - static int rt_fill_info(struct net *net, struct sk_buff *skb, u32 pid, u32 seq, int event, int nowait, unsigned int flags) -- cgit v1.2.3 From da527770007fce8e4541947d47918248286da875 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 2 Mar 2011 19:05:33 -0500 Subject: block: Move blk_throtl_exit() call to blk_cleanup_queue() Move blk_throtl_exit() in blk_cleanup_queue() as blk_throtl_exit() is written in such a way that it needs queue lock. In blk_release_queue() there is no gurantee that ->queue_lock is still around. Initially blk_throtl_exit() was in blk_cleanup_queue() but Ingo reported one problem. https://lkml.org/lkml/2010/10/23/86 And a quick fix moved blk_throtl_exit() to blk_release_queue(). commit 7ad58c028652753814054f4e3ac58f925e7343f4 Author: Jens Axboe Date: Sat Oct 23 20:40:26 2010 +0200 block: fix use-after-free bug in blk throttle code This patch reverts above change and does not try to shutdown the throtl work in blk_sync_queue(). By avoiding call to throtl_shutdown_timer_wq() from blk_sync_queue(), we should also avoid the problem reported by Ingo. blk_sync_queue() seems to be used only by md driver and it seems to be using it to make sure q->unplug_fn is not called as md registers its own unplug functions and it is about to free up the data structures used by unplug_fn(). Block throttle does not call back into unplug_fn() or into md. So there is no need to cancel blk throttle work. In fact I think cancelling block throttle work is bad because it might happen that some bios are throttled and scheduled to be dispatched later with the help of pending work and if work is cancelled, these bios might never be dispatched. Block layer also uses blk_sync_queue() during blk_cleanup_queue() and blk_release_queue() time. That should be safe as we are also calling blk_throtl_exit() which should make sure all the throttling related data structures are cleaned up. Signed-off-by: Vivek Goyal Signed-off-by: Jens Axboe --- block/blk-core.c | 7 ++++++- block/blk-sysfs.c | 2 -- block/blk-throttle.c | 6 +++--- include/linux/blkdev.h | 2 -- 4 files changed, 9 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index bc2b7c5004e1..accff29ad674 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -380,13 +380,16 @@ EXPORT_SYMBOL(blk_stop_queue); * that its ->make_request_fn will not re-add plugging prior to calling * this function. * + * This function does not cancel any asynchronous activity arising + * out of elevator or throttling code. That would require elevaotor_exit() + * and blk_throtl_exit() to be called with queue lock initialized. + * */ void blk_sync_queue(struct request_queue *q) { del_timer_sync(&q->unplug_timer); del_timer_sync(&q->timeout); cancel_work_sync(&q->unplug_work); - throtl_shutdown_timer_wq(q); } EXPORT_SYMBOL(blk_sync_queue); @@ -469,6 +472,8 @@ void blk_cleanup_queue(struct request_queue *q) if (q->elevator) elevator_exit(q->elevator); + blk_throtl_exit(q); + blk_put_queue(q); } EXPORT_SYMBOL(blk_cleanup_queue); diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 41fb69150b4d..261c75c665ae 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -471,8 +471,6 @@ static void blk_release_queue(struct kobject *kobj) blk_sync_queue(q); - blk_throtl_exit(q); - if (rl->rq_pool) mempool_destroy(rl->rq_pool); diff --git a/block/blk-throttle.c b/block/blk-throttle.c index a89043a3caa4..c0f623742165 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -965,7 +965,7 @@ static void throtl_update_blkio_group_write_iops(void *key, throtl_schedule_delayed_work(td->queue, 0); } -void throtl_shutdown_timer_wq(struct request_queue *q) +static void throtl_shutdown_wq(struct request_queue *q) { struct throtl_data *td = q->td; @@ -1099,7 +1099,7 @@ void blk_throtl_exit(struct request_queue *q) BUG_ON(!td); - throtl_shutdown_timer_wq(q); + throtl_shutdown_wq(q); spin_lock_irq(q->queue_lock); throtl_release_tgs(td); @@ -1129,7 +1129,7 @@ void blk_throtl_exit(struct request_queue *q) * update limits through cgroup and another work got queued, cancel * it. */ - throtl_shutdown_timer_wq(q); + throtl_shutdown_wq(q); throtl_td_free(td); } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index e3ee74fc5903..23fb92506c31 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1144,7 +1144,6 @@ extern int blk_throtl_init(struct request_queue *q); extern void blk_throtl_exit(struct request_queue *q); extern int blk_throtl_bio(struct request_queue *q, struct bio **bio); extern void throtl_schedule_delayed_work(struct request_queue *q, unsigned long delay); -extern void throtl_shutdown_timer_wq(struct request_queue *q); #else /* CONFIG_BLK_DEV_THROTTLING */ static inline int blk_throtl_bio(struct request_queue *q, struct bio **bio) { @@ -1154,7 +1153,6 @@ static inline int blk_throtl_bio(struct request_queue *q, struct bio **bio) static inline int blk_throtl_init(struct request_queue *q) { return 0; } static inline int blk_throtl_exit(struct request_queue *q) { return 0; } static inline void throtl_schedule_delayed_work(struct request_queue *q, unsigned long delay) {} -static inline void throtl_shutdown_timer_wq(struct request_queue *q) {} #endif /* CONFIG_BLK_DEV_THROTTLING */ #define MODULE_ALIAS_BLOCKDEV(major,minor) \ -- cgit v1.2.3 From 0a0d128d023a49eb8afa0e988e6773416318a0b5 Mon Sep 17 00:00:00 2001 From: Ilkka Koskinen Date: Wed, 2 Mar 2011 13:24:06 +0000 Subject: mfd: twl4030_codec: Remove unused and duplicate audio_mclk fields audio_mclk can be queried from mfd driver. Therefore, it is not needed in twl4030_codec_audio_data or in twl4030_codec_vibra_data anymore. Signed-off-by: Ilkka Koskinen Acked-by: Peter Ujfalusi Acked-by: Mark Brown Acked-by: Samuel Ortiz Signed-off-by: Tony Lindgren --- include/linux/i2c/twl.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 61b9609e55f2..9d88b717111a 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -637,7 +637,6 @@ extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts); extern int twl4030_remove_script(u8 flags); struct twl4030_codec_audio_data { - unsigned int audio_mclk; /* not used, will be removed */ unsigned int digimic_delay; /* in ms */ unsigned int ramp_delay_value; unsigned int offset_cncl_path; @@ -648,7 +647,6 @@ struct twl4030_codec_audio_data { }; struct twl4030_codec_vibra_data { - unsigned int audio_mclk; unsigned int coexist; }; -- cgit v1.2.3 From 64bc5524906e31c1144af29ba50c585afe333bb3 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 2 Mar 2011 20:07:41 -0500 Subject: drm/radeon/kms: add cayman pci ids Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie --- include/drm/drm_pciids.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include') diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index 5ff1194dc2ea..820ee9029482 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -141,6 +141,20 @@ {0x1002, 0x5e4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \ {0x1002, 0x5e4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \ {0x1002, 0x5e4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6700, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6701, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6702, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6703, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6704, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6705, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6706, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6707, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6708, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6709, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6718, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6719, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x671c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x671d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6720, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6721, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6722, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \ -- cgit v1.2.3 From 23b41168fc942a4a041325a04ecc1bd17d031a3e Mon Sep 17 00:00:00 2001 From: Vlad Dogaru Date: Sat, 26 Feb 2011 22:39:12 +0000 Subject: netdevice: make initial group visible to userspace INIT_NETDEV_GROUP is needed by userspace, move it outside __KERNEL__ guards. Signed-off-by: Vlad Dogaru Signed-off-by: David S. Miller --- include/linux/netdevice.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ffe56c16df8a..8be4056ad251 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -75,9 +75,6 @@ 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: @@ -141,6 +138,9 @@ static inline bool dev_xmit_complete(int rc) #define MAX_ADDR_LEN 32 /* Largest hardware address length */ +/* Initial net device group. All devices belong to group 0 by default. */ +#define INIT_NETDEV_GROUP 0 + #ifdef __KERNEL__ /* * Compute the worst case header length according to the protocols -- cgit v1.2.3 From eed84713bc47ce2f7d675914f297ad9b6227a587 Mon Sep 17 00:00:00 2001 From: Shmulik Ravid Date: Sun, 27 Feb 2011 05:04:31 +0000 Subject: dcbnl: add support for retrieving peer configuration - ieee These 2 patches add the support for retrieving the remote or peer DCBX configuration via dcbnl for embedded DCBX stacks. The peer configuration is part of the DCBX MIB and is useful for debugging and diagnostics of the overall DCB configuration. The first patch add this support for IEEE 802.1Qaz standard the second patch add the same support for the older CEE standard. Diff for v2 - the peer-app-info is CEE specific. Signed-off-by: Shmulik Ravid Signed-off-by: David S. Miller --- include/linux/dcbnl.h | 28 +++++++++++++++++++++ include/net/dcbnl.h | 6 +++++ net/dcb/dcbnl.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) (limited to 'include') diff --git a/include/linux/dcbnl.h b/include/linux/dcbnl.h index 4c5b26e0cc48..2542685f8b3e 100644 --- a/include/linux/dcbnl.h +++ b/include/linux/dcbnl.h @@ -110,6 +110,20 @@ struct dcb_app { __u16 protocol; }; +/** + * struct dcb_peer_app_info - APP feature information sent by the peer + * + * @willing: willing bit in the peer APP tlv + * @error: error bit in the peer APP tlv + * + * In addition to this information the full peer APP tlv also contains + * a table of 'app_count' APP objects defined above. + */ +struct dcb_peer_app_info { + __u8 willing; + __u8 error; +}; + struct dcbmsg { __u8 dcb_family; __u8 cmd; @@ -235,11 +249,25 @@ enum dcbnl_attrs { DCB_ATTR_MAX = __DCB_ATTR_ENUM_MAX - 1, }; +/** + * enum ieee_attrs - IEEE 802.1Qaz get/set attributes + * + * @DCB_ATTR_IEEE_UNSPEC: unspecified + * @DCB_ATTR_IEEE_ETS: negotiated ETS configuration + * @DCB_ATTR_IEEE_PFC: negotiated PFC configuration + * @DCB_ATTR_IEEE_APP_TABLE: negotiated APP configuration + * @DCB_ATTR_IEEE_PEER_ETS: peer ETS configuration - get only + * @DCB_ATTR_IEEE_PEER_PFC: peer PFC configuration - get only + * @DCB_ATTR_IEEE_PEER_APP: peer APP tlv - get only + */ enum ieee_attrs { DCB_ATTR_IEEE_UNSPEC, DCB_ATTR_IEEE_ETS, DCB_ATTR_IEEE_PFC, DCB_ATTR_IEEE_APP_TABLE, + DCB_ATTR_IEEE_PEER_ETS, + DCB_ATTR_IEEE_PEER_PFC, + DCB_ATTR_IEEE_PEER_APP, __DCB_ATTR_IEEE_MAX }; #define DCB_ATTR_IEEE_MAX (__DCB_ATTR_IEEE_MAX - 1) diff --git a/include/net/dcbnl.h b/include/net/dcbnl.h index a8e7852b10ab..7b7180e692ef 100644 --- a/include/net/dcbnl.h +++ b/include/net/dcbnl.h @@ -43,6 +43,8 @@ struct dcbnl_rtnl_ops { int (*ieee_setpfc) (struct net_device *, struct ieee_pfc *); int (*ieee_getapp) (struct net_device *, struct dcb_app *); int (*ieee_setapp) (struct net_device *, struct dcb_app *); + int (*ieee_peer_getets) (struct net_device *, struct ieee_ets *); + int (*ieee_peer_getpfc) (struct net_device *, struct ieee_pfc *); /* CEE std */ u8 (*getstate)(struct net_device *); @@ -77,6 +79,10 @@ struct dcbnl_rtnl_ops { u8 (*getdcbx)(struct net_device *); u8 (*setdcbx)(struct net_device *, u8); + /* peer apps */ + int (*peer_getappinfo)(struct net_device *, struct dcb_peer_app_info *, + u16 *); + int (*peer_getapptable)(struct net_device *, struct dcb_app *); }; diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index d5074a567289..2e6dcf2967e2 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -1224,6 +1224,54 @@ err: return err; } +static int dcbnl_build_peer_app(struct net_device *netdev, struct sk_buff* skb) +{ + struct dcb_peer_app_info info; + struct dcb_app *table = NULL; + const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; + u16 app_count; + int err; + + + /** + * retrieve the peer app configuration form the driver. If the driver + * handlers fail exit without doing anything + */ + err = ops->peer_getappinfo(netdev, &info, &app_count); + if (!err && app_count) { + table = kmalloc(sizeof(struct dcb_app) * app_count, GFP_KERNEL); + if (!table) + return -ENOMEM; + + err = ops->peer_getapptable(netdev, table); + } + + if (!err) { + u16 i; + struct nlattr *app; + + /** + * build the message, from here on the only possible failure + * is due to the skb size + */ + err = -EMSGSIZE; + + app = nla_nest_start(skb, DCB_ATTR_IEEE_PEER_APP); + if (!app) + goto nla_put_failure; + + for (i = 0; i < app_count; i++) + NLA_PUT(skb, DCB_ATTR_IEEE_APP, sizeof(struct dcb_app), + &table[i]); + + nla_nest_end(skb, app); + } + err = 0; + +nla_put_failure: + kfree(table); + return err; +} /* Handle IEEE 802.1Qaz GET commands. */ static int dcbnl_ieee_get(struct net_device *netdev, struct nlattr **tb, @@ -1288,6 +1336,27 @@ static int dcbnl_ieee_get(struct net_device *netdev, struct nlattr **tb, spin_unlock(&dcb_lock); nla_nest_end(skb, app); + /* get peer info if available */ + if (ops->ieee_peer_getets) { + struct ieee_ets ets; + err = ops->ieee_peer_getets(netdev, &ets); + if (!err) + NLA_PUT(skb, DCB_ATTR_IEEE_PEER_ETS, sizeof(ets), &ets); + } + + if (ops->ieee_peer_getpfc) { + struct ieee_pfc pfc; + err = ops->ieee_peer_getpfc(netdev, &pfc); + if (!err) + NLA_PUT(skb, DCB_ATTR_IEEE_PEER_PFC, sizeof(pfc), &pfc); + } + + if (ops->peer_getappinfo && ops->peer_getapptable) { + err = dcbnl_build_peer_app(netdev, skb); + if (err) + goto nla_put_failure; + } + nla_nest_end(skb, ieee); nlmsg_end(skb, nlh); -- cgit v1.2.3 From dc6ed1df5a5f84e45e77e2acb6fd99b995414956 Mon Sep 17 00:00:00 2001 From: Shmulik Ravid Date: Sun, 27 Feb 2011 05:04:38 +0000 Subject: dcbnl: add support for retrieving peer configuration - cee This patch adds the support for retrieving the remote or peer DCBX configuration via dcbnl for embedded DCBX stacks supporting the CEE DCBX standard. Signed-off-by: Shmulik Ravid Signed-off-by: David S. Miller --- include/linux/dcbnl.h | 71 ++++++++++++++++++++++++++++++++++++++++++ include/net/dcbnl.h | 3 ++ net/dcb/dcbnl.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 155 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/dcbnl.h b/include/linux/dcbnl.h index 2542685f8b3e..a3680a16718f 100644 --- a/include/linux/dcbnl.h +++ b/include/linux/dcbnl.h @@ -87,6 +87,45 @@ struct ieee_pfc { __u64 indications[IEEE_8021QAZ_MAX_TCS]; }; +/* CEE DCBX std supported values */ +#define CEE_DCBX_MAX_PGS 8 +#define CEE_DCBX_MAX_PRIO 8 + +/** + * struct cee_pg - CEE Prioity-Group managed object + * + * @willing: willing bit in the PG tlv + * @error: error bit in the PG tlv + * @pg_en: enable bit of the PG feature + * @tcs_supported: number of traffic classes supported + * @pg_bw: bandwidth percentage for each priority group + * @prio_pg: priority to PG mapping indexed by priority + */ +struct cee_pg { + __u8 willing; + __u8 error; + __u8 pg_en; + __u8 tcs_supported; + __u8 pg_bw[CEE_DCBX_MAX_PGS]; + __u8 prio_pg[CEE_DCBX_MAX_PGS]; +}; + +/** + * struct cee_pfc - CEE PFC managed object + * + * @willing: willing bit in the PFC tlv + * @error: error bit in the PFC tlv + * @pfc_en: bitmap indicating pfc enabled traffic classes + * @tcs_supported: number of traffic classes supported + */ +struct cee_pfc { + __u8 willing; + __u8 error; + __u8 pfc_en; + __u8 tcs_supported; +}; + + /* This structure contains the IEEE 802.1Qaz APP managed object. This * object is also used for the CEE std as well. There is no difference * between the objects. @@ -158,6 +197,7 @@ struct dcbmsg { * @DCB_CMD_SDCBX: set DCBX engine configuration * @DCB_CMD_GFEATCFG: get DCBX features flags * @DCB_CMD_SFEATCFG: set DCBX features negotiation flags + * @DCB_CMD_CEE_GET: get CEE aggregated configuration */ enum dcbnl_commands { DCB_CMD_UNDEFINED, @@ -200,6 +240,8 @@ enum dcbnl_commands { DCB_CMD_GFEATCFG, DCB_CMD_SFEATCFG, + DCB_CMD_CEE_GET, + __DCB_CMD_ENUM_MAX, DCB_CMD_MAX = __DCB_CMD_ENUM_MAX - 1, }; @@ -222,6 +264,7 @@ enum dcbnl_commands { * @DCB_ATTR_IEEE: IEEE 802.1Qaz supported attributes (NLA_NESTED) * @DCB_ATTR_DCBX: DCBX engine configuration in the device (NLA_U8) * @DCB_ATTR_FEATCFG: DCBX features flags (NLA_NESTED) + * @DCB_ATTR_CEE: CEE std supported attributes (NLA_NESTED) */ enum dcbnl_attrs { DCB_ATTR_UNDEFINED, @@ -245,6 +288,9 @@ enum dcbnl_attrs { DCB_ATTR_DCBX, DCB_ATTR_FEATCFG, + /* CEE nested attributes */ + DCB_ATTR_CEE, + __DCB_ATTR_ENUM_MAX, DCB_ATTR_MAX = __DCB_ATTR_ENUM_MAX - 1, }; @@ -279,6 +325,31 @@ enum ieee_attrs_app { }; #define DCB_ATTR_IEEE_APP_MAX (__DCB_ATTR_IEEE_APP_MAX - 1) +/** + * enum cee_attrs - CEE DCBX get attributes + * + * @DCB_ATTR_CEE_UNSPEC: unspecified + * @DCB_ATTR_CEE_PEER_PG: peer PG configuration - get only + * @DCB_ATTR_CEE_PEER_PFC: peer PFC configuration - get only + * @DCB_ATTR_CEE_PEER_APP: peer APP tlv - get only + */ +enum cee_attrs { + DCB_ATTR_CEE_UNSPEC, + DCB_ATTR_CEE_PEER_PG, + DCB_ATTR_CEE_PEER_PFC, + DCB_ATTR_CEE_PEER_APP_TABLE, + __DCB_ATTR_CEE_MAX +}; +#define DCB_ATTR_CEE_MAX (__DCB_ATTR_CEE_MAX - 1) + +enum peer_app_attr { + DCB_ATTR_CEE_PEER_APP_UNSPEC, + DCB_ATTR_CEE_PEER_APP_INFO, + DCB_ATTR_CEE_PEER_APP, + __DCB_ATTR_CEE_PEER_APP_MAX +}; +#define DCB_ATTR_CEE_PEER_APP_MAX (__DCB_ATTR_CEE_PEER_APP_MAX - 1) + /** * enum dcbnl_pfc_attrs - DCB Priority Flow Control user priority nested attrs * diff --git a/include/net/dcbnl.h b/include/net/dcbnl.h index 7b7180e692ef..e5983c9053dc 100644 --- a/include/net/dcbnl.h +++ b/include/net/dcbnl.h @@ -84,6 +84,9 @@ struct dcbnl_rtnl_ops { u16 *); int (*peer_getapptable)(struct net_device *, struct dcb_app *); + /* CEE peer */ + int (*cee_peer_getpg) (struct net_device *, struct cee_pg *); + int (*cee_peer_getpfc) (struct net_device *, struct cee_pfc *); }; #endif /* __NET_DCBNL_H__ */ diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index 2e6dcf2967e2..d8b4f725b778 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -1224,7 +1224,9 @@ err: return err; } -static int dcbnl_build_peer_app(struct net_device *netdev, struct sk_buff* skb) +static int dcbnl_build_peer_app(struct net_device *netdev, struct sk_buff* skb, + int app_nested_type, int app_info_type, + int app_entry_type) { struct dcb_peer_app_info info; struct dcb_app *table = NULL; @@ -1256,12 +1258,15 @@ static int dcbnl_build_peer_app(struct net_device *netdev, struct sk_buff* skb) */ err = -EMSGSIZE; - app = nla_nest_start(skb, DCB_ATTR_IEEE_PEER_APP); + app = nla_nest_start(skb, app_nested_type); if (!app) goto nla_put_failure; + if (app_info_type) + NLA_PUT(skb, app_info_type, sizeof(info), &info); + for (i = 0; i < app_count; i++) - NLA_PUT(skb, DCB_ATTR_IEEE_APP, sizeof(struct dcb_app), + NLA_PUT(skb, app_entry_type, sizeof(struct dcb_app), &table[i]); nla_nest_end(skb, app); @@ -1352,7 +1357,10 @@ static int dcbnl_ieee_get(struct net_device *netdev, struct nlattr **tb, } if (ops->peer_getappinfo && ops->peer_getapptable) { - err = dcbnl_build_peer_app(netdev, skb); + err = dcbnl_build_peer_app(netdev, skb, + DCB_ATTR_IEEE_PEER_APP, + DCB_ATTR_IEEE_APP_UNSPEC, + DCB_ATTR_IEEE_APP); if (err) goto nla_put_failure; } @@ -1510,6 +1518,71 @@ err: return ret; } +/* Handle CEE DCBX GET commands. */ +static int dcbnl_cee_get(struct net_device *netdev, struct nlattr **tb, + u32 pid, u32 seq, u16 flags) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + struct dcbmsg *dcb; + struct nlattr *cee; + const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; + int err; + + if (!ops) + return -EOPNOTSUPP; + + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!skb) + return -ENOBUFS; + + nlh = NLMSG_NEW(skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); + + dcb = NLMSG_DATA(nlh); + dcb->dcb_family = AF_UNSPEC; + dcb->cmd = DCB_CMD_CEE_GET; + + NLA_PUT_STRING(skb, DCB_ATTR_IFNAME, netdev->name); + + cee = nla_nest_start(skb, DCB_ATTR_CEE); + if (!cee) + goto nla_put_failure; + + /* get peer info if available */ + if (ops->cee_peer_getpg) { + struct cee_pg pg; + err = ops->cee_peer_getpg(netdev, &pg); + if (!err) + NLA_PUT(skb, DCB_ATTR_CEE_PEER_PG, sizeof(pg), &pg); + } + + if (ops->cee_peer_getpfc) { + struct cee_pfc pfc; + err = ops->cee_peer_getpfc(netdev, &pfc); + if (!err) + NLA_PUT(skb, DCB_ATTR_CEE_PEER_PFC, sizeof(pfc), &pfc); + } + + if (ops->peer_getappinfo && ops->peer_getapptable) { + err = dcbnl_build_peer_app(netdev, skb, + DCB_ATTR_CEE_PEER_APP_TABLE, + DCB_ATTR_CEE_PEER_APP_INFO, + DCB_ATTR_CEE_PEER_APP); + if (err) + goto nla_put_failure; + } + + nla_nest_end(skb, cee); + nlmsg_end(skb, nlh); + + return rtnl_unicast(skb, &init_net, pid); +nla_put_failure: + nlmsg_cancel(skb, nlh); +nlmsg_failure: + kfree_skb(skb); + return -1; +} + static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct net *net = sock_net(skb->sk); @@ -1639,6 +1712,10 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) ret = dcbnl_setfeatcfg(netdev, tb, pid, nlh->nlmsg_seq, nlh->nlmsg_flags); goto out; + case DCB_CMD_CEE_GET: + ret = dcbnl_cee_get(netdev, tb, pid, nlh->nlmsg_seq, + nlh->nlmsg_flags); + goto out; default: goto errout; } -- cgit v1.2.3 From f009918a1c1bbf8607b8aab3959876913a30193a Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 28 Feb 2011 03:27:53 +0000 Subject: RxRPC: Fix v1 keys commit 339412841d7 (RxRPC: Allow key payloads to be passed in XDR form) broke klog for me. I notice the v1 key struct had a kif_version field added: -struct rxkad_key { - u16 security_index; /* RxRPC header security index */ - u16 ticket_len; /* length of ticket[] */ - u32 expiry; /* time at which expires */ - u32 kvno; /* key version number */ - u8 session_key[8]; /* DES session key */ - u8 ticket[0]; /* the encrypted ticket */ -}; +struct rxrpc_key_data_v1 { + u32 kif_version; /* 1 */ + u16 security_index; + u16 ticket_length; + u32 expiry; /* time_t */ + u32 kvno; + u8 session_key[8]; + u8 ticket[0]; +}; However the code in rxrpc_instantiate strips it away: data += sizeof(kver); datalen -= sizeof(kver); Removing kif_version fixes my problem. Signed-off-by: Anton Blanchard Signed-off-by: David Howells Signed-off-by: David S. Miller --- include/keys/rxrpc-type.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/keys/rxrpc-type.h b/include/keys/rxrpc-type.h index 5cb86c307f5d..fc4875433817 100644 --- a/include/keys/rxrpc-type.h +++ b/include/keys/rxrpc-type.h @@ -99,7 +99,6 @@ struct rxrpc_key_token { * structure of raw payloads passed to add_key() or instantiate key */ struct rxrpc_key_data_v1 { - u32 kif_version; /* 1 */ u16 security_index; u16 ticket_length; u32 expiry; /* time_t */ -- cgit v1.2.3 From e37a4970cd7ab6aec9e848cd3c355fd47fd18afd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Mar 2011 18:21:57 +0000 Subject: ASoC: Add a per-card DAPM context This means that rather than adding the board specific DAPM widgets to a random CODEC DAPM context they can be added to the card itself which is a bit cleaner. Previously there only was one DAPM context and it was tied to the single supported CODEC. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 3 +++ sound/soc/soc-core.c | 13 +++++++++++++ 2 files changed, 16 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 65d865f7e8c0..8064cd130356 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -729,6 +729,9 @@ struct snd_soc_card { struct list_head paths; struct list_head dapm_list; + /* Generic DAPM context for the card */ + struct snd_soc_dapm_context dapm; + #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_card_root; struct dentry *debugfs_pop_time; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 64befac3f9c3..24bfc3ff8e17 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1837,6 +1837,11 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } card->snd_card->dev = card->dev; + card->dapm.bias_level = SND_SOC_BIAS_OFF; + card->dapm.dev = card->dev; + card->dapm.card = card; + list_add(&card->dapm.list, &card->dapm_list); + #ifdef CONFIG_PM_SLEEP /* deferred resume work */ INIT_WORK(&card->deferred_resume_work, soc_resume_deferred); @@ -1867,6 +1872,14 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } } + card->dapm.debugfs_dapm = debugfs_create_dir("dapm", + card->debugfs_card_root); + if (!card->dapm.debugfs_dapm) + printk(KERN_WARNING + "Failed to create card DAPM debugfs directory\n"); + + snd_soc_dapm_debugfs_init(&card->dapm); + snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname), "%s", card->name); snprintf(card->snd_card->longname, sizeof(card->snd_card->longname), -- cgit v1.2.3 From b8ad29debd7401d257da923480d32838172c431a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Mar 2011 18:35:51 +0000 Subject: ASoC: Allow card DAPM widgets and routes to be set up at registration These will be added after all devices are registered and allow most DAI init functions in machine drivers to be replaced by simple data. Regular controls are not supported as the registration function still works in terms of CODECs. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 8 ++++++++ sound/soc/soc-core.c | 7 +++++++ 2 files changed, 15 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 8064cd130356..11d59bd13886 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -718,6 +718,14 @@ struct snd_soc_card { struct snd_soc_pcm_runtime *rtd_aux; int num_aux_rtd; + /* + * Card-specific routes and widgets. + */ + struct snd_soc_dapm_widget *dapm_widgets; + int num_dapm_widgets; + struct snd_soc_dapm_route *dapm_routes; + int num_dapm_routes; + struct work_struct deferred_resume_work; /* lists of probed devices belonging to this card */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 24bfc3ff8e17..6a2839c18447 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1872,6 +1872,13 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } } + if (card->dapm_widgets) + snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, + card->num_dapm_widgets); + if (card->dapm_routes) + snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, + card->num_dapm_routes); + card->dapm.debugfs_dapm = debugfs_create_dir("dapm", card->debugfs_card_root); if (!card->dapm.debugfs_dapm) -- cgit v1.2.3 From 28e9ad921d3b7defd8940a3e30e8241c8ed734db Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Mar 2011 18:36:34 +0000 Subject: ASoC: Add a late_probe() callback to cards This is run after the DAPM widgets and routes are added, allowing setup of things like jacks using the routes. The main card probe() is run before anything else so can't be used for this purpose. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 1 + sound/soc/soc-core.c | 9 +++++++++ 2 files changed, 10 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 11d59bd13886..9c2a6dd170f1 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -682,6 +682,7 @@ struct snd_soc_card { bool instantiated; int (*probe)(struct snd_soc_card *card); + int (*late_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 diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6a2839c18447..8926d38fc5a3 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1892,6 +1892,15 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) snprintf(card->snd_card->longname, sizeof(card->snd_card->longname), "%s", card->name); + if (card->late_probe) { + ret = card->late_probe(card); + if (ret < 0) { + dev_err(card->dev, "%s late_probe() failed: %d\n", + card->name, ret); + goto probe_aux_dev_err; + } + } + ret = snd_card_register(card->snd_card); if (ret < 0) { printk(KERN_ERR "asoc: failed to register soundcard for %s\n", card->name); -- cgit v1.2.3 From 2d3a8497f8cc5aca14b722cd37d51f6c15ff9f74 Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Thu, 3 Mar 2011 10:53:20 -0500 Subject: blktrace: Remove blk_fill_rwbs_rq. If we enable trace events to trace block actions, We use blk_fill_rwbs_rq to analyze the corresponding actions in request's cmd_flags, but we only choose the minor 2 bits from it, so most of other flags(e.g, REQ_SYNC) are missing. For example, with a sync write we get: write_test-2409 [001] 160.013869: block_rq_insert: 3,64 W 0 () 258135 + = 8 [write_test] Since now we have integrated the flags of both bio and request, it is safe to pass rq->cmd_flags directly to blk_fill_rwbs and blk_fill_rwbs_rq isn't needed any more. With this patch, after a sync write we get: write_test-2417 [000] 226.603878: block_rq_insert: 3,64 WS 0 () 258135 += 8 [write_test] Signed-off-by: Tao Ma Acked-by: Jeff Moyer Signed-off-by: Jens Axboe --- include/linux/blktrace_api.h | 1 - include/trace/events/block.h | 6 +++--- kernel/trace/blktrace.c | 16 ---------------- 3 files changed, 3 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index 3395cf7130f5..b22fb0d3db0f 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -245,7 +245,6 @@ static inline int blk_cmd_buf_len(struct request *rq) extern void blk_dump_cmd(char *buf, struct request *rq); extern void blk_fill_rwbs(char *rwbs, u32 rw, int bytes); -extern void blk_fill_rwbs_rq(char *rwbs, struct request *rq); #endif /* CONFIG_EVENT_TRACING && CONFIG_BLOCK */ diff --git a/include/trace/events/block.h b/include/trace/events/block.h index aba421d68f6f..78f18adb49c8 100644 --- a/include/trace/events/block.h +++ b/include/trace/events/block.h @@ -31,7 +31,7 @@ DECLARE_EVENT_CLASS(block_rq_with_error, 0 : blk_rq_sectors(rq); __entry->errors = rq->errors; - blk_fill_rwbs_rq(__entry->rwbs, rq); + blk_fill_rwbs(__entry->rwbs, rq->cmd_flags, blk_rq_bytes(rq)); blk_dump_cmd(__get_str(cmd), rq); ), @@ -118,7 +118,7 @@ DECLARE_EVENT_CLASS(block_rq, __entry->bytes = (rq->cmd_type == REQ_TYPE_BLOCK_PC) ? blk_rq_bytes(rq) : 0; - blk_fill_rwbs_rq(__entry->rwbs, rq); + blk_fill_rwbs(__entry->rwbs, rq->cmd_flags, blk_rq_bytes(rq)); blk_dump_cmd(__get_str(cmd), rq); memcpy(__entry->comm, current->comm, TASK_COMM_LEN); ), @@ -563,7 +563,7 @@ TRACE_EVENT(block_rq_remap, __entry->nr_sector = blk_rq_sectors(rq); __entry->old_dev = dev; __entry->old_sector = from; - blk_fill_rwbs_rq(__entry->rwbs, rq); + blk_fill_rwbs(__entry->rwbs, rq->cmd_flags, blk_rq_bytes(rq)); ), TP_printk("%d,%d %s %llu + %u <- (%d,%d) %llu", diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c index d95721f33702..cbafed7d4f38 100644 --- a/kernel/trace/blktrace.c +++ b/kernel/trace/blktrace.c @@ -1827,21 +1827,5 @@ void blk_fill_rwbs(char *rwbs, u32 rw, int bytes) rwbs[i] = '\0'; } -void blk_fill_rwbs_rq(char *rwbs, struct request *rq) -{ - int rw = rq->cmd_flags & 0x03; - int bytes; - - if (rq->cmd_flags & REQ_DISCARD) - rw |= REQ_DISCARD; - - if (rq->cmd_flags & REQ_SECURE) - rw |= REQ_SECURE; - - bytes = blk_rq_bytes(rq); - - blk_fill_rwbs(rwbs, rw, bytes); -} - #endif /* CONFIG_EVENT_TRACING */ -- cgit v1.2.3 From c53fa1ed92cd671a1dfb1e7569e9ab672612ddc6 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 3 Mar 2011 10:55:40 -0800 Subject: netlink: kill loginuid/sessionid/sid members from struct netlink_skb_parms Netlink message processing in the kernel is synchronous these days, the session information can be collected when needed. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netlink.h | 3 --- kernel/audit.c | 6 ++--- kernel/auditfilter.c | 10 +++++--- net/netlabel/netlabel_user.h | 6 ++--- net/netlink/af_netlink.c | 3 --- net/xfrm/xfrm_user.c | 56 +++++++++++++++++++++++++------------------- security/selinux/hooks.c | 6 +++-- 7 files changed, 49 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index e2b9e63afa68..66823b862022 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -161,9 +161,6 @@ struct netlink_skb_parms { __u32 pid; __u32 dst_group; kernel_cap_t eff_cap; - __u32 loginuid; /* Login (audit) uid */ - __u32 sessionid; /* Session id (audit) */ - __u32 sid; /* SELinux security id */ }; #define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)->cb)) diff --git a/kernel/audit.c b/kernel/audit.c index 162e88e33bc9..939500317066 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -673,9 +673,9 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) pid = NETLINK_CREDS(skb)->pid; uid = NETLINK_CREDS(skb)->uid; - loginuid = NETLINK_CB(skb).loginuid; - sessionid = NETLINK_CB(skb).sessionid; - sid = NETLINK_CB(skb).sid; + loginuid = audit_get_loginuid(current); + sessionid = audit_get_sessionid(current); + security_task_getsecid(current, &sid); seq = nlh->nlmsg_seq; data = NLMSG_DATA(nlh); diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index add2819af71b..f8277c80d678 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1238,6 +1238,7 @@ static int audit_filter_user_rules(struct netlink_skb_parms *cb, for (i = 0; i < rule->field_count; i++) { struct audit_field *f = &rule->fields[i]; int result = 0; + u32 sid; switch (f->type) { case AUDIT_PID: @@ -1250,19 +1251,22 @@ static int audit_filter_user_rules(struct netlink_skb_parms *cb, result = audit_comparator(cb->creds.gid, f->op, f->val); break; case AUDIT_LOGINUID: - result = audit_comparator(cb->loginuid, f->op, f->val); + result = audit_comparator(audit_get_loginuid(current), + f->op, f->val); break; case AUDIT_SUBJ_USER: case AUDIT_SUBJ_ROLE: case AUDIT_SUBJ_TYPE: case AUDIT_SUBJ_SEN: case AUDIT_SUBJ_CLR: - if (f->lsm_rule) - result = security_audit_rule_match(cb->sid, + if (f->lsm_rule) { + security_task_getsecid(current, &sid); + result = security_audit_rule_match(sid, f->type, f->op, f->lsm_rule, NULL); + } break; } diff --git a/net/netlabel/netlabel_user.h b/net/netlabel/netlabel_user.h index 6caef8b20611..f4fc4c9ad567 100644 --- a/net/netlabel/netlabel_user.h +++ b/net/netlabel/netlabel_user.h @@ -49,9 +49,9 @@ static inline void netlbl_netlink_auditinfo(struct sk_buff *skb, struct netlbl_audit *audit_info) { - audit_info->secid = NETLINK_CB(skb).sid; - audit_info->loginuid = NETLINK_CB(skb).loginuid; - audit_info->sessionid = NETLINK_CB(skb).sessionid; + security_task_getsecid(current, &audit_info->secid); + audit_info->loginuid = audit_get_loginuid(current); + audit_info->sessionid = audit_get_sessionid(current); } /* NetLabel NETLINK I/O functions */ diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 478181d53c55..97ecd923d7ee 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1362,9 +1362,6 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, NETLINK_CB(skb).pid = nlk->pid; NETLINK_CB(skb).dst_group = dst_group; - NETLINK_CB(skb).loginuid = audit_get_loginuid(current); - NETLINK_CB(skb).sessionid = audit_get_sessionid(current); - security_task_getsecid(current, &(NETLINK_CB(skb).sid)); memcpy(NETLINK_CREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); /* What can I do? Netlink is asynchronous, so that diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 673698d380d7..468ab60d3dc0 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -497,9 +497,9 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, struct xfrm_state *x; int err; struct km_event c; - uid_t loginuid = NETLINK_CB(skb).loginuid; - u32 sessionid = NETLINK_CB(skb).sessionid; - u32 sid = NETLINK_CB(skb).sid; + uid_t loginuid = audit_get_loginuid(current); + u32 sessionid = audit_get_sessionid(current); + u32 sid; err = verify_newsa_info(p, attrs); if (err) @@ -515,6 +515,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, else err = xfrm_state_update(x); + security_task_getsecid(current, &sid); xfrm_audit_state_add(x, err ? 0 : 1, loginuid, sessionid, sid); if (err < 0) { @@ -575,9 +576,9 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, int err = -ESRCH; struct km_event c; struct xfrm_usersa_id *p = nlmsg_data(nlh); - uid_t loginuid = NETLINK_CB(skb).loginuid; - u32 sessionid = NETLINK_CB(skb).sessionid; - u32 sid = NETLINK_CB(skb).sid; + uid_t loginuid = audit_get_loginuid(current); + u32 sessionid = audit_get_sessionid(current); + u32 sid; x = xfrm_user_state_lookup(net, p, attrs, &err); if (x == NULL) @@ -602,6 +603,7 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, km_state_notify(x, &c); out: + security_task_getsecid(current, &sid); xfrm_audit_state_delete(x, err ? 0 : 1, loginuid, sessionid, sid); xfrm_state_put(x); return err; @@ -1265,9 +1267,9 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, struct km_event c; int err; int excl; - uid_t loginuid = NETLINK_CB(skb).loginuid; - u32 sessionid = NETLINK_CB(skb).sessionid; - u32 sid = NETLINK_CB(skb).sid; + uid_t loginuid = audit_get_loginuid(current); + u32 sessionid = audit_get_sessionid(current); + u32 sid; err = verify_newpolicy_info(p); if (err) @@ -1286,6 +1288,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, * a type XFRM_MSG_UPDPOLICY - JHS */ excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY; err = xfrm_policy_insert(p->dir, xp, excl); + security_task_getsecid(current, &sid); xfrm_audit_policy_add(xp, err ? 0 : 1, loginuid, sessionid, sid); if (err) { @@ -1522,10 +1525,11 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, NETLINK_CB(skb).pid); } } else { - uid_t loginuid = NETLINK_CB(skb).loginuid; - u32 sessionid = NETLINK_CB(skb).sessionid; - u32 sid = NETLINK_CB(skb).sid; + uid_t loginuid = audit_get_loginuid(current); + u32 sessionid = audit_get_sessionid(current); + u32 sid; + security_task_getsecid(current, &sid); xfrm_audit_policy_delete(xp, err ? 0 : 1, loginuid, sessionid, sid); @@ -1553,9 +1557,9 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, struct xfrm_audit audit_info; int err; - audit_info.loginuid = NETLINK_CB(skb).loginuid; - audit_info.sessionid = NETLINK_CB(skb).sessionid; - audit_info.secid = NETLINK_CB(skb).sid; + audit_info.loginuid = audit_get_loginuid(current); + audit_info.sessionid = audit_get_sessionid(current); + security_task_getsecid(current, &audit_info.secid); err = xfrm_state_flush(net, p->proto, &audit_info); if (err) { if (err == -ESRCH) /* empty table */ @@ -1720,9 +1724,9 @@ static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, if (err) return err; - audit_info.loginuid = NETLINK_CB(skb).loginuid; - audit_info.sessionid = NETLINK_CB(skb).sessionid; - audit_info.secid = NETLINK_CB(skb).sid; + audit_info.loginuid = audit_get_loginuid(current); + audit_info.sessionid = audit_get_sessionid(current); + security_task_getsecid(current, &audit_info.secid); err = xfrm_policy_flush(net, type, &audit_info); if (err) { if (err == -ESRCH) /* empty table */ @@ -1789,9 +1793,11 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, err = 0; if (up->hard) { - uid_t loginuid = NETLINK_CB(skb).loginuid; - uid_t sessionid = NETLINK_CB(skb).sessionid; - u32 sid = NETLINK_CB(skb).sid; + uid_t loginuid = audit_get_loginuid(current); + u32 sessionid = audit_get_sessionid(current); + u32 sid; + + security_task_getsecid(current, &sid); xfrm_policy_delete(xp, p->dir); xfrm_audit_policy_delete(xp, 1, loginuid, sessionid, sid); @@ -1830,9 +1836,11 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, km_state_expired(x, ue->hard, current->pid); if (ue->hard) { - uid_t loginuid = NETLINK_CB(skb).loginuid; - uid_t sessionid = NETLINK_CB(skb).sessionid; - u32 sid = NETLINK_CB(skb).sid; + uid_t loginuid = audit_get_loginuid(current); + u32 sessionid = audit_get_sessionid(current); + u32 sid; + + security_task_getsecid(current, &sid); __xfrm_state_delete(x); xfrm_audit_state_delete(x, 1, loginuid, sessionid, sid); } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c8d699270687..cef42f5d69a2 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4669,6 +4669,7 @@ static int selinux_netlink_recv(struct sk_buff *skb, int capability) { int err; struct common_audit_data ad; + u32 sid; err = cap_netlink_recv(skb, capability); if (err) @@ -4677,8 +4678,9 @@ static int selinux_netlink_recv(struct sk_buff *skb, int capability) COMMON_AUDIT_DATA_INIT(&ad, CAP); ad.u.cap = capability; - return avc_has_perm(NETLINK_CB(skb).sid, NETLINK_CB(skb).sid, - SECCLASS_CAPABILITY, CAP_TO_MASK(capability), &ad); + security_task_getsecid(current, &sid); + return avc_has_perm(sid, sid, SECCLASS_CAPABILITY, + CAP_TO_MASK(capability), &ad); } static int ipc_alloc_security(struct task_struct *task, -- cgit v1.2.3 From d276055c4e90a7278cd5167ba9755c9b214bcff7 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 3 Mar 2011 11:10:02 -0800 Subject: net_sched: reduce fifo qdisc size Because of various alignements [SLUB / qdisc], we use 512 bytes of memory for one {p|b}fifo qdisc, instead of 256 bytes on 64bit arches and 192 bytes on 32bit ones. Move the "u32 limit" inside "struct Qdisc" (no impact on other qdiscs) Change qdisc_alloc(), first trying a regular allocation before an oversized one. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/sch_generic.h | 1 + net/sched/sch_fifo.c | 34 +++++++++++----------------------- net/sched/sch_generic.c | 18 +++++++++++------- 3 files changed, 23 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 16626a04cb03..1934634f8896 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -83,6 +83,7 @@ struct Qdisc { struct gnet_stats_queue qstats; struct rcu_head rcu_head; spinlock_t busylock; + u32 limit; }; static inline bool qdisc_is_running(const struct Qdisc *qdisc) diff --git a/net/sched/sch_fifo.c b/net/sched/sch_fifo.c index be33f9ddf9dd..66effe2da8e0 100644 --- a/net/sched/sch_fifo.c +++ b/net/sched/sch_fifo.c @@ -19,15 +19,9 @@ /* 1 band FIFO pseudo-"scheduler" */ -struct fifo_sched_data { - u32 limit; -}; - static int bfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch) { - struct fifo_sched_data *q = qdisc_priv(sch); - - if (likely(sch->qstats.backlog + qdisc_pkt_len(skb) <= q->limit)) + if (likely(sch->qstats.backlog + qdisc_pkt_len(skb) <= sch->limit)) return qdisc_enqueue_tail(skb, sch); return qdisc_reshape_fail(skb, sch); @@ -35,9 +29,7 @@ static int bfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch) static int pfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch) { - struct fifo_sched_data *q = qdisc_priv(sch); - - if (likely(skb_queue_len(&sch->q) < q->limit)) + if (likely(skb_queue_len(&sch->q) < sch->limit)) return qdisc_enqueue_tail(skb, sch); return qdisc_reshape_fail(skb, sch); @@ -45,9 +37,7 @@ static int pfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch) static int pfifo_tail_enqueue(struct sk_buff *skb, struct Qdisc *sch) { - struct fifo_sched_data *q = qdisc_priv(sch); - - if (likely(skb_queue_len(&sch->q) < q->limit)) + if (likely(skb_queue_len(&sch->q) < sch->limit)) return qdisc_enqueue_tail(skb, sch); /* queue full, remove one skb to fulfill the limit */ @@ -60,7 +50,6 @@ static int pfifo_tail_enqueue(struct sk_buff *skb, struct Qdisc *sch) static int fifo_init(struct Qdisc *sch, struct nlattr *opt) { - struct fifo_sched_data *q = qdisc_priv(sch); bool bypass; bool is_bfifo = sch->ops == &bfifo_qdisc_ops; @@ -70,20 +59,20 @@ static int fifo_init(struct Qdisc *sch, struct nlattr *opt) if (is_bfifo) limit *= psched_mtu(qdisc_dev(sch)); - q->limit = limit; + sch->limit = limit; } else { struct tc_fifo_qopt *ctl = nla_data(opt); if (nla_len(opt) < sizeof(*ctl)) return -EINVAL; - q->limit = ctl->limit; + sch->limit = ctl->limit; } if (is_bfifo) - bypass = q->limit >= psched_mtu(qdisc_dev(sch)); + bypass = sch->limit >= psched_mtu(qdisc_dev(sch)); else - bypass = q->limit >= 1; + bypass = sch->limit >= 1; if (bypass) sch->flags |= TCQ_F_CAN_BYPASS; @@ -94,8 +83,7 @@ static int fifo_init(struct Qdisc *sch, struct nlattr *opt) static int fifo_dump(struct Qdisc *sch, struct sk_buff *skb) { - struct fifo_sched_data *q = qdisc_priv(sch); - struct tc_fifo_qopt opt = { .limit = q->limit }; + struct tc_fifo_qopt opt = { .limit = sch->limit }; NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); return skb->len; @@ -106,7 +94,7 @@ nla_put_failure: struct Qdisc_ops pfifo_qdisc_ops __read_mostly = { .id = "pfifo", - .priv_size = sizeof(struct fifo_sched_data), + .priv_size = 0, .enqueue = pfifo_enqueue, .dequeue = qdisc_dequeue_head, .peek = qdisc_peek_head, @@ -121,7 +109,7 @@ EXPORT_SYMBOL(pfifo_qdisc_ops); struct Qdisc_ops bfifo_qdisc_ops __read_mostly = { .id = "bfifo", - .priv_size = sizeof(struct fifo_sched_data), + .priv_size = 0, .enqueue = bfifo_enqueue, .dequeue = qdisc_dequeue_head, .peek = qdisc_peek_head, @@ -136,7 +124,7 @@ EXPORT_SYMBOL(bfifo_qdisc_ops); struct Qdisc_ops pfifo_head_drop_qdisc_ops __read_mostly = { .id = "pfifo_head_drop", - .priv_size = sizeof(struct fifo_sched_data), + .priv_size = 0, .enqueue = pfifo_tail_enqueue, .dequeue = qdisc_dequeue_head, .peek = qdisc_peek_head, diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 0da09d508737..a854cab03f1e 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -550,21 +550,25 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, { void *p; struct Qdisc *sch; - unsigned int size; + unsigned int size = QDISC_ALIGN(sizeof(*sch)) + ops->priv_size; int err = -ENOBUFS; - /* ensure that the Qdisc and the private data are 64-byte aligned */ - size = QDISC_ALIGN(sizeof(*sch)); - size += ops->priv_size + (QDISC_ALIGNTO - 1); - p = kzalloc_node(size, GFP_KERNEL, netdev_queue_numa_node_read(dev_queue)); if (!p) goto errout; sch = (struct Qdisc *) QDISC_ALIGN((unsigned long) p); - sch->padded = (char *) sch - (char *) p; - + /* if we got non aligned memory, ask more and do alignment ourself */ + if (sch != p) { + kfree(p); + p = kzalloc_node(size + QDISC_ALIGNTO - 1, GFP_KERNEL, + netdev_queue_numa_node_read(dev_queue)); + if (!p) + goto errout; + sch = (struct Qdisc *) QDISC_ALIGN((unsigned long) p); + sch->padded = (char *) sch - (char *) p; + } INIT_LIST_HEAD(&sch->list); skb_queue_head_init(&sch->q); spin_lock_init(&sch->busylock); -- cgit v1.2.3 From ff36fe2c845cab2102e4826c1ffa0a6ebf487c65 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 3 Mar 2011 16:09:14 -0500 Subject: LSM: Pass -o remount options to the LSM The VFS mount code passes the mount options to the LSM. The LSM will remove options it understands from the data and the VFS will then pass the remaining options onto the underlying filesystem. This is how options like the SELinux context= work. The problem comes in that -o remount never calls into LSM code. So if you include an LSM specific option it will get passed to the filesystem and will cause the remount to fail. An example of where this is a problem is the 'seclabel' option. The SELinux LSM hook will print this word in /proc/mounts if the filesystem is being labeled using xattrs. If you pass this word on mount it will be silently stripped and ignored. But if you pass this word on remount the LSM never gets called and it will be passed to the FS. The FS doesn't know what seclabel means and thus should fail the mount. For example an ext3 fs mounted over loop # mount -o loop /tmp/fs /mnt/tmp # cat /proc/mounts | grep /mnt/tmp /dev/loop0 /mnt/tmp ext3 rw,seclabel,relatime,errors=continue,barrier=0,data=ordered 0 0 # mount -o remount /mnt/tmp mount: /mnt/tmp not mounted already, or bad option # dmesg EXT3-fs (loop0): error: unrecognized mount option "seclabel" or missing value This patch passes the remount mount options to an new LSM hook. Signed-off-by: Eric Paris Reviewed-by: James Morris --- fs/namespace.c | 4 ++++ include/linux/security.h | 13 +++++++++++++ security/capability.c | 6 ++++++ security/security.c | 5 +++++ 4 files changed, 28 insertions(+) (limited to 'include') diff --git a/fs/namespace.c b/fs/namespace.c index 3ddfd9046c44..1b3f2ac59c5e 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1800,6 +1800,10 @@ static int do_remount(struct path *path, int flags, int mnt_flags, if (path->dentry != path->mnt->mnt_root) return -EINVAL; + err = security_sb_remount(sb, data); + if (err) + return err; + down_write(&sb->s_umount); if (flags & MS_BIND) err = change_mount_flags(path->mnt, flags); diff --git a/include/linux/security.h b/include/linux/security.h index 14167f2eb35a..d11ac43ecc49 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -268,6 +268,12 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @orig the original mount data copied from userspace. * @copy copied data which will be passed to the security module. * Returns 0 if the copy was successful. + * @sb_remount: + * Extracts security system specifc mount options and verifys no changes + * are being made to those options. + * @sb superblock being remounted + * @data contains the filesystem-specific data. + * Return 0 if permission is granted. * @sb_umount: * Check permission before the @mnt file system is unmounted. * @mnt contains the mounted file system. @@ -1394,6 +1400,7 @@ struct security_operations { int (*sb_alloc_security) (struct super_block *sb); void (*sb_free_security) (struct super_block *sb); int (*sb_copy_data) (char *orig, char *copy); + int (*sb_remount) (struct super_block *sb, void *data); int (*sb_kern_mount) (struct super_block *sb, int flags, void *data); int (*sb_show_options) (struct seq_file *m, struct super_block *sb); int (*sb_statfs) (struct dentry *dentry); @@ -1676,6 +1683,7 @@ int security_bprm_secureexec(struct linux_binprm *bprm); int security_sb_alloc(struct super_block *sb); void security_sb_free(struct super_block *sb); int security_sb_copy_data(char *orig, char *copy); +int security_sb_remount(struct super_block *sb, void *data); int security_sb_kern_mount(struct super_block *sb, int flags, void *data); int security_sb_show_options(struct seq_file *m, struct super_block *sb); int security_sb_statfs(struct dentry *dentry); @@ -1955,6 +1963,11 @@ static inline int security_sb_copy_data(char *orig, char *copy) return 0; } +static inline int security_sb_remount(struct super_block *sb, void *data) +{ + return 0; +} + static inline int security_sb_kern_mount(struct super_block *sb, int flags, void *data) { return 0; diff --git a/security/capability.c b/security/capability.c index 85b67c8632df..ab3d807accc3 100644 --- a/security/capability.c +++ b/security/capability.c @@ -54,6 +54,11 @@ static int cap_sb_copy_data(char *orig, char *copy) return 0; } +static int cap_sb_remount(struct super_block *sb, void *data) +{ + return 0; +} + static int cap_sb_kern_mount(struct super_block *sb, int flags, void *data) { return 0; @@ -887,6 +892,7 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, sb_alloc_security); set_to_cap_if_null(ops, sb_free_security); set_to_cap_if_null(ops, sb_copy_data); + set_to_cap_if_null(ops, sb_remount); set_to_cap_if_null(ops, sb_kern_mount); set_to_cap_if_null(ops, sb_show_options); set_to_cap_if_null(ops, sb_statfs); diff --git a/security/security.c b/security/security.c index 8f28685ee0d9..b1d6134548bc 100644 --- a/security/security.c +++ b/security/security.c @@ -267,6 +267,11 @@ int security_sb_copy_data(char *orig, char *copy) } EXPORT_SYMBOL(security_sb_copy_data); +int security_sb_remount(struct super_block *sb, void *data) +{ + return security_ops->sb_remount(sb, data); +} + int security_sb_kern_mount(struct super_block *sb, int flags, void *data) { return security_ops->sb_kern_mount(sb, flags, data); -- cgit v1.2.3 From 01a16b21d6adf992aa863186c3c4e561a57c1714 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 3 Mar 2011 13:32:07 -0800 Subject: netlink: kill eff_cap from struct netlink_skb_parms Netlink message processing in the kernel is synchronous these days, capabilities can be checked directly in security_netlink_recv() from the current process. Signed-off-by: Patrick McHardy Reviewed-by: James Morris [chrisw: update to include pohmelfs and uvesafb] Signed-off-by: Chris Wright Signed-off-by: David S. Miller --- drivers/block/drbd/drbd_nl.c | 2 +- drivers/md/dm-log-userspace-transfer.c | 2 +- drivers/staging/pohmelfs/config.c | 2 +- drivers/video/uvesafb.c | 2 +- include/linux/netlink.h | 1 - net/netlink/af_netlink.c | 6 ------ security/commoncap.c | 3 +-- 7 files changed, 5 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index 8cbfaa687d72..fe81c851ca88 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -2177,7 +2177,7 @@ static void drbd_connector_callback(struct cn_msg *req, struct netlink_skb_parms return; } - if (!cap_raised(nsp->eff_cap, CAP_SYS_ADMIN)) { + if (!cap_raised(current_cap(), CAP_SYS_ADMIN)) { retcode = ERR_PERM; goto fail; } diff --git a/drivers/md/dm-log-userspace-transfer.c b/drivers/md/dm-log-userspace-transfer.c index 049eaf12aaab..1f23e048f077 100644 --- a/drivers/md/dm-log-userspace-transfer.c +++ b/drivers/md/dm-log-userspace-transfer.c @@ -134,7 +134,7 @@ static void cn_ulog_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { struct dm_ulog_request *tfr = (struct dm_ulog_request *)(msg + 1); - if (!cap_raised(nsp->eff_cap, CAP_SYS_ADMIN)) + if (!cap_raised(current_cap(), CAP_SYS_ADMIN)) return; spin_lock(&receiving_list_lock); diff --git a/drivers/staging/pohmelfs/config.c b/drivers/staging/pohmelfs/config.c index 89279ba1b737..39413b7d387d 100644 --- a/drivers/staging/pohmelfs/config.c +++ b/drivers/staging/pohmelfs/config.c @@ -525,7 +525,7 @@ static void pohmelfs_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *n { int err; - if (!cap_raised(nsp->eff_cap, CAP_SYS_ADMIN)) + if (!cap_raised(current_cap(), CAP_SYS_ADMIN)) return; switch (msg->flags) { diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index 52ec0959d462..5180a215d781 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c @@ -73,7 +73,7 @@ static void uvesafb_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *ns struct uvesafb_task *utask; struct uvesafb_ktask *task; - if (!cap_raised(nsp->eff_cap, CAP_SYS_ADMIN)) + if (!cap_raised(current_cap(), CAP_SYS_ADMIN)) return; if (msg->seq >= UVESAFB_TASKS_MAX) diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 66823b862022..4c4ac3f3ce5a 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -160,7 +160,6 @@ struct netlink_skb_parms { struct ucred creds; /* Skb credentials */ __u32 pid; __u32 dst_group; - kernel_cap_t eff_cap; }; #define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)->cb)) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 97ecd923d7ee..a808fb1e877d 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1364,12 +1364,6 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, NETLINK_CB(skb).dst_group = dst_group; memcpy(NETLINK_CREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); - /* What can I do? Netlink is asynchronous, so that - we will have to save current capabilities to - check them, when this message will be delivered - to corresponding kernel module. --ANK (980802) - */ - err = -EFAULT; if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { kfree_skb(skb); diff --git a/security/commoncap.c b/security/commoncap.c index 64c2ed9c9015..a83e607d91c3 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -52,13 +52,12 @@ static void warn_setuid_and_fcaps_mixed(const char *fname) int cap_netlink_send(struct sock *sk, struct sk_buff *skb) { - NETLINK_CB(skb).eff_cap = current_cap(); return 0; } int cap_netlink_recv(struct sk_buff *skb, int cap) { - if (!cap_raised(NETLINK_CB(skb).eff_cap, cap)) + if (!cap_raised(current_cap(), cap)) return -EPERM; return 0; } -- cgit v1.2.3 From 9f35421e09c494c36079d7cf5724ae9f832431d7 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 21 Feb 2011 11:17:35 +1000 Subject: drm/core: add ioctl to query device/driver capabilities We're coming to see a need to have a set of generic capability checks in the core DRM, in addition to the driver-specific ioctls that already exist. This patch defines an ioctl to do as such, but does not yet define any capabilities. [airlied: drop the driver callback for now.] Signed-off-by: Ben Skeggs Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_drv.c | 1 + drivers/gpu/drm/drm_ioctl.c | 11 +++++++++++ include/drm/drm.h | 7 +++++++ include/drm/drmP.h | 2 ++ 4 files changed, 21 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 0d04914eb058..93a112d45c1a 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -67,6 +67,7 @@ static struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_getmap, 0), DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, 0), DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, 0), + DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, 0), DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER), DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 117490590f56..d6de9d042b76 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -267,6 +267,17 @@ int drm_getstats(struct drm_device *dev, void *data, return 0; } +/** + * Get device/driver capabilities + */ +int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + struct drm_get_cap *req = data; + + req->value = 0; + return 0; +} + /** * Setversion ioctl. * diff --git a/include/drm/drm.h b/include/drm/drm.h index 8598cc94e169..da4efd162d58 100644 --- a/include/drm/drm.h +++ b/include/drm/drm.h @@ -608,6 +608,12 @@ struct drm_gem_open { __u64 size; }; +/** DRM_IOCTL_GET_CAP ioctl argument type */ +struct drm_get_cap { + __u64 capability; + __u64 value; +}; + #include "drm_mode.h" #define DRM_IOCTL_BASE 'd' @@ -628,6 +634,7 @@ struct drm_gem_open { #define DRM_IOCTL_GEM_CLOSE DRM_IOW (0x09, struct drm_gem_close) #define DRM_IOCTL_GEM_FLINK DRM_IOWR(0x0a, struct drm_gem_flink) #define DRM_IOCTL_GEM_OPEN DRM_IOWR(0x0b, struct drm_gem_open) +#define DRM_IOCTL_GET_CAP DRM_IOWR(0x0c, struct drm_get_cap) #define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, struct drm_unique) #define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, struct drm_auth) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 52a2fd2f7789..835214761d4f 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1276,6 +1276,8 @@ extern int drm_getclient(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_getstats(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int drm_getcap(struct drm_device *dev, void *data, + struct drm_file *file_priv); extern int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_noop(struct drm_device *dev, void *data, -- cgit v1.2.3 From e73f88af66fcc50083fae4b7e1c39b469179a97a Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 4 Mar 2011 14:50:28 +1000 Subject: drm: add cap bit to denote if dumb ioctl is available or not. This allows libkms to make an easier decision. Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_ioctl.c | 8 ++++++++ include/drm/drm.h | 2 ++ 2 files changed, 10 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index d6de9d042b76..7f6912a16761 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -275,6 +275,14 @@ int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv) struct drm_get_cap *req = data; req->value = 0; + switch (req->capability) { + case DRM_CAP_DUMB_BUFFER: + if (dev->driver->dumb_create) + req->value = 1; + break; + default: + return -EINVAL; + } return 0; } diff --git a/include/drm/drm.h b/include/drm/drm.h index da4efd162d58..9ac431396176 100644 --- a/include/drm/drm.h +++ b/include/drm/drm.h @@ -752,6 +752,8 @@ struct drm_event_vblank { __u32 reserved; }; +#define DRM_CAP_DUMB_BUFFER 0x1 + /* typedef area */ #ifndef __KERNEL__ typedef struct drm_clip_rect drm_clip_rect_t; -- cgit v1.2.3 From a7e3ed1e470116c9d12c2f778431a481a6be8ab6 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 3 Mar 2011 10:34:47 +0800 Subject: perf: Add support for supplementary event registers Change logs against Andi's original version: - Extends perf_event_attr:config to config{,1,2} (Peter Zijlstra) - Fixed a major event scheduling issue. There cannot be a ref++ on an event that has already done ref++ once and without calling put_constraint() in between. (Stephane Eranian) - Use thread_cpumask for percore allocation. (Lin Ming) - Use MSR names in the extra reg lists. (Lin Ming) - Remove redundant "c = NULL" in intel_percore_constraints - Fix comment of perf_event_attr::config1 Intel Nehalem/Westmere have a special OFFCORE_RESPONSE event that can be used to monitor any offcore accesses from a core. This is a very useful event for various tunings, and it's also needed to implement the generic LLC-* events correctly. Unfortunately this event requires programming a mask in a separate register. And worse this separate register is per core, not per CPU thread. This patch: - Teaches perf_events that OFFCORE_RESPONSE needs extra parameters. The extra parameters are passed by user space in the perf_event_attr::config1 field. - Adds support to the Intel perf_event core to schedule per core resources. This adds fairly generic infrastructure that can be also used for other per core resources. The basic code has is patterned after the similar AMD northbridge constraints code. Thanks to Stephane Eranian who pointed out some problems in the original version and suggested improvements. Signed-off-by: Andi Kleen Signed-off-by: Lin Ming Signed-off-by: Peter Zijlstra LKML-Reference: <1299119690-13991-2-git-send-email-ming.m.lin@intel.com> Signed-off-by: Ingo Molnar --- arch/x86/include/asm/msr-index.h | 3 + arch/x86/kernel/cpu/perf_event.c | 64 +++++++++++ arch/x86/kernel/cpu/perf_event_intel.c | 198 +++++++++++++++++++++++++++++++++ include/linux/perf_event.h | 13 ++- 4 files changed, 276 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 4d0dfa0d998e..d25e74cc1a50 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -47,6 +47,9 @@ #define MSR_IA32_MCG_STATUS 0x0000017a #define MSR_IA32_MCG_CTL 0x0000017b +#define MSR_OFFCORE_RSP_0 0x000001a6 +#define MSR_OFFCORE_RSP_1 0x000001a7 + #define MSR_IA32_PEBS_ENABLE 0x000003f1 #define MSR_IA32_DS_AREA 0x00000600 #define MSR_IA32_PERF_CAPABILITIES 0x00000345 diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index ea03c725e465..ec6a6db07332 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -93,6 +93,8 @@ struct amd_nb { struct event_constraint event_constraints[X86_PMC_IDX_MAX]; }; +struct intel_percore; + #define MAX_LBR_ENTRIES 16 struct cpu_hw_events { @@ -127,6 +129,13 @@ struct cpu_hw_events { struct perf_branch_stack lbr_stack; struct perf_branch_entry lbr_entries[MAX_LBR_ENTRIES]; + /* + * Intel percore register state. + * Coordinate shared resources between HT threads. + */ + int percore_used; /* Used by this CPU? */ + struct intel_percore *per_core; + /* * AMD specific bits */ @@ -177,6 +186,28 @@ struct cpu_hw_events { #define for_each_event_constraint(e, c) \ for ((e) = (c); (e)->weight; (e)++) +/* + * Extra registers for specific events. + * Some events need large masks and require external MSRs. + * Define a mapping to these extra registers. + */ +struct extra_reg { + unsigned int event; + unsigned int msr; + u64 config_mask; + u64 valid_mask; +}; + +#define EVENT_EXTRA_REG(e, ms, m, vm) { \ + .event = (e), \ + .msr = (ms), \ + .config_mask = (m), \ + .valid_mask = (vm), \ + } +#define INTEL_EVENT_EXTRA_REG(event, msr, vm) \ + EVENT_EXTRA_REG(event, msr, ARCH_PERFMON_EVENTSEL_EVENT, vm) +#define EVENT_EXTRA_END EVENT_EXTRA_REG(0, 0, 0, 0) + union perf_capabilities { struct { u64 lbr_format : 6; @@ -221,6 +252,7 @@ struct x86_pmu { void (*put_event_constraints)(struct cpu_hw_events *cpuc, struct perf_event *event); struct event_constraint *event_constraints; + struct event_constraint *percore_constraints; void (*quirks)(void); int perfctr_second_write; @@ -249,6 +281,11 @@ struct x86_pmu { */ unsigned long lbr_tos, lbr_from, lbr_to; /* MSR base regs */ int lbr_nr; /* hardware stack size */ + + /* + * Extra registers for events + */ + struct extra_reg *extra_regs; }; static struct x86_pmu x86_pmu __read_mostly; @@ -341,6 +378,31 @@ static inline unsigned int x86_pmu_event_addr(int index) return x86_pmu.perfctr + x86_pmu_addr_offset(index); } +/* + * Find and validate any extra registers to set up. + */ +static int x86_pmu_extra_regs(u64 config, struct perf_event *event) +{ + struct extra_reg *er; + + event->hw.extra_reg = 0; + event->hw.extra_config = 0; + + if (!x86_pmu.extra_regs) + return 0; + + for (er = x86_pmu.extra_regs; er->msr; er++) { + if (er->event != (config & er->config_mask)) + continue; + if (event->attr.config1 & ~er->valid_mask) + return -EINVAL; + event->hw.extra_reg = er->msr; + event->hw.extra_config = event->attr.config1; + break; + } + return 0; +} + static atomic_t active_events; static DEFINE_MUTEX(pmc_reserve_mutex); @@ -665,6 +727,8 @@ static void x86_pmu_disable(struct pmu *pmu) static inline void __x86_pmu_enable_event(struct hw_perf_event *hwc, u64 enable_mask) { + if (hwc->extra_reg) + wrmsrl(hwc->extra_reg, hwc->extra_config); wrmsrl(hwc->config_base, hwc->config | enable_mask); } diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index c3ce053ecb46..13cb6cf013f6 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -1,5 +1,27 @@ #ifdef CONFIG_CPU_SUP_INTEL +#define MAX_EXTRA_REGS 2 + +/* + * Per register state. + */ +struct er_account { + int ref; /* reference count */ + unsigned int extra_reg; /* extra MSR number */ + u64 extra_config; /* extra MSR config */ +}; + +/* + * Per core state + * This used to coordinate shared registers for HT threads. + */ +struct intel_percore { + raw_spinlock_t lock; /* protect structure */ + struct er_account regs[MAX_EXTRA_REGS]; + int refcnt; /* number of threads */ + unsigned core_id; +}; + /* * Intel PerfMon, used on Core and later. */ @@ -64,6 +86,18 @@ static struct event_constraint intel_nehalem_event_constraints[] = EVENT_CONSTRAINT_END }; +static struct extra_reg intel_nehalem_extra_regs[] = +{ + INTEL_EVENT_EXTRA_REG(0xb7, MSR_OFFCORE_RSP_0, 0xffff), + EVENT_EXTRA_END +}; + +static struct event_constraint intel_nehalem_percore_constraints[] = +{ + INTEL_EVENT_CONSTRAINT(0xb7, 0), + EVENT_CONSTRAINT_END +}; + static struct event_constraint intel_westmere_event_constraints[] = { FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* INST_RETIRED.ANY */ @@ -89,6 +123,20 @@ static struct event_constraint intel_snb_event_constraints[] = EVENT_CONSTRAINT_END }; +static struct extra_reg intel_westmere_extra_regs[] = +{ + INTEL_EVENT_EXTRA_REG(0xb7, MSR_OFFCORE_RSP_0, 0xffff), + INTEL_EVENT_EXTRA_REG(0xbb, MSR_OFFCORE_RSP_1, 0xffff), + EVENT_EXTRA_END +}; + +static struct event_constraint intel_westmere_percore_constraints[] = +{ + INTEL_EVENT_CONSTRAINT(0xb7, 0), + INTEL_EVENT_CONSTRAINT(0xbb, 0), + EVENT_CONSTRAINT_END +}; + static struct event_constraint intel_gen_event_constraints[] = { FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* INST_RETIRED.ANY */ @@ -906,6 +954,67 @@ intel_bts_constraints(struct perf_event *event) return NULL; } +static struct event_constraint * +intel_percore_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + unsigned int e = hwc->config & ARCH_PERFMON_EVENTSEL_EVENT; + struct event_constraint *c; + struct intel_percore *pc; + struct er_account *era; + int i; + int free_slot; + int found; + + if (!x86_pmu.percore_constraints || hwc->extra_alloc) + return NULL; + + for (c = x86_pmu.percore_constraints; c->cmask; c++) { + if (e != c->code) + continue; + + /* + * Allocate resource per core. + */ + pc = cpuc->per_core; + if (!pc) + break; + c = &emptyconstraint; + raw_spin_lock(&pc->lock); + free_slot = -1; + found = 0; + for (i = 0; i < MAX_EXTRA_REGS; i++) { + era = &pc->regs[i]; + if (era->ref > 0 && hwc->extra_reg == era->extra_reg) { + /* Allow sharing same config */ + if (hwc->extra_config == era->extra_config) { + era->ref++; + cpuc->percore_used = 1; + hwc->extra_alloc = 1; + c = NULL; + } + /* else conflict */ + found = 1; + break; + } else if (era->ref == 0 && free_slot == -1) + free_slot = i; + } + if (!found && free_slot != -1) { + era = &pc->regs[free_slot]; + era->ref = 1; + era->extra_reg = hwc->extra_reg; + era->extra_config = hwc->extra_config; + cpuc->percore_used = 1; + hwc->extra_alloc = 1; + c = NULL; + } + raw_spin_unlock(&pc->lock); + return c; + } + + return NULL; +} + static struct event_constraint * intel_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) { @@ -919,9 +1028,51 @@ intel_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event if (c) return c; + c = intel_percore_constraints(cpuc, event); + if (c) + return c; + return x86_get_event_constraints(cpuc, event); } +static void intel_put_event_constraints(struct cpu_hw_events *cpuc, + struct perf_event *event) +{ + struct extra_reg *er; + struct intel_percore *pc; + struct er_account *era; + struct hw_perf_event *hwc = &event->hw; + int i, allref; + + if (!cpuc->percore_used) + return; + + for (er = x86_pmu.extra_regs; er->msr; er++) { + if (er->event != (hwc->config & er->config_mask)) + continue; + + pc = cpuc->per_core; + raw_spin_lock(&pc->lock); + for (i = 0; i < MAX_EXTRA_REGS; i++) { + era = &pc->regs[i]; + if (era->ref > 0 && + era->extra_config == hwc->extra_config && + era->extra_reg == er->msr) { + era->ref--; + hwc->extra_alloc = 0; + break; + } + } + allref = 0; + for (i = 0; i < MAX_EXTRA_REGS; i++) + allref += pc->regs[i].ref; + if (allref == 0) + cpuc->percore_used = 0; + raw_spin_unlock(&pc->lock); + break; + } +} + static int intel_pmu_hw_config(struct perf_event *event) { int ret = x86_pmu_hw_config(event); @@ -993,11 +1144,43 @@ static __initconst const struct x86_pmu core_pmu = { */ .max_period = (1ULL << 31) - 1, .get_event_constraints = intel_get_event_constraints, + .put_event_constraints = intel_put_event_constraints, .event_constraints = intel_core_event_constraints, }; +static int intel_pmu_cpu_prepare(int cpu) +{ + struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); + + cpuc->per_core = kzalloc_node(sizeof(struct intel_percore), + GFP_KERNEL, cpu_to_node(cpu)); + if (!cpuc->per_core) + return NOTIFY_BAD; + + raw_spin_lock_init(&cpuc->per_core->lock); + cpuc->per_core->core_id = -1; + return NOTIFY_OK; +} + static void intel_pmu_cpu_starting(int cpu) { + struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); + int core_id = topology_core_id(cpu); + int i; + + for_each_cpu(i, topology_thread_cpumask(cpu)) { + struct intel_percore *pc = per_cpu(cpu_hw_events, i).per_core; + + if (pc && pc->core_id == core_id) { + kfree(cpuc->per_core); + cpuc->per_core = pc; + break; + } + } + + cpuc->per_core->core_id = core_id; + cpuc->per_core->refcnt++; + init_debug_store_on_cpu(cpu); /* * Deal with CPUs that don't clear their LBRs on power-up. @@ -1007,6 +1190,15 @@ static void intel_pmu_cpu_starting(int cpu) static void intel_pmu_cpu_dying(int cpu) { + struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); + struct intel_percore *pc = cpuc->per_core; + + if (pc) { + if (pc->core_id == -1 || --pc->refcnt == 0) + kfree(pc); + cpuc->per_core = NULL; + } + fini_debug_store_on_cpu(cpu); } @@ -1031,7 +1223,9 @@ static __initconst const struct x86_pmu intel_pmu = { */ .max_period = (1ULL << 31) - 1, .get_event_constraints = intel_get_event_constraints, + .put_event_constraints = intel_put_event_constraints, + .cpu_prepare = intel_pmu_cpu_prepare, .cpu_starting = intel_pmu_cpu_starting, .cpu_dying = intel_pmu_cpu_dying, }; @@ -1151,7 +1345,9 @@ static __init int intel_pmu_init(void) x86_pmu.event_constraints = intel_nehalem_event_constraints; x86_pmu.pebs_constraints = intel_nehalem_pebs_event_constraints; + x86_pmu.percore_constraints = intel_nehalem_percore_constraints; x86_pmu.enable_all = intel_pmu_nhm_enable_all; + x86_pmu.extra_regs = intel_nehalem_extra_regs; pr_cont("Nehalem events, "); break; @@ -1174,8 +1370,10 @@ static __init int intel_pmu_init(void) intel_pmu_lbr_init_nhm(); x86_pmu.event_constraints = intel_westmere_event_constraints; + x86_pmu.percore_constraints = intel_westmere_percore_constraints; x86_pmu.enable_all = intel_pmu_nhm_enable_all; x86_pmu.pebs_constraints = intel_westmere_pebs_event_constraints; + x86_pmu.extra_regs = intel_westmere_extra_regs; pr_cont("Westmere events, "); break; diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 8ceb5a6fd9c9..614615b8d42b 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -225,8 +225,14 @@ struct perf_event_attr { }; __u32 bp_type; - __u64 bp_addr; - __u64 bp_len; + union { + __u64 bp_addr; + __u64 config1; /* extension of config */ + }; + union { + __u64 bp_len; + __u64 config2; /* extension of config1 */ + }; }; /* @@ -541,6 +547,9 @@ struct hw_perf_event { unsigned long event_base; int idx; int last_cpu; + unsigned int extra_reg; + u64 extra_config; + int extra_alloc; }; struct { /* software */ struct hrtimer hrtimer; -- cgit v1.2.3 From e3e89cc535223433a619d0969db3fa05cdd946b8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 4 Mar 2011 09:23:30 -0800 Subject: Mark ptrace_{traceme,attach,detach} static They are only used inside kernel/ptrace.c, and have been for a long time. We don't want to go back to the bad-old-days when architectures did things on their own, so make them static and private. Signed-off-by: Linus Torvalds --- include/linux/ptrace.h | 3 --- kernel/ptrace.c | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 092a04f874a8..a1147e5dd245 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -102,11 +102,8 @@ extern long arch_ptrace(struct task_struct *child, long request, unsigned long addr, unsigned long data); -extern int ptrace_traceme(void); extern int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len); extern int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len); -extern int ptrace_attach(struct task_struct *tsk); -extern int ptrace_detach(struct task_struct *, unsigned int); extern void ptrace_disable(struct task_struct *); extern int ptrace_check_attach(struct task_struct *task, int kill); extern int ptrace_request(struct task_struct *child, long request, diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 1708b1e2972d..e2302e40b360 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -163,7 +163,7 @@ bool ptrace_may_access(struct task_struct *task, unsigned int mode) return !err; } -int ptrace_attach(struct task_struct *task) +static int ptrace_attach(struct task_struct *task) { int retval; @@ -219,7 +219,7 @@ out: * Performs checks and sets PT_PTRACED. * Should be used by all ptrace implementations for PTRACE_TRACEME. */ -int ptrace_traceme(void) +static int ptrace_traceme(void) { int ret = -EPERM; @@ -293,7 +293,7 @@ static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p) return false; } -int ptrace_detach(struct task_struct *child, unsigned int data) +static int ptrace_detach(struct task_struct *child, unsigned int data) { bool dead = false; -- cgit v1.2.3 From 1d471cd1261a44a3b28350bef7e5113a4609c106 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Wed, 2 Mar 2011 14:52:32 +0100 Subject: ASoC: Add TI tlv320aic32x4 codec support. This patch adds support for tlv320aic3205 and tlv320aic3254 codecs. It doesn't include miniDSP support for aic3254. Signed-off-by: Javier Martin Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/tlv320aic32x4.h | 31 ++ sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tlv320aic32x4.c | 794 +++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tlv320aic32x4.h | 143 +++++++ 5 files changed, 974 insertions(+) create mode 100644 include/sound/tlv320aic32x4.h create mode 100644 sound/soc/codecs/tlv320aic32x4.c create mode 100644 sound/soc/codecs/tlv320aic32x4.h (limited to 'include') diff --git a/include/sound/tlv320aic32x4.h b/include/sound/tlv320aic32x4.h new file mode 100644 index 000000000000..c009f70b4029 --- /dev/null +++ b/include/sound/tlv320aic32x4.h @@ -0,0 +1,31 @@ +/* + * tlv320aic32x4.h -- TLV320AIC32X4 Soc Audio driver platform data + * + * Copyright 2011 Vista Silicon S.L. + * + * Author: Javier Martin + * + * 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 _AIC32X4_PDATA_H +#define _AIC32X4_PDATA_H + +#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001 +#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002 +#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004 +#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008 +#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010 + +#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001 +#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002 + +struct aic32x4_pdata { + u32 power_cfg; + u32 micpga_routing; + bool swapdacs; +}; + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c04da1871297..82a46309ded6 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -40,6 +40,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TLV320AIC23 if I2C select SND_SOC_TLV320AIC26 if SPI_MASTER + select SND_SOC_TVL320AIC32X4 if I2C select SND_SOC_TLV320AIC3X if I2C select SND_SOC_TPA6130A2 if I2C select SND_SOC_TLV320DAC33 if I2C @@ -206,6 +207,9 @@ config SND_SOC_TLV320AIC26 tristate "TI TLV320AIC26 Codec support" if SND_SOC_OF_SIMPLE depends on SPI +config SND_SOC_TVL320AIC32X4 + tristate + config SND_SOC_TLV320AIC3X tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 3bbb08c512d0..b43f9d418c9b 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -28,6 +28,7 @@ snd-soc-stac9766-objs := stac9766.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic26-objs := tlv320aic26.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o +snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o snd-soc-tlv320dac33-objs := tlv320dac33.o snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o @@ -112,6 +113,7 @@ obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o +obj-$(CONFIG_SND_SOC_TVL320AIC32X4) += snd-soc-tlv320aic32x4.o obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c new file mode 100644 index 000000000000..ee82e3896039 --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -0,0 +1,794 @@ +/* + * linux/sound/soc/codecs/tlv320aic32x4.c + * + * Copyright 2011 Vista Silicon S.L. + * + * Author: Javier Martin + * + * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tlv320aic32x4.h" + +struct aic32x4_rate_divs { + u32 mclk; + u32 rate; + u8 p_val; + u8 pll_j; + u16 pll_d; + u16 dosr; + u8 ndac; + u8 mdac; + u8 aosr; + u8 nadc; + u8 madc; + u8 blck_N; +}; + +struct aic32x4_priv { + u32 sysclk; + s32 master; + u8 page_no; + void *control_data; + u32 power_cfg; + u32 micpga_routing; + bool swapdacs; +}; + +/* 0dB min, 1dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_step_1, 0, 100, 0); +/* 0dB min, 0.5dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_step_0_5, 0, 50, 0); + +static const struct snd_kcontrol_new aic32x4_snd_controls[] = { + SOC_DOUBLE_R_TLV("PCM Playback Volume", AIC32X4_LDACVOL, + AIC32X4_RDACVOL, 0, 0x30, 0, tlv_step_0_5), + SOC_DOUBLE_R_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN, + AIC32X4_HPRGAIN, 0, 0x1D, 0, tlv_step_1), + SOC_DOUBLE_R_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN, + AIC32X4_LORGAIN, 0, 0x1D, 0, tlv_step_1), + SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN, + AIC32X4_HPRGAIN, 6, 0x01, 1), + SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN, + AIC32X4_LORGAIN, 6, 0x01, 1), + SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL, + AIC32X4_RMICPGAVOL, 7, 0x01, 1), + + SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0), + SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0), + + SOC_DOUBLE_R_TLV("ADC Level Volume", AIC32X4_LADCVOL, + AIC32X4_RADCVOL, 0, 0x28, 0, tlv_step_0_5), + SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL, + AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), + + SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), + + SOC_SINGLE("AGC Left Switch", AIC32X4_LAGC1, 7, 1, 0), + SOC_SINGLE("AGC Right Switch", AIC32X4_RAGC1, 7, 1, 0), + SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, + 4, 0x07, 0), + SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, + 0, 0x03, 0), + SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, + 6, 0x03, 0), + SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, + 1, 0x1F, 0), + SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, + 0, 0x7F, 0), + SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, + 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, + 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, + 0, 0x1F, 0), + SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, + 0, 0x0F, 0), +}; + +static const struct aic32x4_rate_divs aic32x4_divs[] = { + /* 8k rate */ + {AIC32X4_FREQ_12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24}, + {AIC32X4_FREQ_24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24}, + {AIC32X4_FREQ_25000000, 8000, 2, 7, 3728, 768, 15, 1, 64, 45, 4, 24}, + /* 11.025k rate */ + {AIC32X4_FREQ_12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16}, + {AIC32X4_FREQ_24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16}, + /* 16k rate */ + {AIC32X4_FREQ_12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12}, + {AIC32X4_FREQ_24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12}, + {AIC32X4_FREQ_25000000, 16000, 2, 7, 3728, 384, 15, 1, 64, 18, 5, 12}, + /* 22.05k rate */ + {AIC32X4_FREQ_12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8}, + {AIC32X4_FREQ_24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8}, + {AIC32X4_FREQ_25000000, 22050, 2, 7, 2253, 256, 16, 1, 64, 16, 4, 8}, + /* 32k rate */ + {AIC32X4_FREQ_12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6}, + {AIC32X4_FREQ_24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6}, + /* 44.1k rate */ + {AIC32X4_FREQ_12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4}, + {AIC32X4_FREQ_24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4}, + {AIC32X4_FREQ_25000000, 44100, 2, 7, 2253, 128, 8, 2, 64, 8, 4, 4}, + /* 48k rate */ + {AIC32X4_FREQ_12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4}, + {AIC32X4_FREQ_24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4}, + {AIC32X4_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4} +}; + +static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { + SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0), + SOC_DAPM_SINGLE("IN1_L Switch", AIC32X4_HPLROUTE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new hpr_output_mixer_controls[] = { + SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_HPRROUTE, 3, 1, 0), + SOC_DAPM_SINGLE("IN1_R Switch", AIC32X4_HPRROUTE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new lol_output_mixer_controls[] = { + SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_LOLROUTE, 3, 1, 0), +}; + +static const struct snd_kcontrol_new lor_output_mixer_controls[] = { + SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_LORROUTE, 3, 1, 0), +}; + +static const struct snd_kcontrol_new left_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1_L P Switch", AIC32X4_LMICPGAPIN, 6, 1, 0), + SOC_DAPM_SINGLE("IN2_L P Switch", AIC32X4_LMICPGAPIN, 4, 1, 0), + SOC_DAPM_SINGLE("IN3_L P Switch", AIC32X4_LMICPGAPIN, 2, 1, 0), +}; + +static const struct snd_kcontrol_new right_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1_R P Switch", AIC32X4_RMICPGAPIN, 6, 1, 0), + SOC_DAPM_SINGLE("IN2_R P Switch", AIC32X4_RMICPGAPIN, 4, 1, 0), + SOC_DAPM_SINGLE("IN3_R P Switch", AIC32X4_RMICPGAPIN, 2, 1, 0), +}; + +static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", AIC32X4_DACSETUP, 7, 0), + SND_SOC_DAPM_MIXER("HPL Output Mixer", SND_SOC_NOPM, 0, 0, + &hpl_output_mixer_controls[0], + ARRAY_SIZE(hpl_output_mixer_controls)), + SND_SOC_DAPM_PGA("HPL Power", AIC32X4_OUTPWRCTL, 5, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("LOL Output Mixer", SND_SOC_NOPM, 0, 0, + &lol_output_mixer_controls[0], + ARRAY_SIZE(lol_output_mixer_controls)), + SND_SOC_DAPM_PGA("LOL Power", AIC32X4_OUTPWRCTL, 3, 0, NULL, 0), + + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", AIC32X4_DACSETUP, 6, 0), + SND_SOC_DAPM_MIXER("HPR Output Mixer", SND_SOC_NOPM, 0, 0, + &hpr_output_mixer_controls[0], + ARRAY_SIZE(hpr_output_mixer_controls)), + SND_SOC_DAPM_PGA("HPR Power", AIC32X4_OUTPWRCTL, 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("LOR Output Mixer", SND_SOC_NOPM, 0, 0, + &lor_output_mixer_controls[0], + ARRAY_SIZE(lor_output_mixer_controls)), + SND_SOC_DAPM_PGA("LOR Power", AIC32X4_OUTPWRCTL, 2, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0, + &left_input_mixer_controls[0], + ARRAY_SIZE(left_input_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0, + &right_input_mixer_controls[0], + ARRAY_SIZE(right_input_mixer_controls)), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0), + SND_SOC_DAPM_MICBIAS("Mic Bias", AIC32X4_MICBIAS, 6, 0), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("LOL"), + SND_SOC_DAPM_OUTPUT("LOR"), + SND_SOC_DAPM_INPUT("IN1_L"), + SND_SOC_DAPM_INPUT("IN1_R"), + SND_SOC_DAPM_INPUT("IN2_L"), + SND_SOC_DAPM_INPUT("IN2_R"), + SND_SOC_DAPM_INPUT("IN3_L"), + SND_SOC_DAPM_INPUT("IN3_R"), +}; + +static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { + /* Left Output */ + {"HPL Output Mixer", "L_DAC Switch", "Left DAC"}, + {"HPL Output Mixer", "IN1_L Switch", "IN1_L"}, + + {"HPL Power", NULL, "HPL Output Mixer"}, + {"HPL", NULL, "HPL Power"}, + + {"LOL Output Mixer", "L_DAC Switch", "Left DAC"}, + + {"LOL Power", NULL, "LOL Output Mixer"}, + {"LOL", NULL, "LOL Power"}, + + /* Right Output */ + {"HPR Output Mixer", "R_DAC Switch", "Right DAC"}, + {"HPR Output Mixer", "IN1_R Switch", "IN1_R"}, + + {"HPR Power", NULL, "HPR Output Mixer"}, + {"HPR", NULL, "HPR Power"}, + + {"LOR Output Mixer", "R_DAC Switch", "Right DAC"}, + + {"LOR Power", NULL, "LOR Output Mixer"}, + {"LOR", NULL, "LOR Power"}, + + /* Left input */ + {"Left Input Mixer", "IN1_L P Switch", "IN1_L"}, + {"Left Input Mixer", "IN2_L P Switch", "IN2_L"}, + {"Left Input Mixer", "IN3_L P Switch", "IN3_L"}, + + {"Left ADC", NULL, "Left Input Mixer"}, + + /* Right Input */ + {"Right Input Mixer", "IN1_R P Switch", "IN1_R"}, + {"Right Input Mixer", "IN2_R P Switch", "IN2_R"}, + {"Right Input Mixer", "IN3_R P Switch", "IN3_R"}, + + {"Right ADC", NULL, "Right Input Mixer"}, +}; + +static inline int aic32x4_change_page(struct snd_soc_codec *codec, + unsigned int new_page) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 data[2]; + int ret; + + data[0] = 0x00; + data[1] = new_page & 0xff; + + ret = codec->hw_write(codec->control_data, data, 2); + if (ret == 2) { + aic32x4->page_no = new_page; + return 0; + } else { + return ret; + } +} + +static int aic32x4_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + unsigned int page = reg / 128; + unsigned int fixed_reg = reg % 128; + u8 data[2]; + int ret; + + /* A write to AIC32X4_PSEL is really a non-explicit page change */ + if (reg == AIC32X4_PSEL) + return aic32x4_change_page(codec, val); + + if (aic32x4->page_no != page) { + ret = aic32x4_change_page(codec, page); + if (ret != 0) + return ret; + } + + data[0] = fixed_reg & 0xff; + data[1] = val & 0xff; + + if (codec->hw_write(codec->control_data, data, 2) == 2) + return 0; + else + return -EIO; +} + +static unsigned int aic32x4_read(struct snd_soc_codec *codec, unsigned int reg) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + unsigned int page = reg / 128; + unsigned int fixed_reg = reg % 128; + int ret; + + if (aic32x4->page_no != page) { + ret = aic32x4_change_page(codec, page); + if (ret != 0) + return ret; + } + return i2c_smbus_read_byte_data(codec->control_data, fixed_reg & 0xff); +} + +static inline int aic32x4_get_divs(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(aic32x4_divs); i++) { + if ((aic32x4_divs[i].rate == rate) + && (aic32x4_divs[i].mclk == mclk)) { + return i; + } + } + printk(KERN_ERR "aic32x4: master clock and sample rate is not supported\n"); + return -EINVAL; +} + +static int aic32x4_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(codec, aic32x4_dapm_widgets, + ARRAY_SIZE(aic32x4_dapm_widgets)); + + snd_soc_dapm_add_routes(codec, aic32x4_dapm_routes, + ARRAY_SIZE(aic32x4_dapm_routes)); + + snd_soc_dapm_new_widgets(codec); + return 0; +} + +static int aic32x4_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 aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + + switch (freq) { + case AIC32X4_FREQ_12000000: + case AIC32X4_FREQ_24000000: + case AIC32X4_FREQ_25000000: + aic32x4->sysclk = freq; + return 0; + } + printk(KERN_ERR "aic32x4: invalid frequency to set DAI system clock\n"); + return -EINVAL; +} + +static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 iface_reg_1; + u8 iface_reg_2; + u8 iface_reg_3; + + iface_reg_1 = snd_soc_read(codec, AIC32X4_IFACE1); + iface_reg_1 = iface_reg_1 & ~(3 << 6 | 3 << 2); + iface_reg_2 = snd_soc_read(codec, AIC32X4_IFACE2); + iface_reg_2 = 0; + iface_reg_3 = snd_soc_read(codec, AIC32X4_IFACE3); + iface_reg_3 = iface_reg_3 & ~(1 << 3); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + aic32x4->master = 1; + iface_reg_1 |= AIC32X4_BCLKMASTER | AIC32X4_WCLKMASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + aic32x4->master = 0; + break; + default: + printk(KERN_ERR "aic32x4: invalid DAI master/slave interface\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_DSP_A: + iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT); + iface_reg_3 |= (1 << 3); /* invert bit clock */ + iface_reg_2 = 0x01; /* add offset 1 */ + break; + case SND_SOC_DAIFMT_DSP_B: + iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT); + iface_reg_3 |= (1 << 3); /* invert bit clock */ + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface_reg_1 |= + (AIC32X4_RIGHT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT); + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_reg_1 |= + (AIC32X4_LEFT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT); + break; + default: + printk(KERN_ERR "aic32x4: invalid DAI interface format\n"); + return -EINVAL; + } + + snd_soc_write(codec, AIC32X4_IFACE1, iface_reg_1); + snd_soc_write(codec, AIC32X4_IFACE2, iface_reg_2); + snd_soc_write(codec, AIC32X4_IFACE3, iface_reg_3); + return 0; +} + +static int aic32x4_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 data; + int i; + + i = aic32x4_get_divs(aic32x4->sysclk, params_rate(params)); + if (i < 0) { + printk(KERN_ERR "aic32x4: sampling rate not supported\n"); + return i; + } + + /* Use PLL as CODEC_CLKIN and DAC_MOD_CLK as BDIV_CLKIN */ + snd_soc_write(codec, AIC32X4_CLKMUX, AIC32X4_PLLCLKIN); + snd_soc_write(codec, AIC32X4_IFACE3, AIC32X4_DACMOD2BCLK); + + /* We will fix R value to 1 and will make P & J=K.D as varialble */ + data = snd_soc_read(codec, AIC32X4_PLLPR); + data &= ~(7 << 4); + snd_soc_write(codec, AIC32X4_PLLPR, + (data | (aic32x4_divs[i].p_val << 4) | 0x01)); + + snd_soc_write(codec, AIC32X4_PLLJ, aic32x4_divs[i].pll_j); + + snd_soc_write(codec, AIC32X4_PLLDMSB, (aic32x4_divs[i].pll_d >> 8)); + snd_soc_write(codec, AIC32X4_PLLDLSB, + (aic32x4_divs[i].pll_d & 0xff)); + + /* NDAC divider value */ + data = snd_soc_read(codec, AIC32X4_NDAC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_NDAC, data | aic32x4_divs[i].ndac); + + /* MDAC divider value */ + data = snd_soc_read(codec, AIC32X4_MDAC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_MDAC, data | aic32x4_divs[i].mdac); + + /* DOSR MSB & LSB values */ + snd_soc_write(codec, AIC32X4_DOSRMSB, aic32x4_divs[i].dosr >> 8); + snd_soc_write(codec, AIC32X4_DOSRLSB, + (aic32x4_divs[i].dosr & 0xff)); + + /* NADC divider value */ + data = snd_soc_read(codec, AIC32X4_NADC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_NADC, data | aic32x4_divs[i].nadc); + + /* MADC divider value */ + data = snd_soc_read(codec, AIC32X4_MADC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_MADC, data | aic32x4_divs[i].madc); + + /* AOSR value */ + snd_soc_write(codec, AIC32X4_AOSR, aic32x4_divs[i].aosr); + + /* BCLK N divider */ + data = snd_soc_read(codec, AIC32X4_BCLKN); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_BCLKN, data | aic32x4_divs[i].blck_N); + + data = snd_soc_read(codec, AIC32X4_IFACE1); + data = data & ~(3 << 4); + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + data |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_DOSRMSB_SHIFT); + break; + case SNDRV_PCM_FORMAT_S24_LE: + data |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_DOSRMSB_SHIFT); + break; + case SNDRV_PCM_FORMAT_S32_LE: + data |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_DOSRMSB_SHIFT); + break; + } + snd_soc_write(codec, AIC32X4_IFACE1, data); + + return 0; +} + +static int aic32x4_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u8 dac_reg; + + dac_reg = snd_soc_read(codec, AIC32X4_DACMUTE) & ~AIC32X4_MUTEON; + if (mute) + snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg | AIC32X4_MUTEON); + else + snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg); + return 0; +} + +static int aic32x4_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 value; + + switch (level) { + case SND_SOC_BIAS_ON: + if (aic32x4->master) { + /* Switch on PLL */ + value = snd_soc_read(codec, AIC32X4_PLLPR); + snd_soc_write(codec, AIC32X4_PLLPR, + (value | AIC32X4_PLLEN)); + + /* Switch on NDAC Divider */ + value = snd_soc_read(codec, AIC32X4_NDAC); + snd_soc_write(codec, AIC32X4_NDAC, + value | AIC32X4_NDACEN); + + /* Switch on MDAC Divider */ + value = snd_soc_read(codec, AIC32X4_MDAC); + snd_soc_write(codec, AIC32X4_MDAC, + value | AIC32X4_MDACEN); + + /* Switch on NADC Divider */ + value = snd_soc_read(codec, AIC32X4_NADC); + snd_soc_write(codec, AIC32X4_NADC, + value | AIC32X4_MDACEN); + + /* Switch on MADC Divider */ + value = snd_soc_read(codec, AIC32X4_MADC); + snd_soc_write(codec, AIC32X4_MADC, + value | AIC32X4_MDACEN); + + /* Switch on BCLK_N Divider */ + value = snd_soc_read(codec, AIC32X4_BCLKN); + snd_soc_write(codec, AIC32X4_BCLKN, + value | AIC32X4_BCLKEN); + } + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (aic32x4->master) { + /* Switch off PLL */ + value = snd_soc_read(codec, AIC32X4_PLLPR); + snd_soc_write(codec, AIC32X4_PLLPR, + (value & ~AIC32X4_PLLEN)); + + /* Switch off NDAC Divider */ + value = snd_soc_read(codec, AIC32X4_NDAC); + snd_soc_write(codec, AIC32X4_NDAC, + value & ~AIC32X4_NDACEN); + + /* Switch off MDAC Divider */ + value = snd_soc_read(codec, AIC32X4_MDAC); + snd_soc_write(codec, AIC32X4_MDAC, + value & ~AIC32X4_MDACEN); + + /* Switch off NADC Divider */ + value = snd_soc_read(codec, AIC32X4_NADC); + snd_soc_write(codec, AIC32X4_NADC, + value & ~AIC32X4_NDACEN); + + /* Switch off MADC Divider */ + value = snd_soc_read(codec, AIC32X4_MADC); + snd_soc_write(codec, AIC32X4_MADC, + value & ~AIC32X4_MDACEN); + value = snd_soc_read(codec, AIC32X4_BCLKN); + + /* Switch off BCLK_N Divider */ + snd_soc_write(codec, AIC32X4_BCLKN, + value & ~AIC32X4_BCLKEN); + } + break; + case SND_SOC_BIAS_OFF: + break; + } + codec->bias_level = level; + return 0; +} + +#define AIC32X4_RATES SNDRV_PCM_RATE_8000_48000 +#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_ops aic32x4_ops = { + .hw_params = aic32x4_hw_params, + .digital_mute = aic32x4_mute, + .set_fmt = aic32x4_set_dai_fmt, + .set_sysclk = aic32x4_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver aic32x4_dai = { + .name = "tlv320aic32x4-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AIC32X4_RATES, + .formats = AIC32X4_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AIC32X4_RATES, + .formats = AIC32X4_FORMATS,}, + .ops = &aic32x4_ops, + .symmetric_rates = 1, +}; + +static int aic32x4_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int aic32x4_resume(struct snd_soc_codec *codec) +{ + aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} + +static int aic32x4_probe(struct snd_soc_codec *codec) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u32 tmp_reg; + + codec->hw_write = (hw_write_t) i2c_master_send; + codec->control_data = aic32x4->control_data; + + snd_soc_write(codec, AIC32X4_RESET, 0x01); + + /* Power platform configuration */ + if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) { + snd_soc_write(codec, AIC32X4_MICBIAS, AIC32X4_MICBIAS_LDOIN | + AIC32X4_MICBIAS_2075V); + } + if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) { + snd_soc_write(codec, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE); + } + if (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) { + snd_soc_write(codec, AIC32X4_LDOCTL, AIC32X4_LDOCTLEN); + } + tmp_reg = snd_soc_read(codec, AIC32X4_CMMODE); + if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36) { + tmp_reg |= AIC32X4_LDOIN_18_36; + } + if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED) { + tmp_reg |= AIC32X4_LDOIN2HP; + } + snd_soc_write(codec, AIC32X4_CMMODE, tmp_reg); + + /* Do DACs need to be swapped? */ + if (aic32x4->swapdacs) { + snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2RCHN | AIC32X4_RDAC2LCHN); + } else { + snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN); + } + + /* Mic PGA routing */ + if (aic32x4->micpga_routing | AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) { + snd_soc_write(codec, AIC32X4_LMICPGANIN, AIC32X4_LMICPGANIN_IN2R_10K); + } + if (aic32x4->micpga_routing | AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) { + snd_soc_write(codec, AIC32X4_RMICPGANIN, AIC32X4_RMICPGANIN_IN1L_10K); + } + + aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_add_controls(codec, aic32x4_snd_controls, + ARRAY_SIZE(aic32x4_snd_controls)); + aic32x4_add_widgets(codec); + + return 0; +} + +static int aic32x4_remove(struct snd_soc_codec *codec) +{ + aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = { + .read = aic32x4_read, + .write = aic32x4_write, + .probe = aic32x4_probe, + .remove = aic32x4_remove, + .suspend = aic32x4_suspend, + .resume = aic32x4_resume, + .set_bias_level = aic32x4_set_bias_level, +}; + +static __devinit int aic32x4_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct aic32x4_pdata *pdata = i2c->dev.platform_data; + struct aic32x4_priv *aic32x4; + int ret; + + aic32x4 = kzalloc(sizeof(struct aic32x4_priv), GFP_KERNEL); + if (aic32x4 == NULL) + return -ENOMEM; + + aic32x4->control_data = i2c; + i2c_set_clientdata(i2c, aic32x4); + + if (pdata) { + aic32x4->power_cfg = pdata->power_cfg; + aic32x4->swapdacs = pdata->swapdacs; + aic32x4->micpga_routing = pdata->micpga_routing; + } else { + aic32x4->power_cfg = 0; + aic32x4->swapdacs = false; + aic32x4->micpga_routing = 0; + } + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_aic32x4, &aic32x4_dai, 1); + if (ret < 0) + kfree(aic32x4); + return ret; +} + +static __devexit int aic32x4_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + kfree(i2c_get_clientdata(client)); + return 0; +} + +static const struct i2c_device_id aic32x4_i2c_id[] = { + { "tlv320aic32x4", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id); + +static struct i2c_driver aic32x4_i2c_driver = { + .driver = { + .name = "tlv320aic32x4", + .owner = THIS_MODULE, + }, + .probe = aic32x4_i2c_probe, + .remove = __devexit_p(aic32x4_i2c_remove), + .id_table = aic32x4_i2c_id, +}; + +static int __init aic32x4_modinit(void) +{ + int ret = 0; + + ret = i2c_add_driver(&aic32x4_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register aic32x4 I2C driver: %d\n", + ret); + } + return ret; +} +module_init(aic32x4_modinit); + +static void __exit aic32x4_exit(void) +{ + i2c_del_driver(&aic32x4_i2c_driver); +} +module_exit(aic32x4_exit); + +MODULE_DESCRIPTION("ASoC tlv320aic32x4 codec driver"); +MODULE_AUTHOR("Javier Martin "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h new file mode 100644 index 000000000000..aae2b2440398 --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -0,0 +1,143 @@ +/* + * tlv320aic32x4.h + * + * 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 _TLV320AIC32X4_H +#define _TLV320AIC32X4_H + +/* tlv320aic32x4 register space (in decimal to match datasheet) */ + +#define AIC32X4_PAGE1 128 + +#define AIC32X4_PSEL 0 +#define AIC32X4_RESET 1 +#define AIC32X4_CLKMUX 4 +#define AIC32X4_PLLPR 5 +#define AIC32X4_PLLJ 6 +#define AIC32X4_PLLDMSB 7 +#define AIC32X4_PLLDLSB 8 +#define AIC32X4_NDAC 11 +#define AIC32X4_MDAC 12 +#define AIC32X4_DOSRMSB 13 +#define AIC32X4_DOSRLSB 14 +#define AIC32X4_NADC 18 +#define AIC32X4_MADC 19 +#define AIC32X4_AOSR 20 +#define AIC32X4_CLKMUX2 25 +#define AIC32X4_CLKOUTM 26 +#define AIC32X4_IFACE1 27 +#define AIC32X4_IFACE2 28 +#define AIC32X4_IFACE3 29 +#define AIC32X4_BCLKN 30 +#define AIC32X4_IFACE4 31 +#define AIC32X4_IFACE5 32 +#define AIC32X4_IFACE6 33 +#define AIC32X4_DOUTCTL 53 +#define AIC32X4_DINCTL 54 +#define AIC32X4_DACSPB 60 +#define AIC32X4_ADCSPB 61 +#define AIC32X4_DACSETUP 63 +#define AIC32X4_DACMUTE 64 +#define AIC32X4_LDACVOL 65 +#define AIC32X4_RDACVOL 66 +#define AIC32X4_ADCSETUP 81 +#define AIC32X4_ADCFGA 82 +#define AIC32X4_LADCVOL 83 +#define AIC32X4_RADCVOL 84 +#define AIC32X4_LAGC1 86 +#define AIC32X4_LAGC2 87 +#define AIC32X4_LAGC3 88 +#define AIC32X4_LAGC4 89 +#define AIC32X4_LAGC5 90 +#define AIC32X4_LAGC6 91 +#define AIC32X4_LAGC7 92 +#define AIC32X4_RAGC1 94 +#define AIC32X4_RAGC2 95 +#define AIC32X4_RAGC3 96 +#define AIC32X4_RAGC4 97 +#define AIC32X4_RAGC5 98 +#define AIC32X4_RAGC6 99 +#define AIC32X4_RAGC7 100 +#define AIC32X4_PWRCFG (AIC32X4_PAGE1 + 1) +#define AIC32X4_LDOCTL (AIC32X4_PAGE1 + 2) +#define AIC32X4_OUTPWRCTL (AIC32X4_PAGE1 + 9) +#define AIC32X4_CMMODE (AIC32X4_PAGE1 + 10) +#define AIC32X4_HPLROUTE (AIC32X4_PAGE1 + 12) +#define AIC32X4_HPRROUTE (AIC32X4_PAGE1 + 13) +#define AIC32X4_LOLROUTE (AIC32X4_PAGE1 + 14) +#define AIC32X4_LORROUTE (AIC32X4_PAGE1 + 15) +#define AIC32X4_HPLGAIN (AIC32X4_PAGE1 + 16) +#define AIC32X4_HPRGAIN (AIC32X4_PAGE1 + 17) +#define AIC32X4_LOLGAIN (AIC32X4_PAGE1 + 18) +#define AIC32X4_LORGAIN (AIC32X4_PAGE1 + 19) +#define AIC32X4_HEADSTART (AIC32X4_PAGE1 + 20) +#define AIC32X4_MICBIAS (AIC32X4_PAGE1 + 51) +#define AIC32X4_LMICPGAPIN (AIC32X4_PAGE1 + 52) +#define AIC32X4_LMICPGANIN (AIC32X4_PAGE1 + 54) +#define AIC32X4_RMICPGAPIN (AIC32X4_PAGE1 + 55) +#define AIC32X4_RMICPGANIN (AIC32X4_PAGE1 + 57) +#define AIC32X4_FLOATINGINPUT (AIC32X4_PAGE1 + 58) +#define AIC32X4_LMICPGAVOL (AIC32X4_PAGE1 + 59) +#define AIC32X4_RMICPGAVOL (AIC32X4_PAGE1 + 60) + +#define AIC32X4_FREQ_12000000 12000000 +#define AIC32X4_FREQ_24000000 24000000 +#define AIC32X4_FREQ_25000000 25000000 + +#define AIC32X4_WORD_LEN_16BITS 0x00 +#define AIC32X4_WORD_LEN_20BITS 0x01 +#define AIC32X4_WORD_LEN_24BITS 0x02 +#define AIC32X4_WORD_LEN_32BITS 0x03 + +#define AIC32X4_I2S_MODE 0x00 +#define AIC32X4_DSP_MODE 0x01 +#define AIC32X4_RIGHT_JUSTIFIED_MODE 0x02 +#define AIC32X4_LEFT_JUSTIFIED_MODE 0x03 + +#define AIC32X4_AVDDWEAKDISABLE 0x08 +#define AIC32X4_LDOCTLEN 0x01 + +#define AIC32X4_LDOIN_18_36 0x01 +#define AIC32X4_LDOIN2HP 0x02 + +#define AIC32X4_DACSPBLOCK_MASK 0x1f +#define AIC32X4_ADCSPBLOCK_MASK 0x1f + +#define AIC32X4_PLLJ_SHIFT 6 +#define AIC32X4_DOSRMSB_SHIFT 4 + +#define AIC32X4_PLLCLKIN 0x03 + +#define AIC32X4_MICBIAS_LDOIN 0x08 +#define AIC32X4_MICBIAS_2075V 0x60 + +#define AIC32X4_LMICPGANIN_IN2R_10K 0x10 +#define AIC32X4_RMICPGANIN_IN1L_10K 0x10 + +#define AIC32X4_LMICPGAVOL_NOGAIN 0x80 +#define AIC32X4_RMICPGAVOL_NOGAIN 0x80 + +#define AIC32X4_BCLKMASTER 0x08 +#define AIC32X4_WCLKMASTER 0x04 +#define AIC32X4_PLLEN (0x01 << 7) +#define AIC32X4_NDACEN (0x01 << 7) +#define AIC32X4_MDACEN (0x01 << 7) +#define AIC32X4_NADCEN (0x01 << 7) +#define AIC32X4_MADCEN (0x01 << 7) +#define AIC32X4_BCLKEN (0x01 << 7) +#define AIC32X4_DACEN (0x03 << 6) +#define AIC32X4_RDAC2LCHN (0x02 << 2) +#define AIC32X4_LDAC2RCHN (0x02 << 4) +#define AIC32X4_LDAC2LCHN (0x01 << 4) +#define AIC32X4_RDAC2RCHN (0x01 << 2) + +#define AIC32X4_SSTEP2WCLK 0x01 +#define AIC32X4_MUTEON 0x0C +#define AIC32X4_DACMOD2BCLK 0x01 + +#endif /* _TLV320AIC32X4_H */ -- cgit v1.2.3 From 60bf8bf8815e6adea4c1d0423578c3b8000e2ec8 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 4 Mar 2011 12:24:28 -0800 Subject: libceph: fix msgr backoff With commit f363e45f we replaced a bunch of hacky workqueue mutual exclusion logic with the WQ_NON_REENTRANT flag. One pieces of fallout is that the exponential backoff breaks in certain cases: * con_work attempts to connect. * we get an immediate failure, and the socket state change handler queues immediate work. * con_work calls con_fault, we decide to back off, but can't queue delayed work. In this case, we add a BACKOFF bit to make con_work reschedule delayed work next time it runs (which should be immediately). Signed-off-by: Sage Weil --- include/linux/ceph/messenger.h | 1 + net/ceph/messenger.c | 30 ++++++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index c3011beac30d..eb31e108a64d 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -123,6 +123,7 @@ struct ceph_msg_pos { #define SOCK_CLOSED 11 /* socket state changed to closed */ #define OPENING 13 /* open connection w/ (possibly new) peer */ #define DEAD 14 /* dead, about to kfree */ +#define BACKOFF 15 /* * A single connection with another host. diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 6bd5025f6220..46fbc422ba74 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -1949,6 +1949,19 @@ static void con_work(struct work_struct *work) work.work); mutex_lock(&con->mutex); + if (test_and_clear_bit(BACKOFF, &con->state)) { + dout("con_work %p backing off\n", con); + if (queue_delayed_work(ceph_msgr_wq, &con->work, + round_jiffies_relative(con->delay))) { + dout("con_work %p backoff %lu\n", con, con->delay); + mutex_unlock(&con->mutex); + return; + } else { + con->ops->put(con); + dout("con_work %p FAILED to back off %lu\n", con, + con->delay); + } + } if (test_bit(CLOSED, &con->state)) { /* e.g. if we are replaced */ dout("con_work CLOSED\n"); @@ -2017,11 +2030,24 @@ static void ceph_fault(struct ceph_connection *con) con->delay = BASE_DELAY_INTERVAL; else if (con->delay < MAX_DELAY_INTERVAL) con->delay *= 2; - dout("fault queueing %p delay %lu\n", con, con->delay); con->ops->get(con); if (queue_delayed_work(ceph_msgr_wq, &con->work, - round_jiffies_relative(con->delay)) == 0) + round_jiffies_relative(con->delay))) { + dout("fault queued %p delay %lu\n", con, con->delay); + } else { con->ops->put(con); + dout("fault failed to queue %p delay %lu, backoff\n", + con, con->delay); + /* + * In many cases we see a socket state change + * while con_work is running and end up + * queuing (non-delayed) work, such that we + * can't backoff with a delay. Set a flag so + * that when con_work restarts we schedule the + * delay then. + */ + set_bit(BACKOFF, &con->state); + } } out_unlock: -- cgit v1.2.3 From e76661d0a59e53e5cc4dccbe4b755d1dc8a968ec Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 3 Mar 2011 10:10:15 -0800 Subject: libceph: fix msgr keepalive flag There was some broken keepalive code using a dead variable. Shift to using the proper bit flag. Signed-off-by: Sage Weil --- include/linux/ceph/messenger.h | 1 - net/ceph/messenger.c | 9 ++++----- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index eb31e108a64d..31d91a64838b 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -161,7 +161,6 @@ struct ceph_connection { struct list_head out_queue; struct list_head out_sent; /* sending or sent but unacked */ u64 out_seq; /* last message queued for send */ - bool out_keepalive_pending; u64 in_seq, in_seq_acked; /* last message received, acked */ diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 46fbc422ba74..3252ea974e8f 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -336,7 +336,6 @@ static void reset_connection(struct ceph_connection *con) ceph_msg_put(con->out_msg); con->out_msg = NULL; } - con->out_keepalive_pending = false; con->in_seq = 0; con->in_seq_acked = 0; } @@ -2019,10 +2018,10 @@ static void ceph_fault(struct ceph_connection *con) /* Requeue anything that hasn't been acked */ list_splice_init(&con->out_sent, &con->out_queue); - /* If there are no messages in the queue, place the connection - * in a STANDBY state (i.e., don't try to reconnect just yet). */ - if (list_empty(&con->out_queue) && !con->out_keepalive_pending) { - dout("fault setting STANDBY\n"); + /* If there are no messages queued or keepalive pending, place + * the connection in a STANDBY state */ + if (list_empty(&con->out_queue) && + !test_bit(KEEPALIVE_PENDING, &con->state)) { set_bit(STANDBY, &con->state); } else { /* retry after a delay. */ -- cgit v1.2.3 From 2f5f9486f8c12e3aa40fe3775a18cb14efc5cea2 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 4 Mar 2011 17:36:29 -0800 Subject: mm: change alloc_pages_vma to pass down the policy node for local policy Currently alloc_pages_vma() always uses the local node as policy node for the LOCAL policy. Pass this node down as an argument instead. No behaviour change from this patch, but will be needed for followons. Acked-by: Andrea Arcangeli Signed-off-by: Andi Kleen Reviewed-by: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/gfp.h | 9 +++++---- mm/huge_memory.c | 2 +- mm/mempolicy.c | 11 +++++------ 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/gfp.h b/include/linux/gfp.h index 0b84c61607e8..37b8af5db091 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -332,16 +332,17 @@ alloc_pages(gfp_t gfp_mask, unsigned int order) return alloc_pages_current(gfp_mask, order); } extern struct page *alloc_pages_vma(gfp_t gfp_mask, int order, - struct vm_area_struct *vma, unsigned long addr); + struct vm_area_struct *vma, unsigned long addr, + int node); #else #define alloc_pages(gfp_mask, order) \ alloc_pages_node(numa_node_id(), gfp_mask, order) -#define alloc_pages_vma(gfp_mask, order, vma, addr) \ +#define alloc_pages_vma(gfp_mask, order, vma, addr, node) \ alloc_pages(gfp_mask, order) #endif #define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0) -#define alloc_page_vma(gfp_mask, vma, addr) \ - alloc_pages_vma(gfp_mask, 0, vma, addr) +#define alloc_page_vma(gfp_mask, vma, addr) \ + alloc_pages_vma(gfp_mask, 0, vma, addr, numa_node_id()) extern unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order); extern unsigned long get_zeroed_page(gfp_t gfp_mask); diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 3e29781ee762..c7c2cd925599 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -653,7 +653,7 @@ static inline struct page *alloc_hugepage_vma(int defrag, unsigned long haddr) { return alloc_pages_vma(alloc_hugepage_gfpmask(defrag), - HPAGE_PMD_ORDER, vma, haddr); + HPAGE_PMD_ORDER, vma, haddr, numa_node_id()); } #ifndef CONFIG_NUMA diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 49355a970be2..25a5a9146619 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -1524,10 +1524,9 @@ static nodemask_t *policy_nodemask(gfp_t gfp, struct mempolicy *policy) } /* Return a zonelist indicated by gfp for node representing a mempolicy */ -static struct zonelist *policy_zonelist(gfp_t gfp, struct mempolicy *policy) +static struct zonelist *policy_zonelist(gfp_t gfp, struct mempolicy *policy, + int nd) { - int nd = numa_node_id(); - switch (policy->mode) { case MPOL_PREFERRED: if (!(policy->flags & MPOL_F_LOCAL)) @@ -1679,7 +1678,7 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr, zl = node_zonelist(interleave_nid(*mpol, vma, addr, huge_page_shift(hstate_vma(vma))), gfp_flags); } else { - zl = policy_zonelist(gfp_flags, *mpol); + zl = policy_zonelist(gfp_flags, *mpol, numa_node_id()); if ((*mpol)->mode == MPOL_BIND) *nodemask = &(*mpol)->v.nodes; } @@ -1820,7 +1819,7 @@ static struct page *alloc_page_interleave(gfp_t gfp, unsigned order, */ struct page * alloc_pages_vma(gfp_t gfp, int order, struct vm_area_struct *vma, - unsigned long addr) + unsigned long addr, int node) { struct mempolicy *pol = get_vma_policy(current, vma, addr); struct zonelist *zl; @@ -1836,7 +1835,7 @@ alloc_pages_vma(gfp_t gfp, int order, struct vm_area_struct *vma, put_mems_allowed(); return page; } - zl = policy_zonelist(gfp, pol); + zl = policy_zonelist(gfp, pol, node); if (unlikely(mpol_needs_cond_ref(pol))) { /* * slow path: ref counted shared policy -- cgit v1.2.3 From 236344d6b417d05a3080477639234fd9ca97568d Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 4 Mar 2011 17:36:30 -0800 Subject: mm: add alloc_page_vma_node() Add a alloc_page_vma_node that allows passing the "local" node in. Used in a followon patch. Acked-by: Andrea Arcangeli Signed-off-by: Andi Kleen Reviewed-by: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/gfp.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/gfp.h b/include/linux/gfp.h index 37b8af5db091..dca31761b311 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -343,6 +343,8 @@ extern struct page *alloc_pages_vma(gfp_t gfp_mask, int order, #define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0) #define alloc_page_vma(gfp_mask, vma, addr) \ alloc_pages_vma(gfp_mask, 0, vma, addr, numa_node_id()) +#define alloc_page_vma_node(gfp_mask, vma, addr, node) \ + alloc_pages_vma(gfp_mask, 0, vma, addr, node) extern unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order); extern unsigned long get_zeroed_page(gfp_t gfp_mask); -- cgit v1.2.3 From 4157434c23f8f5126a2ffd3cc7b2c3bd928be075 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 4 Mar 2011 21:31:48 -0800 Subject: ipv4: Use passed-in protocol in ip_route_newports(). Signed-off-by: David S. Miller --- include/net/route.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/route.h b/include/net/route.h index 60daf745216a..8905d90e0044 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -217,7 +217,7 @@ static inline struct rtable *ip_route_newports(struct rtable *rt, .fl4_dst = rt->fl.fl4_dst, .fl4_src = rt->fl.fl4_src, .fl4_tos = rt->fl.fl4_tos, - .proto = rt->fl.proto, + .proto = protocol, .fl_ip_sport = sport, .fl_ip_dport = dport }; -- cgit v1.2.3 From 5e2b61f78411be25f0b84f97d5b5d312f184dfd1 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 4 Mar 2011 21:47:09 -0800 Subject: ipv4: Remove flowi from struct rtable. The only necessary parts are the src/dst addresses, the interface indexes, the TOS, and the mark. The rest is unnecessary bloat, which amounts to nearly 50 bytes on 64-bit. Signed-off-by: David S. Miller --- include/net/route.h | 22 ++++--- net/ipv4/icmp.c | 2 +- net/ipv4/ipmr.c | 52 ++++++++++++---- net/ipv4/route.c | 153 ++++++++++++++++++++++++++---------------------- net/ipv4/xfrm4_policy.c | 7 ++- net/sched/cls_route.c | 2 +- net/sched/em_meta.c | 2 +- 7 files changed, 146 insertions(+), 94 deletions(-) (limited to 'include') diff --git a/include/net/route.h b/include/net/route.h index 8905d90e0044..9257f5f17337 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -53,16 +53,20 @@ struct fib_info; struct rtable { struct dst_entry dst; - /* Cache lookup keys */ - struct flowi fl; + /* Lookup key. */ + __be32 rt_key_dst; + __be32 rt_key_src; int rt_genid; unsigned rt_flags; __u16 rt_type; + __u8 rt_tos; __be32 rt_dst; /* Path destination */ __be32 rt_src; /* Path source */ int rt_iif; + int rt_oif; + __u32 rt_mark; /* Info on neighbour */ __be32 rt_gateway; @@ -76,12 +80,12 @@ struct rtable { static inline bool rt_is_input_route(struct rtable *rt) { - return rt->fl.iif != 0; + return rt->rt_iif != 0; } static inline bool rt_is_output_route(struct rtable *rt) { - return rt->fl.iif == 0; + return rt->rt_iif == 0; } struct ip_rt_acct { @@ -212,11 +216,11 @@ static inline struct rtable *ip_route_newports(struct rtable *rt, __be16 dport, struct sock *sk) { if (sport != orig_sport || dport != orig_dport) { - struct flowi fl = { .oif = rt->fl.oif, - .mark = rt->fl.mark, - .fl4_dst = rt->fl.fl4_dst, - .fl4_src = rt->fl.fl4_src, - .fl4_tos = rt->fl.fl4_tos, + struct flowi fl = { .oif = rt->rt_oif, + .mark = rt->rt_mark, + .fl4_dst = rt->rt_key_dst, + .fl4_src = rt->rt_key_src, + .fl4_tos = rt->rt_tos, .proto = protocol, .fl_ip_sport = sport, .fl_ip_dport = dport }; diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 994a785d98f9..1771ce662548 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -563,7 +563,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) rcu_read_lock(); if (rt_is_input_route(rt) && net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr) - dev = dev_get_by_index_rcu(net, rt->fl.iif); + dev = dev_get_by_index_rcu(net, rt->rt_iif); if (dev) saddr = inet_select_addr(dev, 0, RT_SCOPE_LINK); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 26ca2f2d37ce..9d5f6340af13 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1813,12 +1813,22 @@ int ip_mr_input(struct sk_buff *skb) if (IPCB(skb)->flags & IPSKB_FORWARDED) goto dont_forward; - err = ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt); - if (err < 0) { - kfree_skb(skb); - return err; + { + struct rtable *rt = skb_rtable(skb); + struct flowi fl = { + .fl4_dst = rt->rt_key_dst, + .fl4_src = rt->rt_key_src, + .fl4_tos = rt->rt_tos, + .oif = rt->rt_oif, + .iif = rt->rt_iif, + .mark = rt->rt_mark, + }; + err = ipmr_fib_lookup(net, &fl, &mrt); + if (err < 0) { + kfree_skb(skb); + return err; + } } - if (!local) { if (IPCB(skb)->opt.router_alert) { if (ip_call_ra_chain(skb)) @@ -1946,9 +1956,19 @@ int pim_rcv_v1(struct sk_buff *skb) pim = igmp_hdr(skb); - if (ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt) < 0) - goto drop; - + { + struct rtable *rt = skb_rtable(skb); + struct flowi fl = { + .fl4_dst = rt->rt_key_dst, + .fl4_src = rt->rt_key_src, + .fl4_tos = rt->rt_tos, + .oif = rt->rt_oif, + .iif = rt->rt_iif, + .mark = rt->rt_mark, + }; + if (ipmr_fib_lookup(net, &fl, &mrt) < 0) + goto drop; + } if (!mrt->mroute_do_pim || pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) goto drop; @@ -1978,9 +1998,19 @@ static int pim_rcv(struct sk_buff *skb) csum_fold(skb_checksum(skb, 0, skb->len, 0)))) goto drop; - if (ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt) < 0) - goto drop; - + { + struct rtable *rt = skb_rtable(skb); + struct flowi fl = { + .fl4_dst = rt->rt_key_dst, + .fl4_src = rt->rt_key_src, + .fl4_tos = rt->rt_tos, + .oif = rt->rt_oif, + .iif = rt->rt_iif, + .mark = rt->rt_mark, + }; + if (ipmr_fib_lookup(net, &fl, &mrt) < 0) + goto drop; + } if (__pim_rcv(mrt, skb, sizeof(*pim))) { drop: kfree_skb(skb); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 602473c92019..92a24ea34c1b 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -424,7 +424,7 @@ static int rt_cache_seq_show(struct seq_file *seq, void *v) dst_metric(&r->dst, RTAX_WINDOW), (int)((dst_metric(&r->dst, RTAX_RTT) >> 3) + dst_metric(&r->dst, RTAX_RTTVAR)), - r->fl.fl4_tos, + r->rt_tos, r->dst.hh ? atomic_read(&r->dst.hh->hh_refcnt) : -1, r->dst.hh ? (r->dst.hh->hh_output == dev_queue_xmit) : 0, @@ -711,22 +711,22 @@ static inline bool rt_caching(const struct net *net) net->ipv4.sysctl_rt_cache_rebuild_count; } -static inline bool compare_hash_inputs(const struct flowi *fl1, - const struct flowi *fl2) +static inline bool compare_hash_inputs(const struct rtable *rt1, + const struct rtable *rt2) { - return ((((__force u32)fl1->fl4_dst ^ (__force u32)fl2->fl4_dst) | - ((__force u32)fl1->fl4_src ^ (__force u32)fl2->fl4_src) | - (fl1->iif ^ fl2->iif)) == 0); + return ((((__force u32)rt1->rt_key_dst ^ (__force u32)rt2->rt_key_dst) | + ((__force u32)rt1->rt_key_src ^ (__force u32)rt2->rt_key_src) | + (rt1->rt_iif ^ rt2->rt_iif)) == 0); } -static inline int compare_keys(struct flowi *fl1, struct flowi *fl2) +static inline int compare_keys(struct rtable *rt1, struct rtable *rt2) { - return (((__force u32)fl1->fl4_dst ^ (__force u32)fl2->fl4_dst) | - ((__force u32)fl1->fl4_src ^ (__force u32)fl2->fl4_src) | - (fl1->mark ^ fl2->mark) | - (*(u16 *)&fl1->fl4_tos ^ *(u16 *)&fl2->fl4_tos) | - (fl1->oif ^ fl2->oif) | - (fl1->iif ^ fl2->iif)) == 0; + return (((__force u32)rt1->rt_key_dst ^ (__force u32)rt2->rt_key_dst) | + ((__force u32)rt1->rt_key_src ^ (__force u32)rt2->rt_key_src) | + (rt1->rt_mark ^ rt2->rt_mark) | + (rt1->rt_tos ^ rt2->rt_tos) | + (rt1->rt_oif ^ rt2->rt_oif) | + (rt1->rt_iif ^ rt2->rt_iif)) == 0; } static inline int compare_netns(struct rtable *rt1, struct rtable *rt2) @@ -813,7 +813,7 @@ static int has_noalias(const struct rtable *head, const struct rtable *rth) const struct rtable *aux = head; while (aux != rth) { - if (compare_hash_inputs(&aux->fl, &rth->fl)) + if (compare_hash_inputs(aux, rth)) return 0; aux = rcu_dereference_protected(aux->dst.rt_next, 1); } @@ -1073,7 +1073,7 @@ restart: rt_free(rth); continue; } - if (compare_keys(&rth->fl, &rt->fl) && compare_netns(rth, rt)) { + if (compare_keys(rth, rt) && compare_netns(rth, rt)) { /* Put it first */ *rthp = rth->dst.rt_next; /* @@ -1136,7 +1136,7 @@ restart: rt_emergency_hash_rebuild(net); spin_unlock_bh(rt_hash_lock_addr(hash)); - hash = rt_hash(rt->fl.fl4_dst, rt->fl.fl4_src, + hash = rt_hash(rt->rt_key_dst, rt->rt_key_src, ifindex, rt_genid(net)); goto restart; } @@ -1344,12 +1344,12 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst) ip_rt_put(rt); ret = NULL; } else if (rt->rt_flags & RTCF_REDIRECTED) { - unsigned hash = rt_hash(rt->fl.fl4_dst, rt->fl.fl4_src, - rt->fl.oif, + unsigned hash = rt_hash(rt->rt_key_dst, rt->rt_key_src, + rt->rt_oif, rt_genid(dev_net(dst->dev))); #if RT_CACHE_DEBUG >= 1 printk(KERN_DEBUG "ipv4_negative_advice: redirect to %pI4/%02x dropped\n", - &rt->rt_dst, rt->fl.fl4_tos); + &rt->rt_dst, rt->rt_tos); #endif rt_del(hash, rt); ret = NULL; @@ -1697,8 +1697,17 @@ void ip_rt_get_source(u8 *addr, struct rtable *rt) if (rt_is_output_route(rt)) src = rt->rt_src; else { + struct flowi fl = { + .fl4_dst = rt->rt_key_dst, + .fl4_src = rt->rt_key_src, + .fl4_tos = rt->rt_tos, + .oif = rt->rt_oif, + .iif = rt->rt_iif, + .mark = rt->rt_mark, + }; + rcu_read_lock(); - if (fib_lookup(dev_net(rt->dst.dev), &rt->fl, &res) == 0) + if (fib_lookup(dev_net(rt->dst.dev), &fl, &res) == 0) src = FIB_RES_PREFSRC(res); else src = inet_select_addr(rt->dst.dev, rt->rt_gateway, @@ -1748,7 +1757,8 @@ 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) +static void rt_init_metrics(struct rtable *rt, const struct flowi *oldflp, + struct fib_info *fi) { struct inet_peer *peer; int create = 0; @@ -1756,7 +1766,7 @@ static void rt_init_metrics(struct rtable *rt, struct fib_info *fi) /* If a peer entry exists for this destination, we must hook * it up in order to get at cached metrics. */ - if (rt->fl.flags & FLOWI_FLAG_PRECOW_METRICS) + if (oldflp && (oldflp->flags & FLOWI_FLAG_PRECOW_METRICS)) create = 1; rt->peer = peer = inet_getpeer_v4(rt->rt_dst, create); @@ -1783,7 +1793,8 @@ static void rt_init_metrics(struct rtable *rt, struct fib_info *fi) } } -static void rt_set_nexthop(struct rtable *rt, const struct fib_result *res, +static void rt_set_nexthop(struct rtable *rt, const struct flowi *oldflp, + const struct fib_result *res, struct fib_info *fi, u16 type, u32 itag) { struct dst_entry *dst = &rt->dst; @@ -1792,7 +1803,7 @@ static void rt_set_nexthop(struct rtable *rt, const struct fib_result *res, if (FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) rt->rt_gateway = FIB_RES_GW(*res); - rt_init_metrics(rt, fi); + rt_init_metrics(rt, oldflp, fi); #ifdef CONFIG_IP_ROUTE_CLASSID dst->tclassid = FIB_RES_NH(*res).nh_tclassid; #endif @@ -1861,20 +1872,19 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, rth->dst.output = ip_rt_bug; - rth->fl.fl4_dst = daddr; + rth->rt_key_dst = daddr; rth->rt_dst = daddr; - rth->fl.fl4_tos = tos; - rth->fl.mark = skb->mark; - rth->fl.fl4_src = saddr; + rth->rt_tos = tos; + rth->rt_mark = skb->mark; + rth->rt_key_src = saddr; rth->rt_src = saddr; #ifdef CONFIG_IP_ROUTE_CLASSID rth->dst.tclassid = itag; #endif - rth->rt_iif = - rth->fl.iif = dev->ifindex; + rth->rt_iif = dev->ifindex; rth->dst.dev = init_net.loopback_dev; dev_hold(rth->dst.dev); - rth->fl.oif = 0; + rth->rt_oif = 0; rth->rt_gateway = daddr; rth->rt_spec_dst= spec_dst; rth->rt_genid = rt_genid(dev_net(dev)); @@ -1999,25 +2009,24 @@ static int __mkroute_input(struct sk_buff *skb, goto cleanup; } - rth->fl.fl4_dst = daddr; + rth->rt_key_dst = daddr; rth->rt_dst = daddr; - rth->fl.fl4_tos = tos; - rth->fl.mark = skb->mark; - rth->fl.fl4_src = saddr; + rth->rt_tos = tos; + rth->rt_mark = skb->mark; + rth->rt_key_src = saddr; rth->rt_src = saddr; rth->rt_gateway = daddr; - rth->rt_iif = - rth->fl.iif = in_dev->dev->ifindex; + rth->rt_iif = in_dev->dev->ifindex; rth->dst.dev = (out_dev)->dev; dev_hold(rth->dst.dev); - rth->fl.oif = 0; + rth->rt_oif = 0; rth->rt_spec_dst= spec_dst; rth->dst.input = ip_forward; rth->dst.output = ip_output; rth->rt_genid = rt_genid(dev_net(rth->dst.dev)); - rt_set_nexthop(rth, res, res->fi, res->type, itag); + rt_set_nexthop(rth, NULL, res, res->fi, res->type, itag); rth->rt_flags = flags; @@ -2172,17 +2181,16 @@ local_input: rth->dst.output= ip_rt_bug; rth->rt_genid = rt_genid(net); - rth->fl.fl4_dst = daddr; + rth->rt_key_dst = daddr; rth->rt_dst = daddr; - rth->fl.fl4_tos = tos; - rth->fl.mark = skb->mark; - rth->fl.fl4_src = saddr; + rth->rt_tos = tos; + rth->rt_mark = skb->mark; + rth->rt_key_src = saddr; rth->rt_src = saddr; #ifdef CONFIG_IP_ROUTE_CLASSID rth->dst.tclassid = itag; #endif - rth->rt_iif = - rth->fl.iif = dev->ifindex; + rth->rt_iif = dev->ifindex; rth->dst.dev = net->loopback_dev; dev_hold(rth->dst.dev); rth->rt_gateway = daddr; @@ -2261,12 +2269,12 @@ int ip_route_input_common(struct sk_buff *skb, __be32 daddr, __be32 saddr, for (rth = rcu_dereference(rt_hash_table[hash].chain); rth; rth = rcu_dereference(rth->dst.rt_next)) { - if ((((__force u32)rth->fl.fl4_dst ^ (__force u32)daddr) | - ((__force u32)rth->fl.fl4_src ^ (__force u32)saddr) | - (rth->fl.iif ^ iif) | - rth->fl.oif | - (rth->fl.fl4_tos ^ tos)) == 0 && - rth->fl.mark == skb->mark && + if ((((__force u32)rth->rt_key_dst ^ (__force u32)daddr) | + ((__force u32)rth->rt_key_src ^ (__force u32)saddr) | + (rth->rt_iif ^ iif) | + rth->rt_oif | + (rth->rt_tos ^ tos)) == 0 && + rth->rt_mark == skb->mark && net_eq(dev_net(rth->dst.dev), net) && !rt_is_expired(rth)) { if (noref) { @@ -2374,11 +2382,11 @@ static struct rtable *__mkroute_output(const struct fib_result *res, if (!rth) return ERR_PTR(-ENOBUFS); - rth->fl.fl4_dst = oldflp->fl4_dst; - rth->fl.fl4_tos = tos; - rth->fl.fl4_src = oldflp->fl4_src; - rth->fl.oif = oldflp->oif; - rth->fl.mark = oldflp->mark; + rth->rt_key_dst = oldflp->fl4_dst; + rth->rt_tos = tos; + rth->rt_key_src = oldflp->fl4_src; + rth->rt_oif = oldflp->oif; + rth->rt_mark = oldflp->mark; rth->rt_dst = fl->fl4_dst; rth->rt_src = fl->fl4_src; rth->rt_iif = 0; @@ -2416,7 +2424,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res, #endif } - rt_set_nexthop(rth, res, fi, type, 0); + rt_set_nexthop(rth, oldflp, res, fi, type, 0); rth->rt_flags = flags; return rth; @@ -2629,12 +2637,12 @@ struct rtable *__ip_route_output_key(struct net *net, const struct flowi *flp) rcu_read_lock_bh(); for (rth = rcu_dereference_bh(rt_hash_table[hash].chain); rth; rth = rcu_dereference_bh(rth->dst.rt_next)) { - if (rth->fl.fl4_dst == flp->fl4_dst && - rth->fl.fl4_src == flp->fl4_src && + if (rth->rt_key_dst == flp->fl4_dst && + rth->rt_key_src == flp->fl4_src && rt_is_output_route(rth) && - rth->fl.oif == flp->oif && - rth->fl.mark == flp->mark && - !((rth->fl.fl4_tos ^ flp->fl4_tos) & + rth->rt_oif == flp->oif && + rth->rt_mark == flp->mark && + !((rth->rt_tos ^ flp->fl4_tos) & (IPTOS_RT_MASK | RTO_ONLINK)) && net_eq(dev_net(rth->dst.dev), net) && !rt_is_expired(rth)) { @@ -2693,7 +2701,12 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or if (new->dev) dev_hold(new->dev); - rt->fl = ort->fl; + rt->rt_key_dst = ort->rt_key_dst; + rt->rt_key_src = ort->rt_key_src; + rt->rt_tos = ort->rt_tos; + rt->rt_iif = ort->rt_iif; + rt->rt_oif = ort->rt_oif; + rt->rt_mark = ort->rt_mark; rt->rt_genid = rt_genid(net); rt->rt_flags = ort->rt_flags; @@ -2756,7 +2769,7 @@ static int rt_fill_info(struct net *net, r->rtm_family = AF_INET; r->rtm_dst_len = 32; r->rtm_src_len = 0; - r->rtm_tos = rt->fl.fl4_tos; + r->rtm_tos = rt->rt_tos; r->rtm_table = RT_TABLE_MAIN; NLA_PUT_U32(skb, RTA_TABLE, RT_TABLE_MAIN); r->rtm_type = rt->rt_type; @@ -2768,9 +2781,9 @@ static int rt_fill_info(struct net *net, NLA_PUT_BE32(skb, RTA_DST, rt->rt_dst); - if (rt->fl.fl4_src) { + if (rt->rt_key_src) { r->rtm_src_len = 32; - NLA_PUT_BE32(skb, RTA_SRC, rt->fl.fl4_src); + NLA_PUT_BE32(skb, RTA_SRC, rt->rt_key_src); } if (rt->dst.dev) NLA_PUT_U32(skb, RTA_OIF, rt->dst.dev->ifindex); @@ -2780,7 +2793,7 @@ static int rt_fill_info(struct net *net, #endif if (rt_is_input_route(rt)) NLA_PUT_BE32(skb, RTA_PREFSRC, rt->rt_spec_dst); - else if (rt->rt_src != rt->fl.fl4_src) + else if (rt->rt_src != rt->rt_key_src) NLA_PUT_BE32(skb, RTA_PREFSRC, rt->rt_src); if (rt->rt_dst != rt->rt_gateway) @@ -2789,8 +2802,8 @@ static int rt_fill_info(struct net *net, if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0) goto nla_put_failure; - if (rt->fl.mark) - NLA_PUT_BE32(skb, RTA_MARK, rt->fl.mark); + if (rt->rt_mark) + NLA_PUT_BE32(skb, RTA_MARK, rt->rt_mark); error = rt->dst.error; expires = (rt->peer && rt->peer->pmtu_expires) ? @@ -2824,7 +2837,7 @@ static int rt_fill_info(struct net *net, } } else #endif - NLA_PUT_U32(skb, RTA_IIF, rt->fl.iif); + NLA_PUT_U32(skb, RTA_IIF, rt->rt_iif); } if (rtnl_put_cacheinfo(skb, &rt->dst, id, ts, tsage, diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 45b821480427..c70c42e7e77b 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -70,7 +70,12 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, { struct rtable *rt = (struct rtable *)xdst->route; - xdst->u.rt.fl = *fl; + rt->rt_key_dst = fl->fl4_dst; + rt->rt_key_src = fl->fl4_src; + rt->rt_tos = fl->fl4_tos; + rt->rt_iif = fl->iif; + rt->rt_oif = fl->oif; + rt->rt_mark = fl->mark; xdst->u.dst.dev = dev; dev_hold(dev); diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index d580cdfca093..a907905376df 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -143,7 +143,7 @@ static int route4_classify(struct sk_buff *skb, struct tcf_proto *tp, if (head == NULL) goto old_method; - iif = ((struct rtable *)dst)->fl.iif; + iif = ((struct rtable *)dst)->rt_iif; h = route4_fastmap_hash(id, iif); if (id == head->fastmap[h].id && diff --git a/net/sched/em_meta.c b/net/sched/em_meta.c index e5e174782677..a4de67eca824 100644 --- a/net/sched/em_meta.c +++ b/net/sched/em_meta.c @@ -264,7 +264,7 @@ META_COLLECTOR(int_rtiif) if (unlikely(skb_rtable(skb) == NULL)) *err = -1; else - dst->value = skb_rtable(skb)->fl.iif; + dst->value = skb_rtable(skb)->rt_iif; } /************************************************************************** -- cgit v1.2.3 From 4ba8216cd90560bc402f52076f64d8546e8aefcb Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 25 Jan 2011 22:52:22 +0100 Subject: BKL: That's all, folks This removes the implementation of the big kernel lock, at last. A lot of people have worked on this in the past, I so the credit for this patch should be with everyone who participated in the hunt. The names on the Cc list are the people that were the most active in this, according to the recorded git history, in alphabetical order. Signed-off-by: Arnd Bergmann Acked-by: Alan Cox Cc: Alessio Igor Bogani Cc: Al Viro Cc: Andrew Hendry Cc: Andrew Morton Cc: Christoph Hellwig Cc: Eric W. Biederman Cc: Frederic Weisbecker Cc: Hans Verkuil Acked-by: Ingo Molnar Cc: Jan Blunck Cc: John Kacur Cc: Jonathan Corbet Cc: Linus Torvalds Cc: Matthew Wilcox Cc: Oliver Neukum Cc: Paul Menage Acked-by: Thomas Gleixner Cc: Trond Myklebust --- include/linux/hardirq.h | 9 +--- include/linux/smp_lock.h | 65 ---------------------- init/Kconfig | 5 -- kernel/sched.c | 9 +--- lib/Kconfig.debug | 9 ---- lib/Makefile | 1 - lib/kernel_lock.c | 136 ----------------------------------------------- 7 files changed, 2 insertions(+), 232 deletions(-) delete mode 100644 include/linux/smp_lock.h delete mode 100644 lib/kernel_lock.c (limited to 'include') diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h index 32f9fd6619b4..ba362171e8ae 100644 --- a/include/linux/hardirq.h +++ b/include/linux/hardirq.h @@ -93,13 +93,6 @@ */ #define in_nmi() (preempt_count() & NMI_MASK) -#if defined(CONFIG_PREEMPT) && defined(CONFIG_BKL) -# include -# define PREEMPT_INATOMIC_BASE (current->lock_depth >= 0) -#else -# define PREEMPT_INATOMIC_BASE 0 -#endif - #if defined(CONFIG_PREEMPT) # define PREEMPT_CHECK_OFFSET 1 #else @@ -113,7 +106,7 @@ * used in the general case to determine whether sleeping is possible. * Do not use in_atomic() in driver code. */ -#define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != PREEMPT_INATOMIC_BASE) +#define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != 0) /* * Check whether we were atomic before we did preempt_disable(): diff --git a/include/linux/smp_lock.h b/include/linux/smp_lock.h deleted file mode 100644 index 3a1988202731..000000000000 --- a/include/linux/smp_lock.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef __LINUX_SMPLOCK_H -#define __LINUX_SMPLOCK_H - -#ifdef CONFIG_LOCK_KERNEL -#include - -extern int __lockfunc __reacquire_kernel_lock(void); -extern void __lockfunc __release_kernel_lock(void); - -/* - * Release/re-acquire global kernel lock for the scheduler - */ -#define release_kernel_lock(tsk) do { \ - if (unlikely((tsk)->lock_depth >= 0)) \ - __release_kernel_lock(); \ -} while (0) - -static inline int reacquire_kernel_lock(struct task_struct *task) -{ - if (unlikely(task->lock_depth >= 0)) - return __reacquire_kernel_lock(); - return 0; -} - -extern void __lockfunc -_lock_kernel(const char *func, const char *file, int line) -__acquires(kernel_lock); - -extern void __lockfunc -_unlock_kernel(const char *func, const char *file, int line) -__releases(kernel_lock); - -#define lock_kernel() do { \ - _lock_kernel(__func__, __FILE__, __LINE__); \ -} while (0) - -#define unlock_kernel() do { \ - _unlock_kernel(__func__, __FILE__, __LINE__); \ -} while (0) - -/* - * Various legacy drivers don't really need the BKL in a specific - * function, but they *do* need to know that the BKL became available. - * This function just avoids wrapping a bunch of lock/unlock pairs - * around code which doesn't really need it. - */ -static inline void cycle_kernel_lock(void) -{ - lock_kernel(); - unlock_kernel(); -} - -#else - -#ifdef CONFIG_BKL /* provoke build bug if not set */ -#define lock_kernel() -#define unlock_kernel() -#define cycle_kernel_lock() do { } while(0) -#endif /* CONFIG_BKL */ - -#define release_kernel_lock(task) do { } while(0) -#define reacquire_kernel_lock(task) 0 - -#endif /* CONFIG_LOCK_KERNEL */ -#endif /* __LINUX_SMPLOCK_H */ diff --git a/init/Kconfig b/init/Kconfig index be788c0957d4..a88d1c919a4d 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -69,11 +69,6 @@ config BROKEN_ON_SMP depends on BROKEN || !SMP default y -config LOCK_KERNEL - bool - depends on (SMP || PREEMPT) && BKL - default y - config INIT_ENV_ARG_LIMIT int default 32 if !UML diff --git a/kernel/sched.c b/kernel/sched.c index 18d38e4ec7ba..827c170c6017 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -3945,9 +3944,6 @@ need_resched: rcu_note_context_switch(cpu); prev = rq->curr; - release_kernel_lock(prev); -need_resched_nonpreemptible: - schedule_debug(prev); if (sched_feat(HRTICK)) @@ -4010,9 +4006,6 @@ need_resched_nonpreemptible: post_schedule(rq); - if (unlikely(reacquire_kernel_lock(prev))) - goto need_resched_nonpreemptible; - preempt_enable_no_resched(); if (need_resched()) goto need_resched; @@ -8074,7 +8067,7 @@ static inline int preempt_count_equals(int preempt_offset) { int nested = (preempt_count() & ~PREEMPT_ACTIVE) + rcu_preempt_depth(); - return (nested == PREEMPT_INATOMIC_BASE + preempt_offset); + return (nested == preempt_offset); } void __might_sleep(const char *file, int line, int preempt_offset) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 2b97418c67e2..6f440d82b58d 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -470,15 +470,6 @@ config DEBUG_MUTEXES This feature allows mutex semantics violations to be detected and reported. -config BKL - bool "Big Kernel Lock" if (SMP || PREEMPT) - default y - help - This is the traditional lock that is used in old code instead - of proper locking. All drivers that use the BKL should depend - on this symbol. - Say Y here unless you are working on removing the BKL. - config DEBUG_LOCK_ALLOC bool "Lock debugging: detect incorrect freeing of live locks" depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT diff --git a/lib/Makefile b/lib/Makefile index cbb774f7d41d..de6c609bb4e4 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -43,7 +43,6 @@ obj-$(CONFIG_GENERIC_FIND_LAST_BIT) += find_last_bit.o CFLAGS_hweight.o = $(subst $(quote),,$(CONFIG_ARCH_HWEIGHT_CFLAGS)) obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o -obj-$(CONFIG_LOCK_KERNEL) += kernel_lock.o obj-$(CONFIG_BTREE) += btree.o obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o obj-$(CONFIG_DEBUG_LIST) += list_debug.o diff --git a/lib/kernel_lock.c b/lib/kernel_lock.c deleted file mode 100644 index d80e12265862..000000000000 --- a/lib/kernel_lock.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * lib/kernel_lock.c - * - * This is the traditional BKL - big kernel lock. Largely - * relegated to obsolescence, but used by various less - * important (or lazy) subsystems. - */ -#include -#include -#include -#include - -/* - * The 'big kernel lock' - * - * This spinlock is taken and released recursively by lock_kernel() - * and unlock_kernel(). It is transparently dropped and reacquired - * over schedule(). It is used to protect legacy code that hasn't - * been migrated to a proper locking design yet. - * - * Don't use in new code. - */ -static __cacheline_aligned_in_smp DEFINE_RAW_SPINLOCK(kernel_flag); - - -/* - * Acquire/release the underlying lock from the scheduler. - * - * This is called with preemption disabled, and should - * return an error value if it cannot get the lock and - * TIF_NEED_RESCHED gets set. - * - * If it successfully gets the lock, it should increment - * the preemption count like any spinlock does. - * - * (This works on UP too - do_raw_spin_trylock will never - * return false in that case) - */ -int __lockfunc __reacquire_kernel_lock(void) -{ - while (!do_raw_spin_trylock(&kernel_flag)) { - if (need_resched()) - return -EAGAIN; - cpu_relax(); - } - preempt_disable(); - return 0; -} - -void __lockfunc __release_kernel_lock(void) -{ - do_raw_spin_unlock(&kernel_flag); - preempt_enable_no_resched(); -} - -/* - * These are the BKL spinlocks - we try to be polite about preemption. - * If SMP is not on (ie UP preemption), this all goes away because the - * do_raw_spin_trylock() will always succeed. - */ -#ifdef CONFIG_PREEMPT -static inline void __lock_kernel(void) -{ - preempt_disable(); - if (unlikely(!do_raw_spin_trylock(&kernel_flag))) { - /* - * If preemption was disabled even before this - * was called, there's nothing we can be polite - * about - just spin. - */ - if (preempt_count() > 1) { - do_raw_spin_lock(&kernel_flag); - return; - } - - /* - * Otherwise, let's wait for the kernel lock - * with preemption enabled.. - */ - do { - preempt_enable(); - while (raw_spin_is_locked(&kernel_flag)) - cpu_relax(); - preempt_disable(); - } while (!do_raw_spin_trylock(&kernel_flag)); - } -} - -#else - -/* - * Non-preemption case - just get the spinlock - */ -static inline void __lock_kernel(void) -{ - do_raw_spin_lock(&kernel_flag); -} -#endif - -static inline void __unlock_kernel(void) -{ - /* - * the BKL is not covered by lockdep, so we open-code the - * unlocking sequence (and thus avoid the dep-chain ops): - */ - do_raw_spin_unlock(&kernel_flag); - preempt_enable(); -} - -/* - * Getting the big kernel lock. - * - * This cannot happen asynchronously, so we only need to - * worry about other CPU's. - */ -void __lockfunc _lock_kernel(const char *func, const char *file, int line) -{ - int depth = current->lock_depth + 1; - - if (likely(!depth)) { - might_sleep(); - __lock_kernel(); - } - current->lock_depth = depth; -} - -void __lockfunc _unlock_kernel(const char *func, const char *file, int line) -{ - BUG_ON(current->lock_depth < 0); - if (likely(--current->lock_depth < 0)) - __unlock_kernel(); -} - -EXPORT_SYMBOL(_lock_kernel); -EXPORT_SYMBOL(_unlock_kernel); - -- cgit v1.2.3 From b0c3130d69bda5cd91aa3b3f08e7878df49fde69 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 3 Mar 2011 15:47:21 +0530 Subject: dw_dmac: Pass Channel Allocation Order from platform_data In SPEAr Platform channels 4-7 have more Fifo depth. So we must get better channel first. This patch introduces concept of channel allocation order in dw_dmac. If user doesn't pass anything or 0, than normal (ascending) channel allocation will follow, else channels will be allocated in descending order. Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 6 +++++- include/linux/dw_dmac.h | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 6ab440e532bf..f413e123405a 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1319,7 +1319,11 @@ static int __init dw_probe(struct platform_device *pdev) dwc->chan.device = &dw->dma; dwc->chan.cookie = dwc->completed = 1; dwc->chan.chan_id = i; - list_add_tail(&dwc->chan.device_node, &dw->dma.channels); + if (pdata->chan_allocation_order == CHAN_ALLOCATION_ASCENDING) + list_add_tail(&dwc->chan.device_node, + &dw->dma.channels); + else + list_add(&dwc->chan.device_node, &dw->dma.channels); dwc->ch_regs = &__dw_regs(dw)->CHAN[i]; spin_lock_init(&dwc->lock); diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h index deec66b37180..a18c498984d9 100644 --- a/include/linux/dw_dmac.h +++ b/include/linux/dw_dmac.h @@ -22,6 +22,9 @@ struct dw_dma_platform_data { unsigned int nr_channels; bool is_private; +#define CHAN_ALLOCATION_ASCENDING 0 /* zero to seven */ +#define CHAN_ALLOCATION_DESCENDING 1 /* seven to zero */ + unsigned char chan_allocation_order; }; /** -- cgit v1.2.3 From 93317e8e35b77633d589fe0e132291195757d785 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 3 Mar 2011 15:47:22 +0530 Subject: dw_dmac: Pass Channel Priority from platform_data In Synopsys designware, channel priority is programmable. This patch adds support for passing channel priority through platform data. By default Ascending channel priority will be followed, i.e. channel 0 will get highest priority and channel 7 will get lowest. Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 11 ++++++++++- drivers/dma/dw_dmac_regs.h | 3 +++ include/linux/dw_dmac.h | 4 +++- 3 files changed, 16 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index f413e123405a..318a342fc7ec 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -901,8 +901,11 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) BUG_ON(!dws->dma_dev || dws->dma_dev != dw->dma.dev); cfghi = dws->cfg_hi; - cfglo = dws->cfg_lo; + cfglo = dws->cfg_lo & ~DWC_CFGL_CH_PRIOR_MASK; } + + cfglo |= DWC_CFGL_CH_PRIOR(dwc->priority); + channel_writel(dwc, CFG_LO, cfglo); channel_writel(dwc, CFG_HI, cfghi); @@ -1325,6 +1328,12 @@ static int __init dw_probe(struct platform_device *pdev) else list_add(&dwc->chan.device_node, &dw->dma.channels); + /* 7 is highest priority & 0 is lowest. */ + if (pdata->chan_priority == CHAN_PRIORITY_ASCENDING) + dwc->priority = 7 - i; + else + dwc->priority = i; + dwc->ch_regs = &__dw_regs(dw)->CHAN[i]; spin_lock_init(&dwc->lock); dwc->mask = 1 << i; diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index d9a939f67f46..6a8e6d35f359 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -101,6 +101,8 @@ struct dw_dma_regs { #define DWC_CTLH_BLOCK_TS_MASK 0x00000fff /* Bitfields in CFG_LO. Platform-configurable bits are in */ +#define DWC_CFGL_CH_PRIOR_MASK (0x7 << 5) /* priority mask */ +#define DWC_CFGL_CH_PRIOR(x) ((x) << 5) /* priority */ #define DWC_CFGL_CH_SUSP (1 << 8) /* pause xfer */ #define DWC_CFGL_FIFO_EMPTY (1 << 9) /* pause xfer */ #define DWC_CFGL_HS_DST (1 << 10) /* handshake w/dst */ @@ -134,6 +136,7 @@ struct dw_dma_chan { struct dma_chan chan; void __iomem *ch_regs; u8 mask; + u8 priority; spinlock_t lock; diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h index a18c498984d9..64c76da571ef 100644 --- a/include/linux/dw_dmac.h +++ b/include/linux/dw_dmac.h @@ -25,6 +25,9 @@ struct dw_dma_platform_data { #define CHAN_ALLOCATION_ASCENDING 0 /* zero to seven */ #define CHAN_ALLOCATION_DESCENDING 1 /* seven to zero */ unsigned char chan_allocation_order; +#define CHAN_PRIORITY_ASCENDING 0 /* chan0 highest */ +#define CHAN_PRIORITY_DESCENDING 1 /* chan7 highest */ + unsigned char chan_priority; }; /** @@ -70,7 +73,6 @@ struct dw_dma_slave { #define DWC_CFGH_DST_PER(x) ((x) << 11) /* Platform-configurable bits in CFG_LO */ -#define DWC_CFGL_PRIO(x) ((x) << 5) /* priority */ #define DWC_CFGL_LOCK_CH_XFER (0 << 12) /* scope of LOCK_CH */ #define DWC_CFGL_LOCK_CH_BLOCK (1 << 12) #define DWC_CFGL_LOCK_CH_XACT (2 << 12) -- cgit v1.2.3 From 59c22fc11d12b69da36c6585a38229863ba0bb16 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 3 Mar 2011 15:47:23 +0530 Subject: dw_dmac: Changing type of src_master and dest_master to u8. src_master & dest_master don't required u32 as they have values limited to u8 only. Also their description is missing from doc style comment. This patch fixes above mentioned issues. Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul --- include/linux/dw_dmac.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h index 64c76da571ef..3ba2f066ff46 100644 --- a/include/linux/dw_dmac.h +++ b/include/linux/dw_dmac.h @@ -53,6 +53,8 @@ enum dw_dma_slave_width { * @reg_width: peripheral register width * @cfg_hi: Platform-specific initializer for the CFG_HI register * @cfg_lo: Platform-specific initializer for the CFG_LO register + * @src_master: src master for transfers on allocated channel. + * @dst_master: dest master for transfers on allocated channel. */ struct dw_dma_slave { struct device *dma_dev; @@ -61,8 +63,8 @@ struct dw_dma_slave { enum dw_dma_slave_width reg_width; u32 cfg_hi; u32 cfg_lo; - int src_master; - int dst_master; + u8 src_master; + u8 dst_master; }; /* Platform-configurable bits in CFG_HI */ -- cgit v1.2.3 From ee66509d7f354eecb45ac99f21ea6aa8650dea7e Mon Sep 17 00:00:00 2001 From: Viresh KUMAR Date: Fri, 4 Mar 2011 15:42:51 +0530 Subject: dw_dmac: Allow src/dst msize & flow controller to be configured at runtime Msize or Burst Size is peripheral dependent in case of prep_slave_sg and cyclic_prep transfers, and in case of memcpy transfers it is platform dependent. So msize configuration must come from platform data. Also some peripherals (ex: JPEG), need to be flow controller for dma transfers, so this information in case of slave_sg & cyclic_prep transfers must come from platform data. Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 14 ++++++++------ drivers/dma/dw_dmac_regs.h | 1 + include/linux/dw_dmac.h | 30 ++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 318a342fc7ec..90ea08a53d62 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -36,9 +36,11 @@ struct dw_dma_slave *__slave = (private); \ int dms = __slave ? __slave->dst_master : 0; \ int sms = __slave ? __slave->src_master : 1; \ + u8 smsize = __slave ? __slave->src_msize : 0; \ + u8 dmsize = __slave ? __slave->dst_msize : 0; \ \ - (DWC_CTLL_DST_MSIZE(0) \ - | DWC_CTLL_SRC_MSIZE(0) \ + (DWC_CTLL_DST_MSIZE(dmsize) \ + | DWC_CTLL_SRC_MSIZE(smsize) \ | DWC_CTLL_LLP_D_EN \ | DWC_CTLL_LLP_S_EN \ | DWC_CTLL_DMS(dms) \ @@ -683,7 +685,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | DWC_CTLL_DST_WIDTH(reg_width) | DWC_CTLL_DST_FIX | DWC_CTLL_SRC_INC - | DWC_CTLL_FC_M2P); + | DWC_CTLL_FC(dws->fc)); reg = dws->tx_reg; for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; @@ -728,7 +730,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | DWC_CTLL_SRC_WIDTH(reg_width) | DWC_CTLL_DST_INC | DWC_CTLL_SRC_FIX - | DWC_CTLL_FC_P2M); + | DWC_CTLL_FC(dws->fc)); reg = dws->rx_reg; for_each_sg(sgl, sg, sg_len, i) { @@ -1146,7 +1148,7 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, | DWC_CTLL_SRC_WIDTH(reg_width) | DWC_CTLL_DST_FIX | DWC_CTLL_SRC_INC - | DWC_CTLL_FC_M2P + | DWC_CTLL_FC(dws->fc) | DWC_CTLL_INT_EN); break; case DMA_FROM_DEVICE: @@ -1157,7 +1159,7 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, | DWC_CTLL_DST_WIDTH(reg_width) | DWC_CTLL_DST_INC | DWC_CTLL_SRC_FIX - | DWC_CTLL_FC_P2M + | DWC_CTLL_FC(dws->fc) | DWC_CTLL_INT_EN); break; default: diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 6a8e6d35f359..9a32964bf795 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -86,6 +86,7 @@ struct dw_dma_regs { #define DWC_CTLL_SRC_MSIZE(n) ((n)<<14) #define DWC_CTLL_S_GATH_EN (1 << 17) /* src gather, !FIX */ #define DWC_CTLL_D_SCAT_EN (1 << 18) /* dst scatter, !FIX */ +#define DWC_CTLL_FC(n) ((n) << 20) #define DWC_CTLL_FC_M2M (0 << 20) /* mem-to-mem */ #define DWC_CTLL_FC_M2P (1 << 20) /* mem-to-periph */ #define DWC_CTLL_FC_P2M (2 << 20) /* periph-to-mem */ diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h index 3ba2f066ff46..6998d9376ef9 100644 --- a/include/linux/dw_dmac.h +++ b/include/linux/dw_dmac.h @@ -42,6 +42,30 @@ enum dw_dma_slave_width { DW_DMA_SLAVE_WIDTH_32BIT, }; +/* bursts size */ +enum dw_dma_msize { + DW_DMA_MSIZE_1, + DW_DMA_MSIZE_4, + DW_DMA_MSIZE_8, + DW_DMA_MSIZE_16, + DW_DMA_MSIZE_32, + DW_DMA_MSIZE_64, + DW_DMA_MSIZE_128, + DW_DMA_MSIZE_256, +}; + +/* flow controller */ +enum dw_dma_fc { + DW_DMA_FC_D_M2M, + DW_DMA_FC_D_M2P, + DW_DMA_FC_D_P2M, + DW_DMA_FC_D_P2P, + DW_DMA_FC_P_P2M, + DW_DMA_FC_SP_P2P, + DW_DMA_FC_P_M2P, + DW_DMA_FC_DP_P2P, +}; + /** * struct dw_dma_slave - Controller-specific information about a slave * @@ -55,6 +79,9 @@ enum dw_dma_slave_width { * @cfg_lo: Platform-specific initializer for the CFG_LO register * @src_master: src master for transfers on allocated channel. * @dst_master: dest master for transfers on allocated channel. + * @src_msize: src burst size. + * @dst_msize: dest burst size. + * @fc: flow controller for DMA transfer */ struct dw_dma_slave { struct device *dma_dev; @@ -65,6 +92,9 @@ struct dw_dma_slave { u32 cfg_lo; u8 src_master; u8 dst_master; + u8 src_msize; + u8 dst_msize; + u8 fc; }; /* Platform-configurable bits in CFG_HI */ -- cgit v1.2.3 From 550462378515a82279e07f12e2c105f617f112f8 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 7 Mar 2011 10:28:42 +0530 Subject: serial: msm_serial_hs: Add MSM high speed UART driver This driver supports UART-DM HW on MSM platforms. It uses the on chip DMA to drive data transfers and has optional support for UART power management independent of Linux suspend/resume and wakeup from Rx. The driver was originally developed by Google. It is functionally equivalent to the version available at: http://android.git.kernel.org/?p=kernel/experimental.git the differences being: 1) Remove wakelocks and change unsupported DMA API. 2) Replace clock selection register codes by macros. 3) Fix checkpatch errors and add inline documentation. 4) Add runtime PM hooks for active power state transitions. 5) Handle error path and cleanup resources if required. CC: Nick Pelly Signed-off-by: Sankalp Bose Signed-off-by: Mayank Rana Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 12 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/msm_serial_hs.c | 1880 +++++++++++++++++++++++++++ include/linux/platform_data/msm_serial_hs.h | 49 + 4 files changed, 1942 insertions(+) create mode 100644 drivers/tty/serial/msm_serial_hs.c create mode 100644 include/linux/platform_data/msm_serial_hs.h (limited to 'include') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 90d939a4ee5d..d9ccbf825095 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1319,6 +1319,18 @@ config SERIAL_MSM_CONSOLE depends on SERIAL_MSM=y select SERIAL_CORE_CONSOLE +config SERIAL_MSM_HS + tristate "MSM UART High Speed: Serial Driver" + depends on ARCH_MSM + select SERIAL_CORE + help + If you have a machine based on MSM family of SoCs, you + can enable its onboard high speed serial port by enabling + this option. + + Choose M here to compile it as a module. The module will be + called msm_serial_hs. + config SERIAL_VT8500 bool "VIA VT8500 on-chip serial port support" depends on ARM && ARCH_VT8500 diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 0c6aefb55acf..d94dc005c8a6 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o obj-$(CONFIG_SERIAL_MSM) += msm_serial.o +obj-$(CONFIG_SERIAL_MSM_HS) += msm_serial_hs.o obj-$(CONFIG_SERIAL_NETX) += netx-serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c new file mode 100644 index 000000000000..2e7fc9cee9cc --- /dev/null +++ b/drivers/tty/serial/msm_serial_hs.c @@ -0,0 +1,1880 @@ +/* + * MSM 7k/8k High speed uart driver + * + * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2008 Google Inc. + * Modified: Nick Pelly + * + * 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. + * + * Has optional support for uart power management independent of linux + * suspend/resume: + * + * RX wakeup. + * UART wakeup can be triggered by RX activity (using a wakeup GPIO on the + * UART RX pin). This should only be used if there is not a wakeup + * GPIO on the UART CTS, and the first RX byte is known (for example, with the + * Bluetooth Texas Instruments HCILL protocol), since the first RX byte will + * always be lost. RTS will be asserted even while the UART is off in this mode + * of operation. See msm_serial_hs_platform_data.rx_wakeup_irq. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +/* HSUART Registers */ +#define UARTDM_MR1_ADDR 0x0 +#define UARTDM_MR2_ADDR 0x4 + +/* Data Mover result codes */ +#define RSLT_FIFO_CNTR_BMSK (0xE << 28) +#define RSLT_VLD BIT(1) + +/* write only register */ +#define UARTDM_CSR_ADDR 0x8 +#define UARTDM_CSR_115200 0xFF +#define UARTDM_CSR_57600 0xEE +#define UARTDM_CSR_38400 0xDD +#define UARTDM_CSR_28800 0xCC +#define UARTDM_CSR_19200 0xBB +#define UARTDM_CSR_14400 0xAA +#define UARTDM_CSR_9600 0x99 +#define UARTDM_CSR_7200 0x88 +#define UARTDM_CSR_4800 0x77 +#define UARTDM_CSR_3600 0x66 +#define UARTDM_CSR_2400 0x55 +#define UARTDM_CSR_1200 0x44 +#define UARTDM_CSR_600 0x33 +#define UARTDM_CSR_300 0x22 +#define UARTDM_CSR_150 0x11 +#define UARTDM_CSR_75 0x00 + +/* write only register */ +#define UARTDM_TF_ADDR 0x70 +#define UARTDM_TF2_ADDR 0x74 +#define UARTDM_TF3_ADDR 0x78 +#define UARTDM_TF4_ADDR 0x7C + +/* write only register */ +#define UARTDM_CR_ADDR 0x10 +#define UARTDM_IMR_ADDR 0x14 + +#define UARTDM_IPR_ADDR 0x18 +#define UARTDM_TFWR_ADDR 0x1c +#define UARTDM_RFWR_ADDR 0x20 +#define UARTDM_HCR_ADDR 0x24 +#define UARTDM_DMRX_ADDR 0x34 +#define UARTDM_IRDA_ADDR 0x38 +#define UARTDM_DMEN_ADDR 0x3c + +/* UART_DM_NO_CHARS_FOR_TX */ +#define UARTDM_NCF_TX_ADDR 0x40 + +#define UARTDM_BADR_ADDR 0x44 + +#define UARTDM_SIM_CFG_ADDR 0x80 +/* Read Only register */ +#define UARTDM_SR_ADDR 0x8 + +/* Read Only register */ +#define UARTDM_RF_ADDR 0x70 +#define UARTDM_RF2_ADDR 0x74 +#define UARTDM_RF3_ADDR 0x78 +#define UARTDM_RF4_ADDR 0x7C + +/* Read Only register */ +#define UARTDM_MISR_ADDR 0x10 + +/* Read Only register */ +#define UARTDM_ISR_ADDR 0x14 +#define UARTDM_RX_TOTAL_SNAP_ADDR 0x38 + +#define UARTDM_RXFS_ADDR 0x50 + +/* Register field Mask Mapping */ +#define UARTDM_SR_PAR_FRAME_BMSK BIT(5) +#define UARTDM_SR_OVERRUN_BMSK BIT(4) +#define UARTDM_SR_TXEMT_BMSK BIT(3) +#define UARTDM_SR_TXRDY_BMSK BIT(2) +#define UARTDM_SR_RXRDY_BMSK BIT(0) + +#define UARTDM_CR_TX_DISABLE_BMSK BIT(3) +#define UARTDM_CR_RX_DISABLE_BMSK BIT(1) +#define UARTDM_CR_TX_EN_BMSK BIT(2) +#define UARTDM_CR_RX_EN_BMSK BIT(0) + +/* UARTDM_CR channel_comman bit value (register field is bits 8:4) */ +#define RESET_RX 0x10 +#define RESET_TX 0x20 +#define RESET_ERROR_STATUS 0x30 +#define RESET_BREAK_INT 0x40 +#define START_BREAK 0x50 +#define STOP_BREAK 0x60 +#define RESET_CTS 0x70 +#define RESET_STALE_INT 0x80 +#define RFR_LOW 0xD0 +#define RFR_HIGH 0xE0 +#define CR_PROTECTION_EN 0x100 +#define STALE_EVENT_ENABLE 0x500 +#define STALE_EVENT_DISABLE 0x600 +#define FORCE_STALE_EVENT 0x400 +#define CLEAR_TX_READY 0x300 +#define RESET_TX_ERROR 0x800 +#define RESET_TX_DONE 0x810 + +#define UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK 0xffffff00 +#define UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK 0x3f +#define UARTDM_MR1_CTS_CTL_BMSK 0x40 +#define UARTDM_MR1_RX_RDY_CTL_BMSK 0x80 + +#define UARTDM_MR2_ERROR_MODE_BMSK 0x40 +#define UARTDM_MR2_BITS_PER_CHAR_BMSK 0x30 + +/* bits per character configuration */ +#define FIVE_BPC (0 << 4) +#define SIX_BPC (1 << 4) +#define SEVEN_BPC (2 << 4) +#define EIGHT_BPC (3 << 4) + +#define UARTDM_MR2_STOP_BIT_LEN_BMSK 0xc +#define STOP_BIT_ONE (1 << 2) +#define STOP_BIT_TWO (3 << 2) + +#define UARTDM_MR2_PARITY_MODE_BMSK 0x3 + +/* Parity configuration */ +#define NO_PARITY 0x0 +#define EVEN_PARITY 0x1 +#define ODD_PARITY 0x2 +#define SPACE_PARITY 0x3 + +#define UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK 0xffffff80 +#define UARTDM_IPR_STALE_LSB_BMSK 0x1f + +/* These can be used for both ISR and IMR register */ +#define UARTDM_ISR_TX_READY_BMSK BIT(7) +#define UARTDM_ISR_CURRENT_CTS_BMSK BIT(6) +#define UARTDM_ISR_DELTA_CTS_BMSK BIT(5) +#define UARTDM_ISR_RXLEV_BMSK BIT(4) +#define UARTDM_ISR_RXSTALE_BMSK BIT(3) +#define UARTDM_ISR_RXBREAK_BMSK BIT(2) +#define UARTDM_ISR_RXHUNT_BMSK BIT(1) +#define UARTDM_ISR_TXLEV_BMSK BIT(0) + +/* Field definitions for UART_DM_DMEN*/ +#define UARTDM_TX_DM_EN_BMSK 0x1 +#define UARTDM_RX_DM_EN_BMSK 0x2 + +#define UART_FIFOSIZE 64 +#define UARTCLK 7372800 + +/* Rx DMA request states */ +enum flush_reason { + FLUSH_NONE, + FLUSH_DATA_READY, + FLUSH_DATA_INVALID, /* values after this indicate invalid data */ + FLUSH_IGNORE = FLUSH_DATA_INVALID, + FLUSH_STOP, + FLUSH_SHUTDOWN, +}; + +/* UART clock states */ +enum msm_hs_clk_states_e { + MSM_HS_CLK_PORT_OFF, /* port not in use */ + MSM_HS_CLK_OFF, /* clock disabled */ + MSM_HS_CLK_REQUEST_OFF, /* disable after TX and RX flushed */ + MSM_HS_CLK_ON, /* clock enabled */ +}; + +/* Track the forced RXSTALE flush during clock off sequence. + * These states are only valid during MSM_HS_CLK_REQUEST_OFF */ +enum msm_hs_clk_req_off_state_e { + CLK_REQ_OFF_START, + CLK_REQ_OFF_RXSTALE_ISSUED, + CLK_REQ_OFF_FLUSH_ISSUED, + CLK_REQ_OFF_RXSTALE_FLUSHED, +}; + +/** + * struct msm_hs_tx + * @tx_ready_int_en: ok to dma more tx? + * @dma_in_flight: tx dma in progress + * @xfer: top level DMA command pointer structure + * @command_ptr: third level command struct pointer + * @command_ptr_ptr: second level command list struct pointer + * @mapped_cmd_ptr: DMA view of third level command struct + * @mapped_cmd_ptr_ptr: DMA view of second level command list struct + * @tx_count: number of bytes to transfer in DMA transfer + * @dma_base: DMA view of UART xmit buffer + * + * This structure describes a single Tx DMA transaction. MSM DMA + * commands have two levels of indirection. The top level command + * ptr points to a list of command ptr which in turn points to a + * single DMA 'command'. In our case each Tx transaction consists + * of a single second level pointer pointing to a 'box type' command. + */ +struct msm_hs_tx { + unsigned int tx_ready_int_en; + unsigned int dma_in_flight; + struct msm_dmov_cmd xfer; + dmov_box *command_ptr; + u32 *command_ptr_ptr; + dma_addr_t mapped_cmd_ptr; + dma_addr_t mapped_cmd_ptr_ptr; + int tx_count; + dma_addr_t dma_base; +}; + +/** + * struct msm_hs_rx + * @flush: Rx DMA request state + * @xfer: top level DMA command pointer structure + * @cmdptr_dmaaddr: DMA view of second level command structure + * @command_ptr: third level DMA command pointer structure + * @command_ptr_ptr: second level DMA command list pointer + * @mapped_cmd_ptr: DMA view of the third level command structure + * @wait: wait for DMA completion before shutdown + * @buffer: destination buffer for RX DMA + * @rbuffer: DMA view of buffer + * @pool: dma pool out of which coherent rx buffer is allocated + * @tty_work: private work-queue for tty flip buffer push task + * + * This structure describes a single Rx DMA transaction. Rx DMA + * transactions use box mode DMA commands. + */ +struct msm_hs_rx { + enum flush_reason flush; + struct msm_dmov_cmd xfer; + dma_addr_t cmdptr_dmaaddr; + dmov_box *command_ptr; + u32 *command_ptr_ptr; + dma_addr_t mapped_cmd_ptr; + wait_queue_head_t wait; + dma_addr_t rbuffer; + unsigned char *buffer; + struct dma_pool *pool; + struct work_struct tty_work; +}; + +/** + * struct msm_hs_rx_wakeup + * @irq: IRQ line to be configured as interrupt source on Rx activity + * @ignore: boolean value. 1 = ignore the wakeup interrupt + * @rx_to_inject: extra character to be inserted to Rx tty on wakeup + * @inject_rx: 1 = insert rx_to_inject. 0 = do not insert extra character + * + * This is an optional structure required for UART Rx GPIO IRQ based + * wakeup from low power state. UART wakeup can be triggered by RX activity + * (using a wakeup GPIO on the UART RX pin). This should only be used if + * there is not a wakeup GPIO on the UART CTS, and the first RX byte is + * known (eg., with the Bluetooth Texas Instruments HCILL protocol), + * since the first RX byte will always be lost. RTS will be asserted even + * while the UART is clocked off in this mode of operation. + */ +struct msm_hs_rx_wakeup { + int irq; /* < 0 indicates low power wakeup disabled */ + unsigned char ignore; + unsigned char inject_rx; + char rx_to_inject; +}; + +/** + * struct msm_hs_port + * @uport: embedded uart port structure + * @imr_reg: shadow value of UARTDM_IMR + * @clk: uart input clock handle + * @tx: Tx transaction related data structure + * @rx: Rx transaction related data structure + * @dma_tx_channel: Tx DMA command channel + * @dma_rx_channel Rx DMA command channel + * @dma_tx_crci: Tx channel rate control interface number + * @dma_rx_crci: Rx channel rate control interface number + * @clk_off_timer: Timer to poll DMA event completion before clock off + * @clk_off_delay: clk_off_timer poll interval + * @clk_state: overall clock state + * @clk_req_off_state: post flush clock states + * @rx_wakeup: optional rx_wakeup feature related data + * @exit_lpm_cb: optional callback to exit low power mode + * + * Low level serial port structure. + */ +struct msm_hs_port { + struct uart_port uport; + unsigned long imr_reg; + struct clk *clk; + struct msm_hs_tx tx; + struct msm_hs_rx rx; + + int dma_tx_channel; + int dma_rx_channel; + int dma_tx_crci; + int dma_rx_crci; + + struct hrtimer clk_off_timer; + ktime_t clk_off_delay; + enum msm_hs_clk_states_e clk_state; + enum msm_hs_clk_req_off_state_e clk_req_off_state; + + struct msm_hs_rx_wakeup rx_wakeup; + void (*exit_lpm_cb)(struct uart_port *); +}; + +#define MSM_UARTDM_BURST_SIZE 16 /* DM burst size (in bytes) */ +#define UARTDM_TX_BUF_SIZE UART_XMIT_SIZE +#define UARTDM_RX_BUF_SIZE 512 + +#define UARTDM_NR 2 + +static struct msm_hs_port q_uart_port[UARTDM_NR]; +static struct platform_driver msm_serial_hs_platform_driver; +static struct uart_driver msm_hs_driver; +static struct uart_ops msm_hs_ops; +static struct workqueue_struct *msm_hs_workqueue; + +#define UARTDM_TO_MSM(uart_port) \ + container_of((uart_port), struct msm_hs_port, uport) + +static unsigned int use_low_power_rx_wakeup(struct msm_hs_port + *msm_uport) +{ + return (msm_uport->rx_wakeup.irq >= 0); +} + +static unsigned int msm_hs_read(struct uart_port *uport, + unsigned int offset) +{ + return ioread32(uport->membase + offset); +} + +static void msm_hs_write(struct uart_port *uport, unsigned int offset, + unsigned int value) +{ + iowrite32(value, uport->membase + offset); +} + +static void msm_hs_release_port(struct uart_port *port) +{ + iounmap(port->membase); +} + +static int msm_hs_request_port(struct uart_port *port) +{ + port->membase = ioremap(port->mapbase, PAGE_SIZE); + if (unlikely(!port->membase)) + return -ENOMEM; + + /* configure the CR Protection to Enable */ + msm_hs_write(port, UARTDM_CR_ADDR, CR_PROTECTION_EN); + return 0; +} + +static int __devexit msm_hs_remove(struct platform_device *pdev) +{ + + struct msm_hs_port *msm_uport; + struct device *dev; + + if (pdev->id < 0 || pdev->id >= UARTDM_NR) { + printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id); + return -EINVAL; + } + + msm_uport = &q_uart_port[pdev->id]; + dev = msm_uport->uport.dev; + + dma_unmap_single(dev, msm_uport->rx.mapped_cmd_ptr, sizeof(dmov_box), + DMA_TO_DEVICE); + dma_pool_free(msm_uport->rx.pool, msm_uport->rx.buffer, + msm_uport->rx.rbuffer); + dma_pool_destroy(msm_uport->rx.pool); + + dma_unmap_single(dev, msm_uport->rx.cmdptr_dmaaddr, sizeof(u32 *), + DMA_TO_DEVICE); + dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr_ptr, sizeof(u32 *), + DMA_TO_DEVICE); + dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr, sizeof(dmov_box), + DMA_TO_DEVICE); + + uart_remove_one_port(&msm_hs_driver, &msm_uport->uport); + clk_put(msm_uport->clk); + + /* Free the tx resources */ + kfree(msm_uport->tx.command_ptr); + kfree(msm_uport->tx.command_ptr_ptr); + + /* Free the rx resources */ + kfree(msm_uport->rx.command_ptr); + kfree(msm_uport->rx.command_ptr_ptr); + + iounmap(msm_uport->uport.membase); + + return 0; +} + +static int msm_hs_init_clk_locked(struct uart_port *uport) +{ + int ret; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + ret = clk_enable(msm_uport->clk); + if (ret) { + printk(KERN_ERR "Error could not turn on UART clk\n"); + return ret; + } + + /* Set up the MREG/NREG/DREG/MNDREG */ + ret = clk_set_rate(msm_uport->clk, uport->uartclk); + if (ret) { + printk(KERN_WARNING "Error setting clock rate on UART\n"); + clk_disable(msm_uport->clk); + return ret; + } + + msm_uport->clk_state = MSM_HS_CLK_ON; + return 0; +} + +/* Enable and Disable clocks (Used for power management) */ +static void msm_hs_pm(struct uart_port *uport, unsigned int state, + unsigned int oldstate) +{ + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + if (use_low_power_rx_wakeup(msm_uport) || + msm_uport->exit_lpm_cb) + return; /* ignore linux PM states, + use msm_hs_request_clock API */ + + switch (state) { + case 0: + clk_enable(msm_uport->clk); + break; + case 3: + clk_disable(msm_uport->clk); + break; + default: + dev_err(uport->dev, "msm_serial: Unknown PM state %d\n", + state); + } +} + +/* + * programs the UARTDM_CSR register with correct bit rates + * + * Interrupts should be disabled before we are called, as + * we modify Set Baud rate + * Set receive stale interrupt level, dependant on Bit Rate + * Goal is to have around 8 ms before indicate stale. + * roundup (((Bit Rate * .008) / 10) + 1 + */ +static void msm_hs_set_bps_locked(struct uart_port *uport, + unsigned int bps) +{ + unsigned long rxstale; + unsigned long data; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + switch (bps) { + case 300: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_75); + rxstale = 1; + break; + case 600: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_150); + rxstale = 1; + break; + case 1200: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_300); + rxstale = 1; + break; + case 2400: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_600); + rxstale = 1; + break; + case 4800: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_1200); + rxstale = 1; + break; + case 9600: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_2400); + rxstale = 2; + break; + case 14400: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_3600); + rxstale = 3; + break; + case 19200: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_4800); + rxstale = 4; + break; + case 28800: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_7200); + rxstale = 6; + break; + case 38400: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_9600); + rxstale = 8; + break; + case 57600: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_14400); + rxstale = 16; + break; + case 76800: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_19200); + rxstale = 16; + break; + case 115200: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_28800); + rxstale = 31; + break; + case 230400: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_57600); + rxstale = 31; + break; + case 460800: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_115200); + rxstale = 31; + break; + case 4000000: + case 3686400: + case 3200000: + case 3500000: + case 3000000: + case 2500000: + case 1500000: + case 1152000: + case 1000000: + case 921600: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_115200); + rxstale = 31; + break; + default: + msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_2400); + /* default to 9600 */ + bps = 9600; + rxstale = 2; + break; + } + if (bps > 460800) + uport->uartclk = bps * 16; + else + uport->uartclk = UARTCLK; + + if (clk_set_rate(msm_uport->clk, uport->uartclk)) { + printk(KERN_WARNING "Error setting clock rate on UART\n"); + return; + } + + data = rxstale & UARTDM_IPR_STALE_LSB_BMSK; + data |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2); + + msm_hs_write(uport, UARTDM_IPR_ADDR, data); +} + +/* + * termios : new ktermios + * oldtermios: old ktermios previous setting + * + * Configure the serial port + */ +static void msm_hs_set_termios(struct uart_port *uport, + struct ktermios *termios, + struct ktermios *oldtermios) +{ + unsigned int bps; + unsigned long data; + unsigned long flags; + unsigned int c_cflag = termios->c_cflag; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + spin_lock_irqsave(&uport->lock, flags); + clk_enable(msm_uport->clk); + + /* 300 is the minimum baud support by the driver */ + bps = uart_get_baud_rate(uport, termios, oldtermios, 200, 4000000); + + /* Temporary remapping 200 BAUD to 3.2 mbps */ + if (bps == 200) + bps = 3200000; + + msm_hs_set_bps_locked(uport, bps); + + data = msm_hs_read(uport, UARTDM_MR2_ADDR); + data &= ~UARTDM_MR2_PARITY_MODE_BMSK; + /* set parity */ + if (PARENB == (c_cflag & PARENB)) { + if (PARODD == (c_cflag & PARODD)) + data |= ODD_PARITY; + else if (CMSPAR == (c_cflag & CMSPAR)) + data |= SPACE_PARITY; + else + data |= EVEN_PARITY; + } + + /* Set bits per char */ + data &= ~UARTDM_MR2_BITS_PER_CHAR_BMSK; + + switch (c_cflag & CSIZE) { + case CS5: + data |= FIVE_BPC; + break; + case CS6: + data |= SIX_BPC; + break; + case CS7: + data |= SEVEN_BPC; + break; + default: + data |= EIGHT_BPC; + break; + } + /* stop bits */ + if (c_cflag & CSTOPB) { + data |= STOP_BIT_TWO; + } else { + /* otherwise 1 stop bit */ + data |= STOP_BIT_ONE; + } + data |= UARTDM_MR2_ERROR_MODE_BMSK; + /* write parity/bits per char/stop bit configuration */ + msm_hs_write(uport, UARTDM_MR2_ADDR, data); + + /* Configure HW flow control */ + data = msm_hs_read(uport, UARTDM_MR1_ADDR); + + data &= ~(UARTDM_MR1_CTS_CTL_BMSK | UARTDM_MR1_RX_RDY_CTL_BMSK); + + if (c_cflag & CRTSCTS) { + data |= UARTDM_MR1_CTS_CTL_BMSK; + data |= UARTDM_MR1_RX_RDY_CTL_BMSK; + } + + msm_hs_write(uport, UARTDM_MR1_ADDR, data); + + uport->ignore_status_mask = termios->c_iflag & INPCK; + uport->ignore_status_mask |= termios->c_iflag & IGNPAR; + uport->read_status_mask = (termios->c_cflag & CREAD); + + msm_hs_write(uport, UARTDM_IMR_ADDR, 0); + + /* Set Transmit software time out */ + uart_update_timeout(uport, c_cflag, bps); + + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX); + + if (msm_uport->rx.flush == FLUSH_NONE) { + msm_uport->rx.flush = FLUSH_IGNORE; + msm_dmov_stop_cmd(msm_uport->dma_rx_channel, NULL, 1); + } + + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + + clk_disable(msm_uport->clk); + spin_unlock_irqrestore(&uport->lock, flags); +} + +/* + * Standard API, Transmitter + * Any character in the transmit shift register is sent + */ +static unsigned int msm_hs_tx_empty(struct uart_port *uport) +{ + unsigned int data; + unsigned int ret = 0; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + clk_enable(msm_uport->clk); + + data = msm_hs_read(uport, UARTDM_SR_ADDR); + if (data & UARTDM_SR_TXEMT_BMSK) + ret = TIOCSER_TEMT; + + clk_disable(msm_uport->clk); + + return ret; +} + +/* + * Standard API, Stop transmitter. + * Any character in the transmit shift register is sent as + * well as the current data mover transfer . + */ +static void msm_hs_stop_tx_locked(struct uart_port *uport) +{ + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + msm_uport->tx.tx_ready_int_en = 0; +} + +/* + * Standard API, Stop receiver as soon as possible. + * + * Function immediately terminates the operation of the + * channel receiver and any incoming characters are lost. None + * of the receiver status bits are affected by this command and + * characters that are already in the receive FIFO there. + */ +static void msm_hs_stop_rx_locked(struct uart_port *uport) +{ + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + unsigned int data; + + clk_enable(msm_uport->clk); + + /* disable dlink */ + data = msm_hs_read(uport, UARTDM_DMEN_ADDR); + data &= ~UARTDM_RX_DM_EN_BMSK; + msm_hs_write(uport, UARTDM_DMEN_ADDR, data); + + /* Disable the receiver */ + if (msm_uport->rx.flush == FLUSH_NONE) + msm_dmov_stop_cmd(msm_uport->dma_rx_channel, NULL, 1); + + if (msm_uport->rx.flush != FLUSH_SHUTDOWN) + msm_uport->rx.flush = FLUSH_STOP; + + clk_disable(msm_uport->clk); +} + +/* Transmit the next chunk of data */ +static void msm_hs_submit_tx_locked(struct uart_port *uport) +{ + int left; + int tx_count; + dma_addr_t src_addr; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + struct msm_hs_tx *tx = &msm_uport->tx; + struct circ_buf *tx_buf = &msm_uport->uport.state->xmit; + + if (uart_circ_empty(tx_buf) || uport->state->port.tty->stopped) { + msm_hs_stop_tx_locked(uport); + return; + } + + tx->dma_in_flight = 1; + + tx_count = uart_circ_chars_pending(tx_buf); + + if (UARTDM_TX_BUF_SIZE < tx_count) + tx_count = UARTDM_TX_BUF_SIZE; + + left = UART_XMIT_SIZE - tx_buf->tail; + + if (tx_count > left) + tx_count = left; + + src_addr = tx->dma_base + tx_buf->tail; + dma_sync_single_for_device(uport->dev, src_addr, tx_count, + DMA_TO_DEVICE); + + tx->command_ptr->num_rows = (((tx_count + 15) >> 4) << 16) | + ((tx_count + 15) >> 4); + tx->command_ptr->src_row_addr = src_addr; + + dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr, + sizeof(dmov_box), DMA_TO_DEVICE); + + *tx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(tx->mapped_cmd_ptr); + + dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr, + sizeof(u32 *), DMA_TO_DEVICE); + + /* Save tx_count to use in Callback */ + tx->tx_count = tx_count; + msm_hs_write(uport, UARTDM_NCF_TX_ADDR, tx_count); + + /* Disable the tx_ready interrupt */ + msm_uport->imr_reg &= ~UARTDM_ISR_TX_READY_BMSK; + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, &tx->xfer); +} + +/* Start to receive the next chunk of data */ +static void msm_hs_start_rx_locked(struct uart_port *uport) +{ + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT); + msm_hs_write(uport, UARTDM_DMRX_ADDR, UARTDM_RX_BUF_SIZE); + msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_ENABLE); + msm_uport->imr_reg |= UARTDM_ISR_RXLEV_BMSK; + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + + msm_uport->rx.flush = FLUSH_NONE; + msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel, &msm_uport->rx.xfer); + + /* might have finished RX and be ready to clock off */ + hrtimer_start(&msm_uport->clk_off_timer, msm_uport->clk_off_delay, + HRTIMER_MODE_REL); +} + +/* Enable the transmitter Interrupt */ +static void msm_hs_start_tx_locked(struct uart_port *uport) +{ + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + clk_enable(msm_uport->clk); + + if (msm_uport->exit_lpm_cb) + msm_uport->exit_lpm_cb(uport); + + if (msm_uport->tx.tx_ready_int_en == 0) { + msm_uport->tx.tx_ready_int_en = 1; + msm_hs_submit_tx_locked(uport); + } + + clk_disable(msm_uport->clk); +} + +/* + * This routine is called when we are done with a DMA transfer + * + * This routine is registered with Data mover when we set + * up a Data Mover transfer. It is called from Data mover ISR + * when the DMA transfer is done. + */ +static void msm_hs_dmov_tx_callback(struct msm_dmov_cmd *cmd_ptr, + unsigned int result, + struct msm_dmov_errdata *err) +{ + unsigned long flags; + struct msm_hs_port *msm_uport; + + /* DMA did not finish properly */ + WARN_ON((((result & RSLT_FIFO_CNTR_BMSK) >> 28) == 1) && + !(result & RSLT_VLD)); + + msm_uport = container_of(cmd_ptr, struct msm_hs_port, tx.xfer); + + spin_lock_irqsave(&msm_uport->uport.lock, flags); + clk_enable(msm_uport->clk); + + msm_uport->imr_reg |= UARTDM_ISR_TX_READY_BMSK; + msm_hs_write(&msm_uport->uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + + clk_disable(msm_uport->clk); + spin_unlock_irqrestore(&msm_uport->uport.lock, flags); +} + +/* + * This routine is called when we are done with a DMA transfer or the + * a flush has been sent to the data mover driver. + * + * This routine is registered with Data mover when we set up a Data Mover + * transfer. It is called from Data mover ISR when the DMA transfer is done. + */ +static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, + unsigned int result, + struct msm_dmov_errdata *err) +{ + int retval; + int rx_count; + unsigned long status; + unsigned int error_f = 0; + unsigned long flags; + unsigned int flush; + struct tty_struct *tty; + struct uart_port *uport; + struct msm_hs_port *msm_uport; + + msm_uport = container_of(cmd_ptr, struct msm_hs_port, rx.xfer); + uport = &msm_uport->uport; + + spin_lock_irqsave(&uport->lock, flags); + clk_enable(msm_uport->clk); + + tty = uport->state->port.tty; + + msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); + + status = msm_hs_read(uport, UARTDM_SR_ADDR); + + /* overflow is not connect to data in a FIFO */ + if (unlikely((status & UARTDM_SR_OVERRUN_BMSK) && + (uport->read_status_mask & CREAD))) { + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + uport->icount.buf_overrun++; + error_f = 1; + } + + if (!(uport->ignore_status_mask & INPCK)) + status = status & ~(UARTDM_SR_PAR_FRAME_BMSK); + + if (unlikely(status & UARTDM_SR_PAR_FRAME_BMSK)) { + /* Can not tell difference between parity & frame error */ + uport->icount.parity++; + error_f = 1; + if (uport->ignore_status_mask & IGNPAR) + tty_insert_flip_char(tty, 0, TTY_PARITY); + } + + if (error_f) + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS); + + if (msm_uport->clk_req_off_state == CLK_REQ_OFF_FLUSH_ISSUED) + msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_FLUSHED; + + flush = msm_uport->rx.flush; + if (flush == FLUSH_IGNORE) + msm_hs_start_rx_locked(uport); + if (flush == FLUSH_STOP) + msm_uport->rx.flush = FLUSH_SHUTDOWN; + if (flush >= FLUSH_DATA_INVALID) + goto out; + + rx_count = msm_hs_read(uport, UARTDM_RX_TOTAL_SNAP_ADDR); + + if (0 != (uport->read_status_mask & CREAD)) { + retval = tty_insert_flip_string(tty, msm_uport->rx.buffer, + rx_count); + BUG_ON(retval != rx_count); + } + + msm_hs_start_rx_locked(uport); + +out: + clk_disable(msm_uport->clk); + + spin_unlock_irqrestore(&uport->lock, flags); + + if (flush < FLUSH_DATA_INVALID) + queue_work(msm_hs_workqueue, &msm_uport->rx.tty_work); +} + +static void msm_hs_tty_flip_buffer_work(struct work_struct *work) +{ + struct msm_hs_port *msm_uport = + container_of(work, struct msm_hs_port, rx.tty_work); + struct tty_struct *tty = msm_uport->uport.state->port.tty; + + tty_flip_buffer_push(tty); +} + +/* + * Standard API, Current states of modem control inputs + * + * Since CTS can be handled entirely by HARDWARE we always + * indicate clear to send and count on the TX FIFO to block when + * it fills up. + * + * - TIOCM_DCD + * - TIOCM_CTS + * - TIOCM_DSR + * - TIOCM_RI + * (Unsupported) DCD and DSR will return them high. RI will return low. + */ +static unsigned int msm_hs_get_mctrl_locked(struct uart_port *uport) +{ + return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS; +} + +/* + * True enables UART auto RFR, which indicates we are ready for data if the RX + * buffer is not full. False disables auto RFR, and deasserts RFR to indicate + * we are not ready for data. Must be called with UART clock on. + */ +static void set_rfr_locked(struct uart_port *uport, int auto_rfr) +{ + unsigned int data; + + data = msm_hs_read(uport, UARTDM_MR1_ADDR); + + if (auto_rfr) { + /* enable auto ready-for-receiving */ + data |= UARTDM_MR1_RX_RDY_CTL_BMSK; + msm_hs_write(uport, UARTDM_MR1_ADDR, data); + } else { + /* disable auto ready-for-receiving */ + data &= ~UARTDM_MR1_RX_RDY_CTL_BMSK; + msm_hs_write(uport, UARTDM_MR1_ADDR, data); + /* RFR is active low, set high */ + msm_hs_write(uport, UARTDM_CR_ADDR, RFR_HIGH); + } +} + +/* + * Standard API, used to set or clear RFR + */ +static void msm_hs_set_mctrl_locked(struct uart_port *uport, + unsigned int mctrl) +{ + unsigned int auto_rfr; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + clk_enable(msm_uport->clk); + + auto_rfr = TIOCM_RTS & mctrl ? 1 : 0; + set_rfr_locked(uport, auto_rfr); + + clk_disable(msm_uport->clk); +} + +/* Standard API, Enable modem status (CTS) interrupt */ +static void msm_hs_enable_ms_locked(struct uart_port *uport) +{ + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + clk_enable(msm_uport->clk); + + /* Enable DELTA_CTS Interrupt */ + msm_uport->imr_reg |= UARTDM_ISR_DELTA_CTS_BMSK; + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + + clk_disable(msm_uport->clk); + +} + +/* + * Standard API, Break Signal + * + * Control the transmission of a break signal. ctl eq 0 => break + * signal terminate ctl ne 0 => start break signal + */ +static void msm_hs_break_ctl(struct uart_port *uport, int ctl) +{ + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + clk_enable(msm_uport->clk); + msm_hs_write(uport, UARTDM_CR_ADDR, ctl ? START_BREAK : STOP_BREAK); + clk_disable(msm_uport->clk); +} + +static void msm_hs_config_port(struct uart_port *uport, int cfg_flags) +{ + unsigned long flags; + + spin_lock_irqsave(&uport->lock, flags); + if (cfg_flags & UART_CONFIG_TYPE) { + uport->type = PORT_MSM; + msm_hs_request_port(uport); + } + spin_unlock_irqrestore(&uport->lock, flags); +} + +/* Handle CTS changes (Called from interrupt handler) */ +static void msm_hs_handle_delta_cts(struct uart_port *uport) +{ + unsigned long flags; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + spin_lock_irqsave(&uport->lock, flags); + clk_enable(msm_uport->clk); + + /* clear interrupt */ + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_CTS); + uport->icount.cts++; + + clk_disable(msm_uport->clk); + spin_unlock_irqrestore(&uport->lock, flags); + + /* clear the IOCTL TIOCMIWAIT if called */ + wake_up_interruptible(&uport->state->port.delta_msr_wait); +} + +/* check if the TX path is flushed, and if so clock off + * returns 0 did not clock off, need to retry (still sending final byte) + * -1 did not clock off, do not retry + * 1 if we clocked off + */ +static int msm_hs_check_clock_off_locked(struct uart_port *uport) +{ + unsigned long sr_status; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + struct circ_buf *tx_buf = &uport->state->xmit; + + /* Cancel if tx tty buffer is not empty, dma is in flight, + * or tx fifo is not empty, or rx fifo is not empty */ + if (msm_uport->clk_state != MSM_HS_CLK_REQUEST_OFF || + !uart_circ_empty(tx_buf) || msm_uport->tx.dma_in_flight || + (msm_uport->imr_reg & UARTDM_ISR_TXLEV_BMSK) || + !(msm_uport->imr_reg & UARTDM_ISR_RXLEV_BMSK)) { + return -1; + } + + /* Make sure the uart is finished with the last byte */ + sr_status = msm_hs_read(uport, UARTDM_SR_ADDR); + if (!(sr_status & UARTDM_SR_TXEMT_BMSK)) + return 0; /* retry */ + + /* Make sure forced RXSTALE flush complete */ + switch (msm_uport->clk_req_off_state) { + case CLK_REQ_OFF_START: + msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_ISSUED; + msm_hs_write(uport, UARTDM_CR_ADDR, FORCE_STALE_EVENT); + return 0; /* RXSTALE flush not complete - retry */ + case CLK_REQ_OFF_RXSTALE_ISSUED: + case CLK_REQ_OFF_FLUSH_ISSUED: + return 0; /* RXSTALE flush not complete - retry */ + case CLK_REQ_OFF_RXSTALE_FLUSHED: + break; /* continue */ + } + + if (msm_uport->rx.flush != FLUSH_SHUTDOWN) { + if (msm_uport->rx.flush == FLUSH_NONE) + msm_hs_stop_rx_locked(uport); + return 0; /* come back later to really clock off */ + } + + /* we really want to clock off */ + clk_disable(msm_uport->clk); + msm_uport->clk_state = MSM_HS_CLK_OFF; + + if (use_low_power_rx_wakeup(msm_uport)) { + msm_uport->rx_wakeup.ignore = 1; + enable_irq(msm_uport->rx_wakeup.irq); + } + return 1; +} + +static enum hrtimer_restart msm_hs_clk_off_retry(struct hrtimer *timer) +{ + unsigned long flags; + int ret = HRTIMER_NORESTART; + struct msm_hs_port *msm_uport = container_of(timer, struct msm_hs_port, + clk_off_timer); + struct uart_port *uport = &msm_uport->uport; + + spin_lock_irqsave(&uport->lock, flags); + + if (!msm_hs_check_clock_off_locked(uport)) { + hrtimer_forward_now(timer, msm_uport->clk_off_delay); + ret = HRTIMER_RESTART; + } + + spin_unlock_irqrestore(&uport->lock, flags); + + return ret; +} + +static irqreturn_t msm_hs_isr(int irq, void *dev) +{ + unsigned long flags; + unsigned long isr_status; + struct msm_hs_port *msm_uport = dev; + struct uart_port *uport = &msm_uport->uport; + struct circ_buf *tx_buf = &uport->state->xmit; + struct msm_hs_tx *tx = &msm_uport->tx; + struct msm_hs_rx *rx = &msm_uport->rx; + + spin_lock_irqsave(&uport->lock, flags); + + isr_status = msm_hs_read(uport, UARTDM_MISR_ADDR); + + /* Uart RX starting */ + if (isr_status & UARTDM_ISR_RXLEV_BMSK) { + msm_uport->imr_reg &= ~UARTDM_ISR_RXLEV_BMSK; + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + } + /* Stale rx interrupt */ + if (isr_status & UARTDM_ISR_RXSTALE_BMSK) { + msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT); + + if (msm_uport->clk_req_off_state == CLK_REQ_OFF_RXSTALE_ISSUED) + msm_uport->clk_req_off_state = + CLK_REQ_OFF_FLUSH_ISSUED; + if (rx->flush == FLUSH_NONE) { + rx->flush = FLUSH_DATA_READY; + msm_dmov_stop_cmd(msm_uport->dma_rx_channel, NULL, 1); + } + } + /* tx ready interrupt */ + if (isr_status & UARTDM_ISR_TX_READY_BMSK) { + /* Clear TX Ready */ + msm_hs_write(uport, UARTDM_CR_ADDR, CLEAR_TX_READY); + + if (msm_uport->clk_state == MSM_HS_CLK_REQUEST_OFF) { + msm_uport->imr_reg |= UARTDM_ISR_TXLEV_BMSK; + msm_hs_write(uport, UARTDM_IMR_ADDR, + msm_uport->imr_reg); + } + + /* Complete DMA TX transactions and submit new transactions */ + tx_buf->tail = (tx_buf->tail + tx->tx_count) & ~UART_XMIT_SIZE; + + tx->dma_in_flight = 0; + + uport->icount.tx += tx->tx_count; + if (tx->tx_ready_int_en) + msm_hs_submit_tx_locked(uport); + + if (uart_circ_chars_pending(tx_buf) < WAKEUP_CHARS) + uart_write_wakeup(uport); + } + if (isr_status & UARTDM_ISR_TXLEV_BMSK) { + /* TX FIFO is empty */ + msm_uport->imr_reg &= ~UARTDM_ISR_TXLEV_BMSK; + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + if (!msm_hs_check_clock_off_locked(uport)) + hrtimer_start(&msm_uport->clk_off_timer, + msm_uport->clk_off_delay, + HRTIMER_MODE_REL); + } + + /* Change in CTS interrupt */ + if (isr_status & UARTDM_ISR_DELTA_CTS_BMSK) + msm_hs_handle_delta_cts(uport); + + spin_unlock_irqrestore(&uport->lock, flags); + + return IRQ_HANDLED; +} + +void msm_hs_request_clock_off_locked(struct uart_port *uport) +{ + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + if (msm_uport->clk_state == MSM_HS_CLK_ON) { + msm_uport->clk_state = MSM_HS_CLK_REQUEST_OFF; + msm_uport->clk_req_off_state = CLK_REQ_OFF_START; + if (!use_low_power_rx_wakeup(msm_uport)) + set_rfr_locked(uport, 0); + msm_uport->imr_reg |= UARTDM_ISR_TXLEV_BMSK; + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + } +} + +/** + * msm_hs_request_clock_off - request to (i.e. asynchronously) turn off uart + * clock once pending TX is flushed and Rx DMA command is terminated. + * @uport: uart_port structure for the device instance. + * + * This functions puts the device into a partially active low power mode. It + * waits to complete all pending tx transactions, flushes ongoing Rx DMA + * command and terminates UART side Rx transaction, puts UART HW in non DMA + * mode and then clocks off the device. A client calls this when no UART + * data is expected. msm_request_clock_on() must be called before any further + * UART can be sent or received. + */ +void msm_hs_request_clock_off(struct uart_port *uport) +{ + unsigned long flags; + + spin_lock_irqsave(&uport->lock, flags); + msm_hs_request_clock_off_locked(uport); + spin_unlock_irqrestore(&uport->lock, flags); +} + +void msm_hs_request_clock_on_locked(struct uart_port *uport) +{ + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + unsigned int data; + + switch (msm_uport->clk_state) { + case MSM_HS_CLK_OFF: + clk_enable(msm_uport->clk); + disable_irq_nosync(msm_uport->rx_wakeup.irq); + /* fall-through */ + case MSM_HS_CLK_REQUEST_OFF: + if (msm_uport->rx.flush == FLUSH_STOP || + msm_uport->rx.flush == FLUSH_SHUTDOWN) { + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX); + data = msm_hs_read(uport, UARTDM_DMEN_ADDR); + data |= UARTDM_RX_DM_EN_BMSK; + msm_hs_write(uport, UARTDM_DMEN_ADDR, data); + } + hrtimer_try_to_cancel(&msm_uport->clk_off_timer); + if (msm_uport->rx.flush == FLUSH_SHUTDOWN) + msm_hs_start_rx_locked(uport); + if (!use_low_power_rx_wakeup(msm_uport)) + set_rfr_locked(uport, 1); + if (msm_uport->rx.flush == FLUSH_STOP) + msm_uport->rx.flush = FLUSH_IGNORE; + msm_uport->clk_state = MSM_HS_CLK_ON; + break; + case MSM_HS_CLK_ON: + break; + case MSM_HS_CLK_PORT_OFF: + break; + } +} + +/** + * msm_hs_request_clock_on - Switch the device from partially active low + * power mode to fully active (i.e. clock on) mode. + * @uport: uart_port structure for the device. + * + * This function switches on the input clock, puts UART HW into DMA mode + * and enqueues an Rx DMA command if the device was in partially active + * mode. It has no effect if called with the device in inactive state. + */ +void msm_hs_request_clock_on(struct uart_port *uport) +{ + unsigned long flags; + + spin_lock_irqsave(&uport->lock, flags); + msm_hs_request_clock_on_locked(uport); + spin_unlock_irqrestore(&uport->lock, flags); +} + +static irqreturn_t msm_hs_rx_wakeup_isr(int irq, void *dev) +{ + unsigned int wakeup = 0; + unsigned long flags; + struct msm_hs_port *msm_uport = dev; + struct uart_port *uport = &msm_uport->uport; + struct tty_struct *tty = NULL; + + spin_lock_irqsave(&uport->lock, flags); + if (msm_uport->clk_state == MSM_HS_CLK_OFF) { + /* ignore the first irq - it is a pending irq that occured + * before enable_irq() */ + if (msm_uport->rx_wakeup.ignore) + msm_uport->rx_wakeup.ignore = 0; + else + wakeup = 1; + } + + if (wakeup) { + /* the uart was clocked off during an rx, wake up and + * optionally inject char into tty rx */ + msm_hs_request_clock_on_locked(uport); + if (msm_uport->rx_wakeup.inject_rx) { + tty = uport->state->port.tty; + tty_insert_flip_char(tty, + msm_uport->rx_wakeup.rx_to_inject, + TTY_NORMAL); + queue_work(msm_hs_workqueue, &msm_uport->rx.tty_work); + } + } + + spin_unlock_irqrestore(&uport->lock, flags); + + return IRQ_HANDLED; +} + +static const char *msm_hs_type(struct uart_port *port) +{ + return (port->type == PORT_MSM) ? "MSM_HS_UART" : NULL; +} + +/* Called when port is opened */ +static int msm_hs_startup(struct uart_port *uport) +{ + int ret; + int rfr_level; + unsigned long flags; + unsigned int data; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + struct circ_buf *tx_buf = &uport->state->xmit; + struct msm_hs_tx *tx = &msm_uport->tx; + struct msm_hs_rx *rx = &msm_uport->rx; + + rfr_level = uport->fifosize; + if (rfr_level > 16) + rfr_level -= 16; + + tx->dma_base = dma_map_single(uport->dev, tx_buf->buf, UART_XMIT_SIZE, + DMA_TO_DEVICE); + + /* do not let tty layer execute RX in global workqueue, use a + * dedicated workqueue managed by this driver */ + uport->state->port.tty->low_latency = 1; + + /* turn on uart clk */ + ret = msm_hs_init_clk_locked(uport); + if (unlikely(ret)) { + printk(KERN_ERR "Turning uartclk failed!\n"); + goto err_msm_hs_init_clk; + } + + /* Set auto RFR Level */ + data = msm_hs_read(uport, UARTDM_MR1_ADDR); + data &= ~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK; + data &= ~UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK; + data |= (UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK & (rfr_level << 2)); + data |= (UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK & rfr_level); + msm_hs_write(uport, UARTDM_MR1_ADDR, data); + + /* Make sure RXSTALE count is non-zero */ + data = msm_hs_read(uport, UARTDM_IPR_ADDR); + if (!data) { + data |= 0x1f & UARTDM_IPR_STALE_LSB_BMSK; + msm_hs_write(uport, UARTDM_IPR_ADDR, data); + } + + /* Enable Data Mover Mode */ + data = UARTDM_TX_DM_EN_BMSK | UARTDM_RX_DM_EN_BMSK; + msm_hs_write(uport, UARTDM_DMEN_ADDR, data); + + /* Reset TX */ + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_BREAK_INT); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_CTS); + msm_hs_write(uport, UARTDM_CR_ADDR, RFR_LOW); + /* Turn on Uart Receiver */ + msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_EN_BMSK); + + /* Turn on Uart Transmitter */ + msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_EN_BMSK); + + /* Initialize the tx */ + tx->tx_ready_int_en = 0; + tx->dma_in_flight = 0; + + tx->xfer.complete_func = msm_hs_dmov_tx_callback; + tx->xfer.execute_func = NULL; + + tx->command_ptr->cmd = CMD_LC | + CMD_DST_CRCI(msm_uport->dma_tx_crci) | CMD_MODE_BOX; + + tx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16) + | (MSM_UARTDM_BURST_SIZE); + + tx->command_ptr->row_offset = (MSM_UARTDM_BURST_SIZE << 16); + + tx->command_ptr->dst_row_addr = + msm_uport->uport.mapbase + UARTDM_TF_ADDR; + + + /* Turn on Uart Receive */ + rx->xfer.complete_func = msm_hs_dmov_rx_callback; + rx->xfer.execute_func = NULL; + + rx->command_ptr->cmd = CMD_LC | + CMD_SRC_CRCI(msm_uport->dma_rx_crci) | CMD_MODE_BOX; + + rx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16) + | (MSM_UARTDM_BURST_SIZE); + rx->command_ptr->row_offset = MSM_UARTDM_BURST_SIZE; + rx->command_ptr->src_row_addr = uport->mapbase + UARTDM_RF_ADDR; + + + msm_uport->imr_reg |= UARTDM_ISR_RXSTALE_BMSK; + /* Enable reading the current CTS, no harm even if CTS is ignored */ + msm_uport->imr_reg |= UARTDM_ISR_CURRENT_CTS_BMSK; + + msm_hs_write(uport, UARTDM_TFWR_ADDR, 0); /* TXLEV on empty TX fifo */ + + + ret = request_irq(uport->irq, msm_hs_isr, IRQF_TRIGGER_HIGH, + "msm_hs_uart", msm_uport); + if (unlikely(ret)) { + printk(KERN_ERR "Request msm_hs_uart IRQ failed!\n"); + goto err_request_irq; + } + if (use_low_power_rx_wakeup(msm_uport)) { + ret = request_irq(msm_uport->rx_wakeup.irq, + msm_hs_rx_wakeup_isr, + IRQF_TRIGGER_FALLING, + "msm_hs_rx_wakeup", msm_uport); + if (unlikely(ret)) { + printk(KERN_ERR "Request msm_hs_rx_wakeup IRQ failed!\n"); + free_irq(uport->irq, msm_uport); + goto err_request_irq; + } + disable_irq(msm_uport->rx_wakeup.irq); + } + + spin_lock_irqsave(&uport->lock, flags); + + msm_hs_write(uport, UARTDM_RFWR_ADDR, 0); + msm_hs_start_rx_locked(uport); + + spin_unlock_irqrestore(&uport->lock, flags); + ret = pm_runtime_set_active(uport->dev); + if (ret) + dev_err(uport->dev, "set active error:%d\n", ret); + pm_runtime_enable(uport->dev); + + return 0; + +err_request_irq: +err_msm_hs_init_clk: + dma_unmap_single(uport->dev, tx->dma_base, + UART_XMIT_SIZE, DMA_TO_DEVICE); + return ret; +} + +/* Initialize tx and rx data structures */ +static int __devinit uartdm_init_port(struct uart_port *uport) +{ + int ret = 0; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + struct msm_hs_tx *tx = &msm_uport->tx; + struct msm_hs_rx *rx = &msm_uport->rx; + + /* Allocate the command pointer. Needs to be 64 bit aligned */ + tx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA); + if (!tx->command_ptr) + return -ENOMEM; + + tx->command_ptr_ptr = kmalloc(sizeof(u32 *), GFP_KERNEL | __GFP_DMA); + if (!tx->command_ptr_ptr) { + ret = -ENOMEM; + goto err_tx_command_ptr_ptr; + } + + tx->mapped_cmd_ptr = dma_map_single(uport->dev, tx->command_ptr, + sizeof(dmov_box), DMA_TO_DEVICE); + tx->mapped_cmd_ptr_ptr = dma_map_single(uport->dev, + tx->command_ptr_ptr, + sizeof(u32 *), DMA_TO_DEVICE); + tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr); + + init_waitqueue_head(&rx->wait); + + rx->pool = dma_pool_create("rx_buffer_pool", uport->dev, + UARTDM_RX_BUF_SIZE, 16, 0); + if (!rx->pool) { + pr_err("%s(): cannot allocate rx_buffer_pool", __func__); + ret = -ENOMEM; + goto err_dma_pool_create; + } + + rx->buffer = dma_pool_alloc(rx->pool, GFP_KERNEL, &rx->rbuffer); + if (!rx->buffer) { + pr_err("%s(): cannot allocate rx->buffer", __func__); + ret = -ENOMEM; + goto err_dma_pool_alloc; + } + + /* Allocate the command pointer. Needs to be 64 bit aligned */ + rx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA); + if (!rx->command_ptr) { + pr_err("%s(): cannot allocate rx->command_ptr", __func__); + ret = -ENOMEM; + goto err_rx_command_ptr; + } + + rx->command_ptr_ptr = kmalloc(sizeof(u32 *), GFP_KERNEL | __GFP_DMA); + if (!rx->command_ptr_ptr) { + pr_err("%s(): cannot allocate rx->command_ptr_ptr", __func__); + ret = -ENOMEM; + goto err_rx_command_ptr_ptr; + } + + rx->command_ptr->num_rows = ((UARTDM_RX_BUF_SIZE >> 4) << 16) | + (UARTDM_RX_BUF_SIZE >> 4); + + rx->command_ptr->dst_row_addr = rx->rbuffer; + + rx->mapped_cmd_ptr = dma_map_single(uport->dev, rx->command_ptr, + sizeof(dmov_box), DMA_TO_DEVICE); + + *rx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(rx->mapped_cmd_ptr); + + rx->cmdptr_dmaaddr = dma_map_single(uport->dev, rx->command_ptr_ptr, + sizeof(u32 *), DMA_TO_DEVICE); + rx->xfer.cmdptr = DMOV_CMD_ADDR(rx->cmdptr_dmaaddr); + + INIT_WORK(&rx->tty_work, msm_hs_tty_flip_buffer_work); + + return ret; + +err_rx_command_ptr_ptr: + kfree(rx->command_ptr); +err_rx_command_ptr: + dma_pool_free(msm_uport->rx.pool, msm_uport->rx.buffer, + msm_uport->rx.rbuffer); +err_dma_pool_alloc: + dma_pool_destroy(msm_uport->rx.pool); +err_dma_pool_create: + dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr_ptr, + sizeof(u32 *), DMA_TO_DEVICE); + dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr, + sizeof(dmov_box), DMA_TO_DEVICE); + kfree(msm_uport->tx.command_ptr_ptr); +err_tx_command_ptr_ptr: + kfree(msm_uport->tx.command_ptr); + return ret; +} + +static int __devinit msm_hs_probe(struct platform_device *pdev) +{ + int ret; + struct uart_port *uport; + struct msm_hs_port *msm_uport; + struct resource *resource; + const struct msm_serial_hs_platform_data *pdata = + pdev->dev.platform_data; + + if (pdev->id < 0 || pdev->id >= UARTDM_NR) { + printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id); + return -EINVAL; + } + + msm_uport = &q_uart_port[pdev->id]; + uport = &msm_uport->uport; + + uport->dev = &pdev->dev; + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!resource)) + return -ENXIO; + + uport->mapbase = resource->start; + uport->irq = platform_get_irq(pdev, 0); + if (unlikely(uport->irq < 0)) + return -ENXIO; + + if (unlikely(set_irq_wake(uport->irq, 1))) + return -ENXIO; + + if (pdata == NULL || pdata->rx_wakeup_irq < 0) + msm_uport->rx_wakeup.irq = -1; + else { + msm_uport->rx_wakeup.irq = pdata->rx_wakeup_irq; + msm_uport->rx_wakeup.ignore = 1; + msm_uport->rx_wakeup.inject_rx = pdata->inject_rx_on_wakeup; + msm_uport->rx_wakeup.rx_to_inject = pdata->rx_to_inject; + + if (unlikely(msm_uport->rx_wakeup.irq < 0)) + return -ENXIO; + + if (unlikely(set_irq_wake(msm_uport->rx_wakeup.irq, 1))) + return -ENXIO; + } + + if (pdata == NULL) + msm_uport->exit_lpm_cb = NULL; + else + msm_uport->exit_lpm_cb = pdata->exit_lpm_cb; + + resource = platform_get_resource_byname(pdev, IORESOURCE_DMA, + "uartdm_channels"); + if (unlikely(!resource)) + return -ENXIO; + + msm_uport->dma_tx_channel = resource->start; + msm_uport->dma_rx_channel = resource->end; + + resource = platform_get_resource_byname(pdev, IORESOURCE_DMA, + "uartdm_crci"); + if (unlikely(!resource)) + return -ENXIO; + + msm_uport->dma_tx_crci = resource->start; + msm_uport->dma_rx_crci = resource->end; + + uport->iotype = UPIO_MEM; + uport->fifosize = UART_FIFOSIZE; + uport->ops = &msm_hs_ops; + uport->flags = UPF_BOOT_AUTOCONF; + uport->uartclk = UARTCLK; + msm_uport->imr_reg = 0x0; + msm_uport->clk = clk_get(&pdev->dev, "uartdm_clk"); + if (IS_ERR(msm_uport->clk)) + return PTR_ERR(msm_uport->clk); + + ret = uartdm_init_port(uport); + if (unlikely(ret)) + return ret; + + msm_uport->clk_state = MSM_HS_CLK_PORT_OFF; + hrtimer_init(&msm_uport->clk_off_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + msm_uport->clk_off_timer.function = msm_hs_clk_off_retry; + msm_uport->clk_off_delay = ktime_set(0, 1000000); /* 1ms */ + + uport->line = pdev->id; + return uart_add_one_port(&msm_hs_driver, uport); +} + +static int __init msm_serial_hs_init(void) +{ + int ret, i; + + /* Init all UARTS as non-configured */ + for (i = 0; i < UARTDM_NR; i++) + q_uart_port[i].uport.type = PORT_UNKNOWN; + + msm_hs_workqueue = create_singlethread_workqueue("msm_serial_hs"); + if (unlikely(!msm_hs_workqueue)) + return -ENOMEM; + + ret = uart_register_driver(&msm_hs_driver); + if (unlikely(ret)) { + printk(KERN_ERR "%s failed to load\n", __func__); + goto err_uart_register_driver; + } + + ret = platform_driver_register(&msm_serial_hs_platform_driver); + if (ret) { + printk(KERN_ERR "%s failed to load\n", __func__); + goto err_platform_driver_register; + } + + return ret; + +err_platform_driver_register: + uart_unregister_driver(&msm_hs_driver); +err_uart_register_driver: + destroy_workqueue(msm_hs_workqueue); + return ret; +} +module_init(msm_serial_hs_init); + +/* + * Called by the upper layer when port is closed. + * - Disables the port + * - Unhook the ISR + */ +static void msm_hs_shutdown(struct uart_port *uport) +{ + unsigned long flags; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + BUG_ON(msm_uport->rx.flush < FLUSH_STOP); + + spin_lock_irqsave(&uport->lock, flags); + clk_enable(msm_uport->clk); + + /* Disable the transmitter */ + msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_DISABLE_BMSK); + /* Disable the receiver */ + msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_DISABLE_BMSK); + + pm_runtime_disable(uport->dev); + pm_runtime_set_suspended(uport->dev); + + /* Free the interrupt */ + free_irq(uport->irq, msm_uport); + if (use_low_power_rx_wakeup(msm_uport)) + free_irq(msm_uport->rx_wakeup.irq, msm_uport); + + msm_uport->imr_reg = 0; + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + + wait_event(msm_uport->rx.wait, msm_uport->rx.flush == FLUSH_SHUTDOWN); + + clk_disable(msm_uport->clk); /* to balance local clk_enable() */ + if (msm_uport->clk_state != MSM_HS_CLK_OFF) + clk_disable(msm_uport->clk); /* to balance clk_state */ + msm_uport->clk_state = MSM_HS_CLK_PORT_OFF; + + dma_unmap_single(uport->dev, msm_uport->tx.dma_base, + UART_XMIT_SIZE, DMA_TO_DEVICE); + + spin_unlock_irqrestore(&uport->lock, flags); + + if (cancel_work_sync(&msm_uport->rx.tty_work)) + msm_hs_tty_flip_buffer_work(&msm_uport->rx.tty_work); +} + +static void __exit msm_serial_hs_exit(void) +{ + flush_workqueue(msm_hs_workqueue); + destroy_workqueue(msm_hs_workqueue); + platform_driver_unregister(&msm_serial_hs_platform_driver); + uart_unregister_driver(&msm_hs_driver); +} +module_exit(msm_serial_hs_exit); + +#ifdef CONFIG_PM_RUNTIME +static int msm_hs_runtime_idle(struct device *dev) +{ + /* + * returning success from idle results in runtime suspend to be + * called + */ + return 0; +} + +static int msm_hs_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, struct + platform_device, dev); + struct msm_hs_port *msm_uport = &q_uart_port[pdev->id]; + + msm_hs_request_clock_on(&msm_uport->uport); + return 0; +} + +static int msm_hs_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, struct + platform_device, dev); + struct msm_hs_port *msm_uport = &q_uart_port[pdev->id]; + + msm_hs_request_clock_off(&msm_uport->uport); + return 0; +} +#else +#define msm_hs_runtime_idle NULL +#define msm_hs_runtime_resume NULL +#define msm_hs_runtime_suspend NULL +#endif + +static const struct dev_pm_ops msm_hs_dev_pm_ops = { + .runtime_suspend = msm_hs_runtime_suspend, + .runtime_resume = msm_hs_runtime_resume, + .runtime_idle = msm_hs_runtime_idle, +}; + +static struct platform_driver msm_serial_hs_platform_driver = { + .probe = msm_hs_probe, + .remove = __devexit_p(msm_hs_remove), + .driver = { + .name = "msm_serial_hs", + .owner = THIS_MODULE, + .pm = &msm_hs_dev_pm_ops, + }, +}; + +static struct uart_driver msm_hs_driver = { + .owner = THIS_MODULE, + .driver_name = "msm_serial_hs", + .dev_name = "ttyHS", + .nr = UARTDM_NR, + .cons = 0, +}; + +static struct uart_ops msm_hs_ops = { + .tx_empty = msm_hs_tx_empty, + .set_mctrl = msm_hs_set_mctrl_locked, + .get_mctrl = msm_hs_get_mctrl_locked, + .stop_tx = msm_hs_stop_tx_locked, + .start_tx = msm_hs_start_tx_locked, + .stop_rx = msm_hs_stop_rx_locked, + .enable_ms = msm_hs_enable_ms_locked, + .break_ctl = msm_hs_break_ctl, + .startup = msm_hs_startup, + .shutdown = msm_hs_shutdown, + .set_termios = msm_hs_set_termios, + .pm = msm_hs_pm, + .type = msm_hs_type, + .config_port = msm_hs_config_port, + .release_port = msm_hs_release_port, + .request_port = msm_hs_request_port, +}; + +MODULE_DESCRIPTION("High Speed UART Driver for the MSM chipset"); +MODULE_VERSION("1.2"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/msm_serial_hs.h b/include/linux/platform_data/msm_serial_hs.h new file mode 100644 index 000000000000..98a2046f8b31 --- /dev/null +++ b/include/linux/platform_data/msm_serial_hs.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008 Google, Inc. + * Author: Nick Pelly + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 __ASM_ARCH_MSM_SERIAL_HS_H +#define __ASM_ARCH_MSM_SERIAL_HS_H + +#include + +/* API to request the uart clock off or on for low power management + * Clients should call request_clock_off() when no uart data is expected, + * and must call request_clock_on() before any further uart data can be + * received. */ +extern void msm_hs_request_clock_off(struct uart_port *uport); +extern void msm_hs_request_clock_on(struct uart_port *uport); + +/** + * struct msm_serial_hs_platform_data + * @rx_wakeup_irq: Rx activity irq + * @rx_to_inject: extra character to be inserted to Rx tty on wakeup + * @inject_rx: 1 = insert rx_to_inject. 0 = do not insert extra character + * @exit_lpm_cb: function called before every Tx transaction + * + * This is an optional structure required for UART Rx GPIO IRQ based + * wakeup from low power state. UART wakeup can be triggered by RX activity + * (using a wakeup GPIO on the UART RX pin). This should only be used if + * there is not a wakeup GPIO on the UART CTS, and the first RX byte is + * known (eg., with the Bluetooth Texas Instruments HCILL protocol), + * since the first RX byte will always be lost. RTS will be asserted even + * while the UART is clocked off in this mode of operation. + */ +struct msm_serial_hs_platform_data { + int rx_wakeup_irq; + unsigned char inject_rx_on_wakeup; + char rx_to_inject; + void (*exit_lpm_cb)(struct uart_port *); +}; + +#endif -- cgit v1.2.3 From 9b37596a2e860404503a3f2a6513db60c296bfdc Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 7 Mar 2011 11:11:52 -0500 Subject: USB: move usbcore away from hcd->state The hcd->state variable is a disaster. It's not clearly owned by either usbcore or the host controller drivers, and they both change it from time to time, potentially stepping on each other's toes. It's not protected by any locks. And there's no mechanism to prevent it from going through an invalid transition. This patch (as1451) takes a first step toward fixing these problems. As it turns out, usbcore uses hcd->state for essentially only two things: checking whether the controller's root hub is running and checking whether the controller has died. Therefore the patch adds two new atomic bitflags to the hcd structure, to store these pieces of information. The new flags are used only by usbcore, and a private spinlock prevents invalid combinations (a dead controller's root hub cannot be running). The patch does not change the places where usbcore sets hcd->state, since HCDs may depend on them. Furthermore, there is one place in usb_hcd_irq() where usbcore still must use hcd->state: An HCD's interrupt handler can implicitly indicate that the controller died by setting hcd->state to HC_STATE_HALT. Nevertheless, the new code is a big improvement over the current code. The patch makes one other change. The hcd_bus_suspend() and hcd_bus_resume() routines now check first whether the host controller has died; if it has then they return immediately without calling the HCD's bus_suspend or bus_resume methods. This fixes the major problem reported in Bugzilla #29902: The system fails to suspend after a host controller dies during system resume. Signed-off-by: Alan Stern Tested-by: Alex Terekhov CC: Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 13 +++++------ drivers/usb/core/hcd.c | 55 ++++++++++++++++++++++++++++++++++------------ include/linux/usb/hcd.h | 4 ++++ 3 files changed, 51 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index f71e8e307e0f..d37088591d9a 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -363,8 +363,7 @@ static int check_root_hub_suspended(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); struct usb_hcd *hcd = pci_get_drvdata(pci_dev); - if (!(hcd->state == HC_STATE_SUSPENDED || - hcd->state == HC_STATE_HALT)) { + if (HCD_RH_RUNNING(hcd)) { dev_warn(dev, "Root hub is not suspended\n"); return -EBUSY; } @@ -386,7 +385,7 @@ static int suspend_common(struct device *dev, bool do_wakeup) if (retval) return retval; - if (hcd->driver->pci_suspend) { + if (hcd->driver->pci_suspend && !HCD_DEAD(hcd)) { /* Optimization: Don't suspend if a root-hub wakeup is * pending and it would cause the HCD to wake up anyway. */ @@ -427,7 +426,7 @@ static int resume_common(struct device *dev, int event) struct usb_hcd *hcd = pci_get_drvdata(pci_dev); int retval; - if (hcd->state != HC_STATE_SUSPENDED) { + if (HCD_RH_RUNNING(hcd)) { dev_dbg(dev, "can't resume, not suspended!\n"); return 0; } @@ -442,7 +441,7 @@ static int resume_common(struct device *dev, int event) clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); - if (hcd->driver->pci_resume) { + if (hcd->driver->pci_resume && !HCD_DEAD(hcd)) { if (event != PM_EVENT_AUTO_RESUME) wait_for_companions(pci_dev, hcd); @@ -475,10 +474,10 @@ static int hcd_pci_suspend_noirq(struct device *dev) pci_save_state(pci_dev); - /* If the root hub is HALTed rather than SUSPENDed, + /* If the root hub is dead rather than suspended, * disallow remote wakeup. */ - if (hcd->state == HC_STATE_HALT) + if (HCD_DEAD(hcd)) device_set_wakeup_enable(dev, 0); dev_dbg(dev, "wakeup: %d\n", device_may_wakeup(dev)); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 24765fd6cf12..e7d0c4571bbe 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -983,7 +983,7 @@ static int register_root_hub(struct usb_hcd *hcd) spin_unlock_irq (&hcd_root_hub_lock); /* Did the HC die before the root hub was registered? */ - if (hcd->state == HC_STATE_HALT) + if (HCD_DEAD(hcd) || hcd->state == HC_STATE_HALT) usb_hc_died (hcd); /* This time clean up */ } @@ -1089,13 +1089,10 @@ int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb) * Check the host controller's state and add the URB to the * endpoint's queue. */ - switch (hcd->state) { - case HC_STATE_RUNNING: - case HC_STATE_RESUMING: + if (HCD_RH_RUNNING(hcd)) { urb->unlinked = 0; list_add_tail(&urb->urb_list, &urb->ep->urb_list); - break; - default: + } else { rc = -ESHUTDOWN; goto done; } @@ -1931,7 +1928,7 @@ int usb_hcd_get_frame_number (struct usb_device *udev) { struct usb_hcd *hcd = bus_to_hcd(udev->bus); - if (!HC_IS_RUNNING (hcd->state)) + if (!HCD_RH_RUNNING(hcd)) return -ESHUTDOWN; return hcd->driver->get_frame_number (hcd); } @@ -1948,9 +1945,15 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) dev_dbg(&rhdev->dev, "bus %s%s\n", (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "suspend"); + if (HCD_DEAD(hcd)) { + dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "suspend"); + return 0; + } + if (!hcd->driver->bus_suspend) { status = -ENOENT; } else { + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); hcd->state = HC_STATE_QUIESCING; status = hcd->driver->bus_suspend(hcd); } @@ -1958,7 +1961,12 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) usb_set_device_state(rhdev, USB_STATE_SUSPENDED); hcd->state = HC_STATE_SUSPENDED; } else { - hcd->state = old_state; + spin_lock_irq(&hcd_root_hub_lock); + if (!HCD_DEAD(hcd)) { + set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + hcd->state = old_state; + } + spin_unlock_irq(&hcd_root_hub_lock); dev_dbg(&rhdev->dev, "bus %s fail, err %d\n", "suspend", status); } @@ -1973,9 +1981,13 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) dev_dbg(&rhdev->dev, "usb %s%s\n", (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume"); + if (HCD_DEAD(hcd)) { + dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "resume"); + return 0; + } if (!hcd->driver->bus_resume) return -ENOENT; - if (hcd->state == HC_STATE_RUNNING) + if (HCD_RH_RUNNING(hcd)) return 0; hcd->state = HC_STATE_RESUMING; @@ -1984,10 +1996,15 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) if (status == 0) { /* TRSMRCY = 10 msec */ msleep(10); - usb_set_device_state(rhdev, rhdev->actconfig - ? USB_STATE_CONFIGURED - : USB_STATE_ADDRESS); - hcd->state = HC_STATE_RUNNING; + spin_lock_irq(&hcd_root_hub_lock); + if (!HCD_DEAD(hcd)) { + usb_set_device_state(rhdev, rhdev->actconfig + ? USB_STATE_CONFIGURED + : USB_STATE_ADDRESS); + set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + hcd->state = HC_STATE_RUNNING; + } + spin_unlock_irq(&hcd_root_hub_lock); } else { hcd->state = old_state; dev_dbg(&rhdev->dev, "bus %s fail, err %d\n", @@ -2098,7 +2115,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd) */ local_irq_save(flags); - if (unlikely(hcd->state == HC_STATE_HALT || !HCD_HW_ACCESSIBLE(hcd))) { + if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd))) { rc = IRQ_NONE; } else if (hcd->driver->irq(hcd) == IRQ_NONE) { rc = IRQ_NONE; @@ -2132,6 +2149,8 @@ void usb_hc_died (struct usb_hcd *hcd) dev_err (hcd->self.controller, "HC died; cleaning up\n"); spin_lock_irqsave (&hcd_root_hub_lock, flags); + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + set_bit(HCD_FLAG_DEAD, &hcd->flags); if (hcd->rh_registered) { clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); @@ -2274,6 +2293,12 @@ int usb_add_hcd(struct usb_hcd *hcd, */ device_init_wakeup(&rhdev->dev, 1); + /* HCD_FLAG_RH_RUNNING doesn't matter until the root hub is + * registered. But since the controller can die at any time, + * let's initialize the flag before touching the hardware. + */ + set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + /* "reset" is misnamed; its role is now one-time init. the controller * should already have been reset (and boot firmware kicked off etc). */ @@ -2341,6 +2366,7 @@ int usb_add_hcd(struct usb_hcd *hcd, return retval; error_create_attr_group: + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); if (HC_IS_RUNNING(hcd->state)) hcd->state = HC_STATE_QUIESCING; spin_lock_irq(&hcd_root_hub_lock); @@ -2393,6 +2419,7 @@ void usb_remove_hcd(struct usb_hcd *hcd) usb_get_dev(rhdev); sysfs_remove_group(&rhdev->dev.kobj, &usb_bus_attr_group); + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); if (HC_IS_RUNNING (hcd->state)) hcd->state = HC_STATE_QUIESCING; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 9cfba4f2457b..8b65068c6af9 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -99,6 +99,8 @@ struct usb_hcd { #define HCD_FLAG_POLL_RH 2 /* poll for rh status? */ #define HCD_FLAG_POLL_PENDING 3 /* status has changed? */ #define HCD_FLAG_WAKEUP_PENDING 4 /* root hub is resuming? */ +#define HCD_FLAG_RH_RUNNING 5 /* root hub is running? */ +#define HCD_FLAG_DEAD 6 /* controller has died? */ /* The flags can be tested using these macros; they are likely to * be slightly faster than test_bit(). @@ -108,6 +110,8 @@ struct usb_hcd { #define HCD_POLL_RH(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_RH)) #define HCD_POLL_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_PENDING)) #define HCD_WAKEUP_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_WAKEUP_PENDING)) +#define HCD_RH_RUNNING(hcd) ((hcd)->flags & (1U << HCD_FLAG_RH_RUNNING)) +#define HCD_DEAD(hcd) ((hcd)->flags & (1U << HCD_FLAG_DEAD)) /* Flags that get set only during HCD registration or removal. */ unsigned rh_registered:1;/* is root hub registered? */ -- cgit v1.2.3 From dfb2130c453c2c6d36b5e0f39eca289cbdbb631d Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Fri, 4 Mar 2011 22:45:02 +0530 Subject: USB: Rename "msm72k_otg.c" to "msm_otg.c" This driver is used across all MSM SoCs. Hence give a generic name. All Functions and strutures are also using "msm_otg" as prefix. Signed-off-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 2 +- drivers/usb/host/Kconfig | 2 +- drivers/usb/otg/Kconfig | 2 +- drivers/usb/otg/Makefile | 2 +- drivers/usb/otg/msm72k_otg.c | 1124 ----------------------------------------- drivers/usb/otg/msm_otg.c | 1124 +++++++++++++++++++++++++++++++++++++++++ include/linux/usb/msm_hsusb.h | 2 +- 7 files changed, 1129 insertions(+), 1129 deletions(-) delete mode 100644 drivers/usb/otg/msm72k_otg.c create mode 100644 drivers/usb/otg/msm_otg.c (limited to 'include') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 99ed91c7cdc6..bfde50e20b30 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -552,7 +552,7 @@ config USB_GADGET_CI13XXX_MSM boolean "MIPS USB CI13xxx for MSM" depends on ARCH_MSM select USB_GADGET_DUALSPEED - select USB_MSM_OTG_72K + select USB_MSM_OTG help MSM SoC has chipidea USB controller. This driver uses ci13xxx_udc core. diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 9116d30bcdac..b5a908eacb82 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -156,7 +156,7 @@ config USB_EHCI_MSM bool "Support for MSM on-chip EHCI USB controller" depends on USB_EHCI_HCD && ARCH_MSM select USB_EHCI_ROOT_HUB_TT - select USB_MSM_OTG_72K + select USB_MSM_OTG ---help--- Enables support for the USB Host controller present on the Qualcomm chipsets. Root Hub has inbuilt TT. diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 9ffc8237fb4b..ceb24fee2eac 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -93,7 +93,7 @@ config USB_LANGWELL_OTG To compile this driver as a module, choose M here: the module will be called langwell_otg. -config USB_MSM_OTG_72K +config USB_MSM_OTG tristate "OTG support for Qualcomm on-chip USB controller" depends on (USB || USB_GADGET) && ARCH_MSM select USB_OTG_UTILS diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index a520e715cfd6..e516894260bf 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -16,5 +16,5 @@ obj-$(CONFIG_TWL6030_USB) += twl6030-usb.o obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o obj-$(CONFIG_USB_ULPI) += ulpi.o -obj-$(CONFIG_USB_MSM_OTG_72K) += msm72k_otg.o +obj-$(CONFIG_USB_MSM_OTG) += msm_otg.o obj-$(CONFIG_AB8500_USB) += ab8500-usb.o diff --git a/drivers/usb/otg/msm72k_otg.c b/drivers/usb/otg/msm72k_otg.c deleted file mode 100644 index 296598628b85..000000000000 --- a/drivers/usb/otg/msm72k_otg.c +++ /dev/null @@ -1,1124 +0,0 @@ -/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#define MSM_USB_BASE (motg->regs) -#define DRIVER_NAME "msm_otg" - -#define ULPI_IO_TIMEOUT_USEC (10 * 1000) -static int ulpi_read(struct otg_transceiver *otg, u32 reg) -{ - struct msm_otg *motg = container_of(otg, struct msm_otg, otg); - int cnt = 0; - - /* initiate read operation */ - writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), - USB_ULPI_VIEWPORT); - - /* wait for completion */ - while (cnt < ULPI_IO_TIMEOUT_USEC) { - if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) - break; - udelay(1); - cnt++; - } - - if (cnt >= ULPI_IO_TIMEOUT_USEC) { - dev_err(otg->dev, "ulpi_read: timeout %08x\n", - readl(USB_ULPI_VIEWPORT)); - return -ETIMEDOUT; - } - return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); -} - -static int ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg) -{ - struct msm_otg *motg = container_of(otg, struct msm_otg, otg); - int cnt = 0; - - /* initiate write operation */ - writel(ULPI_RUN | ULPI_WRITE | - ULPI_ADDR(reg) | ULPI_DATA(val), - USB_ULPI_VIEWPORT); - - /* wait for completion */ - while (cnt < ULPI_IO_TIMEOUT_USEC) { - if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) - break; - udelay(1); - cnt++; - } - - if (cnt >= ULPI_IO_TIMEOUT_USEC) { - dev_err(otg->dev, "ulpi_write: timeout\n"); - return -ETIMEDOUT; - } - return 0; -} - -static struct otg_io_access_ops msm_otg_io_ops = { - .read = ulpi_read, - .write = ulpi_write, -}; - -static void ulpi_init(struct msm_otg *motg) -{ - struct msm_otg_platform_data *pdata = motg->pdata; - int *seq = pdata->phy_init_seq; - - if (!seq) - return; - - while (seq[0] >= 0) { - dev_vdbg(motg->otg.dev, "ulpi: write 0x%02x to 0x%02x\n", - seq[0], seq[1]); - ulpi_write(&motg->otg, seq[0], seq[1]); - seq += 2; - } -} - -static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) -{ - int ret; - - if (assert) { - ret = clk_reset(motg->clk, CLK_RESET_ASSERT); - if (ret) - dev_err(motg->otg.dev, "usb hs_clk assert failed\n"); - } else { - ret = clk_reset(motg->clk, CLK_RESET_DEASSERT); - if (ret) - dev_err(motg->otg.dev, "usb hs_clk deassert failed\n"); - } - return ret; -} - -static int msm_otg_phy_clk_reset(struct msm_otg *motg) -{ - int ret; - - ret = clk_reset(motg->phy_reset_clk, CLK_RESET_ASSERT); - if (ret) { - dev_err(motg->otg.dev, "usb phy clk assert failed\n"); - return ret; - } - usleep_range(10000, 12000); - ret = clk_reset(motg->phy_reset_clk, CLK_RESET_DEASSERT); - if (ret) - dev_err(motg->otg.dev, "usb phy clk deassert failed\n"); - return ret; -} - -static int msm_otg_phy_reset(struct msm_otg *motg) -{ - u32 val; - int ret; - int retries; - - ret = msm_otg_link_clk_reset(motg, 1); - if (ret) - return ret; - ret = msm_otg_phy_clk_reset(motg); - if (ret) - return ret; - ret = msm_otg_link_clk_reset(motg, 0); - if (ret) - return ret; - - val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK; - writel(val | PORTSC_PTS_ULPI, USB_PORTSC); - - for (retries = 3; retries > 0; retries--) { - ret = ulpi_write(&motg->otg, ULPI_FUNC_CTRL_SUSPENDM, - ULPI_CLR(ULPI_FUNC_CTRL)); - if (!ret) - break; - ret = msm_otg_phy_clk_reset(motg); - if (ret) - return ret; - } - if (!retries) - return -ETIMEDOUT; - - /* This reset calibrates the phy, if the above write succeeded */ - ret = msm_otg_phy_clk_reset(motg); - if (ret) - return ret; - - for (retries = 3; retries > 0; retries--) { - ret = ulpi_read(&motg->otg, ULPI_DEBUG); - if (ret != -ETIMEDOUT) - break; - ret = msm_otg_phy_clk_reset(motg); - if (ret) - return ret; - } - if (!retries) - return -ETIMEDOUT; - - dev_info(motg->otg.dev, "phy_reset: success\n"); - return 0; -} - -#define LINK_RESET_TIMEOUT_USEC (250 * 1000) -static int msm_otg_reset(struct otg_transceiver *otg) -{ - struct msm_otg *motg = container_of(otg, struct msm_otg, otg); - struct msm_otg_platform_data *pdata = motg->pdata; - int cnt = 0; - int ret; - u32 val = 0; - u32 ulpi_val = 0; - - ret = msm_otg_phy_reset(motg); - if (ret) { - dev_err(otg->dev, "phy_reset failed\n"); - return ret; - } - - ulpi_init(motg); - - writel(USBCMD_RESET, USB_USBCMD); - while (cnt < LINK_RESET_TIMEOUT_USEC) { - if (!(readl(USB_USBCMD) & USBCMD_RESET)) - break; - udelay(1); - cnt++; - } - if (cnt >= LINK_RESET_TIMEOUT_USEC) - return -ETIMEDOUT; - - /* select ULPI phy */ - writel(0x80000000, USB_PORTSC); - - msleep(100); - - writel(0x0, USB_AHBBURST); - writel(0x00, USB_AHBMODE); - - if (pdata->otg_control == OTG_PHY_CONTROL) { - val = readl(USB_OTGSC); - if (pdata->mode == USB_OTG) { - ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID; - val |= OTGSC_IDIE | OTGSC_BSVIE; - } else if (pdata->mode == USB_PERIPHERAL) { - ulpi_val = ULPI_INT_SESS_VALID; - val |= OTGSC_BSVIE; - } - writel(val, USB_OTGSC); - ulpi_write(otg, ulpi_val, ULPI_USB_INT_EN_RISE); - ulpi_write(otg, ulpi_val, ULPI_USB_INT_EN_FALL); - } - - return 0; -} - -#define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000) -#define PHY_RESUME_TIMEOUT_USEC (100 * 1000) - -#ifdef CONFIG_PM_SLEEP -static int msm_otg_suspend(struct msm_otg *motg) -{ - struct otg_transceiver *otg = &motg->otg; - struct usb_bus *bus = otg->host; - struct msm_otg_platform_data *pdata = motg->pdata; - int cnt = 0; - - if (atomic_read(&motg->in_lpm)) - return 0; - - disable_irq(motg->irq); - /* - * Interrupt Latch Register auto-clear feature is not present - * in all PHY versions. Latch register is clear on read type. - * Clear latch register to avoid spurious wakeup from - * low power mode (LPM). - */ - ulpi_read(otg, 0x14); - - /* - * PHY comparators are disabled when PHY enters into low power - * mode (LPM). Keep PHY comparators ON in LPM only when we expect - * VBUS/Id notifications from USB PHY. Otherwise turn off USB - * PHY comparators. This save significant amount of power. - */ - if (pdata->otg_control == OTG_PHY_CONTROL) - ulpi_write(otg, 0x01, 0x30); - - /* - * PLL is not turned off when PHY enters into low power mode (LPM). - * Disable PLL for maximum power savings. - */ - ulpi_write(otg, 0x08, 0x09); - - /* - * PHY may take some time or even fail to enter into low power - * mode (LPM). Hence poll for 500 msec and reset the PHY and link - * in failure case. - */ - writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); - while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { - if (readl(USB_PORTSC) & PORTSC_PHCD) - break; - udelay(1); - cnt++; - } - - if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) { - dev_err(otg->dev, "Unable to suspend PHY\n"); - msm_otg_reset(otg); - enable_irq(motg->irq); - return -ETIMEDOUT; - } - - /* - * PHY has capability to generate interrupt asynchronously in low - * power mode (LPM). This interrupt is level triggered. So USB IRQ - * line must be disabled till async interrupt enable bit is cleared - * in USBCMD register. Assert STP (ULPI interface STOP signal) to - * block data communication from PHY. - */ - writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); - - clk_disable(motg->pclk); - clk_disable(motg->clk); - if (motg->core_clk) - clk_disable(motg->core_clk); - - if (device_may_wakeup(otg->dev)) - enable_irq_wake(motg->irq); - if (bus) - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); - - atomic_set(&motg->in_lpm, 1); - enable_irq(motg->irq); - - dev_info(otg->dev, "USB in low power mode\n"); - - return 0; -} - -static int msm_otg_resume(struct msm_otg *motg) -{ - struct otg_transceiver *otg = &motg->otg; - struct usb_bus *bus = otg->host; - int cnt = 0; - unsigned temp; - - if (!atomic_read(&motg->in_lpm)) - return 0; - - clk_enable(motg->pclk); - clk_enable(motg->clk); - if (motg->core_clk) - clk_enable(motg->core_clk); - - temp = readl(USB_USBCMD); - temp &= ~ASYNC_INTR_CTRL; - temp &= ~ULPI_STP_CTRL; - writel(temp, USB_USBCMD); - - /* - * PHY comes out of low power mode (LPM) in case of wakeup - * from asynchronous interrupt. - */ - if (!(readl(USB_PORTSC) & PORTSC_PHCD)) - goto skip_phy_resume; - - writel(readl(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC); - while (cnt < PHY_RESUME_TIMEOUT_USEC) { - if (!(readl(USB_PORTSC) & PORTSC_PHCD)) - break; - udelay(1); - cnt++; - } - - if (cnt >= PHY_RESUME_TIMEOUT_USEC) { - /* - * This is a fatal error. Reset the link and - * PHY. USB state can not be restored. Re-insertion - * of USB cable is the only way to get USB working. - */ - dev_err(otg->dev, "Unable to resume USB." - "Re-plugin the cable\n"); - msm_otg_reset(otg); - } - -skip_phy_resume: - if (device_may_wakeup(otg->dev)) - disable_irq_wake(motg->irq); - if (bus) - set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); - - if (motg->async_int) { - motg->async_int = 0; - pm_runtime_put(otg->dev); - enable_irq(motg->irq); - } - - atomic_set(&motg->in_lpm, 0); - - dev_info(otg->dev, "USB exited from low power mode\n"); - - return 0; -} -#endif - -static void msm_otg_start_host(struct otg_transceiver *otg, int on) -{ - struct msm_otg *motg = container_of(otg, struct msm_otg, otg); - struct msm_otg_platform_data *pdata = motg->pdata; - struct usb_hcd *hcd; - - if (!otg->host) - return; - - hcd = bus_to_hcd(otg->host); - - if (on) { - dev_dbg(otg->dev, "host on\n"); - - if (pdata->vbus_power) - pdata->vbus_power(1); - /* - * Some boards have a switch cotrolled by gpio - * to enable/disable internal HUB. Enable internal - * HUB before kicking the host. - */ - if (pdata->setup_gpio) - pdata->setup_gpio(OTG_STATE_A_HOST); -#ifdef CONFIG_USB - usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); -#endif - } else { - dev_dbg(otg->dev, "host off\n"); - -#ifdef CONFIG_USB - usb_remove_hcd(hcd); -#endif - if (pdata->setup_gpio) - pdata->setup_gpio(OTG_STATE_UNDEFINED); - if (pdata->vbus_power) - pdata->vbus_power(0); - } -} - -static int msm_otg_set_host(struct otg_transceiver *otg, struct usb_bus *host) -{ - struct msm_otg *motg = container_of(otg, struct msm_otg, otg); - struct usb_hcd *hcd; - - /* - * Fail host registration if this board can support - * only peripheral configuration. - */ - if (motg->pdata->mode == USB_PERIPHERAL) { - dev_info(otg->dev, "Host mode is not supported\n"); - return -ENODEV; - } - - if (!host) { - if (otg->state == OTG_STATE_A_HOST) { - pm_runtime_get_sync(otg->dev); - msm_otg_start_host(otg, 0); - otg->host = NULL; - otg->state = OTG_STATE_UNDEFINED; - schedule_work(&motg->sm_work); - } else { - otg->host = NULL; - } - - return 0; - } - - hcd = bus_to_hcd(host); - hcd->power_budget = motg->pdata->power_budget; - - otg->host = host; - dev_dbg(otg->dev, "host driver registered w/ tranceiver\n"); - - /* - * Kick the state machine work, if peripheral is not supported - * or peripheral is already registered with us. - */ - if (motg->pdata->mode == USB_HOST || otg->gadget) { - pm_runtime_get_sync(otg->dev); - schedule_work(&motg->sm_work); - } - - return 0; -} - -static void msm_otg_start_peripheral(struct otg_transceiver *otg, int on) -{ - struct msm_otg *motg = container_of(otg, struct msm_otg, otg); - struct msm_otg_platform_data *pdata = motg->pdata; - - if (!otg->gadget) - return; - - if (on) { - dev_dbg(otg->dev, "gadget on\n"); - /* - * Some boards have a switch cotrolled by gpio - * to enable/disable internal HUB. Disable internal - * HUB before kicking the gadget. - */ - if (pdata->setup_gpio) - pdata->setup_gpio(OTG_STATE_B_PERIPHERAL); - usb_gadget_vbus_connect(otg->gadget); - } else { - dev_dbg(otg->dev, "gadget off\n"); - usb_gadget_vbus_disconnect(otg->gadget); - if (pdata->setup_gpio) - pdata->setup_gpio(OTG_STATE_UNDEFINED); - } - -} - -static int msm_otg_set_peripheral(struct otg_transceiver *otg, - struct usb_gadget *gadget) -{ - struct msm_otg *motg = container_of(otg, struct msm_otg, otg); - - /* - * Fail peripheral registration if this board can support - * only host configuration. - */ - if (motg->pdata->mode == USB_HOST) { - dev_info(otg->dev, "Peripheral mode is not supported\n"); - return -ENODEV; - } - - if (!gadget) { - if (otg->state == OTG_STATE_B_PERIPHERAL) { - pm_runtime_get_sync(otg->dev); - msm_otg_start_peripheral(otg, 0); - otg->gadget = NULL; - otg->state = OTG_STATE_UNDEFINED; - schedule_work(&motg->sm_work); - } else { - otg->gadget = NULL; - } - - return 0; - } - otg->gadget = gadget; - dev_dbg(otg->dev, "peripheral driver registered w/ tranceiver\n"); - - /* - * Kick the state machine work, if host is not supported - * or host is already registered with us. - */ - if (motg->pdata->mode == USB_PERIPHERAL || otg->host) { - pm_runtime_get_sync(otg->dev); - schedule_work(&motg->sm_work); - } - - return 0; -} - -/* - * We support OTG, Peripheral only and Host only configurations. In case - * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen - * via Id pin status or user request (debugfs). Id/BSV interrupts are not - * enabled when switch is controlled by user and default mode is supplied - * by board file, which can be changed by userspace later. - */ -static void msm_otg_init_sm(struct msm_otg *motg) -{ - struct msm_otg_platform_data *pdata = motg->pdata; - u32 otgsc = readl(USB_OTGSC); - - switch (pdata->mode) { - case USB_OTG: - if (pdata->otg_control == OTG_PHY_CONTROL) { - if (otgsc & OTGSC_ID) - set_bit(ID, &motg->inputs); - else - clear_bit(ID, &motg->inputs); - - if (otgsc & OTGSC_BSV) - set_bit(B_SESS_VLD, &motg->inputs); - else - clear_bit(B_SESS_VLD, &motg->inputs); - } else if (pdata->otg_control == OTG_USER_CONTROL) { - if (pdata->default_mode == USB_HOST) { - clear_bit(ID, &motg->inputs); - } else if (pdata->default_mode == USB_PERIPHERAL) { - set_bit(ID, &motg->inputs); - set_bit(B_SESS_VLD, &motg->inputs); - } else { - set_bit(ID, &motg->inputs); - clear_bit(B_SESS_VLD, &motg->inputs); - } - } - break; - case USB_HOST: - clear_bit(ID, &motg->inputs); - break; - case USB_PERIPHERAL: - set_bit(ID, &motg->inputs); - if (otgsc & OTGSC_BSV) - set_bit(B_SESS_VLD, &motg->inputs); - else - clear_bit(B_SESS_VLD, &motg->inputs); - break; - default: - break; - } -} - -static void msm_otg_sm_work(struct work_struct *w) -{ - struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); - struct otg_transceiver *otg = &motg->otg; - - switch (otg->state) { - case OTG_STATE_UNDEFINED: - dev_dbg(otg->dev, "OTG_STATE_UNDEFINED state\n"); - msm_otg_reset(otg); - msm_otg_init_sm(motg); - otg->state = OTG_STATE_B_IDLE; - /* FALL THROUGH */ - case OTG_STATE_B_IDLE: - dev_dbg(otg->dev, "OTG_STATE_B_IDLE state\n"); - if (!test_bit(ID, &motg->inputs) && otg->host) { - /* disable BSV bit */ - writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC); - msm_otg_start_host(otg, 1); - otg->state = OTG_STATE_A_HOST; - } else if (test_bit(B_SESS_VLD, &motg->inputs) && otg->gadget) { - msm_otg_start_peripheral(otg, 1); - otg->state = OTG_STATE_B_PERIPHERAL; - } - pm_runtime_put_sync(otg->dev); - break; - case OTG_STATE_B_PERIPHERAL: - dev_dbg(otg->dev, "OTG_STATE_B_PERIPHERAL state\n"); - if (!test_bit(B_SESS_VLD, &motg->inputs) || - !test_bit(ID, &motg->inputs)) { - msm_otg_start_peripheral(otg, 0); - otg->state = OTG_STATE_B_IDLE; - msm_otg_reset(otg); - schedule_work(w); - } - break; - case OTG_STATE_A_HOST: - dev_dbg(otg->dev, "OTG_STATE_A_HOST state\n"); - if (test_bit(ID, &motg->inputs)) { - msm_otg_start_host(otg, 0); - otg->state = OTG_STATE_B_IDLE; - msm_otg_reset(otg); - schedule_work(w); - } - break; - default: - break; - } -} - -static irqreturn_t msm_otg_irq(int irq, void *data) -{ - struct msm_otg *motg = data; - struct otg_transceiver *otg = &motg->otg; - u32 otgsc = 0; - - if (atomic_read(&motg->in_lpm)) { - disable_irq_nosync(irq); - motg->async_int = 1; - pm_runtime_get(otg->dev); - return IRQ_HANDLED; - } - - otgsc = readl(USB_OTGSC); - if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS))) - return IRQ_NONE; - - if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) { - if (otgsc & OTGSC_ID) - set_bit(ID, &motg->inputs); - else - clear_bit(ID, &motg->inputs); - dev_dbg(otg->dev, "ID set/clear\n"); - pm_runtime_get_noresume(otg->dev); - } else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) { - if (otgsc & OTGSC_BSV) - set_bit(B_SESS_VLD, &motg->inputs); - else - clear_bit(B_SESS_VLD, &motg->inputs); - dev_dbg(otg->dev, "BSV set/clear\n"); - pm_runtime_get_noresume(otg->dev); - } - - writel(otgsc, USB_OTGSC); - schedule_work(&motg->sm_work); - return IRQ_HANDLED; -} - -static int msm_otg_mode_show(struct seq_file *s, void *unused) -{ - struct msm_otg *motg = s->private; - struct otg_transceiver *otg = &motg->otg; - - switch (otg->state) { - case OTG_STATE_A_HOST: - seq_printf(s, "host\n"); - break; - case OTG_STATE_B_PERIPHERAL: - seq_printf(s, "peripheral\n"); - break; - default: - seq_printf(s, "none\n"); - break; - } - - return 0; -} - -static int msm_otg_mode_open(struct inode *inode, struct file *file) -{ - return single_open(file, msm_otg_mode_show, inode->i_private); -} - -static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct seq_file *s = file->private_data; - struct msm_otg *motg = s->private; - char buf[16]; - struct otg_transceiver *otg = &motg->otg; - int status = count; - enum usb_mode_type req_mode; - - memset(buf, 0x00, sizeof(buf)); - - if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) { - status = -EFAULT; - goto out; - } - - if (!strncmp(buf, "host", 4)) { - req_mode = USB_HOST; - } else if (!strncmp(buf, "peripheral", 10)) { - req_mode = USB_PERIPHERAL; - } else if (!strncmp(buf, "none", 4)) { - req_mode = USB_NONE; - } else { - status = -EINVAL; - goto out; - } - - switch (req_mode) { - case USB_NONE: - switch (otg->state) { - case OTG_STATE_A_HOST: - case OTG_STATE_B_PERIPHERAL: - set_bit(ID, &motg->inputs); - clear_bit(B_SESS_VLD, &motg->inputs); - break; - default: - goto out; - } - break; - case USB_PERIPHERAL: - switch (otg->state) { - case OTG_STATE_B_IDLE: - case OTG_STATE_A_HOST: - set_bit(ID, &motg->inputs); - set_bit(B_SESS_VLD, &motg->inputs); - break; - default: - goto out; - } - break; - case USB_HOST: - switch (otg->state) { - case OTG_STATE_B_IDLE: - case OTG_STATE_B_PERIPHERAL: - clear_bit(ID, &motg->inputs); - break; - default: - goto out; - } - break; - default: - goto out; - } - - pm_runtime_get_sync(otg->dev); - schedule_work(&motg->sm_work); -out: - return status; -} - -const struct file_operations msm_otg_mode_fops = { - .open = msm_otg_mode_open, - .read = seq_read, - .write = msm_otg_mode_write, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct dentry *msm_otg_dbg_root; -static struct dentry *msm_otg_dbg_mode; - -static int msm_otg_debugfs_init(struct msm_otg *motg) -{ - msm_otg_dbg_root = debugfs_create_dir("msm_otg", NULL); - - if (!msm_otg_dbg_root || IS_ERR(msm_otg_dbg_root)) - return -ENODEV; - - msm_otg_dbg_mode = debugfs_create_file("mode", S_IRUGO | S_IWUSR, - msm_otg_dbg_root, motg, &msm_otg_mode_fops); - if (!msm_otg_dbg_mode) { - debugfs_remove(msm_otg_dbg_root); - msm_otg_dbg_root = NULL; - return -ENODEV; - } - - return 0; -} - -static void msm_otg_debugfs_cleanup(void) -{ - debugfs_remove(msm_otg_dbg_mode); - debugfs_remove(msm_otg_dbg_root); -} - -static int __init msm_otg_probe(struct platform_device *pdev) -{ - int ret = 0; - struct resource *res; - struct msm_otg *motg; - struct otg_transceiver *otg; - - dev_info(&pdev->dev, "msm_otg probe\n"); - if (!pdev->dev.platform_data) { - dev_err(&pdev->dev, "No platform data given. Bailing out\n"); - return -ENODEV; - } - - motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL); - if (!motg) { - dev_err(&pdev->dev, "unable to allocate msm_otg\n"); - return -ENOMEM; - } - - motg->pdata = pdev->dev.platform_data; - otg = &motg->otg; - otg->dev = &pdev->dev; - - motg->phy_reset_clk = clk_get(&pdev->dev, "usb_phy_clk"); - if (IS_ERR(motg->phy_reset_clk)) { - dev_err(&pdev->dev, "failed to get usb_phy_clk\n"); - ret = PTR_ERR(motg->phy_reset_clk); - goto free_motg; - } - - motg->clk = clk_get(&pdev->dev, "usb_hs_clk"); - if (IS_ERR(motg->clk)) { - dev_err(&pdev->dev, "failed to get usb_hs_clk\n"); - ret = PTR_ERR(motg->clk); - goto put_phy_reset_clk; - } - - motg->pclk = clk_get(&pdev->dev, "usb_hs_pclk"); - if (IS_ERR(motg->pclk)) { - dev_err(&pdev->dev, "failed to get usb_hs_pclk\n"); - ret = PTR_ERR(motg->pclk); - goto put_clk; - } - - /* - * USB core clock is not present on all MSM chips. This - * clock is introduced to remove the dependency on AXI - * bus frequency. - */ - motg->core_clk = clk_get(&pdev->dev, "usb_hs_core_clk"); - if (IS_ERR(motg->core_clk)) - motg->core_clk = NULL; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "failed to get platform resource mem\n"); - ret = -ENODEV; - goto put_core_clk; - } - - motg->regs = ioremap(res->start, resource_size(res)); - if (!motg->regs) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto put_core_clk; - } - dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs); - - motg->irq = platform_get_irq(pdev, 0); - if (!motg->irq) { - dev_err(&pdev->dev, "platform_get_irq failed\n"); - ret = -ENODEV; - goto free_regs; - } - - clk_enable(motg->clk); - clk_enable(motg->pclk); - if (motg->core_clk) - clk_enable(motg->core_clk); - - writel(0, USB_USBINTR); - writel(0, USB_OTGSC); - - INIT_WORK(&motg->sm_work, msm_otg_sm_work); - ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED, - "msm_otg", motg); - if (ret) { - dev_err(&pdev->dev, "request irq failed\n"); - goto disable_clks; - } - - otg->init = msm_otg_reset; - otg->set_host = msm_otg_set_host; - otg->set_peripheral = msm_otg_set_peripheral; - - otg->io_ops = &msm_otg_io_ops; - - ret = otg_set_transceiver(&motg->otg); - if (ret) { - dev_err(&pdev->dev, "otg_set_transceiver failed\n"); - goto free_irq; - } - - platform_set_drvdata(pdev, motg); - device_init_wakeup(&pdev->dev, 1); - - if (motg->pdata->mode == USB_OTG && - motg->pdata->otg_control == OTG_USER_CONTROL) { - ret = msm_otg_debugfs_init(motg); - if (ret) - dev_dbg(&pdev->dev, "mode debugfs file is" - "not available\n"); - } - - pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); - - return 0; -free_irq: - free_irq(motg->irq, motg); -disable_clks: - clk_disable(motg->pclk); - clk_disable(motg->clk); -free_regs: - iounmap(motg->regs); -put_core_clk: - if (motg->core_clk) - clk_put(motg->core_clk); - clk_put(motg->pclk); -put_clk: - clk_put(motg->clk); -put_phy_reset_clk: - clk_put(motg->phy_reset_clk); -free_motg: - kfree(motg); - return ret; -} - -static int __devexit msm_otg_remove(struct platform_device *pdev) -{ - struct msm_otg *motg = platform_get_drvdata(pdev); - struct otg_transceiver *otg = &motg->otg; - int cnt = 0; - - if (otg->host || otg->gadget) - return -EBUSY; - - msm_otg_debugfs_cleanup(); - cancel_work_sync(&motg->sm_work); - - pm_runtime_resume(&pdev->dev); - - device_init_wakeup(&pdev->dev, 0); - pm_runtime_disable(&pdev->dev); - - otg_set_transceiver(NULL); - free_irq(motg->irq, motg); - - /* - * Put PHY in low power mode. - */ - ulpi_read(otg, 0x14); - ulpi_write(otg, 0x08, 0x09); - - writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); - while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { - if (readl(USB_PORTSC) & PORTSC_PHCD) - break; - udelay(1); - cnt++; - } - if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) - dev_err(otg->dev, "Unable to suspend PHY\n"); - - clk_disable(motg->pclk); - clk_disable(motg->clk); - if (motg->core_clk) - clk_disable(motg->core_clk); - - iounmap(motg->regs); - pm_runtime_set_suspended(&pdev->dev); - - clk_put(motg->phy_reset_clk); - clk_put(motg->pclk); - clk_put(motg->clk); - if (motg->core_clk) - clk_put(motg->core_clk); - - kfree(motg); - - return 0; -} - -#ifdef CONFIG_PM_RUNTIME -static int msm_otg_runtime_idle(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - struct otg_transceiver *otg = &motg->otg; - - dev_dbg(dev, "OTG runtime idle\n"); - - /* - * It is observed some times that a spurious interrupt - * comes when PHY is put into LPM immediately after PHY reset. - * This 1 sec delay also prevents entering into LPM immediately - * after asynchronous interrupt. - */ - if (otg->state != OTG_STATE_UNDEFINED) - pm_schedule_suspend(dev, 1000); - - return -EAGAIN; -} - -static int msm_otg_runtime_suspend(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - - dev_dbg(dev, "OTG runtime suspend\n"); - return msm_otg_suspend(motg); -} - -static int msm_otg_runtime_resume(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - - dev_dbg(dev, "OTG runtime resume\n"); - return msm_otg_resume(motg); -} -#endif - -#ifdef CONFIG_PM_SLEEP -static int msm_otg_pm_suspend(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - - dev_dbg(dev, "OTG PM suspend\n"); - return msm_otg_suspend(motg); -} - -static int msm_otg_pm_resume(struct device *dev) -{ - struct msm_otg *motg = dev_get_drvdata(dev); - int ret; - - dev_dbg(dev, "OTG PM resume\n"); - - ret = msm_otg_resume(motg); - if (ret) - return ret; - - /* - * Runtime PM Documentation recommends bringing the - * device to full powered state upon resume. - */ - pm_runtime_disable(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - - return 0; -} -#endif - -#ifdef CONFIG_PM -static const struct dev_pm_ops msm_otg_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(msm_otg_pm_suspend, msm_otg_pm_resume) - SET_RUNTIME_PM_OPS(msm_otg_runtime_suspend, msm_otg_runtime_resume, - msm_otg_runtime_idle) -}; -#endif - -static struct platform_driver msm_otg_driver = { - .remove = __devexit_p(msm_otg_remove), - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, -#ifdef CONFIG_PM - .pm = &msm_otg_dev_pm_ops, -#endif - }, -}; - -static int __init msm_otg_init(void) -{ - return platform_driver_probe(&msm_otg_driver, msm_otg_probe); -} - -static void __exit msm_otg_exit(void) -{ - platform_driver_unregister(&msm_otg_driver); -} - -module_init(msm_otg_init); -module_exit(msm_otg_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("MSM USB transceiver driver"); diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c new file mode 100644 index 000000000000..296598628b85 --- /dev/null +++ b/drivers/usb/otg/msm_otg.c @@ -0,0 +1,1124 @@ +/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MSM_USB_BASE (motg->regs) +#define DRIVER_NAME "msm_otg" + +#define ULPI_IO_TIMEOUT_USEC (10 * 1000) +static int ulpi_read(struct otg_transceiver *otg, u32 reg) +{ + struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + int cnt = 0; + + /* initiate read operation */ + writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), + USB_ULPI_VIEWPORT); + + /* wait for completion */ + while (cnt < ULPI_IO_TIMEOUT_USEC) { + if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) + break; + udelay(1); + cnt++; + } + + if (cnt >= ULPI_IO_TIMEOUT_USEC) { + dev_err(otg->dev, "ulpi_read: timeout %08x\n", + readl(USB_ULPI_VIEWPORT)); + return -ETIMEDOUT; + } + return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); +} + +static int ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg) +{ + struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + int cnt = 0; + + /* initiate write operation */ + writel(ULPI_RUN | ULPI_WRITE | + ULPI_ADDR(reg) | ULPI_DATA(val), + USB_ULPI_VIEWPORT); + + /* wait for completion */ + while (cnt < ULPI_IO_TIMEOUT_USEC) { + if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) + break; + udelay(1); + cnt++; + } + + if (cnt >= ULPI_IO_TIMEOUT_USEC) { + dev_err(otg->dev, "ulpi_write: timeout\n"); + return -ETIMEDOUT; + } + return 0; +} + +static struct otg_io_access_ops msm_otg_io_ops = { + .read = ulpi_read, + .write = ulpi_write, +}; + +static void ulpi_init(struct msm_otg *motg) +{ + struct msm_otg_platform_data *pdata = motg->pdata; + int *seq = pdata->phy_init_seq; + + if (!seq) + return; + + while (seq[0] >= 0) { + dev_vdbg(motg->otg.dev, "ulpi: write 0x%02x to 0x%02x\n", + seq[0], seq[1]); + ulpi_write(&motg->otg, seq[0], seq[1]); + seq += 2; + } +} + +static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) +{ + int ret; + + if (assert) { + ret = clk_reset(motg->clk, CLK_RESET_ASSERT); + if (ret) + dev_err(motg->otg.dev, "usb hs_clk assert failed\n"); + } else { + ret = clk_reset(motg->clk, CLK_RESET_DEASSERT); + if (ret) + dev_err(motg->otg.dev, "usb hs_clk deassert failed\n"); + } + return ret; +} + +static int msm_otg_phy_clk_reset(struct msm_otg *motg) +{ + int ret; + + ret = clk_reset(motg->phy_reset_clk, CLK_RESET_ASSERT); + if (ret) { + dev_err(motg->otg.dev, "usb phy clk assert failed\n"); + return ret; + } + usleep_range(10000, 12000); + ret = clk_reset(motg->phy_reset_clk, CLK_RESET_DEASSERT); + if (ret) + dev_err(motg->otg.dev, "usb phy clk deassert failed\n"); + return ret; +} + +static int msm_otg_phy_reset(struct msm_otg *motg) +{ + u32 val; + int ret; + int retries; + + ret = msm_otg_link_clk_reset(motg, 1); + if (ret) + return ret; + ret = msm_otg_phy_clk_reset(motg); + if (ret) + return ret; + ret = msm_otg_link_clk_reset(motg, 0); + if (ret) + return ret; + + val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK; + writel(val | PORTSC_PTS_ULPI, USB_PORTSC); + + for (retries = 3; retries > 0; retries--) { + ret = ulpi_write(&motg->otg, ULPI_FUNC_CTRL_SUSPENDM, + ULPI_CLR(ULPI_FUNC_CTRL)); + if (!ret) + break; + ret = msm_otg_phy_clk_reset(motg); + if (ret) + return ret; + } + if (!retries) + return -ETIMEDOUT; + + /* This reset calibrates the phy, if the above write succeeded */ + ret = msm_otg_phy_clk_reset(motg); + if (ret) + return ret; + + for (retries = 3; retries > 0; retries--) { + ret = ulpi_read(&motg->otg, ULPI_DEBUG); + if (ret != -ETIMEDOUT) + break; + ret = msm_otg_phy_clk_reset(motg); + if (ret) + return ret; + } + if (!retries) + return -ETIMEDOUT; + + dev_info(motg->otg.dev, "phy_reset: success\n"); + return 0; +} + +#define LINK_RESET_TIMEOUT_USEC (250 * 1000) +static int msm_otg_reset(struct otg_transceiver *otg) +{ + struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + struct msm_otg_platform_data *pdata = motg->pdata; + int cnt = 0; + int ret; + u32 val = 0; + u32 ulpi_val = 0; + + ret = msm_otg_phy_reset(motg); + if (ret) { + dev_err(otg->dev, "phy_reset failed\n"); + return ret; + } + + ulpi_init(motg); + + writel(USBCMD_RESET, USB_USBCMD); + while (cnt < LINK_RESET_TIMEOUT_USEC) { + if (!(readl(USB_USBCMD) & USBCMD_RESET)) + break; + udelay(1); + cnt++; + } + if (cnt >= LINK_RESET_TIMEOUT_USEC) + return -ETIMEDOUT; + + /* select ULPI phy */ + writel(0x80000000, USB_PORTSC); + + msleep(100); + + writel(0x0, USB_AHBBURST); + writel(0x00, USB_AHBMODE); + + if (pdata->otg_control == OTG_PHY_CONTROL) { + val = readl(USB_OTGSC); + if (pdata->mode == USB_OTG) { + ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID; + val |= OTGSC_IDIE | OTGSC_BSVIE; + } else if (pdata->mode == USB_PERIPHERAL) { + ulpi_val = ULPI_INT_SESS_VALID; + val |= OTGSC_BSVIE; + } + writel(val, USB_OTGSC); + ulpi_write(otg, ulpi_val, ULPI_USB_INT_EN_RISE); + ulpi_write(otg, ulpi_val, ULPI_USB_INT_EN_FALL); + } + + return 0; +} + +#define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000) +#define PHY_RESUME_TIMEOUT_USEC (100 * 1000) + +#ifdef CONFIG_PM_SLEEP +static int msm_otg_suspend(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + struct usb_bus *bus = otg->host; + struct msm_otg_platform_data *pdata = motg->pdata; + int cnt = 0; + + if (atomic_read(&motg->in_lpm)) + return 0; + + disable_irq(motg->irq); + /* + * Interrupt Latch Register auto-clear feature is not present + * in all PHY versions. Latch register is clear on read type. + * Clear latch register to avoid spurious wakeup from + * low power mode (LPM). + */ + ulpi_read(otg, 0x14); + + /* + * PHY comparators are disabled when PHY enters into low power + * mode (LPM). Keep PHY comparators ON in LPM only when we expect + * VBUS/Id notifications from USB PHY. Otherwise turn off USB + * PHY comparators. This save significant amount of power. + */ + if (pdata->otg_control == OTG_PHY_CONTROL) + ulpi_write(otg, 0x01, 0x30); + + /* + * PLL is not turned off when PHY enters into low power mode (LPM). + * Disable PLL for maximum power savings. + */ + ulpi_write(otg, 0x08, 0x09); + + /* + * PHY may take some time or even fail to enter into low power + * mode (LPM). Hence poll for 500 msec and reset the PHY and link + * in failure case. + */ + writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); + while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { + if (readl(USB_PORTSC) & PORTSC_PHCD) + break; + udelay(1); + cnt++; + } + + if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) { + dev_err(otg->dev, "Unable to suspend PHY\n"); + msm_otg_reset(otg); + enable_irq(motg->irq); + return -ETIMEDOUT; + } + + /* + * PHY has capability to generate interrupt asynchronously in low + * power mode (LPM). This interrupt is level triggered. So USB IRQ + * line must be disabled till async interrupt enable bit is cleared + * in USBCMD register. Assert STP (ULPI interface STOP signal) to + * block data communication from PHY. + */ + writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); + + clk_disable(motg->pclk); + clk_disable(motg->clk); + if (motg->core_clk) + clk_disable(motg->core_clk); + + if (device_may_wakeup(otg->dev)) + enable_irq_wake(motg->irq); + if (bus) + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); + + atomic_set(&motg->in_lpm, 1); + enable_irq(motg->irq); + + dev_info(otg->dev, "USB in low power mode\n"); + + return 0; +} + +static int msm_otg_resume(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + struct usb_bus *bus = otg->host; + int cnt = 0; + unsigned temp; + + if (!atomic_read(&motg->in_lpm)) + return 0; + + clk_enable(motg->pclk); + clk_enable(motg->clk); + if (motg->core_clk) + clk_enable(motg->core_clk); + + temp = readl(USB_USBCMD); + temp &= ~ASYNC_INTR_CTRL; + temp &= ~ULPI_STP_CTRL; + writel(temp, USB_USBCMD); + + /* + * PHY comes out of low power mode (LPM) in case of wakeup + * from asynchronous interrupt. + */ + if (!(readl(USB_PORTSC) & PORTSC_PHCD)) + goto skip_phy_resume; + + writel(readl(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC); + while (cnt < PHY_RESUME_TIMEOUT_USEC) { + if (!(readl(USB_PORTSC) & PORTSC_PHCD)) + break; + udelay(1); + cnt++; + } + + if (cnt >= PHY_RESUME_TIMEOUT_USEC) { + /* + * This is a fatal error. Reset the link and + * PHY. USB state can not be restored. Re-insertion + * of USB cable is the only way to get USB working. + */ + dev_err(otg->dev, "Unable to resume USB." + "Re-plugin the cable\n"); + msm_otg_reset(otg); + } + +skip_phy_resume: + if (device_may_wakeup(otg->dev)) + disable_irq_wake(motg->irq); + if (bus) + set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); + + if (motg->async_int) { + motg->async_int = 0; + pm_runtime_put(otg->dev); + enable_irq(motg->irq); + } + + atomic_set(&motg->in_lpm, 0); + + dev_info(otg->dev, "USB exited from low power mode\n"); + + return 0; +} +#endif + +static void msm_otg_start_host(struct otg_transceiver *otg, int on) +{ + struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + struct msm_otg_platform_data *pdata = motg->pdata; + struct usb_hcd *hcd; + + if (!otg->host) + return; + + hcd = bus_to_hcd(otg->host); + + if (on) { + dev_dbg(otg->dev, "host on\n"); + + if (pdata->vbus_power) + pdata->vbus_power(1); + /* + * Some boards have a switch cotrolled by gpio + * to enable/disable internal HUB. Enable internal + * HUB before kicking the host. + */ + if (pdata->setup_gpio) + pdata->setup_gpio(OTG_STATE_A_HOST); +#ifdef CONFIG_USB + usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); +#endif + } else { + dev_dbg(otg->dev, "host off\n"); + +#ifdef CONFIG_USB + usb_remove_hcd(hcd); +#endif + if (pdata->setup_gpio) + pdata->setup_gpio(OTG_STATE_UNDEFINED); + if (pdata->vbus_power) + pdata->vbus_power(0); + } +} + +static int msm_otg_set_host(struct otg_transceiver *otg, struct usb_bus *host) +{ + struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + struct usb_hcd *hcd; + + /* + * Fail host registration if this board can support + * only peripheral configuration. + */ + if (motg->pdata->mode == USB_PERIPHERAL) { + dev_info(otg->dev, "Host mode is not supported\n"); + return -ENODEV; + } + + if (!host) { + if (otg->state == OTG_STATE_A_HOST) { + pm_runtime_get_sync(otg->dev); + msm_otg_start_host(otg, 0); + otg->host = NULL; + otg->state = OTG_STATE_UNDEFINED; + schedule_work(&motg->sm_work); + } else { + otg->host = NULL; + } + + return 0; + } + + hcd = bus_to_hcd(host); + hcd->power_budget = motg->pdata->power_budget; + + otg->host = host; + dev_dbg(otg->dev, "host driver registered w/ tranceiver\n"); + + /* + * Kick the state machine work, if peripheral is not supported + * or peripheral is already registered with us. + */ + if (motg->pdata->mode == USB_HOST || otg->gadget) { + pm_runtime_get_sync(otg->dev); + schedule_work(&motg->sm_work); + } + + return 0; +} + +static void msm_otg_start_peripheral(struct otg_transceiver *otg, int on) +{ + struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + struct msm_otg_platform_data *pdata = motg->pdata; + + if (!otg->gadget) + return; + + if (on) { + dev_dbg(otg->dev, "gadget on\n"); + /* + * Some boards have a switch cotrolled by gpio + * to enable/disable internal HUB. Disable internal + * HUB before kicking the gadget. + */ + if (pdata->setup_gpio) + pdata->setup_gpio(OTG_STATE_B_PERIPHERAL); + usb_gadget_vbus_connect(otg->gadget); + } else { + dev_dbg(otg->dev, "gadget off\n"); + usb_gadget_vbus_disconnect(otg->gadget); + if (pdata->setup_gpio) + pdata->setup_gpio(OTG_STATE_UNDEFINED); + } + +} + +static int msm_otg_set_peripheral(struct otg_transceiver *otg, + struct usb_gadget *gadget) +{ + struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + + /* + * Fail peripheral registration if this board can support + * only host configuration. + */ + if (motg->pdata->mode == USB_HOST) { + dev_info(otg->dev, "Peripheral mode is not supported\n"); + return -ENODEV; + } + + if (!gadget) { + if (otg->state == OTG_STATE_B_PERIPHERAL) { + pm_runtime_get_sync(otg->dev); + msm_otg_start_peripheral(otg, 0); + otg->gadget = NULL; + otg->state = OTG_STATE_UNDEFINED; + schedule_work(&motg->sm_work); + } else { + otg->gadget = NULL; + } + + return 0; + } + otg->gadget = gadget; + dev_dbg(otg->dev, "peripheral driver registered w/ tranceiver\n"); + + /* + * Kick the state machine work, if host is not supported + * or host is already registered with us. + */ + if (motg->pdata->mode == USB_PERIPHERAL || otg->host) { + pm_runtime_get_sync(otg->dev); + schedule_work(&motg->sm_work); + } + + return 0; +} + +/* + * We support OTG, Peripheral only and Host only configurations. In case + * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen + * via Id pin status or user request (debugfs). Id/BSV interrupts are not + * enabled when switch is controlled by user and default mode is supplied + * by board file, which can be changed by userspace later. + */ +static void msm_otg_init_sm(struct msm_otg *motg) +{ + struct msm_otg_platform_data *pdata = motg->pdata; + u32 otgsc = readl(USB_OTGSC); + + switch (pdata->mode) { + case USB_OTG: + if (pdata->otg_control == OTG_PHY_CONTROL) { + if (otgsc & OTGSC_ID) + set_bit(ID, &motg->inputs); + else + clear_bit(ID, &motg->inputs); + + if (otgsc & OTGSC_BSV) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + } else if (pdata->otg_control == OTG_USER_CONTROL) { + if (pdata->default_mode == USB_HOST) { + clear_bit(ID, &motg->inputs); + } else if (pdata->default_mode == USB_PERIPHERAL) { + set_bit(ID, &motg->inputs); + set_bit(B_SESS_VLD, &motg->inputs); + } else { + set_bit(ID, &motg->inputs); + clear_bit(B_SESS_VLD, &motg->inputs); + } + } + break; + case USB_HOST: + clear_bit(ID, &motg->inputs); + break; + case USB_PERIPHERAL: + set_bit(ID, &motg->inputs); + if (otgsc & OTGSC_BSV) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + break; + default: + break; + } +} + +static void msm_otg_sm_work(struct work_struct *w) +{ + struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); + struct otg_transceiver *otg = &motg->otg; + + switch (otg->state) { + case OTG_STATE_UNDEFINED: + dev_dbg(otg->dev, "OTG_STATE_UNDEFINED state\n"); + msm_otg_reset(otg); + msm_otg_init_sm(motg); + otg->state = OTG_STATE_B_IDLE; + /* FALL THROUGH */ + case OTG_STATE_B_IDLE: + dev_dbg(otg->dev, "OTG_STATE_B_IDLE state\n"); + if (!test_bit(ID, &motg->inputs) && otg->host) { + /* disable BSV bit */ + writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC); + msm_otg_start_host(otg, 1); + otg->state = OTG_STATE_A_HOST; + } else if (test_bit(B_SESS_VLD, &motg->inputs) && otg->gadget) { + msm_otg_start_peripheral(otg, 1); + otg->state = OTG_STATE_B_PERIPHERAL; + } + pm_runtime_put_sync(otg->dev); + break; + case OTG_STATE_B_PERIPHERAL: + dev_dbg(otg->dev, "OTG_STATE_B_PERIPHERAL state\n"); + if (!test_bit(B_SESS_VLD, &motg->inputs) || + !test_bit(ID, &motg->inputs)) { + msm_otg_start_peripheral(otg, 0); + otg->state = OTG_STATE_B_IDLE; + msm_otg_reset(otg); + schedule_work(w); + } + break; + case OTG_STATE_A_HOST: + dev_dbg(otg->dev, "OTG_STATE_A_HOST state\n"); + if (test_bit(ID, &motg->inputs)) { + msm_otg_start_host(otg, 0); + otg->state = OTG_STATE_B_IDLE; + msm_otg_reset(otg); + schedule_work(w); + } + break; + default: + break; + } +} + +static irqreturn_t msm_otg_irq(int irq, void *data) +{ + struct msm_otg *motg = data; + struct otg_transceiver *otg = &motg->otg; + u32 otgsc = 0; + + if (atomic_read(&motg->in_lpm)) { + disable_irq_nosync(irq); + motg->async_int = 1; + pm_runtime_get(otg->dev); + return IRQ_HANDLED; + } + + otgsc = readl(USB_OTGSC); + if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS))) + return IRQ_NONE; + + if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) { + if (otgsc & OTGSC_ID) + set_bit(ID, &motg->inputs); + else + clear_bit(ID, &motg->inputs); + dev_dbg(otg->dev, "ID set/clear\n"); + pm_runtime_get_noresume(otg->dev); + } else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) { + if (otgsc & OTGSC_BSV) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + dev_dbg(otg->dev, "BSV set/clear\n"); + pm_runtime_get_noresume(otg->dev); + } + + writel(otgsc, USB_OTGSC); + schedule_work(&motg->sm_work); + return IRQ_HANDLED; +} + +static int msm_otg_mode_show(struct seq_file *s, void *unused) +{ + struct msm_otg *motg = s->private; + struct otg_transceiver *otg = &motg->otg; + + switch (otg->state) { + case OTG_STATE_A_HOST: + seq_printf(s, "host\n"); + break; + case OTG_STATE_B_PERIPHERAL: + seq_printf(s, "peripheral\n"); + break; + default: + seq_printf(s, "none\n"); + break; + } + + return 0; +} + +static int msm_otg_mode_open(struct inode *inode, struct file *file) +{ + return single_open(file, msm_otg_mode_show, inode->i_private); +} + +static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct msm_otg *motg = s->private; + char buf[16]; + struct otg_transceiver *otg = &motg->otg; + int status = count; + enum usb_mode_type req_mode; + + memset(buf, 0x00, sizeof(buf)); + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) { + status = -EFAULT; + goto out; + } + + if (!strncmp(buf, "host", 4)) { + req_mode = USB_HOST; + } else if (!strncmp(buf, "peripheral", 10)) { + req_mode = USB_PERIPHERAL; + } else if (!strncmp(buf, "none", 4)) { + req_mode = USB_NONE; + } else { + status = -EINVAL; + goto out; + } + + switch (req_mode) { + case USB_NONE: + switch (otg->state) { + case OTG_STATE_A_HOST: + case OTG_STATE_B_PERIPHERAL: + set_bit(ID, &motg->inputs); + clear_bit(B_SESS_VLD, &motg->inputs); + break; + default: + goto out; + } + break; + case USB_PERIPHERAL: + switch (otg->state) { + case OTG_STATE_B_IDLE: + case OTG_STATE_A_HOST: + set_bit(ID, &motg->inputs); + set_bit(B_SESS_VLD, &motg->inputs); + break; + default: + goto out; + } + break; + case USB_HOST: + switch (otg->state) { + case OTG_STATE_B_IDLE: + case OTG_STATE_B_PERIPHERAL: + clear_bit(ID, &motg->inputs); + break; + default: + goto out; + } + break; + default: + goto out; + } + + pm_runtime_get_sync(otg->dev); + schedule_work(&motg->sm_work); +out: + return status; +} + +const struct file_operations msm_otg_mode_fops = { + .open = msm_otg_mode_open, + .read = seq_read, + .write = msm_otg_mode_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct dentry *msm_otg_dbg_root; +static struct dentry *msm_otg_dbg_mode; + +static int msm_otg_debugfs_init(struct msm_otg *motg) +{ + msm_otg_dbg_root = debugfs_create_dir("msm_otg", NULL); + + if (!msm_otg_dbg_root || IS_ERR(msm_otg_dbg_root)) + return -ENODEV; + + msm_otg_dbg_mode = debugfs_create_file("mode", S_IRUGO | S_IWUSR, + msm_otg_dbg_root, motg, &msm_otg_mode_fops); + if (!msm_otg_dbg_mode) { + debugfs_remove(msm_otg_dbg_root); + msm_otg_dbg_root = NULL; + return -ENODEV; + } + + return 0; +} + +static void msm_otg_debugfs_cleanup(void) +{ + debugfs_remove(msm_otg_dbg_mode); + debugfs_remove(msm_otg_dbg_root); +} + +static int __init msm_otg_probe(struct platform_device *pdev) +{ + int ret = 0; + struct resource *res; + struct msm_otg *motg; + struct otg_transceiver *otg; + + dev_info(&pdev->dev, "msm_otg probe\n"); + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "No platform data given. Bailing out\n"); + return -ENODEV; + } + + motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL); + if (!motg) { + dev_err(&pdev->dev, "unable to allocate msm_otg\n"); + return -ENOMEM; + } + + motg->pdata = pdev->dev.platform_data; + otg = &motg->otg; + otg->dev = &pdev->dev; + + motg->phy_reset_clk = clk_get(&pdev->dev, "usb_phy_clk"); + if (IS_ERR(motg->phy_reset_clk)) { + dev_err(&pdev->dev, "failed to get usb_phy_clk\n"); + ret = PTR_ERR(motg->phy_reset_clk); + goto free_motg; + } + + motg->clk = clk_get(&pdev->dev, "usb_hs_clk"); + if (IS_ERR(motg->clk)) { + dev_err(&pdev->dev, "failed to get usb_hs_clk\n"); + ret = PTR_ERR(motg->clk); + goto put_phy_reset_clk; + } + + motg->pclk = clk_get(&pdev->dev, "usb_hs_pclk"); + if (IS_ERR(motg->pclk)) { + dev_err(&pdev->dev, "failed to get usb_hs_pclk\n"); + ret = PTR_ERR(motg->pclk); + goto put_clk; + } + + /* + * USB core clock is not present on all MSM chips. This + * clock is introduced to remove the dependency on AXI + * bus frequency. + */ + motg->core_clk = clk_get(&pdev->dev, "usb_hs_core_clk"); + if (IS_ERR(motg->core_clk)) + motg->core_clk = NULL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get platform resource mem\n"); + ret = -ENODEV; + goto put_core_clk; + } + + motg->regs = ioremap(res->start, resource_size(res)); + if (!motg->regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto put_core_clk; + } + dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs); + + motg->irq = platform_get_irq(pdev, 0); + if (!motg->irq) { + dev_err(&pdev->dev, "platform_get_irq failed\n"); + ret = -ENODEV; + goto free_regs; + } + + clk_enable(motg->clk); + clk_enable(motg->pclk); + if (motg->core_clk) + clk_enable(motg->core_clk); + + writel(0, USB_USBINTR); + writel(0, USB_OTGSC); + + INIT_WORK(&motg->sm_work, msm_otg_sm_work); + ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED, + "msm_otg", motg); + if (ret) { + dev_err(&pdev->dev, "request irq failed\n"); + goto disable_clks; + } + + otg->init = msm_otg_reset; + otg->set_host = msm_otg_set_host; + otg->set_peripheral = msm_otg_set_peripheral; + + otg->io_ops = &msm_otg_io_ops; + + ret = otg_set_transceiver(&motg->otg); + if (ret) { + dev_err(&pdev->dev, "otg_set_transceiver failed\n"); + goto free_irq; + } + + platform_set_drvdata(pdev, motg); + device_init_wakeup(&pdev->dev, 1); + + if (motg->pdata->mode == USB_OTG && + motg->pdata->otg_control == OTG_USER_CONTROL) { + ret = msm_otg_debugfs_init(motg); + if (ret) + dev_dbg(&pdev->dev, "mode debugfs file is" + "not available\n"); + } + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + return 0; +free_irq: + free_irq(motg->irq, motg); +disable_clks: + clk_disable(motg->pclk); + clk_disable(motg->clk); +free_regs: + iounmap(motg->regs); +put_core_clk: + if (motg->core_clk) + clk_put(motg->core_clk); + clk_put(motg->pclk); +put_clk: + clk_put(motg->clk); +put_phy_reset_clk: + clk_put(motg->phy_reset_clk); +free_motg: + kfree(motg); + return ret; +} + +static int __devexit msm_otg_remove(struct platform_device *pdev) +{ + struct msm_otg *motg = platform_get_drvdata(pdev); + struct otg_transceiver *otg = &motg->otg; + int cnt = 0; + + if (otg->host || otg->gadget) + return -EBUSY; + + msm_otg_debugfs_cleanup(); + cancel_work_sync(&motg->sm_work); + + pm_runtime_resume(&pdev->dev); + + device_init_wakeup(&pdev->dev, 0); + pm_runtime_disable(&pdev->dev); + + otg_set_transceiver(NULL); + free_irq(motg->irq, motg); + + /* + * Put PHY in low power mode. + */ + ulpi_read(otg, 0x14); + ulpi_write(otg, 0x08, 0x09); + + writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); + while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { + if (readl(USB_PORTSC) & PORTSC_PHCD) + break; + udelay(1); + cnt++; + } + if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) + dev_err(otg->dev, "Unable to suspend PHY\n"); + + clk_disable(motg->pclk); + clk_disable(motg->clk); + if (motg->core_clk) + clk_disable(motg->core_clk); + + iounmap(motg->regs); + pm_runtime_set_suspended(&pdev->dev); + + clk_put(motg->phy_reset_clk); + clk_put(motg->pclk); + clk_put(motg->clk); + if (motg->core_clk) + clk_put(motg->core_clk); + + kfree(motg); + + return 0; +} + +#ifdef CONFIG_PM_RUNTIME +static int msm_otg_runtime_idle(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + struct otg_transceiver *otg = &motg->otg; + + dev_dbg(dev, "OTG runtime idle\n"); + + /* + * It is observed some times that a spurious interrupt + * comes when PHY is put into LPM immediately after PHY reset. + * This 1 sec delay also prevents entering into LPM immediately + * after asynchronous interrupt. + */ + if (otg->state != OTG_STATE_UNDEFINED) + pm_schedule_suspend(dev, 1000); + + return -EAGAIN; +} + +static int msm_otg_runtime_suspend(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + + dev_dbg(dev, "OTG runtime suspend\n"); + return msm_otg_suspend(motg); +} + +static int msm_otg_runtime_resume(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + + dev_dbg(dev, "OTG runtime resume\n"); + return msm_otg_resume(motg); +} +#endif + +#ifdef CONFIG_PM_SLEEP +static int msm_otg_pm_suspend(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + + dev_dbg(dev, "OTG PM suspend\n"); + return msm_otg_suspend(motg); +} + +static int msm_otg_pm_resume(struct device *dev) +{ + struct msm_otg *motg = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "OTG PM resume\n"); + + ret = msm_otg_resume(motg); + if (ret) + return ret; + + /* + * Runtime PM Documentation recommends bringing the + * device to full powered state upon resume. + */ + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; +} +#endif + +#ifdef CONFIG_PM +static const struct dev_pm_ops msm_otg_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(msm_otg_pm_suspend, msm_otg_pm_resume) + SET_RUNTIME_PM_OPS(msm_otg_runtime_suspend, msm_otg_runtime_resume, + msm_otg_runtime_idle) +}; +#endif + +static struct platform_driver msm_otg_driver = { + .remove = __devexit_p(msm_otg_remove), + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &msm_otg_dev_pm_ops, +#endif + }, +}; + +static int __init msm_otg_init(void) +{ + return platform_driver_probe(&msm_otg_driver, msm_otg_probe); +} + +static void __exit msm_otg_exit(void) +{ + platform_driver_unregister(&msm_otg_driver); +} + +module_init(msm_otg_init); +module_exit(msm_otg_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM USB transceiver driver"); diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 3675e03b1539..3657403eac18 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -55,7 +55,7 @@ enum otg_control_type { /** * struct msm_otg_platform_data - platform device data - * for msm72k_otg driver. + * for msm_otg driver. * @phy_init_seq: PHY configuration sequence. val, reg pairs * terminated by -1. * @vbus_power: VBUS power on/off routine. -- cgit v1.2.3 From 256ee435b9a9ee9cca69602fe8046b27ca99fbee Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 1 Mar 2011 07:06:12 +0000 Subject: netdevice: Convert printk to pr_info in netif_tx_stop_queue This allows any caller to be prefaced by any specific pr_fmt to better identify which device driver is using this function inappropriately. Add terminating newline. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 8be4056ad251..71563e7b2bfb 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1765,8 +1765,7 @@ static inline void netif_tx_wake_all_queues(struct net_device *dev) static inline void netif_tx_stop_queue(struct netdev_queue *dev_queue) { if (WARN_ON(!dev_queue)) { - printk(KERN_INFO "netif_stop_queue() cannot be called before " - "register_netdev()"); + pr_info("netif_stop_queue() cannot be called before register_netdev()\n"); return; } set_bit(__QUEUE_STATE_XOFF, &dev_queue->state); -- cgit v1.2.3 From f1a304e7941cc76353363a139cbb6a4b1ca7c737 Mon Sep 17 00:00:00 2001 From: Pratheesh Gangadhar Date: Sat, 5 Mar 2011 04:30:17 +0530 Subject: UIO: add PRUSS UIO driver support This patch implements PRUSS (Programmable Real-time Unit Sub System) UIO driver which exports SOC resources associated with PRUSS like I/O, memories and IRQs to user space. PRUSS is dual 32-bit RISC processors which is efficient in performing embedded tasks that require manipulation of packed memory mapped data structures and handling system events that have tight real time constraints. This driver is currently supported on Texas Instruments DA850, AM18xx and OMAP-L138 devices. For example, PRUSS runs firmware for real-time critical industrial communication data link layer and communicates with application stack running in user space via shared memory and IRQs. Signed-off-by: Pratheesh Gangadhar Reviewed-by: Thomas Gleixner Reviewed-by: Arnd Bergmann Signed-off-by: Hans J. Koch Signed-off-by: Greg Kroah-Hartman --- drivers/uio/Kconfig | 17 +++ drivers/uio/Makefile | 1 + drivers/uio/uio_pruss.c | 247 ++++++++++++++++++++++++++++++++ include/linux/platform_data/uio_pruss.h | 25 ++++ 4 files changed, 290 insertions(+) create mode 100644 drivers/uio/uio_pruss.c create mode 100644 include/linux/platform_data/uio_pruss.h (limited to 'include') diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig index bb440792a1b7..6f3ea9bbc818 100644 --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig @@ -94,4 +94,21 @@ config UIO_NETX To compile this driver as a module, choose M here; the module will be called uio_netx. +config UIO_PRUSS + tristate "Texas Instruments PRUSS driver" + depends on ARCH_DAVINCI_DA850 + help + PRUSS driver for OMAPL138/DA850/AM18XX devices + PRUSS driver requires user space components, examples and user space + driver is available from below SVN repo - you may use anonymous login + + https://gforge.ti.com/gf/project/pru_sw/ + + More info on API is available at below wiki + + http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader + + To compile this driver as a module, choose M here: the module + will be called uio_pruss. + endif diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile index 18fd818c5b97..d4dd9a5552f8 100644 --- a/drivers/uio/Makefile +++ b/drivers/uio/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_UIO_AEC) += uio_aec.o obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o obj-$(CONFIG_UIO_NETX) += uio_netx.o +obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o diff --git a/drivers/uio/uio_pruss.c b/drivers/uio/uio_pruss.c new file mode 100644 index 000000000000..daf6e77de2b1 --- /dev/null +++ b/drivers/uio/uio_pruss.c @@ -0,0 +1,247 @@ +/* + * Programmable Real-Time Unit Sub System (PRUSS) UIO driver (uio_pruss) + * + * This driver exports PRUSS host event out interrupts and PRUSS, L3 RAM, + * and DDR RAM to user space for applications interacting with PRUSS firmware + * + * Copyright (C) 2010-11 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "pruss_uio" +#define DRV_VERSION "1.0" + +static int sram_pool_sz = SZ_16K; +module_param(sram_pool_sz, int, 0); +MODULE_PARM_DESC(sram_pool_sz, "sram pool size to allocate "); + +static int extram_pool_sz = SZ_256K; +module_param(extram_pool_sz, int, 0); +MODULE_PARM_DESC(extram_pool_sz, "external ram pool size to allocate"); + +/* + * Host event IRQ numbers from PRUSS - PRUSS can generate upto 8 interrupt + * events to AINTC of ARM host processor - which can be used for IPC b/w PRUSS + * firmware and user space application, async notification from PRU firmware + * to user space application + * 3 PRU_EVTOUT0 + * 4 PRU_EVTOUT1 + * 5 PRU_EVTOUT2 + * 6 PRU_EVTOUT3 + * 7 PRU_EVTOUT4 + * 8 PRU_EVTOUT5 + * 9 PRU_EVTOUT6 + * 10 PRU_EVTOUT7 +*/ +#define MAX_PRUSS_EVT 8 + +#define PINTC_HIDISR 0x0038 +#define PINTC_HIPIR 0x0900 +#define HIPIR_NOPEND 0x80000000 +#define PINTC_HIER 0x1500 + +struct uio_pruss_dev { + struct uio_info *info; + struct clk *pruss_clk; + dma_addr_t sram_paddr; + dma_addr_t ddr_paddr; + void __iomem *prussio_vaddr; + void *sram_vaddr; + void *ddr_vaddr; + unsigned int hostirq_start; + unsigned int pintc_base; +}; + +static irqreturn_t pruss_handler(int irq, struct uio_info *info) +{ + struct uio_pruss_dev *gdev = info->priv; + int intr_bit = (irq - gdev->hostirq_start + 2); + int val, intr_mask = (1 << intr_bit); + void __iomem *base = gdev->prussio_vaddr + gdev->pintc_base; + void __iomem *intren_reg = base + PINTC_HIER; + void __iomem *intrdis_reg = base + PINTC_HIDISR; + void __iomem *intrstat_reg = base + PINTC_HIPIR + (intr_bit << 2); + + val = ioread32(intren_reg); + /* Is interrupt enabled and active ? */ + if (!(val & intr_mask) && (ioread32(intrstat_reg) & HIPIR_NOPEND)) + return IRQ_NONE; + /* Disable interrupt */ + iowrite32(intr_bit, intrdis_reg); + return IRQ_HANDLED; +} + +static void pruss_cleanup(struct platform_device *dev, + struct uio_pruss_dev *gdev) +{ + int cnt; + struct uio_info *p = gdev->info; + + for (cnt = 0; cnt < MAX_PRUSS_EVT; cnt++, p++) { + uio_unregister_device(p); + kfree(p->name); + } + iounmap(gdev->prussio_vaddr); + if (gdev->ddr_vaddr) { + dma_free_coherent(&dev->dev, extram_pool_sz, gdev->ddr_vaddr, + gdev->ddr_paddr); + } + if (gdev->sram_vaddr) + sram_free(gdev->sram_vaddr, sram_pool_sz); + kfree(gdev->info); + clk_put(gdev->pruss_clk); + kfree(gdev); +} + +static int __devinit pruss_probe(struct platform_device *dev) +{ + struct uio_info *p; + struct uio_pruss_dev *gdev; + struct resource *regs_prussio; + int ret = -ENODEV, cnt = 0, len; + struct uio_pruss_pdata *pdata = dev->dev.platform_data; + + gdev = kzalloc(sizeof(struct uio_pruss_dev), GFP_KERNEL); + if (!gdev) + return -ENOMEM; + + gdev->info = kzalloc(sizeof(*p) * MAX_PRUSS_EVT, GFP_KERNEL); + if (!gdev->info) { + kfree(gdev); + return -ENOMEM; + } + /* Power on PRU in case its not done as part of boot-loader */ + gdev->pruss_clk = clk_get(&dev->dev, "pruss"); + if (IS_ERR(gdev->pruss_clk)) { + dev_err(&dev->dev, "Failed to get clock\n"); + kfree(gdev->info); + kfree(gdev); + ret = PTR_ERR(gdev->pruss_clk); + return ret; + } else { + clk_enable(gdev->pruss_clk); + } + + regs_prussio = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!regs_prussio) { + dev_err(&dev->dev, "No PRUSS I/O resource specified\n"); + goto out_free; + } + + if (!regs_prussio->start) { + dev_err(&dev->dev, "Invalid memory resource\n"); + goto out_free; + } + + gdev->sram_vaddr = sram_alloc(sram_pool_sz, &(gdev->sram_paddr)); + if (!gdev->sram_vaddr) { + dev_err(&dev->dev, "Could not allocate SRAM pool\n"); + goto out_free; + } + + gdev->ddr_vaddr = dma_alloc_coherent(&dev->dev, extram_pool_sz, + &(gdev->ddr_paddr), GFP_KERNEL | GFP_DMA); + if (!gdev->ddr_vaddr) { + dev_err(&dev->dev, "Could not allocate external memory\n"); + goto out_free; + } + + len = resource_size(regs_prussio); + gdev->prussio_vaddr = ioremap(regs_prussio->start, len); + if (!gdev->prussio_vaddr) { + dev_err(&dev->dev, "Can't remap PRUSS I/O address range\n"); + goto out_free; + } + + gdev->pintc_base = pdata->pintc_base; + gdev->hostirq_start = platform_get_irq(dev, 0); + + for (cnt = 0, p = gdev->info; cnt < MAX_PRUSS_EVT; cnt++, p++) { + p->mem[0].addr = regs_prussio->start; + p->mem[0].size = resource_size(regs_prussio); + p->mem[0].memtype = UIO_MEM_PHYS; + + p->mem[1].addr = gdev->sram_paddr; + p->mem[1].size = sram_pool_sz; + p->mem[1].memtype = UIO_MEM_PHYS; + + p->mem[2].addr = gdev->ddr_paddr; + p->mem[2].size = extram_pool_sz; + p->mem[2].memtype = UIO_MEM_PHYS; + + p->name = kasprintf(GFP_KERNEL, "pruss_evt%d", cnt); + p->version = DRV_VERSION; + + /* Register PRUSS IRQ lines */ + p->irq = gdev->hostirq_start + cnt; + p->handler = pruss_handler; + p->priv = gdev; + + ret = uio_register_device(&dev->dev, p); + if (ret < 0) + goto out_free; + } + + platform_set_drvdata(dev, gdev); + return 0; + +out_free: + pruss_cleanup(dev, gdev); + return ret; +} + +static int __devexit pruss_remove(struct platform_device *dev) +{ + struct uio_pruss_dev *gdev = platform_get_drvdata(dev); + + pruss_cleanup(dev, gdev); + platform_set_drvdata(dev, NULL); + return 0; +} + +static struct platform_driver pruss_driver = { + .probe = pruss_probe, + .remove = __devexit_p(pruss_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init pruss_init_module(void) +{ + return platform_driver_register(&pruss_driver); +} + +module_init(pruss_init_module); + +static void __exit pruss_exit_module(void) +{ + platform_driver_unregister(&pruss_driver); +} + +module_exit(pruss_exit_module); + +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR("Amit Chatterjee "); +MODULE_AUTHOR("Pratheesh Gangadhar "); diff --git a/include/linux/platform_data/uio_pruss.h b/include/linux/platform_data/uio_pruss.h new file mode 100644 index 000000000000..f39140aabc6f --- /dev/null +++ b/include/linux/platform_data/uio_pruss.h @@ -0,0 +1,25 @@ +/* + * include/linux/platform_data/uio_pruss.h + * + * Platform data for uio_pruss driver + * + * Copyright (C) 2010-11 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _UIO_PRUSS_H_ +#define _UIO_PRUSS_H_ + +/* To configure the PRUSS INTC base offset for UIO driver */ +struct uio_pruss_pdata { + u32 pintc_base; +}; +#endif /* _UIO_PRUSS_H_ */ -- cgit v1.2.3 From 633e804e89464d3875e59de1959a53f9041d3094 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 7 Mar 2011 15:05:51 +0000 Subject: KEYS: Add an RCU payload dereference macro Add an RCU payload dereference macro as this seems to be a common piece of code amongst key types that use RCU referenced payloads. Signed-off-by: David Howells Signed-off-by: Mimi Zohar Signed-off-by: James Morris --- include/linux/key.h | 4 ++++ security/keys/encrypted.c | 3 +-- security/keys/trusted.c | 3 +-- security/keys/user_defined.c | 3 +-- 4 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/key.h b/include/linux/key.h index 3db0adce1fda..a6b1edcffc34 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -275,6 +275,10 @@ static inline key_serial_t key_serial(struct key *key) return key ? key->serial : 0; } +#define rcu_dereference_key(KEY) \ + (rcu_dereference_protected((KEY)->payload.rcudata, \ + rwsem_is_locked(&((struct key *)(KEY))->sem))) + #ifdef CONFIG_SYSCTL extern ctl_table key_sysctls[]; #endif diff --git a/security/keys/encrypted.c b/security/keys/encrypted.c index 9e7e4ce3fae8..69907a58a683 100644 --- a/security/keys/encrypted.c +++ b/security/keys/encrypted.c @@ -765,8 +765,7 @@ static long encrypted_read(const struct key *key, char __user *buffer, size_t asciiblob_len; int ret; - epayload = rcu_dereference_protected(key->payload.data, - rwsem_is_locked(&((struct key *)key)->sem)); + epayload = rcu_dereference_key(key); /* returns the hex encoded iv, encrypted-data, and hmac as ascii */ asciiblob_len = epayload->datablob_len + ivsize + 1 diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 83fc92e297cd..c99b9368368c 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -1076,8 +1076,7 @@ static long trusted_read(const struct key *key, char __user *buffer, char *bufp; int i; - p = rcu_dereference_protected(key->payload.data, - rwsem_is_locked(&((struct key *)key)->sem)); + p = rcu_dereference_key(key); if (!p) return -EINVAL; if (!buffer || buflen <= 0) diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 02807fb16340..c6ca8662a468 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -184,8 +184,7 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen) struct user_key_payload *upayload; long ret; - upayload = rcu_dereference_protected( - key->payload.data, rwsem_is_locked(&((struct key *)key)->sem)); + upayload = rcu_dereference_key(key); ret = upayload->datalen; /* we can return the data as is */ -- cgit v1.2.3 From b9fffa3877a3ebbe0a5ad5a247358e2f7df15b24 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 7 Mar 2011 15:05:59 +0000 Subject: KEYS: Add a key type op to permit the key description to be vetted Add a key type operation to permit the key type to vet the description of a new key that key_alloc() is about to allocate. The operation may reject the description if it wishes with an error of its choosing. If it does this, the key will not be allocated. Signed-off-by: David Howells Reviewed-by: Mimi Zohar Signed-off-by: James Morris --- Documentation/keys.txt | 7 +++++++ include/linux/key-type.h | 3 +++ net/rxrpc/ar-key.c | 19 +++++++++++++++++++ security/keys/key.c | 8 ++++++++ 4 files changed, 37 insertions(+) (limited to 'include') diff --git a/Documentation/keys.txt b/Documentation/keys.txt index e4dbbdb1bd96..cf68d1fed95d 100644 --- a/Documentation/keys.txt +++ b/Documentation/keys.txt @@ -1062,6 +1062,13 @@ The structure has a number of fields, some of which are mandatory: viable. + (*) int (*vet_description)(const char *description); + + This optional method is called to vet a key description. If the key type + doesn't approve of the key description, it may return an error, otherwise + it should return 0. + + (*) int (*instantiate)(struct key *key, const void *data, size_t datalen); This method is called to attach a payload to a key during construction. diff --git a/include/linux/key-type.h b/include/linux/key-type.h index 65833d4d5998..fc8525e838b7 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -41,6 +41,9 @@ struct key_type { */ size_t def_datalen; + /* vet a description */ + int (*vet_description)(const char *description); + /* instantiate a key of this type * - this method should call key_payload_reserve() to determine if the * user's quota will hold the payload diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c index d763793d39de..43ea7de2fc8e 100644 --- a/net/rxrpc/ar-key.c +++ b/net/rxrpc/ar-key.c @@ -25,6 +25,7 @@ #include #include "ar-internal.h" +static int rxrpc_vet_description_s(const char *); static int rxrpc_instantiate(struct key *, const void *, size_t); static int rxrpc_instantiate_s(struct key *, const void *, size_t); static void rxrpc_destroy(struct key *); @@ -52,12 +53,30 @@ EXPORT_SYMBOL(key_type_rxrpc); */ struct key_type key_type_rxrpc_s = { .name = "rxrpc_s", + .vet_description = rxrpc_vet_description_s, .instantiate = rxrpc_instantiate_s, .match = user_match, .destroy = rxrpc_destroy_s, .describe = rxrpc_describe, }; +/* + * Vet the description for an RxRPC server key + */ +static int rxrpc_vet_description_s(const char *desc) +{ + unsigned long num; + char *p; + + num = simple_strtoul(desc, &p, 10); + if (*p != ':' || num > 65535) + return -EINVAL; + num = simple_strtoul(p + 1, &p, 10); + if (*p || num < 1 || num > 255) + return -EINVAL; + return 0; +} + /* * parse an RxKAD type XDR format token * - the caller guarantees we have at least 4 words diff --git a/security/keys/key.c b/security/keys/key.c index 1c2d43dc5107..8e315ef2e88e 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -249,6 +249,14 @@ struct key *key_alloc(struct key_type *type, const char *desc, if (!desc || !*desc) goto error; + if (type->vet_description) { + ret = type->vet_description(desc); + if (ret < 0) { + key = ERR_PTR(ret); + goto error; + } + } + desclen = strlen(desc) + 1; quotalen = desclen + type->def_datalen; -- cgit v1.2.3 From fdd1b94581782a2ddf9124414e5b7a5f48ce2f9c Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 7 Mar 2011 15:06:09 +0000 Subject: KEYS: Add a new keyctl op to reject a key with a specified error code Add a new keyctl op to reject a key with a specified error code. This works much the same as negating a key, and so keyctl_negate_key() is made a special case of keyctl_reject_key(). The difference is that keyctl_negate_key() selects ENOKEY as the error to be reported. Typically the key would be rejected with EKEYEXPIRED, EKEYREVOKED or EKEYREJECTED, but this is not mandatory. Signed-off-by: David Howells Signed-off-by: James Morris --- Documentation/keys-request-key.txt | 9 +++++---- Documentation/keys.txt | 10 ++++++++-- include/linux/key-type.h | 11 ++++++++++- include/linux/key.h | 1 + include/linux/keyctl.h | 1 + security/keys/compat.c | 3 +++ security/keys/internal.h | 1 + security/keys/key.c | 19 ++++++++++-------- security/keys/keyctl.c | 40 ++++++++++++++++++++++++++++++++++++-- security/keys/keyring.c | 4 ++-- security/keys/request_key.c | 2 +- 11 files changed, 81 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/Documentation/keys-request-key.txt b/Documentation/keys-request-key.txt index 09b55e461740..69686ad12c66 100644 --- a/Documentation/keys-request-key.txt +++ b/Documentation/keys-request-key.txt @@ -127,14 +127,15 @@ This is because process A's keyrings can't simply be attached to of them, and (b) it requires the same UID/GID/Groups all the way through. -====================== -NEGATIVE INSTANTIATION -====================== +==================================== +NEGATIVE INSTANTIATION AND REJECTION +==================================== Rather than instantiating a key, it is possible for the possessor of an authorisation key to negatively instantiate a key that's under construction. This is a short duration placeholder that causes any attempt at re-requesting -the key whilst it exists to fail with error ENOKEY. +the key whilst it exists to fail with error ENOKEY if negated or the specified +error if rejected. This is provided to prevent excessive repeated spawning of /sbin/request-key processes for a key that will never be obtainable. diff --git a/Documentation/keys.txt b/Documentation/keys.txt index cf68d1fed95d..a6a97fdfaddd 100644 --- a/Documentation/keys.txt +++ b/Documentation/keys.txt @@ -657,6 +657,8 @@ The keyctl syscall functions are: long keyctl(KEYCTL_NEGATE, key_serial_t key, unsigned timeout, key_serial_t keyring); + long keyctl(KEYCTL_REJECT, key_serial_t key, + unsigned timeout, unsigned error, key_serial_t keyring); If the kernel calls back to userspace to complete the instantiation of a key, userspace should use this call mark the key as negative before the @@ -669,6 +671,10 @@ The keyctl syscall functions are: that keyring, however all the constraints applying in KEYCTL_LINK apply in this case too. + If the key is rejected, future searches for it will return the specified + error code until the rejected key expires. Negating the key is the same + as rejecting the key with ENOKEY as the error code. + (*) Set the default request-key destination keyring. @@ -1240,8 +1246,8 @@ example, the KDE desktop manager). The program (or whatever it calls) should finish construction of the key by calling KEYCTL_INSTANTIATE, which also permits it to cache the key in one of the keyrings (probably the session ring) before returning. Alternatively, the -key can be marked as negative with KEYCTL_NEGATE; this also permits the key to -be cached in one of the keyrings. +key can be marked as negative with KEYCTL_NEGATE or KEYCTL_REJECT; this also +permits the key to be cached in one of the keyrings. If it returns with the key remaining in the unconstructed state, the key will be marked as being negative, it will be added to the session keyring, and an diff --git a/include/linux/key-type.h b/include/linux/key-type.h index fc8525e838b7..9efd081bb31e 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -105,11 +105,20 @@ extern int key_instantiate_and_link(struct key *key, size_t datalen, struct key *keyring, struct key *instkey); -extern int key_negate_and_link(struct key *key, +extern int key_reject_and_link(struct key *key, unsigned timeout, + unsigned error, struct key *keyring, struct key *instkey); extern void complete_request_key(struct key_construction *cons, int error); +static inline int key_negate_and_link(struct key *key, + unsigned timeout, + struct key *keyring, + struct key *instkey) +{ + return key_reject_and_link(key, timeout, ENOKEY, keyring, instkey); +} + #endif /* CONFIG_KEYS */ #endif /* _LINUX_KEY_TYPE_H */ diff --git a/include/linux/key.h b/include/linux/key.h index a6b1edcffc34..b2bb01719561 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -170,6 +170,7 @@ struct key { struct list_head link; unsigned long x[2]; void *p[2]; + int reject_error; } type_data; /* key data diff --git a/include/linux/keyctl.h b/include/linux/keyctl.h index bd383f1944fb..7022974def0c 100644 --- a/include/linux/keyctl.h +++ b/include/linux/keyctl.h @@ -53,5 +53,6 @@ #define KEYCTL_ASSUME_AUTHORITY 16 /* assume request_key() authorisation */ #define KEYCTL_GET_SECURITY 17 /* get key security label */ #define KEYCTL_SESSION_TO_PARENT 18 /* apply session keyring to parent process */ +#define KEYCTL_REJECT 19 /* reject a partially constructed key */ #endif /* _LINUX_KEYCTL_H */ diff --git a/security/keys/compat.c b/security/keys/compat.c index 07a5f35e3970..17c99d0149ec 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -85,6 +85,9 @@ asmlinkage long compat_sys_keyctl(u32 option, case KEYCTL_SESSION_TO_PARENT: return keyctl_session_to_parent(); + case KEYCTL_REJECT: + return keyctl_reject_key(arg2, arg3, arg4, arg5); + default: return -EOPNOTSUPP; } diff --git a/security/keys/internal.h b/security/keys/internal.h index a52aa7c88b41..286c0959ee51 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -214,6 +214,7 @@ extern long keyctl_assume_authority(key_serial_t); extern long keyctl_get_security(key_serial_t keyid, char __user *buffer, size_t buflen); extern long keyctl_session_to_parent(void); +extern long keyctl_reject_key(key_serial_t, unsigned, unsigned, key_serial_t); /* * Debugging key validation diff --git a/security/keys/key.c b/security/keys/key.c index 8e315ef2e88e..f7f9d93f08d9 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -511,26 +511,29 @@ int key_instantiate_and_link(struct key *key, EXPORT_SYMBOL(key_instantiate_and_link); /** - * key_negate_and_link - Negatively instantiate a key and link it into the keyring. + * key_reject_and_link - Negatively instantiate a key and link it into the keyring. * @key: The key to instantiate. * @timeout: The timeout on the negative key. + * @error: The error to return when the key is hit. * @keyring: Keyring to create a link in on success (or NULL). * @authkey: The authorisation token permitting instantiation. * * Negatively instantiate a key that's in the uninstantiated state and, if - * successful, set its timeout and link it in to the destination keyring if one - * is supplied. The key and any links to the key will be automatically garbage - * collected after the timeout expires. + * successful, set its timeout and stored error and link it in to the + * destination keyring if one is supplied. The key and any links to the key + * will be automatically garbage collected after the timeout expires. * * Negative keys are used to rate limit repeated request_key() calls by causing - * them to return -ENOKEY until the negative key expires. + * them to return the stored error code (typically ENOKEY) until the negative + * key expires. * * If successful, 0 is returned, the authorisation token is revoked and anyone * waiting for the key is woken up. If the key was already instantiated, * -EBUSY will be returned. */ -int key_negate_and_link(struct key *key, +int key_reject_and_link(struct key *key, unsigned timeout, + unsigned error, struct key *keyring, struct key *authkey) { @@ -556,6 +559,7 @@ int key_negate_and_link(struct key *key, atomic_inc(&key->user->nikeys); set_bit(KEY_FLAG_NEGATIVE, &key->flags); set_bit(KEY_FLAG_INSTANTIATED, &key->flags); + key->type_data.reject_error = -error; now = current_kernel_time(); key->expiry = now.tv_sec + timeout; key_schedule_gc(key->expiry + key_gc_delay); @@ -585,8 +589,7 @@ int key_negate_and_link(struct key *key, return ret == 0 ? link_ret : ret; } - -EXPORT_SYMBOL(key_negate_and_link); +EXPORT_SYMBOL(key_reject_and_link); /* * Garbage collect keys in process context so that we don't have to disable diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 31a0fd8189f1..0d7b1946ff94 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1012,13 +1012,43 @@ error: * If successful, 0 will be returned. */ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid) +{ + return keyctl_reject_key(id, timeout, ENOKEY, ringid); +} + +/* + * Negatively instantiate the key with the given timeout (in seconds) and error + * code and link the key into the destination keyring if one is given. + * + * The caller must have the appropriate instantiation permit set for this to + * work (see keyctl_assume_authority). No other permissions are required. + * + * The key and any links to the key will be automatically garbage collected + * after the timeout expires. + * + * Negative keys are used to rate limit repeated request_key() calls by causing + * them to return the specified error code until the negative key expires. + * + * If successful, 0 will be returned. + */ +long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error, + key_serial_t ringid) { const struct cred *cred = current_cred(); struct request_key_auth *rka; struct key *instkey, *dest_keyring; long ret; - kenter("%d,%u,%d", id, timeout, ringid); + kenter("%d,%u,%u,%d", id, timeout, error, ringid); + + /* must be a valid error code and mustn't be a kernel special */ + if (error <= 0 || + error >= MAX_ERRNO || + error == ERESTARTSYS || + error == ERESTARTNOINTR || + error == ERESTARTNOHAND || + error == ERESTART_RESTARTBLOCK) + return -EINVAL; /* the appropriate instantiation authorisation key must have been * assumed before calling this */ @@ -1038,7 +1068,7 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid) goto error; /* instantiate the key and link it into a keyring */ - ret = key_negate_and_link(rka->target_key, timeout, + ret = key_reject_and_link(rka->target_key, timeout, error, dest_keyring, instkey); key_put(dest_keyring); @@ -1492,6 +1522,12 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, case KEYCTL_SESSION_TO_PARENT: return keyctl_session_to_parent(); + case KEYCTL_REJECT: + return keyctl_reject_key((key_serial_t) arg2, + (unsigned) arg3, + (unsigned) arg4, + (key_serial_t) arg5); + default: return -EOPNOTSUPP; } diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 5620f084dede..cdd2f3f88c88 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -352,7 +352,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref, goto error_2; if (key->expiry && now.tv_sec >= key->expiry) goto error_2; - key_ref = ERR_PTR(-ENOKEY); + key_ref = ERR_PTR(key->type_data.reject_error); if (kflags & (1 << KEY_FLAG_NEGATIVE)) goto error_2; goto found; @@ -401,7 +401,7 @@ descend: /* we set a different error code if we pass a negative key */ if (kflags & (1 << KEY_FLAG_NEGATIVE)) { - err = -ENOKEY; + err = key->type_data.reject_error; continue; } diff --git a/security/keys/request_key.c b/security/keys/request_key.c index a3dc0d460def..df3c0417ee40 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -585,7 +585,7 @@ int wait_for_key_construction(struct key *key, bool intr) if (ret < 0) return ret; if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) - return -ENOKEY; + return key->type_data.reject_error; return key_validate(key); } EXPORT_SYMBOL(wait_for_key_construction); -- cgit v1.2.3 From ee009e4a0d4555ed522a631bae9896399674f064 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 7 Mar 2011 15:06:20 +0000 Subject: KEYS: Add an iovec version of KEYCTL_INSTANTIATE Add a keyctl op (KEYCTL_INSTANTIATE_IOV) that is like KEYCTL_INSTANTIATE, but takes an iovec array and concatenates the data in-kernel into one buffer. Since the KEYCTL_INSTANTIATE copies the data anyway, this isn't too much of a problem. Signed-off-by: David Howells Signed-off-by: James Morris --- Documentation/keys.txt | 15 +++++-- arch/x86/Kconfig | 5 +++ include/linux/keyctl.h | 1 + security/keys/compat.c | 47 +++++++++++++++++++++ security/keys/internal.h | 7 ++++ security/keys/keyctl.c | 103 +++++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 167 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/Documentation/keys.txt b/Documentation/keys.txt index a6a97fdfaddd..6523a9e6f293 100644 --- a/Documentation/keys.txt +++ b/Documentation/keys.txt @@ -637,6 +637,9 @@ The keyctl syscall functions are: long keyctl(KEYCTL_INSTANTIATE, key_serial_t key, const void *payload, size_t plen, key_serial_t keyring); + long keyctl(KEYCTL_INSTANTIATE_IOV, key_serial_t key, + const struct iovec *payload_iov, unsigned ioc, + key_serial_t keyring); If the kernel calls back to userspace to complete the instantiation of a key, userspace should use this call to supply data for the key before the @@ -652,6 +655,9 @@ The keyctl syscall functions are: The payload and plen arguments describe the payload data as for add_key(). + The payload_iov and ioc arguments describe the payload data in an iovec + array instead of a single buffer. + (*) Negatively instantiate a partially constructed key. @@ -1244,10 +1250,11 @@ hand the request off to (perhaps a path held in placed in another key by, for example, the KDE desktop manager). The program (or whatever it calls) should finish construction of the key by -calling KEYCTL_INSTANTIATE, which also permits it to cache the key in one of -the keyrings (probably the session ring) before returning. Alternatively, the -key can be marked as negative with KEYCTL_NEGATE or KEYCTL_REJECT; this also -permits the key to be cached in one of the keyrings. +calling KEYCTL_INSTANTIATE or KEYCTL_INSTANTIATE_IOV, which also permits it to +cache the key in one of the keyrings (probably the session ring) before +returning. Alternatively, the key can be marked as negative with KEYCTL_NEGATE +or KEYCTL_REJECT; this also permits the key to be cached in one of the +keyrings. If it returns with the key remaining in the unconstructed state, the key will be marked as being negative, it will be added to the session keyring, and an diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index d5ed94d30aad..b46b75bef496 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2138,6 +2138,11 @@ config SYSVIPC_COMPAT def_bool y depends on COMPAT && SYSVIPC +config KEYS_COMPAT + bool + depends on COMPAT && KEYS + default y + endmenu diff --git a/include/linux/keyctl.h b/include/linux/keyctl.h index 7022974def0c..9b0b865ce622 100644 --- a/include/linux/keyctl.h +++ b/include/linux/keyctl.h @@ -54,5 +54,6 @@ #define KEYCTL_GET_SECURITY 17 /* get key security label */ #define KEYCTL_SESSION_TO_PARENT 18 /* apply session keyring to parent process */ #define KEYCTL_REJECT 19 /* reject a partially constructed key */ +#define KEYCTL_INSTANTIATE_IOV 20 /* instantiate a partially constructed key */ #endif /* _LINUX_KEYCTL_H */ diff --git a/security/keys/compat.c b/security/keys/compat.c index 17c99d0149ec..338b510e9027 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -12,8 +12,51 @@ #include #include #include +#include #include "internal.h" +/* + * Instantiate a key with the specified compatibility multipart payload and + * link the key into the destination keyring if one is given. + * + * The caller must have the appropriate instantiation permit set for this to + * work (see keyctl_assume_authority). No other permissions are required. + * + * If successful, 0 will be returned. + */ +long compat_keyctl_instantiate_key_iov( + key_serial_t id, + const struct compat_iovec __user *_payload_iov, + unsigned ioc, + key_serial_t ringid) +{ + struct iovec iovstack[UIO_FASTIOV], *iov = iovstack; + long ret; + + if (_payload_iov == 0 || ioc == 0) + goto no_payload; + + ret = compat_rw_copy_check_uvector(WRITE, _payload_iov, ioc, + ARRAY_SIZE(iovstack), + iovstack, &iov); + if (ret < 0) + return ret; + if (ret == 0) + goto no_payload_free; + + ret = keyctl_instantiate_key_common(id, iov, ioc, ret, ringid); + + if (iov != iovstack) + kfree(iov); + return ret; + +no_payload_free: + if (iov != iovstack) + kfree(iov); +no_payload: + return keyctl_instantiate_key_common(id, NULL, 0, 0, ringid); +} + /* * The key control system call, 32-bit compatibility version for 64-bit archs * @@ -88,6 +131,10 @@ asmlinkage long compat_sys_keyctl(u32 option, case KEYCTL_REJECT: return keyctl_reject_key(arg2, arg3, arg4, arg5); + case KEYCTL_INSTANTIATE_IOV: + return compat_keyctl_instantiate_key_iov( + arg2, compat_ptr(arg3), arg4, arg5); + default: return -EOPNOTSUPP; } diff --git a/security/keys/internal.h b/security/keys/internal.h index 286c0959ee51..07a025f81902 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -215,6 +215,13 @@ extern long keyctl_get_security(key_serial_t keyid, char __user *buffer, size_t buflen); extern long keyctl_session_to_parent(void); extern long keyctl_reject_key(key_serial_t, unsigned, unsigned, key_serial_t); +extern long keyctl_instantiate_key_iov(key_serial_t, + const struct iovec __user *, + unsigned, key_serial_t); + +extern long keyctl_instantiate_key_common(key_serial_t, + const struct iovec __user *, + unsigned, size_t, key_serial_t); /* * Debugging key validation diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 0d7b1946ff94..427fddcaeb19 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -912,6 +912,21 @@ static int keyctl_change_reqkey_auth(struct key *key) return commit_creds(new); } +/* + * Copy the iovec data from userspace + */ +static long copy_from_user_iovec(void *buffer, const struct iovec *iov, + unsigned ioc) +{ + for (; ioc > 0; ioc--) { + if (copy_from_user(buffer, iov->iov_base, iov->iov_len) != 0) + return -EFAULT; + buffer += iov->iov_len; + iov++; + } + return 0; +} + /* * Instantiate a key with the specified payload and link the key into the * destination keyring if one is given. @@ -921,10 +936,11 @@ static int keyctl_change_reqkey_auth(struct key *key) * * If successful, 0 will be returned. */ -long keyctl_instantiate_key(key_serial_t id, - const void __user *_payload, - size_t plen, - key_serial_t ringid) +long keyctl_instantiate_key_common(key_serial_t id, + const struct iovec *payload_iov, + unsigned ioc, + size_t plen, + key_serial_t ringid) { const struct cred *cred = current_cred(); struct request_key_auth *rka; @@ -953,7 +969,7 @@ long keyctl_instantiate_key(key_serial_t id, /* pull the payload in if one was supplied */ payload = NULL; - if (_payload) { + if (payload_iov) { ret = -ENOMEM; payload = kmalloc(plen, GFP_KERNEL); if (!payload) { @@ -965,8 +981,8 @@ long keyctl_instantiate_key(key_serial_t id, goto error; } - ret = -EFAULT; - if (copy_from_user(payload, _payload, plen) != 0) + ret = copy_from_user_iovec(payload, payload_iov, ioc); + if (ret < 0) goto error2; } @@ -996,6 +1012,72 @@ error: return ret; } +/* + * Instantiate a key with the specified payload and link the key into the + * destination keyring if one is given. + * + * The caller must have the appropriate instantiation permit set for this to + * work (see keyctl_assume_authority). No other permissions are required. + * + * If successful, 0 will be returned. + */ +long keyctl_instantiate_key(key_serial_t id, + const void __user *_payload, + size_t plen, + key_serial_t ringid) +{ + if (_payload && plen) { + struct iovec iov[1] = { + [0].iov_base = (void __user *)_payload, + [0].iov_len = plen + }; + + return keyctl_instantiate_key_common(id, iov, 1, plen, ringid); + } + + return keyctl_instantiate_key_common(id, NULL, 0, 0, ringid); +} + +/* + * Instantiate a key with the specified multipart payload and link the key into + * the destination keyring if one is given. + * + * The caller must have the appropriate instantiation permit set for this to + * work (see keyctl_assume_authority). No other permissions are required. + * + * If successful, 0 will be returned. + */ +long keyctl_instantiate_key_iov(key_serial_t id, + const struct iovec __user *_payload_iov, + unsigned ioc, + key_serial_t ringid) +{ + struct iovec iovstack[UIO_FASTIOV], *iov = iovstack; + long ret; + + if (_payload_iov == 0 || ioc == 0) + goto no_payload; + + ret = rw_copy_check_uvector(WRITE, _payload_iov, ioc, + ARRAY_SIZE(iovstack), iovstack, &iov); + if (ret < 0) + return ret; + if (ret == 0) + goto no_payload_free; + + ret = keyctl_instantiate_key_common(id, iov, ioc, ret, ringid); + + if (iov != iovstack) + kfree(iov); + return ret; + +no_payload_free: + if (iov != iovstack) + kfree(iov); +no_payload: + return keyctl_instantiate_key_common(id, NULL, 0, 0, ringid); +} + /* * Negatively instantiate the key with the given timeout (in seconds) and link * the key into the destination keyring if one is given. @@ -1528,6 +1610,13 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, (unsigned) arg4, (key_serial_t) arg5); + case KEYCTL_INSTANTIATE_IOV: + return keyctl_instantiate_key_iov( + (key_serial_t) arg2, + (const struct iovec __user *) arg3, + (unsigned) arg4, + (key_serial_t) arg5); + default: return -EOPNOTSUPP; } -- cgit v1.2.3 From 6247e086188dd2ba5bfd64f9a876fe42b0cf39fb Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Tue, 1 Feb 2011 07:22:06 +0000 Subject: net: add ndo_fcoe_ddp_target() to support FCoE DDP in target mode The Fiber Channel over Ethernet (FCoE) Direct Data Placement (DDP) can also be used for FCoE target, where the DDP used for read I/O on an initiator can be used on an FCoE target to speed up the write I/O to the target from the initiator. The added ndo_fcoe_ddp_target() works in the similar way as the existing ndo_fcoe_ddp_setup() to allow the underlying hardware set up the DDP context accordingly when it gets called from the FCoE target implementation on top the existing Open-FCoE fcoe/libfc protocol stack so without losing the ability to provide DDP for read I/O as an initiator, it can also provide DDP offload to the write I/O coming from the initiator as a target. Signed-off-by: Yi Zou Signed-off-by: Kiran Patil Tested-by: Kavindya Deegala Signed-off-by: Jeff Kirsher --- include/linux/netdevice.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 71563e7b2bfb..6bd5d460b7c1 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -871,6 +871,10 @@ struct net_device_ops { unsigned int sgc); int (*ndo_fcoe_ddp_done)(struct net_device *dev, u16 xid); + int (*ndo_fcoe_ddp_target)(struct net_device *dev, + u16 xid, + struct scatterlist *sgl, + unsigned int sgc); #define NETDEV_FCOE_WWNN 0 #define NETDEV_FCOE_WWPN 1 int (*ndo_fcoe_get_wwn)(struct net_device *dev, -- cgit v1.2.3 From 1fc050a13473348f5c439de2bb41c8e92dba5588 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 7 Mar 2011 20:54:48 -0800 Subject: ipv4: Cache source address in nexthop entries. When doing output route lookups, we have to select the source address if the user has not specified an explicit one. First, if the route has an explicit preferred source address specified, then we use that. Otherwise we search the route's outgoing interface for a suitable address. This search can be precomputed and cached at route insertion time. The only missing part is that we have to refresh this precomputed value any time addresses are added or removed from the interface, and this is accomplished by fib_update_nh_saddrs(). Signed-off-by: David S. Miller --- include/net/ip_fib.h | 7 +++++-- net/ipv4/fib_frontend.c | 2 ++ net/ipv4/fib_semantics.c | 31 ++++++++++++++++++++++++------- 3 files changed, 31 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 523a170b0ecb..0e140830b85a 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -60,6 +60,7 @@ struct fib_nh { #endif int nh_oif; __be32 nh_gw; + __be32 nh_saddr; }; /* @@ -139,11 +140,13 @@ struct fib_result_nl { #endif /* CONFIG_IP_ROUTE_MULTIPATH */ -#define FIB_RES_PREFSRC(res) ((res).fi->fib_prefsrc ? : __fib_res_prefsrc(&res)) +#define FIB_RES_SADDR(res) (FIB_RES_NH(res).nh_saddr) #define FIB_RES_GW(res) (FIB_RES_NH(res).nh_gw) #define FIB_RES_DEV(res) (FIB_RES_NH(res).nh_dev) #define FIB_RES_OIF(res) (FIB_RES_NH(res).nh_oif) +#define FIB_RES_PREFSRC(res) ((res).fi->fib_prefsrc ? : FIB_RES_SADDR(res)) + struct fib_table { struct hlist_node tb_hlist; u32 tb_id; @@ -224,8 +227,8 @@ extern void fib_select_default(struct fib_result *res); extern int ip_fib_check_default(__be32 gw, struct net_device *dev); extern int fib_sync_down_dev(struct net_device *dev, int force); extern int fib_sync_down_addr(struct net *net, __be32 local); +extern void fib_update_nh_saddrs(struct net_device *dev); 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_trie.c */ diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index ad0778a3fa53..1d2233cd99e6 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -890,10 +890,12 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, #ifdef CONFIG_IP_ROUTE_MULTIPATH fib_sync_up(dev); #endif + fib_update_nh_saddrs(dev); rt_cache_flush(dev_net(dev), -1); break; case NETDEV_DOWN: fib_del_ifaddr(ifa); + fib_update_nh_saddrs(dev); if (ifa->ifa_dev->ifa_list == NULL) { /* Last address was deleted from this interface. * Disable IP. diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 6349a21692ec..952c737f2a27 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -853,6 +853,12 @@ struct fib_info *fib_create_info(struct fib_config *cfg) goto err_inval; } + change_nexthops(fi) { + nexthop_nh->nh_saddr = inet_select_addr(nexthop_nh->nh_dev, + nexthop_nh->nh_gw, + nexthop_nh->nh_scope); + } endfor_nexthops(fi) + link_it: ofi = fib_find_info(fi); if (ofi) { @@ -898,13 +904,6 @@ failure: return ERR_PTR(err); } -/* Find appropriate source address to this destination */ - -__be32 __fib_res_prefsrc(struct fib_result *res) -{ - return inet_select_addr(FIB_RES_DEV(*res), FIB_RES_GW(*res), res->scope); -} - int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, u32 tb_id, u8 type, u8 scope, __be32 dst, int dst_len, u8 tos, struct fib_info *fi, unsigned int flags) @@ -1128,6 +1127,24 @@ out: return; } +void fib_update_nh_saddrs(struct net_device *dev) +{ + struct hlist_head *head; + struct hlist_node *node; + struct fib_nh *nh; + unsigned int hash; + + hash = fib_devindex_hashfn(dev->ifindex); + head = &fib_info_devhash[hash]; + hlist_for_each_entry(nh, node, head, nh_hash) { + if (nh->nh_dev != dev) + continue; + nh->nh_saddr = inet_select_addr(nh->nh_dev, + nh->nh_gw, + nh->nh_scope); + } +} + #ifdef CONFIG_IP_ROUTE_MULTIPATH /* -- cgit v1.2.3 From f0c9f242f947a37675a883deca7f722cac935b0e Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Thu, 20 Jan 2011 02:09:52 +0900 Subject: nilfs2: use common file attribute macros Replaces uses of own inode flags (i.e. NILFS_SECRM_FL, NILFS_UNRM_FL, NILFS_COMPR_FL, and so forth) with common inode flags, and removes the own flag declarations. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/dir.c | 3 --- fs/nilfs2/inode.c | 14 +++++++------- include/linux/nilfs2_fs.h | 20 -------------------- 3 files changed, 7 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c index 9d45773b79e6..b72833a2cc1c 100644 --- a/fs/nilfs2/dir.c +++ b/fs/nilfs2/dir.c @@ -440,7 +440,6 @@ void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de, nilfs_commit_chunk(page, mapping, from, to); nilfs_put_page(page); dir->i_mtime = dir->i_ctime = CURRENT_TIME; -/* NILFS_I(dir)->i_flags &= ~NILFS_BTREE_FL; */ } /* @@ -531,7 +530,6 @@ got_it: nilfs_set_de_type(de, inode); nilfs_commit_chunk(page, page->mapping, from, to); dir->i_mtime = dir->i_ctime = CURRENT_TIME; -/* NILFS_I(dir)->i_flags &= ~NILFS_BTREE_FL; */ nilfs_mark_inode_dirty(dir); /* OFFSET_CACHE */ out_put: @@ -579,7 +577,6 @@ int nilfs_delete_entry(struct nilfs_dir_entry *dir, struct page *page) dir->inode = 0; nilfs_commit_chunk(page, mapping, from, to); inode->i_ctime = inode->i_mtime = CURRENT_TIME; -/* NILFS_I(inode)->i_flags &= ~NILFS_BTREE_FL; */ out: nilfs_put_page(page); return err; diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 3a6967d14e1c..f61f80c7f7cc 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -317,9 +317,9 @@ struct inode *nilfs_new_inode(struct inode *dir, int mode) ii->i_flags = NILFS_I(dir)->i_flags; if (S_ISLNK(mode)) - ii->i_flags &= ~(NILFS_IMMUTABLE_FL | NILFS_APPEND_FL); + ii->i_flags &= ~(FS_IMMUTABLE_FL | FS_APPEND_FL); if (!S_ISDIR(mode)) - ii->i_flags &= ~NILFS_DIRSYNC_FL; + ii->i_flags &= ~FS_DIRSYNC_FL; /* ii->i_file_acl = 0; */ /* ii->i_dir_acl = 0; */ @@ -359,17 +359,17 @@ void nilfs_set_inode_flags(struct inode *inode) inode->i_flags &= ~(S_SYNC | S_APPEND | S_IMMUTABLE | S_NOATIME | S_DIRSYNC); - if (flags & NILFS_SYNC_FL) + if (flags & FS_SYNC_FL) inode->i_flags |= S_SYNC; - if (flags & NILFS_APPEND_FL) + if (flags & FS_APPEND_FL) inode->i_flags |= S_APPEND; - if (flags & NILFS_IMMUTABLE_FL) + if (flags & FS_IMMUTABLE_FL) inode->i_flags |= S_IMMUTABLE; #ifndef NILFS_ATIME_DISABLE - if (flags & NILFS_NOATIME_FL) + if (flags & FS_NOATIME_FL) #endif inode->i_flags |= S_NOATIME; - if (flags & NILFS_DIRSYNC_FL) + if (flags & FS_DIRSYNC_FL) inode->i_flags |= S_DIRSYNC; mapping_set_gfp_mask(inode->i_mapping, mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS); diff --git a/include/linux/nilfs2_fs.h b/include/linux/nilfs2_fs.h index 227e49dd5720..fdcd1bc7f61f 100644 --- a/include/linux/nilfs2_fs.h +++ b/include/linux/nilfs2_fs.h @@ -41,26 +41,6 @@ #include #include -/* - * Inode flags stored in nilfs_inode and on-memory nilfs inode - * - * We define these flags based on ext2-fs because of the - * compatibility reason; to avoid problems in chattr(1) - */ -#define NILFS_SECRM_FL 0x00000001 /* Secure deletion */ -#define NILFS_UNRM_FL 0x00000002 /* Undelete */ -#define NILFS_SYNC_FL 0x00000008 /* Synchronous updates */ -#define NILFS_IMMUTABLE_FL 0x00000010 /* Immutable file */ -#define NILFS_APPEND_FL 0x00000020 /* writes to file may only append */ -#define NILFS_NODUMP_FL 0x00000040 /* do not dump file */ -#define NILFS_NOATIME_FL 0x00000080 /* do not update atime */ -/* Reserved for compression usage... */ -#define NILFS_NOTAIL_FL 0x00008000 /* file tail should not be merged */ -#define NILFS_DIRSYNC_FL 0x00010000 /* dirsync behaviour */ - -#define NILFS_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */ -#define NILFS_FL_USER_MODIFIABLE 0x000380FF /* User modifiable flags */ - #define NILFS_INODE_BMAP_SIZE 7 /** -- cgit v1.2.3 From ae191838b0251d73b9d0a7254c6938406f5f6320 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Fri, 4 Feb 2011 01:19:38 +0900 Subject: nilfs2: optimize rec_len functions This is a similar change to those in ext2/ext3 codebase (commit 40a063f6691ce937 and a4ae3094869f18e2, respectively). The addition of 64k block capability in the rec_len_from_disk and rec_len_to_disk functions added a bit of math overhead which slows down file create workloads needlessly when the architecture cannot even support 64k blocks. This will cut the corner. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/ioctl.c | 1 + include/linux/nilfs2_fs.h | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'include') diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c index d89173edd7fe..5471eed5eccb 100644 --- a/fs/nilfs2/ioctl.c +++ b/fs/nilfs2/ioctl.c @@ -28,6 +28,7 @@ #include #include /* compat_ptr() */ #include /* mnt_want_write(), mnt_drop_write() */ +#include #include #include "nilfs.h" #include "segment.h" diff --git a/include/linux/nilfs2_fs.h b/include/linux/nilfs2_fs.h index fdcd1bc7f61f..3a65e5aa2d76 100644 --- a/include/linux/nilfs2_fs.h +++ b/include/linux/nilfs2_fs.h @@ -326,17 +326,21 @@ static inline unsigned nilfs_rec_len_from_disk(__le16 dlen) { unsigned len = le16_to_cpu(dlen); +#if !defined(__KERNEL__) || (PAGE_CACHE_SIZE >= 65536) if (len == NILFS_MAX_REC_LEN) return 1 << 16; +#endif return len; } static inline __le16 nilfs_rec_len_to_disk(unsigned len) { +#if !defined(__KERNEL__) || (PAGE_CACHE_SIZE >= 65536) if (len == (1 << 16)) return cpu_to_le16(NILFS_MAX_REC_LEN); else if (len > (1 << 16)) BUG(); +#endif return cpu_to_le16(len); } -- cgit v1.2.3 From be667377a8b8cd73e1b923f33fb5be4034aa4bfa Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sat, 5 Mar 2011 00:19:32 +0900 Subject: nilfs2: record used amount of each checkpoint in checkpoint list This records the number of used blocks per checkpoint in each checkpoint entry of cpfile. Even though userland tools can get the block count via nilfs_get_cpinfo ioctl, it was not updated by the nilfs2 kernel code. This fixes the issue and makes it available for userland tools to calculate used amount per checkpoint. Signed-off-by: Ryusuke Konishi Cc: Jiro SEKIBA --- fs/nilfs2/bmap.c | 11 ----------- fs/nilfs2/bmap.h | 3 --- fs/nilfs2/btree.c | 6 +++--- fs/nilfs2/direct.c | 4 ++-- fs/nilfs2/inode.c | 18 ++++++++++++++++++ fs/nilfs2/nilfs.h | 2 ++ include/linux/nilfs2_fs.h | 6 ++++-- 7 files changed, 29 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/fs/nilfs2/bmap.c b/fs/nilfs2/bmap.c index 3ee67c67cc52..85447a2fab33 100644 --- a/fs/nilfs2/bmap.c +++ b/fs/nilfs2/bmap.c @@ -425,17 +425,6 @@ int nilfs_bmap_test_and_clear_dirty(struct nilfs_bmap *bmap) /* * Internal use only */ - -void nilfs_bmap_add_blocks(const struct nilfs_bmap *bmap, int n) -{ - inode_add_bytes(bmap->b_inode, (1 << bmap->b_inode->i_blkbits) * n); -} - -void nilfs_bmap_sub_blocks(const struct nilfs_bmap *bmap, int n) -{ - inode_sub_bytes(bmap->b_inode, (1 << bmap->b_inode->i_blkbits) * n); -} - __u64 nilfs_bmap_data_get_key(const struct nilfs_bmap *bmap, const struct buffer_head *bh) { diff --git a/fs/nilfs2/bmap.h b/fs/nilfs2/bmap.h index bde1c0aa2e15..40d9f453d31c 100644 --- a/fs/nilfs2/bmap.h +++ b/fs/nilfs2/bmap.h @@ -240,9 +240,6 @@ __u64 nilfs_bmap_data_get_key(const struct nilfs_bmap *, __u64 nilfs_bmap_find_target_seq(const struct nilfs_bmap *, __u64); __u64 nilfs_bmap_find_target_in_group(const struct nilfs_bmap *); -void nilfs_bmap_add_blocks(const struct nilfs_bmap *, int); -void nilfs_bmap_sub_blocks(const struct nilfs_bmap *, int); - /* Assume that bmap semaphore is locked. */ static inline int nilfs_bmap_dirty(const struct nilfs_bmap *bmap) diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index 300c2bc00c3f..d451ae0e0bf3 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -1174,7 +1174,7 @@ static int nilfs_btree_insert(struct nilfs_bmap *btree, __u64 key, __u64 ptr) if (ret < 0) goto out; nilfs_btree_commit_insert(btree, path, level, key, ptr); - nilfs_bmap_add_blocks(btree, stats.bs_nblocks); + nilfs_inode_add_blocks(btree->b_inode, stats.bs_nblocks); out: nilfs_btree_free_path(path); @@ -1511,7 +1511,7 @@ static int nilfs_btree_delete(struct nilfs_bmap *btree, __u64 key) if (ret < 0) goto out; nilfs_btree_commit_delete(btree, path, level, dat); - nilfs_bmap_sub_blocks(btree, stats.bs_nblocks); + nilfs_inode_sub_blocks(btree->b_inode, stats.bs_nblocks); out: nilfs_btree_free_path(path); @@ -1776,7 +1776,7 @@ int nilfs_btree_convert_and_insert(struct nilfs_bmap *btree, return ret; nilfs_btree_commit_convert_and_insert(btree, key, ptr, keys, ptrs, n, di, ni, bh); - nilfs_bmap_add_blocks(btree, stats.bs_nblocks); + nilfs_inode_add_blocks(btree->b_inode, stats.bs_nblocks); return 0; } diff --git a/fs/nilfs2/direct.c b/fs/nilfs2/direct.c index 324d80c57518..82f4865e86dd 100644 --- a/fs/nilfs2/direct.c +++ b/fs/nilfs2/direct.c @@ -146,7 +146,7 @@ static int nilfs_direct_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr) if (NILFS_BMAP_USE_VBN(bmap)) nilfs_bmap_set_target_v(bmap, key, req.bpr_ptr); - nilfs_bmap_add_blocks(bmap, 1); + nilfs_inode_add_blocks(bmap->b_inode, 1); } return ret; } @@ -168,7 +168,7 @@ static int nilfs_direct_delete(struct nilfs_bmap *bmap, __u64 key) if (!ret) { nilfs_bmap_commit_end_ptr(bmap, &req, dat); nilfs_direct_set_ptr(bmap, key, NILFS_BMAP_INVALID_PTR); - nilfs_bmap_sub_blocks(bmap, 1); + nilfs_inode_sub_blocks(bmap->b_inode, 1); } return ret; } diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 2534af8d2b5c..22a816ba3621 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -41,6 +41,24 @@ struct nilfs_iget_args { int for_gc; }; +void nilfs_inode_add_blocks(struct inode *inode, int n) +{ + struct nilfs_root *root = NILFS_I(inode)->i_root; + + inode_add_bytes(inode, (1 << inode->i_blkbits) * n); + if (root) + atomic_add(n, &root->blocks_count); +} + +void nilfs_inode_sub_blocks(struct inode *inode, int n) +{ + struct nilfs_root *root = NILFS_I(inode)->i_root; + + inode_sub_bytes(inode, (1 << inode->i_blkbits) * n); + if (root) + atomic_sub(n, &root->blocks_count); +} + /** * nilfs_get_block() - get a file block on the filesystem (callback function) * @inode - inode struct of the target file diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index 45b1fd1d0245..03ba4d88083f 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -251,6 +251,8 @@ int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *, struct nilfs_argv *, void **); /* inode.c */ +void nilfs_inode_add_blocks(struct inode *inode, int n); +void nilfs_inode_sub_blocks(struct inode *inode, int n); extern struct inode *nilfs_new_inode(struct inode *, int); extern void nilfs_free_inode(struct inode *); extern int nilfs_get_block(struct inode *, sector_t, struct buffer_head *, int); diff --git a/include/linux/nilfs2_fs.h b/include/linux/nilfs2_fs.h index 3a65e5aa2d76..ae33ac2db62d 100644 --- a/include/linux/nilfs2_fs.h +++ b/include/linux/nilfs2_fs.h @@ -216,8 +216,10 @@ struct nilfs_super_block { * If there is a bit set in the incompatible feature set that the kernel * doesn't know about, it should refuse to mount the filesystem. */ +#define NILFS_FEATURE_COMPAT_RO_BLOCK_COUNT 0x00000001ULL + #define NILFS_FEATURE_COMPAT_SUPP 0ULL -#define NILFS_FEATURE_COMPAT_RO_SUPP 0ULL +#define NILFS_FEATURE_COMPAT_RO_SUPP NILFS_FEATURE_COMPAT_RO_BLOCK_COUNT #define NILFS_FEATURE_INCOMPAT_SUPP 0ULL /* @@ -509,7 +511,7 @@ struct nilfs_checkpoint { __le64 cp_create; __le64 cp_nblk_inc; __le64 cp_inodes_count; - __le64 cp_blocks_count; /* Reserved (might be deleted) */ + __le64 cp_blocks_count; /* Do not change the byte offset of ifile inode. To keep the compatibility of the disk format, -- cgit v1.2.3 From dfef6dcd35cb4a251f6322ca9b2c06f0bb1aa1f4 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 8 Mar 2011 01:25:28 -0500 Subject: unfuck proc_sysctl ->d_compare() a) struct inode is not going to be freed under ->d_compare(); however, the thing PROC_I(inode)->sysctl points to just might. Fortunately, it's enough to make freeing that sucker delayed, provided that we don't step on its ->unregistering, clear the pointer to it in PROC_I(inode) before dropping the reference and check if it's NULL in ->d_compare(). b) I'm not sure that we *can* walk into NULL inode here (we recheck dentry->seq between verifying that it's still hashed / fetching dentry->d_inode and passing it to ->d_compare() and there's no negative hashed dentries in /proc/sys/*), but if we can walk into that, we really should not have ->d_compare() return 0 on it! Said that, I really suspect that this check can be simply killed. Nick? Signed-off-by: Al Viro --- fs/proc/inode.c | 8 ++++++-- fs/proc/proc_sysctl.c | 7 +++++-- include/linux/sysctl.h | 14 ++++++++++---- kernel/sysctl.c | 15 ++++++++++----- 4 files changed, 31 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 176ce4cda68a..d6a7ca1fdac5 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -27,6 +27,7 @@ static void proc_evict_inode(struct inode *inode) { struct proc_dir_entry *de; + struct ctl_table_header *head; truncate_inode_pages(&inode->i_data, 0); end_writeback(inode); @@ -38,8 +39,11 @@ static void proc_evict_inode(struct inode *inode) de = PROC_I(inode)->pde; if (de) pde_put(de); - if (PROC_I(inode)->sysctl) - sysctl_head_put(PROC_I(inode)->sysctl); + head = PROC_I(inode)->sysctl; + if (head) { + rcu_assign_pointer(PROC_I(inode)->sysctl, NULL); + sysctl_head_put(head); + } } struct vfsmount *proc_mnt; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 09a1f92a34ef..8eb2522111c5 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -408,15 +408,18 @@ static int proc_sys_compare(const struct dentry *parent, const struct dentry *dentry, const struct inode *inode, unsigned int len, const char *str, const struct qstr *name) { + struct ctl_table_header *head; /* Although proc doesn't have negative dentries, rcu-walk means * that inode here can be NULL */ + /* AV: can it, indeed? */ if (!inode) - return 0; + return 1; if (name->len != len) return 1; if (memcmp(name->name, str, len)) return 1; - return !sysctl_is_seen(PROC_I(inode)->sysctl); + head = rcu_dereference(PROC_I(inode)->sysctl); + return !head || !sysctl_is_seen(head); } static const struct dentry_operations proc_sys_dentry_operations = { diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 7bb5cb64f3b8..bb7c2b086fa4 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -25,6 +25,7 @@ #include #include #include +#include struct completion; @@ -1037,10 +1038,15 @@ struct ctl_table_root { struct ctl_table trees. */ struct ctl_table_header { - struct ctl_table *ctl_table; - struct list_head ctl_entry; - int used; - int count; + union { + struct { + struct ctl_table *ctl_table; + struct list_head ctl_entry; + int used; + int count; + }; + struct rcu_head rcu; + }; struct completion *unregistering; struct ctl_table *ctl_table_arg; struct ctl_table_root *root; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 0f1bd83db985..4eed0af5d144 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -194,9 +194,9 @@ static int sysrq_sysctl_handler(ctl_table *table, int write, static struct ctl_table root_table[]; static struct ctl_table_root sysctl_table_root; static struct ctl_table_header root_table_header = { - .count = 1, + {{.count = 1, .ctl_table = root_table, - .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list), + .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list),}}, .root = &sysctl_table_root, .set = &sysctl_table_root.default_set, }; @@ -1567,11 +1567,16 @@ void sysctl_head_get(struct ctl_table_header *head) spin_unlock(&sysctl_lock); } +static void free_head(struct rcu_head *rcu) +{ + kfree(container_of(rcu, struct ctl_table_header, rcu)); +} + void sysctl_head_put(struct ctl_table_header *head) { spin_lock(&sysctl_lock); if (!--head->count) - kfree(head); + call_rcu(&head->rcu, free_head); spin_unlock(&sysctl_lock); } @@ -1948,10 +1953,10 @@ void unregister_sysctl_table(struct ctl_table_header * header) start_unregistering(header); if (!--header->parent->count) { WARN_ON(1); - kfree(header->parent); + call_rcu(&header->parent->rcu, free_head); } if (!--header->count) - kfree(header); + call_rcu(&header->rcu, free_head); spin_unlock(&sysctl_lock); } -- cgit v1.2.3 From df677140281beb608f6748c341af7612f7bfe7a0 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Tue, 8 Mar 2011 08:28:01 +0100 Subject: block: biovec_slab vs. CONFIG_BLK_DEV_INTEGRITY The block integrity subsystem no longer uses the bio_vec slabs so this code can safely be compiled in. Signed-off-by: Martin K. Petersen Signed-off-by: Jens Axboe --- fs/bio.c | 4 +--- include/linux/bio.h | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'include') diff --git a/fs/bio.c b/fs/bio.c index 4bd454fa844e..5694b756ed01 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -43,7 +43,7 @@ static mempool_t *bio_split_pool __read_mostly; * unsigned short */ #define BV(x) { .nr_vecs = x, .name = "biovec-"__stringify(x) } -struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly = { +static struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly = { BV(1), BV(4), BV(16), BV(64), BV(128), BV(BIO_MAX_PAGES), }; #undef BV @@ -1656,12 +1656,10 @@ static void __init biovec_init_slabs(void) int size; struct biovec_slab *bvs = bvec_slabs + i; -#ifndef CONFIG_BLK_DEV_INTEGRITY if (bvs->nr_vecs <= BIO_INLINE_VECS) { bvs->slab = NULL; continue; } -#endif size = bvs->nr_vecs * sizeof(struct bio_vec); bvs->slab = kmem_cache_create(bvs->name, size, 0, diff --git a/include/linux/bio.h b/include/linux/bio.h index 35dcdb3589bc..ce33e6868a2f 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -304,7 +304,6 @@ struct biovec_slab { }; extern struct bio_set *fs_bio_set; -extern struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly; /* * a small number of entries is fine, not going to be performance critical. -- cgit v1.2.3 From 30b542ef453e6832ff682170b2db95d7bca2fe70 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 30 Jan 2011 18:37:33 +0200 Subject: UBI: incorporate maximum write size Incorporate MTD write buffer size into UBI device information because UBIFS needs this field. UBI does not use it ATM, just provides to upper layers in 'struct ubi_device_info'. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 14 ++++++++++++++ drivers/mtd/ubi/kapi.c | 1 + drivers/mtd/ubi/ubi.h | 3 +++ include/linux/mtd/ubi.h | 19 +++++++++++++++++++ 4 files changed, 37 insertions(+) (limited to 'include') diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 5ebe280225d6..f38e8de81811 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -690,11 +690,25 @@ static int io_init(struct ubi_device *ubi) ubi_assert(ubi->hdrs_min_io_size <= ubi->min_io_size); ubi_assert(ubi->min_io_size % ubi->hdrs_min_io_size == 0); + ubi->max_write_size = ubi->mtd->writebufsize; + /* + * Maximum write size has to be greater or equivalent to min. I/O + * size, and be multiple of min. I/O size. + */ + if (ubi->max_write_size < ubi->min_io_size || + ubi->max_write_size % ubi->min_io_size || + !is_power_of_2(ubi->max_write_size)) { + ubi_err("bad write buffer size %d for %d min. I/O unit", + ubi->max_write_size, ubi->min_io_size); + return -EINVAL; + } + /* Calculate default aligned sizes of EC and VID headers */ ubi->ec_hdr_alsize = ALIGN(UBI_EC_HDR_SIZE, ubi->hdrs_min_io_size); ubi->vid_hdr_alsize = ALIGN(UBI_VID_HDR_SIZE, ubi->hdrs_min_io_size); dbg_msg("min_io_size %d", ubi->min_io_size); + dbg_msg("max_write_size %d", ubi->max_write_size); dbg_msg("hdrs_min_io_size %d", ubi->hdrs_min_io_size); dbg_msg("ec_hdr_alsize %d", ubi->ec_hdr_alsize); dbg_msg("vid_hdr_alsize %d", ubi->vid_hdr_alsize); diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 69fa4ef03c53..701df4f848f6 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -41,6 +41,7 @@ void ubi_do_get_device_info(struct ubi_device *ubi, struct ubi_device_info *di) di->ubi_num = ubi->ubi_num; di->leb_size = ubi->leb_size; di->min_io_size = ubi->min_io_size; + di->max_write_size = ubi->max_write_size; di->ro_mode = ubi->ro_mode; di->cdev = ubi->cdev.dev; } diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 0b0149c41fe3..b78994330ebc 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -381,6 +381,8 @@ struct ubi_wl_entry; * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or * not * @nor_flash: non-zero if working on top of NOR flash + * @max_write_size: maximum amount of bytes the underlying flash can write at a + * time (MTD write buffer size) * @mtd: MTD device descriptor * * @peb_buf1: a buffer of PEB size used for different purposes @@ -464,6 +466,7 @@ struct ubi_device { int vid_hdr_shift; unsigned int bad_allowed:1; unsigned int nor_flash:1; + int max_write_size; struct mtd_info *mtd; void *peb_buf1; diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h index b31bd9e9bca3..36c70593ae62 100644 --- a/include/linux/mtd/ubi.h +++ b/include/linux/mtd/ubi.h @@ -117,17 +117,36 @@ struct ubi_volume_info { * @ubi_num: ubi device number * @leb_size: logical eraseblock size on this UBI device * @min_io_size: minimal I/O unit size + * @max_write_size: maximum amount of bytes the underlying flash can write at a + * time (MTD write buffer size) * @ro_mode: if this device is in read-only mode * @cdev: UBI character device major and minor numbers * * Note, @leb_size is the logical eraseblock size offered by the UBI device. * Volumes of this UBI device may have smaller logical eraseblock size if their * alignment is not equivalent to %1. + * + * The @max_write_size field describes flash write maximum write unit. For + * example, NOR flash allows for changing individual bytes, so @min_io_size is + * %1. However, it does not mean than NOR flash has to write data byte-by-byte. + * Instead, CFI NOR flashes have a write-buffer of, e.g., 64 bytes, and when + * writing large chunks of data, they write 64-bytes at a time. Obviously, this + * improves write throughput. + * + * Also, the MTD device may have N interleaved (striped) flash chips + * underneath, in which case @min_io_size can be physical min. I/O size of + * single flash chip, while @max_write_size can be N * @min_io_size. + * + * The @max_write_size field is always greater or equivalent to @min_io_size. + * E.g., some NOR flashes may have (@min_io_size = 1, @max_write_size = 64). In + * contrast, NAND flashes usually have @min_io_size = @max_write_size = NAND + * page size. */ struct ubi_device_info { int ubi_num; int leb_size; int min_io_size; + int max_write_size; int ro_mode; dev_t cdev; }; -- cgit v1.2.3 From f43ec882b8b65de0ebde2e1ad52e8de0349d83ae Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 14 Feb 2011 13:36:54 +0200 Subject: UBI: provide LEB offset information Provide the LEB offset information in the UBI device information data structure. This piece of information is required by UBIFS to find out what are the LEB offsets which are aligned to the max. write size. If LEB offset not aligned to max. write size, then UBIFS has to take this into account to write more optimally. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/kapi.c | 1 + include/linux/mtd/ubi.h | 3 +++ 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 701df4f848f6..d39716e5b204 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -40,6 +40,7 @@ void ubi_do_get_device_info(struct ubi_device *ubi, struct ubi_device_info *di) { di->ubi_num = ubi->ubi_num; di->leb_size = ubi->leb_size; + di->leb_start = ubi->leb_start; di->min_io_size = ubi->min_io_size; di->max_write_size = ubi->max_write_size; di->ro_mode = ubi->ro_mode; diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h index 36c70593ae62..84854edf4436 100644 --- a/include/linux/mtd/ubi.h +++ b/include/linux/mtd/ubi.h @@ -116,6 +116,8 @@ struct ubi_volume_info { * struct ubi_device_info - UBI device description data structure. * @ubi_num: ubi device number * @leb_size: logical eraseblock size on this UBI device + * @leb_start: starting offset of logical eraseblocks within physical + * eraseblocks * @min_io_size: minimal I/O unit size * @max_write_size: maximum amount of bytes the underlying flash can write at a * time (MTD write buffer size) @@ -145,6 +147,7 @@ struct ubi_volume_info { struct ubi_device_info { int ubi_num; int leb_size; + int leb_start; int min_io_size; int max_write_size; int ro_mode; -- cgit v1.2.3 From 997772884036e6e121de39322179989154437d9f Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Mon, 7 Mar 2011 09:58:33 +0100 Subject: debugobjects: Add hint for better object identification In complex subsystems like mac80211 structures can contain several timers and work structs, so identifying a specific instance from the call trace and object type output of debugobjects can be hard. Allow the subsystems which support debugobjects to provide a hint function. This function returns a pointer to a kernel address (preferrably the objects callback function) which is printed along with the debugobjects type. Add hint methods for timer_list, work_struct and hrtimer. [ tglx: Massaged changelog, made it compile ] Signed-off-by: Stanislaw Gruszka LKML-Reference: <20110307085809.GA9334@redhat.com> Signed-off-by: Thomas Gleixner --- include/linux/debugobjects.h | 5 ++++- kernel/hrtimer.c | 6 ++++++ kernel/timer.c | 6 ++++++ kernel/workqueue.c | 6 ++++++ lib/debugobjects.c | 9 ++++++--- 5 files changed, 28 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/debugobjects.h b/include/linux/debugobjects.h index 597692f1fc8d..65970b811e22 100644 --- a/include/linux/debugobjects.h +++ b/include/linux/debugobjects.h @@ -34,7 +34,10 @@ struct debug_obj { /** * struct debug_obj_descr - object type specific debug description structure + * * @name: name of the object typee + * @debug_hint: function returning address, which have associated + * kernel symbol, to allow identify the object * @fixup_init: fixup function, which is called when the init check * fails * @fixup_activate: fixup function, which is called when the activate check @@ -46,7 +49,7 @@ struct debug_obj { */ struct debug_obj_descr { const char *name; - + void *(*debug_hint) (void *addr); int (*fixup_init) (void *addr, enum debug_obj_state state); int (*fixup_activate) (void *addr, enum debug_obj_state state); int (*fixup_destroy) (void *addr, enum debug_obj_state state); diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 0c8d7c048615..e38f5a073d01 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -334,6 +334,11 @@ EXPORT_SYMBOL_GPL(ktime_add_safe); static struct debug_obj_descr hrtimer_debug_descr; +static void *hrtimer_debug_hint(void *addr) +{ + return ((struct hrtimer *) addr)->function; +} + /* * fixup_init is called when: * - an active object is initialized @@ -393,6 +398,7 @@ static int hrtimer_fixup_free(void *addr, enum debug_obj_state state) static struct debug_obj_descr hrtimer_debug_descr = { .name = "hrtimer", + .debug_hint = hrtimer_debug_hint, .fixup_init = hrtimer_fixup_init, .fixup_activate = hrtimer_fixup_activate, .fixup_free = hrtimer_fixup_free, diff --git a/kernel/timer.c b/kernel/timer.c index d6459923d245..33a67925d900 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -404,6 +404,11 @@ static void timer_stats_account_timer(struct timer_list *timer) {} static struct debug_obj_descr timer_debug_descr; +static void *timer_debug_hint(void *addr) +{ + return ((struct timer_list *) addr)->function; +} + /* * fixup_init is called when: * - an active object is initialized @@ -477,6 +482,7 @@ static int timer_fixup_free(void *addr, enum debug_obj_state state) static struct debug_obj_descr timer_debug_descr = { .name = "timer_list", + .debug_hint = timer_debug_hint, .fixup_init = timer_fixup_init, .fixup_activate = timer_fixup_activate, .fixup_free = timer_fixup_free, diff --git a/kernel/workqueue.c b/kernel/workqueue.c index ee6578b578ad..b5fe4c00eb3c 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -316,6 +316,11 @@ static inline int __next_wq_cpu(int cpu, const struct cpumask *mask, static struct debug_obj_descr work_debug_descr; +static void *work_debug_hint(void *addr) +{ + return ((struct work_struct *) addr)->func; +} + /* * fixup_init is called when: * - an active object is initialized @@ -387,6 +392,7 @@ static int work_fixup_free(void *addr, enum debug_obj_state state) static struct debug_obj_descr work_debug_descr = { .name = "work_struct", + .debug_hint = work_debug_hint, .fixup_init = work_fixup_init, .fixup_activate = work_fixup_activate, .fixup_free = work_fixup_free, diff --git a/lib/debugobjects.c b/lib/debugobjects.c index deebcc57d4e6..9d86e45086f5 100644 --- a/lib/debugobjects.c +++ b/lib/debugobjects.c @@ -249,14 +249,17 @@ static struct debug_bucket *get_bucket(unsigned long addr) static void debug_print_object(struct debug_obj *obj, char *msg) { + struct debug_obj_descr *descr = obj->descr; static int limit; - if (limit < 5 && obj->descr != descr_test) { + if (limit < 5 && descr != descr_test) { + void *hint = descr->debug_hint ? + descr->debug_hint(obj->object) : NULL; limit++; WARN(1, KERN_ERR "ODEBUG: %s %s (active state %u) " - "object type: %s\n", + "object type: %s hint: %pS\n", msg, obj_states[obj->state], obj->astate, - obj->descr->name); + descr->name, hint); } debug_objects_warnings++; } -- cgit v1.2.3 From ea7145477a461e09d8d194cac4b996dc4f449107 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 7 Mar 2011 19:10:39 +0100 Subject: x86: Separate out entry text section Put x86 entry code into a separate link section: .entry.text. Separating the entry text section seems to have performance benefits - caused by more efficient instruction cache usage. Running hackbench with perf stat --repeat showed that the change compresses the icache footprint. The icache load miss rate went down by about 15%: before patch: 19417627 L1-icache-load-misses ( +- 0.147% ) after patch: 16490788 L1-icache-load-misses ( +- 0.180% ) The motivation of the patch was to fix a particular kprobes bug that relates to the entry text section, the performance advantage was discovered accidentally. Whole perf output follows: - results for current tip tree: Performance counter stats for './hackbench/hackbench 10' (500 runs): 19417627 L1-icache-load-misses ( +- 0.147% ) 2676914223 instructions # 0.497 IPC ( +- 0.079% ) 5389516026 cycles ( +- 0.144% ) 0.206267711 seconds time elapsed ( +- 0.138% ) - results for current tip tree with the patch applied: Performance counter stats for './hackbench/hackbench 10' (500 runs): 16490788 L1-icache-load-misses ( +- 0.180% ) 2717734941 instructions # 0.502 IPC ( +- 0.079% ) 5414756975 cycles ( +- 0.148% ) 0.206747566 seconds time elapsed ( +- 0.137% ) Signed-off-by: Jiri Olsa Cc: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Cc: Peter Zijlstra Cc: Linus Torvalds Cc: Andrew Morton Cc: Nick Piggin Cc: Eric Dumazet Cc: masami.hiramatsu.pt@hitachi.com Cc: ananth@in.ibm.com Cc: davem@davemloft.net Cc: 2nddept-manager@sdl.hitachi.co.jp LKML-Reference: <20110307181039.GB15197@jolsa.redhat.com> Signed-off-by: Ingo Molnar --- arch/x86/ia32/ia32entry.S | 2 ++ arch/x86/kernel/entry_32.S | 6 ++++-- arch/x86/kernel/entry_64.S | 6 ++++-- arch/x86/kernel/vmlinux.lds.S | 1 + include/asm-generic/sections.h | 1 + include/asm-generic/vmlinux.lds.h | 6 ++++++ 6 files changed, 18 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 518bb99c3394..f729b2e9679c 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -25,6 +25,8 @@ #define sysretl_audit ia32_ret_from_sys_call #endif + .section .entry.text, "ax" + #define IA32_NR_syscalls ((ia32_syscall_end - ia32_sys_call_table)/8) .macro IA32_ARG_FIXUP noebp=0 diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index c8b4efad7ebb..f5accf8eaa78 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -65,6 +65,8 @@ #define sysexit_audit syscall_exit_work #endif + .section .entry.text, "ax" + /* * We use macros for low-level operations which need to be overridden * for paravirtualization. The following will never clobber any registers: @@ -788,7 +790,7 @@ ENDPROC(ptregs_clone) */ .section .init.rodata,"a" ENTRY(interrupt) -.text +.section .entry.text, "ax" .p2align 5 .p2align CONFIG_X86_L1_CACHE_SHIFT ENTRY(irq_entries_start) @@ -807,7 +809,7 @@ vector=FIRST_EXTERNAL_VECTOR .endif .previous .long 1b - .text + .section .entry.text, "ax" vector=vector+1 .endif .endr diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index aed1ffbeb0c9..0a0ed794edb2 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -61,6 +61,8 @@ #define __AUDIT_ARCH_LE 0x40000000 .code64 + .section .entry.text, "ax" + #ifdef CONFIG_FUNCTION_TRACER #ifdef CONFIG_DYNAMIC_FTRACE ENTRY(mcount) @@ -744,7 +746,7 @@ END(stub_rt_sigreturn) */ .section .init.rodata,"a" ENTRY(interrupt) - .text + .section .entry.text .p2align 5 .p2align CONFIG_X86_L1_CACHE_SHIFT ENTRY(irq_entries_start) @@ -763,7 +765,7 @@ vector=FIRST_EXTERNAL_VECTOR .endif .previous .quad 1b - .text + .section .entry.text vector=vector+1 .endif .endr diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S index bf4700755184..6d4341d5c52a 100644 --- a/arch/x86/kernel/vmlinux.lds.S +++ b/arch/x86/kernel/vmlinux.lds.S @@ -105,6 +105,7 @@ SECTIONS SCHED_TEXT LOCK_TEXT KPROBES_TEXT + ENTRY_TEXT IRQENTRY_TEXT *(.fixup) *(.gnu.warning) diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h index b3bfabc258f3..c1a1216e29ce 100644 --- a/include/asm-generic/sections.h +++ b/include/asm-generic/sections.h @@ -11,6 +11,7 @@ extern char _sinittext[], _einittext[]; extern char _end[]; extern char __per_cpu_load[], __per_cpu_start[], __per_cpu_end[]; extern char __kprobes_text_start[], __kprobes_text_end[]; +extern char __entry_text_start[], __entry_text_end[]; extern char __initdata_begin[], __initdata_end[]; extern char __start_rodata[], __end_rodata[]; diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index fe77e3395b40..906c3ceca9a2 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -424,6 +424,12 @@ *(.kprobes.text) \ VMLINUX_SYMBOL(__kprobes_text_end) = .; +#define ENTRY_TEXT \ + ALIGN_FUNCTION(); \ + VMLINUX_SYMBOL(__entry_text_start) = .; \ + *(.entry.text) \ + VMLINUX_SYMBOL(__entry_text_end) = .; + #ifdef CONFIG_FUNCTION_GRAPH_TRACER #define IRQENTRY_TEXT \ ALIGN_FUNCTION(); \ -- cgit v1.2.3 From 89b95ac09e408b5d88a8b3792dc76c863e45fb31 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 7 Mar 2011 16:38:44 +0000 Subject: ASoC: Add DAPM widget and path data to CODEC driver structure Allow a slight simplification of CODEC drivers by allowing DAPM routes and widgets to be provided in a table. They will be instantiated at the end of CODEC probe. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 6 ++++++ sound/soc/soc-core.c | 12 ++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 9c2a6dd170f1..6f197589b6d7 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -562,6 +562,12 @@ struct snd_soc_codec_driver { pm_message_t state); int (*resume)(struct snd_soc_codec *); + /* Default DAPM setup, added after probe() is run */ + const struct snd_soc_dapm_widget *dapm_widgets; + int num_dapm_widgets; + const struct snd_soc_dapm_route *dapm_routes; + int num_dapm_routes; + /* codec IO */ unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index be34f6b95386..c12f2bd23a4e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1464,6 +1464,7 @@ static int soc_probe_codec(struct snd_soc_card *card, struct snd_soc_codec *codec) { int ret = 0; + const struct snd_soc_codec_driver *driver = codec->driver; codec->card = card; codec->dapm.card = card; @@ -1472,8 +1473,8 @@ static int soc_probe_codec(struct snd_soc_card *card, if (!try_module_get(codec->dev->driver->owner)) return -ENODEV; - if (codec->driver->probe) { - ret = codec->driver->probe(codec); + if (driver->probe) { + ret = driver->probe(codec); if (ret < 0) { dev_err(codec->dev, "asoc: failed to probe CODEC %s: %d\n", @@ -1482,6 +1483,13 @@ static int soc_probe_codec(struct snd_soc_card *card, } } + if (driver->dapm_widgets) + snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets, + driver->num_dapm_widgets); + if (driver->dapm_routes) + snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes, + driver->num_dapm_routes); + soc_init_codec_debugfs(codec); /* mark codec as probed and add to card codec list */ -- cgit v1.2.3 From ec4ee52a8f5fb5b8e235ae9f02589d60d54740cc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 7 Mar 2011 20:58:11 +0000 Subject: ASoC: Provide CODEC clocking operations and API calls When multi component systems use DAIless amplifiers which require clocking configuration it is at best hard to use the current clocking API as this requires a DAI even though the device may not even have one. Address this by adding set_sysclk() and set_pll() operations and APIs for CODECs. In order to avoid issues with devices which could be used either with or without DAIs make the DAI variants call through to their CODEC counterparts if there is no DAI specific operation. Converting over entirely would create problems for multi-DAI devices which offer per-DAI clocking setup. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 11 +++++++++++ sound/soc/soc-core.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 6f197589b6d7..14f601f3e189 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -259,6 +259,11 @@ enum snd_soc_compress_type { SND_SOC_RBTREE_COMPRESSION }; +int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id, + unsigned int freq, int dir); +int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out); + 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); @@ -568,6 +573,12 @@ struct snd_soc_codec_driver { const struct snd_soc_dapm_route *dapm_routes; int num_dapm_routes; + /* codec wide operations */ + int (*set_sysclk)(struct snd_soc_codec *codec, + int clk_id, unsigned int freq, int dir); + int (*set_pll)(struct snd_soc_codec *codec, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out); + /* codec IO */ unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c12f2bd23a4e..c2ec6cb05631 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3064,11 +3064,33 @@ int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, { if (dai->driver && dai->driver->ops->set_sysclk) return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); + else if (dai->codec && dai->codec->driver->set_sysclk) + return dai->codec->driver->set_sysclk(dai->codec, clk_id, + freq, dir); else return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); +/** + * snd_soc_codec_set_sysclk - configure CODEC system or master clock. + * @codec: CODEC + * @clk_id: DAI specific clock ID + * @freq: new clock frequency in Hz + * @dir: new clock direction - input/output. + * + * Configures the CODEC master (MCLK) or system (SYSCLK) clocking. + */ +int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id, + unsigned int freq, int dir) +{ + if (codec->driver->set_sysclk) + return codec->driver->set_sysclk(codec, clk_id, freq, dir); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk); + /** * snd_soc_dai_set_clkdiv - configure DAI clock dividers. * @dai: DAI @@ -3105,11 +3127,35 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, if (dai->driver && dai->driver->ops->set_pll) return dai->driver->ops->set_pll(dai, pll_id, source, freq_in, freq_out); + else if (dai->codec && dai->codec->driver->set_pll) + return dai->codec->driver->set_pll(dai->codec, pll_id, source, + freq_in, freq_out); else return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll); +/* + * snd_soc_codec_set_pll - configure codec PLL. + * @codec: CODEC + * @pll_id: DAI specific PLL ID + * @source: DAI specific source for the PLL + * @freq_in: PLL input clock frequency in Hz + * @freq_out: requested PLL output clock frequency in Hz + * + * Configures and enables PLL to generate output clock based on input clock. + */ +int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + if (codec->driver->set_pll) + return codec->driver->set_pll(codec, pll_id, source, + freq_in, freq_out); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll); + /** * snd_soc_dai_set_fmt - configure DAI hardware audio format. * @dai: DAI -- cgit v1.2.3 From efb7ac3f9c28fcb379c51f987b63174f727b7453 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 8 Mar 2011 17:23:24 +0000 Subject: ASoC: Fix prefixing of DAPM controls by factoring prefix into snd_soc_cnew() Currently will ignore prefixes when creating DAPM controls. Since currently all control creation goes through snd_soc_cnew() we can fix this by factoring the prefixing into that function. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 3 ++- sound/soc/soc-core.c | 45 +++++++++++++++++++++++++++++++-------------- sound/soc/soc-dapm.c | 16 ++++++++++++++-- 3 files changed, 47 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 14f601f3e189..bfa4836ea107 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -340,7 +340,8 @@ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec); *Controls */ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, - void *data, char *long_name); + void *data, char *long_name, + const char *prefix); int snd_soc_add_controls(struct snd_soc_codec *codec, const struct snd_kcontrol_new *controls, int num_controls); int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index db3075dd11fe..17efacdb248a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2344,22 +2344,45 @@ EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams); * @_template: control template * @data: control private data * @long_name: control long name + * @prefix: control name prefix * * Create a new mixer control from a template control. * * Returns 0 for success, else error. */ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, - void *data, char *long_name) + void *data, char *long_name, + const char *prefix) { struct snd_kcontrol_new template; + struct snd_kcontrol *kcontrol; + char *name = NULL; + int name_len; memcpy(&template, _template, sizeof(template)); - if (long_name) - template.name = long_name; template.index = 0; - return snd_ctl_new1(&template, data); + if (!long_name) + long_name = template.name; + + if (prefix) { + name_len = strlen(long_name) + strlen(prefix) + 2; + name = kmalloc(name_len, GFP_ATOMIC); + if (!name) + return NULL; + + snprintf(name, name_len, "%s %s", prefix, long_name); + + template.name = name; + } else { + template.name = long_name; + } + + kcontrol = snd_ctl_new1(&template, data); + + kfree(name); + + return kcontrol; } EXPORT_SYMBOL_GPL(snd_soc_cnew); @@ -2378,22 +2401,16 @@ int snd_soc_add_controls(struct snd_soc_codec *codec, const struct snd_kcontrol_new *controls, int num_controls) { struct snd_card *card = codec->card->snd_card; - char prefixed_name[44], *name; int err, i; for (i = 0; i < num_controls; i++) { const struct snd_kcontrol_new *control = &controls[i]; - if (codec->name_prefix) { - snprintf(prefixed_name, sizeof(prefixed_name), "%s %s", - codec->name_prefix, control->name); - name = prefixed_name; - } else { - name = control->name; - } - err = snd_ctl_add(card, snd_soc_cnew(control, codec, name)); + err = snd_ctl_add(card, snd_soc_cnew(control, codec, + control->name, + codec->name_prefix)); if (err < 0) { dev_err(codec->dev, "%s: Failed to add %s: %d\n", - codec->name, name, err); + codec->name, control->name, err); return err; } } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 570db8819d9b..a6fb85d46416 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -369,6 +369,12 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, size_t name_len; struct snd_soc_dapm_path *path; struct snd_card *card = dapm->card->snd_card; + const char *prefix; + + if (dapm->codec) + prefix = dapm->codec->name_prefix; + else + prefix = NULL; /* add kcontrol */ for (i = 0; i < w->num_kcontrols; i++) { @@ -409,7 +415,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, path->long_name[name_len - 1] = '\0'; path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w, - path->long_name); + path->long_name, prefix); ret = snd_ctl_add(card, path->kcontrol); if (ret < 0) { dev_err(dapm->dev, @@ -431,6 +437,7 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_path *path = NULL; struct snd_kcontrol *kcontrol; struct snd_card *card = dapm->card->snd_card; + const char *prefix; int ret = 0; if (!w->num_kcontrols) { @@ -438,7 +445,12 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm, return -EINVAL; } - kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name); + if (dapm->codec) + prefix = dapm->codec->name_prefix; + else + prefix = NULL; + + kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name, prefix); ret = snd_ctl_add(card, kcontrol); if (ret < 0) -- cgit v1.2.3 From a7ac8fc1d8d26c975c460a69aa7b9d5b5d5d29b0 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Mar 2011 11:03:21 -0800 Subject: ipv4: Fix scope value used in route src-address caching. We have to use cfg->fc_scope not the final nh_scope value. Reported-by: Julian Anastasov Signed-off-by: David S. Miller --- include/net/ip_fib.h | 1 + net/ipv4/fib_semantics.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 0e140830b85a..3f6c943faedc 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -51,6 +51,7 @@ struct fib_nh { struct fib_info *nh_parent; unsigned nh_flags; unsigned char nh_scope; + unsigned char nh_cfg_scope; #ifdef CONFIG_IP_ROUTE_MULTIPATH int nh_weight; int nh_power; diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 952c737f2a27..d73d7581b51f 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -854,9 +854,10 @@ struct fib_info *fib_create_info(struct fib_config *cfg) } change_nexthops(fi) { + nexthop_nh->nh_cfg_scope = cfg->fc_scope; nexthop_nh->nh_saddr = inet_select_addr(nexthop_nh->nh_dev, nexthop_nh->nh_gw, - nexthop_nh->nh_scope); + nexthop_nh->nh_cfg_scope); } endfor_nexthops(fi) link_it: @@ -1141,7 +1142,7 @@ void fib_update_nh_saddrs(struct net_device *dev) continue; nh->nh_saddr = inet_select_addr(nh->nh_dev, nh->nh_gw, - nh->nh_scope); + nh->nh_cfg_scope); } } -- cgit v1.2.3 From 51de69523ffe1c17994dc2f260369f29dfdce71c Mon Sep 17 00:00:00 2001 From: Owen Smith Date: Wed, 22 Dec 2010 15:05:00 +0000 Subject: xen: Union the blkif_request request specific fields Prepare for extending the block device ring to allow request specific fields, by moving the request specific fields for reads, writes and barrier requests to a union member. Acked-by: Jens Axboe Signed-off-by: Owen Smith Signed-off-by: Konrad Rzeszutek Wilk --- drivers/block/xen-blkfront.c | 8 ++++---- include/xen/interface/io/blkif.h | 16 +++++++++++----- 2 files changed, 15 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index d7aa39e349a6..cc4514c9d8a6 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -281,7 +281,7 @@ static int blkif_queue_request(struct request *req) info->shadow[id].request = req; ring_req->id = id; - ring_req->sector_number = (blkif_sector_t)blk_rq_pos(req); + ring_req->u.rw.sector_number = (blkif_sector_t)blk_rq_pos(req); ring_req->handle = info->handle; ring_req->operation = rq_data_dir(req) ? @@ -317,7 +317,7 @@ static int blkif_queue_request(struct request *req) rq_data_dir(req) ); info->shadow[id].frame[i] = mfn_to_pfn(buffer_mfn); - ring_req->seg[i] = + ring_req->u.rw.seg[i] = (struct blkif_request_segment) { .gref = ref, .first_sect = fsect, @@ -615,7 +615,7 @@ static void blkif_completion(struct blk_shadow *s) { int i; for (i = 0; i < s->req.nr_segments; i++) - gnttab_end_foreign_access(s->req.seg[i].gref, 0, 0UL); + gnttab_end_foreign_access(s->req.u.rw.seg[i].gref, 0, 0UL); } static irqreturn_t blkif_interrupt(int irq, void *dev_id) @@ -932,7 +932,7 @@ static int blkif_recover(struct blkfront_info *info) /* Rewrite any grant references invalidated by susp/resume. */ for (j = 0; j < req->nr_segments; j++) gnttab_grant_foreign_access_ref( - req->seg[j].gref, + req->u.rw.seg[j].gref, info->xbdev->otherend_id, pfn_to_mfn(info->shadow[req->id].frame[j]), rq_data_dir(info->shadow[req->id].request)); diff --git a/include/xen/interface/io/blkif.h b/include/xen/interface/io/blkif.h index c2d1fa4dc1ee..e4f743cfa151 100644 --- a/include/xen/interface/io/blkif.h +++ b/include/xen/interface/io/blkif.h @@ -51,11 +51,7 @@ typedef uint64_t blkif_sector_t; */ #define BLKIF_MAX_SEGMENTS_PER_REQUEST 11 -struct blkif_request { - uint8_t operation; /* BLKIF_OP_??? */ - uint8_t nr_segments; /* number of segments */ - blkif_vdev_t handle; /* only for read/write requests */ - uint64_t id; /* private guest value, echoed in resp */ +struct blkif_request_rw { blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ struct blkif_request_segment { grant_ref_t gref; /* reference to I/O buffer frame */ @@ -65,6 +61,16 @@ struct blkif_request { } seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; }; +struct blkif_request { + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t id; /* private guest value, echoed in resp */ + union { + struct blkif_request_rw rw; + } u; +}; + struct blkif_response { uint64_t id; /* copied from request */ uint8_t operation; /* copied from request */ -- cgit v1.2.3 From 750912fa366312e9c5bc83eab352898a26750401 Mon Sep 17 00:00:00 2001 From: David Sharp Date: Wed, 8 Dec 2010 13:46:47 -0800 Subject: tracing: Add an 'overwrite' trace_option. Add an "overwrite" trace_option for ftrace to control whether the buffer should be overwritten on overflow or not. The default remains to overwrite old events when the buffer is full. This patch adds the option to instead discard newest events when the buffer is full. This is useful to get a snapshot of traces just after enabling traces. Dropping the current event is also a simpler code path. Signed-off-by: David Sharp LKML-Reference: <1291844807-15481-1-git-send-email-dhsharp@google.com> Signed-off-by: Steven Rostedt --- Documentation/trace/ftrace.txt | 5 +++++ include/linux/ring_buffer.h | 2 ++ kernel/trace/ring_buffer.c | 11 +++++++++++ kernel/trace/trace.c | 17 +++++++++++------ kernel/trace/trace.h | 1 + 5 files changed, 30 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt index 67f1cc473257..1ebc24cf9a55 100644 --- a/Documentation/trace/ftrace.txt +++ b/Documentation/trace/ftrace.txt @@ -454,6 +454,11 @@ x494] <- /root/a.out[+0x4a8] <- /lib/libc-2.7.so[+0x1e1a6] latencies, as described in "Latency trace format". + overwrite - This controls what happens when the trace buffer is + full. If "1" (default), the oldest events are + discarded and overwritten. If "0", then the newest + events are discarded. + ftrace_enabled -------------- diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h index 8d3a2486544d..ab38ac80b0f9 100644 --- a/include/linux/ring_buffer.h +++ b/include/linux/ring_buffer.h @@ -100,6 +100,8 @@ void ring_buffer_free(struct ring_buffer *buffer); int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size); +void ring_buffer_change_overwrite(struct ring_buffer *buffer, int val); + struct ring_buffer_event *ring_buffer_lock_reserve(struct ring_buffer *buffer, unsigned long length); int ring_buffer_unlock_commit(struct ring_buffer *buffer, diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index bd1c35a4fbcc..269db80a961e 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -1429,6 +1429,17 @@ int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size) } EXPORT_SYMBOL_GPL(ring_buffer_resize); +void ring_buffer_change_overwrite(struct ring_buffer *buffer, int val) +{ + mutex_lock(&buffer->mutex); + if (val) + buffer->flags |= RB_FL_OVERWRITE; + else + buffer->flags &= ~RB_FL_OVERWRITE; + mutex_unlock(&buffer->mutex); +} +EXPORT_SYMBOL_GPL(ring_buffer_change_overwrite); + static inline void * __rb_data_page_index(struct buffer_data_page *bpage, unsigned index) { diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 8dc8da6733f9..85e3ee1e474e 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -41,8 +41,6 @@ #include "trace.h" #include "trace_output.h" -#define TRACE_BUFFER_FLAGS (RB_FL_OVERWRITE) - /* * On boot up, the ring buffer is set to the minimum size, so that * we do not waste memory on systems that are not using tracing. @@ -340,7 +338,7 @@ static DECLARE_WAIT_QUEUE_HEAD(trace_wait); /* trace_flags holds trace_options default values */ unsigned long trace_flags = TRACE_ITER_PRINT_PARENT | TRACE_ITER_PRINTK | TRACE_ITER_ANNOTATE | TRACE_ITER_CONTEXT_INFO | TRACE_ITER_SLEEP_TIME | - TRACE_ITER_GRAPH_TIME | TRACE_ITER_RECORD_CMD; + TRACE_ITER_GRAPH_TIME | TRACE_ITER_RECORD_CMD | TRACE_ITER_OVERWRITE; static int trace_stop_count; static DEFINE_SPINLOCK(tracing_start_lock); @@ -425,6 +423,7 @@ static const char *trace_options[] = { "sleep-time", "graph-time", "record-cmd", + "overwrite", NULL }; @@ -2529,6 +2528,9 @@ static void set_tracer_flags(unsigned int mask, int enabled) if (mask == TRACE_ITER_RECORD_CMD) trace_event_enable_cmd_record(enabled); + + if (mask == TRACE_ITER_OVERWRITE) + ring_buffer_change_overwrite(global_trace.buffer, enabled); } static ssize_t @@ -4555,9 +4557,11 @@ void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) __init static int tracer_alloc_buffers(void) { int ring_buf_size; + enum ring_buffer_flags rb_flags; int i; int ret = -ENOMEM; + if (!alloc_cpumask_var(&tracing_buffer_mask, GFP_KERNEL)) goto out; @@ -4570,12 +4574,13 @@ __init static int tracer_alloc_buffers(void) else ring_buf_size = 1; + rb_flags = trace_flags & TRACE_ITER_OVERWRITE ? RB_FL_OVERWRITE : 0; + cpumask_copy(tracing_buffer_mask, cpu_possible_mask); cpumask_copy(tracing_cpumask, cpu_all_mask); /* TODO: make the number of buffers hot pluggable with CPUS */ - global_trace.buffer = ring_buffer_alloc(ring_buf_size, - TRACE_BUFFER_FLAGS); + global_trace.buffer = ring_buffer_alloc(ring_buf_size, rb_flags); if (!global_trace.buffer) { printk(KERN_ERR "tracer: failed to allocate ring buffer!\n"); WARN_ON(1); @@ -4585,7 +4590,7 @@ __init static int tracer_alloc_buffers(void) #ifdef CONFIG_TRACER_MAX_TRACE - max_tr.buffer = ring_buffer_alloc(1, TRACE_BUFFER_FLAGS); + max_tr.buffer = ring_buffer_alloc(1, rb_flags); if (!max_tr.buffer) { printk(KERN_ERR "tracer: failed to allocate max ring buffer!\n"); WARN_ON(1); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 856e73cc1d3f..951d0b7e7062 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -606,6 +606,7 @@ enum trace_iterator_flags { TRACE_ITER_SLEEP_TIME = 0x40000, TRACE_ITER_GRAPH_TIME = 0x80000, TRACE_ITER_RECORD_CMD = 0x100000, + TRACE_ITER_OVERWRITE = 0x200000, }; /* -- cgit v1.2.3 From f44f7f96a20af16f6f12e1c995576d6becf5f57b Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 21 Feb 2011 22:58:51 -0800 Subject: RTC: Initialize kernel state from RTC Mark Brown pointed out a corner case: that RTC alarms should be allowed to be persistent across reboots if the hardware supported it. The rework of the generic layer to virtualize the RTC alarm virtualized much of the alarm handling, and removed the code used to read the alarm time from the hardware. Mark noted if we want the alarm to be persistent across reboots, we need to re-read the alarm value into the virtualized generic layer at boot up, so that the generic layer properly exposes that value. This patch restores much of the earlier removed rtc_read_alarm code and wires it in so that we set the kernel's alarm value to what we find in the hardware at boot time. NOTE: Not all hardware supports persistent RTC alarm state across system reset. rtc-cmos for example will keep the alarm time, but disables the AIE mode irq. Applications should not expect the RTC alarm to be valid after a system reset. We will preserve what we can, to represent the hardware state at boot, but its not guarenteed. Further, in the future, with multiplexed RTC alarms, the soonest alarm to fire may not be the one set via the /dev/rt ioctls. So an application may set the alarm with RTC_ALM_SET, but after a reset find that RTC_ALM_READ returns an earlier time. Again, we preserve what we can, but applications should not expect the RTC alarm state to persist across a system reset. Big thanks to Mark for pointing out the issue! Thanks also to Marcelo for helping think through the solution. CC: Mark Brown CC: Marcelo Roberto Jimenez CC: Thomas Gleixner CC: Alessandro Zummo CC: rtc-linux@googlegroups.com Reported-by: Mark Brown Signed-off-by: John Stultz --- drivers/rtc/class.c | 7 ++ drivers/rtc/interface.c | 180 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/rtc.h | 1 + 3 files changed, 188 insertions(+) (limited to 'include') diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index c404b61386bf..09b4437b3e61 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -117,6 +117,7 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev, struct module *owner) { struct rtc_device *rtc; + struct rtc_wkalrm alrm; int id, err; if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) { @@ -166,6 +167,12 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev, rtc->pie_timer.function = rtc_pie_update_irq; rtc->pie_enabled = 0; + /* Check to see if there is an ALARM already set in hw */ + err = __rtc_read_alarm(rtc, &alrm); + + if (!err && !rtc_valid_tm(&alrm.time)) + rtc_set_alarm(rtc, &alrm); + strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE); dev_set_name(&rtc->dev, "rtc%d", id); diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index cb2f0728fd70..8ec6b069a7f5 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -116,6 +116,186 @@ int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs) } EXPORT_SYMBOL_GPL(rtc_set_mmss); +static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +{ + int err; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + + if (rtc->ops == NULL) + err = -ENODEV; + else if (!rtc->ops->read_alarm) + err = -EINVAL; + else { + memset(alarm, 0, sizeof(struct rtc_wkalrm)); + err = rtc->ops->read_alarm(rtc->dev.parent, alarm); + } + + mutex_unlock(&rtc->ops_lock); + return err; +} + +int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +{ + int err; + struct rtc_time before, now; + int first_time = 1; + unsigned long t_now, t_alm; + enum { none, day, month, year } missing = none; + unsigned days; + + /* The lower level RTC driver may return -1 in some fields, + * creating invalid alarm->time values, for reasons like: + * + * - The hardware may not be capable of filling them in; + * many alarms match only on time-of-day fields, not + * day/month/year calendar data. + * + * - Some hardware uses illegal values as "wildcard" match + * values, which non-Linux firmware (like a BIOS) may try + * to set up as e.g. "alarm 15 minutes after each hour". + * Linux uses only oneshot alarms. + * + * When we see that here, we deal with it by using values from + * a current RTC timestamp for any missing (-1) values. The + * RTC driver prevents "periodic alarm" modes. + * + * But this can be racey, because some fields of the RTC timestamp + * may have wrapped in the interval since we read the RTC alarm, + * which would lead to us inserting inconsistent values in place + * of the -1 fields. + * + * Reading the alarm and timestamp in the reverse sequence + * would have the same race condition, and not solve the issue. + * + * So, we must first read the RTC timestamp, + * then read the RTC alarm value, + * and then read a second RTC timestamp. + * + * If any fields of the second timestamp have changed + * when compared with the first timestamp, then we know + * our timestamp may be inconsistent with that used by + * the low-level rtc_read_alarm_internal() function. + * + * So, when the two timestamps disagree, we just loop and do + * the process again to get a fully consistent set of values. + * + * This could all instead be done in the lower level driver, + * but since more than one lower level RTC implementation needs it, + * then it's probably best best to do it here instead of there.. + */ + + /* Get the "before" timestamp */ + err = rtc_read_time(rtc, &before); + if (err < 0) + return err; + do { + if (!first_time) + memcpy(&before, &now, sizeof(struct rtc_time)); + first_time = 0; + + /* get the RTC alarm values, which may be incomplete */ + err = rtc_read_alarm_internal(rtc, alarm); + if (err) + return err; + + /* full-function RTCs won't have such missing fields */ + if (rtc_valid_tm(&alarm->time) == 0) + return 0; + + /* get the "after" timestamp, to detect wrapped fields */ + err = rtc_read_time(rtc, &now); + if (err < 0) + return err; + + /* note that tm_sec is a "don't care" value here: */ + } while ( before.tm_min != now.tm_min + || before.tm_hour != now.tm_hour + || before.tm_mon != now.tm_mon + || before.tm_year != now.tm_year); + + /* Fill in the missing alarm fields using the timestamp; we + * know there's at least one since alarm->time is invalid. + */ + if (alarm->time.tm_sec == -1) + alarm->time.tm_sec = now.tm_sec; + if (alarm->time.tm_min == -1) + alarm->time.tm_min = now.tm_min; + if (alarm->time.tm_hour == -1) + alarm->time.tm_hour = now.tm_hour; + + /* For simplicity, only support date rollover for now */ + if (alarm->time.tm_mday == -1) { + alarm->time.tm_mday = now.tm_mday; + missing = day; + } + if (alarm->time.tm_mon == -1) { + alarm->time.tm_mon = now.tm_mon; + if (missing == none) + missing = month; + } + if (alarm->time.tm_year == -1) { + alarm->time.tm_year = now.tm_year; + if (missing == none) + missing = year; + } + + /* with luck, no rollover is needed */ + rtc_tm_to_time(&now, &t_now); + rtc_tm_to_time(&alarm->time, &t_alm); + if (t_now < t_alm) + goto done; + + switch (missing) { + + /* 24 hour rollover ... if it's now 10am Monday, an alarm that + * that will trigger at 5am will do so at 5am Tuesday, which + * could also be in the next month or year. This is a common + * case, especially for PCs. + */ + case day: + dev_dbg(&rtc->dev, "alarm rollover: %s\n", "day"); + t_alm += 24 * 60 * 60; + rtc_time_to_tm(t_alm, &alarm->time); + break; + + /* Month rollover ... if it's the 31th, an alarm on the 3rd will + * be next month. An alarm matching on the 30th, 29th, or 28th + * may end up in the month after that! Many newer PCs support + * this type of alarm. + */ + case month: + dev_dbg(&rtc->dev, "alarm rollover: %s\n", "month"); + do { + if (alarm->time.tm_mon < 11) + alarm->time.tm_mon++; + else { + alarm->time.tm_mon = 0; + alarm->time.tm_year++; + } + days = rtc_month_days(alarm->time.tm_mon, + alarm->time.tm_year); + } while (days < alarm->time.tm_mday); + break; + + /* Year rollover ... easy except for leap years! */ + case year: + dev_dbg(&rtc->dev, "alarm rollover: %s\n", "year"); + do { + alarm->time.tm_year++; + } while (rtc_valid_tm(&alarm->time) != 0); + break; + + default: + dev_warn(&rtc->dev, "alarm rollover not handled\n"); + } + +done: + return 0; +} + int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) { int err; diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 89c3e5182991..db3832d5f280 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -227,6 +227,7 @@ extern void rtc_device_unregister(struct rtc_device *rtc); extern int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm); extern int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm); extern int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs); +int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm); extern int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alrm); extern int rtc_set_alarm(struct rtc_device *rtc, -- cgit v1.2.3 From 80d4bb515b78f38738f3378fd1be6039063ab040 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 3 Feb 2011 11:34:50 -0800 Subject: RTC: Cleanup rtc_class_ops->irq_set_state With PIE mode interrupts now emulated in generic code via an hrtimer, no one calls rtc_class_ops->irq_set_state(), so this patch removes it along with driver implementations. CC: Thomas Gleixner CC: Alessandro Zummo CC: Marcelo Roberto Jimenez CC: rtc-linux@googlegroups.com Signed-off-by: John Stultz --- drivers/rtc/rtc-cmos.c | 20 -------------------- drivers/rtc/rtc-davinci.c | 34 ---------------------------------- drivers/rtc/rtc-mrst.c | 20 -------------------- drivers/rtc/rtc-pl031.c | 34 ---------------------------------- drivers/rtc/rtc-pxa.c | 13 ------------- drivers/rtc/rtc-rx8025.c | 25 ------------------------- drivers/rtc/rtc-s3c.c | 32 -------------------------------- drivers/rtc/rtc-sa1100.c | 18 ------------------ drivers/rtc/rtc-sh.c | 1 - drivers/rtc/rtc-vr41xx.c | 11 ----------- include/linux/rtc.h | 1 - 11 files changed, 209 deletions(-) (limited to 'include') diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index c7ff8df347e7..de632e793d46 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -400,25 +400,6 @@ static int cmos_irq_set_freq(struct device *dev, int freq) return 0; } -static int cmos_irq_set_state(struct device *dev, int enabled) -{ - struct cmos_rtc *cmos = dev_get_drvdata(dev); - unsigned long flags; - - if (!is_valid_irq(cmos->irq)) - return -ENXIO; - - spin_lock_irqsave(&rtc_lock, flags); - - if (enabled) - cmos_irq_enable(cmos, RTC_PIE); - else - cmos_irq_disable(cmos, RTC_PIE); - - spin_unlock_irqrestore(&rtc_lock, flags); - return 0; -} - static int cmos_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct cmos_rtc *cmos = dev_get_drvdata(dev); @@ -502,7 +483,6 @@ static const struct rtc_class_ops cmos_rtc_ops = { .set_alarm = cmos_set_alarm, .proc = cmos_procfs, .irq_set_freq = cmos_irq_set_freq, - .irq_set_state = cmos_irq_set_state, .alarm_irq_enable = cmos_alarm_irq_enable, .update_irq_enable = cmos_update_irq_enable, }; diff --git a/drivers/rtc/rtc-davinci.c b/drivers/rtc/rtc-davinci.c index 34647fc1ee98..92da73d40e13 100644 --- a/drivers/rtc/rtc-davinci.c +++ b/drivers/rtc/rtc-davinci.c @@ -473,39 +473,6 @@ static int davinci_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) return 0; } -static int davinci_rtc_irq_set_state(struct device *dev, int enabled) -{ - struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); - unsigned long flags; - u8 rtc_ctrl; - - spin_lock_irqsave(&davinci_rtc_lock, flags); - - rtc_ctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL); - - if (enabled) { - while (rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL) - & PRTCSS_RTC_CTRL_WDTBUS) - cpu_relax(); - - rtc_ctrl |= PRTCSS_RTC_CTRL_TE; - rtcss_write(davinci_rtc, rtc_ctrl, PRTCSS_RTC_CTRL); - - rtcss_write(davinci_rtc, 0x0, PRTCSS_RTC_CLKC_CNT); - - rtc_ctrl |= PRTCSS_RTC_CTRL_TIEN | - PRTCSS_RTC_CTRL_TMMD | - PRTCSS_RTC_CTRL_TMRFLG; - } else - rtc_ctrl &= ~PRTCSS_RTC_CTRL_TIEN; - - rtcss_write(davinci_rtc, rtc_ctrl, PRTCSS_RTC_CTRL); - - spin_unlock_irqrestore(&davinci_rtc_lock, flags); - - return 0; -} - static int davinci_rtc_irq_set_freq(struct device *dev, int freq) { struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); @@ -529,7 +496,6 @@ static struct rtc_class_ops davinci_rtc_ops = { .alarm_irq_enable = davinci_rtc_alarm_irq_enable, .read_alarm = davinci_rtc_read_alarm, .set_alarm = davinci_rtc_set_alarm, - .irq_set_state = davinci_rtc_irq_set_state, .irq_set_freq = davinci_rtc_irq_set_freq, }; diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c index 1db62db8469d..4db96fa306fc 100644 --- a/drivers/rtc/rtc-mrst.c +++ b/drivers/rtc/rtc-mrst.c @@ -236,25 +236,6 @@ static int mrst_set_alarm(struct device *dev, struct rtc_wkalrm *t) return 0; } -static int mrst_irq_set_state(struct device *dev, int enabled) -{ - struct mrst_rtc *mrst = dev_get_drvdata(dev); - unsigned long flags; - - if (!mrst->irq) - return -ENXIO; - - spin_lock_irqsave(&rtc_lock, flags); - - if (enabled) - mrst_irq_enable(mrst, RTC_PIE); - else - mrst_irq_disable(mrst, RTC_PIE); - - spin_unlock_irqrestore(&rtc_lock, flags); - return 0; -} - /* Currently, the vRTC doesn't support UIE ON/OFF */ static int mrst_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { @@ -301,7 +282,6 @@ static const struct rtc_class_ops mrst_rtc_ops = { .read_alarm = mrst_read_alarm, .set_alarm = mrst_set_alarm, .proc = mrst_procfs, - .irq_set_state = mrst_irq_set_state, .alarm_irq_enable = mrst_rtc_alarm_irq_enable, }; diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index b7a6690e5b35..0e7c15b24c1c 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -293,38 +293,6 @@ static int pl031_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) return ret; } -/* Periodic interrupt is only available in ST variants. */ -static int pl031_irq_set_state(struct device *dev, int enabled) -{ - struct pl031_local *ldata = dev_get_drvdata(dev); - - if (enabled == 1) { - /* Clear any pending timer interrupt. */ - writel(RTC_BIT_PI, ldata->base + RTC_ICR); - - writel(readl(ldata->base + RTC_IMSC) | RTC_BIT_PI, - ldata->base + RTC_IMSC); - - /* Now start the timer */ - writel(readl(ldata->base + RTC_TCR) | RTC_TCR_EN, - ldata->base + RTC_TCR); - - } else { - writel(readl(ldata->base + RTC_IMSC) & (~RTC_BIT_PI), - ldata->base + RTC_IMSC); - - /* Also stop the timer */ - writel(readl(ldata->base + RTC_TCR) & (~RTC_TCR_EN), - ldata->base + RTC_TCR); - } - /* Wait at least 1 RTC32 clock cycle to ensure next access - * to RTC_TCR will succeed. - */ - udelay(40); - - return 0; -} - static int pl031_irq_set_freq(struct device *dev, int freq) { struct pl031_local *ldata = dev_get_drvdata(dev); @@ -440,7 +408,6 @@ static struct rtc_class_ops stv1_pl031_ops = { .read_alarm = pl031_read_alarm, .set_alarm = pl031_set_alarm, .alarm_irq_enable = pl031_alarm_irq_enable, - .irq_set_state = pl031_irq_set_state, .irq_set_freq = pl031_irq_set_freq, }; @@ -451,7 +418,6 @@ static struct rtc_class_ops stv2_pl031_ops = { .read_alarm = pl031_stv2_read_alarm, .set_alarm = pl031_stv2_set_alarm, .alarm_irq_enable = pl031_alarm_irq_enable, - .irq_set_state = pl031_irq_set_state, .irq_set_freq = pl031_irq_set_freq, }; diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c index 29e867a1aaa8..b216ae5389c8 100644 --- a/drivers/rtc/rtc-pxa.c +++ b/drivers/rtc/rtc-pxa.c @@ -223,18 +223,6 @@ static int pxa_periodic_irq_set_freq(struct device *dev, int freq) return 0; } -static int pxa_periodic_irq_set_state(struct device *dev, int enabled) -{ - struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); - - if (enabled) - rtsr_set_bits(pxa_rtc, RTSR_PIALE | RTSR_PICE); - else - rtsr_clear_bits(pxa_rtc, RTSR_PIALE | RTSR_PICE); - - return 0; -} - static int pxa_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); @@ -348,7 +336,6 @@ static const struct rtc_class_ops pxa_rtc_ops = { .alarm_irq_enable = pxa_alarm_irq_enable, .update_irq_enable = pxa_update_irq_enable, .proc = pxa_rtc_proc, - .irq_set_state = pxa_periodic_irq_set_state, .irq_set_freq = pxa_periodic_irq_set_freq, }; diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c index af32a62e12a8..fde172fb2abe 100644 --- a/drivers/rtc/rtc-rx8025.c +++ b/drivers/rtc/rtc-rx8025.c @@ -424,37 +424,12 @@ static int rx8025_alarm_irq_enable(struct device *dev, unsigned int enabled) return 0; } -static int rx8025_irq_set_state(struct device *dev, int enabled) -{ - struct i2c_client *client = to_i2c_client(dev); - struct rx8025_data *rx8025 = i2c_get_clientdata(client); - int ctrl1; - int err; - - if (client->irq <= 0) - return -ENXIO; - - ctrl1 = rx8025->ctrl1 & ~RX8025_BIT_CTRL1_CT; - if (enabled) - ctrl1 |= RX8025_BIT_CTRL1_CT_1HZ; - if (ctrl1 != rx8025->ctrl1) { - rx8025->ctrl1 = ctrl1; - err = rx8025_write_reg(rx8025->client, RX8025_REG_CTRL1, - rx8025->ctrl1); - if (err) - return err; - } - - return 0; -} - static struct rtc_class_ops rx8025_rtc_ops = { .read_time = rx8025_get_time, .set_time = rx8025_set_time, .read_alarm = rx8025_read_alarm, .set_alarm = rx8025_set_alarm, .alarm_irq_enable = rx8025_alarm_irq_enable, - .irq_set_state = rx8025_irq_set_state, }; /* diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index b80fa2882408..80fb7e72f9d9 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -93,37 +93,6 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) return 0; } -static int s3c_rtc_setpie(struct device *dev, int enabled) -{ - unsigned int tmp; - - pr_debug("%s: pie=%d\n", __func__, enabled); - - spin_lock_irq(&s3c_rtc_pie_lock); - - if (s3c_rtc_cpu_type == TYPE_S3C64XX) { - tmp = readw(s3c_rtc_base + S3C2410_RTCCON); - tmp &= ~S3C64XX_RTCCON_TICEN; - - if (enabled) - tmp |= S3C64XX_RTCCON_TICEN; - - writew(tmp, s3c_rtc_base + S3C2410_RTCCON); - } else { - tmp = readb(s3c_rtc_base + S3C2410_TICNT); - tmp &= ~S3C2410_TICNT_ENABLE; - - if (enabled) - tmp |= S3C2410_TICNT_ENABLE; - - writeb(tmp, s3c_rtc_base + S3C2410_TICNT); - } - - spin_unlock_irq(&s3c_rtc_pie_lock); - - return 0; -} - static int s3c_rtc_setfreq(struct device *dev, int freq) { struct platform_device *pdev = to_platform_device(dev); @@ -380,7 +349,6 @@ static const struct rtc_class_ops s3c_rtcops = { .read_alarm = s3c_rtc_getalarm, .set_alarm = s3c_rtc_setalarm, .irq_set_freq = s3c_rtc_setfreq, - .irq_set_state = s3c_rtc_setpie, .proc = s3c_rtc_proc, .alarm_irq_enable = s3c_rtc_setaie, }; diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index 5dfe5ffcb0d3..d47b3fc9830f 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -171,23 +171,6 @@ static int sa1100_irq_set_freq(struct device *dev, int freq) static int rtc_timer1_count; -static int sa1100_irq_set_state(struct device *dev, int enabled) -{ - spin_lock_irq(&sa1100_rtc_lock); - if (enabled) { - struct rtc_device *rtc = (struct rtc_device *)dev; - - OSMR1 = timer_freq / rtc->irq_freq + OSCR; - OIER |= OIER_E1; - rtc_timer1_count = 1; - } else { - OIER &= ~OIER_E1; - } - spin_unlock_irq(&sa1100_rtc_lock); - - return 0; -} - static inline int sa1100_timer1_retrigger(struct rtc_device *rtc) { unsigned long diff; @@ -410,7 +393,6 @@ static const struct rtc_class_ops sa1100_rtc_ops = { .set_alarm = sa1100_rtc_set_alarm, .proc = sa1100_rtc_proc, .irq_set_freq = sa1100_irq_set_freq, - .irq_set_state = sa1100_irq_set_state, .alarm_irq_enable = sa1100_rtc_alarm_irq_enable, }; diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index 93314a9e7fa9..ff50a8bc13f6 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c @@ -603,7 +603,6 @@ static struct rtc_class_ops sh_rtc_ops = { .set_time = sh_rtc_set_time, .read_alarm = sh_rtc_read_alarm, .set_alarm = sh_rtc_set_alarm, - .irq_set_state = sh_rtc_irq_set_state, .irq_set_freq = sh_rtc_irq_set_freq, .proc = sh_rtc_proc, .alarm_irq_enable = sh_rtc_alarm_irq_enable, diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c index 769190ac6d11..86f14909f9db 100644 --- a/drivers/rtc/rtc-vr41xx.c +++ b/drivers/rtc/rtc-vr41xx.c @@ -227,16 +227,6 @@ static int vr41xx_rtc_irq_set_freq(struct device *dev, int freq) return 0; } -static int vr41xx_rtc_irq_set_state(struct device *dev, int enabled) -{ - if (enabled) - enable_irq(pie_irq); - else - disable_irq(pie_irq); - - return 0; -} - static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { switch (cmd) { @@ -309,7 +299,6 @@ static const struct rtc_class_ops vr41xx_rtc_ops = { .read_alarm = vr41xx_rtc_read_alarm, .set_alarm = vr41xx_rtc_set_alarm, .irq_set_freq = vr41xx_rtc_irq_set_freq, - .irq_set_state = vr41xx_rtc_irq_set_state, }; static int __devinit rtc_probe(struct platform_device *pdev) diff --git a/include/linux/rtc.h b/include/linux/rtc.h index db3832d5f280..0e2063acc260 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -148,7 +148,6 @@ struct rtc_class_ops { int (*set_alarm)(struct device *, struct rtc_wkalrm *); int (*proc)(struct device *, struct seq_file *); int (*set_mmss)(struct device *, unsigned long secs); - int (*irq_set_state)(struct device *, int enabled); int (*irq_set_freq)(struct device *, int freq); int (*read_callback)(struct device *, int data); int (*alarm_irq_enable)(struct device *, unsigned int enabled); -- cgit v1.2.3 From 696160fec162601d06940862b5b3aa4460344c1b Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 3 Feb 2011 12:02:07 -0800 Subject: RTC: Cleanup rtc_class_ops->irq_set_freq() With the generic rtc code now emulating PIE mode irqs via an hrtimer, no one calls the rtc_class_ops->irq_set_freq call. This patch removes the hook and deletes the driver functions if no one else calls them. CC: Thomas Gleixner CC: Alessandro Zummo CC: Marcelo Roberto Jimenez CC: rtc-linux@googlegroups.com Signed-off-by: John Stultz --- drivers/rtc/rtc-cmos.c | 26 -------------------------- drivers/rtc/rtc-davinci.c | 17 ----------------- drivers/rtc/rtc-pl031.c | 21 --------------------- drivers/rtc/rtc-pxa.c | 15 --------------- drivers/rtc/rtc-s3c.c | 1 - drivers/rtc/rtc-sa1100.c | 1 - drivers/rtc/rtc-sh.c | 1 - drivers/rtc/rtc-vr41xx.c | 21 --------------------- include/linux/rtc.h | 2 -- 9 files changed, 105 deletions(-) (limited to 'include') diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index de632e793d46..bdb1f8e2042a 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -375,31 +375,6 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) return 0; } -static int cmos_irq_set_freq(struct device *dev, int freq) -{ - struct cmos_rtc *cmos = dev_get_drvdata(dev); - int f; - unsigned long flags; - - if (!is_valid_irq(cmos->irq)) - return -ENXIO; - - if (!is_power_of_2(freq)) - return -EINVAL; - /* 0 = no irqs; 1 = 2^15 Hz ... 15 = 2^0 Hz */ - f = ffs(freq); - if (f-- > 16) - return -EINVAL; - f = 16 - f; - - spin_lock_irqsave(&rtc_lock, flags); - hpet_set_periodic_freq(freq); - CMOS_WRITE(RTC_REF_CLCK_32KHZ | f, RTC_FREQ_SELECT); - spin_unlock_irqrestore(&rtc_lock, flags); - - return 0; -} - static int cmos_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct cmos_rtc *cmos = dev_get_drvdata(dev); @@ -482,7 +457,6 @@ static const struct rtc_class_ops cmos_rtc_ops = { .read_alarm = cmos_read_alarm, .set_alarm = cmos_set_alarm, .proc = cmos_procfs, - .irq_set_freq = cmos_irq_set_freq, .alarm_irq_enable = cmos_alarm_irq_enable, .update_irq_enable = cmos_update_irq_enable, }; diff --git a/drivers/rtc/rtc-davinci.c b/drivers/rtc/rtc-davinci.c index 92da73d40e13..dfd98a235ad1 100644 --- a/drivers/rtc/rtc-davinci.c +++ b/drivers/rtc/rtc-davinci.c @@ -473,22 +473,6 @@ static int davinci_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) return 0; } -static int davinci_rtc_irq_set_freq(struct device *dev, int freq) -{ - struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); - unsigned long flags; - u16 tmr_counter = (0x8000 >> (ffs(freq) - 1)); - - spin_lock_irqsave(&davinci_rtc_lock, flags); - - rtcss_write(davinci_rtc, tmr_counter & 0xFF, PRTCSS_RTC_TMR0); - rtcss_write(davinci_rtc, (tmr_counter & 0xFF00) >> 8, PRTCSS_RTC_TMR1); - - spin_unlock_irqrestore(&davinci_rtc_lock, flags); - - return 0; -} - static struct rtc_class_ops davinci_rtc_ops = { .ioctl = davinci_rtc_ioctl, .read_time = davinci_rtc_read_time, @@ -496,7 +480,6 @@ static struct rtc_class_ops davinci_rtc_ops = { .alarm_irq_enable = davinci_rtc_alarm_irq_enable, .read_alarm = davinci_rtc_read_alarm, .set_alarm = davinci_rtc_set_alarm, - .irq_set_freq = davinci_rtc_irq_set_freq, }; static int __init davinci_rtc_probe(struct platform_device *pdev) diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index 0e7c15b24c1c..d829ea63c4fb 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -293,25 +293,6 @@ static int pl031_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) return ret; } -static int pl031_irq_set_freq(struct device *dev, int freq) -{ - struct pl031_local *ldata = dev_get_drvdata(dev); - - /* Cant set timer if it is already enabled */ - if (readl(ldata->base + RTC_TCR) & RTC_TCR_EN) { - dev_err(dev, "can't change frequency while timer enabled\n"); - return -EINVAL; - } - - /* If self start bit in RTC_TCR is set timer will start here, - * but we never set that bit. Instead we start the timer when - * set_state is called with enabled == 1. - */ - writel(RTC_TIMER_FREQ / freq, ldata->base + RTC_TLR); - - return 0; -} - static int pl031_remove(struct amba_device *adev) { struct pl031_local *ldata = dev_get_drvdata(&adev->dev); @@ -408,7 +389,6 @@ static struct rtc_class_ops stv1_pl031_ops = { .read_alarm = pl031_read_alarm, .set_alarm = pl031_set_alarm, .alarm_irq_enable = pl031_alarm_irq_enable, - .irq_set_freq = pl031_irq_set_freq, }; /* And the second ST derivative */ @@ -418,7 +398,6 @@ static struct rtc_class_ops stv2_pl031_ops = { .read_alarm = pl031_stv2_read_alarm, .set_alarm = pl031_stv2_set_alarm, .alarm_irq_enable = pl031_alarm_irq_enable, - .irq_set_freq = pl031_irq_set_freq, }; static struct amba_id pl031_ids[] = { diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c index b216ae5389c8..a1fdc802598a 100644 --- a/drivers/rtc/rtc-pxa.c +++ b/drivers/rtc/rtc-pxa.c @@ -209,20 +209,6 @@ static void pxa_rtc_release(struct device *dev) free_irq(pxa_rtc->irq_1Hz, dev); } -static int pxa_periodic_irq_set_freq(struct device *dev, int freq) -{ - struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); - int period_ms; - - if (freq < 1 || freq > MAXFREQ_PERIODIC) - return -EINVAL; - - period_ms = 1000 / freq; - rtc_writel(pxa_rtc, PIAR, period_ms); - - return 0; -} - static int pxa_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); @@ -336,7 +322,6 @@ static const struct rtc_class_ops pxa_rtc_ops = { .alarm_irq_enable = pxa_alarm_irq_enable, .update_irq_enable = pxa_update_irq_enable, .proc = pxa_rtc_proc, - .irq_set_freq = pxa_periodic_irq_set_freq, }; static int __init pxa_rtc_probe(struct platform_device *pdev) diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 80fb7e72f9d9..714964913e5e 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -348,7 +348,6 @@ static const struct rtc_class_ops s3c_rtcops = { .set_time = s3c_rtc_settime, .read_alarm = s3c_rtc_getalarm, .set_alarm = s3c_rtc_setalarm, - .irq_set_freq = s3c_rtc_setfreq, .proc = s3c_rtc_proc, .alarm_irq_enable = s3c_rtc_setaie, }; diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index d47b3fc9830f..d1a2b0bc3b24 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -392,7 +392,6 @@ static const struct rtc_class_ops sa1100_rtc_ops = { .read_alarm = sa1100_rtc_read_alarm, .set_alarm = sa1100_rtc_set_alarm, .proc = sa1100_rtc_proc, - .irq_set_freq = sa1100_irq_set_freq, .alarm_irq_enable = sa1100_rtc_alarm_irq_enable, }; diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index ff50a8bc13f6..148544979a54 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c @@ -603,7 +603,6 @@ static struct rtc_class_ops sh_rtc_ops = { .set_time = sh_rtc_set_time, .read_alarm = sh_rtc_read_alarm, .set_alarm = sh_rtc_set_alarm, - .irq_set_freq = sh_rtc_irq_set_freq, .proc = sh_rtc_proc, .alarm_irq_enable = sh_rtc_alarm_irq_enable, }; diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c index 86f14909f9db..c5698cda366a 100644 --- a/drivers/rtc/rtc-vr41xx.c +++ b/drivers/rtc/rtc-vr41xx.c @@ -207,26 +207,6 @@ static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) return 0; } -static int vr41xx_rtc_irq_set_freq(struct device *dev, int freq) -{ - u64 count; - - if (!is_power_of_2(freq)) - return -EINVAL; - count = RTC_FREQUENCY; - do_div(count, freq); - - spin_lock_irq(&rtc_lock); - - periodic_count = count; - rtc1_write(RTCL1LREG, periodic_count); - rtc1_write(RTCL1HREG, periodic_count >> 16); - - spin_unlock_irq(&rtc_lock); - - return 0; -} - static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { switch (cmd) { @@ -298,7 +278,6 @@ static const struct rtc_class_ops vr41xx_rtc_ops = { .set_time = vr41xx_rtc_set_time, .read_alarm = vr41xx_rtc_read_alarm, .set_alarm = vr41xx_rtc_set_alarm, - .irq_set_freq = vr41xx_rtc_irq_set_freq, }; static int __devinit rtc_probe(struct platform_device *pdev) diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 0e2063acc260..741a51cc4460 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -133,7 +133,6 @@ extern struct class *rtc_class; * The (current) exceptions are mostly filesystem hooks: * - the proc() hook for procfs * - non-ioctl() chardev hooks: open(), release(), read_callback() - * - periodic irq calls: irq_set_state(), irq_set_freq() * * REVISIT those periodic irq calls *do* have ops_lock when they're * issued through ioctl() ... @@ -148,7 +147,6 @@ struct rtc_class_ops { int (*set_alarm)(struct device *, struct rtc_wkalrm *); int (*proc)(struct device *, struct seq_file *); int (*set_mmss)(struct device *, unsigned long secs); - int (*irq_set_freq)(struct device *, int freq); int (*read_callback)(struct device *, int data); int (*alarm_irq_enable)(struct device *, unsigned int enabled); int (*update_irq_enable)(struct device *, unsigned int enabled); -- cgit v1.2.3 From 51ba60c5bb3b0f71bee26404ddc22d8e4109e88a Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 3 Feb 2011 12:13:50 -0800 Subject: RTC: Cleanup rtc_class_ops->update_irq_enable() Now that the generic code handles UIE mode irqs via periodic alarm interrupts, no one calls the rtc_class_ops->update_irq_enable() method anymore. This patch removes the driver hooks and implementations of update_irq_enable if no one else is calling it. CC: Thomas Gleixner CC: Alessandro Zummo CC: Marcelo Roberto Jimenez CC: rtc-linux@googlegroups.com Signed-off-by: John Stultz --- drivers/rtc/rtc-cmos.c | 20 -------------------- drivers/rtc/rtc-ds1511.c | 17 ----------------- drivers/rtc/rtc-ds1553.c | 17 ----------------- drivers/rtc/rtc-ds3232.c | 18 ------------------ drivers/rtc/rtc-jz4740.c | 7 ------- drivers/rtc/rtc-mc13xxx.c | 7 ------- drivers/rtc/rtc-mpc5121.c | 20 -------------------- drivers/rtc/rtc-mxc.c | 7 ------- drivers/rtc/rtc-nuc900.c | 15 --------------- drivers/rtc/rtc-pcap.c | 6 ------ drivers/rtc/rtc-pcf50633.c | 22 +--------------------- drivers/rtc/rtc-pxa.c | 16 ---------------- drivers/rtc/rtc-stmp3xxx.c | 15 --------------- drivers/rtc/rtc-twl.c | 13 ------------- drivers/rtc/rtc-wm831x.c | 16 ---------------- drivers/rtc/rtc-wm8350.c | 21 --------------------- include/linux/rtc.h | 1 - 17 files changed, 1 insertion(+), 237 deletions(-) (limited to 'include') diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index bdb1f8e2042a..dc2a0ba969ce 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -394,25 +394,6 @@ static int cmos_alarm_irq_enable(struct device *dev, unsigned int enabled) return 0; } -static int cmos_update_irq_enable(struct device *dev, unsigned int enabled) -{ - struct cmos_rtc *cmos = dev_get_drvdata(dev); - unsigned long flags; - - if (!is_valid_irq(cmos->irq)) - return -EINVAL; - - spin_lock_irqsave(&rtc_lock, flags); - - if (enabled) - cmos_irq_enable(cmos, RTC_UIE); - else - cmos_irq_disable(cmos, RTC_UIE); - - spin_unlock_irqrestore(&rtc_lock, flags); - return 0; -} - #if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE) static int cmos_procfs(struct device *dev, struct seq_file *seq) @@ -458,7 +439,6 @@ static const struct rtc_class_ops cmos_rtc_ops = { .set_alarm = cmos_set_alarm, .proc = cmos_procfs, .alarm_irq_enable = cmos_alarm_irq_enable, - .update_irq_enable = cmos_update_irq_enable, }; /*----------------------------------------------------------------*/ diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c index 37268e97de49..3fffd708711f 100644 --- a/drivers/rtc/rtc-ds1511.c +++ b/drivers/rtc/rtc-ds1511.c @@ -397,29 +397,12 @@ static int ds1511_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) return 0; } -static int ds1511_rtc_update_irq_enable(struct device *dev, - unsigned int enabled) -{ - struct platform_device *pdev = to_platform_device(dev); - struct rtc_plat_data *pdata = platform_get_drvdata(pdev); - - if (pdata->irq <= 0) - return -EINVAL; - if (enabled) - pdata->irqen |= RTC_UF; - else - pdata->irqen &= ~RTC_UF; - ds1511_rtc_update_alarm(pdata); - return 0; -} - static const struct rtc_class_ops ds1511_rtc_ops = { .read_time = ds1511_rtc_read_time, .set_time = ds1511_rtc_set_time, .read_alarm = ds1511_rtc_read_alarm, .set_alarm = ds1511_rtc_set_alarm, .alarm_irq_enable = ds1511_rtc_alarm_irq_enable, - .update_irq_enable = ds1511_rtc_update_irq_enable, }; static ssize_t diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c index ff432e2ca275..fee41b97c9e8 100644 --- a/drivers/rtc/rtc-ds1553.c +++ b/drivers/rtc/rtc-ds1553.c @@ -227,29 +227,12 @@ static int ds1553_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) return 0; } -static int ds1553_rtc_update_irq_enable(struct device *dev, - unsigned int enabled) -{ - struct platform_device *pdev = to_platform_device(dev); - struct rtc_plat_data *pdata = platform_get_drvdata(pdev); - - if (pdata->irq <= 0) - return -EINVAL; - if (enabled) - pdata->irqen |= RTC_UF; - else - pdata->irqen &= ~RTC_UF; - ds1553_rtc_update_alarm(pdata); - return 0; -} - static const struct rtc_class_ops ds1553_rtc_ops = { .read_time = ds1553_rtc_read_time, .set_time = ds1553_rtc_set_time, .read_alarm = ds1553_rtc_read_alarm, .set_alarm = ds1553_rtc_set_alarm, .alarm_irq_enable = ds1553_rtc_alarm_irq_enable, - .update_irq_enable = ds1553_rtc_update_irq_enable, }; static ssize_t ds1553_nvram_read(struct file *filp, struct kobject *kobj, diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index 950735415a7c..27b7bf672ac6 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c @@ -339,23 +339,6 @@ static int ds3232_alarm_irq_enable(struct device *dev, unsigned int enabled) return 0; } -static int ds3232_update_irq_enable(struct device *dev, unsigned int enabled) -{ - struct i2c_client *client = to_i2c_client(dev); - struct ds3232 *ds3232 = i2c_get_clientdata(client); - - if (client->irq <= 0) - return -EINVAL; - - if (enabled) - ds3232->rtc->irq_data |= RTC_UF; - else - ds3232->rtc->irq_data &= ~RTC_UF; - - ds3232_update_alarm(client); - return 0; -} - static irqreturn_t ds3232_irq(int irq, void *dev_id) { struct i2c_client *client = dev_id; @@ -406,7 +389,6 @@ static const struct rtc_class_ops ds3232_rtc_ops = { .read_alarm = ds3232_read_alarm, .set_alarm = ds3232_set_alarm, .alarm_irq_enable = ds3232_alarm_irq_enable, - .update_irq_enable = ds3232_update_irq_enable, }; static int __devinit ds3232_probe(struct i2c_client *client, diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c index 2e16f72c9056..b6473631d182 100644 --- a/drivers/rtc/rtc-jz4740.c +++ b/drivers/rtc/rtc-jz4740.c @@ -168,12 +168,6 @@ static int jz4740_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) return ret; } -static int jz4740_rtc_update_irq_enable(struct device *dev, unsigned int enable) -{ - struct jz4740_rtc *rtc = dev_get_drvdata(dev); - return jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ_IRQ, enable); -} - static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) { struct jz4740_rtc *rtc = dev_get_drvdata(dev); @@ -185,7 +179,6 @@ static struct rtc_class_ops jz4740_rtc_ops = { .set_mmss = jz4740_rtc_set_mmss, .read_alarm = jz4740_rtc_read_alarm, .set_alarm = jz4740_rtc_set_alarm, - .update_irq_enable = jz4740_rtc_update_irq_enable, .alarm_irq_enable = jz4740_rtc_alarm_irq_enable, }; diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c index 5314b153bfba..c42006469559 100644 --- a/drivers/rtc/rtc-mc13xxx.c +++ b/drivers/rtc/rtc-mc13xxx.c @@ -282,12 +282,6 @@ static irqreturn_t mc13xxx_rtc_update_handler(int irq, void *dev) return IRQ_HANDLED; } -static int mc13xxx_rtc_update_irq_enable(struct device *dev, - unsigned int enabled) -{ - return mc13xxx_rtc_irq_enable(dev, enabled, MC13XXX_IRQ_1HZ); -} - static int mc13xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { @@ -300,7 +294,6 @@ static const struct rtc_class_ops mc13xxx_rtc_ops = { .read_alarm = mc13xxx_rtc_read_alarm, .set_alarm = mc13xxx_rtc_set_alarm, .alarm_irq_enable = mc13xxx_rtc_alarm_irq_enable, - .update_irq_enable = mc13xxx_rtc_update_irq_enable, }; static irqreturn_t mc13xxx_rtc_reset_handler(int irq, void *dev) diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c index dfcdf0901d21..b40c1ff1ebc8 100644 --- a/drivers/rtc/rtc-mpc5121.c +++ b/drivers/rtc/rtc-mpc5121.c @@ -240,32 +240,12 @@ static int mpc5121_rtc_alarm_irq_enable(struct device *dev, return 0; } -static int mpc5121_rtc_update_irq_enable(struct device *dev, - unsigned int enabled) -{ - struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); - struct mpc5121_rtc_regs __iomem *regs = rtc->regs; - int val; - - val = in_8(®s->int_enable); - - if (enabled) - val = (val & ~0x8) | 0x1; - else - val &= ~0x1; - - out_8(®s->int_enable, val); - - return 0; -} - static const struct rtc_class_ops mpc5121_rtc_ops = { .read_time = mpc5121_rtc_read_time, .set_time = mpc5121_rtc_set_time, .read_alarm = mpc5121_rtc_read_alarm, .set_alarm = mpc5121_rtc_set_alarm, .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable, - .update_irq_enable = mpc5121_rtc_update_irq_enable, }; static int __devinit mpc5121_rtc_probe(struct platform_device *op, diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c index 0b06c1e03fd5..826ab64a8fa9 100644 --- a/drivers/rtc/rtc-mxc.c +++ b/drivers/rtc/rtc-mxc.c @@ -274,12 +274,6 @@ static int mxc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) return 0; } -static int mxc_rtc_update_irq_enable(struct device *dev, unsigned int enabled) -{ - mxc_rtc_irq_enable(dev, RTC_1HZ_BIT, enabled); - return 0; -} - /* * This function reads the current RTC time into tm in Gregorian date. */ @@ -368,7 +362,6 @@ static struct rtc_class_ops mxc_rtc_ops = { .read_alarm = mxc_rtc_read_alarm, .set_alarm = mxc_rtc_set_alarm, .alarm_irq_enable = mxc_rtc_alarm_irq_enable, - .update_irq_enable = mxc_rtc_update_irq_enable, }; static int __init mxc_rtc_probe(struct platform_device *pdev) diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c index ddb0857e15a4..781068d62f23 100644 --- a/drivers/rtc/rtc-nuc900.c +++ b/drivers/rtc/rtc-nuc900.c @@ -134,20 +134,6 @@ static void nuc900_rtc_bin2bcd(struct device *dev, struct rtc_time *settm, gettm->bcd_hour = bin2bcd(settm->tm_hour) << 16; } -static int nuc900_update_irq_enable(struct device *dev, unsigned int enabled) -{ - struct nuc900_rtc *rtc = dev_get_drvdata(dev); - - if (enabled) - __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)| - (TICKINTENB), rtc->rtc_reg + REG_RTC_RIER); - else - __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)& - (~TICKINTENB), rtc->rtc_reg + REG_RTC_RIER); - - return 0; -} - static int nuc900_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct nuc900_rtc *rtc = dev_get_drvdata(dev); @@ -234,7 +220,6 @@ static struct rtc_class_ops nuc900_rtc_ops = { .read_alarm = nuc900_rtc_read_alarm, .set_alarm = nuc900_rtc_set_alarm, .alarm_irq_enable = nuc900_alarm_irq_enable, - .update_irq_enable = nuc900_update_irq_enable, }; static int __devinit nuc900_rtc_probe(struct platform_device *pdev) diff --git a/drivers/rtc/rtc-pcap.c b/drivers/rtc/rtc-pcap.c index 25c0b3fd44f1..a633abc42896 100644 --- a/drivers/rtc/rtc-pcap.c +++ b/drivers/rtc/rtc-pcap.c @@ -131,18 +131,12 @@ static int pcap_rtc_alarm_irq_enable(struct device *dev, unsigned int en) return pcap_rtc_irq_enable(dev, PCAP_IRQ_TODA, en); } -static int pcap_rtc_update_irq_enable(struct device *dev, unsigned int en) -{ - return pcap_rtc_irq_enable(dev, PCAP_IRQ_1HZ, en); -} - static const struct rtc_class_ops pcap_rtc_ops = { .read_time = pcap_rtc_read_time, .read_alarm = pcap_rtc_read_alarm, .set_alarm = pcap_rtc_set_alarm, .set_mmss = pcap_rtc_set_mmss, .alarm_irq_enable = pcap_rtc_alarm_irq_enable, - .update_irq_enable = pcap_rtc_update_irq_enable, }; static int __devinit pcap_rtc_probe(struct platform_device *pdev) diff --git a/drivers/rtc/rtc-pcf50633.c b/drivers/rtc/rtc-pcf50633.c index 16edf94ab42f..f90c574f9d05 100644 --- a/drivers/rtc/rtc-pcf50633.c +++ b/drivers/rtc/rtc-pcf50633.c @@ -106,25 +106,6 @@ pcf50633_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) return 0; } -static int -pcf50633_rtc_update_irq_enable(struct device *dev, unsigned int enabled) -{ - struct pcf50633_rtc *rtc = dev_get_drvdata(dev); - int err; - - if (enabled) - err = pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_SECOND); - else - err = pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_SECOND); - - if (err < 0) - return err; - - rtc->second_enabled = enabled; - - return 0; -} - static int pcf50633_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct pcf50633_rtc *rtc; @@ -262,8 +243,7 @@ static struct rtc_class_ops pcf50633_rtc_ops = { .set_time = pcf50633_rtc_set_time, .read_alarm = pcf50633_rtc_read_alarm, .set_alarm = pcf50633_rtc_set_alarm, - .alarm_irq_enable = pcf50633_rtc_alarm_irq_enable, - .update_irq_enable = pcf50633_rtc_update_irq_enable, + .alarm_irq_enable = pcf50633_rtc_alarm_irq_enable, }; static void pcf50633_rtc_irq(int irq, void *data) diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c index a1fdc802598a..fc9f4991574b 100644 --- a/drivers/rtc/rtc-pxa.c +++ b/drivers/rtc/rtc-pxa.c @@ -224,21 +224,6 @@ static int pxa_alarm_irq_enable(struct device *dev, unsigned int enabled) return 0; } -static int pxa_update_irq_enable(struct device *dev, unsigned int enabled) -{ - struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); - - spin_lock_irq(&pxa_rtc->lock); - - if (enabled) - rtsr_set_bits(pxa_rtc, RTSR_HZE); - else - rtsr_clear_bits(pxa_rtc, RTSR_HZE); - - spin_unlock_irq(&pxa_rtc->lock); - return 0; -} - static int pxa_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); @@ -320,7 +305,6 @@ static const struct rtc_class_ops pxa_rtc_ops = { .read_alarm = pxa_rtc_read_alarm, .set_alarm = pxa_rtc_set_alarm, .alarm_irq_enable = pxa_alarm_irq_enable, - .update_irq_enable = pxa_update_irq_enable, .proc = pxa_rtc_proc, }; diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c index 7e7d0c806f2d..572e9534b591 100644 --- a/drivers/rtc/rtc-stmp3xxx.c +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -115,19 +115,6 @@ static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled) return 0; } -static int stmp3xxx_update_irq_enable(struct device *dev, unsigned int enabled) -{ - struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); - - if (enabled) - stmp3xxx_setl(BM_RTC_CTRL_ONEMSEC_IRQ_EN, - rtc_data->io + HW_RTC_CTRL); - else - stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN, - rtc_data->io + HW_RTC_CTRL); - return 0; -} - static int stmp3xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) { struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); @@ -149,8 +136,6 @@ static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) static struct rtc_class_ops stmp3xxx_rtc_ops = { .alarm_irq_enable = stmp3xxx_alarm_irq_enable, - .update_irq_enable = - stmp3xxx_update_irq_enable, .read_time = stmp3xxx_rtc_gettime, .set_mmss = stmp3xxx_rtc_set_mmss, .read_alarm = stmp3xxx_rtc_read_alarm, diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index ed1b86828124..f9a2799c44d6 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -213,18 +213,6 @@ static int twl_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) return ret; } -static int twl_rtc_update_irq_enable(struct device *dev, unsigned enabled) -{ - int ret; - - if (enabled) - ret = set_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); - else - ret = mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); - - return ret; -} - /* * Gets current TWL RTC time and date parameters. * @@ -433,7 +421,6 @@ static struct rtc_class_ops twl_rtc_ops = { .read_alarm = twl_rtc_read_alarm, .set_alarm = twl_rtc_set_alarm, .alarm_irq_enable = twl_rtc_alarm_irq_enable, - .update_irq_enable = twl_rtc_update_irq_enable, }; /*----------------------------------------------------------------------*/ diff --git a/drivers/rtc/rtc-wm831x.c b/drivers/rtc/rtc-wm831x.c index 82931dc65c0b..bdc909bd56da 100644 --- a/drivers/rtc/rtc-wm831x.c +++ b/drivers/rtc/rtc-wm831x.c @@ -315,21 +315,6 @@ static int wm831x_rtc_alarm_irq_enable(struct device *dev, return wm831x_rtc_stop_alarm(wm831x_rtc); } -static int wm831x_rtc_update_irq_enable(struct device *dev, - unsigned int enabled) -{ - struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); - int val; - - if (enabled) - val = 1 << WM831X_RTC_PINT_FREQ_SHIFT; - else - val = 0; - - return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL, - WM831X_RTC_PINT_FREQ_MASK, val); -} - static irqreturn_t wm831x_alm_irq(int irq, void *data) { struct wm831x_rtc *wm831x_rtc = data; @@ -354,7 +339,6 @@ static const struct rtc_class_ops wm831x_rtc_ops = { .read_alarm = wm831x_rtc_readalarm, .set_alarm = wm831x_rtc_setalarm, .alarm_irq_enable = wm831x_rtc_alarm_irq_enable, - .update_irq_enable = wm831x_rtc_update_irq_enable, }; #ifdef CONFIG_PM diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c index 3d0dc76b38af..66421426e404 100644 --- a/drivers/rtc/rtc-wm8350.c +++ b/drivers/rtc/rtc-wm8350.c @@ -302,26 +302,6 @@ static int wm8350_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) return ret; } -static int wm8350_rtc_update_irq_enable(struct device *dev, - unsigned int enabled) -{ - struct wm8350 *wm8350 = dev_get_drvdata(dev); - - /* Suppress duplicate changes since genirq nests enable and - * disable calls. */ - if (enabled == wm8350->rtc.update_enabled) - return 0; - - if (enabled) - wm8350_unmask_irq(wm8350, WM8350_IRQ_RTC_SEC); - else - wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC); - - wm8350->rtc.update_enabled = enabled; - - return 0; -} - static irqreturn_t wm8350_rtc_alarm_handler(int irq, void *data) { struct wm8350 *wm8350 = data; @@ -357,7 +337,6 @@ static const struct rtc_class_ops wm8350_rtc_ops = { .read_alarm = wm8350_rtc_readalarm, .set_alarm = wm8350_rtc_setalarm, .alarm_irq_enable = wm8350_rtc_alarm_irq_enable, - .update_irq_enable = wm8350_rtc_update_irq_enable, }; #ifdef CONFIG_PM diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 741a51cc4460..2ca7e8a78060 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -149,7 +149,6 @@ struct rtc_class_ops { int (*set_mmss)(struct device *, unsigned long secs); int (*read_callback)(struct device *, int data); int (*alarm_irq_enable)(struct device *, unsigned int enabled); - int (*update_irq_enable)(struct device *, unsigned int enabled); }; #define RTC_DEVICE_NAME_SIZE 20 -- cgit v1.2.3 From f7ae8d59f66154df0424fd94035c89981fed3379 Mon Sep 17 00:00:00 2001 From: Rémi Denis-Courmont Date: Tue, 8 Mar 2011 22:44:10 +0000 Subject: Phonet: allocate sock from accept syscall rather than soft IRQ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This moves most of the accept logic to process context like other socket stacks do. Then we can use a few more common socket helpers and simplify a bit. Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- include/net/phonet/pep.h | 1 - net/phonet/pep.c | 284 +++++++++++++++++++---------------------------- net/phonet/socket.c | 10 +- 3 files changed, 121 insertions(+), 174 deletions(-) (limited to 'include') diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h index 38eed1be6ffd..b669fe6dbc3b 100644 --- a/include/net/phonet/pep.h +++ b/include/net/phonet/pep.h @@ -28,7 +28,6 @@ struct pep_sock { /* XXX: union-ify listening vs connected stuff ? */ /* Listening socket stuff: */ - struct hlist_head ackq; struct hlist_head hlist; /* Connected socket stuff: */ diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 610794a416e8..c0fab4cfcef7 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -42,7 +42,7 @@ * TCP_ESTABLISHED connected pipe in enabled state * * pep_sock locking: - * - sk_state, ackq, hlist: sock lock needed + * - sk_state, hlist: sock lock needed * - listener: read only * - pipe_handle: read only */ @@ -202,11 +202,12 @@ static int pep_accept_conn(struct sock *sk, struct sk_buff *skb) GFP_KERNEL); } -static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code) +static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code, + gfp_t priority) { static const u8 data[4] = { PAD, PAD, PAD, 0 /* sub-blocks */ }; WARN_ON(code == PN_PIPE_NO_ERROR); - return pep_reply(sk, skb, code, data, sizeof(data), GFP_ATOMIC); + return pep_reply(sk, skb, code, data, sizeof(data), priority); } /* Control requests are not sent by the pipe service and have a specific @@ -365,7 +366,7 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) switch (hdr->message_id) { case PNS_PEP_CONNECT_REQ: - pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE); + pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_ATOMIC); break; case PNS_PEP_DISCONNECT_REQ: @@ -574,7 +575,6 @@ static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb) sk->sk_state = TCP_SYN_RECV; sk->sk_backlog_rcv = pipe_do_rcv; - sk->sk_destruct = pipe_destruct; pn->rx_credits = 0; sk->sk_state_change(sk); @@ -582,96 +582,6 @@ static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb) } #endif -static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb) -{ - struct sock *newsk; - struct pep_sock *newpn, *pn = pep_sk(sk); - struct pnpipehdr *hdr; - struct sockaddr_pn dst, src; - u16 peer_type; - u8 pipe_handle, enabled, n_sb; - u8 aligned = 0; - - if (!pskb_pull(skb, sizeof(*hdr) + 4)) - return -EINVAL; - - hdr = pnp_hdr(skb); - pipe_handle = hdr->pipe_handle; - switch (hdr->state_after_connect) { - case PN_PIPE_DISABLE: - enabled = 0; - break; - case PN_PIPE_ENABLE: - enabled = 1; - break; - default: - pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM); - return -EINVAL; - } - peer_type = hdr->other_pep_type << 8; - - /* Parse sub-blocks (options) */ - n_sb = hdr->data[4]; - while (n_sb > 0) { - u8 type, buf[1], len = sizeof(buf); - const u8 *data = pep_get_sb(skb, &type, &len, buf); - - if (data == NULL) - return -EINVAL; - switch (type) { - case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE: - if (len < 1) - return -EINVAL; - peer_type = (peer_type & 0xff00) | data[0]; - break; - case PN_PIPE_SB_ALIGNED_DATA: - aligned = data[0] != 0; - break; - } - n_sb--; - } - - skb = skb_clone(skb, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - /* Create a new to-be-accepted sock */ - newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_ATOMIC, sk->sk_prot); - if (!newsk) { - kfree_skb(skb); - return -ENOMEM; - } - sock_init_data(NULL, newsk); - newsk->sk_state = TCP_SYN_RECV; - newsk->sk_backlog_rcv = pipe_do_rcv; - newsk->sk_protocol = sk->sk_protocol; - newsk->sk_destruct = pipe_destruct; - - newpn = pep_sk(newsk); - pn_skb_get_dst_sockaddr(skb, &dst); - pn_skb_get_src_sockaddr(skb, &src); - newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst); - newpn->pn_sk.dobject = pn_sockaddr_get_object(&src); - newpn->pn_sk.resource = pn_sockaddr_get_resource(&dst); - skb_queue_head_init(&newpn->ctrlreq_queue); - newpn->pipe_handle = pipe_handle; - atomic_set(&newpn->tx_credits, 0); - newpn->peer_type = peer_type; - newpn->rx_credits = 0; - newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL; - newpn->init_enable = enabled; - newpn->aligned = aligned; - - BUG_ON(!skb_queue_empty(&newsk->sk_receive_queue)); - skb_queue_head(&newsk->sk_receive_queue, skb); - if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_data_ready(sk, 0); - - sk_acceptq_added(sk); - sk_add_node(newsk, &pn->ackq); - return 0; -} - /* Listening sock must be locked */ static struct sock *pep_find_pipe(const struct hlist_head *hlist, const struct sockaddr_pn *dst, @@ -726,22 +636,18 @@ static int pep_do_rcv(struct sock *sk, struct sk_buff *skb) if (sknode) return sk_receive_skb(sknode, skb, 1); - /* Look for a pipe handle pending accept */ - sknode = pep_find_pipe(&pn->ackq, &dst, pipe_handle); - if (sknode) { - sock_put(sknode); - if (net_ratelimit()) - printk(KERN_WARNING"Phonet unconnected PEP ignored"); - goto drop; - } - switch (hdr->message_id) { case PNS_PEP_CONNECT_REQ: - if (sk->sk_state == TCP_LISTEN && !sk_acceptq_is_full(sk)) - pep_connreq_rcv(sk, skb); - else - pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE); - break; + if (sk->sk_state != TCP_LISTEN || sk_acceptq_is_full(sk)) { + pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, + GFP_ATOMIC); + break; + } + skb_queue_head(&sk->sk_receive_queue, skb); + sk_acceptq_added(sk); + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_data_ready(sk, 0); + return NET_RX_SUCCESS; #ifdef CONFIG_PHONET_PIPECTRLR case PNS_PEP_CONNECT_RESP: @@ -799,24 +705,16 @@ static void pep_sock_close(struct sock *sk, long timeout) sk_common_release(sk); lock_sock(sk); - if (sk->sk_state == TCP_LISTEN) { - /* Destroy the listen queue */ - struct sock *sknode; - struct hlist_node *p, *n; - - sk_for_each_safe(sknode, p, n, &pn->ackq) - sk_del_node_init(sknode); - sk->sk_state = TCP_CLOSE; - } else if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) { + if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) { #ifndef CONFIG_PHONET_PIPECTRLR /* Forcefully remove dangling Phonet pipe */ pipe_do_remove(sk); #else /* send pep disconnect request */ pipe_handler_request(sk, PNS_PEP_DISCONNECT_REQ, PAD, NULL, 0); - sk->sk_state = TCP_CLOSE; #endif } + sk->sk_state = TCP_CLOSE; ifindex = pn->ifindex; pn->ifindex = 0; @@ -827,69 +725,121 @@ static void pep_sock_close(struct sock *sk, long timeout) sock_put(sk); } -static int pep_wait_connreq(struct sock *sk, int noblock) +static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp) { - struct task_struct *tsk = current; - struct pep_sock *pn = pep_sk(sk); - long timeo = sock_rcvtimeo(sk, noblock); - - for (;;) { - DEFINE_WAIT(wait); + struct pep_sock *pn = pep_sk(sk), *newpn; + struct sock *newsk = NULL; + struct sk_buff *skb; + struct pnpipehdr *hdr; + struct sockaddr_pn dst, src; + int err; + u16 peer_type; + u8 pipe_handle, enabled, n_sb; + u8 aligned = 0; - if (sk->sk_state != TCP_LISTEN) - return -EINVAL; - if (!hlist_empty(&pn->ackq)) - break; - if (!timeo) - return -EWOULDBLOCK; - if (signal_pending(tsk)) - return sock_intr_errno(timeo); + skb = skb_recv_datagram(sk, 0, flags & O_NONBLOCK, errp); + if (!skb) + return NULL; - prepare_to_wait_exclusive(sk_sleep(sk), &wait, - TASK_INTERRUPTIBLE); - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - finish_wait(sk_sleep(sk), &wait); + lock_sock(sk); + if (sk->sk_state != TCP_LISTEN) { + err = -EINVAL; + goto drop; } + sk_acceptq_removed(sk); - return 0; -} + err = -EPROTO; + if (!pskb_may_pull(skb, sizeof(*hdr) + 4)) + goto drop; -static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp) -{ - struct pep_sock *pn = pep_sk(sk); - struct sock *newsk = NULL; - struct sk_buff *oskb; - int err; + hdr = pnp_hdr(skb); + pipe_handle = hdr->pipe_handle; + switch (hdr->state_after_connect) { + case PN_PIPE_DISABLE: + enabled = 0; + break; + case PN_PIPE_ENABLE: + enabled = 1; + break; + default: + pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM, + GFP_KERNEL); + goto drop; + } + peer_type = hdr->other_pep_type << 8; - lock_sock(sk); - err = pep_wait_connreq(sk, flags & O_NONBLOCK); - if (err) - goto out; + /* Parse sub-blocks (options) */ + n_sb = hdr->data[4]; + while (n_sb > 0) { + u8 type, buf[1], len = sizeof(buf); + const u8 *data = pep_get_sb(skb, &type, &len, buf); - newsk = __sk_head(&pn->ackq); + if (data == NULL) + goto drop; + switch (type) { + case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE: + if (len < 1) + goto drop; + peer_type = (peer_type & 0xff00) | data[0]; + break; + case PN_PIPE_SB_ALIGNED_DATA: + aligned = data[0] != 0; + break; + } + n_sb--; + } - oskb = skb_dequeue(&newsk->sk_receive_queue); - err = pep_accept_conn(newsk, oskb); - if (err) { - skb_queue_head(&newsk->sk_receive_queue, oskb); + /* Check for duplicate pipe handle */ + newsk = pep_find_pipe(&pn->hlist, &dst, pipe_handle); + if (unlikely(newsk)) { + __sock_put(newsk); newsk = NULL; - goto out; + pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_KERNEL); + goto drop; + } + + /* Create a new to-be-accepted sock */ + newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_KERNEL, sk->sk_prot); + if (!newsk) { + pep_reject_conn(sk, skb, PN_PIPE_ERR_OVERLOAD, GFP_KERNEL); + err = -ENOBUFS; + goto drop; } - kfree_skb(oskb); + sock_init_data(NULL, newsk); + newsk->sk_state = TCP_SYN_RECV; + newsk->sk_backlog_rcv = pipe_do_rcv; + newsk->sk_protocol = sk->sk_protocol; + newsk->sk_destruct = pipe_destruct; + + newpn = pep_sk(newsk); + pn_skb_get_dst_sockaddr(skb, &dst); + pn_skb_get_src_sockaddr(skb, &src); + newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst); + newpn->pn_sk.dobject = pn_sockaddr_get_object(&src); + newpn->pn_sk.resource = pn_sockaddr_get_resource(&dst); sock_hold(sk); - pep_sk(newsk)->listener = sk; + newpn->listener = sk; + skb_queue_head_init(&newpn->ctrlreq_queue); + newpn->pipe_handle = pipe_handle; + atomic_set(&newpn->tx_credits, 0); + newpn->ifindex = 0; + newpn->peer_type = peer_type; + newpn->rx_credits = 0; + newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL; + newpn->init_enable = enabled; + newpn->aligned = aligned; - sock_hold(newsk); - sk_del_node_init(newsk); - sk_acceptq_removed(sk); + err = pep_accept_conn(newsk, skb); + if (err) { + sock_put(newsk); + newsk = NULL; + goto drop; + } sk_add_node(newsk, &pn->hlist); - __sock_put(newsk); - -out: +drop: release_sock(sk); + kfree_skb(skb); *errp = err; return newsk; } @@ -937,7 +887,7 @@ static int pep_init(struct sock *sk) { struct pep_sock *pn = pep_sk(sk); - INIT_HLIST_HEAD(&pn->ackq); + sk->sk_destruct = pipe_destruct; INIT_HLIST_HEAD(&pn->hlist); skb_queue_head_init(&pn->ctrlreq_queue); pn->pipe_handle = PN_PIPE_INVALID_HANDLE; diff --git a/net/phonet/socket.c b/net/phonet/socket.c index 65a03338769e..1eccfc35bcc0 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -327,6 +327,9 @@ static int pn_socket_accept(struct socket *sock, struct socket *newsock, struct sock *newsk; int err; + if (unlikely(sk->sk_state != TCP_LISTEN)) + return -EINVAL; + newsk = sk->sk_prot->accept(sk, flags, &err); if (!newsk) return err; @@ -363,13 +366,8 @@ static unsigned int pn_socket_poll(struct file *file, struct socket *sock, poll_wait(file, sk_sleep(sk), wait); - switch (sk->sk_state) { - case TCP_LISTEN: - return hlist_empty(&pn->ackq) ? 0 : POLLIN; - case TCP_CLOSE: + if (sk->sk_state == TCP_CLOSE) return POLLERR; - } - if (!skb_queue_empty(&sk->sk_receive_queue)) mask |= POLLIN | POLLRDNORM; if (!skb_queue_empty(&pn->ctrlreq_queue)) -- cgit v1.2.3 From acaf7df610ff3faf1778ce40d601fc3dd4a41b40 Mon Sep 17 00:00:00 2001 From: Rémi Denis-Courmont Date: Tue, 8 Mar 2011 22:44:11 +0000 Subject: Phonet: provide pipe socket option to retrieve the pipe identifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User-space sometimes needs this information. In particular, the GPRS context or the AT commands pipe setups may use the pipe handle as a reference. This removes the settable pipe handle with CONFIG_PHONET_PIPECTRLR. It did not handle error cases correctly. Furthermore, the kernel *could* implement a smart scheme for allocating handles (if ever needed), but userspace really cannot. Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- Documentation/networking/phonet.txt | 7 ++++--- include/linux/phonet.h | 2 +- net/phonet/pep.c | 15 +++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/Documentation/networking/phonet.txt b/Documentation/networking/phonet.txt index 24ad2adba6e5..cacac968c1c3 100644 --- a/Documentation/networking/phonet.txt +++ b/Documentation/networking/phonet.txt @@ -181,6 +181,10 @@ The pipe protocol provides two socket options at the SOL_PNPIPE level: interface index of the network interface created by PNPIPE_ENCAP, or zero if encapsulation is off. + PNPIPE_HANDLE is a read-only integer value. It contains the underlying + identifier ("pipe handle") of the pipe. This is only defined for + socket descriptors that are already connected or being connected. + Phonet Pipe-controller Implementation ------------------------------------- @@ -199,9 +203,6 @@ between itself and a remote pipe-end point (e.g. modem). The implementation adds socket options at SOL_PNPIPE level: - PNPIPE_PIPE_HANDLE - It accepts an integer argument for setting value of pipe handle. - PNPIPE_ENABLE accepts one integer value (int). If set to zero, the pipe is disabled. If the value is non-zero, the pipe is enabled. If the pipe is not (yet) connected, ENOTCONN is error is returned. diff --git a/include/linux/phonet.h b/include/linux/phonet.h index 26c8df786918..32a0965da953 100644 --- a/include/linux/phonet.h +++ b/include/linux/phonet.h @@ -36,7 +36,7 @@ /* Socket options for SOL_PNPIPE level */ #define PNPIPE_ENCAP 1 #define PNPIPE_IFINDEX 2 -#define PNPIPE_PIPE_HANDLE 3 +#define PNPIPE_HANDLE 3 #define PNPIPE_ENABLE 4 /* unused slot */ diff --git a/net/phonet/pep.c b/net/phonet/pep.c index c0fab4cfcef7..abfb795af142 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -853,6 +853,7 @@ static int pep_sock_connect(struct sock *sk, struct sockaddr *addr, int len) pn->pn_sk.dobject = pn_sockaddr_get_object(spn); pn->pn_sk.resource = pn_sockaddr_get_resource(spn); + pn->pipe_handle = 1; /* anything but INVALID_HANDLE */ return pipe_handler_request(sk, PNS_PEP_CONNECT_REQ, PN_PIPE_DISABLE, data, 4); } @@ -909,14 +910,6 @@ static int pep_setsockopt(struct sock *sk, int level, int optname, lock_sock(sk); switch (optname) { -#ifdef CONFIG_PHONET_PIPECTRLR - case PNPIPE_PIPE_HANDLE: - if (val) { - pn->pipe_handle = val; - break; - } -#endif - case PNPIPE_ENCAP: if (val && val != PNPIPE_ENCAP_IP) { err = -EINVAL; @@ -982,6 +975,12 @@ static int pep_getsockopt(struct sock *sk, int level, int optname, val = pn->ifindex; break; + case PNPIPE_HANDLE: + val = pn->pipe_handle; + if (val == PN_PIPE_INVALID_HANDLE) + return -EINVAL; + break; + #ifdef CONFIG_PHONET_PIPECTRLR case PNPIPE_ENABLE: val = sk->sk_state == TCP_ESTABLISHED; -- cgit v1.2.3 From a015f6f49968c330b236ca2f6c2170820414f922 Mon Sep 17 00:00:00 2001 From: Rémi Denis-Courmont Date: Tue, 8 Mar 2011 22:44:13 +0000 Subject: Phonet: kill the ST-Ericsson pipe controller Kconfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is now a run-time choice so that a single kernel can support both old and new generation ISI modems. Support for manually enabling the pipe flow is removed as it did not work properly, does not fit well with the socket API, and I am not aware of any use at the moment. Signed-off-by: Rémi Denis-Courmont Signed-off-by: David S. Miller --- Documentation/networking/phonet.txt | 13 ------------- include/linux/phonet.h | 2 -- net/phonet/Kconfig | 12 ------------ net/phonet/pep.c | 25 ------------------------- 4 files changed, 52 deletions(-) (limited to 'include') diff --git a/Documentation/networking/phonet.txt b/Documentation/networking/phonet.txt index 3d127791cb06..81003581f47a 100644 --- a/Documentation/networking/phonet.txt +++ b/Documentation/networking/phonet.txt @@ -205,19 +205,6 @@ The pipe protocol provides two socket options at the SOL_PNPIPE level: socket descriptors that are already connected or being connected. -Phonet Pipe-controller Implementation -------------------------------------- - -Phonet Pipe-controller is enabled by selecting the CONFIG_PHONET_PIPECTRLR -Kconfig option. - -The implementation adds socket options at SOL_PNPIPE level: - - PNPIPE_ENABLE accepts one integer value (int). If set to zero, the pipe - is disabled. If the value is non-zero, the pipe is enabled. If the pipe - is not (yet) connected, ENOTCONN is error is returned. - - Authors ------- diff --git a/include/linux/phonet.h b/include/linux/phonet.h index 32a0965da953..6fb13841db45 100644 --- a/include/linux/phonet.h +++ b/include/linux/phonet.h @@ -37,8 +37,6 @@ #define PNPIPE_ENCAP 1 #define PNPIPE_IFINDEX 2 #define PNPIPE_HANDLE 3 -#define PNPIPE_ENABLE 4 -/* unused slot */ #define PNADDR_ANY 0 #define PNADDR_BROADCAST 0xFC diff --git a/net/phonet/Kconfig b/net/phonet/Kconfig index 0d9b8a220a78..6ec7d55b1769 100644 --- a/net/phonet/Kconfig +++ b/net/phonet/Kconfig @@ -14,15 +14,3 @@ config PHONET To compile this driver as a module, choose M here: the module will be called phonet. If unsure, say N. - -config PHONET_PIPECTRLR - bool "Phonet Pipe Controller (EXPERIMENTAL)" - depends on PHONET && EXPERIMENTAL - default N - help - The Pipe Controller implementation in Phonet stack to support Pipe - data with Nokia Slim modems like WG2.5 used on ST-Ericsson U8500 - platform. - - This option is incompatible with older Nokia modems. - Say N here unless you really know what you are doing. diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 671effb4ea15..68e635f11de8 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -167,15 +167,6 @@ static int pipe_handler_send_created_ind(struct sock *sk) data, 4, GFP_ATOMIC); } -#ifdef CONFIG_PHONET_PIPECTRLR -static int pipe_handler_enable_pipe(struct sock *sk, int enable) -{ - u8 id = enable ? PNS_PEP_ENABLE_REQ : PNS_PEP_DISABLE_REQ; - - return pipe_handler_request(sk, id, PAD, NULL, 0); -} -#endif - static int pep_accept_conn(struct sock *sk, struct sk_buff *skb) { static const u8 data[20] = { @@ -968,16 +959,6 @@ static int pep_setsockopt(struct sock *sk, int level, int optname, } goto out_norel; -#ifdef CONFIG_PHONET_PIPECTRLR - case PNPIPE_ENABLE: - if ((1 << sk->sk_state) & ~(TCPF_SYN_RECV|TCPF_ESTABLISHED)) { - err = -ENOTCONN; - break; - } - err = pipe_handler_enable_pipe(sk, val); - break; -#endif - default: err = -ENOPROTOOPT; } @@ -1013,12 +994,6 @@ static int pep_getsockopt(struct sock *sk, int level, int optname, return -EINVAL; break; -#ifdef CONFIG_PHONET_PIPECTRLR - case PNPIPE_ENABLE: - val = sk->sk_state == TCP_ESTABLISHED; - break; -#endif - default: return -ENOPROTOOPT; } -- cgit v1.2.3 From 80751e2b8ffcbbe065e850d943301aa1ab219599 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Mon, 7 Mar 2011 11:14:23 -0800 Subject: ieee80211: add IEEE80211_COUNTRY_STRING_LEN definition and make use of it in wireless drivers Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/at76c50x-usb.h | 2 +- drivers/net/wireless/ipw2x00/ipw2200.h | 2 +- drivers/net/wireless/libertas/host.h | 2 +- drivers/net/wireless/wl1251/wl12xx_80211.h | 3 +-- drivers/net/wireless/wl12xx/wl12xx_80211.h | 3 +-- include/linux/ieee80211.h | 3 +++ 6 files changed, 8 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/at76c50x-usb.h b/drivers/net/wireless/at76c50x-usb.h index 4a37447dfc01..f14a65473fe8 100644 --- a/drivers/net/wireless/at76c50x-usb.h +++ b/drivers/net/wireless/at76c50x-usb.h @@ -290,7 +290,7 @@ struct mib_mac_mgmt { u8 res; u8 multi_domain_capability_implemented; u8 multi_domain_capability_enabled; - u8 country_string[3]; + u8 country_string[IEEE80211_COUNTRY_STRING_LEN]; u8 reserved[3]; } __packed; diff --git a/drivers/net/wireless/ipw2x00/ipw2200.h b/drivers/net/wireless/ipw2x00/ipw2200.h index d7d049c7a4fa..d9e1d9bad581 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.h +++ b/drivers/net/wireless/ipw2x00/ipw2200.h @@ -961,7 +961,7 @@ struct ipw_country_channel_info { struct ipw_country_info { u8 id; u8 length; - u8 country_str[3]; + u8 country_str[IEEE80211_COUNTRY_STRING_LEN]; struct ipw_country_channel_info groups[7]; } __packed; diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h index 5eac1351a021..6cb6935ee4a3 100644 --- a/drivers/net/wireless/libertas/host.h +++ b/drivers/net/wireless/libertas/host.h @@ -387,7 +387,7 @@ struct lbs_offset_value { struct mrvl_ie_domain_param_set { struct mrvl_ie_header header; - u8 country_code[3]; + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; struct ieee80211_country_ie_triplet triplet[MAX_11D_TRIPLETS]; } __packed; diff --git a/drivers/net/wireless/wl1251/wl12xx_80211.h b/drivers/net/wireless/wl1251/wl12xx_80211.h index 184628027213..1417b1445c3d 100644 --- a/drivers/net/wireless/wl1251/wl12xx_80211.h +++ b/drivers/net/wireless/wl1251/wl12xx_80211.h @@ -54,7 +54,6 @@ /* This really should be 8, but not for our firmware */ #define MAX_SUPPORTED_RATES 32 -#define COUNTRY_STRING_LEN 3 #define MAX_COUNTRY_TRIPLETS 32 /* Headers */ @@ -98,7 +97,7 @@ struct country_triplet { struct wl12xx_ie_country { struct wl12xx_ie_header header; - u8 country_string[COUNTRY_STRING_LEN]; + u8 country_string[IEEE80211_COUNTRY_STRING_LEN]; struct country_triplet triplets[MAX_COUNTRY_TRIPLETS]; } __packed; diff --git a/drivers/net/wireless/wl12xx/wl12xx_80211.h b/drivers/net/wireless/wl12xx/wl12xx_80211.h index 67dcf8f28cd3..18fe542360f2 100644 --- a/drivers/net/wireless/wl12xx/wl12xx_80211.h +++ b/drivers/net/wireless/wl12xx/wl12xx_80211.h @@ -55,7 +55,6 @@ /* This really should be 8, but not for our firmware */ #define MAX_SUPPORTED_RATES 32 -#define COUNTRY_STRING_LEN 3 #define MAX_COUNTRY_TRIPLETS 32 /* Headers */ @@ -99,7 +98,7 @@ struct country_triplet { struct wl12xx_ie_country { struct wl12xx_ie_header header; - u8 country_string[COUNTRY_STRING_LEN]; + u8 country_string[IEEE80211_COUNTRY_STRING_LEN]; struct country_triplet triplets[MAX_COUNTRY_TRIPLETS]; } __packed; diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 294169e31364..2d1c6117d92c 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1325,6 +1325,9 @@ enum { /* Although the spec says 8 I'm seeing 6 in practice */ #define IEEE80211_COUNTRY_IE_MIN_LEN 6 +/* The Country String field of the element shall be 3 octets in length */ +#define IEEE80211_COUNTRY_STRING_LEN 3 + /* * For regulatory extension stuff see IEEE 802.11-2007 * Annex I (page 1141) and Annex J (page 1147). Also -- cgit v1.2.3 From 2f4e1b3970973bbb57cc3a3b9d67e67c1c648c37 Mon Sep 17 00:00:00 2001 From: Mario Schuknecht Date: Wed, 9 Mar 2011 14:08:09 -0800 Subject: tcp: ioctl type SIOCOUTQNSD returns amount of data not sent In contrast to SIOCOUTQ which returns the amount of data sent but not yet acknowledged plus data not yet sent this patch only returns the data not sent. For various methods of live streaming bitrate control it may be helpful to know how much data are in the tcp outqueue are not sent yet. Signed-off-by: Mario Schuknecht Signed-off-by: Steffen Sledz Signed-off-by: David S. Miller --- include/linux/sockios.h | 4 +++- net/ipv4/tcp.c | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/sockios.h b/include/linux/sockios.h index 241f179347d9..7997a506ad41 100644 --- a/include/linux/sockios.h +++ b/include/linux/sockios.h @@ -22,7 +22,7 @@ /* Linux-specific socket ioctls */ #define SIOCINQ FIONREAD -#define SIOCOUTQ TIOCOUTQ +#define SIOCOUTQ TIOCOUTQ /* output queue size (not sent + not acked) */ /* Routing table calls. */ #define SIOCADDRT 0x890B /* add routing table entry */ @@ -83,6 +83,8 @@ #define SIOCWANDEV 0x894A /* get/set netdev parameters */ +#define SIOCOUTQNSD 0x894B /* output queue size (not sent only) */ + /* ARP cache control calls. */ /* 0x8950 - 0x8952 * obsolete calls, don't re-use */ #define SIOCDARP 0x8953 /* delete ARP table entry */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index a17a5a72b98d..b22d45010545 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -505,6 +505,15 @@ int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg) else answ = tp->write_seq - tp->snd_una; break; + case SIOCOUTQNSD: + if (sk->sk_state == TCP_LISTEN) + return -EINVAL; + + if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) + answ = 0; + else + answ = tp->write_seq - tp->snd_nxt; + break; default: return -ENOIOCTLCMD; } -- cgit v1.2.3 From 8909c9ad8ff03611c9c96c9a92656213e4bb495b Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Wed, 2 Mar 2011 00:33:13 +0300 Subject: net: don't allow CAP_NET_ADMIN to load non-netdev kernel modules Since a8f80e8ff94ecba629542d9b4b5f5a8ee3eb565c any process with CAP_NET_ADMIN may load any module from /lib/modules/. This doesn't mean that CAP_NET_ADMIN is a superset of CAP_SYS_MODULE as modules are limited to /lib/modules/**. However, CAP_NET_ADMIN capability shouldn't allow anybody load any module not related to networking. This patch restricts an ability of autoloading modules to netdev modules with explicit aliases. This fixes CVE-2011-1019. Arnd Bergmann suggested to leave untouched the old pre-v2.6.32 behavior of loading netdev modules by name (without any prefix) for processes with CAP_SYS_MODULE to maintain the compatibility with network scripts that use autoloading netdev modules by aliases like "eth0", "wlan0". Currently there are only three users of the feature in the upstream kernel: ipip, ip_gre and sit. root@albatros:~# capsh --drop=$(seq -s, 0 11),$(seq -s, 13 34) -- root@albatros:~# grep Cap /proc/$$/status CapInh: 0000000000000000 CapPrm: fffffff800001000 CapEff: fffffff800001000 CapBnd: fffffff800001000 root@albatros:~# modprobe xfs FATAL: Error inserting xfs (/lib/modules/2.6.38-rc6-00001-g2bf4ca3/kernel/fs/xfs/xfs.ko): Operation not permitted root@albatros:~# lsmod | grep xfs root@albatros:~# ifconfig xfs xfs: error fetching interface information: Device not found root@albatros:~# lsmod | grep xfs root@albatros:~# lsmod | grep sit root@albatros:~# ifconfig sit sit: error fetching interface information: Device not found root@albatros:~# lsmod | grep sit root@albatros:~# ifconfig sit0 sit0 Link encap:IPv6-in-IPv4 NOARP MTU:1480 Metric:1 root@albatros:~# lsmod | grep sit sit 10457 0 tunnel4 2957 1 sit For CAP_SYS_MODULE module loading is still relaxed: root@albatros:~# grep Cap /proc/$$/status CapInh: 0000000000000000 CapPrm: ffffffffffffffff CapEff: ffffffffffffffff CapBnd: ffffffffffffffff root@albatros:~# ifconfig xfs xfs: error fetching interface information: Device not found root@albatros:~# lsmod | grep xfs xfs 745319 0 Reference: https://lkml.org/lkml/2011/2/24/203 Signed-off-by: Vasiliy Kulikov Signed-off-by: Michael Tokarev Acked-by: David S. Miller Acked-by: Kees Cook Signed-off-by: James Morris --- include/linux/netdevice.h | 3 +++ net/core/dev.c | 12 ++++++++++-- net/ipv4/ip_gre.c | 2 +- net/ipv4/ipip.c | 2 +- net/ipv6/sit.c | 2 +- 5 files changed, 16 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d971346b0340..71caf7a5e6c6 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2392,6 +2392,9 @@ extern int netdev_notice(const struct net_device *dev, const char *format, ...) extern int netdev_info(const struct net_device *dev, const char *format, ...) __attribute__ ((format (printf, 2, 3))); +#define MODULE_ALIAS_NETDEV(device) \ + MODULE_ALIAS("netdev-" device) + #if defined(DEBUG) #define netdev_dbg(__dev, format, args...) \ netdev_printk(KERN_DEBUG, __dev, format, ##args) diff --git a/net/core/dev.c b/net/core/dev.c index 8ae6631abcc2..6561021d22d1 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1114,13 +1114,21 @@ EXPORT_SYMBOL(netdev_bonding_change); void dev_load(struct net *net, const char *name) { struct net_device *dev; + int no_module; rcu_read_lock(); dev = dev_get_by_name_rcu(net, name); rcu_read_unlock(); - if (!dev && capable(CAP_NET_ADMIN)) - request_module("%s", name); + no_module = !dev; + if (no_module && capable(CAP_NET_ADMIN)) + no_module = request_module("netdev-%s", name); + if (no_module && capable(CAP_SYS_MODULE)) { + if (!request_module("%s", name)) + pr_err("Loading kernel module for a network device " +"with CAP_SYS_MODULE (deprecated). Use CAP_NET_ADMIN and alias netdev-%s " +"instead\n", name); + } } EXPORT_SYMBOL(dev_load); diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 6613edfac28c..d1d0e2c256fc 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1765,4 +1765,4 @@ module_exit(ipgre_fini); MODULE_LICENSE("GPL"); MODULE_ALIAS_RTNL_LINK("gre"); MODULE_ALIAS_RTNL_LINK("gretap"); -MODULE_ALIAS("gre0"); +MODULE_ALIAS_NETDEV("gre0"); diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 988f52fba54a..a5f58e7cbb26 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -913,4 +913,4 @@ static void __exit ipip_fini(void) module_init(ipip_init); module_exit(ipip_fini); MODULE_LICENSE("GPL"); -MODULE_ALIAS("tunl0"); +MODULE_ALIAS_NETDEV("tunl0"); diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 8ce38f10a547..d2c16e10f650 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1290,4 +1290,4 @@ static int __init sit_init(void) module_init(sit_init); module_exit(sit_cleanup); MODULE_LICENSE("GPL"); -MODULE_ALIAS("sit0"); +MODULE_ALIAS_NETDEV("sit0"); -- cgit v1.2.3 From 684adca4f84365ca327e06dba696b62de7a79eca Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 10 Mar 2011 11:14:17 +1100 Subject: sysctl: the include of rcupdate.h is only needed in the kernel Fixes this build-check error: include/linux/sysctl.h:28: included file 'linux/rcupdate.h' is not exported Signed-off-by: Stephen Rothwell Signed-off-by: Linus Torvalds --- include/linux/sysctl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index bb7c2b086fa4..11684d9e6bd2 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -25,7 +25,6 @@ #include #include #include -#include struct completion; @@ -931,6 +930,7 @@ enum #ifdef __KERNEL__ #include +#include /* For the /proc/sys support */ struct ctl_table; -- cgit v1.2.3 From 3cca6dc1c81e2407928dc4c6105252146fd3924f Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 2 Mar 2011 11:08:00 -0500 Subject: block: add API for delaying work/request_fn a little bit Currently we use plugging for that, but as plugging is going away, we need an alternative mechanism. Signed-off-by: Jens Axboe --- block/blk-core.c | 29 +++++++++++++++++++++++++++++ include/linux/blkdev.h | 6 ++++++ 2 files changed, 35 insertions(+) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 3cc17e6064d6..e958c7a1e462 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -197,6 +197,32 @@ void blk_dump_rq_flags(struct request *rq, char *msg) } EXPORT_SYMBOL(blk_dump_rq_flags); +static void blk_delay_work(struct work_struct *work) +{ + struct request_queue *q; + + q = container_of(work, struct request_queue, delay_work.work); + spin_lock_irq(q->queue_lock); + q->request_fn(q); + spin_unlock_irq(q->queue_lock); +} + +/** + * blk_delay_queue - restart queueing after defined interval + * @q: The &struct request_queue in question + * @msecs: Delay in msecs + * + * Description: + * Sometimes queueing needs to be postponed for a little while, to allow + * resources to come back. This function will make sure that queueing is + * restarted around the specified time. + */ +void blk_delay_queue(struct request_queue *q, unsigned long msecs) +{ + schedule_delayed_work(&q->delay_work, msecs_to_jiffies(msecs)); +} +EXPORT_SYMBOL(blk_delay_queue); + /* * "plug" the device if there are no outstanding requests: this will * force the transfer to start only after we have put all the requests @@ -363,6 +389,7 @@ EXPORT_SYMBOL(blk_start_queue); void blk_stop_queue(struct request_queue *q) { blk_remove_plug(q); + cancel_delayed_work(&q->delay_work); queue_flag_set(QUEUE_FLAG_STOPPED, q); } EXPORT_SYMBOL(blk_stop_queue); @@ -387,6 +414,7 @@ void blk_sync_queue(struct request_queue *q) del_timer_sync(&q->timeout); cancel_work_sync(&q->unplug_work); throtl_shutdown_timer_wq(q); + cancel_delayed_work_sync(&q->delay_work); } EXPORT_SYMBOL(blk_sync_queue); @@ -534,6 +562,7 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) INIT_LIST_HEAD(&q->flush_queue[1]); INIT_LIST_HEAD(&q->flush_data_in_flight); INIT_WORK(&q->unplug_work, blk_unplug_work); + INIT_DELAYED_WORK(&q->delay_work, blk_delay_work); kobject_init(&q->kobj, &blk_queue_ktype); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index e3ee74fc5903..f55b2a8b6610 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -300,6 +300,11 @@ struct request_queue unsigned long unplug_delay; /* After this many jiffies */ struct work_struct unplug_work; + /* + * Delayed queue handling + */ + struct delayed_work delay_work; + struct backing_dev_info backing_dev_info; /* @@ -677,6 +682,7 @@ extern int blk_insert_cloned_request(struct request_queue *q, extern void blk_plug_device(struct request_queue *); extern void blk_plug_device_unlocked(struct request_queue *); extern int blk_remove_plug(struct request_queue *); +extern void blk_delay_queue(struct request_queue *, unsigned long); extern void blk_recount_segments(struct request_queue *, struct bio *); extern int scsi_cmd_ioctl(struct request_queue *, struct gendisk *, fmode_t, unsigned int, void __user *); -- cgit v1.2.3 From 73c101011926c5832e6e141682180c4debe2cf45 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 8 Mar 2011 13:19:51 +0100 Subject: block: initial patch for on-stack per-task plugging This patch adds support for creating a queuing context outside of the queue itself. This enables us to batch up pieces of IO before grabbing the block device queue lock and submitting them to the IO scheduler. The context is created on the stack of the process and assigned in the task structure, so that we can auto-unplug it if we hit a schedule event. The current queue plugging happens implicitly if IO is submitted to an empty device, yet callers have to remember to unplug that IO when they are going to wait for it. This is an ugly API and has caused bugs in the past. Additionally, it requires hacks in the vm (->sync_page() callback) to handle that logic. By switching to an explicit plugging scheme we make the API a lot nicer and can get rid of the ->sync_page() hack in the vm. Signed-off-by: Jens Axboe --- block/blk-core.c | 369 ++++++++++++++++++++++++++++++++++------------ block/blk-flush.c | 3 +- block/elevator.c | 6 +- include/linux/blk_types.h | 2 + include/linux/blkdev.h | 42 ++++++ include/linux/elevator.h | 1 + include/linux/sched.h | 6 + kernel/exit.c | 1 + kernel/fork.c | 3 + kernel/sched.c | 12 ++ 10 files changed, 344 insertions(+), 101 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index e958c7a1e462..6efb55cc5af0 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -27,6 +27,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include @@ -203,7 +204,7 @@ static void blk_delay_work(struct work_struct *work) q = container_of(work, struct request_queue, delay_work.work); spin_lock_irq(q->queue_lock); - q->request_fn(q); + __blk_run_queue(q); spin_unlock_irq(q->queue_lock); } @@ -686,6 +687,8 @@ int blk_get_queue(struct request_queue *q) static inline void blk_free_request(struct request_queue *q, struct request *rq) { + BUG_ON(rq->cmd_flags & REQ_ON_PLUG); + if (rq->cmd_flags & REQ_ELVPRIV) elv_put_request(q, rq); mempool_free(rq, q->rq.rq_pool); @@ -1051,6 +1054,13 @@ void blk_requeue_request(struct request_queue *q, struct request *rq) } EXPORT_SYMBOL(blk_requeue_request); +static void add_acct_request(struct request_queue *q, struct request *rq, + int where) +{ + drive_stat_acct(rq, 1); + __elv_add_request(q, rq, where, 0); +} + /** * blk_insert_request - insert a special request into a request queue * @q: request queue where request should be inserted @@ -1093,8 +1103,7 @@ void blk_insert_request(struct request_queue *q, struct request *rq, if (blk_rq_tagged(rq)) blk_queue_end_tag(q, rq); - drive_stat_acct(rq, 1); - __elv_add_request(q, rq, where, 0); + add_acct_request(q, rq, where); __blk_run_queue(q); spin_unlock_irqrestore(q->queue_lock, flags); } @@ -1215,6 +1224,113 @@ void blk_add_request_payload(struct request *rq, struct page *page, } EXPORT_SYMBOL_GPL(blk_add_request_payload); +static bool bio_attempt_back_merge(struct request_queue *q, struct request *req, + struct bio *bio) +{ + const int ff = bio->bi_rw & REQ_FAILFAST_MASK; + + /* + * Debug stuff, kill later + */ + if (!rq_mergeable(req)) { + blk_dump_rq_flags(req, "back"); + return false; + } + + if (!ll_back_merge_fn(q, req, bio)) + return false; + + trace_block_bio_backmerge(q, bio); + + if ((req->cmd_flags & REQ_FAILFAST_MASK) != ff) + blk_rq_set_mixed_merge(req); + + req->biotail->bi_next = bio; + req->biotail = bio; + req->__data_len += bio->bi_size; + req->ioprio = ioprio_best(req->ioprio, bio_prio(bio)); + + drive_stat_acct(req, 0); + return true; +} + +static bool bio_attempt_front_merge(struct request_queue *q, + struct request *req, struct bio *bio) +{ + const int ff = bio->bi_rw & REQ_FAILFAST_MASK; + sector_t sector; + + /* + * Debug stuff, kill later + */ + if (!rq_mergeable(req)) { + blk_dump_rq_flags(req, "front"); + return false; + } + + if (!ll_front_merge_fn(q, req, bio)) + return false; + + trace_block_bio_frontmerge(q, bio); + + if ((req->cmd_flags & REQ_FAILFAST_MASK) != ff) + blk_rq_set_mixed_merge(req); + + sector = bio->bi_sector; + + bio->bi_next = req->bio; + req->bio = bio; + + /* + * may not be valid. if the low level driver said + * it didn't need a bounce buffer then it better + * not touch req->buffer either... + */ + req->buffer = bio_data(bio); + req->__sector = bio->bi_sector; + req->__data_len += bio->bi_size; + req->ioprio = ioprio_best(req->ioprio, bio_prio(bio)); + + drive_stat_acct(req, 0); + return true; +} + +/* + * Attempts to merge with the plugged list in the current process. Returns + * true if merge was succesful, otherwise false. + */ +static bool attempt_plug_merge(struct task_struct *tsk, struct request_queue *q, + struct bio *bio) +{ + struct blk_plug *plug; + struct request *rq; + bool ret = false; + + plug = tsk->plug; + if (!plug) + goto out; + + list_for_each_entry_reverse(rq, &plug->list, queuelist) { + int el_ret; + + if (rq->q != q) + continue; + + el_ret = elv_try_merge(rq, bio); + if (el_ret == ELEVATOR_BACK_MERGE) { + ret = bio_attempt_back_merge(q, rq, bio); + if (ret) + break; + } else if (el_ret == ELEVATOR_FRONT_MERGE) { + ret = bio_attempt_front_merge(q, rq, bio); + if (ret) + break; + } + } +out: + return ret; +} + void init_request_from_bio(struct request *req, struct bio *bio) { req->cpu = bio->bi_comp_cpu; @@ -1230,26 +1346,12 @@ void init_request_from_bio(struct request *req, struct bio *bio) blk_rq_bio_prep(req->q, req, bio); } -/* - * Only disabling plugging for non-rotational devices if it does tagging - * as well, otherwise we do need the proper merging - */ -static inline bool queue_should_plug(struct request_queue *q) -{ - return !(blk_queue_nonrot(q) && blk_queue_tagged(q)); -} - static int __make_request(struct request_queue *q, struct bio *bio) { - struct request *req; - int el_ret; - unsigned int bytes = bio->bi_size; - const unsigned short prio = bio_prio(bio); const bool sync = !!(bio->bi_rw & REQ_SYNC); - const bool unplug = !!(bio->bi_rw & REQ_UNPLUG); - const unsigned long ff = bio->bi_rw & REQ_FAILFAST_MASK; - int where = ELEVATOR_INSERT_SORT; - int rw_flags; + struct blk_plug *plug; + int el_ret, rw_flags, where = ELEVATOR_INSERT_SORT; + struct request *req; /* * low level driver can indicate that it wants pages above a @@ -1258,78 +1360,36 @@ static int __make_request(struct request_queue *q, struct bio *bio) */ blk_queue_bounce(q, &bio); - spin_lock_irq(q->queue_lock); - if (bio->bi_rw & (REQ_FLUSH | REQ_FUA)) { + spin_lock_irq(q->queue_lock); where = ELEVATOR_INSERT_FLUSH; goto get_rq; } - if (elv_queue_empty(q)) - goto get_rq; - - el_ret = elv_merge(q, &req, bio); - switch (el_ret) { - case ELEVATOR_BACK_MERGE: - BUG_ON(!rq_mergeable(req)); - - if (!ll_back_merge_fn(q, req, bio)) - break; - - trace_block_bio_backmerge(q, bio); - - if ((req->cmd_flags & REQ_FAILFAST_MASK) != ff) - blk_rq_set_mixed_merge(req); - - req->biotail->bi_next = bio; - req->biotail = bio; - req->__data_len += bytes; - req->ioprio = ioprio_best(req->ioprio, prio); - if (!blk_rq_cpu_valid(req)) - req->cpu = bio->bi_comp_cpu; - drive_stat_acct(req, 0); - elv_bio_merged(q, req, bio); - if (!attempt_back_merge(q, req)) - elv_merged_request(q, req, el_ret); + /* + * Check if we can merge with the plugged list before grabbing + * any locks. + */ + if (attempt_plug_merge(current, q, bio)) goto out; - case ELEVATOR_FRONT_MERGE: - BUG_ON(!rq_mergeable(req)); - - if (!ll_front_merge_fn(q, req, bio)) - break; - - trace_block_bio_frontmerge(q, bio); + spin_lock_irq(q->queue_lock); - if ((req->cmd_flags & REQ_FAILFAST_MASK) != ff) { - blk_rq_set_mixed_merge(req); - req->cmd_flags &= ~REQ_FAILFAST_MASK; - req->cmd_flags |= ff; + el_ret = elv_merge(q, &req, bio); + if (el_ret == ELEVATOR_BACK_MERGE) { + BUG_ON(req->cmd_flags & REQ_ON_PLUG); + if (bio_attempt_back_merge(q, req, bio)) { + if (!attempt_back_merge(q, req)) + elv_merged_request(q, req, el_ret); + goto out_unlock; + } + } else if (el_ret == ELEVATOR_FRONT_MERGE) { + BUG_ON(req->cmd_flags & REQ_ON_PLUG); + if (bio_attempt_front_merge(q, req, bio)) { + if (!attempt_front_merge(q, req)) + elv_merged_request(q, req, el_ret); + goto out_unlock; } - - bio->bi_next = req->bio; - req->bio = bio; - - /* - * may not be valid. if the low level driver said - * it didn't need a bounce buffer then it better - * not touch req->buffer either... - */ - req->buffer = bio_data(bio); - req->__sector = bio->bi_sector; - req->__data_len += bytes; - req->ioprio = ioprio_best(req->ioprio, prio); - if (!blk_rq_cpu_valid(req)) - req->cpu = bio->bi_comp_cpu; - drive_stat_acct(req, 0); - elv_bio_merged(q, req, bio); - if (!attempt_front_merge(q, req)) - elv_merged_request(q, req, el_ret); - goto out; - - /* ELV_NO_MERGE: elevator says don't/can't merge. */ - default: - ; } get_rq: @@ -1356,20 +1416,35 @@ get_rq: */ init_request_from_bio(req, bio); - spin_lock_irq(q->queue_lock); if (test_bit(QUEUE_FLAG_SAME_COMP, &q->queue_flags) || - bio_flagged(bio, BIO_CPU_AFFINE)) - req->cpu = blk_cpu_to_group(smp_processor_id()); - if (queue_should_plug(q) && elv_queue_empty(q)) - blk_plug_device(q); - - /* insert the request into the elevator */ - drive_stat_acct(req, 1); - __elv_add_request(q, req, where, 0); + bio_flagged(bio, BIO_CPU_AFFINE)) { + req->cpu = blk_cpu_to_group(get_cpu()); + put_cpu(); + } + + plug = current->plug; + if (plug && !sync) { + if (!plug->should_sort && !list_empty(&plug->list)) { + struct request *__rq; + + __rq = list_entry_rq(plug->list.prev); + if (__rq->q != q) + plug->should_sort = 1; + } + /* + * Debug flag, kill later + */ + req->cmd_flags |= REQ_ON_PLUG; + list_add_tail(&req->queuelist, &plug->list); + drive_stat_acct(req, 1); + } else { + spin_lock_irq(q->queue_lock); + add_acct_request(q, req, where); + __blk_run_queue(q); +out_unlock: + spin_unlock_irq(q->queue_lock); + } out: - if (unplug || !queue_should_plug(q)) - __generic_unplug_device(q); - spin_unlock_irq(q->queue_lock); return 0; } @@ -1772,9 +1847,7 @@ int blk_insert_cloned_request(struct request_queue *q, struct request *rq) */ BUG_ON(blk_queued_rq(rq)); - drive_stat_acct(rq, 1); - __elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 0); - + add_acct_request(q, rq, ELEVATOR_INSERT_BACK); spin_unlock_irqrestore(q->queue_lock, flags); return 0; @@ -2659,6 +2732,106 @@ int kblockd_schedule_delayed_work(struct request_queue *q, } EXPORT_SYMBOL(kblockd_schedule_delayed_work); +#define PLUG_MAGIC 0x91827364 + +void blk_start_plug(struct blk_plug *plug) +{ + struct task_struct *tsk = current; + + plug->magic = PLUG_MAGIC; + INIT_LIST_HEAD(&plug->list); + plug->should_sort = 0; + + /* + * If this is a nested plug, don't actually assign it. It will be + * flushed on its own. + */ + if (!tsk->plug) { + /* + * Store ordering should not be needed here, since a potential + * preempt will imply a full memory barrier + */ + tsk->plug = plug; + } +} +EXPORT_SYMBOL(blk_start_plug); + +static int plug_rq_cmp(void *priv, struct list_head *a, struct list_head *b) +{ + struct request *rqa = container_of(a, struct request, queuelist); + struct request *rqb = container_of(b, struct request, queuelist); + + return !(rqa->q == rqb->q); +} + +static void flush_plug_list(struct blk_plug *plug) +{ + struct request_queue *q; + unsigned long flags; + struct request *rq; + + BUG_ON(plug->magic != PLUG_MAGIC); + + if (list_empty(&plug->list)) + return; + + if (plug->should_sort) + list_sort(NULL, &plug->list, plug_rq_cmp); + + q = NULL; + local_irq_save(flags); + while (!list_empty(&plug->list)) { + rq = list_entry_rq(plug->list.next); + list_del_init(&rq->queuelist); + BUG_ON(!(rq->cmd_flags & REQ_ON_PLUG)); + BUG_ON(!rq->q); + if (rq->q != q) { + if (q) { + __blk_run_queue(q); + spin_unlock(q->queue_lock); + } + q = rq->q; + spin_lock(q->queue_lock); + } + rq->cmd_flags &= ~REQ_ON_PLUG; + + /* + * rq is already accounted, so use raw insert + */ + __elv_add_request(q, rq, ELEVATOR_INSERT_SORT, 0); + } + + if (q) { + __blk_run_queue(q); + spin_unlock(q->queue_lock); + } + + BUG_ON(!list_empty(&plug->list)); + local_irq_restore(flags); +} + +static void __blk_finish_plug(struct task_struct *tsk, struct blk_plug *plug) +{ + flush_plug_list(plug); + + if (plug == tsk->plug) + tsk->plug = NULL; +} + +void blk_finish_plug(struct blk_plug *plug) +{ + if (plug) + __blk_finish_plug(current, plug); +} +EXPORT_SYMBOL(blk_finish_plug); + +void __blk_flush_plug(struct task_struct *tsk, struct blk_plug *plug) +{ + __blk_finish_plug(tsk, plug); + tsk->plug = plug; +} +EXPORT_SYMBOL(__blk_flush_plug); + int __init blk_dev_init(void) { BUILD_BUG_ON(__REQ_NR_BITS > 8 * diff --git a/block/blk-flush.c b/block/blk-flush.c index a867e3f524f3..1e2aa8a8908c 100644 --- a/block/blk-flush.c +++ b/block/blk-flush.c @@ -264,10 +264,9 @@ static bool blk_kick_flush(struct request_queue *q) static void flush_data_end_io(struct request *rq, int error) { 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) + if (blk_flush_complete_seq(rq, REQ_FSEQ_DATA, error)) __blk_run_queue(q); } diff --git a/block/elevator.c b/block/elevator.c index f98e92edc937..25713927c0d3 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -113,7 +113,7 @@ int elv_rq_merge_ok(struct request *rq, struct bio *bio) } EXPORT_SYMBOL(elv_rq_merge_ok); -static inline int elv_try_merge(struct request *__rq, struct bio *bio) +int elv_try_merge(struct request *__rq, struct bio *bio) { int ret = ELEVATOR_NO_MERGE; @@ -421,6 +421,8 @@ void elv_dispatch_sort(struct request_queue *q, struct request *rq) struct list_head *entry; int stop_flags; + BUG_ON(rq->cmd_flags & REQ_ON_PLUG); + if (q->last_merge == rq) q->last_merge = NULL; @@ -696,6 +698,8 @@ void elv_insert(struct request_queue *q, struct request *rq, int where) void __elv_add_request(struct request_queue *q, struct request *rq, int where, int plug) { + BUG_ON(rq->cmd_flags & REQ_ON_PLUG); + if (rq->cmd_flags & REQ_SOFTBARRIER) { /* barriers are scheduling boundary, update end_sector */ if (rq->cmd_type == REQ_TYPE_FS || diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index dddedfc0af81..16b286473042 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -152,6 +152,7 @@ enum rq_flag_bits { __REQ_IO_STAT, /* account I/O stat */ __REQ_MIXED_MERGE, /* merge of different types, fail separately */ __REQ_SECURE, /* secure discard (used with __REQ_DISCARD) */ + __REQ_ON_PLUG, /* on plug list */ __REQ_NR_BITS, /* stops here */ }; @@ -193,5 +194,6 @@ enum rq_flag_bits { #define REQ_IO_STAT (1 << __REQ_IO_STAT) #define REQ_MIXED_MERGE (1 << __REQ_MIXED_MERGE) #define REQ_SECURE (1 << __REQ_SECURE) +#define REQ_ON_PLUG (1 << __REQ_ON_PLUG) #endif /* __LINUX_BLK_TYPES_H */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index f55b2a8b6610..5873037eeb91 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -871,6 +871,31 @@ struct request_queue *blk_alloc_queue(gfp_t); struct request_queue *blk_alloc_queue_node(gfp_t, int); extern void blk_put_queue(struct request_queue *); +struct blk_plug { + unsigned long magic; + struct list_head list; + unsigned int should_sort; +}; + +extern void blk_start_plug(struct blk_plug *); +extern void blk_finish_plug(struct blk_plug *); +extern void __blk_flush_plug(struct task_struct *, struct blk_plug *); + +static inline void blk_flush_plug(struct task_struct *tsk) +{ + struct blk_plug *plug = tsk->plug; + + if (unlikely(plug)) + __blk_flush_plug(tsk, plug); +} + +static inline bool blk_needs_flush_plug(struct task_struct *tsk) +{ + struct blk_plug *plug = tsk->plug; + + return plug && !list_empty(&plug->list); +} + /* * tag stuff */ @@ -1294,6 +1319,23 @@ static inline long nr_blockdev_pages(void) return 0; } +static inline void blk_start_plug(struct list_head *list) +{ +} + +static inline void blk_finish_plug(struct list_head *list) +{ +} + +static inline void blk_flush_plug(struct task_struct *tsk) +{ +} + +static inline bool blk_needs_flush_plug(struct task_struct *tsk) +{ + return false; +} + #endif /* CONFIG_BLOCK */ #endif diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 39b68edb388d..8857cf9adbb7 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -105,6 +105,7 @@ extern void elv_add_request(struct request_queue *, struct request *, int, int); extern void __elv_add_request(struct request_queue *, struct request *, int, int); extern void elv_insert(struct request_queue *, struct request *, int); extern int elv_merge(struct request_queue *, struct request **, struct bio *); +extern int elv_try_merge(struct request *, struct bio *); extern void elv_merge_requests(struct request_queue *, struct request *, struct request *); extern void elv_merged_request(struct request_queue *, struct request *, int); diff --git a/include/linux/sched.h b/include/linux/sched.h index 777d8a5ed06b..96ac22643742 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -99,6 +99,7 @@ struct robust_list_head; struct bio_list; struct fs_struct; struct perf_event_context; +struct blk_plug; /* * List of flags we want to share for kernel threads, @@ -1429,6 +1430,11 @@ struct task_struct { /* stacked block device info */ struct bio_list *bio_list; +#ifdef CONFIG_BLOCK +/* stack plugging */ + struct blk_plug *plug; +#endif + /* VM state */ struct reclaim_state *reclaim_state; diff --git a/kernel/exit.c b/kernel/exit.c index f9a45ebcc7b1..6a488ad2dce5 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -908,6 +908,7 @@ NORET_TYPE void do_exit(long code) profile_task_exit(tsk); WARN_ON(atomic_read(&tsk->fs_excl)); + WARN_ON(blk_needs_flush_plug(tsk)); if (unlikely(in_interrupt())) panic("Aiee, killing interrupt handler!"); diff --git a/kernel/fork.c b/kernel/fork.c index 25e429152ddc..027c80e5162f 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1204,6 +1204,9 @@ static struct task_struct *copy_process(unsigned long clone_flags, * Clear TID on mm_release()? */ p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL; +#ifdef CONFIG_BLOCK + p->plug = NULL; +#endif #ifdef CONFIG_FUTEX p->robust_list = NULL; #ifdef CONFIG_COMPAT diff --git a/kernel/sched.c b/kernel/sched.c index 18d38e4ec7ba..ca098bf4cc65 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -3978,6 +3978,16 @@ need_resched_nonpreemptible: switch_count = &prev->nvcsw; } + /* + * If we are going to sleep and we have plugged IO queued, make + * sure to submit it to avoid deadlocks. + */ + if (prev->state != TASK_RUNNING && blk_needs_flush_plug(prev)) { + raw_spin_unlock(&rq->lock); + blk_flush_plug(prev); + raw_spin_lock(&rq->lock); + } + pre_schedule(rq, prev); if (unlikely(!rq->nr_running)) @@ -5333,6 +5343,7 @@ void __sched io_schedule(void) delayacct_blkio_start(); atomic_inc(&rq->nr_iowait); + blk_flush_plug(current); current->in_iowait = 1; schedule(); current->in_iowait = 0; @@ -5348,6 +5359,7 @@ long __sched io_schedule_timeout(long timeout) delayacct_blkio_start(); atomic_inc(&rq->nr_iowait); + blk_flush_plug(current); current->in_iowait = 1; ret = schedule_timeout(timeout); current->in_iowait = 0; -- cgit v1.2.3 From 7eaceaccab5f40bbfda044629a6298616aeaed50 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 10 Mar 2011 08:52:07 +0100 Subject: block: remove per-queue plugging Code has been converted over to the new explicit on-stack plugging, and delay users have been converted to use the new API for that. So lets kill off the old plugging along with aops->sync_page(). Signed-off-by: Jens Axboe --- Documentation/block/biodoc.txt | 5 -- block/blk-core.c | 173 +++++------------------------------- block/blk-exec.c | 4 +- block/blk-flush.c | 3 +- block/blk-settings.c | 8 -- block/blk-throttle.c | 1 - block/blk.h | 2 - block/cfq-iosched.c | 8 -- block/deadline-iosched.c | 9 -- block/elevator.c | 43 +-------- block/noop-iosched.c | 8 -- drivers/block/cciss.c | 6 -- drivers/block/cpqarray.c | 3 - drivers/block/drbd/drbd_actlog.c | 2 - drivers/block/drbd/drbd_bitmap.c | 1 - drivers/block/drbd/drbd_int.h | 14 --- drivers/block/drbd/drbd_main.c | 33 +------ drivers/block/drbd/drbd_receiver.c | 20 +---- drivers/block/drbd/drbd_req.c | 4 - drivers/block/drbd/drbd_worker.c | 1 - drivers/block/drbd/drbd_wrappers.h | 18 ---- drivers/block/floppy.c | 1 - drivers/block/loop.c | 13 --- drivers/block/pktcdvd.c | 2 - drivers/block/umem.c | 16 +--- drivers/ide/ide-atapi.c | 3 +- drivers/ide/ide-io.c | 4 - drivers/ide/ide-park.c | 2 +- drivers/md/bitmap.c | 3 +- drivers/md/dm-crypt.c | 9 +- drivers/md/dm-kcopyd.c | 52 ++--------- drivers/md/dm-raid.c | 2 +- drivers/md/dm-raid1.c | 2 - drivers/md/dm-table.c | 24 ----- drivers/md/dm.c | 33 ++----- drivers/md/linear.c | 17 ---- drivers/md/md.c | 7 -- drivers/md/multipath.c | 31 ------- drivers/md/raid0.c | 16 ---- drivers/md/raid1.c | 83 ++++------------- drivers/md/raid10.c | 87 ++++-------------- drivers/md/raid5.c | 62 ++----------- drivers/md/raid5.h | 2 +- drivers/message/i2o/i2o_block.c | 6 +- drivers/mmc/card/queue.c | 3 +- drivers/s390/block/dasd.c | 2 +- drivers/s390/char/tape_block.c | 1 - drivers/scsi/scsi_transport_fc.c | 2 +- drivers/scsi/scsi_transport_sas.c | 6 +- drivers/target/target_core_iblock.c | 7 +- fs/adfs/inode.c | 1 - fs/affs/file.c | 2 - fs/aio.c | 4 +- fs/befs/linuxvfs.c | 1 - fs/bfs/file.c | 1 - fs/block_dev.c | 1 - fs/btrfs/disk-io.c | 79 ---------------- fs/btrfs/inode.c | 1 - fs/btrfs/volumes.c | 91 +++---------------- fs/buffer.c | 31 +------ fs/cifs/file.c | 30 ------- fs/direct-io.c | 5 +- fs/efs/inode.c | 1 - fs/exofs/inode.c | 1 - fs/ext2/inode.c | 2 - fs/ext3/inode.c | 3 - fs/ext4/inode.c | 4 - fs/fat/inode.c | 1 - fs/freevxfs/vxfs_subr.c | 1 - fs/fuse/inode.c | 1 - fs/gfs2/aops.c | 3 - fs/gfs2/meta_io.c | 1 - fs/hfs/inode.c | 2 - fs/hfsplus/inode.c | 2 - fs/hpfs/file.c | 1 - fs/isofs/inode.c | 1 - fs/jfs/inode.c | 1 - fs/jfs/jfs_metapage.c | 1 - fs/logfs/dev_bdev.c | 2 - fs/minix/inode.c | 1 - fs/nilfs2/btnode.c | 6 +- fs/nilfs2/gcinode.c | 1 - fs/nilfs2/inode.c | 1 - fs/nilfs2/mdt.c | 9 +- fs/nilfs2/page.c | 5 +- fs/nilfs2/page.h | 3 +- fs/ntfs/aops.c | 4 - fs/ntfs/compress.c | 3 +- fs/ocfs2/aops.c | 1 - fs/ocfs2/cluster/heartbeat.c | 4 - fs/omfs/file.c | 1 - fs/qnx4/inode.c | 1 - fs/reiserfs/inode.c | 1 - fs/sysv/itree.c | 1 - fs/ubifs/super.c | 1 - fs/udf/file.c | 1 - fs/udf/inode.c | 1 - fs/ufs/inode.c | 1 - fs/ufs/truncate.c | 2 +- fs/xfs/linux-2.6/xfs_aops.c | 1 - fs/xfs/linux-2.6/xfs_buf.c | 13 ++- include/linux/backing-dev.h | 16 ---- include/linux/blkdev.h | 31 ++----- include/linux/buffer_head.h | 1 - include/linux/device-mapper.h | 5 -- include/linux/elevator.h | 7 +- include/linux/fs.h | 1 - include/linux/pagemap.h | 12 --- include/linux/swap.h | 2 - mm/backing-dev.c | 6 -- mm/filemap.c | 67 ++------------ mm/memory-failure.c | 8 +- mm/nommu.c | 4 - mm/page-writeback.c | 2 +- mm/readahead.c | 12 --- mm/shmem.c | 1 - mm/swap_state.c | 5 +- mm/swapfile.c | 37 -------- mm/vmscan.c | 2 +- 119 files changed, 151 insertions(+), 1269 deletions(-) (limited to 'include') diff --git a/Documentation/block/biodoc.txt b/Documentation/block/biodoc.txt index b9a83dd24732..2a7b38c832c7 100644 --- a/Documentation/block/biodoc.txt +++ b/Documentation/block/biodoc.txt @@ -963,11 +963,6 @@ elevator_dispatch_fn* fills the dispatch queue with ready requests. elevator_add_req_fn* called to add a new request into the scheduler -elevator_queue_empty_fn returns true if the merge queue is empty. - Drivers shouldn't use this, but rather check - if elv_next_request is NULL (without losing the - request if one exists!) - elevator_former_req_fn elevator_latter_req_fn These return the request before or after the one specified in disk sort order. Used by the diff --git a/block/blk-core.c b/block/blk-core.c index 6efb55cc5af0..82a45898ba76 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -198,6 +198,19 @@ void blk_dump_rq_flags(struct request *rq, char *msg) } EXPORT_SYMBOL(blk_dump_rq_flags); +/* + * Make sure that plugs that were pending when this function was entered, + * are now complete and requests pushed to the queue. +*/ +static inline void queue_sync_plugs(struct request_queue *q) +{ + /* + * If the current process is plugged and has barriers submitted, + * we will livelock if we don't unplug first. + */ + blk_flush_plug(current); +} + static void blk_delay_work(struct work_struct *work) { struct request_queue *q; @@ -224,137 +237,6 @@ void blk_delay_queue(struct request_queue *q, unsigned long msecs) } EXPORT_SYMBOL(blk_delay_queue); -/* - * "plug" the device if there are no outstanding requests: this will - * force the transfer to start only after we have put all the requests - * on the list. - * - * This is called with interrupts off and no requests on the queue and - * with the queue lock held. - */ -void blk_plug_device(struct request_queue *q) -{ - WARN_ON(!irqs_disabled()); - - /* - * don't plug a stopped queue, it must be paired with blk_start_queue() - * which will restart the queueing - */ - if (blk_queue_stopped(q)) - return; - - if (!queue_flag_test_and_set(QUEUE_FLAG_PLUGGED, q)) { - mod_timer(&q->unplug_timer, jiffies + q->unplug_delay); - trace_block_plug(q); - } -} -EXPORT_SYMBOL(blk_plug_device); - -/** - * blk_plug_device_unlocked - plug a device without queue lock held - * @q: The &struct request_queue to plug - * - * Description: - * Like @blk_plug_device(), but grabs the queue lock and disables - * interrupts. - **/ -void blk_plug_device_unlocked(struct request_queue *q) -{ - unsigned long flags; - - spin_lock_irqsave(q->queue_lock, flags); - blk_plug_device(q); - spin_unlock_irqrestore(q->queue_lock, flags); -} -EXPORT_SYMBOL(blk_plug_device_unlocked); - -/* - * remove the queue from the plugged list, if present. called with - * queue lock held and interrupts disabled. - */ -int blk_remove_plug(struct request_queue *q) -{ - WARN_ON(!irqs_disabled()); - - if (!queue_flag_test_and_clear(QUEUE_FLAG_PLUGGED, q)) - return 0; - - del_timer(&q->unplug_timer); - return 1; -} -EXPORT_SYMBOL(blk_remove_plug); - -/* - * remove the plug and let it rip.. - */ -void __generic_unplug_device(struct request_queue *q) -{ - if (unlikely(blk_queue_stopped(q))) - return; - if (!blk_remove_plug(q) && !blk_queue_nonrot(q)) - return; - - q->request_fn(q); -} - -/** - * generic_unplug_device - fire a request queue - * @q: The &struct request_queue in question - * - * Description: - * Linux uses plugging to build bigger requests queues before letting - * the device have at them. If a queue is plugged, the I/O scheduler - * is still adding and merging requests on the queue. Once the queue - * gets unplugged, the request_fn defined for the queue is invoked and - * transfers started. - **/ -void generic_unplug_device(struct request_queue *q) -{ - if (blk_queue_plugged(q)) { - spin_lock_irq(q->queue_lock); - __generic_unplug_device(q); - spin_unlock_irq(q->queue_lock); - } -} -EXPORT_SYMBOL(generic_unplug_device); - -static void blk_backing_dev_unplug(struct backing_dev_info *bdi, - struct page *page) -{ - struct request_queue *q = bdi->unplug_io_data; - - blk_unplug(q); -} - -void blk_unplug_work(struct work_struct *work) -{ - struct request_queue *q = - container_of(work, struct request_queue, unplug_work); - - trace_block_unplug_io(q); - q->unplug_fn(q); -} - -void blk_unplug_timeout(unsigned long data) -{ - struct request_queue *q = (struct request_queue *)data; - - trace_block_unplug_timer(q); - kblockd_schedule_work(q, &q->unplug_work); -} - -void blk_unplug(struct request_queue *q) -{ - /* - * devices don't necessarily have an ->unplug_fn defined - */ - if (q->unplug_fn) { - trace_block_unplug_io(q); - q->unplug_fn(q); - } -} -EXPORT_SYMBOL(blk_unplug); - /** * blk_start_queue - restart a previously stopped queue * @q: The &struct request_queue in question @@ -389,7 +271,6 @@ EXPORT_SYMBOL(blk_start_queue); **/ void blk_stop_queue(struct request_queue *q) { - blk_remove_plug(q); cancel_delayed_work(&q->delay_work); queue_flag_set(QUEUE_FLAG_STOPPED, q); } @@ -411,11 +292,10 @@ EXPORT_SYMBOL(blk_stop_queue); */ void blk_sync_queue(struct request_queue *q) { - del_timer_sync(&q->unplug_timer); del_timer_sync(&q->timeout); - cancel_work_sync(&q->unplug_work); throtl_shutdown_timer_wq(q); cancel_delayed_work_sync(&q->delay_work); + queue_sync_plugs(q); } EXPORT_SYMBOL(blk_sync_queue); @@ -430,14 +310,9 @@ EXPORT_SYMBOL(blk_sync_queue); */ void __blk_run_queue(struct request_queue *q) { - blk_remove_plug(q); - if (unlikely(blk_queue_stopped(q))) return; - if (elv_queue_empty(q)) - return; - /* * Only recurse once to avoid overrunning the stack, let the unplug * handling reinvoke the handler shortly if we already got there. @@ -445,10 +320,8 @@ void __blk_run_queue(struct request_queue *q) if (!queue_flag_test_and_set(QUEUE_FLAG_REENTER, q)) { q->request_fn(q); queue_flag_clear(QUEUE_FLAG_REENTER, q); - } else { - queue_flag_set(QUEUE_FLAG_PLUGGED, q); - kblockd_schedule_work(q, &q->unplug_work); - } + } else + queue_delayed_work(kblockd_workqueue, &q->delay_work, 0); } EXPORT_SYMBOL(__blk_run_queue); @@ -535,8 +408,6 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) if (!q) return NULL; - q->backing_dev_info.unplug_io_fn = blk_backing_dev_unplug; - q->backing_dev_info.unplug_io_data = q; q->backing_dev_info.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; q->backing_dev_info.state = 0; @@ -556,13 +427,11 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) setup_timer(&q->backing_dev_info.laptop_mode_wb_timer, laptop_mode_timer_fn, (unsigned long) q); - 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->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); INIT_DELAYED_WORK(&q->delay_work, blk_delay_work); kobject_init(&q->kobj, &blk_queue_ktype); @@ -652,7 +521,6 @@ blk_init_allocated_queue_node(struct request_queue *q, request_fn_proc *rfn, q->request_fn = rfn; q->prep_rq_fn = NULL; q->unprep_rq_fn = NULL; - q->unplug_fn = generic_unplug_device; q->queue_flags = QUEUE_FLAG_DEFAULT; q->queue_lock = lock; @@ -910,8 +778,8 @@ out: } /* - * No available requests for this queue, unplug the device and wait for some - * requests to become available. + * No available requests for this queue, wait for some requests to become + * available. * * Called with q->queue_lock held, and returns with it unlocked. */ @@ -932,7 +800,6 @@ static struct request *get_request_wait(struct request_queue *q, int rw_flags, trace_block_sleeprq(q, bio, rw_flags & 1); - __generic_unplug_device(q); spin_unlock_irq(q->queue_lock); io_schedule(); @@ -1058,7 +925,7 @@ static void add_acct_request(struct request_queue *q, struct request *rq, int where) { drive_stat_acct(rq, 1); - __elv_add_request(q, rq, where, 0); + __elv_add_request(q, rq, where); } /** @@ -2798,7 +2665,7 @@ static void flush_plug_list(struct blk_plug *plug) /* * rq is already accounted, so use raw insert */ - __elv_add_request(q, rq, ELEVATOR_INSERT_SORT, 0); + __elv_add_request(q, rq, ELEVATOR_INSERT_SORT); } if (q) { diff --git a/block/blk-exec.c b/block/blk-exec.c index cf1456a02acd..81e31819a597 100644 --- a/block/blk-exec.c +++ b/block/blk-exec.c @@ -54,8 +54,8 @@ void blk_execute_rq_nowait(struct request_queue *q, struct gendisk *bd_disk, rq->end_io = done; WARN_ON(irqs_disabled()); spin_lock_irq(q->queue_lock); - __elv_add_request(q, rq, where, 1); - __generic_unplug_device(q); + __elv_add_request(q, rq, where); + __blk_run_queue(q); /* the queue is stopped so it won't be plugged+unplugged */ if (rq->cmd_type == REQ_TYPE_PM_RESUME) q->request_fn(q); diff --git a/block/blk-flush.c b/block/blk-flush.c index 1e2aa8a8908c..671fa9da7560 100644 --- a/block/blk-flush.c +++ b/block/blk-flush.c @@ -194,7 +194,6 @@ static void flush_end_io(struct request *flush_rq, int 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; @@ -213,7 +212,7 @@ static void flush_end_io(struct request *flush_rq, int error) } /* after populating an empty queue, kick it to avoid stall */ - if (queued && was_empty) + if (queued) __blk_run_queue(q); } diff --git a/block/blk-settings.c b/block/blk-settings.c index 36c8c1f2af18..c8d68921dddb 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -164,14 +164,6 @@ void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn) blk_queue_congestion_threshold(q); q->nr_batching = BLK_BATCH_REQ; - q->unplug_thresh = 4; /* hmm */ - q->unplug_delay = msecs_to_jiffies(3); /* 3 milliseconds */ - if (q->unplug_delay == 0) - q->unplug_delay = 1; - - q->unplug_timer.function = blk_unplug_timeout; - q->unplug_timer.data = (unsigned long)q; - blk_set_default_limits(&q->limits); blk_queue_max_hw_sectors(q, BLK_SAFE_MAX_SECTORS); diff --git a/block/blk-throttle.c b/block/blk-throttle.c index a89043a3caa4..b8dcdc2663a1 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -800,7 +800,6 @@ out: if (nr_disp) { while((bio = bio_list_pop(&bio_list_on_stack))) generic_make_request(bio); - blk_unplug(q); } return nr_disp; } diff --git a/block/blk.h b/block/blk.h index 284b500852bd..49d21af81d07 100644 --- a/block/blk.h +++ b/block/blk.h @@ -18,8 +18,6 @@ int blk_rq_append_bio(struct request_queue *q, struct request *rq, void blk_dequeue_request(struct request *rq); void __blk_queue_free_tags(struct request_queue *q); -void blk_unplug_work(struct work_struct *work); -void blk_unplug_timeout(unsigned long data); void blk_rq_timed_out_timer(unsigned long data); void blk_delete_timer(struct request *); void blk_add_timer(struct request *); diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 3202c7e87fb3..ef631539dd2a 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -499,13 +499,6 @@ static inline void cfq_schedule_dispatch(struct cfq_data *cfqd) } } -static int cfq_queue_empty(struct request_queue *q) -{ - struct cfq_data *cfqd = q->elevator->elevator_data; - - return !cfqd->rq_queued; -} - /* * Scale schedule slice based on io priority. Use the sync time slice only * if a queue is marked sync and has sync io queued. A sync queue with async @@ -4061,7 +4054,6 @@ static struct elevator_type iosched_cfq = { .elevator_add_req_fn = cfq_insert_request, .elevator_activate_req_fn = cfq_activate_request, .elevator_deactivate_req_fn = cfq_deactivate_request, - .elevator_queue_empty_fn = cfq_queue_empty, .elevator_completed_req_fn = cfq_completed_request, .elevator_former_req_fn = elv_rb_former_request, .elevator_latter_req_fn = elv_rb_latter_request, diff --git a/block/deadline-iosched.c b/block/deadline-iosched.c index b547cbca7b23..5139c0ea1864 100644 --- a/block/deadline-iosched.c +++ b/block/deadline-iosched.c @@ -326,14 +326,6 @@ dispatch_request: return 1; } -static int deadline_queue_empty(struct request_queue *q) -{ - struct deadline_data *dd = q->elevator->elevator_data; - - return list_empty(&dd->fifo_list[WRITE]) - && list_empty(&dd->fifo_list[READ]); -} - static void deadline_exit_queue(struct elevator_queue *e) { struct deadline_data *dd = e->elevator_data; @@ -445,7 +437,6 @@ static struct elevator_type iosched_deadline = { .elevator_merge_req_fn = deadline_merged_requests, .elevator_dispatch_fn = deadline_dispatch_requests, .elevator_add_req_fn = deadline_add_request, - .elevator_queue_empty_fn = deadline_queue_empty, .elevator_former_req_fn = elv_rb_former_request, .elevator_latter_req_fn = elv_rb_latter_request, .elevator_init_fn = deadline_init_queue, diff --git a/block/elevator.c b/block/elevator.c index 25713927c0d3..3ea208256e78 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -619,21 +619,12 @@ void elv_quiesce_end(struct request_queue *q) void elv_insert(struct request_queue *q, struct request *rq, int where) { - int unplug_it = 1; - trace_block_rq_insert(q, rq); rq->q = q; switch (where) { case ELEVATOR_INSERT_REQUEUE: - /* - * Most requeues happen because of a busy condition, - * don't force unplug of the queue for that case. - * Clear unplug_it and fall through. - */ - unplug_it = 0; - case ELEVATOR_INSERT_FRONT: rq->cmd_flags |= REQ_SOFTBARRIER; list_add(&rq->queuelist, &q->queue_head); @@ -679,24 +670,14 @@ void elv_insert(struct request_queue *q, struct request *rq, int where) rq->cmd_flags |= REQ_SOFTBARRIER; blk_insert_flush(rq); break; - default: printk(KERN_ERR "%s: bad insertion point %d\n", __func__, where); BUG(); } - - if (unplug_it && blk_queue_plugged(q)) { - int nrq = q->rq.count[BLK_RW_SYNC] + q->rq.count[BLK_RW_ASYNC] - - queue_in_flight(q); - - if (nrq >= q->unplug_thresh) - __generic_unplug_device(q); - } } -void __elv_add_request(struct request_queue *q, struct request *rq, int where, - int plug) +void __elv_add_request(struct request_queue *q, struct request *rq, int where) { BUG_ON(rq->cmd_flags & REQ_ON_PLUG); @@ -711,38 +692,20 @@ void __elv_add_request(struct request_queue *q, struct request *rq, int where, where == ELEVATOR_INSERT_SORT) where = ELEVATOR_INSERT_BACK; - if (plug) - blk_plug_device(q); - elv_insert(q, rq, where); } EXPORT_SYMBOL(__elv_add_request); -void elv_add_request(struct request_queue *q, struct request *rq, int where, - int plug) +void elv_add_request(struct request_queue *q, struct request *rq, int where) { unsigned long flags; spin_lock_irqsave(q->queue_lock, flags); - __elv_add_request(q, rq, where, plug); + __elv_add_request(q, rq, where); spin_unlock_irqrestore(q->queue_lock, flags); } EXPORT_SYMBOL(elv_add_request); -int elv_queue_empty(struct request_queue *q) -{ - struct elevator_queue *e = q->elevator; - - if (!list_empty(&q->queue_head)) - return 0; - - if (e->ops->elevator_queue_empty_fn) - return e->ops->elevator_queue_empty_fn(q); - - return 1; -} -EXPORT_SYMBOL(elv_queue_empty); - struct request *elv_latter_request(struct request_queue *q, struct request *rq) { struct elevator_queue *e = q->elevator; diff --git a/block/noop-iosched.c b/block/noop-iosched.c index 232c4b38cd37..06389e9ef96d 100644 --- a/block/noop-iosched.c +++ b/block/noop-iosched.c @@ -39,13 +39,6 @@ static void noop_add_request(struct request_queue *q, struct request *rq) list_add_tail(&rq->queuelist, &nd->queue); } -static int noop_queue_empty(struct request_queue *q) -{ - struct noop_data *nd = q->elevator->elevator_data; - - return list_empty(&nd->queue); -} - static struct request * noop_former_request(struct request_queue *q, struct request *rq) { @@ -90,7 +83,6 @@ static struct elevator_type elevator_noop = { .elevator_merge_req_fn = noop_merged_requests, .elevator_dispatch_fn = noop_dispatch, .elevator_add_req_fn = noop_add_request, - .elevator_queue_empty_fn = noop_queue_empty, .elevator_former_req_fn = noop_former_request, .elevator_latter_req_fn = noop_latter_request, .elevator_init_fn = noop_init_queue, diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 9279272b3732..35658f445fca 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -3170,12 +3170,6 @@ static void do_cciss_request(struct request_queue *q) int sg_index = 0; int chained = 0; - /* We call start_io here in case there is a command waiting on the - * queue that has not been sent. - */ - if (blk_queue_plugged(q)) - goto startio; - queue: creq = blk_peek_request(q); if (!creq) diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index 946dad4caef3..b2fceb53e809 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -911,9 +911,6 @@ static void do_ida_request(struct request_queue *q) struct scatterlist tmp_sg[SG_MAX]; int i, dir, seg; - if (blk_queue_plugged(q)) - goto startio; - queue_next: creq = blk_peek_request(q); if (!creq) diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c index ba95cba192be..2096628d6e65 100644 --- a/drivers/block/drbd/drbd_actlog.c +++ b/drivers/block/drbd/drbd_actlog.c @@ -689,8 +689,6 @@ void drbd_al_to_on_disk_bm(struct drbd_conf *mdev) } } - drbd_blk_run_queue(bdev_get_queue(mdev->ldev->md_bdev)); - /* always (try to) flush bitmap to stable storage */ drbd_md_flush(mdev); diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c index fd42832f785b..0645ca829a94 100644 --- a/drivers/block/drbd/drbd_bitmap.c +++ b/drivers/block/drbd/drbd_bitmap.c @@ -840,7 +840,6 @@ static int bm_rw(struct drbd_conf *mdev, int rw) __must_hold(local) for (i = 0; i < num_pages; i++) bm_page_io_async(mdev, b, i, rw); - drbd_blk_run_queue(bdev_get_queue(mdev->ldev->md_bdev)); wait_event(b->bm_io_wait, atomic_read(&b->bm_async_io) == 0); if (test_bit(BM_MD_IO_ERROR, &b->bm_flags)) { diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index 3803a0348937..0b5718e19586 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -2382,20 +2382,6 @@ static inline int drbd_queue_order_type(struct drbd_conf *mdev) return QUEUE_ORDERED_NONE; } -static inline void drbd_blk_run_queue(struct request_queue *q) -{ - if (q && q->unplug_fn) - q->unplug_fn(q); -} - -static inline void drbd_kick_lo(struct drbd_conf *mdev) -{ - if (get_ldev(mdev)) { - drbd_blk_run_queue(bdev_get_queue(mdev->ldev->backing_bdev)); - put_ldev(mdev); - } -} - static inline void drbd_md_flush(struct drbd_conf *mdev) { int r; diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 29cd0dc9fe4f..6049cb85310d 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -2719,35 +2719,6 @@ static int drbd_release(struct gendisk *gd, fmode_t mode) return 0; } -static void drbd_unplug_fn(struct request_queue *q) -{ - struct drbd_conf *mdev = q->queuedata; - - /* unplug FIRST */ - spin_lock_irq(q->queue_lock); - blk_remove_plug(q); - spin_unlock_irq(q->queue_lock); - - /* only if connected */ - spin_lock_irq(&mdev->req_lock); - if (mdev->state.pdsk >= D_INCONSISTENT && mdev->state.conn >= C_CONNECTED) { - D_ASSERT(mdev->state.role == R_PRIMARY); - if (test_and_clear_bit(UNPLUG_REMOTE, &mdev->flags)) { - /* add to the data.work queue, - * unless already queued. - * XXX this might be a good addition to drbd_queue_work - * anyways, to detect "double queuing" ... */ - if (list_empty(&mdev->unplug_work.list)) - drbd_queue_work(&mdev->data.work, - &mdev->unplug_work); - } - } - spin_unlock_irq(&mdev->req_lock); - - if (mdev->state.disk >= D_INCONSISTENT) - drbd_kick_lo(mdev); -} - static void drbd_set_defaults(struct drbd_conf *mdev) { /* This way we get a compile error when sync_conf grows, @@ -3222,9 +3193,7 @@ struct drbd_conf *drbd_new_device(unsigned int minor) blk_queue_max_segment_size(q, DRBD_MAX_SEGMENT_SIZE); blk_queue_bounce_limit(q, BLK_BOUNCE_ANY); blk_queue_merge_bvec(q, drbd_merge_bvec); - q->queue_lock = &mdev->req_lock; /* needed since we use */ - /* plugging on a queue, that actually has no requests! */ - q->unplug_fn = drbd_unplug_fn; + q->queue_lock = &mdev->req_lock; mdev->md_io_page = alloc_page(GFP_KERNEL); if (!mdev->md_io_page) diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index 24487d4fb202..84132f8bf8a4 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -187,15 +187,6 @@ static struct page *drbd_pp_first_pages_or_try_alloc(struct drbd_conf *mdev, int return NULL; } -/* kick lower level device, if we have more than (arbitrary number) - * reference counts on it, which typically are locally submitted io - * requests. don't use unacked_cnt, so we speed up proto A and B, too. */ -static void maybe_kick_lo(struct drbd_conf *mdev) -{ - if (atomic_read(&mdev->local_cnt) >= mdev->net_conf->unplug_watermark) - drbd_kick_lo(mdev); -} - static void reclaim_net_ee(struct drbd_conf *mdev, struct list_head *to_be_freed) { struct drbd_epoch_entry *e; @@ -219,7 +210,6 @@ static void drbd_kick_lo_and_reclaim_net(struct drbd_conf *mdev) LIST_HEAD(reclaimed); struct drbd_epoch_entry *e, *t; - maybe_kick_lo(mdev); spin_lock_irq(&mdev->req_lock); reclaim_net_ee(mdev, &reclaimed); spin_unlock_irq(&mdev->req_lock); @@ -436,8 +426,7 @@ void _drbd_wait_ee_list_empty(struct drbd_conf *mdev, struct list_head *head) while (!list_empty(head)) { prepare_to_wait(&mdev->ee_wait, &wait, TASK_UNINTERRUPTIBLE); spin_unlock_irq(&mdev->req_lock); - drbd_kick_lo(mdev); - schedule(); + io_schedule(); finish_wait(&mdev->ee_wait, &wait); spin_lock_irq(&mdev->req_lock); } @@ -1147,7 +1136,6 @@ next_bio: drbd_generic_make_request(mdev, fault_type, bio); } while (bios); - maybe_kick_lo(mdev); return 0; fail: @@ -1167,9 +1155,6 @@ static int receive_Barrier(struct drbd_conf *mdev, enum drbd_packets cmd, unsign inc_unacked(mdev); - if (mdev->net_conf->wire_protocol != DRBD_PROT_C) - drbd_kick_lo(mdev); - mdev->current_epoch->barrier_nr = p->barrier; rv = drbd_may_finish_epoch(mdev, mdev->current_epoch, EV_GOT_BARRIER_NR); @@ -3556,9 +3541,6 @@ static int receive_skip(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned static int receive_UnplugRemote(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned int data_size) { - if (mdev->state.disk >= D_INCONSISTENT) - drbd_kick_lo(mdev); - /* Make sure we've acked all the TCP data associated * with the data requests being unplugged */ drbd_tcp_quickack(mdev->data.socket); diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index 11a75d32a2e2..ad3fc6228f27 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -960,10 +960,6 @@ allocate_barrier: bio_endio(req->private_bio, -EIO); } - /* we need to plug ALWAYS since we possibly need to kick lo_dev. - * we plug after submit, so we won't miss an unplug event */ - drbd_plug_device(mdev); - return 0; fail_conflicting: diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c index 34f224b018b3..e027446590d3 100644 --- a/drivers/block/drbd/drbd_worker.c +++ b/drivers/block/drbd/drbd_worker.c @@ -792,7 +792,6 @@ int drbd_resync_finished(struct drbd_conf *mdev) * queue (or even the read operations for those packets * is not finished by now). Retry in 100ms. */ - drbd_kick_lo(mdev); __set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(HZ / 10); w = kmalloc(sizeof(struct drbd_work), GFP_ATOMIC); diff --git a/drivers/block/drbd/drbd_wrappers.h b/drivers/block/drbd/drbd_wrappers.h index defdb5013ea3..53586fa5ae1b 100644 --- a/drivers/block/drbd/drbd_wrappers.h +++ b/drivers/block/drbd/drbd_wrappers.h @@ -45,24 +45,6 @@ static inline void drbd_generic_make_request(struct drbd_conf *mdev, generic_make_request(bio); } -static inline void drbd_plug_device(struct drbd_conf *mdev) -{ - struct request_queue *q; - q = bdev_get_queue(mdev->this_bdev); - - spin_lock_irq(q->queue_lock); - -/* XXX the check on !blk_queue_plugged is redundant, - * implicitly checked in blk_plug_device */ - - if (!blk_queue_plugged(q)) { - blk_plug_device(q); - del_timer(&q->unplug_timer); - /* unplugging should not happen automatically... */ - } - spin_unlock_irq(q->queue_lock); -} - static inline int drbd_crypto_is_hash(struct crypto_tfm *tfm) { return (crypto_tfm_alg_type(tfm) & CRYPTO_ALG_TYPE_HASH_MASK) diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index b9ba04fc2b34..271142b9e2cd 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -3837,7 +3837,6 @@ static int __floppy_read_block_0(struct block_device *bdev) bio.bi_end_io = floppy_rb0_complete; submit_bio(READ, &bio); - generic_unplug_device(bdev_get_queue(bdev)); process_fd_request(); wait_for_completion(&complete); diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 49e6a545eb63..01b8e4a87c9f 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -541,17 +541,6 @@ out: return 0; } -/* - * kick off io on the underlying address space - */ -static void loop_unplug(struct request_queue *q) -{ - struct loop_device *lo = q->queuedata; - - queue_flag_clear_unlocked(QUEUE_FLAG_PLUGGED, q); - blk_run_address_space(lo->lo_backing_file->f_mapping); -} - struct switch_request { struct file *file; struct completion wait; @@ -918,7 +907,6 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, */ blk_queue_make_request(lo->lo_queue, loop_make_request); lo->lo_queue->queuedata = lo; - lo->lo_queue->unplug_fn = loop_unplug; if (!(lo_flags & LO_FLAGS_READ_ONLY) && file->f_op->fsync) blk_queue_flush(lo->lo_queue, REQ_FLUSH); @@ -1020,7 +1008,6 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) kthread_stop(lo->lo_thread); - lo->lo_queue->unplug_fn = NULL; lo->lo_backing_file = NULL; loop_release_xfer(lo); diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 77d70eebb6b2..d20e13f80001 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -1606,8 +1606,6 @@ static int kcdrwd(void *foobar) min_sleep_time = pkt->sleep_time; } - generic_unplug_device(bdev_get_queue(pd->bdev)); - VPRINTK("kcdrwd: sleeping\n"); residue = schedule_timeout(min_sleep_time); VPRINTK("kcdrwd: wake up\n"); diff --git a/drivers/block/umem.c b/drivers/block/umem.c index 8be57151f5d6..653439faa729 100644 --- a/drivers/block/umem.c +++ b/drivers/block/umem.c @@ -241,8 +241,7 @@ static void dump_dmastat(struct cardinfo *card, unsigned int dmastat) * * Whenever IO on the active page completes, the Ready page is activated * and the ex-Active page is clean out and made Ready. - * Otherwise the Ready page is only activated when it becomes full, or - * when mm_unplug_device is called via the unplug_io_fn. + * Otherwise the Ready page is only activated when it becomes full. * * If a request arrives while both pages a full, it is queued, and b_rdev is * overloaded to record whether it was a read or a write. @@ -333,17 +332,6 @@ static inline void reset_page(struct mm_page *page) page->biotail = &page->bio; } -static void mm_unplug_device(struct request_queue *q) -{ - struct cardinfo *card = q->queuedata; - unsigned long flags; - - spin_lock_irqsave(&card->lock, flags); - if (blk_remove_plug(q)) - activate(card); - spin_unlock_irqrestore(&card->lock, flags); -} - /* * If there is room on Ready page, take * one bh off list and add it. @@ -535,7 +523,6 @@ static int mm_make_request(struct request_queue *q, struct bio *bio) *card->biotail = bio; bio->bi_next = NULL; card->biotail = &bio->bi_next; - blk_plug_device(q); spin_unlock_irq(&card->lock); return 0; @@ -907,7 +894,6 @@ static int __devinit mm_pci_probe(struct pci_dev *dev, blk_queue_make_request(card->queue, mm_make_request); card->queue->queue_lock = &card->lock; card->queue->queuedata = card; - card->queue->unplug_fn = mm_unplug_device; tasklet_init(&card->tasklet, process_page, (unsigned long)card); diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index e88a2cf17711..6f218e014e99 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -233,8 +233,7 @@ int ide_queue_sense_rq(ide_drive_t *drive, void *special) drive->hwif->rq = NULL; - elv_add_request(drive->queue, &drive->sense_rq, - ELEVATOR_INSERT_FRONT, 0); + elv_add_request(drive->queue, &drive->sense_rq, ELEVATOR_INSERT_FRONT); return 0; } EXPORT_SYMBOL_GPL(ide_queue_sense_rq); diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 999dac054bcc..f4077840d3ab 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -549,8 +549,6 @@ plug_device_2: if (rq) blk_requeue_request(q, rq); - if (!elv_queue_empty(q)) - blk_plug_device(q); } void ide_requeue_and_plug(ide_drive_t *drive, struct request *rq) @@ -562,8 +560,6 @@ void ide_requeue_and_plug(ide_drive_t *drive, struct request *rq) if (rq) blk_requeue_request(q, rq); - if (!elv_queue_empty(q)) - blk_plug_device(q); spin_unlock_irqrestore(q->queue_lock, flags); } diff --git a/drivers/ide/ide-park.c b/drivers/ide/ide-park.c index 88a380c5a470..6ab9ab2a5081 100644 --- a/drivers/ide/ide-park.c +++ b/drivers/ide/ide-park.c @@ -52,7 +52,7 @@ static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout) rq->cmd[0] = REQ_UNPARK_HEADS; rq->cmd_len = 1; rq->cmd_type = REQ_TYPE_SPECIAL; - elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 1); + elv_add_request(q, rq, ELEVATOR_INSERT_FRONT); out: return; diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index 9a35320fb59f..54bfc274b39a 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -1339,8 +1339,7 @@ int bitmap_startwrite(struct bitmap *bitmap, sector_t offset, unsigned long sect prepare_to_wait(&bitmap->overflow_wait, &__wait, TASK_UNINTERRUPTIBLE); spin_unlock_irq(&bitmap->lock); - md_unplug(bitmap->mddev); - schedule(); + io_schedule(); finish_wait(&bitmap->overflow_wait, &__wait); continue; } diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 4e054bd91664..2c62c1169f78 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -991,11 +991,6 @@ static void clone_init(struct dm_crypt_io *io, struct bio *clone) clone->bi_destructor = dm_crypt_bio_destructor; } -static void kcryptd_unplug(struct crypt_config *cc) -{ - blk_unplug(bdev_get_queue(cc->dev->bdev)); -} - static int kcryptd_io_read(struct dm_crypt_io *io, gfp_t gfp) { struct crypt_config *cc = io->target->private; @@ -1008,10 +1003,8 @@ static int kcryptd_io_read(struct dm_crypt_io *io, gfp_t gfp) * one in order to decrypt the whole bio data *afterwards*. */ clone = bio_alloc_bioset(gfp, bio_segments(base_bio), cc->bs); - if (!clone) { - kcryptd_unplug(cc); + if (!clone) return 1; - } crypt_inc_pending(io); diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c index 924f5f0084c2..400cf35094a4 100644 --- a/drivers/md/dm-kcopyd.c +++ b/drivers/md/dm-kcopyd.c @@ -37,13 +37,6 @@ struct dm_kcopyd_client { unsigned int nr_pages; unsigned int nr_free_pages; - /* - * Block devices to unplug. - * Non-NULL pointer means that a block device has some pending requests - * and needs to be unplugged. - */ - struct block_device *unplug[2]; - struct dm_io_client *io_client; wait_queue_head_t destroyq; @@ -315,31 +308,6 @@ static int run_complete_job(struct kcopyd_job *job) return 0; } -/* - * Unplug the block device at the specified index. - */ -static void unplug(struct dm_kcopyd_client *kc, int rw) -{ - if (kc->unplug[rw] != NULL) { - blk_unplug(bdev_get_queue(kc->unplug[rw])); - kc->unplug[rw] = NULL; - } -} - -/* - * Prepare block device unplug. If there's another device - * to be unplugged at the same array index, we unplug that - * device first. - */ -static void prepare_unplug(struct dm_kcopyd_client *kc, int rw, - struct block_device *bdev) -{ - if (likely(kc->unplug[rw] == bdev)) - return; - unplug(kc, rw); - kc->unplug[rw] = bdev; -} - static void complete_io(unsigned long error, void *context) { struct kcopyd_job *job = (struct kcopyd_job *) context; @@ -386,15 +354,12 @@ static int run_io_job(struct kcopyd_job *job) .client = job->kc->io_client, }; - if (job->rw == READ) { + if (job->rw == READ) r = dm_io(&io_req, 1, &job->source, NULL); - prepare_unplug(job->kc, READ, job->source.bdev); - } else { + else { if (job->num_dests > 1) io_req.bi_rw |= REQ_UNPLUG; r = dm_io(&io_req, job->num_dests, job->dests, NULL); - if (!(io_req.bi_rw & REQ_UNPLUG)) - prepare_unplug(job->kc, WRITE, job->dests[0].bdev); } return r; @@ -466,6 +431,7 @@ static void do_work(struct work_struct *work) { struct dm_kcopyd_client *kc = container_of(work, struct dm_kcopyd_client, kcopyd_work); + struct blk_plug plug; /* * The order that these are called is *very* important. @@ -473,18 +439,12 @@ static void do_work(struct work_struct *work) * Pages jobs when successful will jump onto the io jobs * list. io jobs call wake when they complete and it all * starts again. - * - * Note that io_jobs add block devices to the unplug array, - * this array is cleared with "unplug" calls. It is thus - * forbidden to run complete_jobs after io_jobs and before - * unplug because the block device could be destroyed in - * job completion callback. */ + blk_start_plug(&plug); process_jobs(&kc->complete_jobs, kc, run_complete_job); process_jobs(&kc->pages_jobs, kc, run_pages_job); process_jobs(&kc->io_jobs, kc, run_io_job); - unplug(kc, READ); - unplug(kc, WRITE); + blk_finish_plug(&plug); } /* @@ -665,8 +625,6 @@ int dm_kcopyd_client_create(unsigned int nr_pages, INIT_LIST_HEAD(&kc->io_jobs); INIT_LIST_HEAD(&kc->pages_jobs); - memset(kc->unplug, 0, sizeof(kc->unplug)); - kc->job_pool = mempool_create_slab_pool(MIN_JOBS, _job_cache); if (!kc->job_pool) goto bad_slab; diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index b9e1e15ef11c..5ef136cdba91 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -394,7 +394,7 @@ static void raid_unplug(struct dm_target_callbacks *cb) { struct raid_set *rs = container_of(cb, struct raid_set, callbacks); - md_raid5_unplug_device(rs->md.private); + md_raid5_kick_device(rs->md.private); } /* diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index dee326775c60..976ad4688afc 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -842,8 +842,6 @@ static void do_mirror(struct work_struct *work) do_reads(ms, &reads); do_writes(ms, &writes); do_failures(ms, &failures); - - dm_table_unplug_all(ms->ti->table); } /*----------------------------------------------------------------- diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 38e4eb1bb965..f50a7b952257 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -1275,29 +1275,6 @@ int dm_table_any_busy_target(struct dm_table *t) return 0; } -void dm_table_unplug_all(struct dm_table *t) -{ - struct dm_dev_internal *dd; - struct list_head *devices = dm_table_get_devices(t); - struct dm_target_callbacks *cb; - - list_for_each_entry(dd, devices, list) { - struct request_queue *q = bdev_get_queue(dd->dm_dev.bdev); - char b[BDEVNAME_SIZE]; - - if (likely(q)) - blk_unplug(q); - else - DMWARN_LIMIT("%s: Cannot unplug nonexistent device %s", - dm_device_name(t->md), - bdevname(dd->dm_dev.bdev, b)); - } - - list_for_each_entry(cb, &t->target_callbacks, list) - if (cb->unplug_fn) - cb->unplug_fn(cb); -} - struct mapped_device *dm_table_get_md(struct dm_table *t) { return t->md; @@ -1345,4 +1322,3 @@ EXPORT_SYMBOL(dm_table_get_mode); EXPORT_SYMBOL(dm_table_get_md); EXPORT_SYMBOL(dm_table_put); EXPORT_SYMBOL(dm_table_get); -EXPORT_SYMBOL(dm_table_unplug_all); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index eaa3af0e0632..d22b9905c168 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -807,8 +807,6 @@ void dm_requeue_unmapped_request(struct request *clone) dm_unprep_request(rq); spin_lock_irqsave(q->queue_lock, flags); - if (elv_queue_empty(q)) - blk_plug_device(q); blk_requeue_request(q, rq); spin_unlock_irqrestore(q->queue_lock, flags); @@ -1613,10 +1611,10 @@ static void dm_request_fn(struct request_queue *q) * number of in-flight I/Os after the queue is stopped in * dm_suspend(). */ - while (!blk_queue_plugged(q) && !blk_queue_stopped(q)) { + while (!blk_queue_stopped(q)) { rq = blk_peek_request(q); if (!rq) - goto plug_and_out; + goto delay_and_out; /* always use block 0 to find the target for flushes for now */ pos = 0; @@ -1627,7 +1625,7 @@ static void dm_request_fn(struct request_queue *q) BUG_ON(!dm_target_is_valid(ti)); if (ti->type->busy && ti->type->busy(ti)) - goto plug_and_out; + goto delay_and_out; blk_start_request(rq); clone = rq->special; @@ -1647,11 +1645,8 @@ requeued: BUG_ON(!irqs_disabled()); spin_lock(q->queue_lock); -plug_and_out: - if (!elv_queue_empty(q)) - /* Some requests still remain, retry later */ - blk_plug_device(q); - +delay_and_out: + blk_delay_queue(q, HZ / 10); out: dm_table_put(map); @@ -1680,20 +1675,6 @@ static int dm_lld_busy(struct request_queue *q) return r; } -static void dm_unplug_all(struct request_queue *q) -{ - struct mapped_device *md = q->queuedata; - struct dm_table *map = dm_get_live_table(md); - - if (map) { - if (dm_request_based(md)) - generic_unplug_device(q); - - dm_table_unplug_all(map); - dm_table_put(map); - } -} - static int dm_any_congested(void *congested_data, int bdi_bits) { int r = bdi_bits; @@ -1817,7 +1798,6 @@ static void dm_init_md_queue(struct mapped_device *md) md->queue->backing_dev_info.congested_data = md; blk_queue_make_request(md->queue, dm_request); blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY); - md->queue->unplug_fn = dm_unplug_all; blk_queue_merge_bvec(md->queue, dm_merge_bvec); blk_queue_flush(md->queue, REQ_FLUSH | REQ_FUA); } @@ -2263,8 +2243,6 @@ static int dm_wait_for_completion(struct mapped_device *md, int interruptible) int r = 0; DECLARE_WAITQUEUE(wait, current); - dm_unplug_all(md->queue); - add_wait_queue(&md->wait, &wait); while (1) { @@ -2539,7 +2517,6 @@ int dm_resume(struct mapped_device *md) clear_bit(DMF_SUSPENDED, &md->flags); - dm_table_unplug_all(map); r = 0; out: dm_table_put(map); diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 8a2f767f26d8..38861b5b9d90 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -87,22 +87,6 @@ static int linear_mergeable_bvec(struct request_queue *q, return maxsectors << 9; } -static void linear_unplug(struct request_queue *q) -{ - mddev_t *mddev = q->queuedata; - linear_conf_t *conf; - int i; - - rcu_read_lock(); - conf = rcu_dereference(mddev->private); - - for (i=0; i < mddev->raid_disks; i++) { - struct request_queue *r_queue = bdev_get_queue(conf->disks[i].rdev->bdev); - blk_unplug(r_queue); - } - rcu_read_unlock(); -} - static int linear_congested(void *data, int bits) { mddev_t *mddev = data; @@ -225,7 +209,6 @@ static int linear_run (mddev_t *mddev) md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec); - mddev->queue->unplug_fn = linear_unplug; mddev->queue->backing_dev_info.congested_fn = linear_congested; mddev->queue->backing_dev_info.congested_data = mddev; md_integrity_register(mddev); diff --git a/drivers/md/md.c b/drivers/md/md.c index 0cc30ecda4c1..ca0d79c264b9 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -4812,7 +4812,6 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) __md_stop_writes(mddev); md_stop(mddev); mddev->queue->merge_bvec_fn = NULL; - mddev->queue->unplug_fn = NULL; mddev->queue->backing_dev_info.congested_fn = NULL; /* tell userspace to handle 'inactive' */ @@ -6669,8 +6668,6 @@ EXPORT_SYMBOL_GPL(md_allow_write); void md_unplug(mddev_t *mddev) { - if (mddev->queue) - blk_unplug(mddev->queue); if (mddev->plug) mddev->plug->unplug_fn(mddev->plug); } @@ -6853,7 +6850,6 @@ void md_do_sync(mddev_t *mddev) >= mddev->resync_max - mddev->curr_resync_completed )) { /* time to update curr_resync_completed */ - md_unplug(mddev); wait_event(mddev->recovery_wait, atomic_read(&mddev->recovery_active) == 0); mddev->curr_resync_completed = j; @@ -6929,7 +6925,6 @@ void md_do_sync(mddev_t *mddev) * about not overloading the IO subsystem. (things like an * e2fsck being done on the RAID array should execute fast) */ - md_unplug(mddev); cond_resched(); currspeed = ((unsigned long)(io_sectors-mddev->resync_mark_cnt))/2 @@ -6948,8 +6943,6 @@ void md_do_sync(mddev_t *mddev) * this also signals 'finished resyncing' to md_stop */ out: - md_unplug(mddev); - wait_event(mddev->recovery_wait, !atomic_read(&mddev->recovery_active)); /* tell personality that we are finished */ diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index 6d7ddf32ef2e..1cc8ed44e4ad 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -106,36 +106,6 @@ static void multipath_end_request(struct bio *bio, int error) rdev_dec_pending(rdev, conf->mddev); } -static void unplug_slaves(mddev_t *mddev) -{ - multipath_conf_t *conf = mddev->private; - int i; - - rcu_read_lock(); - for (i=0; iraid_disks; i++) { - mdk_rdev_t *rdev = rcu_dereference(conf->multipaths[i].rdev); - if (rdev && !test_bit(Faulty, &rdev->flags) - && atomic_read(&rdev->nr_pending)) { - struct request_queue *r_queue = bdev_get_queue(rdev->bdev); - - atomic_inc(&rdev->nr_pending); - rcu_read_unlock(); - - blk_unplug(r_queue); - - rdev_dec_pending(rdev, mddev); - rcu_read_lock(); - } - } - rcu_read_unlock(); -} - -static void multipath_unplug(struct request_queue *q) -{ - unplug_slaves(q->queuedata); -} - - static int multipath_make_request(mddev_t *mddev, struct bio * bio) { multipath_conf_t *conf = mddev->private; @@ -518,7 +488,6 @@ static int multipath_run (mddev_t *mddev) */ md_set_array_sectors(mddev, multipath_size(mddev, 0, 0)); - mddev->queue->unplug_fn = multipath_unplug; mddev->queue->backing_dev_info.congested_fn = multipath_congested; mddev->queue->backing_dev_info.congested_data = mddev; md_integrity_register(mddev); diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 637a96855edb..6338c0fe6208 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -25,21 +25,6 @@ #include "raid0.h" #include "raid5.h" -static void raid0_unplug(struct request_queue *q) -{ - mddev_t *mddev = q->queuedata; - raid0_conf_t *conf = mddev->private; - mdk_rdev_t **devlist = conf->devlist; - int raid_disks = conf->strip_zone[0].nb_dev; - int i; - - for (i=0; i < raid_disks; i++) { - struct request_queue *r_queue = bdev_get_queue(devlist[i]->bdev); - - blk_unplug(r_queue); - } -} - static int raid0_congested(void *data, int bits) { mddev_t *mddev = data; @@ -272,7 +257,6 @@ static int create_strip_zones(mddev_t *mddev, raid0_conf_t **private_conf) mdname(mddev), (unsigned long long)smallest->sectors); } - mddev->queue->unplug_fn = raid0_unplug; mddev->queue->backing_dev_info.congested_fn = raid0_congested; mddev->queue->backing_dev_info.congested_data = mddev; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index a23ffa397ba9..b67d822d57ae 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -52,23 +52,16 @@ #define NR_RAID1_BIOS 256 -static void unplug_slaves(mddev_t *mddev); - static void allow_barrier(conf_t *conf); static void lower_barrier(conf_t *conf); static void * r1bio_pool_alloc(gfp_t gfp_flags, void *data) { struct pool_info *pi = data; - r1bio_t *r1_bio; int size = offsetof(r1bio_t, bios[pi->raid_disks]); /* allocate a r1bio with room for raid_disks entries in the bios array */ - r1_bio = kzalloc(size, gfp_flags); - if (!r1_bio && pi->mddev) - unplug_slaves(pi->mddev); - - return r1_bio; + return kzalloc(size, gfp_flags); } static void r1bio_pool_free(void *r1_bio, void *data) @@ -91,10 +84,8 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data) int i, j; r1_bio = r1bio_pool_alloc(gfp_flags, pi); - if (!r1_bio) { - unplug_slaves(pi->mddev); + if (!r1_bio) return NULL; - } /* * Allocate bios : 1 for reading, n-1 for writing @@ -520,37 +511,6 @@ static int read_balance(conf_t *conf, r1bio_t *r1_bio) return new_disk; } -static void unplug_slaves(mddev_t *mddev) -{ - conf_t *conf = mddev->private; - int i; - - rcu_read_lock(); - for (i=0; iraid_disks; i++) { - mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[i].rdev); - if (rdev && !test_bit(Faulty, &rdev->flags) && atomic_read(&rdev->nr_pending)) { - struct request_queue *r_queue = bdev_get_queue(rdev->bdev); - - atomic_inc(&rdev->nr_pending); - rcu_read_unlock(); - - blk_unplug(r_queue); - - rdev_dec_pending(rdev, mddev); - rcu_read_lock(); - } - } - rcu_read_unlock(); -} - -static void raid1_unplug(struct request_queue *q) -{ - mddev_t *mddev = q->queuedata; - - unplug_slaves(mddev); - md_wakeup_thread(mddev->thread); -} - static int raid1_congested(void *data, int bits) { mddev_t *mddev = data; @@ -580,20 +540,16 @@ static int raid1_congested(void *data, int bits) } -static int flush_pending_writes(conf_t *conf) +static void flush_pending_writes(conf_t *conf) { /* Any writes that have been queued but are awaiting * bitmap updates get flushed here. - * We return 1 if any requests were actually submitted. */ - int rv = 0; - spin_lock_irq(&conf->device_lock); if (conf->pending_bio_list.head) { struct bio *bio; bio = bio_list_get(&conf->pending_bio_list); - blk_remove_plug(conf->mddev->queue); spin_unlock_irq(&conf->device_lock); /* flush any pending bitmap writes to * disk before proceeding w/ I/O */ @@ -605,10 +561,14 @@ static int flush_pending_writes(conf_t *conf) generic_make_request(bio); bio = next; } - rv = 1; } else spin_unlock_irq(&conf->device_lock); - return rv; +} + +static void md_kick_device(mddev_t *mddev) +{ + blk_flush_plug(current); + md_wakeup_thread(mddev->thread); } /* Barriers.... @@ -640,8 +600,7 @@ static void raise_barrier(conf_t *conf) /* Wait until no block IO is waiting */ wait_event_lock_irq(conf->wait_barrier, !conf->nr_waiting, - conf->resync_lock, - raid1_unplug(conf->mddev->queue)); + conf->resync_lock, md_kick_device(conf->mddev)); /* block any new IO from starting */ conf->barrier++; @@ -649,8 +608,7 @@ static void raise_barrier(conf_t *conf) /* Now wait for all pending IO to complete */ wait_event_lock_irq(conf->wait_barrier, !conf->nr_pending && conf->barrier < RESYNC_DEPTH, - conf->resync_lock, - raid1_unplug(conf->mddev->queue)); + conf->resync_lock, md_kick_device(conf->mddev)); spin_unlock_irq(&conf->resync_lock); } @@ -672,7 +630,7 @@ static void wait_barrier(conf_t *conf) conf->nr_waiting++; wait_event_lock_irq(conf->wait_barrier, !conf->barrier, conf->resync_lock, - raid1_unplug(conf->mddev->queue)); + md_kick_device(conf->mddev)); conf->nr_waiting--; } conf->nr_pending++; @@ -709,7 +667,7 @@ static void freeze_array(conf_t *conf) conf->nr_pending == conf->nr_queued+1, conf->resync_lock, ({ flush_pending_writes(conf); - raid1_unplug(conf->mddev->queue); })); + md_kick_device(conf->mddev); })); spin_unlock_irq(&conf->resync_lock); } static void unfreeze_array(conf_t *conf) @@ -959,7 +917,6 @@ static int make_request(mddev_t *mddev, struct bio * bio) atomic_inc(&r1_bio->remaining); spin_lock_irqsave(&conf->device_lock, flags); bio_list_add(&conf->pending_bio_list, mbio); - blk_plug_device(mddev->queue); spin_unlock_irqrestore(&conf->device_lock, flags); } r1_bio_write_done(r1_bio, bio->bi_vcnt, behind_pages, behind_pages != NULL); @@ -968,7 +925,7 @@ static int make_request(mddev_t *mddev, struct bio * bio) /* In case raid1d snuck in to freeze_array */ wake_up(&conf->wait_barrier); - if (do_sync) + if (do_sync || !bitmap) md_wakeup_thread(mddev->thread); return 0; @@ -1558,7 +1515,6 @@ static void raid1d(mddev_t *mddev) unsigned long flags; conf_t *conf = mddev->private; struct list_head *head = &conf->retry_list; - int unplug=0; mdk_rdev_t *rdev; md_check_recovery(mddev); @@ -1566,7 +1522,7 @@ static void raid1d(mddev_t *mddev) for (;;) { char b[BDEVNAME_SIZE]; - unplug += flush_pending_writes(conf); + flush_pending_writes(conf); spin_lock_irqsave(&conf->device_lock, flags); if (list_empty(head)) { @@ -1580,10 +1536,9 @@ static void raid1d(mddev_t *mddev) mddev = r1_bio->mddev; conf = mddev->private; - if (test_bit(R1BIO_IsSync, &r1_bio->state)) { + if (test_bit(R1BIO_IsSync, &r1_bio->state)) sync_request_write(mddev, r1_bio); - unplug = 1; - } else { + else { int disk; /* we got a read error. Maybe the drive is bad. Maybe just @@ -1633,14 +1588,11 @@ static void raid1d(mddev_t *mddev) bio->bi_end_io = raid1_end_read_request; bio->bi_rw = READ | do_sync; bio->bi_private = r1_bio; - unplug = 1; generic_make_request(bio); } } cond_resched(); } - if (unplug) - unplug_slaves(mddev); } @@ -2064,7 +2016,6 @@ static int run(mddev_t *mddev) md_set_array_sectors(mddev, raid1_size(mddev, 0, 0)); - mddev->queue->unplug_fn = raid1_unplug; mddev->queue->backing_dev_info.congested_fn = raid1_congested; mddev->queue->backing_dev_info.congested_data = mddev; md_integrity_register(mddev); diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 3b607b28741b..e79f1c5bf71b 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -57,23 +57,16 @@ */ #define NR_RAID10_BIOS 256 -static void unplug_slaves(mddev_t *mddev); - static void allow_barrier(conf_t *conf); static void lower_barrier(conf_t *conf); static void * r10bio_pool_alloc(gfp_t gfp_flags, void *data) { conf_t *conf = data; - r10bio_t *r10_bio; int size = offsetof(struct r10bio_s, devs[conf->copies]); /* allocate a r10bio with room for raid_disks entries in the bios array */ - r10_bio = kzalloc(size, gfp_flags); - if (!r10_bio && conf->mddev) - unplug_slaves(conf->mddev); - - return r10_bio; + return kzalloc(size, gfp_flags); } static void r10bio_pool_free(void *r10_bio, void *data) @@ -106,10 +99,8 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data) int nalloc; r10_bio = r10bio_pool_alloc(gfp_flags, conf); - if (!r10_bio) { - unplug_slaves(conf->mddev); + if (!r10_bio) return NULL; - } if (test_bit(MD_RECOVERY_SYNC, &conf->mddev->recovery)) nalloc = conf->copies; /* resync */ @@ -597,37 +588,6 @@ rb_out: return disk; } -static void unplug_slaves(mddev_t *mddev) -{ - conf_t *conf = mddev->private; - int i; - - rcu_read_lock(); - for (i=0; i < conf->raid_disks; i++) { - mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[i].rdev); - if (rdev && !test_bit(Faulty, &rdev->flags) && atomic_read(&rdev->nr_pending)) { - struct request_queue *r_queue = bdev_get_queue(rdev->bdev); - - atomic_inc(&rdev->nr_pending); - rcu_read_unlock(); - - blk_unplug(r_queue); - - rdev_dec_pending(rdev, mddev); - rcu_read_lock(); - } - } - rcu_read_unlock(); -} - -static void raid10_unplug(struct request_queue *q) -{ - mddev_t *mddev = q->queuedata; - - unplug_slaves(q->queuedata); - md_wakeup_thread(mddev->thread); -} - static int raid10_congested(void *data, int bits) { mddev_t *mddev = data; @@ -649,20 +609,16 @@ static int raid10_congested(void *data, int bits) return ret; } -static int flush_pending_writes(conf_t *conf) +static void flush_pending_writes(conf_t *conf) { /* Any writes that have been queued but are awaiting * bitmap updates get flushed here. - * We return 1 if any requests were actually submitted. */ - int rv = 0; - spin_lock_irq(&conf->device_lock); if (conf->pending_bio_list.head) { struct bio *bio; bio = bio_list_get(&conf->pending_bio_list); - blk_remove_plug(conf->mddev->queue); spin_unlock_irq(&conf->device_lock); /* flush any pending bitmap writes to disk * before proceeding w/ I/O */ @@ -674,11 +630,16 @@ static int flush_pending_writes(conf_t *conf) generic_make_request(bio); bio = next; } - rv = 1; } else spin_unlock_irq(&conf->device_lock); - return rv; } + +static void md_kick_device(mddev_t *mddev) +{ + blk_flush_plug(current); + md_wakeup_thread(mddev->thread); +} + /* Barriers.... * Sometimes we need to suspend IO while we do something else, * either some resync/recovery, or reconfigure the array. @@ -708,8 +669,7 @@ static void raise_barrier(conf_t *conf, int force) /* Wait until no block IO is waiting (unless 'force') */ wait_event_lock_irq(conf->wait_barrier, force || !conf->nr_waiting, - conf->resync_lock, - raid10_unplug(conf->mddev->queue)); + conf->resync_lock, md_kick_device(conf->mddev)); /* block any new IO from starting */ conf->barrier++; @@ -717,8 +677,7 @@ static void raise_barrier(conf_t *conf, int force) /* No wait for all pending IO to complete */ wait_event_lock_irq(conf->wait_barrier, !conf->nr_pending && conf->barrier < RESYNC_DEPTH, - conf->resync_lock, - raid10_unplug(conf->mddev->queue)); + conf->resync_lock, md_kick_device(conf->mddev)); spin_unlock_irq(&conf->resync_lock); } @@ -739,7 +698,7 @@ static void wait_barrier(conf_t *conf) conf->nr_waiting++; wait_event_lock_irq(conf->wait_barrier, !conf->barrier, conf->resync_lock, - raid10_unplug(conf->mddev->queue)); + md_kick_device(conf->mddev)); conf->nr_waiting--; } conf->nr_pending++; @@ -776,7 +735,7 @@ static void freeze_array(conf_t *conf) conf->nr_pending == conf->nr_queued+1, conf->resync_lock, ({ flush_pending_writes(conf); - raid10_unplug(conf->mddev->queue); })); + md_kick_device(conf->mddev); })); spin_unlock_irq(&conf->resync_lock); } @@ -971,7 +930,6 @@ static int make_request(mddev_t *mddev, struct bio * bio) atomic_inc(&r10_bio->remaining); spin_lock_irqsave(&conf->device_lock, flags); bio_list_add(&conf->pending_bio_list, mbio); - blk_plug_device(mddev->queue); spin_unlock_irqrestore(&conf->device_lock, flags); } @@ -988,7 +946,7 @@ static int make_request(mddev_t *mddev, struct bio * bio) /* In case raid10d snuck in to freeze_array */ wake_up(&conf->wait_barrier); - if (do_sync) + if (do_sync || !mddev->bitmap) md_wakeup_thread(mddev->thread); return 0; @@ -1681,7 +1639,6 @@ static void raid10d(mddev_t *mddev) unsigned long flags; conf_t *conf = mddev->private; struct list_head *head = &conf->retry_list; - int unplug=0; mdk_rdev_t *rdev; md_check_recovery(mddev); @@ -1689,7 +1646,7 @@ static void raid10d(mddev_t *mddev) for (;;) { char b[BDEVNAME_SIZE]; - unplug += flush_pending_writes(conf); + flush_pending_writes(conf); spin_lock_irqsave(&conf->device_lock, flags); if (list_empty(head)) { @@ -1703,13 +1660,11 @@ static void raid10d(mddev_t *mddev) mddev = r10_bio->mddev; conf = mddev->private; - if (test_bit(R10BIO_IsSync, &r10_bio->state)) { + if (test_bit(R10BIO_IsSync, &r10_bio->state)) sync_request_write(mddev, r10_bio); - unplug = 1; - } else if (test_bit(R10BIO_IsRecover, &r10_bio->state)) { + else if (test_bit(R10BIO_IsRecover, &r10_bio->state)) recovery_request_write(mddev, r10_bio); - unplug = 1; - } else { + else { int mirror; /* we got a read error. Maybe the drive is bad. Maybe just * the block and we can fix it. @@ -1756,14 +1711,11 @@ static void raid10d(mddev_t *mddev) bio->bi_rw = READ | do_sync; bio->bi_private = r10_bio; bio->bi_end_io = raid10_end_read_request; - unplug = 1; generic_make_request(bio); } } cond_resched(); } - if (unplug) - unplug_slaves(mddev); } @@ -2376,7 +2328,6 @@ static int run(mddev_t *mddev) md_set_array_sectors(mddev, size); mddev->resync_max_sectors = size; - mddev->queue->unplug_fn = raid10_unplug; mddev->queue->backing_dev_info.congested_fn = raid10_congested; mddev->queue->backing_dev_info.congested_data = mddev; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 702812824195..e867ee42b152 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -433,8 +433,6 @@ static int has_failed(raid5_conf_t *conf) return 0; } -static void unplug_slaves(mddev_t *mddev); - static struct stripe_head * get_active_stripe(raid5_conf_t *conf, sector_t sector, int previous, int noblock, int noquiesce) @@ -463,8 +461,7 @@ get_active_stripe(raid5_conf_t *conf, sector_t sector, < (conf->max_nr_stripes *3/4) || !conf->inactive_blocked), conf->device_lock, - md_raid5_unplug_device(conf) - ); + md_raid5_kick_device(conf)); conf->inactive_blocked = 0; } else init_stripe(sh, sector, previous); @@ -1473,8 +1470,7 @@ static int resize_stripes(raid5_conf_t *conf, int newsize) wait_event_lock_irq(conf->wait_for_stripe, !list_empty(&conf->inactive_list), conf->device_lock, - unplug_slaves(conf->mddev) - ); + blk_flush_plug(current)); osh = get_free_stripe(conf); spin_unlock_irq(&conf->device_lock); atomic_set(&nsh->count, 1); @@ -3645,58 +3641,19 @@ static void activate_bit_delay(raid5_conf_t *conf) } } -static void unplug_slaves(mddev_t *mddev) +void md_raid5_kick_device(raid5_conf_t *conf) { - raid5_conf_t *conf = mddev->private; - int i; - int devs = max(conf->raid_disks, conf->previous_raid_disks); - - rcu_read_lock(); - for (i = 0; i < devs; i++) { - mdk_rdev_t *rdev = rcu_dereference(conf->disks[i].rdev); - if (rdev && !test_bit(Faulty, &rdev->flags) && atomic_read(&rdev->nr_pending)) { - struct request_queue *r_queue = bdev_get_queue(rdev->bdev); - - atomic_inc(&rdev->nr_pending); - rcu_read_unlock(); - - blk_unplug(r_queue); - - rdev_dec_pending(rdev, mddev); - rcu_read_lock(); - } - } - rcu_read_unlock(); -} - -void md_raid5_unplug_device(raid5_conf_t *conf) -{ - unsigned long flags; - - spin_lock_irqsave(&conf->device_lock, flags); - - if (plugger_remove_plug(&conf->plug)) { - conf->seq_flush++; - raid5_activate_delayed(conf); - } + blk_flush_plug(current); + raid5_activate_delayed(conf); md_wakeup_thread(conf->mddev->thread); - - spin_unlock_irqrestore(&conf->device_lock, flags); - - unplug_slaves(conf->mddev); } -EXPORT_SYMBOL_GPL(md_raid5_unplug_device); +EXPORT_SYMBOL_GPL(md_raid5_kick_device); static void raid5_unplug(struct plug_handle *plug) { raid5_conf_t *conf = container_of(plug, raid5_conf_t, plug); - md_raid5_unplug_device(conf); -} -static void raid5_unplug_queue(struct request_queue *q) -{ - mddev_t *mddev = q->queuedata; - md_raid5_unplug_device(mddev->private); + md_raid5_kick_device(conf); } int md_raid5_congested(mddev_t *mddev, int bits) @@ -4100,7 +4057,7 @@ static int make_request(mddev_t *mddev, struct bio * bi) * add failed due to overlap. Flush everything * and wait a while */ - md_raid5_unplug_device(conf); + md_raid5_kick_device(conf); release_stripe(sh); schedule(); goto retry; @@ -4365,7 +4322,6 @@ static inline sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *ski if (sector_nr >= max_sector) { /* just being told to finish up .. nothing much to do */ - unplug_slaves(mddev); if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) { end_reshape(conf); @@ -4569,7 +4525,6 @@ static void raid5d(mddev_t *mddev) spin_unlock_irq(&conf->device_lock); async_tx_issue_pending_all(); - unplug_slaves(mddev); pr_debug("--- raid5d inactive\n"); } @@ -5205,7 +5160,6 @@ static int run(mddev_t *mddev) mddev->queue->backing_dev_info.congested_data = mddev; mddev->queue->backing_dev_info.congested_fn = raid5_congested; mddev->queue->queue_lock = &conf->device_lock; - mddev->queue->unplug_fn = raid5_unplug_queue; chunk_size = mddev->chunk_sectors << 9; blk_queue_io_min(mddev->queue, chunk_size); diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h index 2ace0582b409..8d563a4f022a 100644 --- a/drivers/md/raid5.h +++ b/drivers/md/raid5.h @@ -503,6 +503,6 @@ static inline int algorithm_is_DDF(int layout) } extern int md_raid5_congested(mddev_t *mddev, int bits); -extern void md_raid5_unplug_device(raid5_conf_t *conf); +extern void md_raid5_kick_device(raid5_conf_t *conf); extern int raid5_set_cache_size(mddev_t *mddev, int size); #endif diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c index ae7cad185898..b29eb4eaa86e 100644 --- a/drivers/message/i2o/i2o_block.c +++ b/drivers/message/i2o/i2o_block.c @@ -895,11 +895,7 @@ static void i2o_block_request_fn(struct request_queue *q) { struct request *req; - while (!blk_queue_plugged(q)) { - req = blk_peek_request(q); - if (!req) - break; - + while ((req = blk_peek_request(q)) != NULL) { if (req->cmd_type == REQ_TYPE_FS) { struct i2o_block_delayed_request *dreq; struct i2o_block_request *ireq = req->special; diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 4e42d030e097..2ae727568df9 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -55,8 +55,7 @@ static int mmc_queue_thread(void *d) spin_lock_irq(q->queue_lock); set_current_state(TASK_INTERRUPTIBLE); - if (!blk_queue_plugged(q)) - req = blk_fetch_request(q); + req = blk_fetch_request(q); mq->req = req; spin_unlock_irq(q->queue_lock); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 794bfd962266..4d2df2f76ea0 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1917,7 +1917,7 @@ static void __dasd_process_request_queue(struct dasd_block *block) return; } /* Now we try to fetch requests from the request queue */ - while (!blk_queue_plugged(queue) && (req = blk_peek_request(queue))) { + while ((req = blk_peek_request(queue))) { if (basedev->features & DASD_FEATURE_READONLY && rq_data_dir(req) == WRITE) { DBF_DEV_EVENT(DBF_ERR, basedev, diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c index 55d2d0f4eabc..f061b2527b44 100644 --- a/drivers/s390/char/tape_block.c +++ b/drivers/s390/char/tape_block.c @@ -161,7 +161,6 @@ tapeblock_requeue(struct work_struct *work) { spin_lock_irq(&device->blk_data.request_queue_lock); while ( - !blk_queue_plugged(queue) && blk_peek_request(queue) && nr_queued < TAPEBLOCK_MIN_REQUEUE ) { diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 998c01be3234..2cefabd5bdb5 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -3913,7 +3913,7 @@ fc_bsg_request_handler(struct request_queue *q, struct Scsi_Host *shost, if (!get_device(dev)) return; - while (!blk_queue_plugged(q)) { + while (1) { if (rport && (rport->port_state == FC_PORTSTATE_BLOCKED) && !(rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT)) break; diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 927e99cb7225..c6fcf76cade5 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -173,11 +173,7 @@ static void sas_smp_request(struct request_queue *q, struct Scsi_Host *shost, int ret; int (*handler)(struct Scsi_Host *, struct sas_rphy *, struct request *); - while (!blk_queue_plugged(q)) { - req = blk_fetch_request(q); - if (!req) - break; - + while ((req = blk_fetch_request(q)) != NULL) { spin_unlock_irq(q->queue_lock); handler = to_sas_internal(shost->transportt)->f->smp_handler; diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 67f0c09983c8..c1b539d7b0d3 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -392,9 +392,8 @@ static int iblock_do_task(struct se_task *task) { struct se_device *dev = task->task_se_cmd->se_dev; struct iblock_req *req = IBLOCK_REQ(task); - struct iblock_dev *ibd = (struct iblock_dev *)req->ib_dev; - struct request_queue *q = bdev_get_queue(ibd->ibd_bd); struct bio *bio = req->ib_bio, *nbio = NULL; + struct blk_plug plug; int rw; if (task->task_data_direction == DMA_TO_DEVICE) { @@ -412,6 +411,7 @@ static int iblock_do_task(struct se_task *task) rw = READ; } + blk_start_plug(&plug); while (bio) { nbio = bio->bi_next; bio->bi_next = NULL; @@ -421,9 +421,8 @@ static int iblock_do_task(struct se_task *task) submit_bio(rw, bio); bio = nbio; } + blk_finish_plug(&plug); - if (q->unplug_fn) - q->unplug_fn(q); return PYX_TRANSPORT_SENT_TO_TRANSPORT; } diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c index 65794b8fe79e..1cc84b276131 100644 --- a/fs/adfs/inode.c +++ b/fs/adfs/inode.c @@ -73,7 +73,6 @@ static sector_t _adfs_bmap(struct address_space *mapping, sector_t block) static const struct address_space_operations adfs_aops = { .readpage = adfs_readpage, .writepage = adfs_writepage, - .sync_page = block_sync_page, .write_begin = adfs_write_begin, .write_end = generic_write_end, .bmap = _adfs_bmap diff --git a/fs/affs/file.c b/fs/affs/file.c index 0a90dcd46de2..acf321b70fcd 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -429,7 +429,6 @@ static sector_t _affs_bmap(struct address_space *mapping, sector_t block) const struct address_space_operations affs_aops = { .readpage = affs_readpage, .writepage = affs_writepage, - .sync_page = block_sync_page, .write_begin = affs_write_begin, .write_end = generic_write_end, .bmap = _affs_bmap @@ -786,7 +785,6 @@ out: const struct address_space_operations affs_aops_ofs = { .readpage = affs_readpage_ofs, //.writepage = affs_writepage_ofs, - //.sync_page = affs_sync_page_ofs, .write_begin = affs_write_begin_ofs, .write_end = affs_write_end_ofs }; diff --git a/fs/aio.c b/fs/aio.c index fc557a3be0a9..c5ea494ea9e2 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -1550,9 +1550,11 @@ static void aio_batch_free(struct hlist_head *batch_hash) struct hlist_node *pos, *n; int i; + /* + * TODO: kill this + */ for (i = 0; i < AIO_BATCH_HASH_SIZE; i++) { hlist_for_each_entry_safe(abe, pos, n, &batch_hash[i], list) { - blk_run_address_space(abe->mapping); iput(abe->mapping->host); hlist_del(&abe->list); mempool_free(abe, abe_pool); diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index b1d0c794747b..06457ed8f3e7 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -75,7 +75,6 @@ static const struct inode_operations befs_dir_inode_operations = { static const struct address_space_operations befs_aops = { .readpage = befs_readpage, - .sync_page = block_sync_page, .bmap = befs_bmap, }; diff --git a/fs/bfs/file.c b/fs/bfs/file.c index eb67edd0f8ea..f20e8a71062f 100644 --- a/fs/bfs/file.c +++ b/fs/bfs/file.c @@ -186,7 +186,6 @@ static sector_t bfs_bmap(struct address_space *mapping, sector_t block) const struct address_space_operations bfs_aops = { .readpage = bfs_readpage, .writepage = bfs_writepage, - .sync_page = block_sync_page, .write_begin = bfs_write_begin, .write_end = generic_write_end, .bmap = bfs_bmap, diff --git a/fs/block_dev.c b/fs/block_dev.c index 4fb8a3431531..fffc2c672396 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1520,7 +1520,6 @@ static int blkdev_releasepage(struct page *page, gfp_t wait) static const struct address_space_operations def_blk_aops = { .readpage = blkdev_readpage, .writepage = blkdev_writepage, - .sync_page = block_sync_page, .write_begin = blkdev_write_begin, .write_end = blkdev_write_end, .writepages = generic_writepages, diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index e1aa8d607bc7..ada1f6bd0a57 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -847,7 +847,6 @@ static const struct address_space_operations btree_aops = { .writepages = btree_writepages, .releasepage = btree_releasepage, .invalidatepage = btree_invalidatepage, - .sync_page = block_sync_page, #ifdef CONFIG_MIGRATION .migratepage = btree_migratepage, #endif @@ -1330,82 +1329,6 @@ static int btrfs_congested_fn(void *congested_data, int bdi_bits) return ret; } -/* - * this unplugs every device on the box, and it is only used when page - * is null - */ -static void __unplug_io_fn(struct backing_dev_info *bdi, struct page *page) -{ - struct btrfs_device *device; - struct btrfs_fs_info *info; - - info = (struct btrfs_fs_info *)bdi->unplug_io_data; - list_for_each_entry(device, &info->fs_devices->devices, dev_list) { - if (!device->bdev) - continue; - - bdi = blk_get_backing_dev_info(device->bdev); - if (bdi->unplug_io_fn) - bdi->unplug_io_fn(bdi, page); - } -} - -static void btrfs_unplug_io_fn(struct backing_dev_info *bdi, struct page *page) -{ - struct inode *inode; - struct extent_map_tree *em_tree; - struct extent_map *em; - struct address_space *mapping; - u64 offset; - - /* the generic O_DIRECT read code does this */ - if (1 || !page) { - __unplug_io_fn(bdi, page); - return; - } - - /* - * page->mapping may change at any time. Get a consistent copy - * and use that for everything below - */ - smp_mb(); - mapping = page->mapping; - if (!mapping) - return; - - inode = mapping->host; - - /* - * don't do the expensive searching for a small number of - * devices - */ - if (BTRFS_I(inode)->root->fs_info->fs_devices->open_devices <= 2) { - __unplug_io_fn(bdi, page); - return; - } - - offset = page_offset(page); - - em_tree = &BTRFS_I(inode)->extent_tree; - read_lock(&em_tree->lock); - em = lookup_extent_mapping(em_tree, offset, PAGE_CACHE_SIZE); - read_unlock(&em_tree->lock); - if (!em) { - __unplug_io_fn(bdi, page); - return; - } - - if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - free_extent_map(em); - __unplug_io_fn(bdi, page); - return; - } - offset = offset - em->start; - btrfs_unplug_page(&BTRFS_I(inode)->root->fs_info->mapping_tree, - em->block_start + offset, page); - free_extent_map(em); -} - /* * If this fails, caller must call bdi_destroy() to get rid of the * bdi again. @@ -1420,8 +1343,6 @@ static int setup_bdi(struct btrfs_fs_info *info, struct backing_dev_info *bdi) return err; bdi->ra_pages = default_backing_dev_info.ra_pages; - bdi->unplug_io_fn = btrfs_unplug_io_fn; - bdi->unplug_io_data = info; bdi->congested_fn = btrfs_congested_fn; bdi->congested_data = info; return 0; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index fb9bd7832b6d..462e08e724b0 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7218,7 +7218,6 @@ static const struct address_space_operations btrfs_aops = { .writepage = btrfs_writepage, .writepages = btrfs_writepages, .readpages = btrfs_readpages, - .sync_page = block_sync_page, .direct_IO = btrfs_direct_IO, .invalidatepage = btrfs_invalidatepage, .releasepage = btrfs_releasepage, diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index af7dbca15276..6e0e82a1b188 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -162,7 +162,6 @@ static noinline int run_scheduled_bios(struct btrfs_device *device) struct bio *cur; int again = 0; unsigned long num_run; - unsigned long num_sync_run; unsigned long batch_run = 0; unsigned long limit; unsigned long last_waited = 0; @@ -173,11 +172,6 @@ static noinline int run_scheduled_bios(struct btrfs_device *device) limit = btrfs_async_submit_limit(fs_info); limit = limit * 2 / 3; - /* we want to make sure that every time we switch from the sync - * list to the normal list, we unplug - */ - num_sync_run = 0; - loop: spin_lock(&device->io_lock); @@ -223,15 +217,6 @@ loop_lock: spin_unlock(&device->io_lock); - /* - * if we're doing the regular priority list, make sure we unplug - * for any high prio bios we've sent down - */ - if (pending_bios == &device->pending_bios && num_sync_run > 0) { - num_sync_run = 0; - blk_run_backing_dev(bdi, NULL); - } - while (pending) { rmb(); @@ -259,19 +244,11 @@ loop_lock: BUG_ON(atomic_read(&cur->bi_cnt) == 0); - if (cur->bi_rw & REQ_SYNC) - num_sync_run++; - submit_bio(cur->bi_rw, cur); num_run++; batch_run++; - if (need_resched()) { - if (num_sync_run) { - blk_run_backing_dev(bdi, NULL); - num_sync_run = 0; - } + if (need_resched()) cond_resched(); - } /* * we made progress, there is more work to do and the bdi @@ -304,13 +281,8 @@ loop_lock: * against it before looping */ last_waited = ioc->last_waited; - if (need_resched()) { - if (num_sync_run) { - blk_run_backing_dev(bdi, NULL); - num_sync_run = 0; - } + if (need_resched()) cond_resched(); - } continue; } spin_lock(&device->io_lock); @@ -323,22 +295,6 @@ loop_lock: } } - if (num_sync_run) { - num_sync_run = 0; - blk_run_backing_dev(bdi, NULL); - } - /* - * IO has already been through a long path to get here. Checksumming, - * async helper threads, perhaps compression. We've done a pretty - * good job of collecting a batch of IO and should just unplug - * the device right away. - * - * This will help anyone who is waiting on the IO, they might have - * already unplugged, but managed to do so before the bio they - * cared about found its way down here. - */ - blk_run_backing_dev(bdi, NULL); - cond_resched(); if (again) goto loop; @@ -2948,7 +2904,7 @@ static int find_live_mirror(struct map_lookup *map, int first, int num, static int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, u64 logical, u64 *length, struct btrfs_multi_bio **multi_ret, - int mirror_num, struct page *unplug_page) + int mirror_num) { struct extent_map *em; struct map_lookup *map; @@ -2980,11 +2936,6 @@ again: em = lookup_extent_mapping(em_tree, logical, *length); read_unlock(&em_tree->lock); - if (!em && unplug_page) { - kfree(multi); - return 0; - } - if (!em) { printk(KERN_CRIT "unable to find logical %llu len %llu\n", (unsigned long long)logical, @@ -3040,13 +2991,13 @@ again: *length = em->len - offset; } - if (!multi_ret && !unplug_page) + if (!multi_ret) goto out; num_stripes = 1; stripe_index = 0; if (map->type & BTRFS_BLOCK_GROUP_RAID1) { - if (unplug_page || (rw & REQ_WRITE)) + if (rw & REQ_WRITE) num_stripes = map->num_stripes; else if (mirror_num) stripe_index = mirror_num - 1; @@ -3068,7 +3019,7 @@ again: stripe_index = do_div(stripe_nr, factor); stripe_index *= map->sub_stripes; - if (unplug_page || (rw & REQ_WRITE)) + if (rw & REQ_WRITE) num_stripes = map->sub_stripes; else if (mirror_num) stripe_index += mirror_num - 1; @@ -3088,22 +3039,10 @@ again: BUG_ON(stripe_index >= map->num_stripes); for (i = 0; i < num_stripes; i++) { - if (unplug_page) { - struct btrfs_device *device; - struct backing_dev_info *bdi; - - device = map->stripes[stripe_index].dev; - if (device->bdev) { - bdi = blk_get_backing_dev_info(device->bdev); - if (bdi->unplug_io_fn) - bdi->unplug_io_fn(bdi, unplug_page); - } - } else { - multi->stripes[i].physical = - map->stripes[stripe_index].physical + - stripe_offset + stripe_nr * map->stripe_len; - multi->stripes[i].dev = map->stripes[stripe_index].dev; - } + multi->stripes[i].physical = + map->stripes[stripe_index].physical + + stripe_offset + stripe_nr * map->stripe_len; + multi->stripes[i].dev = map->stripes[stripe_index].dev; stripe_index++; } if (multi_ret) { @@ -3121,7 +3060,7 @@ int btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, struct btrfs_multi_bio **multi_ret, int mirror_num) { return __btrfs_map_block(map_tree, rw, logical, length, multi_ret, - mirror_num, NULL); + mirror_num); } int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree, @@ -3189,14 +3128,6 @@ int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree, return 0; } -int btrfs_unplug_page(struct btrfs_mapping_tree *map_tree, - u64 logical, struct page *page) -{ - u64 length = PAGE_CACHE_SIZE; - return __btrfs_map_block(map_tree, READ, logical, &length, - NULL, 0, page); -} - static void end_bio_multi_stripe(struct bio *bio, int err) { struct btrfs_multi_bio *multi = bio->bi_private; diff --git a/fs/buffer.c b/fs/buffer.c index 2219a76e2caf..f903f2e5b4fe 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -54,23 +54,15 @@ init_buffer(struct buffer_head *bh, bh_end_io_t *handler, void *private) } EXPORT_SYMBOL(init_buffer); -static int sync_buffer(void *word) +static int sleep_on_buffer(void *word) { - struct block_device *bd; - struct buffer_head *bh - = container_of(word, struct buffer_head, b_state); - - smp_mb(); - bd = bh->b_bdev; - if (bd) - blk_run_address_space(bd->bd_inode->i_mapping); io_schedule(); return 0; } void __lock_buffer(struct buffer_head *bh) { - wait_on_bit_lock(&bh->b_state, BH_Lock, sync_buffer, + wait_on_bit_lock(&bh->b_state, BH_Lock, sleep_on_buffer, TASK_UNINTERRUPTIBLE); } EXPORT_SYMBOL(__lock_buffer); @@ -90,7 +82,7 @@ EXPORT_SYMBOL(unlock_buffer); */ void __wait_on_buffer(struct buffer_head * bh) { - wait_on_bit(&bh->b_state, BH_Lock, sync_buffer, TASK_UNINTERRUPTIBLE); + wait_on_bit(&bh->b_state, BH_Lock, sleep_on_buffer, TASK_UNINTERRUPTIBLE); } EXPORT_SYMBOL(__wait_on_buffer); @@ -749,7 +741,7 @@ static int fsync_buffers_list(spinlock_t *lock, struct list_head *list) { struct buffer_head *bh; struct list_head tmp; - struct address_space *mapping, *prev_mapping = NULL; + struct address_space *mapping; int err = 0, err2; INIT_LIST_HEAD(&tmp); @@ -783,10 +775,6 @@ static int fsync_buffers_list(spinlock_t *lock, struct list_head *list) * wait_on_buffer() will do that for us * through sync_buffer(). */ - if (prev_mapping && prev_mapping != mapping) - blk_run_address_space(prev_mapping); - prev_mapping = mapping; - brelse(bh); spin_lock(lock); } @@ -3138,17 +3126,6 @@ out: } EXPORT_SYMBOL(try_to_free_buffers); -void block_sync_page(struct page *page) -{ - struct address_space *mapping; - - smp_mb(); - mapping = page_mapping(page); - if (mapping) - blk_run_backing_dev(mapping->backing_dev_info, page); -} -EXPORT_SYMBOL(block_sync_page); - /* * There are no bdflush tunables left. But distributions are * still running obsolete flush daemons, so we terminate them here. diff --git a/fs/cifs/file.c b/fs/cifs/file.c index e964b1cd5dd0..c27d236738fc 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1569,34 +1569,6 @@ int cifs_fsync(struct file *file, int datasync) return rc; } -/* static void cifs_sync_page(struct page *page) -{ - struct address_space *mapping; - struct inode *inode; - unsigned long index = page->index; - unsigned int rpages = 0; - int rc = 0; - - cFYI(1, "sync page %p", page); - mapping = page->mapping; - if (!mapping) - return 0; - inode = mapping->host; - if (!inode) - return; */ - -/* fill in rpages then - result = cifs_pagein_inode(inode, index, rpages); */ /* BB finish */ - -/* cFYI(1, "rpages is %d for sync page of Index %ld", rpages, index); - -#if 0 - if (rc < 0) - return rc; - return 0; -#endif -} */ - /* * As file closes, flush all cached write data for this inode checking * for write behind errors. @@ -2510,7 +2482,6 @@ const struct address_space_operations cifs_addr_ops = { .set_page_dirty = __set_page_dirty_nobuffers, .releasepage = cifs_release_page, .invalidatepage = cifs_invalidate_page, - /* .sync_page = cifs_sync_page, */ /* .direct_IO = */ }; @@ -2528,6 +2499,5 @@ const struct address_space_operations cifs_addr_ops_smallbuf = { .set_page_dirty = __set_page_dirty_nobuffers, .releasepage = cifs_release_page, .invalidatepage = cifs_invalidate_page, - /* .sync_page = cifs_sync_page, */ /* .direct_IO = */ }; diff --git a/fs/direct-io.c b/fs/direct-io.c index b044705eedd4..df709b3b860a 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -1110,11 +1110,8 @@ direct_io_worker(int rw, struct kiocb *iocb, struct inode *inode, ((rw & READ) || (dio->result == dio->size))) ret = -EIOCBQUEUED; - if (ret != -EIOCBQUEUED) { - /* All IO is now issued, send it on its way */ - blk_run_address_space(inode->i_mapping); + if (ret != -EIOCBQUEUED) dio_await_completion(dio); - } /* * Sync will always be dropping the final ref and completing the diff --git a/fs/efs/inode.c b/fs/efs/inode.c index a8e7797b9477..9c13412e6c99 100644 --- a/fs/efs/inode.c +++ b/fs/efs/inode.c @@ -23,7 +23,6 @@ static sector_t _efs_bmap(struct address_space *mapping, sector_t block) } static const struct address_space_operations efs_aops = { .readpage = efs_readpage, - .sync_page = block_sync_page, .bmap = _efs_bmap }; diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index a7555238c41a..82b94c8f5d22 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -795,7 +795,6 @@ const struct address_space_operations exofs_aops = { .direct_IO = NULL, /* TODO: Should be trivial to do */ /* With these NULL has special meaning or default is not exported */ - .sync_page = NULL, .get_xip_mem = NULL, .migratepage = NULL, .launder_page = NULL, diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 40ad210a5049..c47f706878b5 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -860,7 +860,6 @@ const struct address_space_operations ext2_aops = { .readpage = ext2_readpage, .readpages = ext2_readpages, .writepage = ext2_writepage, - .sync_page = block_sync_page, .write_begin = ext2_write_begin, .write_end = ext2_write_end, .bmap = ext2_bmap, @@ -880,7 +879,6 @@ const struct address_space_operations ext2_nobh_aops = { .readpage = ext2_readpage, .readpages = ext2_readpages, .writepage = ext2_nobh_writepage, - .sync_page = block_sync_page, .write_begin = ext2_nobh_write_begin, .write_end = nobh_write_end, .bmap = ext2_bmap, diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index ae94f6d949f5..fe2541d250e4 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1894,7 +1894,6 @@ static const struct address_space_operations ext3_ordered_aops = { .readpage = ext3_readpage, .readpages = ext3_readpages, .writepage = ext3_ordered_writepage, - .sync_page = block_sync_page, .write_begin = ext3_write_begin, .write_end = ext3_ordered_write_end, .bmap = ext3_bmap, @@ -1910,7 +1909,6 @@ static const struct address_space_operations ext3_writeback_aops = { .readpage = ext3_readpage, .readpages = ext3_readpages, .writepage = ext3_writeback_writepage, - .sync_page = block_sync_page, .write_begin = ext3_write_begin, .write_end = ext3_writeback_write_end, .bmap = ext3_bmap, @@ -1926,7 +1924,6 @@ static const struct address_space_operations ext3_journalled_aops = { .readpage = ext3_readpage, .readpages = ext3_readpages, .writepage = ext3_journalled_writepage, - .sync_page = block_sync_page, .write_begin = ext3_write_begin, .write_end = ext3_journalled_write_end, .set_page_dirty = ext3_journalled_set_page_dirty, diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 9f7f9e49914f..9297ad46c465 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3903,7 +3903,6 @@ static const struct address_space_operations ext4_ordered_aops = { .readpage = ext4_readpage, .readpages = ext4_readpages, .writepage = ext4_writepage, - .sync_page = block_sync_page, .write_begin = ext4_write_begin, .write_end = ext4_ordered_write_end, .bmap = ext4_bmap, @@ -3919,7 +3918,6 @@ static const struct address_space_operations ext4_writeback_aops = { .readpage = ext4_readpage, .readpages = ext4_readpages, .writepage = ext4_writepage, - .sync_page = block_sync_page, .write_begin = ext4_write_begin, .write_end = ext4_writeback_write_end, .bmap = ext4_bmap, @@ -3935,7 +3933,6 @@ static const struct address_space_operations ext4_journalled_aops = { .readpage = ext4_readpage, .readpages = ext4_readpages, .writepage = ext4_writepage, - .sync_page = block_sync_page, .write_begin = ext4_write_begin, .write_end = ext4_journalled_write_end, .set_page_dirty = ext4_journalled_set_page_dirty, @@ -3951,7 +3948,6 @@ static const struct address_space_operations ext4_da_aops = { .readpages = ext4_readpages, .writepage = ext4_writepage, .writepages = ext4_da_writepages, - .sync_page = block_sync_page, .write_begin = ext4_da_write_begin, .write_end = ext4_da_write_end, .bmap = ext4_bmap, diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 86753fe10bd1..f4ff09fb79b1 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -236,7 +236,6 @@ static const struct address_space_operations fat_aops = { .readpages = fat_readpages, .writepage = fat_writepage, .writepages = fat_writepages, - .sync_page = block_sync_page, .write_begin = fat_write_begin, .write_end = fat_write_end, .direct_IO = fat_direct_IO, diff --git a/fs/freevxfs/vxfs_subr.c b/fs/freevxfs/vxfs_subr.c index 1429f3ae1e86..5d318c44f855 100644 --- a/fs/freevxfs/vxfs_subr.c +++ b/fs/freevxfs/vxfs_subr.c @@ -44,7 +44,6 @@ static sector_t vxfs_bmap(struct address_space *, sector_t); const struct address_space_operations vxfs_aops = { .readpage = vxfs_readpage, .bmap = vxfs_bmap, - .sync_page = block_sync_page, }; inline void diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 9e3f68cc1bd1..09e8d51eeb64 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -868,7 +868,6 @@ static int fuse_bdi_init(struct fuse_conn *fc, struct super_block *sb) fc->bdi.name = "fuse"; fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; - fc->bdi.unplug_io_fn = default_unplug_io_fn; /* fuse does it's own writeback accounting */ fc->bdi.capabilities = BDI_CAP_NO_ACCT_WB; diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 4f36f8832b9b..2f87ad27efd0 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -1116,7 +1116,6 @@ static const struct address_space_operations gfs2_writeback_aops = { .writepages = gfs2_writeback_writepages, .readpage = gfs2_readpage, .readpages = gfs2_readpages, - .sync_page = block_sync_page, .write_begin = gfs2_write_begin, .write_end = gfs2_write_end, .bmap = gfs2_bmap, @@ -1132,7 +1131,6 @@ static const struct address_space_operations gfs2_ordered_aops = { .writepage = gfs2_ordered_writepage, .readpage = gfs2_readpage, .readpages = gfs2_readpages, - .sync_page = block_sync_page, .write_begin = gfs2_write_begin, .write_end = gfs2_write_end, .set_page_dirty = gfs2_set_page_dirty, @@ -1150,7 +1148,6 @@ static const struct address_space_operations gfs2_jdata_aops = { .writepages = gfs2_jdata_writepages, .readpage = gfs2_readpage, .readpages = gfs2_readpages, - .sync_page = block_sync_page, .write_begin = gfs2_write_begin, .write_end = gfs2_write_end, .set_page_dirty = gfs2_set_page_dirty, diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c index 939739c7b3f9..a566331db4e1 100644 --- a/fs/gfs2/meta_io.c +++ b/fs/gfs2/meta_io.c @@ -94,7 +94,6 @@ static int gfs2_aspace_writepage(struct page *page, struct writeback_control *wb const struct address_space_operations gfs2_meta_aops = { .writepage = gfs2_aspace_writepage, .releasepage = gfs2_releasepage, - .sync_page = block_sync_page, }; /** diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index dffb4e996643..fff16c968e67 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -150,7 +150,6 @@ static int hfs_writepages(struct address_space *mapping, const struct address_space_operations hfs_btree_aops = { .readpage = hfs_readpage, .writepage = hfs_writepage, - .sync_page = block_sync_page, .write_begin = hfs_write_begin, .write_end = generic_write_end, .bmap = hfs_bmap, @@ -160,7 +159,6 @@ const struct address_space_operations hfs_btree_aops = { const struct address_space_operations hfs_aops = { .readpage = hfs_readpage, .writepage = hfs_writepage, - .sync_page = block_sync_page, .write_begin = hfs_write_begin, .write_end = generic_write_end, .bmap = hfs_bmap, diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index a8df651747f0..b248a6cfcad9 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -146,7 +146,6 @@ static int hfsplus_writepages(struct address_space *mapping, const struct address_space_operations hfsplus_btree_aops = { .readpage = hfsplus_readpage, .writepage = hfsplus_writepage, - .sync_page = block_sync_page, .write_begin = hfsplus_write_begin, .write_end = generic_write_end, .bmap = hfsplus_bmap, @@ -156,7 +155,6 @@ const struct address_space_operations hfsplus_btree_aops = { const struct address_space_operations hfsplus_aops = { .readpage = hfsplus_readpage, .writepage = hfsplus_writepage, - .sync_page = block_sync_page, .write_begin = hfsplus_write_begin, .write_end = generic_write_end, .bmap = hfsplus_bmap, diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index c0340887c7ea..9e84257b3ad5 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -120,7 +120,6 @@ static sector_t _hpfs_bmap(struct address_space *mapping, sector_t block) const struct address_space_operations hpfs_aops = { .readpage = hpfs_readpage, .writepage = hpfs_writepage, - .sync_page = block_sync_page, .write_begin = hpfs_write_begin, .write_end = generic_write_end, .bmap = _hpfs_bmap diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index a0f3833c0dbf..3db5ba4568fc 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -1158,7 +1158,6 @@ static sector_t _isofs_bmap(struct address_space *mapping, sector_t block) static const struct address_space_operations isofs_aops = { .readpage = isofs_readpage, - .sync_page = block_sync_page, .bmap = _isofs_bmap }; diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index 9978803ceedc..eddbb373209e 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -352,7 +352,6 @@ const struct address_space_operations jfs_aops = { .readpages = jfs_readpages, .writepage = jfs_writepage, .writepages = jfs_writepages, - .sync_page = block_sync_page, .write_begin = jfs_write_begin, .write_end = nobh_write_end, .bmap = jfs_bmap, diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 48b44bd8267b..6740d34cd82b 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -583,7 +583,6 @@ static void metapage_invalidatepage(struct page *page, unsigned long offset) const struct address_space_operations jfs_metapage_aops = { .readpage = metapage_readpage, .writepage = metapage_writepage, - .sync_page = block_sync_page, .releasepage = metapage_releasepage, .invalidatepage = metapage_invalidatepage, .set_page_dirty = __set_page_dirty_nobuffers, diff --git a/fs/logfs/dev_bdev.c b/fs/logfs/dev_bdev.c index 723bc5bca09a..1adc8d455f0e 100644 --- a/fs/logfs/dev_bdev.c +++ b/fs/logfs/dev_bdev.c @@ -39,7 +39,6 @@ static int sync_request(struct page *page, struct block_device *bdev, int rw) bio.bi_end_io = request_complete; submit_bio(rw, &bio); - generic_unplug_device(bdev_get_queue(bdev)); wait_for_completion(&complete); return test_bit(BIO_UPTODATE, &bio.bi_flags) ? 0 : -EIO; } @@ -168,7 +167,6 @@ static void bdev_writeseg(struct super_block *sb, u64 ofs, size_t len) } len = PAGE_ALIGN(len); __bdev_writeseg(sb, ofs, ofs >> PAGE_SHIFT, len >> PAGE_SHIFT); - generic_unplug_device(bdev_get_queue(logfs_super(sb)->s_bdev)); } diff --git a/fs/minix/inode.c b/fs/minix/inode.c index ae0b83f476a6..adcdc0a4e182 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -399,7 +399,6 @@ static sector_t minix_bmap(struct address_space *mapping, sector_t block) static const struct address_space_operations minix_aops = { .readpage = minix_readpage, .writepage = minix_writepage, - .sync_page = block_sync_page, .write_begin = minix_write_begin, .write_end = generic_write_end, .bmap = minix_bmap diff --git a/fs/nilfs2/btnode.c b/fs/nilfs2/btnode.c index 388e9e8f5286..f4f1c08807ed 100644 --- a/fs/nilfs2/btnode.c +++ b/fs/nilfs2/btnode.c @@ -40,14 +40,10 @@ void nilfs_btnode_cache_init_once(struct address_space *btnc) nilfs_mapping_init_once(btnc); } -static const struct address_space_operations def_btnode_aops = { - .sync_page = block_sync_page, -}; - void nilfs_btnode_cache_init(struct address_space *btnc, struct backing_dev_info *bdi) { - nilfs_mapping_init(btnc, bdi, &def_btnode_aops); + nilfs_mapping_init(btnc, bdi); } void nilfs_btnode_cache_clear(struct address_space *btnc) diff --git a/fs/nilfs2/gcinode.c b/fs/nilfs2/gcinode.c index caf9a6a3fb54..1c2a3e23f8b2 100644 --- a/fs/nilfs2/gcinode.c +++ b/fs/nilfs2/gcinode.c @@ -49,7 +49,6 @@ #include "ifile.h" static const struct address_space_operations def_gcinode_aops = { - .sync_page = block_sync_page, }; /* diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 2fd440d8d6b8..c89d5d1ea7c7 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -262,7 +262,6 @@ nilfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, const struct address_space_operations nilfs_aops = { .writepage = nilfs_writepage, .readpage = nilfs_readpage, - .sync_page = block_sync_page, .writepages = nilfs_writepages, .set_page_dirty = nilfs_set_page_dirty, .readpages = nilfs_readpages, diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c index 6a0e2a189f60..3fdb61d79c9a 100644 --- a/fs/nilfs2/mdt.c +++ b/fs/nilfs2/mdt.c @@ -399,7 +399,6 @@ nilfs_mdt_write_page(struct page *page, struct writeback_control *wbc) static const struct address_space_operations def_mdt_aops = { .writepage = nilfs_mdt_write_page, - .sync_page = block_sync_page, }; static const struct inode_operations def_mdt_iops; @@ -438,10 +437,6 @@ void nilfs_mdt_set_entry_size(struct inode *inode, unsigned entry_size, mi->mi_first_entry_offset = DIV_ROUND_UP(header_size, entry_size); } -static const struct address_space_operations shadow_map_aops = { - .sync_page = block_sync_page, -}; - /** * nilfs_mdt_setup_shadow_map - setup shadow map and bind it to metadata file * @inode: inode of the metadata file @@ -455,9 +450,9 @@ int nilfs_mdt_setup_shadow_map(struct inode *inode, INIT_LIST_HEAD(&shadow->frozen_buffers); nilfs_mapping_init_once(&shadow->frozen_data); - nilfs_mapping_init(&shadow->frozen_data, bdi, &shadow_map_aops); + nilfs_mapping_init(&shadow->frozen_data, bdi); nilfs_mapping_init_once(&shadow->frozen_btnodes); - nilfs_mapping_init(&shadow->frozen_btnodes, bdi, &shadow_map_aops); + nilfs_mapping_init(&shadow->frozen_btnodes, bdi); mi->mi_shadow = shadow; return 0; } diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c index 0c432416cfef..3da37cc5de34 100644 --- a/fs/nilfs2/page.c +++ b/fs/nilfs2/page.c @@ -506,15 +506,14 @@ void nilfs_mapping_init_once(struct address_space *mapping) } void nilfs_mapping_init(struct address_space *mapping, - struct backing_dev_info *bdi, - const struct address_space_operations *aops) + struct backing_dev_info *bdi) { mapping->host = NULL; mapping->flags = 0; mapping_set_gfp_mask(mapping, GFP_NOFS); mapping->assoc_mapping = NULL; mapping->backing_dev_info = bdi; - mapping->a_ops = aops; + mapping->a_ops = NULL; } /* diff --git a/fs/nilfs2/page.h b/fs/nilfs2/page.h index 622df27cd891..ba4d6fd40b04 100644 --- a/fs/nilfs2/page.h +++ b/fs/nilfs2/page.h @@ -63,8 +63,7 @@ void nilfs_copy_back_pages(struct address_space *, struct address_space *); void nilfs_clear_dirty_pages(struct address_space *); void nilfs_mapping_init_once(struct address_space *mapping); void nilfs_mapping_init(struct address_space *mapping, - struct backing_dev_info *bdi, - const struct address_space_operations *aops); + struct backing_dev_info *bdi); unsigned nilfs_page_count_clean_buffers(struct page *, unsigned, unsigned); unsigned long nilfs_find_uncommitted_extent(struct inode *inode, sector_t start_blk, diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index c3c2c7ac9020..0b1e885b8cf8 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -1543,8 +1543,6 @@ err_out: */ const struct address_space_operations ntfs_aops = { .readpage = ntfs_readpage, /* Fill page with data. */ - .sync_page = block_sync_page, /* Currently, just unplugs the - disk request queue. */ #ifdef NTFS_RW .writepage = ntfs_writepage, /* Write dirty page to disk. */ #endif /* NTFS_RW */ @@ -1560,8 +1558,6 @@ const struct address_space_operations ntfs_aops = { */ const struct address_space_operations ntfs_mst_aops = { .readpage = ntfs_readpage, /* Fill page with data. */ - .sync_page = block_sync_page, /* Currently, just unplugs the - disk request queue. */ #ifdef NTFS_RW .writepage = ntfs_writepage, /* Write dirty page to disk. */ .set_page_dirty = __set_page_dirty_nobuffers, /* Set the page dirty diff --git a/fs/ntfs/compress.c b/fs/ntfs/compress.c index 6551c7cbad92..ef9ed854255c 100644 --- a/fs/ntfs/compress.c +++ b/fs/ntfs/compress.c @@ -698,8 +698,7 @@ lock_retry_remap: "uptodate! Unplugging the disk queue " "and rescheduling."); get_bh(tbh); - blk_run_address_space(mapping); - schedule(); + io_schedule(); put_bh(tbh); if (unlikely(!buffer_uptodate(tbh))) goto read_err; diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 1fbb0e20131b..daea0359e974 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -2043,7 +2043,6 @@ const struct address_space_operations ocfs2_aops = { .write_begin = ocfs2_write_begin, .write_end = ocfs2_write_end, .bmap = ocfs2_bmap, - .sync_page = block_sync_page, .direct_IO = ocfs2_direct_IO, .invalidatepage = ocfs2_invalidatepage, .releasepage = ocfs2_releasepage, diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index b108e863d8f6..1adab287bd24 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -367,11 +367,7 @@ static inline void o2hb_bio_wait_dec(struct o2hb_bio_wait_ctxt *wc, static void o2hb_wait_on_io(struct o2hb_region *reg, struct o2hb_bio_wait_ctxt *wc) { - struct address_space *mapping = reg->hr_bdev->bd_inode->i_mapping; - - blk_run_address_space(mapping); o2hb_bio_wait_dec(wc, 1); - wait_for_completion(&wc->wc_io_complete); } diff --git a/fs/omfs/file.c b/fs/omfs/file.c index 8a6d34fa668a..d738a7e493dd 100644 --- a/fs/omfs/file.c +++ b/fs/omfs/file.c @@ -372,7 +372,6 @@ const struct address_space_operations omfs_aops = { .readpages = omfs_readpages, .writepage = omfs_writepage, .writepages = omfs_writepages, - .sync_page = block_sync_page, .write_begin = omfs_write_begin, .write_end = generic_write_end, .bmap = omfs_bmap, diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index e63b4171d583..2b0646613f5a 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -335,7 +335,6 @@ static sector_t qnx4_bmap(struct address_space *mapping, sector_t block) static const struct address_space_operations qnx4_aops = { .readpage = qnx4_readpage, .writepage = qnx4_writepage, - .sync_page = block_sync_page, .write_begin = qnx4_write_begin, .write_end = generic_write_end, .bmap = qnx4_bmap diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 0bae036831e2..03674675f886 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -3212,7 +3212,6 @@ const struct address_space_operations reiserfs_address_space_operations = { .readpages = reiserfs_readpages, .releasepage = reiserfs_releasepage, .invalidatepage = reiserfs_invalidatepage, - .sync_page = block_sync_page, .write_begin = reiserfs_write_begin, .write_end = reiserfs_write_end, .bmap = reiserfs_aop_bmap, diff --git a/fs/sysv/itree.c b/fs/sysv/itree.c index 9ca66276315e..fa8d43c92bb8 100644 --- a/fs/sysv/itree.c +++ b/fs/sysv/itree.c @@ -488,7 +488,6 @@ static sector_t sysv_bmap(struct address_space *mapping, sector_t block) const struct address_space_operations sysv_aops = { .readpage = sysv_readpage, .writepage = sysv_writepage, - .sync_page = block_sync_page, .write_begin = sysv_write_begin, .write_end = generic_write_end, .bmap = sysv_bmap diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 6e11c2975dcf..81368d4d4a2c 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1979,7 +1979,6 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent) */ c->bdi.name = "ubifs", c->bdi.capabilities = BDI_CAP_MAP_COPY; - c->bdi.unplug_io_fn = default_unplug_io_fn; err = bdi_init(&c->bdi); if (err) goto out_close; diff --git a/fs/udf/file.c b/fs/udf/file.c index 89c78486cbbe..94e4553491c9 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -98,7 +98,6 @@ static int udf_adinicb_write_end(struct file *file, const struct address_space_operations udf_adinicb_aops = { .readpage = udf_adinicb_readpage, .writepage = udf_adinicb_writepage, - .sync_page = block_sync_page, .write_begin = simple_write_begin, .write_end = udf_adinicb_write_end, }; diff --git a/fs/udf/inode.c b/fs/udf/inode.c index c6a2e782b97b..fa96fc0fe12b 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -133,7 +133,6 @@ static sector_t udf_bmap(struct address_space *mapping, sector_t block) const struct address_space_operations udf_aops = { .readpage = udf_readpage, .writepage = udf_writepage, - .sync_page = block_sync_page, .write_begin = udf_write_begin, .write_end = generic_write_end, .bmap = udf_bmap, diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 2b251f2093af..83b28444eb17 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -588,7 +588,6 @@ static sector_t ufs_bmap(struct address_space *mapping, sector_t block) const struct address_space_operations ufs_aops = { .readpage = ufs_readpage, .writepage = ufs_writepage, - .sync_page = block_sync_page, .write_begin = ufs_write_begin, .write_end = generic_write_end, .bmap = ufs_bmap diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c index a58f9155fc9a..ff0e79276f2d 100644 --- a/fs/ufs/truncate.c +++ b/fs/ufs/truncate.c @@ -481,7 +481,7 @@ int ufs_truncate(struct inode *inode, loff_t old_i_size) break; if (IS_SYNC(inode) && (inode->i_state & I_DIRTY)) ufs_sync_inode (inode); - blk_run_address_space(inode->i_mapping); + blk_flush_plug(current); yield(); } diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index ec7bbb5645b6..83c1c20d145a 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1495,7 +1495,6 @@ const struct address_space_operations xfs_address_space_operations = { .readpages = xfs_vm_readpages, .writepage = xfs_vm_writepage, .writepages = xfs_vm_writepages, - .sync_page = block_sync_page, .releasepage = xfs_vm_releasepage, .invalidatepage = xfs_vm_invalidatepage, .write_begin = xfs_vm_write_begin, diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index ac1c7e8378dd..4f8f53c4d42c 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -991,7 +991,7 @@ xfs_buf_lock( if (atomic_read(&bp->b_pin_count) && (bp->b_flags & XBF_STALE)) xfs_log_force(bp->b_target->bt_mount, 0); if (atomic_read(&bp->b_io_remaining)) - blk_run_address_space(bp->b_target->bt_mapping); + blk_flush_plug(current); down(&bp->b_sema); XB_SET_OWNER(bp); @@ -1035,9 +1035,7 @@ xfs_buf_wait_unpin( set_current_state(TASK_UNINTERRUPTIBLE); if (atomic_read(&bp->b_pin_count) == 0) break; - if (atomic_read(&bp->b_io_remaining)) - blk_run_address_space(bp->b_target->bt_mapping); - schedule(); + io_schedule(); } remove_wait_queue(&bp->b_waiters, &wait); set_current_state(TASK_RUNNING); @@ -1443,7 +1441,7 @@ xfs_buf_iowait( trace_xfs_buf_iowait(bp, _RET_IP_); if (atomic_read(&bp->b_io_remaining)) - blk_run_address_space(bp->b_target->bt_mapping); + blk_flush_plug(current); wait_for_completion(&bp->b_iowait); trace_xfs_buf_iowait_done(bp, _RET_IP_); @@ -1667,7 +1665,6 @@ xfs_mapping_buftarg( struct inode *inode; struct address_space *mapping; static const struct address_space_operations mapping_aops = { - .sync_page = block_sync_page, .migratepage = fail_migrate_page, }; @@ -1948,7 +1945,7 @@ xfsbufd( count++; } if (count) - blk_run_address_space(target->bt_mapping); + blk_flush_plug(current); } while (!kthread_should_stop()); @@ -1996,7 +1993,7 @@ xfs_flush_buftarg( if (wait) { /* Expedite and wait for IO to complete. */ - blk_run_address_space(target->bt_mapping); + blk_flush_plug(current); while (!list_empty(&wait_list)) { bp = list_first_entry(&wait_list, struct xfs_buf, b_list); diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 4ce34fa937d4..96f4094b706d 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -66,8 +66,6 @@ struct backing_dev_info { unsigned int capabilities; /* Device capabilities */ congested_fn *congested_fn; /* Function pointer if device is md/dm */ void *congested_data; /* Pointer to aux data for congested func */ - void (*unplug_io_fn)(struct backing_dev_info *, struct page *); - void *unplug_io_data; char *name; @@ -251,7 +249,6 @@ int bdi_set_max_ratio(struct backing_dev_info *bdi, unsigned int max_ratio); extern struct backing_dev_info default_backing_dev_info; extern struct backing_dev_info noop_backing_dev_info; -void default_unplug_io_fn(struct backing_dev_info *bdi, struct page *page); int writeback_in_progress(struct backing_dev_info *bdi); @@ -336,17 +333,4 @@ static inline int bdi_sched_wait(void *word) return 0; } -static inline void blk_run_backing_dev(struct backing_dev_info *bdi, - struct page *page) -{ - if (bdi && bdi->unplug_io_fn) - bdi->unplug_io_fn(bdi, page); -} - -static inline void blk_run_address_space(struct address_space *mapping) -{ - if (mapping) - blk_run_backing_dev(mapping->backing_dev_info, NULL); -} - #endif /* _LINUX_BACKING_DEV_H */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 5873037eeb91..64ab2a1bb167 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -196,7 +196,6 @@ typedef void (request_fn_proc) (struct request_queue *q); typedef int (make_request_fn) (struct request_queue *q, struct bio *bio); typedef int (prep_rq_fn) (struct request_queue *, struct request *); typedef void (unprep_rq_fn) (struct request_queue *, struct request *); -typedef void (unplug_fn) (struct request_queue *); struct bio_vec; struct bvec_merge_data { @@ -279,7 +278,6 @@ struct request_queue make_request_fn *make_request_fn; prep_rq_fn *prep_rq_fn; unprep_rq_fn *unprep_rq_fn; - unplug_fn *unplug_fn; merge_bvec_fn *merge_bvec_fn; softirq_done_fn *softirq_done_fn; rq_timed_out_fn *rq_timed_out_fn; @@ -292,14 +290,6 @@ struct request_queue sector_t end_sector; struct request *boundary_rq; - /* - * Auto-unplugging state - */ - struct timer_list unplug_timer; - int unplug_thresh; /* After this many requests */ - unsigned long unplug_delay; /* After this many jiffies */ - struct work_struct unplug_work; - /* * Delayed queue handling */ @@ -399,14 +389,13 @@ struct request_queue #define QUEUE_FLAG_ASYNCFULL 4 /* write queue has been filled */ #define QUEUE_FLAG_DEAD 5 /* queue being torn down */ #define QUEUE_FLAG_REENTER 6 /* Re-entrancy avoidance */ -#define QUEUE_FLAG_PLUGGED 7 /* queue is plugged */ -#define QUEUE_FLAG_ELVSWITCH 8 /* don't use elevator, just do FIFO */ -#define QUEUE_FLAG_BIDI 9 /* queue supports bidi requests */ -#define QUEUE_FLAG_NOMERGES 10 /* disable merge attempts */ -#define QUEUE_FLAG_SAME_COMP 11 /* force complete on same CPU */ -#define QUEUE_FLAG_FAIL_IO 12 /* fake timeout */ -#define QUEUE_FLAG_STACKABLE 13 /* supports request stacking */ -#define QUEUE_FLAG_NONROT 14 /* non-rotational device (SSD) */ +#define QUEUE_FLAG_ELVSWITCH 7 /* don't use elevator, just do FIFO */ +#define QUEUE_FLAG_BIDI 8 /* queue supports bidi requests */ +#define QUEUE_FLAG_NOMERGES 9 /* disable merge attempts */ +#define QUEUE_FLAG_SAME_COMP 10 /* force complete on same CPU */ +#define QUEUE_FLAG_FAIL_IO 11 /* fake timeout */ +#define QUEUE_FLAG_STACKABLE 12 /* supports request stacking */ +#define QUEUE_FLAG_NONROT 13 /* non-rotational device (SSD) */ #define QUEUE_FLAG_VIRT QUEUE_FLAG_NONROT /* paravirt device */ #define QUEUE_FLAG_IO_STAT 15 /* do IO stats */ #define QUEUE_FLAG_DISCARD 16 /* supports DISCARD */ @@ -484,7 +473,6 @@ static inline void queue_flag_clear(unsigned int flag, struct request_queue *q) __clear_bit(flag, &q->queue_flags); } -#define blk_queue_plugged(q) test_bit(QUEUE_FLAG_PLUGGED, &(q)->queue_flags) #define blk_queue_tagged(q) test_bit(QUEUE_FLAG_QUEUED, &(q)->queue_flags) #define blk_queue_stopped(q) test_bit(QUEUE_FLAG_STOPPED, &(q)->queue_flags) #define blk_queue_nomerges(q) test_bit(QUEUE_FLAG_NOMERGES, &(q)->queue_flags) @@ -679,9 +667,6 @@ extern int blk_rq_prep_clone(struct request *rq, struct request *rq_src, extern void blk_rq_unprep_clone(struct request *rq); extern int blk_insert_cloned_request(struct request_queue *q, struct request *rq); -extern void blk_plug_device(struct request_queue *); -extern void blk_plug_device_unlocked(struct request_queue *); -extern int blk_remove_plug(struct request_queue *); extern void blk_delay_queue(struct request_queue *, unsigned long); extern void blk_recount_segments(struct request_queue *, struct bio *); extern int scsi_cmd_ioctl(struct request_queue *, struct gendisk *, fmode_t, @@ -726,7 +711,6 @@ extern int blk_execute_rq(struct request_queue *, struct gendisk *, struct request *, int); extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *, struct request *, int, rq_end_io_fn *); -extern void blk_unplug(struct request_queue *q); static inline struct request_queue *bdev_get_queue(struct block_device *bdev) { @@ -863,7 +847,6 @@ extern struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bd extern int blk_rq_map_sg(struct request_queue *, struct request *, struct scatterlist *); extern void blk_dump_rq_flags(struct request *, char *); -extern void generic_unplug_device(struct request_queue *); extern long nr_blockdev_pages(void); int blk_get_queue(struct request_queue *); diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 68d1fe7b877c..f5df23561b96 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -219,7 +219,6 @@ int generic_cont_expand_simple(struct inode *inode, loff_t size); int block_commit_write(struct page *page, unsigned from, unsigned to); int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, get_block_t get_block); -void block_sync_page(struct page *); sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *); int block_truncate_page(struct address_space *, loff_t, get_block_t *); int nobh_write_begin(struct address_space *, loff_t, unsigned, unsigned, diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 272496d1fae4..e2768834f397 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -285,11 +285,6 @@ void dm_table_add_target_callbacks(struct dm_table *t, struct dm_target_callback */ int dm_table_complete(struct dm_table *t); -/* - * Unplug all devices in a table. - */ -void dm_table_unplug_all(struct dm_table *t); - /* * Table reference counting. */ diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 8857cf9adbb7..ec6f72b84477 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -20,7 +20,6 @@ typedef void (elevator_bio_merged_fn) (struct request_queue *, typedef int (elevator_dispatch_fn) (struct request_queue *, int); typedef void (elevator_add_req_fn) (struct request_queue *, struct request *); -typedef int (elevator_queue_empty_fn) (struct request_queue *); typedef struct request *(elevator_request_list_fn) (struct request_queue *, struct request *); typedef void (elevator_completed_req_fn) (struct request_queue *, struct request *); typedef int (elevator_may_queue_fn) (struct request_queue *, int); @@ -46,7 +45,6 @@ struct elevator_ops elevator_activate_req_fn *elevator_activate_req_fn; elevator_deactivate_req_fn *elevator_deactivate_req_fn; - elevator_queue_empty_fn *elevator_queue_empty_fn; elevator_completed_req_fn *elevator_completed_req_fn; elevator_request_list_fn *elevator_former_req_fn; @@ -101,8 +99,8 @@ struct elevator_queue */ extern void elv_dispatch_sort(struct request_queue *, struct request *); extern void elv_dispatch_add_tail(struct request_queue *, struct request *); -extern void elv_add_request(struct request_queue *, struct request *, int, int); -extern void __elv_add_request(struct request_queue *, struct request *, int, int); +extern void elv_add_request(struct request_queue *, struct request *, int); +extern void __elv_add_request(struct request_queue *, struct request *, int); extern void elv_insert(struct request_queue *, struct request *, int); extern int elv_merge(struct request_queue *, struct request **, struct bio *); extern int elv_try_merge(struct request *, struct bio *); @@ -112,7 +110,6 @@ extern void elv_merged_request(struct request_queue *, struct request *, int); extern void elv_bio_merged(struct request_queue *q, struct request *, struct bio *); extern void elv_requeue_request(struct request_queue *, struct request *); -extern int elv_queue_empty(struct request_queue *); extern struct request *elv_former_request(struct request_queue *, struct request *); extern struct request *elv_latter_request(struct request_queue *, struct request *); extern int elv_register_queue(struct request_queue *q); diff --git a/include/linux/fs.h b/include/linux/fs.h index bd3215940c37..9f2cf69911b8 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -583,7 +583,6 @@ typedef int (*read_actor_t)(read_descriptor_t *, struct page *, struct address_space_operations { int (*writepage)(struct page *page, struct writeback_control *wbc); int (*readpage)(struct file *, struct page *); - void (*sync_page)(struct page *); /* Write back some dirty pages from this mapping. */ int (*writepages)(struct address_space *, struct writeback_control *); diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 9c66e994540f..e112b8db2f3c 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -298,7 +298,6 @@ static inline pgoff_t linear_page_index(struct vm_area_struct *vma, extern void __lock_page(struct page *page); extern int __lock_page_killable(struct page *page); -extern void __lock_page_nosync(struct page *page); extern int __lock_page_or_retry(struct page *page, struct mm_struct *mm, unsigned int flags); extern void unlock_page(struct page *page); @@ -341,17 +340,6 @@ static inline int lock_page_killable(struct page *page) return 0; } -/* - * lock_page_nosync should only be used if we can't pin the page's inode. - * Doesn't play quite so well with block device plugging. - */ -static inline void lock_page_nosync(struct page *page) -{ - might_sleep(); - if (!trylock_page(page)) - __lock_page_nosync(page); -} - /* * lock_page_or_retry - Lock the page, unless this would block and the * caller indicated that it can handle a retry. diff --git a/include/linux/swap.h b/include/linux/swap.h index 4d559325d919..9ee321833b21 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -299,8 +299,6 @@ extern void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t pgoff, struct page **pagep, swp_entry_t *ent); #endif -extern void swap_unplug_io_fn(struct backing_dev_info *, struct page *); - #ifdef CONFIG_SWAP /* linux/mm/page_io.c */ extern int swap_readpage(struct page *); diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 027100d30227..c91e139a652e 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -14,17 +14,11 @@ static atomic_long_t bdi_seq = ATOMIC_LONG_INIT(0); -void default_unplug_io_fn(struct backing_dev_info *bdi, struct page *page) -{ -} -EXPORT_SYMBOL(default_unplug_io_fn); - struct backing_dev_info default_backing_dev_info = { .name = "default", .ra_pages = VM_MAX_READAHEAD * 1024 / PAGE_CACHE_SIZE, .state = 0, .capabilities = BDI_CAP_MAP_COPY, - .unplug_io_fn = default_unplug_io_fn, }; EXPORT_SYMBOL_GPL(default_backing_dev_info); diff --git a/mm/filemap.c b/mm/filemap.c index 83a45d35468b..380776c2a9ac 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -155,45 +155,15 @@ void remove_from_page_cache(struct page *page) } EXPORT_SYMBOL(remove_from_page_cache); -static int sync_page(void *word) +static int sleep_on_page(void *word) { - struct address_space *mapping; - struct page *page; - - page = container_of((unsigned long *)word, struct page, flags); - - /* - * page_mapping() is being called without PG_locked held. - * Some knowledge of the state and use of the page is used to - * reduce the requirements down to a memory barrier. - * The danger here is of a stale page_mapping() return value - * indicating a struct address_space different from the one it's - * associated with when it is associated with one. - * After smp_mb(), it's either the correct page_mapping() for - * the page, or an old page_mapping() and the page's own - * page_mapping() has gone NULL. - * The ->sync_page() address_space operation must tolerate - * page_mapping() going NULL. By an amazing coincidence, - * this comes about because none of the users of the page - * in the ->sync_page() methods make essential use of the - * page_mapping(), merely passing the page down to the backing - * device's unplug functions when it's non-NULL, which in turn - * ignore it for all cases but swap, where only page_private(page) is - * of interest. When page_mapping() does go NULL, the entire - * call stack gracefully ignores the page and returns. - * -- wli - */ - smp_mb(); - mapping = page_mapping(page); - if (mapping && mapping->a_ops && mapping->a_ops->sync_page) - mapping->a_ops->sync_page(page); io_schedule(); return 0; } -static int sync_page_killable(void *word) +static int sleep_on_page_killable(void *word) { - sync_page(word); + sleep_on_page(word); return fatal_signal_pending(current) ? -EINTR : 0; } @@ -479,12 +449,6 @@ struct page *__page_cache_alloc(gfp_t gfp) EXPORT_SYMBOL(__page_cache_alloc); #endif -static int __sleep_on_page_lock(void *word) -{ - io_schedule(); - return 0; -} - /* * In order to wait for pages to become available there must be * waitqueues associated with pages. By using a hash table of @@ -512,7 +476,7 @@ void wait_on_page_bit(struct page *page, int bit_nr) DEFINE_WAIT_BIT(wait, &page->flags, bit_nr); if (test_bit(bit_nr, &page->flags)) - __wait_on_bit(page_waitqueue(page), &wait, sync_page, + __wait_on_bit(page_waitqueue(page), &wait, sleep_on_page, TASK_UNINTERRUPTIBLE); } EXPORT_SYMBOL(wait_on_page_bit); @@ -576,17 +540,12 @@ EXPORT_SYMBOL(end_page_writeback); /** * __lock_page - get a lock on the page, assuming we need to sleep to get it * @page: the page to lock - * - * Ugly. Running sync_page() in state TASK_UNINTERRUPTIBLE is scary. If some - * random driver's requestfn sets TASK_RUNNING, we could busywait. However - * chances are that on the second loop, the block layer's plug list is empty, - * so sync_page() will then return in state TASK_UNINTERRUPTIBLE. */ void __lock_page(struct page *page) { DEFINE_WAIT_BIT(wait, &page->flags, PG_locked); - __wait_on_bit_lock(page_waitqueue(page), &wait, sync_page, + __wait_on_bit_lock(page_waitqueue(page), &wait, sleep_on_page, TASK_UNINTERRUPTIBLE); } EXPORT_SYMBOL(__lock_page); @@ -596,24 +555,10 @@ int __lock_page_killable(struct page *page) DEFINE_WAIT_BIT(wait, &page->flags, PG_locked); return __wait_on_bit_lock(page_waitqueue(page), &wait, - sync_page_killable, TASK_KILLABLE); + sleep_on_page_killable, TASK_KILLABLE); } EXPORT_SYMBOL_GPL(__lock_page_killable); -/** - * __lock_page_nosync - get a lock on the page, without calling sync_page() - * @page: the page to lock - * - * Variant of lock_page that does not require the caller to hold a reference - * on the page's mapping. - */ -void __lock_page_nosync(struct page *page) -{ - DEFINE_WAIT_BIT(wait, &page->flags, PG_locked); - __wait_on_bit_lock(page_waitqueue(page), &wait, __sleep_on_page_lock, - TASK_UNINTERRUPTIBLE); -} - int __lock_page_or_retry(struct page *page, struct mm_struct *mm, unsigned int flags) { diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 0207c2f6f8bd..bfba796d374d 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -945,7 +945,7 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn, collect_procs(ppage, &tokill); if (hpage != ppage) - lock_page_nosync(ppage); + lock_page(ppage); ret = try_to_unmap(ppage, ttu); if (ret != SWAP_SUCCESS) @@ -1038,7 +1038,7 @@ int __memory_failure(unsigned long pfn, int trapno, int flags) * Check "just unpoisoned", "filter hit", and * "race with other subpage." */ - lock_page_nosync(hpage); + lock_page(hpage); if (!PageHWPoison(hpage) || (hwpoison_filter(p) && TestClearPageHWPoison(p)) || (p != hpage && TestSetPageHWPoison(hpage))) { @@ -1088,7 +1088,7 @@ int __memory_failure(unsigned long pfn, int trapno, int flags) * It's very difficult to mess with pages currently under IO * and in many cases impossible, so we just avoid it here. */ - lock_page_nosync(hpage); + lock_page(hpage); /* * unpoison always clear PG_hwpoison inside page lock @@ -1231,7 +1231,7 @@ int unpoison_memory(unsigned long pfn) return 0; } - lock_page_nosync(page); + lock_page(page); /* * This test is racy because PG_hwpoison is set outside of page lock. * That's acceptable because that won't trigger kernel panic. Instead, diff --git a/mm/nommu.c b/mm/nommu.c index f59e1424d3db..fb6cbd6abe16 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -1842,10 +1842,6 @@ int remap_vmalloc_range(struct vm_area_struct *vma, void *addr, } EXPORT_SYMBOL(remap_vmalloc_range); -void swap_unplug_io_fn(struct backing_dev_info *bdi, struct page *page) -{ -} - unsigned long arch_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 2cb01f6ec5d0..cc0ede169e41 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -1239,7 +1239,7 @@ int set_page_dirty_lock(struct page *page) { int ret; - lock_page_nosync(page); + lock_page(page); ret = set_page_dirty(page); unlock_page(page); return ret; diff --git a/mm/readahead.c b/mm/readahead.c index 77506a291a2d..cbddc3e17246 100644 --- a/mm/readahead.c +++ b/mm/readahead.c @@ -554,17 +554,5 @@ page_cache_async_readahead(struct address_space *mapping, /* do read-ahead */ ondemand_readahead(mapping, ra, filp, true, offset, req_size); - -#ifdef CONFIG_BLOCK - /* - * Normally the current page is !uptodate and lock_page() will be - * immediately called to implicitly unplug the device. However this - * is not always true for RAID conifgurations, where data arrives - * not strictly in their submission order. In this case we need to - * explicitly kick off the IO. - */ - if (PageUptodate(page)) - blk_run_backing_dev(mapping->backing_dev_info, NULL); -#endif } EXPORT_SYMBOL_GPL(page_cache_async_readahead); diff --git a/mm/shmem.c b/mm/shmem.c index 5ee67c990602..24d23f5bedf1 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -224,7 +224,6 @@ static const struct vm_operations_struct shmem_vm_ops; static struct backing_dev_info shmem_backing_dev_info __read_mostly = { .ra_pages = 0, /* No readahead */ .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK | BDI_CAP_SWAP_BACKED, - .unplug_io_fn = default_unplug_io_fn, }; static LIST_HEAD(shmem_swaplist); diff --git a/mm/swap_state.c b/mm/swap_state.c index 5c8cfabbc9bc..46680461785b 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -24,12 +24,10 @@ /* * swapper_space is a fiction, retained to simplify the path through - * vmscan's shrink_page_list, to make sync_page look nicer, and to allow - * future use of radix_tree tags in the swap cache. + * vmscan's shrink_page_list. */ static const struct address_space_operations swap_aops = { .writepage = swap_writepage, - .sync_page = block_sync_page, .set_page_dirty = __set_page_dirty_nobuffers, .migratepage = migrate_page, }; @@ -37,7 +35,6 @@ static const struct address_space_operations swap_aops = { static struct backing_dev_info swap_backing_dev_info = { .name = "swap", .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK | BDI_CAP_SWAP_BACKED, - .unplug_io_fn = swap_unplug_io_fn, }; struct address_space swapper_space = { diff --git a/mm/swapfile.c b/mm/swapfile.c index 07a458d72fa8..7ceea78ceb20 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -94,39 +94,6 @@ __try_to_reclaim_swap(struct swap_info_struct *si, unsigned long offset) return ret; } -/* - * We need this because the bdev->unplug_fn can sleep and we cannot - * hold swap_lock while calling the unplug_fn. And swap_lock - * cannot be turned into a mutex. - */ -static DECLARE_RWSEM(swap_unplug_sem); - -void swap_unplug_io_fn(struct backing_dev_info *unused_bdi, struct page *page) -{ - swp_entry_t entry; - - down_read(&swap_unplug_sem); - entry.val = page_private(page); - if (PageSwapCache(page)) { - struct block_device *bdev = swap_info[swp_type(entry)]->bdev; - struct backing_dev_info *bdi; - - /* - * If the page is removed from swapcache from under us (with a - * racy try_to_unuse/swapoff) we need an additional reference - * count to avoid reading garbage from page_private(page) above. - * If the WARN_ON triggers during a swapoff it maybe the race - * condition and it's harmless. However if it triggers without - * swapoff it signals a problem. - */ - WARN_ON(page_count(page) <= 1); - - bdi = bdev->bd_inode->i_mapping->backing_dev_info; - blk_run_backing_dev(bdi, page); - } - up_read(&swap_unplug_sem); -} - /* * swapon tell device that all the old swap contents can be discarded, * to allow the swap device to optimize its wear-levelling. @@ -1643,10 +1610,6 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) goto out_dput; } - /* wait for any unplug function to finish */ - down_write(&swap_unplug_sem); - up_write(&swap_unplug_sem); - destroy_swap_extents(p); if (p->flags & SWP_CONTINUED) free_swap_count_continuations(p); diff --git a/mm/vmscan.c b/mm/vmscan.c index 17497d0cd8b9..251bed73ac03 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -358,7 +358,7 @@ static int may_write_to_queue(struct backing_dev_info *bdi, static void handle_write_error(struct address_space *mapping, struct page *page, int error) { - lock_page_nosync(page); + lock_page(page); if (page_mapping(page) == mapping) mapping_set_error(mapping, error); unlock_page(page); -- cgit v1.2.3 From 721a9602e6607417c6bc15b18e97a2f35266c690 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 9 Mar 2011 11:56:30 +0100 Subject: block: kill off REQ_UNPLUG With the plugging now being explicitly controlled by the submitter, callers need not pass down unplugging hints to the block layer. If they want to unplug, it's because they manually plugged on their own - in which case, they should just unplug at will. Signed-off-by: Jens Axboe --- block/blk-core.c | 2 +- drivers/block/drbd/drbd_actlog.c | 2 +- drivers/block/drbd/drbd_int.h | 2 +- drivers/block/drbd/drbd_main.c | 3 +-- drivers/block/drbd/drbd_receiver.c | 9 +-------- drivers/md/bitmap.c | 2 +- drivers/md/dm-io.c | 2 +- drivers/md/dm-kcopyd.c | 5 +---- drivers/md/md.c | 5 ++--- fs/btrfs/extent_io.c | 2 +- fs/buffer.c | 14 ++++---------- fs/direct-io.c | 2 +- fs/ext4/page-io.c | 3 +-- fs/gfs2/log.c | 4 ++-- fs/gfs2/lops.c | 12 ++++++------ fs/gfs2/meta_io.c | 2 +- fs/jbd/commit.c | 2 +- fs/jbd2/commit.c | 6 +++--- fs/nilfs2/segbuf.c | 2 +- fs/xfs/linux-2.6/xfs_aops.c | 3 +-- include/linux/blk_types.h | 2 -- include/linux/fs.h | 28 +++++++++------------------- kernel/power/block_io.c | 2 +- mm/page_io.c | 2 +- 24 files changed, 43 insertions(+), 75 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 82a45898ba76..7e9715ae18c8 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1290,7 +1290,7 @@ get_rq: } plug = current->plug; - if (plug && !sync) { + if (plug) { if (!plug->should_sort && !list_empty(&plug->list)) { struct request *__rq; diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c index 2096628d6e65..aca302492ff2 100644 --- a/drivers/block/drbd/drbd_actlog.c +++ b/drivers/block/drbd/drbd_actlog.c @@ -80,7 +80,7 @@ static int _drbd_md_sync_page_io(struct drbd_conf *mdev, if ((rw & WRITE) && !test_bit(MD_NO_FUA, &mdev->flags)) rw |= REQ_FUA; - rw |= REQ_UNPLUG | REQ_SYNC; + rw |= REQ_SYNC; bio = bio_alloc(GFP_NOIO, 1); bio->bi_bdev = bdev->md_bdev; diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index 0b5718e19586..b0bd27dfc1e8 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -377,7 +377,7 @@ union p_header { #define DP_HARDBARRIER 1 /* depricated */ #define DP_RW_SYNC 2 /* equals REQ_SYNC */ #define DP_MAY_SET_IN_SYNC 4 -#define DP_UNPLUG 8 /* equals REQ_UNPLUG */ +#define DP_UNPLUG 8 /* not used anymore */ #define DP_FUA 16 /* equals REQ_FUA */ #define DP_FLUSH 32 /* equals REQ_FLUSH */ #define DP_DISCARD 64 /* equals REQ_DISCARD */ diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 6049cb85310d..8a43ce0edeed 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -2477,12 +2477,11 @@ static u32 bio_flags_to_wire(struct drbd_conf *mdev, unsigned long bi_rw) { if (mdev->agreed_pro_version >= 95) return (bi_rw & REQ_SYNC ? DP_RW_SYNC : 0) | - (bi_rw & REQ_UNPLUG ? DP_UNPLUG : 0) | (bi_rw & REQ_FUA ? DP_FUA : 0) | (bi_rw & REQ_FLUSH ? DP_FLUSH : 0) | (bi_rw & REQ_DISCARD ? DP_DISCARD : 0); else - return bi_rw & (REQ_SYNC | REQ_UNPLUG) ? DP_RW_SYNC : 0; + return bi_rw & REQ_SYNC ? DP_RW_SYNC : 0; } /* Used to send write requests diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index 84132f8bf8a4..8e68be939deb 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -1100,8 +1100,6 @@ next_bio: /* > e->sector, unless this is the first bio */ bio->bi_sector = sector; bio->bi_bdev = mdev->ldev->backing_bdev; - /* we special case some flags in the multi-bio case, see below - * (REQ_UNPLUG) */ bio->bi_rw = rw; bio->bi_private = e; bio->bi_end_io = drbd_endio_sec; @@ -1130,10 +1128,6 @@ next_bio: bios = bios->bi_next; bio->bi_next = NULL; - /* strip off REQ_UNPLUG unless it is the last bio */ - if (bios) - bio->bi_rw &= ~REQ_UNPLUG; - drbd_generic_make_request(mdev, fault_type, bio); } while (bios); return 0; @@ -1621,12 +1615,11 @@ static unsigned long write_flags_to_bio(struct drbd_conf *mdev, u32 dpf) { if (mdev->agreed_pro_version >= 95) return (dpf & DP_RW_SYNC ? REQ_SYNC : 0) | - (dpf & DP_UNPLUG ? REQ_UNPLUG : 0) | (dpf & DP_FUA ? REQ_FUA : 0) | (dpf & DP_FLUSH ? REQ_FUA : 0) | (dpf & DP_DISCARD ? REQ_DISCARD : 0); else - return dpf & DP_RW_SYNC ? (REQ_SYNC | REQ_UNPLUG) : 0; + return dpf & DP_RW_SYNC ? REQ_SYNC : 0; } /* mirrored write */ diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index 54bfc274b39a..ca203cb23f3c 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -347,7 +347,7 @@ static void write_page(struct bitmap *bitmap, struct page *page, int wait) atomic_inc(&bitmap->pending_writes); set_buffer_locked(bh); set_buffer_mapped(bh); - submit_bh(WRITE | REQ_UNPLUG | REQ_SYNC, bh); + submit_bh(WRITE | REQ_SYNC, bh); bh = bh->b_this_page; } diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index 136d4f71a116..76a5af00a26b 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -352,7 +352,7 @@ static void dispatch_io(int rw, unsigned int num_regions, BUG_ON(num_regions > DM_IO_MAX_REGIONS); if (sync) - rw |= REQ_SYNC | REQ_UNPLUG; + rw |= REQ_SYNC; /* * For multiple regions we need to be careful to rewind diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c index 400cf35094a4..1bb73a13ca40 100644 --- a/drivers/md/dm-kcopyd.c +++ b/drivers/md/dm-kcopyd.c @@ -356,11 +356,8 @@ static int run_io_job(struct kcopyd_job *job) if (job->rw == READ) r = dm_io(&io_req, 1, &job->source, NULL); - else { - if (job->num_dests > 1) - io_req.bi_rw |= REQ_UNPLUG; + else r = dm_io(&io_req, job->num_dests, job->dests, NULL); - } return r; } diff --git a/drivers/md/md.c b/drivers/md/md.c index ca0d79c264b9..28f9c1ee4e3a 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -777,8 +777,7 @@ void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev, bio->bi_end_io = super_written; atomic_inc(&mddev->pending_writes); - submit_bio(REQ_WRITE | REQ_SYNC | REQ_UNPLUG | REQ_FLUSH | REQ_FUA, - bio); + submit_bio(REQ_WRITE | REQ_SYNC | REQ_FLUSH | REQ_FUA, bio); } void md_super_wait(mddev_t *mddev) @@ -806,7 +805,7 @@ int sync_page_io(mdk_rdev_t *rdev, sector_t sector, int size, struct completion event; int ret; - rw |= REQ_SYNC | REQ_UNPLUG; + rw |= REQ_SYNC; bio->bi_bdev = (metadata_op && rdev->meta_bdev) ? rdev->meta_bdev : rdev->bdev; diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 92ac5192c518..b76f7cd47401 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2182,7 +2182,7 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc, unsigned long nr_written = 0; if (wbc->sync_mode == WB_SYNC_ALL) - write_flags = WRITE_SYNC_PLUG; + write_flags = WRITE_SYNC; else write_flags = WRITE; diff --git a/fs/buffer.c b/fs/buffer.c index f903f2e5b4fe..42534f67d71b 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -767,7 +767,7 @@ static int fsync_buffers_list(spinlock_t *lock, struct list_head *list) * still in flight on potentially older * contents. */ - write_dirty_buffer(bh, WRITE_SYNC_PLUG); + write_dirty_buffer(bh, WRITE_SYNC); /* * Kick off IO for the previous mapping. Note @@ -1602,14 +1602,8 @@ EXPORT_SYMBOL(unmap_underlying_metadata); * prevents this contention from occurring. * * If block_write_full_page() is called with wbc->sync_mode == - * WB_SYNC_ALL, the writes are posted using WRITE_SYNC_PLUG; this - * causes the writes to be flagged as synchronous writes, but the - * block device queue will NOT be unplugged, since usually many pages - * will be pushed to the out before the higher-level caller actually - * waits for the writes to be completed. The various wait functions, - * such as wait_on_writeback_range() will ultimately call sync_page() - * which will ultimately call blk_run_backing_dev(), which will end up - * unplugging the device queue. + * WB_SYNC_ALL, the writes are posted using WRITE_SYNC; this + * causes the writes to be flagged as synchronous writes. */ static int __block_write_full_page(struct inode *inode, struct page *page, get_block_t *get_block, struct writeback_control *wbc, @@ -1622,7 +1616,7 @@ static int __block_write_full_page(struct inode *inode, struct page *page, const unsigned blocksize = 1 << inode->i_blkbits; int nr_underway = 0; int write_op = (wbc->sync_mode == WB_SYNC_ALL ? - WRITE_SYNC_PLUG : WRITE); + WRITE_SYNC : WRITE); BUG_ON(!PageLocked(page)); diff --git a/fs/direct-io.c b/fs/direct-io.c index df709b3b860a..426083136099 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -1173,7 +1173,7 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, struct dio *dio; if (rw & WRITE) - rw = WRITE_ODIRECT_PLUG; + rw = WRITE_ODIRECT; if (bdev) bdev_blkbits = blksize_bits(bdev_logical_block_size(bdev)); diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index 955cc309142f..e2cd90e4bb7c 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -310,8 +310,7 @@ static int io_submit_init(struct ext4_io_submit *io, io_end->offset = (page->index << PAGE_CACHE_SHIFT) + bh_offset(bh); io->io_bio = bio; - io->io_op = (wbc->sync_mode == WB_SYNC_ALL ? - WRITE_SYNC_PLUG : WRITE); + io->io_op = (wbc->sync_mode == WB_SYNC_ALL ? WRITE_SYNC : WRITE); io->io_next_block = bh->b_blocknr; return 0; } diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index eb01f3575e10..7f1c11202342 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -121,7 +121,7 @@ __acquires(&sdp->sd_log_lock) lock_buffer(bh); if (test_clear_buffer_dirty(bh)) { bh->b_end_io = end_buffer_write_sync; - submit_bh(WRITE_SYNC_PLUG, bh); + submit_bh(WRITE_SYNC, bh); } else { unlock_buffer(bh); brelse(bh); @@ -647,7 +647,7 @@ static void gfs2_ordered_write(struct gfs2_sbd *sdp) lock_buffer(bh); if (buffer_mapped(bh) && test_clear_buffer_dirty(bh)) { bh->b_end_io = end_buffer_write_sync; - submit_bh(WRITE_SYNC_PLUG, bh); + submit_bh(WRITE_SYNC, bh); } else { unlock_buffer(bh); brelse(bh); diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c index bf33f822058d..48b545a1979a 100644 --- a/fs/gfs2/lops.c +++ b/fs/gfs2/lops.c @@ -200,7 +200,7 @@ static void buf_lo_before_commit(struct gfs2_sbd *sdp) } gfs2_log_unlock(sdp); - submit_bh(WRITE_SYNC_PLUG, bh); + submit_bh(WRITE_SYNC, bh); gfs2_log_lock(sdp); n = 0; @@ -210,7 +210,7 @@ static void buf_lo_before_commit(struct gfs2_sbd *sdp) gfs2_log_unlock(sdp); lock_buffer(bd2->bd_bh); bh = gfs2_log_fake_buf(sdp, bd2->bd_bh); - submit_bh(WRITE_SYNC_PLUG, bh); + submit_bh(WRITE_SYNC, bh); gfs2_log_lock(sdp); if (++n >= num) break; @@ -352,7 +352,7 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp) sdp->sd_log_num_revoke--; if (offset + sizeof(u64) > sdp->sd_sb.sb_bsize) { - submit_bh(WRITE_SYNC_PLUG, bh); + submit_bh(WRITE_SYNC, bh); bh = gfs2_log_get_buf(sdp); mh = (struct gfs2_meta_header *)bh->b_data; @@ -369,7 +369,7 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp) } gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke); - submit_bh(WRITE_SYNC_PLUG, bh); + submit_bh(WRITE_SYNC, bh); } static void revoke_lo_before_scan(struct gfs2_jdesc *jd, @@ -571,7 +571,7 @@ static void gfs2_write_blocks(struct gfs2_sbd *sdp, struct buffer_head *bh, ptr = bh_log_ptr(bh); get_bh(bh); - submit_bh(WRITE_SYNC_PLUG, bh); + submit_bh(WRITE_SYNC, bh); gfs2_log_lock(sdp); while(!list_empty(list)) { bd = list_entry(list->next, struct gfs2_bufdata, bd_le.le_list); @@ -597,7 +597,7 @@ static void gfs2_write_blocks(struct gfs2_sbd *sdp, struct buffer_head *bh, } else { bh1 = gfs2_log_fake_buf(sdp, bd->bd_bh); } - submit_bh(WRITE_SYNC_PLUG, bh1); + submit_bh(WRITE_SYNC, bh1); gfs2_log_lock(sdp); ptr += 2; } diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c index a566331db4e1..867b713cba92 100644 --- a/fs/gfs2/meta_io.c +++ b/fs/gfs2/meta_io.c @@ -37,7 +37,7 @@ static int gfs2_aspace_writepage(struct page *page, struct writeback_control *wb struct buffer_head *bh, *head; int nr_underway = 0; int write_op = REQ_META | - (wbc->sync_mode == WB_SYNC_ALL ? WRITE_SYNC_PLUG : WRITE); + (wbc->sync_mode == WB_SYNC_ALL ? WRITE_SYNC : WRITE); BUG_ON(!PageLocked(page)); BUG_ON(!page_has_buffers(page)); diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index 34a4861c14b8..66be299acb1b 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c @@ -333,7 +333,7 @@ void journal_commit_transaction(journal_t *journal) * instead we rely on sync_buffer() doing the unplug for us. */ if (commit_transaction->t_synchronous_commit) - write_op = WRITE_SYNC_PLUG; + write_op = WRITE_SYNC; spin_lock(&commit_transaction->t_handle_lock); while (commit_transaction->t_updates) { DEFINE_WAIT(wait); diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index f3ad1598b201..3da1cc4346d5 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -137,9 +137,9 @@ static int journal_submit_commit_record(journal_t *journal, if (journal->j_flags & JBD2_BARRIER && !JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)) - ret = submit_bh(WRITE_SYNC_PLUG | WRITE_FLUSH_FUA, bh); + ret = submit_bh(WRITE_SYNC | WRITE_FLUSH_FUA, bh); else - ret = submit_bh(WRITE_SYNC_PLUG, bh); + ret = submit_bh(WRITE_SYNC, bh); *cbh = bh; return ret; @@ -369,7 +369,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) * instead we rely on sync_buffer() doing the unplug for us. */ if (commit_transaction->t_synchronous_commit) - write_op = WRITE_SYNC_PLUG; + write_op = WRITE_SYNC; trace_jbd2_commit_locking(journal, commit_transaction); stats.run.rs_wait = commit_transaction->t_max_wait; stats.run.rs_locked = jiffies; diff --git a/fs/nilfs2/segbuf.c b/fs/nilfs2/segbuf.c index 0f83e93935b2..2853ff20f85a 100644 --- a/fs/nilfs2/segbuf.c +++ b/fs/nilfs2/segbuf.c @@ -509,7 +509,7 @@ static int nilfs_segbuf_write(struct nilfs_segment_buffer *segbuf, * Last BIO is always sent through the following * submission. */ - rw |= REQ_SYNC | REQ_UNPLUG; + rw |= REQ_SYNC; res = nilfs_segbuf_submit_bio(segbuf, &wi, rw); } diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 83c1c20d145a..6bbb0ee33253 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -413,8 +413,7 @@ xfs_submit_ioend_bio( if (xfs_ioend_new_eof(ioend)) xfs_mark_inode_dirty(XFS_I(ioend->io_inode)); - submit_bio(wbc->sync_mode == WB_SYNC_ALL ? - WRITE_SYNC_PLUG : WRITE, bio); + submit_bio(wbc->sync_mode == WB_SYNC_ALL ? WRITE_SYNC : WRITE, bio); } STATIC struct bio * diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 16b286473042..be50d9e70a7d 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -128,7 +128,6 @@ enum rq_flag_bits { __REQ_NOIDLE, /* don't anticipate more IO after this one */ /* bio only flags */ - __REQ_UNPLUG, /* unplug the immediately after submission */ __REQ_RAHEAD, /* read ahead, can fail anytime */ __REQ_THROTTLED, /* This bio has already been subjected to * throttling rules. Don't do it again. */ @@ -172,7 +171,6 @@ enum rq_flag_bits { REQ_NOIDLE | REQ_FLUSH | REQ_FUA) #define REQ_CLONE_MASK REQ_COMMON_MASK -#define REQ_UNPLUG (1 << __REQ_UNPLUG) #define REQ_RAHEAD (1 << __REQ_RAHEAD) #define REQ_THROTTLED (1 << __REQ_THROTTLED) diff --git a/include/linux/fs.h b/include/linux/fs.h index 9f2cf69911b8..543e226ea6a3 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -135,16 +135,10 @@ struct inodes_stat_t { * block layer could (in theory) choose to ignore this * request if it runs into resource problems. * WRITE A normal async write. Device will be plugged. - * WRITE_SYNC_PLUG Synchronous write. Identical to WRITE, but passes down + * WRITE_SYNC Synchronous write. Identical to WRITE, but passes down * the hint that someone will be waiting on this IO - * shortly. The device must still be unplugged explicitly, - * WRITE_SYNC_PLUG does not do this as we could be - * submitting more writes before we actually wait on any - * of them. - * WRITE_SYNC Like WRITE_SYNC_PLUG, but also unplugs the device - * immediately after submission. The write equivalent - * of READ_SYNC. - * WRITE_ODIRECT_PLUG Special case write for O_DIRECT only. + * shortly. The write equivalent of READ_SYNC. + * WRITE_ODIRECT Special case write for O_DIRECT only. * WRITE_FLUSH Like WRITE_SYNC but with preceding cache flush. * WRITE_FUA Like WRITE_SYNC but data is guaranteed to be on * non-volatile media on completion. @@ -160,18 +154,14 @@ struct inodes_stat_t { #define WRITE RW_MASK #define READA RWA_MASK -#define READ_SYNC (READ | REQ_SYNC | REQ_UNPLUG) +#define READ_SYNC (READ | REQ_SYNC) #define READ_META (READ | REQ_META) -#define WRITE_SYNC_PLUG (WRITE | REQ_SYNC | REQ_NOIDLE) -#define WRITE_SYNC (WRITE | REQ_SYNC | REQ_NOIDLE | REQ_UNPLUG) -#define WRITE_ODIRECT_PLUG (WRITE | REQ_SYNC) +#define WRITE_SYNC (WRITE | REQ_SYNC | REQ_NOIDLE) +#define WRITE_ODIRECT (WRITE | REQ_SYNC) #define WRITE_META (WRITE | REQ_META) -#define WRITE_FLUSH (WRITE | REQ_SYNC | REQ_NOIDLE | REQ_UNPLUG | \ - REQ_FLUSH) -#define WRITE_FUA (WRITE | REQ_SYNC | REQ_NOIDLE | REQ_UNPLUG | \ - REQ_FUA) -#define WRITE_FLUSH_FUA (WRITE | REQ_SYNC | REQ_NOIDLE | REQ_UNPLUG | \ - REQ_FLUSH | REQ_FUA) +#define WRITE_FLUSH (WRITE | REQ_SYNC | REQ_NOIDLE | REQ_FLUSH) +#define WRITE_FUA (WRITE | REQ_SYNC | REQ_NOIDLE | REQ_FUA) +#define WRITE_FLUSH_FUA (WRITE | REQ_SYNC | REQ_NOIDLE | REQ_FLUSH | REQ_FUA) #define SEL_IN 1 #define SEL_OUT 2 diff --git a/kernel/power/block_io.c b/kernel/power/block_io.c index 83bbc7c02df9..d09dd10c5a5e 100644 --- a/kernel/power/block_io.c +++ b/kernel/power/block_io.c @@ -28,7 +28,7 @@ static int submit(int rw, struct block_device *bdev, sector_t sector, struct page *page, struct bio **bio_chain) { - const int bio_rw = rw | REQ_SYNC | REQ_UNPLUG; + const int bio_rw = rw | REQ_SYNC; struct bio *bio; bio = bio_alloc(__GFP_WAIT | __GFP_HIGH, 1); diff --git a/mm/page_io.c b/mm/page_io.c index 2dee975bf469..dc76b4d0611e 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -106,7 +106,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc) goto out; } if (wbc->sync_mode == WB_SYNC_ALL) - rw |= REQ_SYNC | REQ_UNPLUG; + rw |= REQ_SYNC; count_vm_event(PSWPOUT); set_page_writeback(page); unlock_page(page); -- cgit v1.2.3 From 4d3cf1bc557dc8b88e1cabf1980b3baa3380a641 Mon Sep 17 00:00:00 2001 From: Jiro SEKIBA Date: Thu, 10 Mar 2011 16:54:15 +0900 Subject: nilfs2: move NILFS_SUPER_MAGIC to linux/magic.h move NILFS_SUPER_MAGIC macro to linux/magic.h from linux/nilfs2_fs.h in the same manner as other filesystem magic number defined in the file. Signed-off-by: Jiro SEKIBA Signed-off-by: Ryusuke Konishi --- include/linux/magic.h | 1 + include/linux/nilfs2_fs.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/magic.h b/include/linux/magic.h index 62730ea2b56e..248930efdd31 100644 --- a/include/linux/magic.h +++ b/include/linux/magic.h @@ -23,6 +23,7 @@ #define XENFS_SUPER_MAGIC 0xabba1974 #define EXT4_SUPER_MAGIC 0xEF53 #define BTRFS_SUPER_MAGIC 0x9123683E +#define NILFS_SUPER_MAGIC 0x3434 #define HPFS_SUPER_MAGIC 0xf995e849 #define ISOFS_SUPER_MAGIC 0x9660 #define JFFS2_SUPER_MAGIC 0x72b6 diff --git a/include/linux/nilfs2_fs.h b/include/linux/nilfs2_fs.h index ae33ac2db62d..8768c469e93e 100644 --- a/include/linux/nilfs2_fs.h +++ b/include/linux/nilfs2_fs.h @@ -40,6 +40,7 @@ #include #include +#include #define NILFS_INODE_BMAP_SIZE 7 @@ -242,7 +243,6 @@ struct nilfs_super_block { #define NILFS_USER_INO 11 /* Fisrt user's file inode number */ #define NILFS_SB_OFFSET_BYTES 1024 /* byte offset of nilfs superblock */ -#define NILFS_SUPER_MAGIC 0x3434 /* NILFS filesystem magic number */ #define NILFS_SEG_MIN_BLOCKS 16 /* Minimum number of blocks in a full segment */ -- cgit v1.2.3 From d4894f3ea7375dd9492b5d3d2ecb0b6e4bdb604e Mon Sep 17 00:00:00 2001 From: Matt Carlson Date: Wed, 9 Mar 2011 16:58:21 +0000 Subject: tg3: Add code to verify RODATA checksum of VPD This patch adds code to verify the checksum stored in the "RV" info keyword of the RODATA VPD section. Signed-off-by: Matt Carlson Reviewed-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/tg3.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/pci.h | 1 + 2 files changed, 36 insertions(+) (limited to 'include') diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 8f7160812e06..ffb09795101e 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -10499,6 +10499,41 @@ static int tg3_test_nvram(struct tg3 *tp) if (csum != le32_to_cpu(buf[0xfc/4])) goto out; + for (i = 0; i < TG3_NVM_VPD_LEN; i += 4) { + /* The data is in little-endian format in NVRAM. + * Use the big-endian read routines to preserve + * the byte order as it exists in NVRAM. + */ + if (tg3_nvram_read_be32(tp, TG3_NVM_VPD_OFF + i, &buf[i/4])) + goto out; + } + + i = pci_vpd_find_tag((u8 *)buf, 0, TG3_NVM_VPD_LEN, + PCI_VPD_LRDT_RO_DATA); + if (i > 0) { + j = pci_vpd_lrdt_size(&((u8 *)buf)[i]); + if (j < 0) + goto out; + + if (i + PCI_VPD_LRDT_TAG_SIZE + j > TG3_NVM_VPD_LEN) + goto out; + + i += PCI_VPD_LRDT_TAG_SIZE; + j = pci_vpd_find_info_keyword((u8 *)buf, i, j, + PCI_VPD_RO_KEYWORD_CHKSUM); + if (j > 0) { + u8 csum8 = 0; + + j += PCI_VPD_INFO_FLD_HDR_SIZE; + + for (i = 0; i <= j; i++) + csum8 += ((u8 *)buf)[i]; + + if (csum8) + goto out; + } + } + err = 0; out: diff --git a/include/linux/pci.h b/include/linux/pci.h index 559d02897075..ff5bccb87136 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1479,6 +1479,7 @@ void pci_request_acs(void); #define PCI_VPD_RO_KEYWORD_PARTNO "PN" #define PCI_VPD_RO_KEYWORD_MFR_ID "MN" #define PCI_VPD_RO_KEYWORD_VENDOR0 "V0" +#define PCI_VPD_RO_KEYWORD_CHKSUM "RV" /** * pci_vpd_lrdt_size - Extracts the Large Resource Data Type length -- cgit v1.2.3 From 422028b1ca4c07995af82a18abced022ff4c296c Mon Sep 17 00:00:00 2001 From: Philipp Reisner Date: Wed, 27 Oct 2010 11:12:07 +0200 Subject: drbd: New configuration parameters for dealing with network congestion net { on_congestion {block|pull-ahead|disconnect}; congestion-fill {sectors}; congestion-extents {al-extents}; } Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_nl.c | 7 +++++++ include/linux/drbd.h | 7 +++++++ include/linux/drbd_limits.h | 9 +++++++++ include/linux/drbd_nl.h | 3 +++ 4 files changed, 26 insertions(+) (limited to 'include') diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index 9e27d82a9a19..f969d8717e23 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -1323,6 +1323,8 @@ static int drbd_nl_net_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, new_conf->wire_protocol = DRBD_PROT_C; new_conf->ping_timeo = DRBD_PING_TIMEO_DEF; new_conf->rr_conflict = DRBD_RR_CONFLICT_DEF; + new_conf->on_congestion = DRBD_ON_CONGESTION_DEF; + new_conf->cong_extents = DRBD_CONG_EXTENTS_DEF; if (!net_conf_from_tags(mdev, nlp->tag_list, new_conf)) { retcode = ERR_MANDATORY_TAG; @@ -1344,6 +1346,11 @@ static int drbd_nl_net_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, } } + if (new_conf->on_congestion != OC_BLOCK && new_conf->wire_protocol != DRBD_PROT_A) { + retcode = ERR_CONG_NOT_PROTO_A; + goto fail; + } + if (mdev->state.role == R_PRIMARY && new_conf->want_lose) { retcode = ERR_DISCARD; goto fail; diff --git a/include/linux/drbd.h b/include/linux/drbd.h index ef44c7a0638c..03a08baabf11 100644 --- a/include/linux/drbd.h +++ b/include/linux/drbd.h @@ -96,6 +96,12 @@ enum drbd_on_no_data { OND_SUSPEND_IO }; +enum drbd_on_congestion { + OC_BLOCK, + OC_PULL_AHEAD, + OC_DISCONNECT, +}; + /* KEEP the order, do not delete or insert. Only append. */ enum drbd_ret_codes { ERR_CODE_BASE = 100, @@ -146,6 +152,7 @@ enum drbd_ret_codes { ERR_PERM = 152, ERR_NEED_APV_93 = 153, ERR_STONITH_AND_PROT_A = 154, + ERR_CONG_NOT_PROTO_A = 155, /* insert new ones above this line */ AFTER_LAST_ERR_CODE diff --git a/include/linux/drbd_limits.h b/include/linux/drbd_limits.h index 4ac33f34b77e..abf418724e52 100644 --- a/include/linux/drbd_limits.h +++ b/include/linux/drbd_limits.h @@ -129,6 +129,7 @@ #define DRBD_AFTER_SB_2P_DEF ASB_DISCONNECT #define DRBD_RR_CONFLICT_DEF ASB_DISCONNECT #define DRBD_ON_NO_DATA_DEF OND_IO_ERROR +#define DRBD_ON_CONGESTION_DEF OC_BLOCK #define DRBD_MAX_BIO_BVECS_MIN 0 #define DRBD_MAX_BIO_BVECS_MAX 128 @@ -154,5 +155,13 @@ #define DRBD_C_MIN_RATE_MAX (4 << 20) #define DRBD_C_MIN_RATE_DEF 4096 +#define DRBD_CONG_FILL_MIN 0 +#define DRBD_CONG_FILL_MAX (10<<21) /* 10GByte in sectors */ +#define DRBD_CONG_FILL_DEF 0 + +#define DRBD_CONG_EXTENTS_MIN DRBD_AL_EXTENTS_MIN +#define DRBD_CONG_EXTENTS_MAX DRBD_AL_EXTENTS_MAX +#define DRBD_CONG_EXTENTS_DEF DRBD_AL_EXTENTS_DEF + #undef RANGE #endif diff --git a/include/linux/drbd_nl.h b/include/linux/drbd_nl.h index ade91107c9a5..8cde3945d1f7 100644 --- a/include/linux/drbd_nl.h +++ b/include/linux/drbd_nl.h @@ -56,6 +56,9 @@ NL_PACKET(net_conf, 5, NL_INTEGER( 39, T_MAY_IGNORE, rr_conflict) NL_INTEGER( 40, T_MAY_IGNORE, ping_timeo) NL_INTEGER( 67, T_MAY_IGNORE, rcvbuf_size) + NL_INTEGER( 81, T_MAY_IGNORE, on_congestion) + NL_INTEGER( 82, T_MAY_IGNORE, cong_fill) + NL_INTEGER( 83, T_MAY_IGNORE, cong_extents) /* 59 addr_family was available in GIT, never released */ NL_BIT( 60, T_MANDATORY, mind_af) NL_BIT( 27, T_MAY_IGNORE, want_lose) -- cgit v1.2.3 From 67531718d8f1259f01ab84c2aa25f7b03c7afd46 Mon Sep 17 00:00:00 2001 From: Philipp Reisner Date: Wed, 27 Oct 2010 12:21:30 +0200 Subject: drbd: Implemented two new connection states Ahead/Behind In this connection mode, the ahead node no longer replicates application IO. The behind's disk becomes out dated. Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_int.h | 2 ++ drivers/block/drbd/drbd_main.c | 12 ++++++++++-- drivers/block/drbd/drbd_receiver.c | 3 +++ drivers/block/drbd/drbd_req.c | 23 +++++++++++++++++++++++ drivers/block/drbd/drbd_strings.c | 4 +++- include/linux/drbd.h | 4 ++++ 6 files changed, 45 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index c804e44b9455..21b7439438cd 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -2217,6 +2217,8 @@ static inline int drbd_state_is_stable(union drbd_state s) case C_VERIFY_T: case C_PAUSED_SYNC_S: case C_PAUSED_SYNC_T: + case C_AHEAD: + case C_BEHIND: /* maybe stable, look at the disk state */ break; diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index e81d009dd061..46f27d6c0b21 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -871,16 +871,19 @@ static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state if (ns.conn >= C_CONNECTED && ((ns.disk == D_CONSISTENT || ns.disk == D_OUTDATED) || - (ns.disk == D_NEGOTIATING && ns.conn == C_WF_BITMAP_T))) { + (ns.disk == D_NEGOTIATING && ns.conn == C_WF_BITMAP_T) || + ns.conn >= C_AHEAD)) { switch (ns.conn) { case C_WF_BITMAP_T: case C_PAUSED_SYNC_T: + case C_BEHIND: ns.disk = D_OUTDATED; break; case C_CONNECTED: case C_WF_BITMAP_S: case C_SYNC_SOURCE: case C_PAUSED_SYNC_S: + case C_AHEAD: ns.disk = D_UP_TO_DATE; break; case C_SYNC_TARGET: @@ -893,16 +896,18 @@ static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state } if (ns.conn >= C_CONNECTED && - (ns.pdsk == D_CONSISTENT || ns.pdsk == D_OUTDATED)) { + (ns.pdsk == D_CONSISTENT || ns.pdsk == D_OUTDATED || ns.conn >= C_AHEAD)) { switch (ns.conn) { case C_CONNECTED: case C_WF_BITMAP_T: case C_PAUSED_SYNC_T: case C_SYNC_TARGET: + case C_BEHIND: ns.pdsk = D_UP_TO_DATE; break; case C_WF_BITMAP_S: case C_PAUSED_SYNC_S: + case C_AHEAD: /* remap any consistent state to D_OUTDATED, * but disallow "upgrade" of not even consistent states. */ @@ -1374,6 +1379,9 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os, if (os.conn == C_WF_REPORT_PARAMS && ns.conn >= C_CONNECTED) drbd_send_state(mdev); + if (os.conn != C_AHEAD && ns.conn == C_AHEAD) + drbd_send_state(mdev); + /* We are in the progress to start a full sync... */ if ((os.conn != C_STARTING_SYNC_T && ns.conn == C_STARTING_SYNC_T) || (os.conn != C_STARTING_SYNC_S && ns.conn == C_STARTING_SYNC_S)) diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index f3052d871d31..b19e8b2c4ce5 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -3179,6 +3179,9 @@ static int receive_state(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned if (ns.conn == C_WF_REPORT_PARAMS) ns.conn = C_CONNECTED; + if (peer_state.conn == C_AHEAD) + ns.conn = C_BEHIND; + if (mdev->p_uuid && peer_state.disk >= D_NEGOTIATING && get_ldev_if_state(mdev, D_NEGOTIATING)) { int cr; /* consider resync */ diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index 5c60d77d447c..60288fb3c4d7 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -948,6 +948,29 @@ allocate_barrier: ? queue_for_net_write : queue_for_net_read); } + + if (remote && mdev->net_conf->on_congestion != OC_BLOCK) { + int congested = 0; + + if (mdev->net_conf->cong_fill && + atomic_read(&mdev->ap_in_flight) >= mdev->net_conf->cong_fill) { + dev_info(DEV, "Congestion-fill threshold reached\n"); + congested = 1; + } + + if (mdev->act_log->used >= mdev->net_conf->cong_extents) { + dev_info(DEV, "Congestion-extents threshold reached\n"); + congested = 1; + } + + if (congested) { + if (mdev->net_conf->on_congestion == OC_PULL_AHEAD) + _drbd_set_state(_NS(mdev, conn, C_AHEAD), 0, NULL); + else /*mdev->net_conf->on_congestion == OC_DISCONNECT */ + _drbd_set_state(_NS(mdev, conn, C_DISCONNECTING), 0, NULL); + } + } + spin_unlock_irq(&mdev->req_lock); kfree(b); /* if someone else has beaten us to it... */ diff --git a/drivers/block/drbd/drbd_strings.c b/drivers/block/drbd/drbd_strings.c index 85179e1fb50a..5b970adc3b6f 100644 --- a/drivers/block/drbd/drbd_strings.c +++ b/drivers/block/drbd/drbd_strings.c @@ -48,6 +48,8 @@ static const char *drbd_conn_s_names[] = { [C_PAUSED_SYNC_T] = "PausedSyncT", [C_VERIFY_S] = "VerifyS", [C_VERIFY_T] = "VerifyT", + [C_AHEAD] = "Ahead", + [C_BEHIND] = "Behind", }; static const char *drbd_role_s_names[] = { @@ -92,7 +94,7 @@ static const char *drbd_state_sw_errors[] = { const char *drbd_conn_str(enum drbd_conns s) { /* enums are unsigned... */ - return s > C_PAUSED_SYNC_T ? "TOO_LARGE" : drbd_conn_s_names[s]; + return s > C_BEHIND ? "TOO_LARGE" : drbd_conn_s_names[s]; } const char *drbd_role_str(enum drbd_role s) diff --git a/include/linux/drbd.h b/include/linux/drbd.h index 03a08baabf11..23f31be6f00d 100644 --- a/include/linux/drbd.h +++ b/include/linux/drbd.h @@ -206,6 +206,10 @@ enum drbd_conns { C_VERIFY_T, C_PAUSED_SYNC_S, C_PAUSED_SYNC_T, + + C_AHEAD, + C_BEHIND, + C_MASK = 31 }; -- cgit v1.2.3 From 73a01a18b9c28a0fab1131ece5b0a9bc00a879b8 Mon Sep 17 00:00:00 2001 From: Philipp Reisner Date: Wed, 27 Oct 2010 14:33:00 +0200 Subject: drbd: New packet for Ahead/Behind mode: P_OUT_OF_SYNC Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_actlog.c | 10 +++++---- drivers/block/drbd/drbd_int.h | 14 +++++++++++- drivers/block/drbd/drbd_main.c | 10 +++++++++ drivers/block/drbd/drbd_receiver.c | 10 +++++++++ drivers/block/drbd/drbd_req.c | 44 +++++++++++++++++++++++++++----------- drivers/block/drbd/drbd_req.h | 4 +++- drivers/block/drbd/drbd_worker.c | 16 ++++++++++++++ include/linux/drbd.h | 2 +- 8 files changed, 91 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c index b4adb58c7472..33f6cc537d08 100644 --- a/drivers/block/drbd/drbd_actlog.c +++ b/drivers/block/drbd/drbd_actlog.c @@ -1007,22 +1007,22 @@ void __drbd_set_in_sync(struct drbd_conf *mdev, sector_t sector, int size, * called by tl_clear and drbd_send_dblock (==drbd_make_request). * so this can be _any_ process. */ -void __drbd_set_out_of_sync(struct drbd_conf *mdev, sector_t sector, int size, +int __drbd_set_out_of_sync(struct drbd_conf *mdev, sector_t sector, int size, const char *file, const unsigned int line) { unsigned long sbnr, ebnr, lbnr, flags; sector_t esector, nr_sectors; - unsigned int enr, count; + unsigned int enr, count = 0; struct lc_element *e; if (size <= 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_BIO_SIZE) { dev_err(DEV, "sector: %llus, size: %d\n", (unsigned long long)sector, size); - return; + return 0; } if (!get_ldev(mdev)) - return; /* no disk, no metadata, no bitmap to set bits in */ + return 0; /* no disk, no metadata, no bitmap to set bits in */ nr_sectors = drbd_get_capacity(mdev->this_bdev); esector = sector + (size >> 9) - 1; @@ -1052,6 +1052,8 @@ void __drbd_set_out_of_sync(struct drbd_conf *mdev, sector_t sector, int size, out: put_ldev(mdev); + + return count; } static diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index 21b7439438cd..471331236826 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -212,6 +212,7 @@ enum drbd_packets { /* P_CKPT_FENCE_REQ = 0x25, * currently reserved for protocol D */ /* P_CKPT_DISABLE_REQ = 0x26, * currently reserved for protocol D */ P_DELAY_PROBE = 0x27, /* is used on BOTH sockets */ + P_OUT_OF_SYNC = 0x28, /* Mark as out of sync (Outrunning), data socket */ P_MAX_CMD = 0x28, P_MAY_IGNORE = 0x100, /* Flag to test if (cmd > P_MAY_IGNORE) ... */ @@ -269,6 +270,7 @@ static inline const char *cmdname(enum drbd_packets cmd) [P_RS_IS_IN_SYNC] = "CsumRSIsInSync", [P_COMPRESSED_BITMAP] = "CBitmap", [P_DELAY_PROBE] = "DelayProbe", + [P_OUT_OF_SYNC] = "OutOfSync", [P_MAX_CMD] = NULL, }; @@ -550,6 +552,13 @@ struct p_discard { u32 pad; } __packed; +struct p_block_desc { + struct p_header80 head; + u64 sector; + u32 blksize; + u32 pad; /* to multiple of 8 Byte */ +} __packed; + /* Valid values for the encoding field. * Bump proto version when changing this. */ enum drbd_bitmap_code { @@ -647,6 +656,7 @@ union p_polymorph { struct p_block_req block_req; struct p_delay_probe93 delay_probe93; struct p_rs_uuid rs_uuid; + struct p_block_desc block_desc; } __packed; /**********************************************************************/ @@ -1221,6 +1231,7 @@ extern int drbd_send_ack_dp(struct drbd_conf *mdev, enum drbd_packets cmd, struct p_data *dp, int data_size); extern int drbd_send_ack_ex(struct drbd_conf *mdev, enum drbd_packets cmd, sector_t sector, int blksize, u64 block_id); +extern int drbd_send_oos(struct drbd_conf *mdev, struct drbd_request *req); extern int drbd_send_block(struct drbd_conf *mdev, enum drbd_packets cmd, struct drbd_epoch_entry *e); extern int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req); @@ -1534,6 +1545,7 @@ extern int w_send_read_req(struct drbd_conf *, struct drbd_work *, int); extern int w_prev_work_done(struct drbd_conf *, struct drbd_work *, int); extern int w_e_reissue(struct drbd_conf *, struct drbd_work *, int); extern int w_restart_disk_io(struct drbd_conf *, struct drbd_work *, int); +extern int w_send_oos(struct drbd_conf *, struct drbd_work *, int); extern void resync_timer_fn(unsigned long data); @@ -1626,7 +1638,7 @@ extern void __drbd_set_in_sync(struct drbd_conf *mdev, sector_t sector, int size, const char *file, const unsigned int line); #define drbd_set_in_sync(mdev, sector, size) \ __drbd_set_in_sync(mdev, sector, size, __FILE__, __LINE__) -extern void __drbd_set_out_of_sync(struct drbd_conf *mdev, sector_t sector, +extern int __drbd_set_out_of_sync(struct drbd_conf *mdev, sector_t sector, int size, const char *file, const unsigned int line); #define drbd_set_out_of_sync(mdev, sector, size) \ __drbd_set_out_of_sync(mdev, sector, size, __FILE__, __LINE__) diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 46f27d6c0b21..0dc93f43a476 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -2634,6 +2634,16 @@ int drbd_send_block(struct drbd_conf *mdev, enum drbd_packets cmd, return ok; } +int drbd_send_oos(struct drbd_conf *mdev, struct drbd_request *req) +{ + struct p_block_desc p; + + p.sector = cpu_to_be64(req->sector); + p.blksize = cpu_to_be32(req->size); + + return drbd_send_cmd(mdev, USE_DATA_SOCKET, P_OUT_OF_SYNC, &p.head, sizeof(p)); +} + /* drbd_send distinguishes two cases: diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index b19e8b2c4ce5..04a08e7541cc 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -3562,6 +3562,15 @@ static int receive_UnplugRemote(struct drbd_conf *mdev, enum drbd_packets cmd, u return TRUE; } +static int receive_out_of_sync(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned int data_size) +{ + struct p_block_desc *p = &mdev->data.rbuf.block_desc; + + drbd_set_out_of_sync(mdev, be64_to_cpu(p->sector), be32_to_cpu(p->blksize)); + + return TRUE; +} + typedef int (*drbd_cmd_handler_f)(struct drbd_conf *, enum drbd_packets cmd, unsigned int to_receive); struct data_cmd { @@ -3592,6 +3601,7 @@ static struct data_cmd drbd_cmd_handler[] = { [P_OV_REPLY] = { 1, sizeof(struct p_block_req), receive_DataRequest }, [P_CSUM_RS_REQUEST] = { 1, sizeof(struct p_block_req), receive_DataRequest }, [P_DELAY_PROBE] = { 0, sizeof(struct p_delay_probe93), receive_skip }, + [P_OUT_OF_SYNC] = { 0, sizeof(struct p_block_desc), receive_out_of_sync }, /* anything missing from this table is in * the asender_tbl, see get_asender_cmd */ [P_MAX_CMD] = { 0, 0, NULL }, diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index 60288fb3c4d7..a8d1ff2bda27 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -142,7 +142,7 @@ static void _about_to_complete_local_write(struct drbd_conf *mdev, /* before we can signal completion to the upper layers, * we may need to close the current epoch */ - if (mdev->state.conn >= C_CONNECTED && + if (mdev->state.conn >= C_CONNECTED && mdev->state.conn < C_AHEAD && req->epoch == mdev->newest_tle->br_number) queue_barrier(mdev); @@ -545,6 +545,14 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what, break; + case queue_for_send_oos: + req->rq_state |= RQ_NET_QUEUED; + req->w.cb = w_send_oos; + drbd_queue_work(&mdev->data.work, &req->w); + break; + + case oos_handed_to_network: + /* actually the same */ case send_canceled: /* treat it the same */ case send_failed: @@ -756,7 +764,7 @@ static int drbd_make_request_common(struct drbd_conf *mdev, struct bio *bio) const sector_t sector = bio->bi_sector; struct drbd_tl_epoch *b = NULL; struct drbd_request *req; - int local, remote; + int local, remote, send_oos = 0; int err = -EIO; int ret = 0; @@ -820,8 +828,11 @@ static int drbd_make_request_common(struct drbd_conf *mdev, struct bio *bio) } remote = remote && (mdev->state.pdsk == D_UP_TO_DATE || - (mdev->state.pdsk == D_INCONSISTENT && - mdev->state.conn >= C_CONNECTED)); + (mdev->state.pdsk >= D_INCONSISTENT && + mdev->state.conn >= C_CONNECTED && + mdev->state.conn < C_AHEAD)); + send_oos = (rw == WRITE && mdev->state.conn == C_AHEAD && + mdev->state.pdsk >= D_INCONSISTENT); if (!(local || remote) && !is_susp(mdev->state)) { if (__ratelimit(&drbd_ratelimit_state)) @@ -835,7 +846,7 @@ static int drbd_make_request_common(struct drbd_conf *mdev, struct bio *bio) * but there is a race between testing the bit and pointer outside the * spinlock, and grabbing the spinlock. * if we lost that race, we retry. */ - if (rw == WRITE && remote && + if (rw == WRITE && (remote || send_oos) && mdev->unused_spare_tle == NULL && test_bit(CREATE_BARRIER, &mdev->flags)) { allocate_barrier: @@ -860,11 +871,15 @@ allocate_barrier: goto fail_free_complete; } - if (remote) { + if (remote || send_oos) { remote = (mdev->state.pdsk == D_UP_TO_DATE || - (mdev->state.pdsk == D_INCONSISTENT && - mdev->state.conn >= C_CONNECTED)); - if (!remote) + (mdev->state.pdsk >= D_INCONSISTENT && + mdev->state.conn >= C_CONNECTED && + mdev->state.conn < C_AHEAD)); + send_oos = (rw == WRITE && mdev->state.conn == C_AHEAD && + mdev->state.pdsk >= D_INCONSISTENT); + + if (!(remote || send_oos)) dev_warn(DEV, "lost connection while grabbing the req_lock!\n"); if (!(local || remote)) { dev_err(DEV, "IO ERROR: neither local nor remote disk\n"); @@ -877,7 +892,7 @@ allocate_barrier: mdev->unused_spare_tle = b; b = NULL; } - if (rw == WRITE && remote && + if (rw == WRITE && (remote || send_oos) && mdev->unused_spare_tle == NULL && test_bit(CREATE_BARRIER, &mdev->flags)) { /* someone closed the current epoch @@ -900,7 +915,7 @@ allocate_barrier: * barrier packet. To get the write ordering right, we only have to * make sure that, if this is a write request and it triggered a * barrier packet, this request is queued within the same spinlock. */ - if (remote && mdev->unused_spare_tle && + if ((remote || send_oos) && mdev->unused_spare_tle && test_and_clear_bit(CREATE_BARRIER, &mdev->flags)) { _tl_add_barrier(mdev, mdev->unused_spare_tle); mdev->unused_spare_tle = NULL; @@ -948,8 +963,11 @@ allocate_barrier: ? queue_for_net_write : queue_for_net_read); } + if (send_oos && drbd_set_out_of_sync(mdev, sector, size)) + _req_mod(req, queue_for_send_oos); - if (remote && mdev->net_conf->on_congestion != OC_BLOCK) { + if (remote && + mdev->net_conf->on_congestion != OC_BLOCK && mdev->agreed_pro_version >= 96) { int congested = 0; if (mdev->net_conf->cong_fill && @@ -964,6 +982,8 @@ allocate_barrier: } if (congested) { + queue_barrier(mdev); + if (mdev->net_conf->on_congestion == OC_PULL_AHEAD) _drbd_set_state(_NS(mdev, conn, C_AHEAD), 0, NULL); else /*mdev->net_conf->on_congestion == OC_DISCONNECT */ diff --git a/drivers/block/drbd/drbd_req.h b/drivers/block/drbd/drbd_req.h index 69d350fe7c1e..40d3dcd8fc81 100644 --- a/drivers/block/drbd/drbd_req.h +++ b/drivers/block/drbd/drbd_req.h @@ -82,14 +82,16 @@ enum drbd_req_event { to_be_submitted, /* XXX yes, now I am inconsistent... - * these two are not "events" but "actions" + * these are not "events" but "actions" * oh, well... */ queue_for_net_write, queue_for_net_read, + queue_for_send_oos, send_canceled, send_failed, handed_over_to_network, + oos_handed_to_network, connection_lost_while_pending, read_retry_remote_canceled, recv_acked_by_peer, diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c index 782d87237cb4..67499077c482 100644 --- a/drivers/block/drbd/drbd_worker.c +++ b/drivers/block/drbd/drbd_worker.c @@ -1237,6 +1237,22 @@ int w_send_write_hint(struct drbd_conf *mdev, struct drbd_work *w, int cancel) return drbd_send_short_cmd(mdev, P_UNPLUG_REMOTE); } +int w_send_oos(struct drbd_conf *mdev, struct drbd_work *w, int cancel) +{ + struct drbd_request *req = container_of(w, struct drbd_request, w); + int ok; + + if (unlikely(cancel)) { + req_mod(req, send_canceled); + return 1; + } + + ok = drbd_send_oos(mdev, req); + req_mod(req, oos_handed_to_network); + + return ok; +} + /** * w_send_dblock() - Worker callback to send a P_DATA packet in order to mirror a write request * @mdev: DRBD device. diff --git a/include/linux/drbd.h b/include/linux/drbd.h index 23f31be6f00d..41da654cc0b1 100644 --- a/include/linux/drbd.h +++ b/include/linux/drbd.h @@ -56,7 +56,7 @@ extern const char *drbd_buildtag(void); #define REL_VERSION "8.3.9" #define API_VERSION 88 #define PRO_VERSION_MIN 86 -#define PRO_VERSION_MAX 95 +#define PRO_VERSION_MAX 96 enum drbd_io_error_p { -- cgit v1.2.3 From 42ff269d1022a86be4f526cf674998c47b7ab856 Mon Sep 17 00:00:00 2001 From: Lars Ellenberg Date: Wed, 24 Nov 2010 10:11:14 +0100 Subject: drbd: add packet_type 27 (return_code_only) to netlink api In case we ever should add an other packet type, we must not reuse 27, as that currently used for "empty" return code only replies. Document it as such. Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_nl.c | 6 ++++-- include/linux/drbd_nl.h | 6 +++++- include/linux/drbd_tag_magic.h | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index 80a389d24cdd..6a6dde6c51c6 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -2195,7 +2195,8 @@ static void drbd_connector_callback(struct cn_msg *req, struct netlink_skb_parms goto fail; } - if (nlp->packet_type >= P_nl_after_last_packet) { + if (nlp->packet_type >= P_nl_after_last_packet || + nlp->packet_type == P_return_code_only) { retcode = ERR_PACKET_NR; goto fail; } @@ -2219,7 +2220,7 @@ static void drbd_connector_callback(struct cn_msg *req, struct netlink_skb_parms reply = (struct drbd_nl_cfg_reply *) cn_reply->data; reply->packet_type = - cm->reply_body_size ? nlp->packet_type : P_nl_after_last_packet; + cm->reply_body_size ? nlp->packet_type : P_return_code_only; reply->minor = nlp->drbd_minor; reply->ret_code = NO_ERROR; /* Might by modified by cm->function. */ /* reply->tag_list; might be modified by cm->function. */ @@ -2525,6 +2526,7 @@ void drbd_nl_send_reply(struct cn_msg *req, int ret_code) cn_reply->len = sizeof(struct drbd_nl_cfg_reply); cn_reply->flags = 0; + reply->packet_type = P_return_code_only; reply->minor = ((struct drbd_nl_cfg_req *)req->data)->drbd_minor; reply->ret_code = ret_code; diff --git a/include/linux/drbd_nl.h b/include/linux/drbd_nl.h index 8cde3945d1f7..6fc614b06c4d 100644 --- a/include/linux/drbd_nl.h +++ b/include/linux/drbd_nl.h @@ -146,9 +146,13 @@ NL_PACKET(new_c_uuid, 26, NL_BIT( 63, T_MANDATORY, clear_bm) ) +#ifdef NL_RESPONSE +NL_RESPONSE(return_code_only, 27) +#endif + #undef NL_PACKET #undef NL_INTEGER #undef NL_INT64 #undef NL_BIT #undef NL_STRING - +#undef NL_RESPONSE diff --git a/include/linux/drbd_tag_magic.h b/include/linux/drbd_tag_magic.h index fcdff8410e99..f14a165e82dc 100644 --- a/include/linux/drbd_tag_magic.h +++ b/include/linux/drbd_tag_magic.h @@ -7,6 +7,7 @@ /* declare packet_type enums */ enum packet_types { #define NL_PACKET(name, number, fields) P_ ## name = number, +#define NL_RESPONSE(name, number) P_ ## name = number, #define NL_INTEGER(pn, pr, member) #define NL_INT64(pn, pr, member) #define NL_BIT(pn, pr, member) -- cgit v1.2.3 From 2561b9c1f1d63077c41903fc6ad58dc9ec47248b Mon Sep 17 00:00:00 2001 From: Philipp Reisner Date: Fri, 3 Dec 2010 15:22:48 +0100 Subject: drbd: --force option for disconnect As the network connection can be lost at any time, a --force option for disconnect is just a matter of completeness. Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_nl.c | 15 +++++++++++++++ include/linux/drbd_nl.h | 4 +++- 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index 6a6dde6c51c6..cd0459f0403f 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -1531,6 +1531,21 @@ static int drbd_nl_disconnect(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nl struct drbd_nl_cfg_reply *reply) { int retcode; + struct disconnect dc; + + memset(&dc, 0, sizeof(struct disconnect)); + if (!disconnect_from_tags(mdev, nlp->tag_list, &dc)) { + retcode = ERR_MANDATORY_TAG; + goto fail; + } + + if (dc.force) { + spin_lock_irq(&mdev->req_lock); + if (mdev->state.conn >= C_WF_CONNECTION) + _drbd_set_state(_NS(mdev, conn, C_DISCONNECTING), CS_HARD, NULL); + spin_unlock_irq(&mdev->req_lock); + goto done; + } retcode = _drbd_request_state(mdev, NS(conn, C_DISCONNECTING), CS_ORDERED); diff --git a/include/linux/drbd_nl.h b/include/linux/drbd_nl.h index 6fc614b06c4d..ab6159e4fcf0 100644 --- a/include/linux/drbd_nl.h +++ b/include/linux/drbd_nl.h @@ -69,7 +69,9 @@ NL_PACKET(net_conf, 5, NL_BIT( 70, T_MANDATORY, dry_run) ) -NL_PACKET(disconnect, 6, ) +NL_PACKET(disconnect, 6, + NL_BIT( 84, T_MAY_IGNORE, force) +) NL_PACKET(resize, 7, NL_INT64( 29, T_MAY_IGNORE, resize_size) -- cgit v1.2.3 From 116676ca621a862a8124969772f4dd61c8b40eee Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 8 Dec 2010 13:33:11 +0100 Subject: drbd: Rename enum drbd_ret_codes to enum drbd_ret_code Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_main.c | 2 +- drivers/block/drbd/drbd_nl.c | 4 ++-- include/linux/drbd.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index f43e2aa354a6..8d69e3a1b3c2 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -3570,7 +3570,7 @@ void drbd_md_sync(struct drbd_conf *mdev) * @mdev: DRBD device. * @bdev: Device from which the meta data should be read in. * - * Return 0 (NO_ERROR) on success, and an enum drbd_ret_codes in case + * Return 0 (NO_ERROR) on success, and an enum drbd_ret_code in case * something goes wrong. Currently only: ERR_IO_MD_DISK, ERR_MD_INVALID. */ int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev) diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index cd0459f0403f..fe336592e538 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -849,7 +849,7 @@ static void drbd_suspend_al(struct drbd_conf *mdev) static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, struct drbd_nl_cfg_reply *reply) { - enum drbd_ret_codes retcode; + enum drbd_ret_code retcode; enum determine_dev_size dd; sector_t max_possible_sectors; sector_t min_md_device_sectors; @@ -1278,7 +1278,7 @@ static int drbd_nl_net_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, struct drbd_nl_cfg_reply *reply) { int i, ns; - enum drbd_ret_codes retcode; + enum drbd_ret_code retcode; struct net_conf *new_conf = NULL; struct crypto_hash *tfm = NULL; struct crypto_hash *integrity_w_tfm = NULL; diff --git a/include/linux/drbd.h b/include/linux/drbd.h index 41da654cc0b1..d92f989036c9 100644 --- a/include/linux/drbd.h +++ b/include/linux/drbd.h @@ -103,7 +103,7 @@ enum drbd_on_congestion { }; /* KEEP the order, do not delete or insert. Only append. */ -enum drbd_ret_codes { +enum drbd_ret_code { ERR_CODE_BASE = 100, NO_ERROR = 101, ERR_LOCAL_ADDR = 102, -- cgit v1.2.3 From c8b325632f0e5ffdaeca3d1f3be77c9399316a40 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 8 Dec 2010 01:06:16 +0100 Subject: drbd: Rename enum drbd_state_ret_codes to enum drbd_state_rv Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_main.c | 5 +++-- drivers/block/drbd/drbd_strings.c | 2 +- include/linux/drbd.h | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 8d69e3a1b3c2..cddf311b7429 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -510,8 +510,9 @@ static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state int drbd_send_state_req(struct drbd_conf *, union drbd_state, union drbd_state); -static enum drbd_state_ret_codes _req_st_cond(struct drbd_conf *mdev, - union drbd_state mask, union drbd_state val) +static enum drbd_state_rv +_req_st_cond(struct drbd_conf *mdev, union drbd_state mask, + union drbd_state val) { union drbd_state os, ns; unsigned long flags; diff --git a/drivers/block/drbd/drbd_strings.c b/drivers/block/drbd/drbd_strings.c index 5b970adc3b6f..c44a2a602772 100644 --- a/drivers/block/drbd/drbd_strings.c +++ b/drivers/block/drbd/drbd_strings.c @@ -107,7 +107,7 @@ const char *drbd_disk_str(enum drbd_disk_state s) return s > D_UP_TO_DATE ? "TOO_LARGE" : drbd_disk_s_names[s]; } -const char *drbd_set_st_err_str(enum drbd_state_ret_codes err) +const char *drbd_set_st_err_str(enum drbd_state_rv err) { return err <= SS_AFTER_LAST_ERROR ? "TOO_SMALL" : err > SS_TWO_PRIMARIES ? "TOO_LARGE" diff --git a/include/linux/drbd.h b/include/linux/drbd.h index d92f989036c9..d10431fab004 100644 --- a/include/linux/drbd.h +++ b/include/linux/drbd.h @@ -270,7 +270,7 @@ union drbd_state { unsigned int i; }; -enum drbd_state_ret_codes { +enum drbd_state_rv { SS_CW_NO_NEED = 4, SS_CW_SUCCESS = 3, SS_NOTHING_TO_DO = 2, @@ -301,7 +301,7 @@ enum drbd_state_ret_codes { extern const char *drbd_conn_str(enum drbd_conns); extern const char *drbd_role_str(enum drbd_role); extern const char *drbd_disk_str(enum drbd_disk_state); -extern const char *drbd_set_st_err_str(enum drbd_state_ret_codes); +extern const char *drbd_set_st_err_str(enum drbd_state_rv); #define SHARED_SECRET_MAX 64 -- cgit v1.2.3 From 2b8a90b55533c66258a1ff0fb27b8cffa95665c4 Mon Sep 17 00:00:00 2001 From: Philipp Reisner Date: Mon, 10 Jan 2011 11:15:17 +0100 Subject: drbd: Corrected off-by-one error in DRBD_MINOR_COUNT_MAX Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_main.c | 7 ++++--- include/linux/drbd_limits.h | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 4074d6699307..da98bff7c333 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -85,7 +85,8 @@ MODULE_AUTHOR("Philipp Reisner , " MODULE_DESCRIPTION("drbd - Distributed Replicated Block Device v" REL_VERSION); MODULE_VERSION(REL_VERSION); MODULE_LICENSE("GPL"); -MODULE_PARM_DESC(minor_count, "Maximum number of drbd devices (1-255)"); +MODULE_PARM_DESC(minor_count, "Maximum number of drbd devices (" + __stringify(DRBD_MINOR_COUNT_MIN) "-" __stringify(DRBD_MINOR_COUNT_MAX) ")"); MODULE_ALIAS_BLOCKDEV_MAJOR(DRBD_MAJOR); #include @@ -115,7 +116,7 @@ module_param(fault_devs, int, 0644); #endif /* module parameter, defined */ -unsigned int minor_count = 32; +unsigned int minor_count = DRBD_MINOR_COUNT_DEF; int disable_sendpage; int allow_oos; unsigned int cn_idx = CN_IDX_DRBD; @@ -3456,7 +3457,7 @@ int __init drbd_init(void) return -EINVAL; } - if (1 > minor_count || minor_count > 255) { + if (minor_count < DRBD_MINOR_COUNT_MIN || minor_count > DRBD_MINOR_COUNT_MAX) { printk(KERN_ERR "drbd: invalid minor_count (%d)\n", minor_count); #ifdef MODULE diff --git a/include/linux/drbd_limits.h b/include/linux/drbd_limits.h index abf418724e52..bb264a5732de 100644 --- a/include/linux/drbd_limits.h +++ b/include/linux/drbd_limits.h @@ -16,7 +16,8 @@ #define DEBUG_RANGE_CHECK 0 #define DRBD_MINOR_COUNT_MIN 1 -#define DRBD_MINOR_COUNT_MAX 255 +#define DRBD_MINOR_COUNT_MAX 256 +#define DRBD_MINOR_COUNT_DEF 32 #define DRBD_DIALOG_REFRESH_MIN 0 #define DRBD_DIALOG_REFRESH_MAX 600 -- cgit v1.2.3 From cd88d030d41a9b0100fd5fee872024e6ebc8b276 Mon Sep 17 00:00:00 2001 From: Philipp Reisner Date: Thu, 20 Jan 2011 11:46:41 +0100 Subject: drbd: Provide hints with the error message when clearing the sync pause flag When the user clears the sync-pause flag, and sync stays in pause state, give hints to the user, why it still is in pause state. Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_nl.c | 12 ++++++++++-- include/linux/drbd.h | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index 434b621f76a9..ffe3a97fef9b 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -1952,9 +1952,17 @@ static int drbd_nl_resume_sync(struct drbd_conf *mdev, struct drbd_nl_cfg_req *n struct drbd_nl_cfg_reply *reply) { int retcode = NO_ERROR; + union drbd_state s; - if (drbd_request_state(mdev, NS(user_isp, 0)) == SS_NOTHING_TO_DO) - retcode = ERR_PAUSE_IS_CLEAR; + if (drbd_request_state(mdev, NS(user_isp, 0)) == SS_NOTHING_TO_DO) { + s = mdev->state; + if (s.conn == C_PAUSED_SYNC_S || s.conn == C_PAUSED_SYNC_T) { + retcode = s.aftr_isp ? ERR_PIC_AFTER_DEP : + s.peer_isp ? ERR_PIC_PEER_DEP : ERR_PAUSE_IS_CLEAR; + } else { + retcode = ERR_PAUSE_IS_CLEAR; + } + } reply->ret_code = retcode; return 0; diff --git a/include/linux/drbd.h b/include/linux/drbd.h index d10431fab004..ba5c785d3f7d 100644 --- a/include/linux/drbd.h +++ b/include/linux/drbd.h @@ -153,6 +153,8 @@ enum drbd_ret_code { ERR_NEED_APV_93 = 153, ERR_STONITH_AND_PROT_A = 154, ERR_CONG_NOT_PROTO_A = 155, + ERR_PIC_AFTER_DEP = 156, + ERR_PIC_PEER_DEP = 157, /* insert new ones above this line */ AFTER_LAST_ERR_CODE -- cgit v1.2.3 From c5a91619793d444e5103ec5841045bf878718398 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Tue, 25 Jan 2011 17:33:38 +0100 Subject: drbd: Remove unused function atodb_endio() Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_actlog.c | 27 --------------------------- drivers/block/drbd/drbd_worker.c | 15 ++++++--------- include/linux/drbd.h | 2 +- 3 files changed, 7 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c index a6050791401b..2a1642bc451d 100644 --- a/drivers/block/drbd/drbd_actlog.c +++ b/drivers/block/drbd/drbd_actlog.c @@ -542,33 +542,6 @@ cancel: return 1; } -static void atodb_endio(struct bio *bio, int error) -{ - struct drbd_atodb_wait *wc = bio->bi_private; - struct drbd_conf *mdev = wc->mdev; - struct page *page; - int uptodate = bio_flagged(bio, BIO_UPTODATE); - - /* strange behavior of some lower level drivers... - * fail the request by clearing the uptodate flag, - * but do not return any error?! */ - if (!error && !uptodate) - error = -EIO; - - drbd_chk_io_error(mdev, error, true); - if (error && wc->error == 0) - wc->error = error; - - if (atomic_dec_and_test(&wc->count)) - complete(&wc->io_done); - - page = bio->bi_io_vec[0].bv_page; - put_page(page); - bio_put(bio); - mdev->bm_writ_cnt++; - put_ldev(mdev); -} - /** * drbd_al_apply_to_bm() - Sets the bitmap to diry(1) where covered ba active AL extents * @mdev: DRBD device. diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c index cfd324b9f95b..3d70d8d015d9 100644 --- a/drivers/block/drbd/drbd_worker.c +++ b/drivers/block/drbd/drbd_worker.c @@ -44,15 +44,12 @@ static int w_make_resync_request(struct drbd_conf *mdev, -/* defined here: - drbd_md_io_complete - drbd_endio_sec - drbd_endio_pri - - * more endio handlers: - atodb_endio in drbd_actlog.c - drbd_bm_async_io_complete in drbd_bitmap.c - +/* endio handlers: + * drbd_md_io_complete (defined here) + * drbd_endio_pri (defined here) + * drbd_endio_sec (defined here) + * bm_async_io_complete (defined in drbd_bitmap.c) + * * For all these callbacks, note the following: * The callbacks will be called in irq context by the IDE drivers, * and in Softirqs/Tasklets/BH context by the SCSI drivers. diff --git a/include/linux/drbd.h b/include/linux/drbd.h index ba5c785d3f7d..d18d673ebc78 100644 --- a/include/linux/drbd.h +++ b/include/linux/drbd.h @@ -53,7 +53,7 @@ extern const char *drbd_buildtag(void); -#define REL_VERSION "8.3.9" +#define REL_VERSION "8.3.10" #define API_VERSION 88 #define PRO_VERSION_MIN 86 #define PRO_VERSION_MAX 96 -- cgit v1.2.3 From 3b0fd9d75598584478d1d3f6551f8a8a9696c34e Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 16 Feb 2011 03:49:01 +0000 Subject: fbdev: sh_mobile_lcdcfb: add backlight support Support for backlight devices controlled through board-specific routines. Backlights can be defined per-channel and follow fbdev directives to switch off as the LCD blanks or is turned on/off. Signed-off-by: Alexandre Courbot Signed-off-by: Paul Mundt --- drivers/video/Kconfig | 1 + drivers/video/sh_mobile_lcdcfb.c | 81 ++++++++++++++++++++++++++++++++++++++++ drivers/video/sh_mobile_lcdcfb.h | 2 + include/video/sh_mobile_lcdc.h | 9 +++++ 4 files changed, 93 insertions(+) (limited to 'include') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 6bafb51bb437..a222d65ae642 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2005,6 +2005,7 @@ config FB_SH_MOBILE_LCDC select FB_SYS_IMAGEBLIT select FB_SYS_FOPS select FB_DEFERRED_IO + select FB_BACKLIGHT select SH_MIPI_DSI if SH_LCD_MIPI_DSI ---help--- Frame buffer driver for the on-chip SH-Mobile LCD controller. diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index bf12e53aed5c..e040e46d7d91 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include