From d0959024d8fb6555ba8bfdc6624cc7b7c2e675fd Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Wed, 20 Oct 2010 15:57:30 -0700 Subject: timer_list: Remove alignment padding on 64 bit when CONFIG_TIMER_STATS Reorder struct timer_list to remove 8 bytes of alignment padding on 64 bit builds when CONFIG_TIMER_STATS is selected. timer_list is widely used across the kernel so many structures will benefit and shrink in size. For example, with my config on x86_64 per_cpu_dm_data shrinks from 136 to 128 bytes and ahci_port_priv shrinks from 1032 to 968 bytes. Signed-off-by: Richard Kennedy Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Thomas Gleixner --- include/linux/timer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/timer.h b/include/linux/timer.h index 38cf093ef62c..f3dccdb44f95 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -24,9 +24,9 @@ struct timer_list { int slack; #ifdef CONFIG_TIMER_STATS + int start_pid; void *start_site; char start_comm[16]; - int start_pid; #endif #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; -- cgit v1.2.3 From aaabe31c25a439b92cc281b14ca18b85bae7e7a6 Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Wed, 20 Oct 2010 15:57:30 -0700 Subject: timer: Initialize the field slack of timer_list TIMER_INITIALIZER() should initialize the field slack of timer_list as __init_timer() does. Signed-off-by: Changli Gao Cc: Arjan van de Ven Signed-off-by: Andrew Morton Signed-off-by: Thomas Gleixner --- include/linux/timer.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/timer.h b/include/linux/timer.h index f3dccdb44f95..1794674c1a52 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -54,6 +54,7 @@ extern struct tvec_base boot_tvec_bases; .expires = (_expires), \ .data = (_data), \ .base = &boot_tvec_bases, \ + .slack = -1, \ __TIMER_LOCKDEP_MAP_INITIALIZER( \ __FILE__ ":" __stringify(__LINE__)) \ } -- cgit v1.2.3 From dd6414b50fa2b1cd247a8aa8f8bd42414b7453e1 Mon Sep 17 00:00:00 2001 From: Phil Carmody Date: Wed, 20 Oct 2010 15:57:33 -0700 Subject: timer: Permit statically-declared work with deferrable timers Currently, you have to just define a delayed_work uninitialised, and then initialise it before first use. That's a tad clumsy. At risk of playing mind-games with the compiler, fooling it into doing pointer arithmetic with compile-time-constants, this lets clients properly initialise delayed work with deferrable timers statically. This patch was inspired by the issues which lead Artem Bityutskiy to commit 8eab945c5616fc984 ("sunrpc: make the cache cleaner workqueue deferrable"). Signed-off-by: Phil Carmody Acked-by: Artem Bityutskiy Cc: Arjan van de Ven Signed-off-by: Andrew Morton Signed-off-by: Thomas Gleixner --- include/linux/timer.h | 25 +++++++++++++++++++++++++ include/linux/workqueue.h | 8 ++++++++ kernel/timer.c | 15 +-------------- 3 files changed, 34 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/timer.h b/include/linux/timer.h index 1794674c1a52..cbfb7a355d30 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -48,6 +48,18 @@ extern struct tvec_base boot_tvec_bases; #define __TIMER_LOCKDEP_MAP_INITIALIZER(_kn) #endif +/* + * Note that all tvec_bases are 2 byte aligned and lower bit of + * base in timer_list is guaranteed to be zero. Use the LSB to + * indicate whether the timer is deferrable. + * + * A deferrable timer will work normally when the system is busy, but + * will not cause a CPU to come out of idle just to service it; instead, + * the timer will be serviced when the CPU eventually wakes up with a + * subsequent non-deferrable timer. + */ +#define TBASE_DEFERRABLE_FLAG (0x1) + #define TIMER_INITIALIZER(_function, _expires, _data) { \ .entry = { .prev = TIMER_ENTRY_STATIC }, \ .function = (_function), \ @@ -59,6 +71,19 @@ extern struct tvec_base boot_tvec_bases; __FILE__ ":" __stringify(__LINE__)) \ } +#define TBASE_MAKE_DEFERRED(ptr) ((struct tvec_base *) \ + ((unsigned char *)(ptr) + TBASE_DEFERRABLE_FLAG)) + +#define TIMER_DEFERRED_INITIALIZER(_function, _expires, _data) {\ + .entry = { .prev = TIMER_ENTRY_STATIC }, \ + .function = (_function), \ + .expires = (_expires), \ + .data = (_data), \ + .base = TBASE_MAKE_DEFERRED(&boot_tvec_bases), \ + __TIMER_LOCKDEP_MAP_INITIALIZER( \ + __FILE__ ":" __stringify(__LINE__)) \ + } + #define DEFINE_TIMER(_name, _function, _expires, _data) \ struct timer_list _name = \ TIMER_INITIALIZER(_function, _expires, _data) diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index f11100f96482..88238c15ec3e 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -127,12 +127,20 @@ struct execute_work { .timer = TIMER_INITIALIZER(NULL, 0, 0), \ } +#define __DEFERRED_WORK_INITIALIZER(n, f) { \ + .work = __WORK_INITIALIZER((n).work, (f)), \ + .timer = TIMER_DEFERRED_INITIALIZER(NULL, 0, 0), \ + } + #define DECLARE_WORK(n, f) \ struct work_struct n = __WORK_INITIALIZER(n, f) #define DECLARE_DELAYED_WORK(n, f) \ struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f) +#define DECLARE_DEFERRED_WORK(n, f) \ + struct delayed_work n = __DEFERRED_WORK_INITIALIZER(n, f) + /* * initialize a work item's function pointer */ diff --git a/kernel/timer.c b/kernel/timer.c index 97bf05baade7..72853b256ff2 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -88,18 +88,6 @@ struct tvec_base boot_tvec_bases; EXPORT_SYMBOL(boot_tvec_bases); static DEFINE_PER_CPU(struct tvec_base *, tvec_bases) = &boot_tvec_bases; -/* - * Note that all tvec_bases are 2 byte aligned and lower bit of - * base in timer_list is guaranteed to be zero. Use the LSB to - * indicate whether the timer is deferrable. - * - * A deferrable timer will work normally when the system is busy, but - * will not cause a CPU to come out of idle just to service it; instead, - * the timer will be serviced when the CPU eventually wakes up with a - * subsequent non-deferrable timer. - */ -#define TBASE_DEFERRABLE_FLAG (0x1) - /* Functions below help us manage 'deferrable' flag */ static inline unsigned int tbase_get_deferrable(struct tvec_base *base) { @@ -113,8 +101,7 @@ static inline struct tvec_base *tbase_get_base(struct tvec_base *base) static inline void timer_set_deferrable(struct timer_list *timer) { - timer->base = ((struct tvec_base *)((unsigned long)(timer->base) | - TBASE_DEFERRABLE_FLAG)); + timer->base = TBASE_MAKE_DEFERRED(timer->base); } static inline void -- cgit v1.2.3 From 6f1bc451e6a79470b122a37ee1fc6bbca450f444 Mon Sep 17 00:00:00 2001 From: Yong Zhang Date: Wed, 20 Oct 2010 15:57:31 -0700 Subject: timer: Make try_to_del_timer_sync() the same on SMP and UP On UP try_to_del_timer_sync() is mapped to del_timer() which does not take the running timer callback into account, so it has different semantics. Remove the SMP dependency of try_to_del_timer_sync() by using base->running_timer in the UP case as well. [ tglx: Removed set_running_timer() inline and tweaked the changelog ] Signed-off-by: Yong Zhang Cc: Ingo Molnar Cc: Peter Zijlstra Acked-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Thomas Gleixner --- include/linux/timer.h | 4 ++-- kernel/timer.c | 17 +++-------------- 2 files changed, 5 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/timer.h b/include/linux/timer.h index cbfb7a355d30..6abd9138beda 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -274,11 +274,11 @@ static inline void timer_stats_timer_clear_start_info(struct timer_list *timer) extern void add_timer(struct timer_list *timer); +extern int try_to_del_timer_sync(struct timer_list *timer); + #ifdef CONFIG_SMP - extern int try_to_del_timer_sync(struct timer_list *timer); extern int del_timer_sync(struct timer_list *timer); #else -# define try_to_del_timer_sync(t) del_timer(t) # define del_timer_sync(t) del_timer(t) #endif diff --git a/kernel/timer.c b/kernel/timer.c index 72853b256ff2..47b86c1e3226 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -330,15 +330,6 @@ void set_timer_slack(struct timer_list *timer, int slack_hz) } EXPORT_SYMBOL_GPL(set_timer_slack); - -static inline void set_running_timer(struct tvec_base *base, - struct timer_list *timer) -{ -#ifdef CONFIG_SMP - base->running_timer = timer; -#endif -} - static void internal_add_timer(struct tvec_base *base, struct timer_list *timer) { unsigned long expires = timer->expires; @@ -923,15 +914,12 @@ int del_timer(struct timer_list *timer) } EXPORT_SYMBOL(del_timer); -#ifdef CONFIG_SMP /** * try_to_del_timer_sync - Try to deactivate a timer * @timer: timer do del * * This function tries to deactivate a timer. Upon successful (ret >= 0) * exit the timer is not queued and the handler is not running on any CPU. - * - * It must not be called from interrupt contexts. */ int try_to_del_timer_sync(struct timer_list *timer) { @@ -960,6 +948,7 @@ out: } EXPORT_SYMBOL(try_to_del_timer_sync); +#ifdef CONFIG_SMP /** * del_timer_sync - deactivate a timer and wait for the handler to finish. * @timer: the timer to be deactivated @@ -1098,7 +1087,7 @@ static inline void __run_timers(struct tvec_base *base) timer_stats_account_timer(timer); - set_running_timer(base, timer); + base->running_timer = timer; detach_timer(timer, 1); spin_unlock_irq(&base->lock); @@ -1106,7 +1095,7 @@ static inline void __run_timers(struct tvec_base *base) spin_lock_irq(&base->lock); } } - set_running_timer(base, NULL); + base->running_timer = NULL; spin_unlock_irq(&base->lock); } -- cgit v1.2.3 From fe7de49f9d4e53f24ec9ef762a503f70b562341c Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Wed, 20 Oct 2010 16:01:12 -0700 Subject: sched: Make sched_param argument static in sched_setscheduler() callers Andrew Morton pointed out almost all sched_setscheduler() callers are using fixed parameters and can be converted to static. It reduces runtime memory use a little. Signed-off-by: KOSAKI Motohiro Reported-by: Andrew Morton Acked-by: James Morris Cc: Ingo Molnar Cc: Steven Rostedt Signed-off-by: Andrew Morton Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar --- include/linux/sched.h | 5 +++-- kernel/irq/manage.c | 4 +++- kernel/kthread.c | 2 +- kernel/sched.c | 6 +++--- kernel/softirq.c | 4 +++- kernel/trace/trace_selftest.c | 2 +- kernel/watchdog.c | 2 +- 7 files changed, 15 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 0383601a927c..849c8670583d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1942,9 +1942,10 @@ extern int task_nice(const struct task_struct *p); extern int can_nice(const struct task_struct *p, const int nice); extern int task_curr(const struct task_struct *p); extern int idle_cpu(int cpu); -extern int sched_setscheduler(struct task_struct *, int, struct sched_param *); +extern int sched_setscheduler(struct task_struct *, int, + const struct sched_param *); extern int sched_setscheduler_nocheck(struct task_struct *, int, - struct sched_param *); + const struct sched_param *); extern struct task_struct *idle_task(int cpu); extern struct task_struct *curr_task(int cpu); extern void set_curr_task(int cpu, struct task_struct *p); diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 644e8d5fa367..850f030fa0c2 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -573,7 +573,9 @@ irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action) { } */ static int irq_thread(void *data) { - struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, }; + static struct sched_param param = { + .sched_priority = MAX_USER_RT_PRIO/2, + }; struct irqaction *action = data; struct irq_desc *desc = irq_to_desc(action->irq); int wake, oneshot = desc->status & IRQ_ONESHOT; diff --git a/kernel/kthread.c b/kernel/kthread.c index 2dc3786349d1..74cf6f5e7ade 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -148,7 +148,7 @@ struct task_struct *kthread_create(int (*threadfn)(void *data), wait_for_completion(&create.done); if (!IS_ERR(create.result)) { - struct sched_param param = { .sched_priority = 0 }; + static struct sched_param param = { .sched_priority = 0 }; va_list args; va_start(args, namefmt); diff --git a/kernel/sched.c b/kernel/sched.c index d42992bccdfa..51944e8c38a8 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4701,7 +4701,7 @@ static bool check_same_owner(struct task_struct *p) } static int __sched_setscheduler(struct task_struct *p, int policy, - struct sched_param *param, bool user) + const struct sched_param *param, bool user) { int retval, oldprio, oldpolicy = -1, on_rq, running; unsigned long flags; @@ -4856,7 +4856,7 @@ recheck: * NOTE that the task may be already dead. */ int sched_setscheduler(struct task_struct *p, int policy, - struct sched_param *param) + const struct sched_param *param) { return __sched_setscheduler(p, policy, param, true); } @@ -4874,7 +4874,7 @@ EXPORT_SYMBOL_GPL(sched_setscheduler); * but our caller might not have that capability. */ int sched_setscheduler_nocheck(struct task_struct *p, int policy, - struct sched_param *param) + const struct sched_param *param) { return __sched_setscheduler(p, policy, param, false); } diff --git a/kernel/softirq.c b/kernel/softirq.c index fc978889b194..081869ed3a9f 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -851,7 +851,9 @@ static int __cpuinit cpu_callback(struct notifier_block *nfb, cpumask_any(cpu_online_mask)); case CPU_DEAD: case CPU_DEAD_FROZEN: { - struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; + static struct sched_param param = { + .sched_priority = MAX_RT_PRIO-1 + }; p = per_cpu(ksoftirqd, hotcpu); per_cpu(ksoftirqd, hotcpu) = NULL; diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index 155a415b3209..562c56e048fd 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -558,7 +558,7 @@ trace_selftest_startup_nop(struct tracer *trace, struct trace_array *tr) static int trace_wakeup_test_thread(void *data) { /* Make this a RT thread, doesn't need to be too high */ - struct sched_param param = { .sched_priority = 5 }; + static struct sched_param param = { .sched_priority = 5 }; struct completion *x = data; sched_setscheduler(current, SCHED_FIFO, ¶m); diff --git a/kernel/watchdog.c b/kernel/watchdog.c index bafba687a6d8..94ca779aa9c2 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -307,7 +307,7 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) */ static int watchdog(void *unused) { - struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; + static struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; struct hrtimer *hrtimer = &__raw_get_cpu_var(watchdog_hrtimer); sched_setscheduler(current, SCHED_FIFO, ¶m); -- cgit v1.2.3 From fc766e4c4965915ab52a1d1fa3c7a7b3e7bc07f0 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 29 Oct 2010 03:09:24 +0000 Subject: decnet: RCU conversion and get rid of dev_base_lock While tracking dev_base_lock users, I found decnet used it in dnet_select_source(), but for a wrong purpose: Writers only hold RTNL, not dev_base_lock, so readers must use RCU if they cannot use RTNL. Adds an rcu_head in struct dn_ifaddr and handle proper RCU management. Adds __rcu annotation in dn_route as well. Signed-off-by: Eric Dumazet Acked-by: Steven Whitehouse Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 +- include/net/dn_dev.h | 27 ++++++++----- include/net/dst.h | 8 ++-- net/decnet/af_decnet.c | 2 +- net/decnet/dn_dev.c | 100 +++++++++++++++++++++++++++------------------- net/decnet/dn_fib.c | 6 ++- net/decnet/dn_neigh.c | 2 +- net/decnet/dn_route.c | 68 +++++++++++++++++-------------- 8 files changed, 127 insertions(+), 88 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d8fd2c23a1b9..578debb801f4 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -951,7 +951,7 @@ struct net_device { #endif void *atalk_ptr; /* AppleTalk link */ struct in_device __rcu *ip_ptr; /* IPv4 specific data */ - void *dn_ptr; /* DECnet specific data */ + struct dn_dev __rcu *dn_ptr; /* DECnet specific data */ struct inet6_dev __rcu *ip6_ptr; /* IPv6 specific data */ void *ec_ptr; /* Econet specific data */ void *ax25_ptr; /* AX.25 specific data */ diff --git a/include/net/dn_dev.h b/include/net/dn_dev.h index 0916bbf3bdff..b9e32db03f20 100644 --- a/include/net/dn_dev.h +++ b/include/net/dn_dev.h @@ -5,13 +5,14 @@ struct dn_dev; struct dn_ifaddr { - struct dn_ifaddr *ifa_next; + struct dn_ifaddr __rcu *ifa_next; struct dn_dev *ifa_dev; __le16 ifa_local; __le16 ifa_address; __u8 ifa_flags; __u8 ifa_scope; char ifa_label[IFNAMSIZ]; + struct rcu_head rcu; }; #define DN_DEV_S_RU 0 /* Run - working normally */ @@ -83,7 +84,7 @@ struct dn_dev_parms { struct dn_dev { - struct dn_ifaddr *ifa_list; + struct dn_ifaddr __rcu *ifa_list; struct net_device *dev; struct dn_dev_parms parms; char use_long; @@ -171,19 +172,27 @@ extern int unregister_dnaddr_notifier(struct notifier_block *nb); static inline int dn_dev_islocal(struct net_device *dev, __le16 addr) { - struct dn_dev *dn_db = dev->dn_ptr; + struct dn_dev *dn_db; struct dn_ifaddr *ifa; + int res = 0; + rcu_read_lock(); + dn_db = rcu_dereference(dev->dn_ptr); if (dn_db == NULL) { printk(KERN_DEBUG "dn_dev_islocal: Called for non DECnet device\n"); - return 0; + goto out; } - for(ifa = dn_db->ifa_list; ifa; ifa = ifa->ifa_next) - if ((addr ^ ifa->ifa_local) == 0) - return 1; - - return 0; + for (ifa = rcu_dereference(dn_db->ifa_list); + ifa != NULL; + ifa = rcu_dereference(ifa->ifa_next)) + if ((addr ^ ifa->ifa_local) == 0) { + res = 1; + break; + } +out: + rcu_read_unlock(); + return res; } #endif /* _NET_DN_DEV_H */ diff --git a/include/net/dst.h b/include/net/dst.h index ffe9cb719c0e..a5bd72646d65 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -94,10 +94,10 @@ struct dst_entry { int __use; unsigned long lastuse; union { - struct dst_entry *next; - struct rtable __rcu *rt_next; - struct rt6_info *rt6_next; - struct dn_route *dn_next; + struct dst_entry *next; + struct rtable __rcu *rt_next; + struct rt6_info *rt6_next; + struct dn_route __rcu *dn_next; }; }; diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index d6b93d19790f..18b8a2cbdf77 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -1848,7 +1848,7 @@ unsigned dn_mss_from_pmtu(struct net_device *dev, int mtu) { unsigned mss = 230 - DN_MAX_NSP_DATA_HEADER; if (dev) { - struct dn_dev *dn_db = dev->dn_ptr; + struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr); mtu -= LL_RESERVED_SPACE(dev); if (dn_db->use_long) mtu -= 21; diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index 4c409b46aa35..0ba15633c418 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -267,7 +267,7 @@ static int dn_forwarding_proc(ctl_table *table, int write, if (table->extra1 == NULL) return -EINVAL; - dn_db = dev->dn_ptr; + dn_db = rcu_dereference_raw(dev->dn_ptr); old = dn_db->parms.forwarding; err = proc_dointvec(table, write, buffer, lenp, ppos); @@ -332,14 +332,19 @@ static struct dn_ifaddr *dn_dev_alloc_ifa(void) return ifa; } -static __inline__ void dn_dev_free_ifa(struct dn_ifaddr *ifa) +static void dn_dev_free_ifa_rcu(struct rcu_head *head) { - kfree(ifa); + kfree(container_of(head, struct dn_ifaddr, rcu)); } -static void dn_dev_del_ifa(struct dn_dev *dn_db, struct dn_ifaddr **ifap, int destroy) +static void dn_dev_free_ifa(struct dn_ifaddr *ifa) { - struct dn_ifaddr *ifa1 = *ifap; + call_rcu(&ifa->rcu, dn_dev_free_ifa_rcu); +} + +static void dn_dev_del_ifa(struct dn_dev *dn_db, struct dn_ifaddr __rcu **ifap, int destroy) +{ + struct dn_ifaddr *ifa1 = rtnl_dereference(*ifap); unsigned char mac_addr[6]; struct net_device *dev = dn_db->dev; @@ -373,7 +378,9 @@ static int dn_dev_insert_ifa(struct dn_dev *dn_db, struct dn_ifaddr *ifa) ASSERT_RTNL(); /* Check for duplicates */ - for(ifa1 = dn_db->ifa_list; ifa1; ifa1 = ifa1->ifa_next) { + for (ifa1 = rtnl_dereference(dn_db->ifa_list); + ifa1 != NULL; + ifa1 = rtnl_dereference(ifa1->ifa_next)) { if (ifa1->ifa_local == ifa->ifa_local) return -EEXIST; } @@ -386,7 +393,7 @@ static int dn_dev_insert_ifa(struct dn_dev *dn_db, struct dn_ifaddr *ifa) } ifa->ifa_next = dn_db->ifa_list; - dn_db->ifa_list = ifa; + rcu_assign_pointer(dn_db->ifa_list, ifa); dn_ifaddr_notify(RTM_NEWADDR, ifa); blocking_notifier_call_chain(&dnaddr_chain, NETDEV_UP, ifa); @@ -396,7 +403,7 @@ static int dn_dev_insert_ifa(struct dn_dev *dn_db, struct dn_ifaddr *ifa) static int dn_dev_set_ifa(struct net_device *dev, struct dn_ifaddr *ifa) { - struct dn_dev *dn_db = dev->dn_ptr; + struct dn_dev *dn_db = rtnl_dereference(dev->dn_ptr); int rv; if (dn_db == NULL) { @@ -425,7 +432,8 @@ int dn_dev_ioctl(unsigned int cmd, void __user *arg) struct sockaddr_dn *sdn = (struct sockaddr_dn *)&ifr->ifr_addr; struct dn_dev *dn_db; struct net_device *dev; - struct dn_ifaddr *ifa = NULL, **ifap = NULL; + struct dn_ifaddr *ifa = NULL; + struct dn_ifaddr __rcu **ifap = NULL; int ret = 0; if (copy_from_user(ifr, arg, DN_IFREQ_SIZE)) @@ -454,8 +462,10 @@ int dn_dev_ioctl(unsigned int cmd, void __user *arg) goto done; } - if ((dn_db = dev->dn_ptr) != NULL) { - for (ifap = &dn_db->ifa_list; (ifa=*ifap) != NULL; ifap = &ifa->ifa_next) + if ((dn_db = rtnl_dereference(dev->dn_ptr)) != NULL) { + for (ifap = &dn_db->ifa_list; + (ifa = rtnl_dereference(*ifap)) != NULL; + ifap = &ifa->ifa_next) if (strcmp(ifr->ifr_name, ifa->ifa_label) == 0) break; } @@ -558,7 +568,7 @@ static struct dn_dev *dn_dev_by_index(int ifindex) dev = __dev_get_by_index(&init_net, ifindex); if (dev) - dn_dev = dev->dn_ptr; + dn_dev = rtnl_dereference(dev->dn_ptr); return dn_dev; } @@ -576,7 +586,8 @@ static int dn_nl_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) struct nlattr *tb[IFA_MAX+1]; struct dn_dev *dn_db; struct ifaddrmsg *ifm; - struct dn_ifaddr *ifa, **ifap; + struct dn_ifaddr *ifa; + struct dn_ifaddr __rcu **ifap; int err = -EINVAL; if (!net_eq(net, &init_net)) @@ -592,7 +603,9 @@ static int dn_nl_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) goto errout; err = -EADDRNOTAVAIL; - for (ifap = &dn_db->ifa_list; (ifa = *ifap); ifap = &ifa->ifa_next) { + for (ifap = &dn_db->ifa_list; + (ifa = rtnl_dereference(*ifap)) != NULL; + ifap = &ifa->ifa_next) { if (tb[IFA_LOCAL] && nla_memcmp(tb[IFA_LOCAL], &ifa->ifa_local, 2)) continue; @@ -632,7 +645,7 @@ static int dn_nl_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) if ((dev = __dev_get_by_index(&init_net, ifm->ifa_index)) == NULL) return -ENODEV; - if ((dn_db = dev->dn_ptr) == NULL) { + if ((dn_db = rtnl_dereference(dev->dn_ptr)) == NULL) { dn_db = dn_dev_create(dev, &err); if (!dn_db) return err; @@ -748,11 +761,11 @@ static int dn_nl_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) skip_naddr = 0; } - if ((dn_db = dev->dn_ptr) == NULL) + if ((dn_db = rtnl_dereference(dev->dn_ptr)) == NULL) goto cont; - for (ifa = dn_db->ifa_list, dn_idx = 0; ifa; - ifa = ifa->ifa_next, dn_idx++) { + for (ifa = rtnl_dereference(dn_db->ifa_list), dn_idx = 0; ifa; + ifa = rtnl_dereference(ifa->ifa_next), dn_idx++) { if (dn_idx < skip_naddr) continue; @@ -773,21 +786,22 @@ done: static int dn_dev_get_first(struct net_device *dev, __le16 *addr) { - struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr; + struct dn_dev *dn_db; struct dn_ifaddr *ifa; int rv = -ENODEV; + rcu_read_lock(); + dn_db = rcu_dereference(dev->dn_ptr); if (dn_db == NULL) goto out; - rtnl_lock(); - ifa = dn_db->ifa_list; + ifa = rcu_dereference(dn_db->ifa_list); if (ifa != NULL) { *addr = ifa->ifa_local; rv = 0; } - rtnl_unlock(); out: + rcu_read_unlock(); return rv; } @@ -823,7 +837,7 @@ static void dn_send_endnode_hello(struct net_device *dev, struct dn_ifaddr *ifa) struct endnode_hello_message *msg; struct sk_buff *skb = NULL; __le16 *pktlen; - struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr; + struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr); if ((skb = dn_alloc_skb(NULL, sizeof(*msg), GFP_ATOMIC)) == NULL) return; @@ -889,7 +903,7 @@ static int dn_am_i_a_router(struct dn_neigh *dn, struct dn_dev *dn_db, struct dn static void dn_send_router_hello(struct net_device *dev, struct dn_ifaddr *ifa) { int n; - struct dn_dev *dn_db = dev->dn_ptr; + struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr); struct dn_neigh *dn = (struct dn_neigh *)dn_db->router; struct sk_buff *skb; size_t size; @@ -960,7 +974,7 @@ static void dn_send_router_hello(struct net_device *dev, struct dn_ifaddr *ifa) static void dn_send_brd_hello(struct net_device *dev, struct dn_ifaddr *ifa) { - struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr; + struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr); if (dn_db->parms.forwarding == 0) dn_send_endnode_hello(dev, ifa); @@ -998,7 +1012,7 @@ static void dn_send_ptp_hello(struct net_device *dev, struct dn_ifaddr *ifa) static int dn_eth_up(struct net_device *dev) { - struct dn_dev *dn_db = dev->dn_ptr; + struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr); if (dn_db->parms.forwarding == 0) dev_mc_add(dev, dn_rt_all_end_mcast); @@ -1012,7 +1026,7 @@ static int dn_eth_up(struct net_device *dev) static void dn_eth_down(struct net_device *dev) { - struct dn_dev *dn_db = dev->dn_ptr; + struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr); if (dn_db->parms.forwarding == 0) dev_mc_del(dev, dn_rt_all_end_mcast); @@ -1025,12 +1039,16 @@ static void dn_dev_set_timer(struct net_device *dev); static void dn_dev_timer_func(unsigned long arg) { struct net_device *dev = (struct net_device *)arg; - struct dn_dev *dn_db = dev->dn_ptr; + struct dn_dev *dn_db; struct dn_ifaddr *ifa; + rcu_read_lock(); + dn_db = rcu_dereference(dev->dn_ptr); if (dn_db->t3 <= dn_db->parms.t2) { if (dn_db->parms.timer3) { - for(ifa = dn_db->ifa_list; ifa; ifa = ifa->ifa_next) { + for (ifa = rcu_dereference(dn_db->ifa_list); + ifa; + ifa = rcu_dereference(ifa->ifa_next)) { if (!(ifa->ifa_flags & IFA_F_SECONDARY)) dn_db->parms.timer3(dev, ifa); } @@ -1039,13 +1057,13 @@ static void dn_dev_timer_func(unsigned long arg) } else { dn_db->t3 -= dn_db->parms.t2; } - + rcu_read_unlock(); dn_dev_set_timer(dev); } static void dn_dev_set_timer(struct net_device *dev) { - struct dn_dev *dn_db = dev->dn_ptr; + struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr); if (dn_db->parms.t2 > dn_db->parms.t3) dn_db->parms.t2 = dn_db->parms.t3; @@ -1077,8 +1095,8 @@ static struct dn_dev *dn_dev_create(struct net_device *dev, int *err) return NULL; memcpy(&dn_db->parms, p, sizeof(struct dn_dev_parms)); - smp_wmb(); - dev->dn_ptr = dn_db; + + rcu_assign_pointer(dev->dn_ptr, dn_db); dn_db->dev = dev; init_timer(&dn_db->timer); @@ -1086,7 +1104,7 @@ static struct dn_dev *dn_dev_create(struct net_device *dev, int *err) dn_db->neigh_parms = neigh_parms_alloc(dev, &dn_neigh_table); if (!dn_db->neigh_parms) { - dev->dn_ptr = NULL; + rcu_assign_pointer(dev->dn_ptr, NULL); kfree(dn_db); return NULL; } @@ -1125,7 +1143,7 @@ void dn_dev_up(struct net_device *dev) struct dn_ifaddr *ifa; __le16 addr = decnet_address; int maybe_default = 0; - struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr; + struct dn_dev *dn_db = rtnl_dereference(dev->dn_ptr); if ((dev->type != ARPHRD_ETHER) && (dev->type != ARPHRD_LOOPBACK)) return; @@ -1176,7 +1194,7 @@ void dn_dev_up(struct net_device *dev) static void dn_dev_delete(struct net_device *dev) { - struct dn_dev *dn_db = dev->dn_ptr; + struct dn_dev *dn_db = rtnl_dereference(dev->dn_ptr); if (dn_db == NULL) return; @@ -1204,13 +1222,13 @@ static void dn_dev_delete(struct net_device *dev) void dn_dev_down(struct net_device *dev) { - struct dn_dev *dn_db = dev->dn_ptr; + struct dn_dev *dn_db = rtnl_dereference(dev->dn_ptr); struct dn_ifaddr *ifa; if (dn_db == NULL) return; - while((ifa = dn_db->ifa_list) != NULL) { + while ((ifa = rtnl_dereference(dn_db->ifa_list)) != NULL) { dn_dev_del_ifa(dn_db, &dn_db->ifa_list, 0); dn_dev_free_ifa(ifa); } @@ -1270,7 +1288,7 @@ static inline int is_dn_dev(struct net_device *dev) } static void *dn_dev_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(rcu) + __acquires(RCU) { int i; struct net_device *dev; @@ -1313,7 +1331,7 @@ static void *dn_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) } static void dn_dev_seq_stop(struct seq_file *seq, void *v) - __releases(rcu) + __releases(RCU) { rcu_read_unlock(); } @@ -1340,7 +1358,7 @@ static int dn_dev_seq_show(struct seq_file *seq, void *v) struct net_device *dev = v; char peer_buf[DN_ASCBUF_LEN]; char router_buf[DN_ASCBUF_LEN]; - struct dn_dev *dn_db = dev->dn_ptr; + struct dn_dev *dn_db = rcu_dereference(dev->dn_ptr); seq_printf(seq, "%-8s %1s %04u %04u %04lu %04lu" " %04hu %03d %02x %-10s %-7s %-7s\n", diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c index 4ab96c15166d..0ef0a81bcd72 100644 --- a/net/decnet/dn_fib.c +++ b/net/decnet/dn_fib.c @@ -610,10 +610,12 @@ static void dn_fib_del_ifaddr(struct dn_ifaddr *ifa) /* Scan device list */ rcu_read_lock(); for_each_netdev_rcu(&init_net, dev) { - dn_db = dev->dn_ptr; + dn_db = rcu_dereference(dev->dn_ptr); if (dn_db == NULL) continue; - for(ifa2 = dn_db->ifa_list; ifa2; ifa2 = ifa2->ifa_next) { + for (ifa2 = rcu_dereference(dn_db->ifa_list); + ifa2 != NULL; + ifa2 = rcu_dereference(ifa2->ifa_next)) { if (ifa2->ifa_local == ifa->ifa_local) { found_it = 1; break; diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index a085dbcf5c7f..602dade7e9a3 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -391,7 +391,7 @@ int dn_neigh_router_hello(struct sk_buff *skb) write_lock(&neigh->lock); neigh->used = jiffies; - dn_db = (struct dn_dev *)neigh->dev->dn_ptr; + dn_db = rcu_dereference(neigh->dev->dn_ptr); if (!(neigh->nud_state & NUD_PERMANENT)) { neigh->updated = jiffies; diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index df0f3e54ff8a..94a9eb1d313e 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -93,7 +93,7 @@ struct dn_rt_hash_bucket { - struct dn_route *chain; + struct dn_route __rcu *chain; spinlock_t lock; }; @@ -157,15 +157,17 @@ static inline void dnrt_drop(struct dn_route *rt) static void dn_dst_check_expire(unsigned long dummy) { int i; - struct dn_route *rt, **rtp; + struct dn_route *rt; + struct dn_route __rcu **rtp; unsigned long now = jiffies; unsigned long expire = 120 * HZ; - for(i = 0; i <= dn_rt_hash_mask; i++) { + for (i = 0; i <= dn_rt_hash_mask; i++) { rtp = &dn_rt_hash_table[i].chain; spin_lock(&dn_rt_hash_table[i].lock); - while((rt=*rtp) != NULL) { + while ((rt = rcu_dereference_protected(*rtp, + lockdep_is_held(&dn_rt_hash_table[i].lock))) != NULL) { if (atomic_read(&rt->dst.__refcnt) || (now - rt->dst.lastuse) < expire) { rtp = &rt->dst.dn_next; @@ -186,17 +188,19 @@ static void dn_dst_check_expire(unsigned long dummy) static int dn_dst_gc(struct dst_ops *ops) { - struct dn_route *rt, **rtp; + struct dn_route *rt; + struct dn_route __rcu **rtp; int i; unsigned long now = jiffies; unsigned long expire = 10 * HZ; - for(i = 0; i <= dn_rt_hash_mask; i++) { + for (i = 0; i <= dn_rt_hash_mask; i++) { spin_lock_bh(&dn_rt_hash_table[i].lock); rtp = &dn_rt_hash_table[i].chain; - while((rt=*rtp) != NULL) { + while ((rt = rcu_dereference_protected(*rtp, + lockdep_is_held(&dn_rt_hash_table[i].lock))) != NULL) { if (atomic_read(&rt->dst.__refcnt) || (now - rt->dst.lastuse) < expire) { rtp = &rt->dst.dn_next; @@ -227,7 +231,7 @@ static void dn_dst_update_pmtu(struct dst_entry *dst, u32 mtu) { u32 min_mtu = 230; struct dn_dev *dn = dst->neighbour ? - (struct dn_dev *)dst->neighbour->dev->dn_ptr : NULL; + rcu_dereference_raw(dst->neighbour->dev->dn_ptr) : NULL; if (dn && dn->use_long == 0) min_mtu -= 6; @@ -277,13 +281,15 @@ static inline int compare_keys(struct flowi *fl1, struct flowi *fl2) static int dn_insert_route(struct dn_route *rt, unsigned hash, struct dn_route **rp) { - struct dn_route *rth, **rthp; + struct dn_route *rth; + struct dn_route __rcu **rthp; unsigned long now = jiffies; rthp = &dn_rt_hash_table[hash].chain; spin_lock_bh(&dn_rt_hash_table[hash].lock); - while((rth = *rthp) != NULL) { + while ((rth = rcu_dereference_protected(*rthp, + lockdep_is_held(&dn_rt_hash_table[hash].lock))) != NULL) { if (compare_keys(&rth->fl, &rt->fl)) { /* Put it first */ *rthp = rth->dst.dn_next; @@ -315,15 +321,15 @@ static void dn_run_flush(unsigned long dummy) int i; struct dn_route *rt, *next; - for(i = 0; i < dn_rt_hash_mask; i++) { + for (i = 0; i < dn_rt_hash_mask; i++) { spin_lock_bh(&dn_rt_hash_table[i].lock); - if ((rt = xchg(&dn_rt_hash_table[i].chain, NULL)) == NULL) + if ((rt = xchg((struct dn_route **)&dn_rt_hash_table[i].chain, NULL)) == NULL) goto nothing_to_declare; - for(; rt; rt=next) { - next = rt->dst.dn_next; - rt->dst.dn_next = NULL; + for(; rt; rt = next) { + next = rcu_dereference_raw(rt->dst.dn_next); + RCU_INIT_POINTER(rt->dst.dn_next, NULL); dst_free((struct dst_entry *)rt); } @@ -458,15 +464,16 @@ static int dn_return_long(struct sk_buff *skb) */ static int dn_route_rx_packet(struct sk_buff *skb) { - struct dn_skb_cb *cb = DN_SKB_CB(skb); + struct dn_skb_cb *cb; int err; if ((err = dn_route_input(skb)) == 0) return dst_input(skb); + cb = DN_SKB_CB(skb); if (decnet_debug_level & 4) { char *devname = skb->dev ? skb->dev->name : "???"; - struct dn_skb_cb *cb = DN_SKB_CB(skb); + printk(KERN_DEBUG "DECnet: dn_route_rx_packet: rt_flags=0x%02x dev=%s len=%d src=0x%04hx dst=0x%04hx err=%d type=%d\n", (int)cb->rt_flags, devname, skb->len, @@ -573,7 +580,7 @@ int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type struct dn_skb_cb *cb; unsigned char flags = 0; __u16 len = le16_to_cpu(*(__le16 *)skb->data); - struct dn_dev *dn = (struct dn_dev *)dev->dn_ptr; + struct dn_dev *dn = rcu_dereference(dev->dn_ptr); unsigned char padlen = 0; if (!net_eq(dev_net(dev), &init_net)) @@ -728,7 +735,7 @@ static int dn_forward(struct sk_buff *skb) { struct dn_skb_cb *cb = DN_SKB_CB(skb); struct dst_entry *dst = skb_dst(skb); - struct dn_dev *dn_db = dst->dev->dn_ptr; + struct dn_dev *dn_db = rcu_dereference(dst->dev->dn_ptr); struct dn_route *rt; struct neighbour *neigh = dst->neighbour; int header_len; @@ -835,13 +842,16 @@ static inline int dn_match_addr(__le16 addr1, __le16 addr2) static __le16 dnet_select_source(const struct net_device *dev, __le16 daddr, int scope) { __le16 saddr = 0; - struct dn_dev *dn_db = dev->dn_ptr; + struct dn_dev *dn_db; struct dn_ifaddr *ifa; int best_match = 0; int ret; - read_lock(&dev_base_lock); - for(ifa = dn_db->ifa_list; ifa; ifa = ifa->ifa_next) { + rcu_read_lock(); + dn_db = rcu_dereference(dev->dn_ptr); + for (ifa = rcu_dereference(dn_db->ifa_list); + ifa != NULL; + ifa = rcu_dereference(ifa->ifa_next)) { if (ifa->ifa_scope > scope) continue; if (!daddr) { @@ -854,7 +864,7 @@ static __le16 dnet_select_source(const struct net_device *dev, __le16 daddr, int if (best_match == 0) saddr = ifa->ifa_local; } - read_unlock(&dev_base_lock); + rcu_read_unlock(); return saddr; } @@ -1020,7 +1030,7 @@ source_ok: err = -ENODEV; if (dev_out == NULL) goto out; - dn_db = dev_out->dn_ptr; + dn_db = rcu_dereference_raw(dev_out->dn_ptr); /* Possible improvement - check all devices for local addr */ if (dn_dev_islocal(dev_out, fl.fld_dst)) { dev_put(dev_out); @@ -1233,7 +1243,7 @@ static int dn_route_input_slow(struct sk_buff *skb) dev_hold(in_dev); - if ((dn_db = in_dev->dn_ptr) == NULL) + if ((dn_db = rcu_dereference(in_dev->dn_ptr)) == NULL) goto out; /* Zero source addresses are not allowed */ @@ -1677,15 +1687,15 @@ static struct dn_route *dn_rt_cache_get_next(struct seq_file *seq, struct dn_rou { struct dn_rt_cache_iter_state *s = seq->private; - rt = rt->dst.dn_next; - while(!rt) { + rt = rcu_dereference_bh(rt->dst.dn_next); + while (!rt) { rcu_read_unlock_bh(); if (--s->bucket < 0) break; rcu_read_lock_bh(); - rt = dn_rt_hash_table[s->bucket].chain; + rt = rcu_dereference_bh(dn_rt_hash_table[s->bucket].chain); } - return rcu_dereference_bh(rt); + return rt; } static void *dn_rt_cache_seq_start(struct seq_file *seq, loff_t *pos) -- cgit v1.2.3 From 1da4b1c6a4dfb5a13d7147a27c1ac53fed09befd Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Tue, 9 Nov 2010 11:22:58 +0000 Subject: x86/mrst: Add SFI platform device parsing code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SFI provides a series of tables. These describe the platform devices present including SPI and I²C devices, as well as various sensors, keypads and other glue as well as interfaces provided via the SCU IPC mechanism (intel_scu_ipc.c) This patch is a merge of the core elements and relevant fixes from the Intel development code by Feng, Alek, myself into a single coherent patch for upstream submission. It provides the needed infrastructure to register I2C, SPI and platform devices described by the tables, as well as handlers for some of the hardware already supported in kernel. The 0.8 firmware also provides GPIO tables. Devices are created at boot time or if they are SCU dependant at the point an SCU is discovered. The existing Linux device mechanisms will then handle the device binding. At an abstract level this is an SFI to Linux device translator. Device/platform specific setup/glue is in this file. This is done so that the drivers for the generic I²C and SPI bus devices remain cross platform as they should. (Updated from RFC version to correct the emc1403 name used by the firmware and a wrongly used #define) Signed-off-by: Alek Du LKML-Reference: <20101109112158.20013.6158.stgit@localhost.localdomain> [Clean ups, removal of 0.7 support] Signed-off-by: Feng Tang [Clean ups] Signed-off-by: Alan Cox Signed-off-by: Thomas Gleixner --- arch/x86/Kconfig | 2 + arch/x86/include/asm/mrst.h | 4 + arch/x86/platform/mrst/mrst.c | 515 ++++++++++++++++++++++++++++++++++- drivers/platform/x86/intel_scu_ipc.c | 5 + include/linux/sfi.h | 8 +- 5 files changed, 527 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index e8327686d3c5..b306b84fc8c8 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -385,6 +385,8 @@ config X86_MRST depends on X86_EXTENDED_PLATFORM depends on X86_IO_APIC select APB_TIMER + select I2C + select SPI ---help--- Moorestown is Intel's Low Power Intel Architecture (LPIA) based Moblin Internet Device(MID) platform. Moorestown consists of two chips: diff --git a/arch/x86/include/asm/mrst.h b/arch/x86/include/asm/mrst.h index 4a711a684b17..283debd29fc0 100644 --- a/arch/x86/include/asm/mrst.h +++ b/arch/x86/include/asm/mrst.h @@ -50,4 +50,8 @@ extern void mrst_early_console_init(void); extern struct console early_hsu_console; extern void hsu_early_console_init(void); + +extern void intel_scu_devices_create(void); +extern void intel_scu_devices_destroy(void); + #endif /* _ASM_X86_MRST_H */ diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c index 79ae68154e87..cfa1af24edd5 100644 --- a/arch/x86/platform/mrst/mrst.c +++ b/arch/x86/platform/mrst/mrst.c @@ -9,9 +9,19 @@ * as published by the Free Software Foundation; version 2 * of the License. */ + +#define pr_fmt(fmt) "mrst: " fmt + #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -23,8 +33,10 @@ #include #include #include +#include #include + /* * the clockevent devices on Moorestown/Medfield can be APBT or LAPIC clock, * cmdline option x86_mrst_timer can be used to override the configuration @@ -102,10 +114,10 @@ static int __init sfi_parse_mtmr(struct sfi_table_header *table) memcpy(sfi_mtimer_array, pentry, totallen); } - printk(KERN_INFO "SFI: MTIMER info (num = %d):\n", sfi_mtimer_num); + pr_debug("SFI MTIMER info (num = %d):\n", sfi_mtimer_num); pentry = sfi_mtimer_array; for (totallen = 0; totallen < sfi_mtimer_num; totallen++, pentry++) { - printk(KERN_INFO "timer[%d]: paddr = 0x%08x, freq = %dHz," + pr_debug("timer[%d]: paddr = 0x%08x, freq = %dHz," " irq = %d\n", totallen, (u32)pentry->phys_addr, pentry->freq_hz, pentry->irq); if (!pentry->irq) @@ -176,10 +188,10 @@ int __init sfi_parse_mrtc(struct sfi_table_header *table) memcpy(sfi_mrtc_array, pentry, totallen); } - printk(KERN_INFO "SFI: RTC info (num = %d):\n", sfi_mrtc_num); + pr_debug("SFI RTC info (num = %d):\n", sfi_mrtc_num); pentry = sfi_mrtc_array; for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) { - printk(KERN_INFO "RTC[%d]: paddr = 0x%08x, irq = %d\n", + pr_debug("RTC[%d]: paddr = 0x%08x, irq = %d\n", totallen, (u32)pentry->phys_addr, pentry->irq); mp_irq.type = MP_IOAPIC; mp_irq.irqtype = mp_INT; @@ -309,3 +321,498 @@ static inline int __init setup_x86_mrst_timer(char *arg) return 0; } __setup("x86_mrst_timer=", setup_x86_mrst_timer); + +/* + * Parsing GPIO table first, since the DEVS table will need this table + * to map the pin name to the actual pin. + */ +static struct sfi_gpio_table_entry *gpio_table; +static int gpio_num_entry; + +static int __init sfi_parse_gpio(struct sfi_table_header *table) +{ + struct sfi_table_simple *sb; + struct sfi_gpio_table_entry *pentry; + int num, i; + + if (gpio_table) + return 0; + sb = (struct sfi_table_simple *)table; + num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry); + pentry = (struct sfi_gpio_table_entry *)sb->pentry; + + gpio_table = (struct sfi_gpio_table_entry *) + kmalloc(num * sizeof(*pentry), GFP_KERNEL); + if (!gpio_table) + return -1; + memcpy(gpio_table, pentry, num * sizeof(*pentry)); + gpio_num_entry = num; + + pr_debug("GPIO pin info:\n"); + for (i = 0; i < num; i++, pentry++) + pr_debug("info[%2d]: controller = %16.16s, pin_name = %16.16s," + " pin = %d\n", i, + pentry->controller_name, + pentry->pin_name, + pentry->pin_no); + return 0; +} + +static int get_gpio_by_name(const char *name) +{ + struct sfi_gpio_table_entry *pentry = gpio_table; + int i; + + if (!pentry) + return -1; + for (i = 0; i < gpio_num_entry; i++, pentry++) { + if (!strncmp(name, pentry->pin_name, SFI_NAME_LEN)) + return pentry->pin_no; + } + return -1; +} + +/* + * Here defines the array of devices platform data that IAFW would export + * through SFI "DEVS" table, we use name and type to match the device and + * its platform data. + */ +struct devs_id { + char name[SFI_NAME_LEN + 1]; + u8 type; + u8 delay; + void *(*get_platform_data)(void *info); +}; + +/* the offset for the mapping of global gpio pin to irq */ +#define MRST_IRQ_OFFSET 0x100 + +static void __init *pmic_gpio_platform_data(void *info) +{ + static struct intel_pmic_gpio_platform_data pmic_gpio_pdata; + int gpio_base = get_gpio_by_name("pmic_gpio_base"); + + if (gpio_base == -1) + gpio_base = 64; + pmic_gpio_pdata.gpio_base = gpio_base; + pmic_gpio_pdata.irq_base = gpio_base + MRST_IRQ_OFFSET; + pmic_gpio_pdata.gpiointr = 0xffffeff8; + + return &pmic_gpio_pdata; +} + +static void __init *max3111_platform_data(void *info) +{ + struct spi_board_info *spi_info = info; + int intr = get_gpio_by_name("max3111_int"); + + if (intr == -1) + return NULL; + spi_info->irq = intr + MRST_IRQ_OFFSET; + return NULL; +} + +/* we have multiple max7315 on the board ... */ +#define MAX7315_NUM 2 +static void __init *max7315_platform_data(void *info) +{ + static struct pca953x_platform_data max7315_pdata[MAX7315_NUM]; + static int nr; + struct pca953x_platform_data *max7315 = &max7315_pdata[nr]; + struct i2c_board_info *i2c_info = info; + int gpio_base, intr; + char base_pin_name[SFI_NAME_LEN + 1]; + char intr_pin_name[SFI_NAME_LEN + 1]; + + if (nr == MAX7315_NUM) { + pr_err("too many max7315s, we only support %d\n", + MAX7315_NUM); + return NULL; + } + /* we have several max7315 on the board, we only need load several + * instances of the same pca953x driver to cover them + */ + strcpy(i2c_info->type, "max7315"); + if (nr++) { + sprintf(base_pin_name, "max7315_%d_base", nr); + sprintf(intr_pin_name, "max7315_%d_int", nr); + } else { + strcpy(base_pin_name, "max7315_base"); + strcpy(intr_pin_name, "max7315_int"); + } + + gpio_base = get_gpio_by_name(base_pin_name); + intr = get_gpio_by_name(intr_pin_name); + + if (gpio_base == -1) + return NULL; + max7315->gpio_base = gpio_base; + if (intr != -1) { + i2c_info->irq = intr + MRST_IRQ_OFFSET; + max7315->irq_base = gpio_base + MRST_IRQ_OFFSET; + } else { + i2c_info->irq = -1; + max7315->irq_base = -1; + } + return max7315; +} + +static void __init *emc1403_platform_data(void *info) +{ + static short intr2nd_pdata; + struct i2c_board_info *i2c_info = info; + int intr = get_gpio_by_name("thermal_int"); + int intr2nd = get_gpio_by_name("thermal_alert"); + + if (intr == -1 || intr2nd == -1) + return NULL; + + i2c_info->irq = intr + MRST_IRQ_OFFSET; + intr2nd_pdata = intr2nd + MRST_IRQ_OFFSET; + + return &intr2nd_pdata; +} + +static void __init *lis331dl_platform_data(void *info) +{ + static short intr2nd_pdata; + struct i2c_board_info *i2c_info = info; + int intr = get_gpio_by_name("accel_int"); + int intr2nd = get_gpio_by_name("accel_2"); + + if (intr == -1 || intr2nd == -1) + return NULL; + + i2c_info->irq = intr + MRST_IRQ_OFFSET; + intr2nd_pdata = intr2nd + MRST_IRQ_OFFSET; + + return &intr2nd_pdata; +} + +static const struct devs_id __initconst device_ids[] = { + {"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data}, + {"spi_max3111", SFI_DEV_TYPE_SPI, 0, &max3111_platform_data}, + {"i2c_max7315", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, + {"i2c_max7315_2", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, + {"emc1403", SFI_DEV_TYPE_I2C, 1, &emc1403_platform_data}, + {"i2c_accel", SFI_DEV_TYPE_I2C, 0, &lis331dl_platform_data}, + {}, +}; + +#define MAX_IPCDEVS 24 +static struct platform_device *ipc_devs[MAX_IPCDEVS]; +static int ipc_next_dev; + +#define MAX_SCU_SPI 24 +static struct spi_board_info *spi_devs[MAX_SCU_SPI]; +static int spi_next_dev; + +#define MAX_SCU_I2C 24 +static struct i2c_board_info *i2c_devs[MAX_SCU_I2C]; +static int i2c_bus[MAX_SCU_I2C]; +static int i2c_next_dev; + +static void __init intel_scu_device_register(struct platform_device *pdev) +{ + if(ipc_next_dev == MAX_IPCDEVS) + pr_err("too many SCU IPC devices"); + else + ipc_devs[ipc_next_dev++] = pdev; +} + +static void __init intel_scu_spi_device_register(struct spi_board_info *sdev) +{ + struct spi_board_info *new_dev; + + if (spi_next_dev == MAX_SCU_SPI) { + pr_err("too many SCU SPI devices"); + return; + } + + new_dev = kzalloc(sizeof(*sdev), GFP_KERNEL); + if (!new_dev) { + pr_err("failed to alloc mem for delayed spi dev %s\n", + sdev->modalias); + return; + } + memcpy(new_dev, sdev, sizeof(*sdev)); + + spi_devs[spi_next_dev++] = new_dev; +} + +static void __init intel_scu_i2c_device_register(int bus, + struct i2c_board_info *idev) +{ + struct i2c_board_info *new_dev; + + if (i2c_next_dev == MAX_SCU_I2C) { + pr_err("too many SCU I2C devices"); + return; + } + + new_dev = kzalloc(sizeof(*idev), GFP_KERNEL); + if (!new_dev) { + pr_err("failed to alloc mem for delayed i2c dev %s\n", + idev->type); + return; + } + memcpy(new_dev, idev, sizeof(*idev)); + + i2c_bus[i2c_next_dev] = bus; + i2c_devs[i2c_next_dev++] = new_dev; +} + +/* Called by IPC driver */ +void intel_scu_devices_create(void) +{ + int i; + + for (i = 0; i < ipc_next_dev; i++) + platform_device_add(ipc_devs[i]); + + for (i = 0; i < spi_next_dev; i++) + spi_register_board_info(spi_devs[i], 1); + + for (i = 0; i < i2c_next_dev; i++) { + struct i2c_adapter *adapter; + struct i2c_client *client; + + adapter = i2c_get_adapter(i2c_bus[i]); + if (adapter) { + client = i2c_new_device(adapter, i2c_devs[i]); + if (!client) + pr_err("can't create i2c device %s\n", + i2c_devs[i]->type); + } else + i2c_register_board_info(i2c_bus[i], i2c_devs[i], 1); + } +} +EXPORT_SYMBOL_GPL(intel_scu_devices_create); + +/* Called by IPC driver */ +void intel_scu_devices_destroy(void) +{ + int i; + + for (i = 0; i < ipc_next_dev; i++) + platform_device_del(ipc_devs[i]); +} +EXPORT_SYMBOL_GPL(intel_scu_devices_destroy); + +static void __init install_irq_resource(struct platform_device *pdev, int irq) +{ + /* Single threaded */ + static struct resource __initdata res = { + .name = "IRQ", + .flags = IORESOURCE_IRQ, + }; + res.start = irq; + platform_device_add_resources(pdev, &res, 1); +} + +static void __init sfi_handle_ipc_dev(struct platform_device *pdev) +{ + const struct devs_id *dev = device_ids; + void *pdata = NULL; + + while (dev->name[0]) { + if (dev->type == SFI_DEV_TYPE_IPC && + !strncmp(dev->name, pdev->name, SFI_NAME_LEN)) { + pdata = dev->get_platform_data(pdev); + break; + } + dev++; + } + pdev->dev.platform_data = pdata; + intel_scu_device_register(pdev); +} + +static void __init sfi_handle_spi_dev(struct spi_board_info *spi_info) +{ + const struct devs_id *dev = device_ids; + void *pdata = NULL; + + while (dev->name[0]) { + if (dev->type == SFI_DEV_TYPE_SPI && + !strncmp(dev->name, spi_info->modalias, SFI_NAME_LEN)) { + pdata = dev->get_platform_data(spi_info); + break; + } + dev++; + } + spi_info->platform_data = pdata; + if (dev->delay) + intel_scu_spi_device_register(spi_info); + else + spi_register_board_info(spi_info, 1); +} + +static void __init sfi_handle_i2c_dev(int bus, struct i2c_board_info *i2c_info) +{ + const struct devs_id *dev = device_ids; + void *pdata = NULL; + + while (dev->name[0]) { + if (dev->type == SFI_DEV_TYPE_I2C && + !strncmp(dev->name, i2c_info->type, SFI_NAME_LEN)) { + pdata = dev->get_platform_data(i2c_info); + break; + } + dev++; + } + i2c_info->platform_data = pdata; + + if (dev->delay) + intel_scu_i2c_device_register(bus, i2c_info); + else + i2c_register_board_info(bus, i2c_info, 1); + } + + +static int __init sfi_parse_devs(struct sfi_table_header *table) +{ + struct sfi_table_simple *sb; + struct sfi_device_table_entry *pentry; + struct spi_board_info spi_info; + struct i2c_board_info i2c_info; + struct platform_device *pdev; + int num, i, bus; + int ioapic; + struct io_apic_irq_attr irq_attr; + + sb = (struct sfi_table_simple *)table; + num = SFI_GET_NUM_ENTRIES(sb, struct sfi_device_table_entry); + pentry = (struct sfi_device_table_entry *)sb->pentry; + + for (i = 0; i < num; i++, pentry++) { + if (pentry->irq != (u8)0xff) { /* native RTE case */ + /* these SPI2 devices are not exposed to system as PCI + * devices, but they have separate RTE entry in IOAPIC + * so we have to enable them one by one here + */ + ioapic = mp_find_ioapic(pentry->irq); + irq_attr.ioapic = ioapic; + irq_attr.ioapic_pin = pentry->irq; + irq_attr.trigger = 1; + irq_attr.polarity = 1; + io_apic_set_pci_routing(NULL, pentry->irq, &irq_attr); + } + switch (pentry->type) { + case SFI_DEV_TYPE_IPC: + /* ID as IRQ is a hack that will go away */ + pdev = platform_device_alloc(pentry->name, pentry->irq); + if (pdev == NULL) { + pr_err("out of memory for SFI platform device '%s'.\n", + pentry->name); + continue; + } + install_irq_resource(pdev, pentry->irq); + pr_debug("info[%2d]: IPC bus, name = %16.16s, " + "irq = 0x%2x\n", i, pentry->name, pentry->irq); + sfi_handle_ipc_dev(pdev); + break; + case SFI_DEV_TYPE_SPI: + memset(&spi_info, 0, sizeof(spi_info)); + strncpy(spi_info.modalias, pentry->name, SFI_NAME_LEN); + spi_info.irq = pentry->irq; + spi_info.bus_num = pentry->host_num; + spi_info.chip_select = pentry->addr; + spi_info.max_speed_hz = pentry->max_freq; + pr_debug("info[%2d]: SPI bus = %d, name = %16.16s, " + "irq = 0x%2x, max_freq = %d, cs = %d\n", i, + spi_info.bus_num, + spi_info.modalias, + spi_info.irq, + spi_info.max_speed_hz, + spi_info.chip_select); + sfi_handle_spi_dev(&spi_info); + break; + case SFI_DEV_TYPE_I2C: + memset(&i2c_info, 0, sizeof(i2c_info)); + bus = pentry->host_num; + strncpy(i2c_info.type, pentry->name, SFI_NAME_LEN); + i2c_info.irq = pentry->irq; + i2c_info.addr = pentry->addr; + pr_debug("info[%2d]: I2C bus = %d, name = %16.16s, " + "irq = 0x%2x, addr = 0x%x\n", i, bus, + i2c_info.type, + i2c_info.irq, + i2c_info.addr); + sfi_handle_i2c_dev(bus, &i2c_info); + break; + case SFI_DEV_TYPE_UART: + case SFI_DEV_TYPE_HSI: + default: + ; + } + } + return 0; +} + +static int __init mrst_platform_init(void) +{ + sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, sfi_parse_gpio); + sfi_table_parse(SFI_SIG_DEVS, NULL, NULL, sfi_parse_devs); + return 0; +} +arch_initcall(mrst_platform_init); + +/* + * we will search these buttons in SFI GPIO table (by name) + * and register them dynamically. Please add all possible + * buttons here, we will shrink them if no GPIO found. + */ +static struct gpio_keys_button gpio_button[] = { + {KEY_POWER, -1, 1, "power_btn", EV_KEY, 0, 3000}, + {KEY_PROG1, -1, 1, "prog_btn1", EV_KEY, 0, 20}, + {KEY_PROG2, -1, 1, "prog_btn2", EV_KEY, 0, 20}, + {SW_LID, -1, 1, "lid_switch", EV_SW, 0, 20}, + {KEY_VOLUMEUP, -1, 1, "vol_up", EV_KEY, 0, 20}, + {KEY_VOLUMEDOWN, -1, 1, "vol_down", EV_KEY, 0, 20}, + {KEY_CAMERA, -1, 1, "camera_full", EV_KEY, 0, 20}, + {KEY_CAMERA_FOCUS, -1, 1, "camera_half", EV_KEY, 0, 20}, + {SW_KEYPAD_SLIDE, -1, 1, "MagSw1", EV_SW, 0, 20}, + {SW_KEYPAD_SLIDE, -1, 1, "MagSw2", EV_SW, 0, 20}, +}; + +static struct gpio_keys_platform_data mrst_gpio_keys = { + .buttons = gpio_button, + .rep = 1, + .nbuttons = -1, /* will fill it after search */ +}; + +static struct platform_device pb_device = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &mrst_gpio_keys, + }, +}; + +/* + * Shrink the non-existent buttons, register the gpio button + * device if there is some + */ +static int __init pb_keys_init(void) +{ + struct gpio_keys_button *gb = gpio_button; + int i, num, good = 0; + + num = sizeof(gpio_button) / sizeof(struct gpio_keys_button); + for (i = 0; i < num; i++) { + gb[i].gpio = get_gpio_by_name(gb[i].desc); + if (gb[i].gpio == -1) + continue; + + if (i != good) + gb[good] = gb[i]; + good++; + } + + if (good) { + mrst_gpio_keys.nbuttons = good; + return platform_device_register(&pb_device); + } + return 0; +} +late_initcall(pb_keys_init); diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 41a9e34899ac..ca35b0ce944a 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -26,6 +26,7 @@ #include #include #include +#include /* IPC defines the following message types */ #define IPCMSG_WATCHDOG_TIMER 0xF8 /* Set Kernel Watchdog Threshold */ @@ -699,6 +700,9 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) iounmap(ipcdev.ipc_base); return -ENOMEM; } + + intel_scu_devices_create(); + return 0; } @@ -720,6 +724,7 @@ static void ipc_remove(struct pci_dev *pdev) iounmap(ipcdev.ipc_base); iounmap(ipcdev.i2c_base); ipcdev.pdev = NULL; + intel_scu_devices_destroy(); } static const struct pci_device_id pci_ids[] = { diff --git a/include/linux/sfi.h b/include/linux/sfi.h index 7f770c638e99..fe817918b30e 100644 --- a/include/linux/sfi.h +++ b/include/linux/sfi.h @@ -77,6 +77,8 @@ #define SFI_OEM_ID_SIZE 6 #define SFI_OEM_TABLE_ID_SIZE 8 +#define SFI_NAME_LEN 16 + #define SFI_SYST_SEARCH_BEGIN 0x000E0000 #define SFI_SYST_SEARCH_END 0x000FFFFF @@ -156,13 +158,13 @@ struct sfi_device_table_entry { u16 addr; u8 irq; u32 max_freq; - char name[16]; + char name[SFI_NAME_LEN]; } __packed; struct sfi_gpio_table_entry { - char controller_name[16]; + char controller_name[SFI_NAME_LEN]; u16 pin_no; - char pin_name[16]; + char pin_name[SFI_NAME_LEN]; } __packed; typedef int (*sfi_table_handler) (struct sfi_table_header *table); -- cgit v1.2.3 From 751305d9b2fd3e03eaab7808e976241d85ca4353 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Thu, 28 Oct 2010 18:23:01 +0100 Subject: viafb: General power management infrastructure Multiple devices need S/R hooks (framebuffer, GPIO, camera). Add infrastructure and convert existing framebuffer code to the new model. This patch should create no functional change. Based on earlier work by Jonathan Corbet. Signed-off-by: Daniel Drake Acked-by: Jonathan Corbet Signed-off-by: Florian Tobias Schandinat --- drivers/video/via/via-core.c | 79 ++++++++++++++++++++++++++++++++++++++++++-- drivers/video/via/viafbdev.c | 34 +++++++++---------- drivers/video/via/viafbdev.h | 2 -- include/linux/via-core.h | 15 +++++++++ 4 files changed, 107 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/video/via/via-core.c b/drivers/video/via/via-core.c index 31e30338e893..42be3d955887 100644 --- a/drivers/video/via/via-core.c +++ b/drivers/video/via/via-core.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include /* * The default port config. @@ -563,6 +565,78 @@ static void via_teardown_subdevs(void) } } +/* + * Power management functions + */ +#ifdef CONFIG_PM +static LIST_HEAD(viafb_pm_hooks); +static DEFINE_MUTEX(viafb_pm_hooks_lock); + +void viafb_pm_register(struct viafb_pm_hooks *hooks) +{ + INIT_LIST_HEAD(&hooks->list); + + mutex_lock(&viafb_pm_hooks_lock); + list_add_tail(&hooks->list, &viafb_pm_hooks); + mutex_unlock(&viafb_pm_hooks_lock); +} +EXPORT_SYMBOL_GPL(viafb_pm_register); + +void viafb_pm_unregister(struct viafb_pm_hooks *hooks) +{ + mutex_lock(&viafb_pm_hooks_lock); + list_del(&hooks->list); + mutex_unlock(&viafb_pm_hooks_lock); +} +EXPORT_SYMBOL_GPL(viafb_pm_unregister); + +static int via_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct viafb_pm_hooks *hooks; + + if (state.event != PM_EVENT_SUSPEND) + return 0; + /* + * "I've occasionally hit a few drivers that caused suspend + * failures, and each and every time it was a driver bug, and + * the right thing to do was to just ignore the error and suspend + * anyway - returning an error code and trying to undo the suspend + * is not what anybody ever really wants, even if our model + *_allows_ for it." + * -- Linus Torvalds, Dec. 7, 2009 + */ + mutex_lock(&viafb_pm_hooks_lock); + list_for_each_entry_reverse(hooks, &viafb_pm_hooks, list) + hooks->suspend(hooks->private); + mutex_unlock(&viafb_pm_hooks_lock); + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; +} + +static int via_resume(struct pci_dev *pdev) +{ + struct viafb_pm_hooks *hooks; + + /* Get the bus side powered up */ + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + if (pci_enable_device(pdev)) + return 0; + + pci_set_master(pdev); + + /* Now bring back any subdevs */ + mutex_lock(&viafb_pm_hooks_lock); + list_for_each_entry(hooks, &viafb_pm_hooks, list) + hooks->resume(hooks->private); + mutex_unlock(&viafb_pm_hooks_lock); + + return 0; +} +#endif /* CONFIG_PM */ static int __devinit via_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -572,6 +646,7 @@ static int __devinit via_pci_probe(struct pci_dev *pdev, ret = pci_enable_device(pdev); if (ret) return ret; + /* * Global device initialization. */ @@ -651,8 +726,8 @@ static struct pci_driver via_driver = { .probe = via_pci_probe, .remove = __devexit_p(via_pci_remove), #ifdef CONFIG_PM - .suspend = viafb_suspend, - .resume = viafb_resume, + .suspend = via_suspend, + .resume = via_resume, #endif }; diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index d298cfccd6fc..289edd519527 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -1672,31 +1672,19 @@ static int parse_mode(const char *str, u32 *xres, u32 *yres) #ifdef CONFIG_PM -int viafb_suspend(struct pci_dev *pdev, pm_message_t state) +static int viafb_suspend(void *unused) { - if (state.event == PM_EVENT_SUSPEND) { - acquire_console_sem(); - fb_set_suspend(viafbinfo, 1); - - viafb_sync(viafbinfo); - - pci_save_state(pdev); - pci_disable_device(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - release_console_sem(); - } + acquire_console_sem(); + fb_set_suspend(viafbinfo, 1); + viafb_sync(viafbinfo); + release_console_sem(); return 0; } -int viafb_resume(struct pci_dev *pdev) +static int viafb_resume(void *unused) { acquire_console_sem(); - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - if (pci_enable_device(pdev)) - goto fail; - pci_set_master(pdev); if (viaparinfo->shared->vdev->engine_mmio) viafb_reset_engine(viaparinfo); viafb_set_par(viafbinfo); @@ -1704,11 +1692,15 @@ int viafb_resume(struct pci_dev *pdev) viafb_set_par(viafbinfo1); fb_set_suspend(viafbinfo, 0); -fail: release_console_sem(); return 0; } +static struct viafb_pm_hooks viafb_fb_pm_hooks = { + .suspend = viafb_suspend, + .resume = viafb_resume +}; + #endif @@ -1899,6 +1891,10 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev) viafb_init_proc(viaparinfo->shared); viafb_init_dac(IGA2); + +#ifdef CONFIG_PM + viafb_pm_register(&viafb_fb_pm_hooks); +#endif return 0; out_fb_unreg: diff --git a/drivers/video/via/viafbdev.h b/drivers/video/via/viafbdev.h index 4960e3da6645..d66f963e930e 100644 --- a/drivers/video/via/viafbdev.h +++ b/drivers/video/via/viafbdev.h @@ -108,6 +108,4 @@ void via_fb_pci_remove(struct pci_dev *pdev); /* Temporary */ int viafb_init(void); void viafb_exit(void); -int viafb_suspend(struct pci_dev *pdev, pm_message_t state); -int viafb_resume(struct pci_dev *pdev); #endif /* __VIAFBDEV_H__ */ diff --git a/include/linux/via-core.h b/include/linux/via-core.h index 7ffb521e1a7a..a4327a0c8efc 100644 --- a/include/linux/via-core.h +++ b/include/linux/via-core.h @@ -59,6 +59,21 @@ struct via_port_cfg { u8 ioport_index; }; +/* + * Allow subdevs to register suspend/resume hooks. + */ +#ifdef CONFIG_PM +struct viafb_pm_hooks { + struct list_head list; + int (*suspend)(void *private); + int (*resume)(void *private); + void *private; +}; + +void viafb_pm_register(struct viafb_pm_hooks *hooks); +void viafb_pm_unregister(struct viafb_pm_hooks *hooks); +#endif /* CONFIG_PM */ + /* * This is the global viafb "device" containing stuff needed by * all subdevs. -- cgit v1.2.3 From c36940e678fc30779c99246c034deca1fed61ae4 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 2 Nov 2010 11:27:16 +0000 Subject: fbdev: sh_mobile_hdmi: add support for more precise HDMI clock configuration The HDMI clock has to be reconfigured for different video modes. However, the precision of the supplying SoC clock on SH-Mobile systems is often insufficient. This patch allows to additionally reconfigure the parent clock to achieve the optimal HDMI clock frequency, in case this is supported by the platform. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Paul Mundt --- drivers/video/sh_mobile_hdmi.c | 112 +++++++++++++++++++++++++---------------- include/video/sh_mobile_hdmi.h | 3 ++ 2 files changed, 71 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index d7df10315d8d..ef41c215abae 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c @@ -685,11 +685,21 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi) } static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, - const struct fb_videomode *mode) + const struct fb_videomode *mode, + unsigned long *hdmi_rate, unsigned long *parent_rate) { - long target = PICOS2KHZ(mode->pixclock) * 1000, - rate = clk_round_rate(hdmi->hdmi_clk, target); - unsigned long rate_error = rate > 0 ? abs(rate - target) : ULONG_MAX; + unsigned long target = PICOS2KHZ(mode->pixclock) * 1000, rate_error; + struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; + + *hdmi_rate = clk_round_rate(hdmi->hdmi_clk, target); + if ((long)*hdmi_rate < 0) + *hdmi_rate = clk_get_rate(hdmi->hdmi_clk); + + rate_error = (long)*hdmi_rate > 0 ? abs(*hdmi_rate - target) : ULONG_MAX; + if (rate_error && pdata->clk_optimize_parent) + rate_error = pdata->clk_optimize_parent(target, hdmi_rate, parent_rate); + else if (clk_get_parent(hdmi->hdmi_clk)) + *parent_rate = clk_get_rate(clk_get_parent(hdmi->hdmi_clk)); dev_dbg(hdmi->dev, "%u-%u-%u-%u x %u-%u-%u-%u\n", mode->left_margin, mode->xres, @@ -697,14 +707,15 @@ static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, mode->upper_margin, mode->yres, mode->lower_margin, mode->vsync_len); - dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz\n", target, - rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0, - mode->refresh); + dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz, p=%luHz\n", target, + rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0, + mode->refresh, *parent_rate); return rate_error; } -static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) +static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, + unsigned long *parent_rate) { struct fb_var_screeninfo tmpvar; struct fb_var_screeninfo *var = &tmpvar; @@ -754,11 +765,14 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) for (i = 0, mode = hdmi->monspec.modedb; f_width && f_height && i < hdmi->monspec.modedb_len && !exact_match; i++, mode++) { - unsigned long rate_error = sh_hdmi_rate_error(hdmi, mode); + unsigned long rate_error; /* No interest in unmatching modes */ if (f_width != mode->xres || f_height != mode->yres) continue; + + rate_error = sh_hdmi_rate_error(hdmi, mode, hdmi_rate, parent_rate); + if (f_refresh == mode->refresh || (!f_refresh && !rate_error)) /* * Exact match if either the refresh rate matches or it @@ -802,7 +816,7 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) if (modelist) { found = &modelist->mode; - found_rate_error = sh_hdmi_rate_error(hdmi, found); + found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, parent_rate); } } @@ -810,10 +824,6 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) if (!found) return -ENXIO; - dev_info(hdmi->dev, "Using %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", - modelist ? "default" : "EDID", found->xres, found->yres, - found->refresh, PICOS2KHZ(found->pixclock) * 1000, found_rate_error); - if ((found->xres == 720 && found->yres == 480) || (found->xres == 1280 && found->yres == 720) || (found->xres == 1920 && found->yres == 1080)) @@ -821,6 +831,11 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) else hdmi->preprogrammed_mode = false; + dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", + modelist ? "default" : "EDID", hdmi->preprogrammed_mode ? "VIC" : "external", + found->xres, found->yres, found->refresh, + PICOS2KHZ(found->pixclock) * 1000, found_rate_error); + fb_videomode_to_var(&hdmi->var, found); sh_hdmi_external_video_param(hdmi); @@ -972,39 +987,38 @@ static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi) /** * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock - * @hdmi: driver context - * @pixclock: pixel clock period in picoseconds - * return: configured positive rate if successful - * 0 if couldn't set the rate, but managed to enable the clock - * negative error, if couldn't enable the clock + * @hdmi: driver context + * @hdmi_rate: HDMI clock frequency in Hz + * @parent_rate: if != 0 - set parent clock rate for optimal precision + * return: configured positive rate if successful + * 0 if couldn't set the rate, but managed to enable the + * clock, negative error, if couldn't enable the clock */ -static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long pixclock) +static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate, + unsigned long parent_rate) { - long rate; + struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; int ret; - rate = PICOS2KHZ(pixclock) * 1000; - rate = clk_round_rate(hdmi->hdmi_clk, rate); - if (rate > 0) { - ret = clk_set_rate(hdmi->hdmi_clk, rate); + if (parent_rate && clk_get_parent(hdmi->hdmi_clk)) { + ret = clk_set_rate(clk_get_parent(hdmi->hdmi_clk), parent_rate); if (ret < 0) { - dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", rate, ret); - rate = 0; + dev_warn(hdmi->dev, "Cannot set parent rate %ld: %d\n", parent_rate, ret); + hdmi_rate = clk_round_rate(hdmi->hdmi_clk, hdmi_rate); } else { - dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", rate); + dev_dbg(hdmi->dev, "HDMI set parent frequency %lu\n", parent_rate); } - } else { - rate = 0; - dev_warn(hdmi->dev, "Cannot get suitable rate: %ld\n", rate); } - ret = clk_enable(hdmi->hdmi_clk); + ret = clk_set_rate(hdmi->hdmi_clk, hdmi_rate); if (ret < 0) { - dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret); - return ret; + dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", hdmi_rate, ret); + hdmi_rate = 0; + } else { + dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", hdmi_rate); } - return rate; + return hdmi_rate; } /* Hotplug interrupt occurred, read EDID */ @@ -1024,16 +1038,17 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) mutex_lock(&hdmi->mutex); if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { + unsigned long parent_rate = 0, hdmi_rate; + /* A device has been plugged in */ pm_runtime_get_sync(hdmi->dev); - ret = sh_hdmi_read_edid(hdmi); + ret = sh_hdmi_read_edid(hdmi, &hdmi_rate, &parent_rate); if (ret < 0) goto out; /* Reconfigure the clock */ - clk_disable(hdmi->hdmi_clk); - ret = sh_hdmi_clk_configure(hdmi, hdmi->var.pixclock); + ret = sh_hdmi_clk_configure(hdmi, hdmi_rate, parent_rate); if (ret < 0) goto out; @@ -1166,13 +1181,22 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) goto egetclk; } - /* Some arbitrary relaxed pixclock just to get things started */ - rate = sh_hdmi_clk_configure(hdmi, 37037); + /* An arbitrary relaxed pixclock just to get things started: from standard 480p */ + rate = clk_round_rate(hdmi->hdmi_clk, PICOS2KHZ(37037)); + if (rate > 0) + rate = sh_hdmi_clk_configure(hdmi, rate, 0); + if (rate < 0) { ret = rate; goto erate; } + ret = clk_enable(hdmi->hdmi_clk); + if (ret < 0) { + dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret); + goto erate; + } + dev_dbg(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate); if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { @@ -1190,10 +1214,6 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, hdmi); - /* Product and revision IDs are 0 in sh-mobile version */ - dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", - hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); - /* Set up LCDC callbacks */ board_cfg = &pdata->lcd_chan->board_cfg; board_cfg->owner = THIS_MODULE; @@ -1206,6 +1226,10 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_resume(&pdev->dev); + /* Product and revision IDs are 0 in sh-mobile version */ + dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", + hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); + ret = request_irq(irq, sh_hdmi_hotplug, 0, dev_name(&pdev->dev), hdmi); if (ret < 0) { diff --git a/include/video/sh_mobile_hdmi.h b/include/video/sh_mobile_hdmi.h index 1e1aa54ab2e4..b56932927d0a 100644 --- a/include/video/sh_mobile_hdmi.h +++ b/include/video/sh_mobile_hdmi.h @@ -13,6 +13,7 @@ struct sh_mobile_lcdc_chan_cfg; struct device; +struct clk; /* * flags format @@ -33,6 +34,8 @@ struct sh_mobile_hdmi_info { struct sh_mobile_lcdc_chan_cfg *lcd_chan; struct device *lcd_dev; unsigned int flags; + long (*clk_optimize_parent)(unsigned long target, unsigned long *best_freq, + unsigned long *parent_freq); }; #endif -- cgit v1.2.3 From f6cd24777513fcc673d432cc29ef59881d3e4df1 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 4 Nov 2010 11:13:48 +0100 Subject: irq: Better struct irqaction layout We currently use kmalloc-96 slab for struct irqaction allocations on 64bit arches. This is unfortunate because of possible false sharing and two cache lines accesses. Move 'name' and 'dir' fields at the end of the structure, and force a suitable alignement. Hot path fields now use one cache line on x86_64. Signed-off-by: Eric Dumazet Reviewed-by: Andi Kleen Cc: Peter Zijlstra LKML-Reference: <1288865628.2659.69.camel@edumazet-laptop> Signed-off-by: Thomas Gleixner --- include/linux/interrupt.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 79d0c4f6d071..55e0d4253e49 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -114,15 +114,15 @@ typedef irqreturn_t (*irq_handler_t)(int, void *); struct irqaction { irq_handler_t handler; unsigned long flags; - const char *name; void *dev_id; struct irqaction *next; int irq; - struct proc_dir_entry *dir; irq_handler_t thread_fn; struct task_struct *thread; unsigned long thread_flags; -}; + const char *name; + struct proc_dir_entry *dir; +} ____cacheline_internodealigned_in_smp; extern irqreturn_t no_action(int cpl, void *dev_id); -- cgit v1.2.3 From 5e4f083f78d03e9f8d2e327daccde16976f9bb00 Mon Sep 17 00:00:00 2001 From: Yong Zhang Date: Sun, 24 Oct 2010 11:50:53 +0800 Subject: hrtimer: Remove stale comment on curr_timer curr_timer doesn't resident in struct hrtimer_cpu_base anymore. Signed-off-by: Yong Zhang LKML-Reference: <1287892253-2587-1-git-send-email-yong.zhang0@gmail.com> Signed-off-by: Thomas Gleixner --- include/linux/hrtimer.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index fd0c1b857d3d..dd9954b79342 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -158,7 +158,6 @@ struct hrtimer_clock_base { * @lock: lock protecting the base and associated clock bases * and timers * @clock_base: array of clock bases for this cpu - * @curr_timer: the timer which is executing a callback right now * @expires_next: absolute time of the next event which was scheduled * via clock_set_next_event() * @hres_active: State of high resolution mode -- cgit v1.2.3 From 46b13fc5c0f239f36e84665c73087d5fa86bfd86 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 11 Nov 2010 06:57:19 +0000 Subject: neigh: reorder struct neighbour It is important to move nud_state outside of the often modified cache line (because of refcnt), to reduce false sharing in neigh_event_send() This is a followup of commit 0ed8ddf4045f (neigh: Protect neigh->ha[] with a seqlock) This gives a 7% speedup on routing test with IP route cache disabled. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/neighbour.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 55590ab16b3e..815b2ce9f4a4 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -96,16 +96,16 @@ struct neighbour { struct neigh_parms *parms; unsigned long confirmed; unsigned long updated; - __u8 flags; - __u8 nud_state; - __u8 type; - __u8 dead; + rwlock_t lock; atomic_t refcnt; struct sk_buff_head arp_queue; struct timer_list timer; unsigned long used; atomic_t probes; - rwlock_t lock; + __u8 flags; + __u8 nud_state; + __u8 type; + __u8 dead; seqlock_t ha_lock; unsigned char ha[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))]; struct hh_cache *hh; -- cgit v1.2.3 From 72cdd1d971c0deb1619c5c339270570c43647a78 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 11 Nov 2010 07:14:07 +0000 Subject: net: get rid of rtable->idev It seems idev field in struct rtable has no special purpose, but adding extra atomic ops. We hold refcounts on the device itself (using percpu data, so pretty cheap in current kernel). infiniband case is solved using dst.dev instead of idev->dev Removal of this field means routing without route cache is now using shared data, percpu data, and only potential contention is a pair of atomic ops on struct neighbour per forwarded packet. About 5% speedup on routing test. Signed-off-by: Eric Dumazet Cc: Herbert Xu Cc: Roland Dreier Cc: Sean Hefty Cc: Hal Rosenstock Signed-off-by: David S. Miller --- drivers/infiniband/core/addr.c | 8 ++++---- include/net/route.h | 2 -- net/ipv4/route.c | 37 ++++--------------------------------- net/ipv4/xfrm4_policy.c | 24 ------------------------ 4 files changed, 8 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index a5ea1bce9689..c15fd2ea56c1 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -200,7 +200,7 @@ static int addr4_resolve(struct sockaddr_in *src_in, src_in->sin_family = AF_INET; src_in->sin_addr.s_addr = rt->rt_src; - if (rt->idev->dev->flags & IFF_LOOPBACK) { + if (rt->dst.dev->flags & IFF_LOOPBACK) { ret = rdma_translate_ip((struct sockaddr *) dst_in, addr); if (!ret) memcpy(addr->dst_dev_addr, addr->src_dev_addr, MAX_ADDR_LEN); @@ -208,12 +208,12 @@ static int addr4_resolve(struct sockaddr_in *src_in, } /* If the device does ARP internally, return 'done' */ - if (rt->idev->dev->flags & IFF_NOARP) { - rdma_copy_addr(addr, rt->idev->dev, NULL); + if (rt->dst.dev->flags & IFF_NOARP) { + rdma_copy_addr(addr, rt->dst.dev, NULL); goto put; } - neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->idev->dev); + neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->dst.dev); if (!neigh || !(neigh->nud_state & NUD_VALID)) { neigh_event_send(rt->dst.neighbour, NULL); ret = -ENODATA; diff --git a/include/net/route.h b/include/net/route.h index 7e5e73bfa4de..cea533eaa853 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -55,8 +55,6 @@ struct rtable { /* Cache lookup keys */ struct flowi fl; - struct in_device *idev; - int rt_genid; unsigned rt_flags; __u16 rt_type; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 987bf9adb318..5955965c7953 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -140,13 +140,15 @@ static unsigned long expires_ljiffies; static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie); static void ipv4_dst_destroy(struct dst_entry *dst); -static void ipv4_dst_ifdown(struct dst_entry *dst, - struct net_device *dev, int how); static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst); static void ipv4_link_failure(struct sk_buff *skb); static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu); static int rt_garbage_collect(struct dst_ops *ops); +static void ipv4_dst_ifdown(struct dst_entry *dst, struct net_device *dev, + int how) +{ +} static struct dst_ops ipv4_dst_ops = { .family = AF_INET, @@ -1433,8 +1435,6 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, rt->dst.child = NULL; if (rt->dst.dev) dev_hold(rt->dst.dev); - if (rt->idev) - in_dev_hold(rt->idev); rt->dst.obsolete = -1; rt->dst.lastuse = jiffies; rt->dst.path = &rt->dst; @@ -1728,33 +1728,13 @@ static void ipv4_dst_destroy(struct dst_entry *dst) { struct rtable *rt = (struct rtable *) dst; struct inet_peer *peer = rt->peer; - struct in_device *idev = rt->idev; if (peer) { rt->peer = NULL; inet_putpeer(peer); } - - if (idev) { - rt->idev = NULL; - in_dev_put(idev); - } } -static void ipv4_dst_ifdown(struct dst_entry *dst, struct net_device *dev, - int how) -{ - struct rtable *rt = (struct rtable *) dst; - struct in_device *idev = rt->idev; - if (dev != dev_net(dev)->loopback_dev && idev && idev->dev == dev) { - struct in_device *loopback_idev = - in_dev_get(dev_net(dev)->loopback_dev); - if (loopback_idev) { - rt->idev = loopback_idev; - in_dev_put(idev); - } - } -} static void ipv4_link_failure(struct sk_buff *skb) { @@ -1910,7 +1890,6 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, rth->fl.iif = dev->ifindex; rth->dst.dev = init_net.loopback_dev; dev_hold(rth->dst.dev); - rth->idev = in_dev_get(rth->dst.dev); rth->fl.oif = 0; rth->rt_gateway = daddr; rth->rt_spec_dst= spec_dst; @@ -2050,7 +2029,6 @@ static int __mkroute_input(struct sk_buff *skb, rth->fl.iif = in_dev->dev->ifindex; rth->dst.dev = (out_dev)->dev; dev_hold(rth->dst.dev); - rth->idev = in_dev_get(rth->dst.dev); rth->fl.oif = 0; rth->rt_spec_dst= spec_dst; @@ -2231,7 +2209,6 @@ local_input: rth->fl.iif = dev->ifindex; rth->dst.dev = net->loopback_dev; dev_hold(rth->dst.dev); - rth->idev = in_dev_get(rth->dst.dev); rth->rt_gateway = daddr; rth->rt_spec_dst= spec_dst; rth->dst.input= ip_local_deliver; @@ -2417,9 +2394,6 @@ static int __mkroute_output(struct rtable **result, if (!rth) return -ENOBUFS; - in_dev_hold(in_dev); - rth->idev = in_dev; - atomic_set(&rth->dst.__refcnt, 1); rth->dst.flags= DST_HOST; if (IN_DEV_CONF_GET(in_dev, NOXFRM)) @@ -2759,9 +2733,6 @@ static int ipv4_dst_blackhole(struct net *net, struct rtable **rp, struct flowi rt->fl = ort->fl; - rt->idev = ort->idev; - if (rt->idev) - in_dev_hold(rt->idev); rt->rt_genid = rt_genid(net); rt->rt_flags = ort->rt_flags; rt->rt_type = ort->rt_type; diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 4464f3bff6a7..dd1fd8c473fc 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -80,10 +80,6 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, xdst->u.dst.dev = dev; dev_hold(dev); - xdst->u.rt.idev = in_dev_get(dev); - if (!xdst->u.rt.idev) - return -ENODEV; - xdst->u.rt.peer = rt->peer; if (rt->peer) atomic_inc(&rt->peer->refcnt); @@ -189,8 +185,6 @@ static void xfrm4_dst_destroy(struct dst_entry *dst) { struct xfrm_dst *xdst = (struct xfrm_dst *)dst; - if (likely(xdst->u.rt.idev)) - in_dev_put(xdst->u.rt.idev); if (likely(xdst->u.rt.peer)) inet_putpeer(xdst->u.rt.peer); xfrm_dst_destroy(xdst); @@ -199,27 +193,9 @@ static void xfrm4_dst_destroy(struct dst_entry *dst) static void xfrm4_dst_ifdown(struct dst_entry *dst, struct net_device *dev, int unregister) { - struct xfrm_dst *xdst; - if (!unregister) return; - xdst = (struct xfrm_dst *)dst; - if (xdst->u.rt.idev->dev == dev) { - struct in_device *loopback_idev = - in_dev_get(dev_net(dev)->loopback_dev); - BUG_ON(!loopback_idev); - - do { - in_dev_put(xdst->u.rt.idev); - xdst->u.rt.idev = loopback_idev; - in_dev_hold(loopback_idev); - xdst = (struct xfrm_dst *)xdst->u.dst.child; - } while (xdst->u.dst.xfrm); - - __in_dev_put(loopback_idev); - } - xfrm_dst_ifdown(dst, dev); } -- cgit v1.2.3 From c753796769e4fb0cd813b6e5801b3c01f4681d4f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 11 Nov 2010 17:07:48 -0800 Subject: ipv4: Make rt->fl.iif tests lest obscure. When we test rt->fl.iif against zero, we're seeing if it's an output or an input route. Make that explicit with some helper functions. Signed-off-by: David S. Miller --- include/net/dn_route.h | 10 ++++++++++ include/net/route.h | 10 ++++++++++ net/decnet/dn_route.c | 4 ++-- net/ipv4/icmp.c | 4 ++-- net/ipv4/igmp.c | 2 +- net/ipv4/ip_gre.c | 2 +- net/ipv4/ipmr.c | 2 +- net/ipv4/route.c | 20 ++++++++++---------- net/netfilter/ipvs/ip_vs_xmit.c | 8 +++++--- 9 files changed, 42 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/net/dn_route.h b/include/net/dn_route.h index ccadab3aa3f6..9b185df265fb 100644 --- a/include/net/dn_route.h +++ b/include/net/dn_route.h @@ -80,6 +80,16 @@ struct dn_route { unsigned rt_type; }; +static inline bool dn_is_input_route(struct dn_route *rt) +{ + return rt->fl.iif != 0; +} + +static inline bool dn_is_output_route(struct dn_route *rt) +{ + return rt->fl.iif == 0; +} + extern void dn_route_init(void); extern void dn_route_cleanup(void); diff --git a/include/net/route.h b/include/net/route.h index cea533eaa853..5cd46d1c0e14 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -71,6 +71,16 @@ struct rtable { struct inet_peer *peer; /* long-living peer info */ }; +static inline bool rt_is_input_route(struct rtable *rt) +{ + return rt->fl.iif != 0; +} + +static inline bool rt_is_output_route(struct rtable *rt) +{ + return rt->fl.iif == 0; +} + struct ip_rt_acct { __u32 o_bytes; __u32 o_packets; diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 94a9eb1d313e..474d54dd08c2 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1181,7 +1181,7 @@ static int __dn_route_output_key(struct dst_entry **pprt, const struct flowi *fl if ((flp->fld_dst == rt->fl.fld_dst) && (flp->fld_src == rt->fl.fld_src) && (flp->mark == rt->fl.mark) && - (rt->fl.iif == 0) && + dn_is_output_route(rt) && (rt->fl.oif == flp->oif)) { dst_use(&rt->dst, jiffies); rcu_read_unlock_bh(); @@ -1512,7 +1512,7 @@ static int dn_rt_fill_info(struct sk_buff *skb, u32 pid, u32 seq, if (rtnl_put_cacheinfo(skb, &rt->dst, 0, 0, 0, expires, rt->dst.error) < 0) goto rtattr_failure; - if (rt->fl.iif) + if (dn_is_input_route(rt)) RTA_PUT(skb, RTA_IIF, sizeof(int), &rt->fl.iif); nlh->nlmsg_len = skb_tail_pointer(skb) - b; diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 96bc7f9475a3..c6e2affafbd3 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -506,8 +506,8 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) struct net_device *dev = NULL; rcu_read_lock(); - if (rt->fl.iif && - net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr) + if (rt_is_input_route(rt) && + net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr) dev = dev_get_by_index_rcu(net, rt->fl.iif); if (dev) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index c8877c6c7216..08d0d81ffc15 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -961,7 +961,7 @@ int igmp_rcv(struct sk_buff *skb) case IGMP_HOST_MEMBERSHIP_REPORT: case IGMPV2_HOST_MEMBERSHIP_REPORT: /* Is it our report looped back? */ - if (skb_rtable(skb)->fl.iif == 0) + if (rt_is_output_route(skb_rtable(skb))) break; /* don't rely on MC router hearing unicast reports */ if (skb->pkt_type == PACKET_MULTICAST || diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 70ff77f02eee..cab2057d5430 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -634,7 +634,7 @@ static int ipgre_rcv(struct sk_buff *skb) #ifdef CONFIG_NET_IPGRE_BROADCAST if (ipv4_is_multicast(iph->daddr)) { /* Looped back packet, drop it! */ - if (skb_rtable(skb)->fl.iif == 0) + if (rt_is_output_route(skb_rtable(skb))) goto drop; tunnel->dev->stats.multicast++; skb->pkt_type = PACKET_BROADCAST; diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 86dd5691af46..ef2b0089e0ea 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1654,7 +1654,7 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt, if (mrt->vif_table[vif].dev != skb->dev) { int true_vifi; - if (skb_rtable(skb)->fl.iif == 0) { + if (rt_is_output_route(skb_rtable(skb))) { /* It is our own packet, looped back. * Very complicated situation... * diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 5955965c7953..66610ea3c87b 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -623,7 +623,7 @@ static inline int rt_fast_clean(struct rtable *rth) /* Kill broadcast/multicast entries very aggresively, if they collide in hash table with more useful entries */ return (rth->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) && - rth->fl.iif && rth->dst.rt_next; + rt_is_input_route(rth) && rth->dst.rt_next; } static inline int rt_valuable(struct rtable *rth) @@ -668,7 +668,7 @@ static inline u32 rt_score(struct rtable *rt) if (rt_valuable(rt)) score |= (1<<31); - if (!rt->fl.iif || + if (rt_is_output_route(rt) || !(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL))) score |= (1<<30); @@ -1126,7 +1126,7 @@ restart: */ rt->dst.flags |= DST_NOCACHE; - if (rt->rt_type == RTN_UNICAST || rt->fl.iif == 0) { + if (rt->rt_type == RTN_UNICAST || rt_is_output_route(rt)) { int err = arp_bind_neighbour(&rt->dst); if (err) { if (net_ratelimit()) @@ -1224,7 +1224,7 @@ restart: /* Try to bind route to arp only if it is output route or unicast forwarding path. */ - if (rt->rt_type == RTN_UNICAST || rt->fl.iif == 0) { + if (rt->rt_type == RTN_UNICAST || rt_is_output_route(rt)) { int err = arp_bind_neighbour(&rt->dst); if (err) { spin_unlock_bh(rt_hash_lock_addr(hash)); @@ -1406,7 +1406,7 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, if (rth->fl.fl4_dst != daddr || rth->fl.fl4_src != skeys[i] || rth->fl.oif != ikeys[k] || - rth->fl.iif != 0 || + rt_is_input_route(rth) || rt_is_expired(rth) || !net_eq(dev_net(rth->dst.dev), net)) { rthp = &rth->dst.rt_next; @@ -1666,7 +1666,7 @@ unsigned short ip_rt_frag_needed(struct net *net, struct iphdr *iph, rth->rt_dst != daddr || rth->rt_src != iph->saddr || rth->fl.oif != ikeys[k] || - rth->fl.iif != 0 || + rt_is_input_route(rth) || dst_metric_locked(&rth->dst, RTAX_MTU) || !net_eq(dev_net(rth->dst.dev), net) || rt_is_expired(rth)) @@ -1770,7 +1770,7 @@ void ip_rt_get_source(u8 *addr, struct rtable *rt) __be32 src; struct fib_result res; - if (rt->fl.iif == 0) + if (rt_is_output_route(rt)) src = rt->rt_src; else { rcu_read_lock(); @@ -2669,7 +2669,7 @@ int __ip_route_output_key(struct net *net, struct rtable **rp, rth = rcu_dereference_bh(rth->dst.rt_next)) { if (rth->fl.fl4_dst == flp->fl4_dst && rth->fl.fl4_src == flp->fl4_src && - rth->fl.iif == 0 && + rt_is_output_route(rth) && rth->fl.oif == flp->oif && rth->fl.mark == flp->mark && !((rth->fl.fl4_tos ^ flp->fl4_tos) & @@ -2824,7 +2824,7 @@ static int rt_fill_info(struct net *net, if (rt->dst.tclassid) NLA_PUT_U32(skb, RTA_FLOW, rt->dst.tclassid); #endif - if (rt->fl.iif) + 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) NLA_PUT_BE32(skb, RTA_PREFSRC, rt->rt_src); @@ -2849,7 +2849,7 @@ static int rt_fill_info(struct net *net, } } - if (rt->fl.iif) { + if (rt_is_input_route(rt)) { #ifdef CONFIG_IP_MROUTE __be32 dst = rt->rt_dst; diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index de04ea39cde8..10bd39c0ae2d 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -169,7 +169,7 @@ __ip_vs_reroute_locally(struct sk_buff *skb) struct net *net = dev_net(dev); struct iphdr *iph = ip_hdr(skb); - if (rt->fl.iif) { + if (rt_is_input_route(rt)) { unsigned long orefdst = skb->_skb_refdst; if (ip_route_input(skb, iph->daddr, iph->saddr, @@ -552,7 +552,8 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, #endif /* From world but DNAT to loopback address? */ - if (local && ipv4_is_loopback(rt->rt_dst) && skb_rtable(skb)->fl.iif) { + if (local && ipv4_is_loopback(rt->rt_dst) && + rt_is_input_route(skb_rtable(skb))) { IP_VS_DBG_RL_PKT(1, AF_INET, pp, skb, 0, "ip_vs_nat_xmit(): " "stopping DNAT to loopback address"); goto tx_error_put; @@ -1165,7 +1166,8 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, #endif /* From world but DNAT to loopback address? */ - if (local && ipv4_is_loopback(rt->rt_dst) && skb_rtable(skb)->fl.iif) { + if (local && ipv4_is_loopback(rt->rt_dst) && + rt_is_input_route(skb_rtable(skb))) { IP_VS_DBG(1, "%s(): " "stopping DNAT to loopback %pI4\n", __func__, &cp->daddr.ip); -- cgit v1.2.3 From 1d7138de878d1d4210727c1200193e69596f93b3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 12 Nov 2010 05:46:50 +0000 Subject: igmp: RCU conversion of in_dev->mc_list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit in_dev->mc_list is protected by one rwlock (in_dev->mc_list_lock). This can easily be converted to a RCU protection. Writers hold RTNL, so mc_list_lock is removed, not replaced by a spinlock. Signed-off-by: Eric Dumazet Cc: Cypher Wu Cc: Américo Wang Signed-off-by: David S. Miller --- include/linux/igmp.h | 12 ++- include/linux/inetdevice.h | 5 +- include/net/inet_sock.h | 2 +- net/ipv4/igmp.c | 223 +++++++++++++++++++++------------------------ 4 files changed, 115 insertions(+), 127 deletions(-) (limited to 'include') diff --git a/include/linux/igmp.h b/include/linux/igmp.h index 93fc2449af10..7d164670f264 100644 --- a/include/linux/igmp.h +++ b/include/linux/igmp.h @@ -167,10 +167,10 @@ struct ip_sf_socklist { */ struct ip_mc_socklist { - struct ip_mc_socklist *next; + struct ip_mc_socklist __rcu *next_rcu; struct ip_mreqn multi; unsigned int sfmode; /* MCAST_{INCLUDE,EXCLUDE} */ - struct ip_sf_socklist *sflist; + struct ip_sf_socklist __rcu *sflist; struct rcu_head rcu; }; @@ -186,11 +186,14 @@ struct ip_sf_list { struct ip_mc_list { struct in_device *interface; __be32 multiaddr; + unsigned int sfmode; struct ip_sf_list *sources; struct ip_sf_list *tomb; - unsigned int sfmode; unsigned long sfcount[2]; - struct ip_mc_list *next; + union { + struct ip_mc_list *next; + struct ip_mc_list __rcu *next_rcu; + }; struct timer_list timer; int users; atomic_t refcnt; @@ -201,6 +204,7 @@ struct ip_mc_list { char loaded; unsigned char gsquery; /* check source marks? */ unsigned char crcount; + struct rcu_head rcu; }; /* V3 exponential field decoding */ diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index ccd5b07d678d..380ba6bc5db1 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -52,9 +52,8 @@ struct in_device { atomic_t refcnt; int dead; struct in_ifaddr *ifa_list; /* IP ifaddr chain */ - rwlock_t mc_list_lock; - struct ip_mc_list *mc_list; /* IP multicast filter chain */ - int mc_count; /* Number of installed mcasts */ + struct ip_mc_list __rcu *mc_list; /* IP multicast filter chain */ + int mc_count; /* Number of installed mcasts */ spinlock_t mc_tomb_lock; struct ip_mc_list *mc_tomb; unsigned long mr_v1_seen; diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 1989cfd7405f..8945f9fb192a 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -141,7 +141,7 @@ struct inet_sock { nodefrag:1; int mc_index; __be32 mc_addr; - struct ip_mc_socklist *mc_list; + struct ip_mc_socklist __rcu *mc_list; struct { unsigned int flags; unsigned int fragsize; diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 08d0d81ffc15..6f49d6c087da 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -149,11 +149,17 @@ static void ip_mc_clear_src(struct ip_mc_list *pmc); static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, int sfcount, __be32 *psfsrc, int delta); + +static void ip_mc_list_reclaim(struct rcu_head *head) +{ + kfree(container_of(head, struct ip_mc_list, rcu)); +} + static void ip_ma_put(struct ip_mc_list *im) { if (atomic_dec_and_test(&im->refcnt)) { in_dev_put(im->interface); - kfree(im); + call_rcu(&im->rcu, ip_mc_list_reclaim); } } @@ -163,7 +169,7 @@ static void ip_ma_put(struct ip_mc_list *im) * Timer management */ -static __inline__ void igmp_stop_timer(struct ip_mc_list *im) +static void igmp_stop_timer(struct ip_mc_list *im) { spin_lock_bh(&im->lock); if (del_timer(&im->timer)) @@ -496,14 +502,24 @@ empty_source: return skb; } +#define for_each_pmc_rcu(in_dev, pmc) \ + for (pmc = rcu_dereference(in_dev->mc_list); \ + pmc != NULL; \ + pmc = rcu_dereference(pmc->next_rcu)) + +#define for_each_pmc_rtnl(in_dev, pmc) \ + for (pmc = rtnl_dereference(in_dev->mc_list); \ + pmc != NULL; \ + pmc = rtnl_dereference(pmc->next_rcu)) + static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc) { struct sk_buff *skb = NULL; int type; if (!pmc) { - read_lock(&in_dev->mc_list_lock); - for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { + rcu_read_lock(); + for_each_pmc_rcu(in_dev, pmc) { if (pmc->multiaddr == IGMP_ALL_HOSTS) continue; spin_lock_bh(&pmc->lock); @@ -514,7 +530,7 @@ static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc) skb = add_grec(skb, pmc, type, 0, 0); spin_unlock_bh(&pmc->lock); } - read_unlock(&in_dev->mc_list_lock); + rcu_read_unlock(); } else { spin_lock_bh(&pmc->lock); if (pmc->sfcount[MCAST_EXCLUDE]) @@ -556,7 +572,7 @@ static void igmpv3_send_cr(struct in_device *in_dev) struct sk_buff *skb = NULL; int type, dtype; - read_lock(&in_dev->mc_list_lock); + rcu_read_lock(); spin_lock_bh(&in_dev->mc_tomb_lock); /* deleted MCA's */ @@ -593,7 +609,7 @@ static void igmpv3_send_cr(struct in_device *in_dev) spin_unlock_bh(&in_dev->mc_tomb_lock); /* change recs */ - for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { + for_each_pmc_rcu(in_dev, pmc) { spin_lock_bh(&pmc->lock); if (pmc->sfcount[MCAST_EXCLUDE]) { type = IGMPV3_BLOCK_OLD_SOURCES; @@ -616,7 +632,7 @@ static void igmpv3_send_cr(struct in_device *in_dev) } spin_unlock_bh(&pmc->lock); } - read_unlock(&in_dev->mc_list_lock); + rcu_read_unlock(); if (!skb) return; @@ -813,14 +829,14 @@ static void igmp_heard_report(struct in_device *in_dev, __be32 group) if (group == IGMP_ALL_HOSTS) return; - read_lock(&in_dev->mc_list_lock); - for (im=in_dev->mc_list; im!=NULL; im=im->next) { + rcu_read_lock(); + for_each_pmc_rcu(in_dev, im) { if (im->multiaddr == group) { igmp_stop_timer(im); break; } } - read_unlock(&in_dev->mc_list_lock); + rcu_read_unlock(); } static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, @@ -906,8 +922,8 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, * - Use the igmp->igmp_code field as the maximum * delay possible */ - read_lock(&in_dev->mc_list_lock); - for (im=in_dev->mc_list; im!=NULL; im=im->next) { + rcu_read_lock(); + for_each_pmc_rcu(in_dev, im) { int changed; if (group && group != im->multiaddr) @@ -925,7 +941,7 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, if (changed) igmp_mod_timer(im, max_delay); } - read_unlock(&in_dev->mc_list_lock); + rcu_read_unlock(); } /* called in rcu_read_lock() section */ @@ -1110,8 +1126,8 @@ static void igmpv3_clear_delrec(struct in_device *in_dev) kfree(pmc); } /* clear dead sources, too */ - read_lock(&in_dev->mc_list_lock); - for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { + rcu_read_lock(); + for_each_pmc_rcu(in_dev, pmc) { struct ip_sf_list *psf, *psf_next; spin_lock_bh(&pmc->lock); @@ -1123,7 +1139,7 @@ static void igmpv3_clear_delrec(struct in_device *in_dev) kfree(psf); } } - read_unlock(&in_dev->mc_list_lock); + rcu_read_unlock(); } #endif @@ -1209,7 +1225,7 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) ASSERT_RTNL(); - for (im=in_dev->mc_list; im; im=im->next) { + for_each_pmc_rtnl(in_dev, im) { if (im->multiaddr == addr) { im->users++; ip_mc_add_src(in_dev, &addr, MCAST_EXCLUDE, 0, NULL, 0); @@ -1217,7 +1233,7 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) } } - im = kmalloc(sizeof(*im), GFP_KERNEL); + im = kzalloc(sizeof(*im), GFP_KERNEL); if (!im) goto out; @@ -1227,26 +1243,18 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) im->multiaddr = addr; /* initial mode is (EX, empty) */ im->sfmode = MCAST_EXCLUDE; - im->sfcount[MCAST_INCLUDE] = 0; im->sfcount[MCAST_EXCLUDE] = 1; - im->sources = NULL; - im->tomb = NULL; - im->crcount = 0; atomic_set(&im->refcnt, 1); spin_lock_init(&im->lock); #ifdef CONFIG_IP_MULTICAST - im->tm_running = 0; setup_timer(&im->timer, &igmp_timer_expire, (unsigned long)im); im->unsolicit_count = IGMP_Unsolicited_Report_Count; - im->reporter = 0; - im->gsquery = 0; #endif - im->loaded = 0; - write_lock_bh(&in_dev->mc_list_lock); - im->next = in_dev->mc_list; - in_dev->mc_list = im; + + im->next_rcu = in_dev->mc_list; in_dev->mc_count++; - write_unlock_bh(&in_dev->mc_list_lock); + rcu_assign_pointer(in_dev->mc_list, im); + #ifdef CONFIG_IP_MULTICAST igmpv3_del_delrec(in_dev, im->multiaddr); #endif @@ -1287,17 +1295,18 @@ EXPORT_SYMBOL(ip_mc_rejoin_group); void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) { - struct ip_mc_list *i, **ip; + struct ip_mc_list *i; + struct ip_mc_list __rcu **ip; ASSERT_RTNL(); - for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) { + for (ip = &in_dev->mc_list; + (i = rtnl_dereference(*ip)) != NULL; + ip = &i->next_rcu) { if (i->multiaddr == addr) { if (--i->users == 0) { - write_lock_bh(&in_dev->mc_list_lock); - *ip = i->next; + *ip = i->next_rcu; in_dev->mc_count--; - write_unlock_bh(&in_dev->mc_list_lock); igmp_group_dropped(i); if (!in_dev->dead) @@ -1316,34 +1325,34 @@ EXPORT_SYMBOL(ip_mc_dec_group); void ip_mc_unmap(struct in_device *in_dev) { - struct ip_mc_list *i; + struct ip_mc_list *pmc; ASSERT_RTNL(); - for (i = in_dev->mc_list; i; i = i->next) - igmp_group_dropped(i); + for_each_pmc_rtnl(in_dev, pmc) + igmp_group_dropped(pmc); } void ip_mc_remap(struct in_device *in_dev) { - struct ip_mc_list *i; + struct ip_mc_list *pmc; ASSERT_RTNL(); - for (i = in_dev->mc_list; i; i = i->next) - igmp_group_added(i); + for_each_pmc_rtnl(in_dev, pmc) + igmp_group_added(pmc); } /* Device going down */ void ip_mc_down(struct in_device *in_dev) { - struct ip_mc_list *i; + struct ip_mc_list *pmc; ASSERT_RTNL(); - for (i=in_dev->mc_list; i; i=i->next) - igmp_group_dropped(i); + for_each_pmc_rtnl(in_dev, pmc) + igmp_group_dropped(pmc); #ifdef CONFIG_IP_MULTICAST in_dev->mr_ifc_count = 0; @@ -1374,7 +1383,6 @@ void ip_mc_init_dev(struct in_device *in_dev) in_dev->mr_qrv = IGMP_Unsolicited_Report_Count; #endif - rwlock_init(&in_dev->mc_list_lock); spin_lock_init(&in_dev->mc_tomb_lock); } @@ -1382,14 +1390,14 @@ void ip_mc_init_dev(struct in_device *in_dev) void ip_mc_up(struct in_device *in_dev) { - struct ip_mc_list *i; + struct ip_mc_list *pmc; ASSERT_RTNL(); ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS); - for (i=in_dev->mc_list; i; i=i->next) - igmp_group_added(i); + for_each_pmc_rtnl(in_dev, pmc) + igmp_group_added(pmc); } /* @@ -1405,17 +1413,13 @@ void ip_mc_destroy_dev(struct in_device *in_dev) /* Deactivate timers */ ip_mc_down(in_dev); - write_lock_bh(&in_dev->mc_list_lock); - while ((i = in_dev->mc_list) != NULL) { - in_dev->mc_list = i->next; + while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) { + in_dev->mc_list = i->next_rcu; in_dev->mc_count--; - write_unlock_bh(&in_dev->mc_list_lock); + igmp_group_dropped(i); ip_ma_put(i); - - write_lock_bh(&in_dev->mc_list_lock); } - write_unlock_bh(&in_dev->mc_list_lock); } /* RTNL is locked */ @@ -1513,18 +1517,18 @@ static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode, if (!in_dev) return -ENODEV; - read_lock(&in_dev->mc_list_lock); - for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { + rcu_read_lock(); + for_each_pmc_rcu(in_dev, pmc) { if (*pmca == pmc->multiaddr) break; } if (!pmc) { /* MCA not found?? bug */ - read_unlock(&in_dev->mc_list_lock); + rcu_read_unlock(); return -ESRCH; } spin_lock_bh(&pmc->lock); - read_unlock(&in_dev->mc_list_lock); + rcu_read_unlock(); #ifdef CONFIG_IP_MULTICAST sf_markstate(pmc); #endif @@ -1685,18 +1689,18 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, if (!in_dev) return -ENODEV; - read_lock(&in_dev->mc_list_lock); - for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) { + rcu_read_lock(); + for_each_pmc_rcu(in_dev, pmc) { if (*pmca == pmc->multiaddr) break; } if (!pmc) { /* MCA not found?? bug */ - read_unlock(&in_dev->mc_list_lock); + rcu_read_unlock(); return -ESRCH; } spin_lock_bh(&pmc->lock); - read_unlock(&in_dev->mc_list_lock); + rcu_read_unlock(); #ifdef CONFIG_IP_MULTICAST sf_markstate(pmc); @@ -1793,7 +1797,7 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) err = -EADDRINUSE; ifindex = imr->imr_ifindex; - for (i = inet->mc_list; i; i = i->next) { + for_each_pmc_rtnl(inet, i) { if (i->multi.imr_multiaddr.s_addr == addr && i->multi.imr_ifindex == ifindex) goto done; @@ -1807,7 +1811,7 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) goto done; memcpy(&iml->multi, imr, sizeof(*imr)); - iml->next = inet->mc_list; + iml->next_rcu = inet->mc_list; iml->sflist = NULL; iml->sfmode = MCAST_EXCLUDE; rcu_assign_pointer(inet->mc_list, iml); @@ -1821,17 +1825,14 @@ EXPORT_SYMBOL(ip_mc_join_group); static void ip_sf_socklist_reclaim(struct rcu_head *rp) { - struct ip_sf_socklist *psf; - - psf = container_of(rp, struct ip_sf_socklist, rcu); + kfree(container_of(rp, struct ip_sf_socklist, rcu)); /* sk_omem_alloc should have been decreased by the caller*/ - kfree(psf); } static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, struct in_device *in_dev) { - struct ip_sf_socklist *psf = iml->sflist; + struct ip_sf_socklist *psf = rtnl_dereference(iml->sflist); int err; if (psf == NULL) { @@ -1851,11 +1852,8 @@ static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, static void ip_mc_socklist_reclaim(struct rcu_head *rp) { - struct ip_mc_socklist *iml; - - iml = container_of(rp, struct ip_mc_socklist, rcu); + kfree(container_of(rp, struct ip_mc_socklist, rcu)); /* sk_omem_alloc should have been decreased by the caller*/ - kfree(iml); } @@ -1866,7 +1864,8 @@ static void ip_mc_socklist_reclaim(struct rcu_head *rp) int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) { struct inet_sock *inet = inet_sk(sk); - struct ip_mc_socklist *iml, **imlp; + struct ip_mc_socklist *iml; + struct ip_mc_socklist __rcu **imlp; struct in_device *in_dev; struct net *net = sock_net(sk); __be32 group = imr->imr_multiaddr.s_addr; @@ -1876,7 +1875,9 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) rtnl_lock(); in_dev = ip_mc_find_dev(net, imr); ifindex = imr->imr_ifindex; - for (imlp = &inet->mc_list; (iml = *imlp) != NULL; imlp = &iml->next) { + for (imlp = &inet->mc_list; + (iml = rtnl_dereference(*imlp)) != NULL; + imlp = &iml->next_rcu) { if (iml->multi.imr_multiaddr.s_addr != group) continue; if (ifindex) { @@ -1888,7 +1889,7 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) (void) ip_mc_leave_src(sk, iml, in_dev); - rcu_assign_pointer(*imlp, iml->next); + *imlp = iml->next_rcu; if (in_dev) ip_mc_dec_group(in_dev, group); @@ -1934,7 +1935,7 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct } err = -EADDRNOTAVAIL; - for (pmc=inet->mc_list; pmc; pmc=pmc->next) { + for_each_pmc_rtnl(inet, pmc) { if ((pmc->multi.imr_multiaddr.s_addr == imr.imr_multiaddr.s_addr) && (pmc->multi.imr_ifindex == imr.imr_ifindex)) @@ -1958,7 +1959,7 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct pmc->sfmode = omode; } - psl = pmc->sflist; + psl = rtnl_dereference(pmc->sflist); if (!add) { if (!psl) goto done; /* err = -EADDRNOTAVAIL */ @@ -2077,7 +2078,7 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) goto done; } - for (pmc=inet->mc_list; pmc; pmc=pmc->next) { + for_each_pmc_rtnl(inet, pmc) { if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && pmc->multi.imr_ifindex == imr.imr_ifindex) break; @@ -2107,7 +2108,7 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) (void) ip_mc_add_src(in_dev, &msf->imsf_multiaddr, msf->imsf_fmode, 0, NULL, 0); } - psl = pmc->sflist; + psl = rtnl_dereference(pmc->sflist); if (psl) { (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, psl->sl_count, psl->sl_addr, 0); @@ -2155,7 +2156,7 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, } err = -EADDRNOTAVAIL; - for (pmc=inet->mc_list; pmc; pmc=pmc->next) { + for_each_pmc_rtnl(inet, pmc) { if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && pmc->multi.imr_ifindex == imr.imr_ifindex) break; @@ -2163,7 +2164,7 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, if (!pmc) /* must have a prior join */ goto done; msf->imsf_fmode = pmc->sfmode; - psl = pmc->sflist; + psl = rtnl_dereference(pmc->sflist); rtnl_unlock(); if (!psl) { len = 0; @@ -2208,7 +2209,7 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, err = -EADDRNOTAVAIL; - for (pmc=inet->mc_list; pmc; pmc=pmc->next) { + for_each_pmc_rtnl(inet, pmc) { if (pmc->multi.imr_multiaddr.s_addr == addr && pmc->multi.imr_ifindex == gsf->gf_interface) break; @@ -2216,7 +2217,7 @@ int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, if (!pmc) /* must have a prior join */ goto done; gsf->gf_fmode = pmc->sfmode; - psl = pmc->sflist; + psl = rtnl_dereference(pmc->sflist); rtnl_unlock(); count = psl ? psl->sl_count : 0; copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc; @@ -2257,7 +2258,7 @@ int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, int dif) goto out; rcu_read_lock(); - for (pmc=rcu_dereference(inet->mc_list); pmc; pmc=rcu_dereference(pmc->next)) { + for_each_pmc_rcu(inet, pmc) { if (pmc->multi.imr_multiaddr.s_addr == loc_addr && pmc->multi.imr_ifindex == dif) break; @@ -2265,7 +2266,7 @@ int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, int dif) ret = inet->mc_all; if (!pmc) goto unlock; - psl = pmc->sflist; + psl = rcu_dereference(pmc->sflist); ret = (pmc->sfmode == MCAST_EXCLUDE); if (!psl) goto unlock; @@ -2300,10 +2301,10 @@ void ip_mc_drop_socket(struct sock *sk) return; rtnl_lock(); - while ((iml = inet->mc_list) != NULL) { + while ((iml = rtnl_dereference(inet->mc_list)) != NULL) { struct in_device *in_dev; - rcu_assign_pointer(inet->mc_list, iml->next); + inet->mc_list = iml->next_rcu; in_dev = inetdev_by_index(net, iml->multi.imr_ifindex); (void) ip_mc_leave_src(sk, iml, in_dev); if (in_dev != NULL) { @@ -2323,8 +2324,8 @@ int ip_check_mc(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 p struct ip_sf_list *psf; int rv = 0; - read_lock(&in_dev->mc_list_lock); - for (im=in_dev->mc_list; im; im=im->next) { + rcu_read_lock(); + for_each_pmc_rcu(in_dev, im) { if (im->multiaddr == mc_addr) break; } @@ -2345,7 +2346,7 @@ int ip_check_mc(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 p } else rv = 1; /* unspecified source; tentatively allow */ } - read_unlock(&in_dev->mc_list_lock); + rcu_read_unlock(); return rv; } @@ -2371,13 +2372,11 @@ static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq) in_dev = __in_dev_get_rcu(state->dev); if (!in_dev) continue; - read_lock(&in_dev->mc_list_lock); - im = in_dev->mc_list; + im = rcu_dereference(in_dev->mc_list); if (im) { state->in_dev = in_dev; break; } - read_unlock(&in_dev->mc_list_lock); } return im; } @@ -2385,11 +2384,9 @@ static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq) static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_list *im) { struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); - im = im->next; - while (!im) { - if (likely(state->in_dev != NULL)) - read_unlock(&state->in_dev->mc_list_lock); + im = rcu_dereference(im->next_rcu); + while (!im) { state->dev = next_net_device_rcu(state->dev); if (!state->dev) { state->in_dev = NULL; @@ -2398,8 +2395,7 @@ static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_li state->in_dev = __in_dev_get_rcu(state->dev); if (!state->in_dev) continue; - read_lock(&state->in_dev->mc_list_lock); - im = state->in_dev->mc_list; + im = rcu_dereference(state->in_dev->mc_list); } return im; } @@ -2435,10 +2431,8 @@ static void igmp_mc_seq_stop(struct seq_file *seq, void *v) __releases(rcu) { struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); - if (likely(state->in_dev != NULL)) { - read_unlock(&state->in_dev->mc_list_lock); - state->in_dev = NULL; - } + + state->in_dev = NULL; state->dev = NULL; rcu_read_unlock(); } @@ -2460,7 +2454,7 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v) querier = "NONE"; #endif - if (state->in_dev->mc_list == im) { + if (rcu_dereference(state->in_dev->mc_list) == im) { seq_printf(seq, "%d\t%-10s: %5d %7s\n", state->dev->ifindex, state->dev->name, state->in_dev->mc_count, querier); } @@ -2519,8 +2513,7 @@ static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq) idev = __in_dev_get_rcu(state->dev); if (unlikely(idev == NULL)) continue; - read_lock(&idev->mc_list_lock); - im = idev->mc_list; + im = rcu_dereference(idev->mc_list); if (likely(im != NULL)) { spin_lock_bh(&im->lock); psf = im->sources; @@ -2531,7 +2524,6 @@ static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq) } spin_unlock_bh(&im->lock); } - read_unlock(&idev->mc_list_lock); } return psf; } @@ -2545,9 +2537,6 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l spin_unlock_bh(&state->im->lock); state->im = state->im->next; while (!state->im) { - if (likely(state->idev != NULL)) - read_unlock(&state->idev->mc_list_lock); - state->dev = next_net_device_rcu(state->dev); if (!state->dev) { state->idev = NULL; @@ -2556,8 +2545,7 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l state->idev = __in_dev_get_rcu(state->dev); if (!state->idev) continue; - read_lock(&state->idev->mc_list_lock); - state->im = state->idev->mc_list; + state->im = rcu_dereference(state->idev->mc_list); } if (!state->im) break; @@ -2603,10 +2591,7 @@ static void igmp_mcf_seq_stop(struct seq_file *seq, void *v) spin_unlock_bh(&state->im->lock); state->im = NULL; } - if (likely(state->idev != NULL)) { - read_unlock(&state->idev->mc_list_lock); - state->idev = NULL; - } + state->idev = NULL; state->dev = NULL; rcu_read_unlock(); } -- cgit v1.2.3 From 9fbbdde93231ad7f35c217aa6bbbc7995133f483 Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Thu, 11 Nov 2010 15:44:43 +0100 Subject: video: add fb_edid_add_monspecs for parsing extended edid information Modern monitors/tvs have more extended EDID information blocks which can contain extra detailed modes. This adds a fb_edid_add_monspecs function which drivers can use to parse those additions blocks. Signed-off-by: Erik Gilling Signed-off-by: Guennadi Liakhovetski Signed-off-by: Paul Mundt --- drivers/video/fbmon.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fb.h | 2 ++ 2 files changed, 59 insertions(+) (limited to 'include') diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index 563a98b88e9b..a0b5a93b72d2 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -973,6 +973,63 @@ void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) DPRINTK("========================================\n"); } +void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) +{ + unsigned char *block; + struct fb_videomode *mode, *m; + int num = 0, i, first = 1; + + if (edid == NULL) + return; + + if (!edid_checksum(edid)) + return; + + if (edid[0] != 0x2) + return; + + mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL); + if (mode == NULL) + return; + + block = edid + edid[0x2]; + + DPRINTK(" Extended Detailed Timings\n"); + + for (i = 0; i < (128 - edid[0x2]) / DETAILED_TIMING_DESCRIPTION_SIZE; + i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) { + if (!(block[0] == 0x00 && block[1] == 0x00)) { + get_detailed_timing(block, &mode[num]); + if (first) { + mode[num].flag |= FB_MODE_IS_FIRST; + first = 0; + } + num++; + } + } + + /* Yikes, EDID data is totally useless */ + if (!num) { + kfree(mode); + return; + } + + m = kzalloc((specs->modedb_len + num) * + sizeof(struct fb_videomode), GFP_KERNEL); + + if (!m) { + kfree(mode); + return; + } + + memmove(m, specs->modedb, specs->modedb_len * sizeof(struct fb_videomode)); + memmove(m + specs->modedb_len, mode, num * sizeof(struct fb_videomode)); + kfree(mode); + kfree(specs->modedb); + specs->modedb = m; + specs->modedb_len = specs->modedb_len + num; +} + /* * VESA Generalized Timing Formula (GTF) */ diff --git a/include/linux/fb.h b/include/linux/fb.h index 7fca3dc4e475..6f0274d96f0c 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -1092,6 +1092,8 @@ extern int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var); extern const unsigned char *fb_firmware_edid(struct device *device); extern void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs); +extern void fb_edid_add_monspecs(unsigned char *edid, + struct fb_monspecs *specs); extern void fb_destroy_modedb(struct fb_videomode *modedb); extern int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb); extern unsigned char *fb_ddc_read(struct i2c_adapter *adapter); -- cgit v1.2.3 From 0ad83f6882c41df1a7fa387086029e162038c1f2 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 11 Nov 2010 15:45:04 +0100 Subject: fbdev: when parsing E-EDID blocks, also use SVD entries Add parsing of E-EDID SVD entries. In this first version only a few CEA/EIA-861E modes are implemented, more can be added as needed. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Paul Mundt --- drivers/video/fbmon.c | 37 +++++++++++++++++++++++++++++++++---- drivers/video/modedb.c | 43 +++++++++++++++++++++++++++++++++++++++++++ include/linux/fb.h | 1 + 3 files changed, 77 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index b25399abcf49..4f57485f8c54 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -983,7 +983,8 @@ void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) unsigned char *block; struct fb_videomode *m; int num = 0, i; - u8 edt[(128 - 4) / DETAILED_TIMING_DESCRIPTION_SIZE]; + u8 svd[64], edt[(128 - 4) / DETAILED_TIMING_DESCRIPTION_SIZE]; + u8 pos = 4, svd_n = 0; if (!edid) return; @@ -995,6 +996,21 @@ void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) edid[2] < 4 || edid[2] > 128 - DETAILED_TIMING_DESCRIPTION_SIZE) return; + DPRINTK(" Short Video Descriptors\n"); + + while (pos < edid[2]) { + u8 len = edid[pos] & 0x1f, type = (edid[pos] >> 5) & 7; + pr_debug("Data block %u of %u bytes\n", type, len); + if (type == 2) + for (i = pos; i < pos + len; i++) { + u8 idx = edid[pos + i] & 0x7f; + svd[svd_n++] = idx; + pr_debug("N%sative mode #%d\n", + edid[pos + i] & 0x80 ? "" : "on-n", idx); + } + pos += len + 1; + } + block = edid + edid[2]; DPRINTK(" Extended Detailed Timings\n"); @@ -1005,10 +1021,10 @@ void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) edt[num++] = block - edid; /* Yikes, EDID data is totally useless */ - if (!num) + if (!(num + svd_n)) return; - m = kzalloc((specs->modedb_len + num) * + m = kzalloc((specs->modedb_len + num + svd_n) * sizeof(struct fb_videomode), GFP_KERNEL); if (!m) @@ -1023,9 +1039,22 @@ void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) pr_debug("Adding %ux%u@%u\n", m[i].xres, m[i].yres, m[i].refresh); } + for (i = specs->modedb_len + num; i < specs->modedb_len + num + svd_n; i++) { + int idx = svd[i - specs->modedb_len - num]; + if (!idx || idx > 63) { + pr_warning("Reserved SVD code %d\n", idx); + } else if (idx > ARRAY_SIZE(cea_modes) || !cea_modes[idx].xres) { + pr_warning("Unimplemented SVD code %d\n", idx); + } else { + memcpy(&m[i], cea_modes + idx, sizeof(m[i])); + pr_debug("Adding SVD #%d: %ux%u@%u\n", idx, + m[i].xres, m[i].yres, m[i].refresh); + } + } + kfree(specs->modedb); specs->modedb = m; - specs->modedb_len = specs->modedb_len + num; + specs->modedb_len = specs->modedb_len + num + svd_n; } /* diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c index 0a4dbdc1693a..9a0ae6ca5427 100644 --- a/drivers/video/modedb.c +++ b/drivers/video/modedb.c @@ -278,6 +278,49 @@ static const struct fb_videomode modedb[] = { }; #ifdef CONFIG_FB_MODE_HELPERS +const struct fb_videomode cea_modes[64] = { + /* #1: 640x480p@59.94/60Hz */ + [1] = { + NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0, FB_VMODE_NONINTERLACED, 0, + }, + /* #3: 720x480p@59.94/60Hz */ + [3] = { + NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0, FB_VMODE_NONINTERLACED, 0, + }, + /* #5: 1920x1080i@59.94/60Hz */ + [5] = { + NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5, 0, FB_VMODE_INTERLACED, 0, + }, + /* #7: 720(1440)x480iH@59.94/60Hz */ + [7] = { + NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0, FB_VMODE_INTERLACED, 0, + }, + /* #9: 720(1440)x240pH@59.94/60Hz */ + [9] = { + NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0, FB_VMODE_NONINTERLACED, 0, + }, + /* #18: 720x576pH@50Hz */ + [18] = { + NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0, FB_VMODE_NONINTERLACED, 0, + }, + /* #19: 1280x720p@50Hz */ + [19] = { + NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5, 0, FB_VMODE_NONINTERLACED, 0, + }, + /* #20: 1920x1080i@50Hz */ + [20] = { + NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5, 0, FB_VMODE_INTERLACED, 0, + }, + /* #32: 1920x1080p@23.98/24Hz */ + [32] = { + NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5, 0, FB_VMODE_NONINTERLACED, 0, + }, + /* #35: (2880)x480p4x@59.94/60Hz */ + [35] = { + NULL, 50, 2880, 480, 11100, 240, 64, 30, 9, 248, 6, 0, FB_VMODE_NONINTERLACED, 0, + }, +}; + const struct fb_videomode vesa_modes[] = { /* 0 640x350-85 VESA */ { NULL, 85, 640, 350, 31746, 96, 32, 60, 32, 64, 3, diff --git a/include/linux/fb.h b/include/linux/fb.h index 6f0274d96f0c..e154a79b8322 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -1151,6 +1151,7 @@ struct fb_videomode { extern const char *fb_mode_option; extern const struct fb_videomode vesa_modes[]; +extern const struct fb_videomode cea_modes[64]; struct fb_modelist { struct list_head list; -- cgit v1.2.3 From d83447f0944e73d690218d79c07762ffa4ceb9e4 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Sun, 14 Nov 2010 17:25:46 +0100 Subject: dccp ccid-2: Schedule Sync as out-of-band mechanism The problem with Ack Vectors is that i) their length is variable and can in principle grow quite large, ii) it is hard to predict exactly how large they will be. Due to the second point it seems not a good idea to reduce the MPS; in particular when on average there is enough room for the Ack Vector and an increase in length is momentarily due to some burst loss, after which the Ack Vector returns to its normal/average length. The solution taken by this patch is to subtract a minimum-expected Ack Vector length from the MPS, and to defer any larger Ack Vectors onto a separate Sync - but only if indeed there is no space left on the skb. This patch provides the infrastructure to schedule Sync-packets for transporting (urgent) out-of-band data. Its signalling is quicker than scheduling an Ack, since it does not need to wait for new application data. Signed-off-by: Gerrit Renker --- include/linux/dccp.h | 2 ++ net/dccp/options.c | 24 ++++++++++++++++++++---- net/dccp/output.c | 15 +++++++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 749f01ccd26e..eed52bcd35d0 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -462,6 +462,7 @@ struct dccp_ackvec; * @dccps_hc_rx_insert_options - receiver wants to add options when acking * @dccps_hc_tx_insert_options - sender wants to add options when sending * @dccps_server_timewait - server holds timewait state on close (RFC 4340, 8.3) + * @dccps_sync_scheduled - flag which signals "send out-of-band message soon" * @dccps_xmitlet - tasklet scheduled by the TX CCID to dequeue data packets * @dccps_xmit_timer - used by the TX CCID to delay sending (rate-based pacing) * @dccps_syn_rtt - RTT sample from Request/Response exchange (in usecs) @@ -503,6 +504,7 @@ struct dccp_sock { __u8 dccps_hc_rx_insert_options:1; __u8 dccps_hc_tx_insert_options:1; __u8 dccps_server_timewait:1; + __u8 dccps_sync_scheduled:1; struct tasklet_struct dccps_xmitlet; struct timer_list dccps_xmit_timer; }; diff --git a/net/dccp/options.c b/net/dccp/options.c index 7743df00f5b1..dabd6ee34d45 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c @@ -427,6 +427,7 @@ static int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) { struct dccp_sock *dp = dccp_sk(sk); struct dccp_ackvec *av = dp->dccps_hc_rx_ackvec; + struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); const u16 buflen = dccp_ackvec_buflen(av); /* Figure out how many options do we need to represent the ackvec */ const u8 nr_opts = DIV_ROUND_UP(buflen, DCCP_SINGLE_OPT_MAXLEN); @@ -435,10 +436,25 @@ static int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) const unsigned char *tail, *from; unsigned char *to; - if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) + if (dcb->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) { + DCCP_WARN("Lacking space for %u bytes on %s packet\n", len, + dccp_packet_name(dcb->dccpd_type)); return -1; - - DCCP_SKB_CB(skb)->dccpd_opt_len += len; + } + /* + * Since Ack Vectors are variable-length, we can not always predict + * their size. To catch exception cases where the space is running out + * on the skb, a separate Sync is scheduled to carry the Ack Vector. + */ + if (len > DCCPAV_MIN_OPTLEN && + len + dcb->dccpd_opt_len + skb->len > dp->dccps_mss_cache) { + DCCP_WARN("No space left for Ack Vector (%u) on skb (%u+%u), " + "MPS=%u ==> reduce payload size?\n", len, skb->len, + dcb->dccpd_opt_len, dp->dccps_mss_cache); + dp->dccps_sync_scheduled = 1; + return 0; + } + dcb->dccpd_opt_len += len; to = skb_push(skb, len); len = buflen; @@ -479,7 +495,7 @@ static int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) /* * Each sent Ack Vector is recorded in the list, as per A.2 of RFC 4340. */ - if (dccp_ackvec_update_records(av, DCCP_SKB_CB(skb)->dccpd_seq, nonce)) + if (dccp_ackvec_update_records(av, dcb->dccpd_seq, nonce)) return -ENOBUFS; return 0; } diff --git a/net/dccp/output.c b/net/dccp/output.c index 45b91853f5ae..d96dd9d362ae 100644 --- a/net/dccp/output.c +++ b/net/dccp/output.c @@ -283,6 +283,15 @@ static void dccp_xmit_packet(struct sock *sk) * any local drop will eventually be reported via receiver feedback. */ ccid_hc_tx_packet_sent(dp->dccps_hc_tx_ccid, sk, len); + + /* + * If the CCID needs to transfer additional header options out-of-band + * (e.g. Ack Vectors or feature-negotiation options), it activates this + * flag to schedule a Sync. The Sync will automatically incorporate all + * currently pending header options, thus clearing the backlog. + */ + if (dp->dccps_sync_scheduled) + dccp_send_sync(sk, dp->dccps_gsr, DCCP_PKT_SYNC); } /** @@ -636,6 +645,12 @@ void dccp_send_sync(struct sock *sk, const u64 ackno, DCCP_SKB_CB(skb)->dccpd_type = pkt_type; DCCP_SKB_CB(skb)->dccpd_ack_seq = ackno; + /* + * Clear the flag in case the Sync was scheduled for out-of-band data, + * such as carrying a long Ack Vector. + */ + dccp_sk(sk)->dccps_sync_scheduled = 0; + dccp_transmit_skb(sk, skb); } -- cgit v1.2.3 From 58e998c6d23988490162cef0784b19ea274d90bb Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Fri, 29 Oct 2010 12:14:55 +0000 Subject: offloading: Force software GSO for multiple vlan tags. We currently use vlan_features to check for TSO support if there is a vlan tag. However, it's quite likely that the NIC is not able to do TSO when there is an arbitrary number of tags. Therefore if there is more than one tag (in-band or out-of-band), fall back to software emulation. Signed-off-by: Jesse Gross CC: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/netdevice.h | 7 +++---- net/core/dev.c | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 578debb801f4..6e4cfbc53d4c 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2239,6 +2239,8 @@ unsigned long netdev_fix_features(unsigned long features, const char *name); void netif_stacked_transfer_operstate(const struct net_device *rootdev, struct net_device *dev); +int netif_get_vlan_features(struct sk_buff *skb, struct net_device *dev); + static inline int net_gso_ok(int features, int gso_type) { int feature = gso_type << NETIF_F_GSO_SHIFT; @@ -2254,10 +2256,7 @@ static inline int skb_gso_ok(struct sk_buff *skb, int features) static inline int netif_needs_gso(struct net_device *dev, struct sk_buff *skb) { if (skb_is_gso(skb)) { - int features = dev->features; - - if (skb->protocol == htons(ETH_P_8021Q) || skb->vlan_tci) - features &= dev->vlan_features; + int features = netif_get_vlan_features(skb, dev); return (!skb_gso_ok(skb, features) || unlikely(skb->ip_summed != CHECKSUM_PARTIAL)); diff --git a/net/core/dev.c b/net/core/dev.c index 368930a988e3..8b500c3e0297 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1968,6 +1968,22 @@ static inline void skb_orphan_try(struct sk_buff *skb) } } +int netif_get_vlan_features(struct sk_buff *skb, struct net_device *dev) +{ + __be16 protocol = skb->protocol; + + if (protocol == htons(ETH_P_8021Q)) { + struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; + protocol = veh->h_vlan_encapsulated_proto; + } else if (!skb->vlan_tci) + return dev->features; + + if (protocol != htons(ETH_P_8021Q)) + return dev->features & dev->vlan_features; + else + return 0; +} + /* * Returns true if either: * 1. skb has frag_list and the device doesn't support FRAGLIST, or -- cgit v1.2.3 From 2e48928d8a0f38c1b5c81eb3f1294de8a6382c68 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 20 Oct 2010 10:16:58 -0700 Subject: rfkill: remove dead code The following code is defined but never used. Signed-off-by: Stephen Hemminger Signed-off-by: John W. Linville --- include/linux/rfkill.h | 31 ------------------------------- net/rfkill/core.c | 14 -------------- 2 files changed, 45 deletions(-) (limited to 'include') diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index 08c32e4f261a..c6c608482cba 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -354,37 +354,6 @@ static inline bool rfkill_blocked(struct rfkill *rfkill) } #endif /* RFKILL || RFKILL_MODULE */ - -#ifdef CONFIG_RFKILL_LEDS -/** - * rfkill_get_led_trigger_name - Get the LED trigger name for the button's LED. - * This function might return a NULL pointer if registering of the - * LED trigger failed. Use this as "default_trigger" for the LED. - */ -const char *rfkill_get_led_trigger_name(struct rfkill *rfkill); - -/** - * rfkill_set_led_trigger_name -- set the LED trigger name - * @rfkill: rfkill struct - * @name: LED trigger name - * - * This function sets the LED trigger name of the radio LED - * trigger that rfkill creates. It is optional, but if called - * must be called before rfkill_register() to be effective. - */ -void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name); -#else -static inline const char *rfkill_get_led_trigger_name(struct rfkill *rfkill) -{ - return NULL; -} - -static inline void -rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name) -{ -} -#endif - #endif /* __KERNEL__ */ #endif /* RFKILL_H */ diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 04f599089e6d..0198191b756d 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -149,20 +149,6 @@ static void rfkill_led_trigger_activate(struct led_classdev *led) rfkill_led_trigger_event(rfkill); } -const char *rfkill_get_led_trigger_name(struct rfkill *rfkill) -{ - return rfkill->led_trigger.name; -} -EXPORT_SYMBOL(rfkill_get_led_trigger_name); - -void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name) -{ - BUG_ON(!rfkill); - - rfkill->ledtrigname = name; -} -EXPORT_SYMBOL(rfkill_set_led_trigger_name); - static int rfkill_led_trigger_register(struct rfkill *rfkill) { rfkill->led_trigger.name = rfkill->ledtrigname -- cgit v1.2.3 From 749b527b21465fb079796c03ffb4302584dc31c1 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 20 Oct 2010 10:18:54 -0700 Subject: cfg80211: fix allowing country IEs for WIPHY_FLAG_STRICT_REGULATORY We should be enabling country IE hints for WIPHY_FLAG_STRICT_REGULATORY even if we haven't yet recieved regulatory domain hint for the driver if it needed one. Without this Country IEs are not passed on to drivers that have set WIPHY_FLAG_STRICT_REGULATORY, today this is just all Atheros chipset drivers: ath5k, ath9k, ar9170, carl9170. This was part of the original design, however it was completely overlooked... Cc: Easwar Krishnan Cc: stable@kernel.org Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- include/net/cfg80211.h | 15 ++++++++------- net/wireless/reg.c | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 2a7936d7851d..e5702f5ac57c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1321,13 +1321,14 @@ struct cfg80211_ops { * initiator is %REGDOM_SET_BY_CORE). * @WIPHY_FLAG_STRICT_REGULATORY: tells us the driver for this device will * ignore regulatory domain settings until it gets its own regulatory - * domain via its regulatory_hint(). After its gets its own regulatory - * domain it will only allow further regulatory domain settings to - * further enhance compliance. For example if channel 13 and 14 are - * disabled by this regulatory domain no user regulatory domain can - * enable these channels at a later time. This can be used for devices - * which do not have calibration information gauranteed for frequencies - * or settings outside of its regulatory domain. + * domain via its regulatory_hint() unless the regulatory hint is + * from a country IE. After its gets its own regulatory domain it will + * only allow further regulatory domain settings to further enhance + * compliance. For example if channel 13 and 14 are disabled by this + * regulatory domain no user regulatory domain can enable these channels + * at a later time. This can be used for devices which do not have + * calibration information guaranteed for frequencies or settings + * outside of its regulatory domain. * @WIPHY_FLAG_DISABLE_BEACON_HINTS: enable this if your driver needs to ensure * that passive scan flags and beaconing flags may not be lifted by * cfg80211 due to regulatory beacon hints. For more information on beacon diff --git a/net/wireless/reg.c b/net/wireless/reg.c index b64596fe7363..1bc8131a5185 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -813,6 +813,7 @@ static bool ignore_reg_update(struct wiphy *wiphy, * desired regulatory domain set */ if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && + initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && !is_world_regdom(last_request->alpha2)) return true; return false; -- cgit v1.2.3 From ca4ffe8f2848169a8ded0ea8a60b2d81925564c9 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 20 Oct 2010 10:18:55 -0700 Subject: cfg80211: fix disabling channels based on hints After a module loads you will have loaded the world roaming regulatory domain or a custom regulatory domain. Further regulatory hints are welcomed and should be respected unless the regulatory hint is coming from a country IE as the IEEE spec allows for a country IE to be a subset of what is allowed by the local regulatory agencies. So disable all channels that do not fit a regulatory domain sent from a unless the hint is from a country IE and the country IE had no information about the band we are currently processing. This fixes a few regulatory issues, for example for drivers that depend on CRDA and had no 5 GHz freqencies allowed were not properly disabling 5 GHz at all, furthermore it also allows users to restrict devices further as was intended. If you recieve a country IE upon association we will also disable the channels that are not allowed if the country IE had at least one channel on the respective band we are procesing. This was the original intention behind this design but it was completely overlooked... Cc: David Quan Cc: Jouni Malinen cc: Easwar Krishnan Cc: stable@kernel.org Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- include/linux/nl80211.h | 6 +++++- net/wireless/reg.c | 20 +++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 0edb2566c14c..fb877b5621b7 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1307,7 +1307,11 @@ enum nl80211_bitrate_attr { * wireless core it thinks its knows the regulatory domain we should be in. * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an * 802.11 country information element with regulatory information it - * thinks we should consider. + * thinks we should consider. cfg80211 only processes the country + * code from the IE, and relies on the regulatory domain information + * structure pased by userspace (CRDA) from our wireless-regdb. + * If a channel is enabled but the country code indicates it should + * be disabled we disable the channel and re-enable it upon disassociation. */ enum nl80211_reg_initiator { NL80211_REGDOM_SET_BY_CORE, diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 1bc8131a5185..8ab65f2afe70 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -750,8 +750,26 @@ static void handle_channel(struct wiphy *wiphy, desired_bw_khz, ®_rule); - if (r) + if (r) { + /* + * We will disable all channels that do not match our + * recieved regulatory rule unless the hint is coming + * from a Country IE and the Country IE had no information + * about a band. The IEEE 802.11 spec allows for an AP + * to send only a subset of the regulatory rules allowed, + * so an AP in the US that only supports 2.4 GHz may only send + * a country IE with information for the 2.4 GHz band + * while 5 GHz is still supported. + */ + if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && + r == -ERANGE) + return; + + REG_DBG_PRINT("cfg80211: Disabling freq %d MHz\n", + chan->center_freq); + chan->flags = IEEE80211_CHAN_DISABLED; return; + } power_rule = ®_rule->power_rule; freq_range = ®_rule->freq_range; -- cgit v1.2.3 From c8aea565e8f715d9f10064b1cbfbc15bf75df501 Mon Sep 17 00:00:00 2001 From: Gery Kahn Date: Tue, 5 Oct 2010 16:09:05 +0200 Subject: wl1271: ref_clock cosmetic changes Cosmetic cleanup for ref_clock code while configured by board. Signed-off-by: Gery Kahn Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/wl1271_boot.c | 10 ++++------ include/linux/wl12xx.h | 8 ++++++++ 2 files changed, 12 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c index b91021242098..5b190728ca55 100644 --- a/drivers/net/wireless/wl12xx/wl1271_boot.c +++ b/drivers/net/wireless/wl12xx/wl1271_boot.c @@ -471,20 +471,19 @@ int wl1271_boot(struct wl1271 *wl) { int ret = 0; u32 tmp, clk, pause; - int ref_clock = wl->ref_clock; wl1271_boot_hw_version(wl); - if (ref_clock == 0 || ref_clock == 2 || ref_clock == 4) + if (wl->ref_clock == 0 || wl->ref_clock == 2 || wl->ref_clock == 4) /* ref clk: 19.2/38.4/38.4-XTAL */ clk = 0x3; - else if (ref_clock == 1 || ref_clock == 3) + else if (wl->ref_clock == 1 || wl->ref_clock == 3) /* ref clk: 26/52 */ clk = 0x5; else return -EINVAL; - if (ref_clock != 0) { + if (wl->ref_clock != 0) { u16 val; /* Set clock type (open drain) */ val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE); @@ -529,8 +528,7 @@ int wl1271_boot(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); - /* 2 */ - clk |= (ref_clock << 1) << 4; + clk |= (wl->ref_clock << 1) << 4; wl1271_write32(wl, DRPW_SCRATCH_START, clk); wl1271_set_partition(wl, &part_table[PART_WORK]); diff --git a/include/linux/wl12xx.h b/include/linux/wl12xx.h index 4f902e1908aa..bebb8efea0a6 100644 --- a/include/linux/wl12xx.h +++ b/include/linux/wl12xx.h @@ -24,6 +24,14 @@ #ifndef _LINUX_WL12XX_H #define _LINUX_WL12XX_H +/* The board reference clock values */ +enum { + WL12XX_REFCLOCK_19 = 0, /* 19.2 MHz */ + WL12XX_REFCLOCK_26 = 1, /* 26 MHz */ + WL12XX_REFCLOCK_38 = 2, /* 38.4 MHz */ + WL12XX_REFCLOCK_54 = 3, /* 54 MHz */ +}; + struct wl12xx_platform_data { void (*set_power)(bool enable); /* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */ -- cgit v1.2.3 From 7919a57bc608140aa8614c19eac40c6916fb61d2 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Mon, 30 Aug 2010 19:04:01 +0000 Subject: bitops: Provide generic sign_extend32 function This patch moves code out from wireless drivers where two different functions are defined in three code locations for the same purpose and provides a common function to sign extend a 32-bit value. Signed-off-by: Andreas Herrmann Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/phy.c | 8 +------- drivers/net/wireless/ath/ath9k/ar5008_phy.c | 12 ++++++------ drivers/net/wireless/ath/ath9k/ar9002_phy.c | 8 ++++---- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 12 ++++++------ drivers/net/wireless/ath/ath9k/hw.h | 6 ------ drivers/net/wireless/iwlwifi/iwl-4965.c | 20 ++------------------ include/linux/bitops.h | 11 +++++++++++ 7 files changed, 30 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index 219367884e64..6b43f535ff53 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -1102,18 +1102,12 @@ int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) PHY calibration \*****************/ -static int sign_extend(int val, const int nbits) -{ - int order = BIT(nbits-1); - return (val ^ order) - order; -} - static s32 ath5k_hw_read_measured_noise_floor(struct ath5k_hw *ah) { s32 val; val = ath5k_hw_reg_read(ah, AR5K_PHY_NF); - return sign_extend(AR5K_REG_MS(val, AR5K_PHY_NF_MINCCA_PWR), 9); + return sign_extend32(AR5K_REG_MS(val, AR5K_PHY_NF_MINCCA_PWR), 8); } void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 777a602176f2..c83a22cfbe1e 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -1490,25 +1490,25 @@ static void ar5008_hw_do_getnf(struct ath_hw *ah, int16_t nf; nf = MS(REG_READ(ah, AR_PHY_CCA), AR_PHY_MINCCA_PWR); - nfarray[0] = sign_extend(nf, 9); + nfarray[0] = sign_extend32(nf, 8); nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), AR_PHY_CH1_MINCCA_PWR); - nfarray[1] = sign_extend(nf, 9); + nfarray[1] = sign_extend32(nf, 8); nf = MS(REG_READ(ah, AR_PHY_CH2_CCA), AR_PHY_CH2_MINCCA_PWR); - nfarray[2] = sign_extend(nf, 9); + nfarray[2] = sign_extend32(nf, 8); if (!IS_CHAN_HT40(ah->curchan)) return; nf = MS(REG_READ(ah, AR_PHY_EXT_CCA), AR_PHY_EXT_MINCCA_PWR); - nfarray[3] = sign_extend(nf, 9); + nfarray[3] = sign_extend32(nf, 8); nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA), AR_PHY_CH1_EXT_MINCCA_PWR); - nfarray[4] = sign_extend(nf, 9); + nfarray[4] = sign_extend32(nf, 8); nf = MS(REG_READ(ah, AR_PHY_CH2_EXT_CCA), AR_PHY_CH2_EXT_MINCCA_PWR); - nfarray[5] = sign_extend(nf, 9); + nfarray[5] = sign_extend32(nf, 8); } /* diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index c00cdc67b55b..3fb97fdc1240 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -473,21 +473,21 @@ static void ar9002_hw_do_getnf(struct ath_hw *ah, int16_t nf; nf = MS(REG_READ(ah, AR_PHY_CCA), AR9280_PHY_MINCCA_PWR); - nfarray[0] = sign_extend(nf, 9); + nfarray[0] = sign_extend32(nf, 8); nf = MS(REG_READ(ah, AR_PHY_EXT_CCA), AR9280_PHY_EXT_MINCCA_PWR); if (IS_CHAN_HT40(ah->curchan)) - nfarray[3] = sign_extend(nf, 9); + nfarray[3] = sign_extend32(nf, 8); if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) return; nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), AR9280_PHY_CH1_MINCCA_PWR); - nfarray[1] = sign_extend(nf, 9); + nfarray[1] = sign_extend32(nf, 8); nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA), AR9280_PHY_CH1_EXT_MINCCA_PWR); if (IS_CHAN_HT40(ah->curchan)) - nfarray[4] = sign_extend(nf, 9); + nfarray[4] = sign_extend32(nf, 8); } static void ar9002_hw_set_nf_limits(struct ath_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 06a9c4cd2f44..44c5454b2ad8 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -1023,25 +1023,25 @@ static void ar9003_hw_do_getnf(struct ath_hw *ah, int16_t nf; nf = MS(REG_READ(ah, AR_PHY_CCA_0), AR_PHY_MINCCA_PWR); - nfarray[0] = sign_extend(nf, 9); + nfarray[0] = sign_extend32(nf, 8); nf = MS(REG_READ(ah, AR_PHY_CCA_1), AR_PHY_CH1_MINCCA_PWR); - nfarray[1] = sign_extend(nf, 9); + nfarray[1] = sign_extend32(nf, 8); nf = MS(REG_READ(ah, AR_PHY_CCA_2), AR_PHY_CH2_MINCCA_PWR); - nfarray[2] = sign_extend(nf, 9); + nfarray[2] = sign_extend32(nf, 8); if (!IS_CHAN_HT40(ah->curchan)) return; nf = MS(REG_READ(ah, AR_PHY_EXT_CCA), AR_PHY_EXT_MINCCA_PWR); - nfarray[3] = sign_extend(nf, 9); + nfarray[3] = sign_extend32(nf, 8); nf = MS(REG_READ(ah, AR_PHY_EXT_CCA_1), AR_PHY_CH1_EXT_MINCCA_PWR); - nfarray[4] = sign_extend(nf, 9); + nfarray[4] = sign_extend32(nf, 8); nf = MS(REG_READ(ah, AR_PHY_EXT_CCA_2), AR_PHY_CH2_EXT_MINCCA_PWR); - nfarray[5] = sign_extend(nf, 9); + nfarray[5] = sign_extend32(nf, 8); } static void ar9003_hw_set_nf_limits(struct ath_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index e5b72262fd96..f821a28bcda3 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -825,12 +825,6 @@ static inline struct ath_hw_ops *ath9k_hw_ops(struct ath_hw *ah) return &ah->ops; } -static inline int sign_extend(int val, const int nbits) -{ - int order = BIT(nbits-1); - return (val ^ order) - order; -} - /* Initialization, Detach, Reset */ const char *ath9k_hw_probe(u16 vendorid, u16 devid); void ath9k_hw_deinit(struct ath_hw *ah); diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index cd14843878ae..4748d067eb1d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -1686,22 +1686,6 @@ static void iwl4965_txq_update_byte_cnt_tbl(struct iwl_priv *priv, tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent; } -/** - * sign_extend - Sign extend a value using specified bit as sign-bit - * - * Example: sign_extend(9, 3) would return -7 as bit3 of 1001b is 1 - * and bit0..2 is 001b which when sign extended to 1111111111111001b is -7. - * - * @param oper value to sign extend - * @param index 0 based bit index (0<=index<32) to sign bit - */ -static s32 sign_extend(u32 oper, int index) -{ - u8 shift = 31 - index; - - return (s32)(oper << shift) >> shift; -} - /** * iwl4965_hw_get_temperature - return the calibrated temperature (in Kelvin) * @statistics: Provides the temperature reading from the uCode @@ -1739,9 +1723,9 @@ static int iwl4965_hw_get_temperature(struct iwl_priv *priv) * "initialize" ALIVE response. */ if (!test_bit(STATUS_TEMPERATURE, &priv->status)) - vt = sign_extend(R4, 23); + vt = sign_extend32(R4, 23); else - vt = sign_extend(le32_to_cpu(priv->_agn.statistics. + vt = sign_extend32(le32_to_cpu(priv->_agn.statistics. general.common.temperature), 23); IWL_DEBUG_TEMP(priv, "Calib values R[1-3]: %d %d %d R4: %d\n", R1, R2, R3, vt); diff --git a/include/linux/bitops.h b/include/linux/bitops.h index 827cc95711ef..2184c6b97aeb 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -109,6 +109,17 @@ static inline __u8 ror8(__u8 word, unsigned int shift) return (word >> shift) | (word << (8 - shift)); } +/** + * sign_extend32 - sign extend a 32-bit value using specified bit as sign-bit + * @value: value to sign extend + * @index: 0 based bit index (0<=index<32) to sign bit + */ +static inline __s32 sign_extend32(__u32 value, int index) +{ + __u8 shift = 31 - index; + return (__s32)(value << shift) >> shift; +} + static inline unsigned fls_long(unsigned long l) { if (sizeof(l) == 4) -- cgit v1.2.3 From cc9ff19da9bf76a2f70bcb80225a1c587c162e52 Mon Sep 17 00:00:00 2001 From: Timo Teräs Date: Wed, 3 Nov 2010 04:41:38 +0000 Subject: xfrm: use gre key as flow upper protocol info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GRE Key field is intended to be used for identifying an individual traffic flow within a tunnel. It is useful to be able to have XFRM policy selector matches to have different policies for different GRE tunnels. Signed-off-by: Timo Teräs Signed-off-by: David S. Miller --- include/net/flow.h | 2 ++ include/net/xfrm.h | 6 ++++++ net/ipv4/ip_gre.c | 12 +++++++----- net/ipv4/xfrm4_policy.c | 15 +++++++++++++++ 4 files changed, 30 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/flow.h b/include/net/flow.h index 0ac3fb5e0973..7196e6864b8d 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -67,6 +67,7 @@ struct flowi { } dnports; __be32 spi; + __be32 gre_key; struct { __u8 type; @@ -78,6 +79,7 @@ struct flowi { #define fl_icmp_code uli_u.icmpt.code #define fl_ipsec_spi uli_u.spi #define fl_mh_type uli_u.mht.type +#define fl_gre_key uli_u.gre_key __u32 secid; /* used by xfrm; see secid.txt */ } __attribute__((__aligned__(BITS_PER_LONG/8))); diff --git a/include/net/xfrm.h b/include/net/xfrm.h index bcfb6b24b019..54b283229488 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -805,6 +805,9 @@ __be16 xfrm_flowi_sport(struct flowi *fl) case IPPROTO_MH: port = htons(fl->fl_mh_type); break; + case IPPROTO_GRE: + port = htonl(fl->fl_gre_key) >> 16; + break; default: port = 0; /*XXX*/ } @@ -826,6 +829,9 @@ __be16 xfrm_flowi_dport(struct flowi *fl) case IPPROTO_ICMPV6: port = htons(fl->fl_icmp_code); break; + case IPPROTO_GRE: + port = htonl(fl->fl_gre_key) & 0xffff; + break; default: port = 0; /*XXX*/ } diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index cab2057d5430..aace653710f6 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -779,9 +779,9 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev .tos = RT_TOS(tos) } }, - .proto = IPPROTO_GRE - } -; + .proto = IPPROTO_GRE, + .fl_gre_key = tunnel->parms.o_key + }; if (ip_route_output_key(dev_net(dev), &rt, &fl)) { dev->stats.tx_carrier_errors++; goto tx_error; @@ -958,7 +958,8 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev) .tos = RT_TOS(iph->tos) } }, - .proto = IPPROTO_GRE + .proto = IPPROTO_GRE, + .fl_gre_key = tunnel->parms.o_key }; struct rtable *rt; @@ -1223,7 +1224,8 @@ static int ipgre_open(struct net_device *dev) .tos = RT_TOS(t->parms.iph.tos) } }, - .proto = IPPROTO_GRE + .proto = IPPROTO_GRE, + .fl_gre_key = t->parms.o_key }; struct rtable *rt; diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index dd1fd8c473fc..4a8c5335770c 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -154,6 +155,20 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse) fl->fl_ipsec_spi = htonl(ntohs(ipcomp_hdr[1])); } break; + + case IPPROTO_GRE: + if (pskb_may_pull(skb, xprth + 12 - skb->data)) { + __be16 *greflags = (__be16 *)xprth; + __be32 *gre_hdr = (__be32 *)xprth; + + if (greflags[0] & GRE_KEY) { + if (greflags[0] & GRE_CSUM) + gre_hdr++; + fl->fl_gre_key = gre_hdr[1]; + } + } + break; + default: fl->fl_ipsec_spi = 0; break; -- cgit v1.2.3 From fe8222406c8277a21172479d3a8283d31c209028 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 9 Nov 2010 10:47:38 +0000 Subject: net: Simplify RX queue allocation This patch move RX queue allocation to alloc_netdev_mq and freeing of the queues to free_netdev (symmetric to TX queue allocation). Each kobject RX queue takes a reference to the queue's device so that the device can't be freed before all the kobjects have been released-- this obviates the need for reference counts specific to RX queues. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 +-- net/core/dev.c | 19 ++++++++++--------- net/core/net-sysfs.c | 7 ++----- 3 files changed, 13 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 6e4cfbc53d4c..fccb11f879e5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -592,8 +592,7 @@ struct netdev_rx_queue { struct rps_map __rcu *rps_map; struct rps_dev_flow_table __rcu *rps_flow_table; struct kobject kobj; - struct netdev_rx_queue *first; - atomic_t count; + struct net_device *dev; } ____cacheline_aligned_in_smp; #endif /* CONFIG_RPS */ diff --git a/net/core/dev.c b/net/core/dev.c index 75490670e0a9..8725d168d1f5 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5051,12 +5051,8 @@ static int netif_alloc_rx_queues(struct net_device *dev) } dev->_rx = rx; - /* - * Set a pointer to first element in the array which holds the - * reference count. - */ for (i = 0; i < count; i++) - rx[i].first = rx; + rx[i].dev = dev; #endif return 0; } @@ -5132,10 +5128,6 @@ int register_netdevice(struct net_device *dev) dev->iflink = -1; - ret = netif_alloc_rx_queues(dev); - if (ret) - goto out; - netdev_init_queues(dev); /* Init, if this function is available */ @@ -5601,6 +5593,8 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, #ifdef CONFIG_RPS dev->num_rx_queues = queue_count; dev->real_num_rx_queues = queue_count; + if (netif_alloc_rx_queues(dev)) + goto free_pcpu; #endif dev->gso_max_size = GSO_MAX_SIZE; @@ -5618,6 +5612,10 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, free_pcpu: free_percpu(dev->pcpu_refcnt); kfree(dev->_tx); +#ifdef CONFIG_RPS + kfree(dev->_rx); +#endif + free_p: kfree(p); return NULL; @@ -5639,6 +5637,9 @@ void free_netdev(struct net_device *dev) release_net(dev_net(dev)); kfree(dev->_tx); +#ifdef CONFIG_RPS + kfree(dev->_rx); +#endif kfree(rcu_dereference_raw(dev->ingress_queue)); diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index a5ff5a89f376..3ba526b56fe3 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -706,7 +706,6 @@ static struct attribute *rx_queue_default_attrs[] = { static void rx_queue_release(struct kobject *kobj) { struct netdev_rx_queue *queue = to_rx_queue(kobj); - struct netdev_rx_queue *first = queue->first; struct rps_map *map; struct rps_dev_flow_table *flow_table; @@ -719,8 +718,7 @@ static void rx_queue_release(struct kobject *kobj) if (flow_table) call_rcu(&flow_table->rcu, rps_dev_flow_table_release); - if (atomic_dec_and_test(&first->count)) - kfree(first); + dev_put(queue->dev); } static struct kobj_type rx_queue_ktype = { @@ -732,7 +730,6 @@ static struct kobj_type rx_queue_ktype = { static int rx_queue_add_kobject(struct net_device *net, int index) { struct netdev_rx_queue *queue = net->_rx + index; - struct netdev_rx_queue *first = queue->first; struct kobject *kobj = &queue->kobj; int error = 0; @@ -745,7 +742,7 @@ static int rx_queue_add_kobject(struct net_device *net, int index) } kobject_uevent(kobj, KOBJ_ADD); - atomic_inc(&first->count); + dev_hold(queue->dev); return error; } -- cgit v1.2.3 From c59504ebc5baa628706d10c2d3c7e1f4bc3c2147 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 14 Nov 2010 17:04:57 +0000 Subject: include/linux/if_macvlan.h: Remove unnecessary semicolons Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/linux/if_macvlan.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h index 8a2fd66a8b5f..ac96a2d76291 100644 --- a/include/linux/if_macvlan.h +++ b/include/linux/if_macvlan.h @@ -69,7 +69,7 @@ static inline void macvlan_count_rx(const struct macvlan_dev *vlan, rx_stats = this_cpu_ptr(vlan->rx_stats); if (likely(success)) { u64_stats_update_begin(&rx_stats->syncp); - rx_stats->rx_packets++;; + rx_stats->rx_packets++; rx_stats->rx_bytes += len; if (multicast) rx_stats->rx_multicast++; -- cgit v1.2.3 From d577f1ccdd8ae8bfbe6063eb2ba2a350259e9031 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 14 Nov 2010 17:04:58 +0000 Subject: include/net/caif/cfctrl.h: Remove unnecessary semicolons Signed-off-by: Joe Perches Acked-by: Sjur Braendeland Signed-off-by: David S. Miller --- include/net/caif/cfctrl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/caif/cfctrl.h b/include/net/caif/cfctrl.h index 9402543fc20d..e54f6396fa4c 100644 --- a/include/net/caif/cfctrl.h +++ b/include/net/caif/cfctrl.h @@ -51,7 +51,7 @@ struct cfctrl_rsp { void (*restart_rsp)(void); void (*radioset_rsp)(void); void (*reject_rsp)(struct cflayer *layer, u8 linkid, - struct cflayer *client_layer);; + struct cflayer *client_layer); }; /* Link Setup Parameters for CAIF-Links. */ -- cgit v1.2.3 From a386f99025f13b32502fe5dedf223c20d7283826 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 15 Nov 2010 06:38:11 +0000 Subject: bridge: add proper RCU annotation to should_route_hook Add br_should_route_hook_t typedef, this is the only way we can get a clean RCU implementation for function pointer. Move route_hook to location where it is used. Signed-off-by: Eric Dumazet Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/if_bridge.h | 4 +++- net/bridge/br.c | 4 ---- net/bridge/br_input.c | 10 +++++++--- net/bridge/netfilter/ebtable_broute.c | 3 ++- 4 files changed, 12 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index 0d241a5c4909..f7e73c338c40 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -102,7 +102,9 @@ struct __fdb_entry { #include extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *)); -extern int (*br_should_route_hook)(struct sk_buff *skb); + +typedef int (*br_should_route_hook_t)(struct sk_buff *skb); +extern br_should_route_hook_t __rcu *br_should_route_hook; #endif diff --git a/net/bridge/br.c b/net/bridge/br.c index c8436fa31344..84bbb82599b2 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -22,8 +22,6 @@ #include "br_private.h" -int (*br_should_route_hook)(struct sk_buff *skb); - static const struct stp_proto br_stp_proto = { .rcv = br_stp_rcv, }; @@ -102,8 +100,6 @@ static void __exit br_deinit(void) br_fdb_fini(); } -EXPORT_SYMBOL(br_should_route_hook); - module_init(br_init) module_exit(br_deinit) MODULE_LICENSE("GPL"); diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 25207a1f182b..6f6d8e1b776f 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -21,6 +21,10 @@ /* Bridge group multicast address 802.1d (pg 51). */ const u8 br_group_address[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; +/* Hook for brouter */ +br_should_route_hook_t __rcu *br_should_route_hook __read_mostly; +EXPORT_SYMBOL(br_should_route_hook); + static int br_pass_frame_up(struct sk_buff *skb) { struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev; @@ -139,7 +143,7 @@ struct sk_buff *br_handle_frame(struct sk_buff *skb) { struct net_bridge_port *p; const unsigned char *dest = eth_hdr(skb)->h_dest; - int (*rhook)(struct sk_buff *skb); + br_should_route_hook_t *rhook; if (unlikely(skb->pkt_type == PACKET_LOOPBACK)) return skb; @@ -173,8 +177,8 @@ forward: switch (p->state) { case BR_STATE_FORWARDING: rhook = rcu_dereference(br_should_route_hook); - if (rhook != NULL) { - if (rhook(skb)) + if (rhook) { + if ((*rhook)(skb)) return skb; dest = eth_hdr(skb)->h_dest; } diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c index ae3f106c3908..1bcaf36ad612 100644 --- a/net/bridge/netfilter/ebtable_broute.c +++ b/net/bridge/netfilter/ebtable_broute.c @@ -87,7 +87,8 @@ static int __init ebtable_broute_init(void) if (ret < 0) return ret; /* see br_input.c */ - rcu_assign_pointer(br_should_route_hook, ebt_broute); + rcu_assign_pointer(br_should_route_hook, + (br_should_route_hook_t *)ebt_broute); return 0; } -- cgit v1.2.3 From 61391cde9eefac5cfcf6d214aa80c77e58b1626b Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 15 Nov 2010 06:38:12 +0000 Subject: netdev: add rcu annotations to receive handler hook Suggested by Eric's bridge RCU changes. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index fccb11f879e5..b45c1b8b1d19 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -994,8 +994,8 @@ struct net_device { unsigned int real_num_rx_queues; #endif - rx_handler_func_t *rx_handler; - void *rx_handler_data; + rx_handler_func_t __rcu *rx_handler; + void __rcu *rx_handler_data; struct netdev_queue __rcu *ingress_queue; -- cgit v1.2.3 From 96f8d864afd646e4a52ea55462b7d83e3b94fd5c Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 16 Nov 2010 14:00:24 +0900 Subject: fbdev: move udlfb out of staging. udlfb has undergone a fair bit of cleanup recently and is effectively at the point where it can be liberated from staging purgatory and promoted to a real driver. The outstanding cleanups are all minor, with some of them dependent on drivers/video headers, so these will be done incrementally from udlfb's new home. Requested-by: Bernie Thompson Signed-off-by: Paul Mundt --- Documentation/fb/udlfb.txt | 144 +++ drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/udlfb/Kconfig | 14 - drivers/staging/udlfb/Makefile | 1 - drivers/staging/udlfb/udlfb.c | 1916 --------------------------------------- drivers/staging/udlfb/udlfb.h | 117 --- drivers/staging/udlfb/udlfb.txt | 144 --- drivers/video/Kconfig | 14 + drivers/video/Makefile | 1 + drivers/video/udlfb.c | 1915 ++++++++++++++++++++++++++++++++++++++ include/video/udlfb.h | 117 +++ 12 files changed, 2191 insertions(+), 2195 deletions(-) create mode 100644 Documentation/fb/udlfb.txt delete mode 100644 drivers/staging/udlfb/Kconfig delete mode 100644 drivers/staging/udlfb/Makefile delete mode 100644 drivers/staging/udlfb/udlfb.c delete mode 100644 drivers/staging/udlfb/udlfb.h delete mode 100644 drivers/staging/udlfb/udlfb.txt create mode 100644 drivers/video/udlfb.c create mode 100644 include/video/udlfb.h (limited to 'include') diff --git a/Documentation/fb/udlfb.txt b/Documentation/fb/udlfb.txt new file mode 100644 index 000000000000..7fdde2a02a27 --- /dev/null +++ b/Documentation/fb/udlfb.txt @@ -0,0 +1,144 @@ + +What is udlfb? +=============== + +This is a driver for DisplayLink USB 2.0 era graphics chips. + +DisplayLink chips provide simple hline/blit operations with some compression, +pairing that with a hardware framebuffer (16MB) on the other end of the +USB wire. That hardware framebuffer is able to drive the VGA, DVI, or HDMI +monitor with no CPU involvement until a pixel has to change. + +The CPU or other local resource does all the rendering; optinally compares the +result with a local shadow of the remote hardware framebuffer to identify +the minimal set of pixels that have changed; and compresses and sends those +pixels line-by-line via USB bulk transfers. + +Because of the efficiency of bulk transfers and a protocol on top that +does not require any acks - the effect is very low latency that +can support surprisingly high resolutions with good performance for +non-gaming and non-video applications. + +Mode setting, EDID read, etc are other bulk or control transfers. Mode +setting is very flexible - able to set nearly arbitrary modes from any timing. + +Advantages of USB graphics in general: + + * Ability to add a nearly arbitrary number of displays to any USB 2.0 + capable system. On Linux, number of displays is limited by fbdev interface + (FB_MAX is currently 32). Of course, all USB devices on the same + host controller share the same 480Mbs USB 2.0 interface. + +Advantages of supporting DisplayLink chips with kernel framebuffer interface: + + * The actual hardware functionality of DisplayLink chips matches nearly + one-to-one with the fbdev interface, making the driver quite small and + tight relative to the functionality it provides. + * X servers and other applications can use the standard fbdev interface + from user mode to talk to the device, without needing to know anything + about USB or DisplayLink's protocol at all. A "displaylink" X driver + and a slightly modified "fbdev" X driver are among those that already do. + +Disadvantages: + + * Fbdev's mmap interface assumes a real hardware framebuffer is mapped. + In the case of USB graphics, it is just an allocated (virtual) buffer. + Writes need to be detected and encoded into USB bulk transfers by the CPU. + Accurate damage/changed area notifications work around this problem. + In the future, hopefully fbdev will be enhanced with an small standard + interface to allow mmap clients to report damage, for the benefit + of virtual or remote framebuffers. + * Fbdev does not arbitrate client ownership of the framebuffer well. + * Fbcon assumes the first framebuffer it finds should be consumed for console. + * It's not clear what the future of fbdev is, given the rise of KMS/DRM. + +How to use it? +============== + +Udlfb, when loaded as a module, will match against all USB 2.0 generation +DisplayLink chips (Alex and Ollie family). It will then attempt to read the EDID +of the monitor, and set the best common mode between the DisplayLink device +and the monitor's capabilities. + +If the DisplayLink device is successful, it will paint a "green screen" which +means that from a hardware and fbdev software perspective, everything is good. + +At that point, a /dev/fb? interface will be present for user-mode applications +to open and begin writing to the framebuffer of the DisplayLink device using +standard fbdev calls. Note that if mmap() is used, by default the user mode +application must send down damage notifcations to trigger repaints of the +changed regions. Alternatively, udlfb can be recompiled with experimental +defio support enabled, to support a page-fault based detection mechanism +that can work without explicit notifcation. + +The most common client of udlfb is xf86-video-displaylink or a modified +xf86-video-fbdev X server. These servers have no real DisplayLink specific +code. They write to the standard framebuffer interface and rely on udlfb +to do its thing. The one extra feature they have is the ability to report +rectangles from the X DAMAGE protocol extension down to udlfb via udlfb's +damage interface (which will hopefully be standardized for all virtual +framebuffers that need damage info). These damage notifications allow +udlfb to efficiently process the changed pixels. + +Module Options +============== + +Special configuration for udlfb is usually unnecessary. There are a few +options, however. + +From the command line, pass options to modprobe +modprobe udlfb defio=1 console=1 + +Or for permanent option, create file like /etc/modprobe.d/options with text +options udlfb defio=1 console=1 + +Accepted options: + +fb_defio Make use of the fb_defio (CONFIG_FB_DEFERRED_IO) kernel + module to track changed areas of the framebuffer by page faults. + Standard fbdev applications that use mmap but that do not + report damage, may be able to work with this enabled. + Disabled by default because of overhead and other issues. + +console Allow fbcon to attach to udlfb provided framebuffers. This + is disabled by default because fbcon will aggressively consume + the first framebuffer it finds, which isn't usually what the + user wants in the case of USB displays. + +Sysfs Attributes +================ + +Udlfb creates several files in /sys/class/graphics/fb? +Where ? is the sequential framebuffer id of the particular DisplayLink device + +edid If a valid EDID blob is written to this file (typically + by a udev rule), then udlfb will use this EDID as a + backup in case reading the actual EDID of the monitor + attached to the DisplayLink device fails. This is + especially useful for fixed panels, etc. that cannot + communicate their capabilities via EDID. Reading + this file returns the current EDID of the attached + monitor (or last backup value written). This is + useful to get the EDID of the attached monitor, + which can be passed to utilities like parse-edid. + +metrics_bytes_rendered 32-bit count of pixel bytes rendered + +metrics_bytes_identical 32-bit count of how many of those bytes were found to be + unchanged, based on a shadow framebuffer check + +metrics_bytes_sent 32-bit count of how many bytes were transferred over + USB to communicate the resulting changed pixels to the + hardware. Includes compression and protocol overhead + +metrics_cpu_kcycles_used 32-bit count of CPU cycles used in processing the + above pixels (in thousands of cycles). + +metrics_reset Write-only. Any write to this file resets all metrics + above to zero. Note that the 32-bit counters above + roll over very quickly. To get reliable results, design + performance tests to start and finish in a very short + period of time (one minute or less is safe). + +-- +Bernie Thompson diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 5eafdf435550..df31a7228079 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -111,8 +111,6 @@ source "drivers/staging/vt6655/Kconfig" source "drivers/staging/vt6656/Kconfig" -source "drivers/staging/udlfb/Kconfig" - source "drivers/staging/hv/Kconfig" source "drivers/staging/vme/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index a97a955c094b..7a15c0c82b69 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -38,7 +38,6 @@ obj-$(CONFIG_USB_SERIAL_QUATECH_USB2) += quatech_usb2/ obj-$(CONFIG_OCTEON_ETHERNET) += octeon/ obj-$(CONFIG_VT6655) += vt6655/ obj-$(CONFIG_VT6656) += vt6656/ -obj-$(CONFIG_FB_UDL) += udlfb/ obj-$(CONFIG_HYPERV) += hv/ obj-$(CONFIG_VME_BUS) += vme/ obj-$(CONFIG_MRST_RAR_HANDLER) += memrar/ diff --git a/drivers/staging/udlfb/Kconfig b/drivers/staging/udlfb/Kconfig deleted file mode 100644 index 65bd5db4ca56..000000000000 --- a/drivers/staging/udlfb/Kconfig +++ /dev/null @@ -1,14 +0,0 @@ -config FB_UDL - tristate "Displaylink USB Framebuffer support" - depends on FB && USB - select FB_MODE_HELPERS - select FB_SYS_FILLRECT - select FB_SYS_COPYAREA - select FB_SYS_IMAGEBLIT - select FB_SYS_FOPS - select FB_DEFERRED_IO - ---help--- - This is a kernel framebuffer driver for DisplayLink USB devices. - Supports fbdev clients like xf86-video-fbdev, kdrive, fbi, and - mplayer -vo fbdev. Supports all USB 2.0 era DisplayLink devices. - To compile as a module, choose M here: the module name is udlfb. diff --git a/drivers/staging/udlfb/Makefile b/drivers/staging/udlfb/Makefile deleted file mode 100644 index 30d9e675b10f..000000000000 --- a/drivers/staging/udlfb/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_FB_UDL) += udlfb.o diff --git a/drivers/staging/udlfb/udlfb.c b/drivers/staging/udlfb/udlfb.c deleted file mode 100644 index fed25105970a..000000000000 --- a/drivers/staging/udlfb/udlfb.c +++ /dev/null @@ -1,1916 +0,0 @@ -/* - * udlfb.c -- Framebuffer driver for DisplayLink USB controller - * - * Copyright (C) 2009 Roberto De Ioris - * Copyright (C) 2009 Jaya Kumar - * Copyright (C) 2009 Bernie Thompson - * - * This file is subject to the terms and conditions of the GNU General Public - * License v2. See the file COPYING in the main directory of this archive for - * more details. - * - * Layout is based on skeletonfb by James Simmons and Geert Uytterhoeven, - * usb-skeleton by GregKH. - * - * Device-specific portions based on information from Displaylink, with work - * from Florian Echtler, Henrik Bjerregaard Pedersen, and others. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udlfb.h" - -static struct fb_fix_screeninfo dlfb_fix = { - .id = "udlfb", - .type = FB_TYPE_PACKED_PIXELS, - .visual = FB_VISUAL_TRUECOLOR, - .xpanstep = 0, - .ypanstep = 0, - .ywrapstep = 0, - .accel = FB_ACCEL_NONE, -}; - -static const u32 udlfb_info_flags = FBINFO_DEFAULT | FBINFO_READS_FAST | -#ifdef FBINFO_VIRTFB - FBINFO_VIRTFB | -#endif - FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT | - FBINFO_HWACCEL_COPYAREA | FBINFO_MISC_ALWAYS_SETPAR; - -/* - * There are many DisplayLink-based products, all with unique PIDs. We are able - * to support all volume ones (circa 2009) with a single driver, so we match - * globally on VID. TODO: Probe() needs to detect when we might be running - * "future" chips, and bail on those, so a compatible driver can match. - */ -static struct usb_device_id id_table[] = { - {.idVendor = 0x17e9, .match_flags = USB_DEVICE_ID_MATCH_VENDOR,}, - {}, -}; -MODULE_DEVICE_TABLE(usb, id_table); - -/* module options */ -static int console; /* Optionally allow fbcon to consume first framebuffer */ -static int fb_defio; /* Optionally enable experimental fb_defio mmap support */ - -/* dlfb keeps a list of urbs for efficient bulk transfers */ -static void dlfb_urb_completion(struct urb *urb); -static struct urb *dlfb_get_urb(struct dlfb_data *dev); -static int dlfb_submit_urb(struct dlfb_data *dev, struct urb * urb, size_t len); -static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size); -static void dlfb_free_urb_list(struct dlfb_data *dev); - -/* - * All DisplayLink bulk operations start with 0xAF, followed by specific code - * All operations are written to buffers which then later get sent to device - */ -static char *dlfb_set_register(char *buf, u8 reg, u8 val) -{ - *buf++ = 0xAF; - *buf++ = 0x20; - *buf++ = reg; - *buf++ = val; - return buf; -} - -static char *dlfb_vidreg_lock(char *buf) -{ - return dlfb_set_register(buf, 0xFF, 0x00); -} - -static char *dlfb_vidreg_unlock(char *buf) -{ - return dlfb_set_register(buf, 0xFF, 0xFF); -} - -/* - * On/Off for driving the DisplayLink framebuffer to the display - * 0x00 H and V sync on - * 0x01 H and V sync off (screen blank but powered) - * 0x07 DPMS powerdown (requires modeset to come back) - */ -static char *dlfb_enable_hvsync(char *buf, bool enable) -{ - if (enable) - return dlfb_set_register(buf, 0x1F, 0x00); - else - return dlfb_set_register(buf, 0x1F, 0x07); -} - -static char *dlfb_set_color_depth(char *buf, u8 selection) -{ - return dlfb_set_register(buf, 0x00, selection); -} - -static char *dlfb_set_base16bpp(char *wrptr, u32 base) -{ - /* the base pointer is 16 bits wide, 0x20 is hi byte. */ - wrptr = dlfb_set_register(wrptr, 0x20, base >> 16); - wrptr = dlfb_set_register(wrptr, 0x21, base >> 8); - return dlfb_set_register(wrptr, 0x22, base); -} - -/* - * DisplayLink HW has separate 16bpp and 8bpp framebuffers. - * In 24bpp modes, the low 323 RGB bits go in the 8bpp framebuffer - */ -static char *dlfb_set_base8bpp(char *wrptr, u32 base) -{ - wrptr = dlfb_set_register(wrptr, 0x26, base >> 16); - wrptr = dlfb_set_register(wrptr, 0x27, base >> 8); - return dlfb_set_register(wrptr, 0x28, base); -} - -static char *dlfb_set_register_16(char *wrptr, u8 reg, u16 value) -{ - wrptr = dlfb_set_register(wrptr, reg, value >> 8); - return dlfb_set_register(wrptr, reg+1, value); -} - -/* - * This is kind of weird because the controller takes some - * register values in a different byte order than other registers. - */ -static char *dlfb_set_register_16be(char *wrptr, u8 reg, u16 value) -{ - wrptr = dlfb_set_register(wrptr, reg, value); - return dlfb_set_register(wrptr, reg+1, value >> 8); -} - -/* - * LFSR is linear feedback shift register. The reason we have this is - * because the display controller needs to minimize the clock depth of - * various counters used in the display path. So this code reverses the - * provided value into the lfsr16 value by counting backwards to get - * the value that needs to be set in the hardware comparator to get the - * same actual count. This makes sense once you read above a couple of - * times and think about it from a hardware perspective. - */ -static u16 dlfb_lfsr16(u16 actual_count) -{ - u32 lv = 0xFFFF; /* This is the lfsr value that the hw starts with */ - - while (actual_count--) { - lv = ((lv << 1) | - (((lv >> 15) ^ (lv >> 4) ^ (lv >> 2) ^ (lv >> 1)) & 1)) - & 0xFFFF; - } - - return (u16) lv; -} - -/* - * This does LFSR conversion on the value that is to be written. - * See LFSR explanation above for more detail. - */ -static char *dlfb_set_register_lfsr16(char *wrptr, u8 reg, u16 value) -{ - return dlfb_set_register_16(wrptr, reg, dlfb_lfsr16(value)); -} - -/* - * This takes a standard fbdev screeninfo struct and all of its monitor mode - * details and converts them into the DisplayLink equivalent register commands. - */ -static char *dlfb_set_vid_cmds(char *wrptr, struct fb_var_screeninfo *var) -{ - u16 xds, yds; - u16 xde, yde; - u16 yec; - - /* x display start */ - xds = var->left_margin + var->hsync_len; - wrptr = dlfb_set_register_lfsr16(wrptr, 0x01, xds); - /* x display end */ - xde = xds + var->xres; - wrptr = dlfb_set_register_lfsr16(wrptr, 0x03, xde); - - /* y display start */ - yds = var->upper_margin + var->vsync_len; - wrptr = dlfb_set_register_lfsr16(wrptr, 0x05, yds); - /* y display end */ - yde = yds + var->yres; - wrptr = dlfb_set_register_lfsr16(wrptr, 0x07, yde); - - /* x end count is active + blanking - 1 */ - wrptr = dlfb_set_register_lfsr16(wrptr, 0x09, - xde + var->right_margin - 1); - - /* libdlo hardcodes hsync start to 1 */ - wrptr = dlfb_set_register_lfsr16(wrptr, 0x0B, 1); - - /* hsync end is width of sync pulse + 1 */ - wrptr = dlfb_set_register_lfsr16(wrptr, 0x0D, var->hsync_len + 1); - - /* hpixels is active pixels */ - wrptr = dlfb_set_register_16(wrptr, 0x0F, var->xres); - - /* yendcount is vertical active + vertical blanking */ - yec = var->yres + var->upper_margin + var->lower_margin + - var->vsync_len; - wrptr = dlfb_set_register_lfsr16(wrptr, 0x11, yec); - - /* libdlo hardcodes vsync start to 0 */ - wrptr = dlfb_set_register_lfsr16(wrptr, 0x13, 0); - - /* vsync end is width of vsync pulse */ - wrptr = dlfb_set_register_lfsr16(wrptr, 0x15, var->vsync_len); - - /* vpixels is active pixels */ - wrptr = dlfb_set_register_16(wrptr, 0x17, var->yres); - - /* convert picoseconds to 5kHz multiple for pclk5k = x * 1E12/5k */ - wrptr = dlfb_set_register_16be(wrptr, 0x1B, - 200*1000*1000/var->pixclock); - - return wrptr; -} - -/* - * This takes a standard fbdev screeninfo struct that was fetched or prepared - * and then generates the appropriate command sequence that then drives the - * display controller. - */ -static int dlfb_set_video_mode(struct dlfb_data *dev, - struct fb_var_screeninfo *var) -{ - char *buf; - char *wrptr; - int retval = 0; - int writesize; - struct urb *urb; - - if (!atomic_read(&dev->usb_active)) - return -EPERM; - - urb = dlfb_get_urb(dev); - if (!urb) - return -ENOMEM; - - buf = (char *) urb->transfer_buffer; - - /* - * This first section has to do with setting the base address on the - * controller * associated with the display. There are 2 base - * pointers, currently, we only * use the 16 bpp segment. - */ - wrptr = dlfb_vidreg_lock(buf); - wrptr = dlfb_set_color_depth(wrptr, 0x00); - /* set base for 16bpp segment to 0 */ - wrptr = dlfb_set_base16bpp(wrptr, 0); - /* set base for 8bpp segment to end of fb */ - wrptr = dlfb_set_base8bpp(wrptr, dev->info->fix.smem_len); - - wrptr = dlfb_set_vid_cmds(wrptr, var); - wrptr = dlfb_enable_hvsync(wrptr, true); - wrptr = dlfb_vidreg_unlock(wrptr); - - writesize = wrptr - buf; - - retval = dlfb_submit_urb(dev, urb, writesize); - - return retval; -} - -static int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma) -{ - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end - vma->vm_start; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - unsigned long page, pos; - - if (offset + size > info->fix.smem_len) - return -EINVAL; - - pos = (unsigned long)info->fix.smem_start + offset; - - dl_notice("mmap() framebuffer addr:%lu size:%lu\n", - pos, size); - - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ - return 0; -} - -/* - * Trims identical data from front and back of line - * Sets new front buffer address and width - * And returns byte count of identical pixels - * Assumes CPU natural alignment (unsigned long) - * for back and front buffer ptrs and width - */ -static int dlfb_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes) -{ - int j, k; - const unsigned long *back = (const unsigned long *) bback; - const unsigned long *front = (const unsigned long *) *bfront; - const int width = *width_bytes / sizeof(unsigned long); - int identical = width; - int start = width; - int end = width; - - prefetch((void *) front); - prefetch((void *) back); - - for (j = 0; j < width; j++) { - if (back[j] != front[j]) { - start = j; - break; - } - } - - for (k = width - 1; k > j; k--) { - if (back[k] != front[k]) { - end = k+1; - break; - } - } - - identical = start + (width - end); - *bfront = (u8 *) &front[start]; - *width_bytes = (end - start) * sizeof(unsigned long); - - return identical * sizeof(unsigned long); -} - -/* - * Render a command stream for an encoded horizontal line segment of pixels. - * - * A command buffer holds several commands. - * It always begins with a fresh command header - * (the protocol doesn't require this, but we enforce it to allow - * multiple buffers to be potentially encoded and sent in parallel). - * A single command encodes one contiguous horizontal line of pixels - * - * The function relies on the client to do all allocation, so that - * rendering can be done directly to output buffers (e.g. USB URBs). - * The function fills the supplied command buffer, providing information - * on where it left off, so the client may call in again with additional - * buffers if the line will take several buffers to complete. - * - * A single command can transmit a maximum of 256 pixels, - * regardless of the compression ratio (protocol design limit). - * To the hardware, 0 for a size byte means 256 - * - * Rather than 256 pixel commands which are either rl or raw encoded, - * the rlx command simply assumes alternating raw and rl spans within one cmd. - * This has a slightly larger header overhead, but produces more even results. - * It also processes all data (read and write) in a single pass. - * Performance benchmarks of common cases show it having just slightly better - * compression than 256 pixel raw or rle commands, with similar CPU consumpion. - * But for very rl friendly data, will compress not quite as well. - */ -static void dlfb_compress_hline( - const uint16_t **pixel_start_ptr, - const uint16_t *const pixel_end, - uint32_t *device_address_ptr, - uint8_t **command_buffer_ptr, - const uint8_t *const cmd_buffer_end) -{ - const uint16_t *pixel = *pixel_start_ptr; - uint32_t dev_addr = *device_address_ptr; - uint8_t *cmd = *command_buffer_ptr; - const int bpp = 2; - - while ((pixel_end > pixel) && - (cmd_buffer_end - MIN_RLX_CMD_BYTES > cmd)) { - uint8_t *raw_pixels_count_byte = 0; - uint8_t *cmd_pixels_count_byte = 0; - const uint16_t *raw_pixel_start = 0; - const uint16_t *cmd_pixel_start, *cmd_pixel_end = 0; - - prefetchw((void *) cmd); /* pull in one cache line at least */ - - *cmd++ = 0xAF; - *cmd++ = 0x6B; - *cmd++ = (uint8_t) ((dev_addr >> 16) & 0xFF); - *cmd++ = (uint8_t) ((dev_addr >> 8) & 0xFF); - *cmd++ = (uint8_t) ((dev_addr) & 0xFF); - - cmd_pixels_count_byte = cmd++; /* we'll know this later */ - cmd_pixel_start = pixel; - - raw_pixels_count_byte = cmd++; /* we'll know this later */ - raw_pixel_start = pixel; - - cmd_pixel_end = pixel + min(MAX_CMD_PIXELS + 1, - min((int)(pixel_end - pixel), - (int)(cmd_buffer_end - cmd) / bpp)); - - prefetch_range((void *) pixel, (cmd_pixel_end - pixel) * bpp); - - while (pixel < cmd_pixel_end) { - const uint16_t * const repeating_pixel = pixel; - - *(uint16_t *)cmd = cpu_to_be16p(pixel); - cmd += 2; - pixel++; - - if (unlikely((pixel < cmd_pixel_end) && - (*pixel == *repeating_pixel))) { - /* go back and fill in raw pixel count */ - *raw_pixels_count_byte = ((repeating_pixel - - raw_pixel_start) + 1) & 0xFF; - - while ((pixel < cmd_pixel_end) - && (*pixel == *repeating_pixel)) { - pixel++; - } - - /* immediately after raw data is repeat byte */ - *cmd++ = ((pixel - repeating_pixel) - 1) & 0xFF; - - /* Then start another raw pixel span */ - raw_pixel_start = pixel; - raw_pixels_count_byte = cmd++; - } - } - - if (pixel > raw_pixel_start) { - /* finalize last RAW span */ - *raw_pixels_count_byte = (pixel-raw_pixel_start) & 0xFF; - } - - *cmd_pixels_count_byte = (pixel - cmd_pixel_start) & 0xFF; - dev_addr += (pixel - cmd_pixel_start) * bpp; - } - - if (cmd_buffer_end <= MIN_RLX_CMD_BYTES + cmd) { - /* Fill leftover bytes with no-ops */ - if (cmd_buffer_end > cmd) - memset(cmd, 0xAF, cmd_buffer_end - cmd); - cmd = (uint8_t *) cmd_buffer_end; - } - - *command_buffer_ptr = cmd; - *pixel_start_ptr = pixel; - *device_address_ptr = dev_addr; - - return; -} - -/* - * There are 3 copies of every pixel: The front buffer that the fbdev - * client renders to, the actual framebuffer across the USB bus in hardware - * (that we can only write to, slowly, and can never read), and (optionally) - * our shadow copy that tracks what's been sent to that hardware buffer. - */ -static int dlfb_render_hline(struct dlfb_data *dev, struct urb **urb_ptr, - const char *front, char **urb_buf_ptr, - u32 byte_offset, u32 byte_width, - int *ident_ptr, int *sent_ptr) -{ - const u8 *line_start, *line_end, *next_pixel; - u32 dev_addr = dev->base16 + byte_offset; - struct urb *urb = *urb_ptr; - u8 *cmd = *urb_buf_ptr; - u8 *cmd_end = (u8 *) urb->transfer_buffer + urb->transfer_buffer_length; - - line_start = (u8 *) (front + byte_offset); - next_pixel = line_start; - line_end = next_pixel + byte_width; - - if (dev->backing_buffer) { - int offset; - const u8 *back_start = (u8 *) (dev->backing_buffer - + byte_offset); - - *ident_ptr += dlfb_trim_hline(back_start, &next_pixel, - &byte_width); - - offset = next_pixel - line_start; - line_end = next_pixel + byte_width; - dev_addr += offset; - back_start += offset; - line_start += offset; - - memcpy((char *)back_start, (char *) line_start, - byte_width); - } - - while (next_pixel < line_end) { - - dlfb_compress_hline((const uint16_t **) &next_pixel, - (const uint16_t *) line_end, &dev_addr, - (u8 **) &cmd, (u8 *) cmd_end); - - if (cmd >= cmd_end) { - int len = cmd - (u8 *) urb->transfer_buffer; - if (dlfb_submit_urb(dev, urb, len)) - return 1; /* lost pixels is set */ - *sent_ptr += len; - urb = dlfb_get_urb(dev); - if (!urb) - return 1; /* lost_pixels is set */ - *urb_ptr = urb; - cmd = urb->transfer_buffer; - cmd_end = &cmd[urb->transfer_buffer_length]; - } - } - - *urb_buf_ptr = cmd; - - return 0; -} - -int dlfb_handle_damage(struct dlfb_data *dev, int x, int y, - int width, int height, char *data) -{ - int i, ret; - char *cmd; - cycles_t start_cycles, end_cycles; - int bytes_sent = 0; - int bytes_identical = 0; - struct urb *urb; - int aligned_x; - - start_cycles = get_cycles(); - - aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long)); - width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long)); - x = aligned_x; - - if ((width <= 0) || - (x + width > dev->info->var.xres) || - (y + height > dev->info->var.yres)) - return -EINVAL; - - if (!atomic_read(&dev->usb_active)) - return 0; - - urb = dlfb_get_urb(dev); - if (!urb) - return 0; - cmd = urb->transfer_buffer; - - for (i = y; i < y + height ; i++) { - const int line_offset = dev->info->fix.line_length * i; - const int byte_offset = line_offset + (x * BPP); - - if (dlfb_render_hline(dev, &urb, - (char *) dev->info->fix.smem_start, - &cmd, byte_offset, width * BPP, - &bytes_identical, &bytes_sent)) - goto error; - } - - if (cmd > (char *) urb->transfer_buffer) { - /* Send partial buffer remaining before exiting */ - int len = cmd - (char *) urb->transfer_buffer; - ret = dlfb_submit_urb(dev, urb, len); - bytes_sent += len; - } else - dlfb_urb_completion(urb); - -error: - atomic_add(bytes_sent, &dev->bytes_sent); - atomic_add(bytes_identical, &dev->bytes_identical); - atomic_add(width*height*2, &dev->bytes_rendered); - end_cycles = get_cycles(); - atomic_add(((unsigned int) ((end_cycles - start_cycles) - >> 10)), /* Kcycles */ - &dev->cpu_kcycles_used); - - return 0; -} - -static ssize_t dlfb_ops_read(struct fb_info *info, char __user *buf, - size_t count, loff_t *ppos) -{ - ssize_t result = -ENOSYS; - -#if defined CONFIG_FB_SYS_FOPS || defined CONFIG_FB_SYS_FOPS_MODULE - result = fb_sys_read(info, buf, count, ppos); -#endif - - return result; -} - -/* - * Path triggered by usermode clients who write to filesystem - * e.g. cat filename > /dev/fb1 - * Not used by X Windows or text-mode console. But useful for testing. - * Slow because of extra copy and we must assume all pixels dirty. - */ -static ssize_t dlfb_ops_write(struct fb_info *info, const char __user *buf, - size_t count, loff_t *ppos) -{ - ssize_t result = -ENOSYS; - struct dlfb_data *dev = info->par; - u32 offset = (u32) *ppos; - -#if defined CONFIG_FB_SYS_FOPS || defined CONFIG_FB_SYS_FOPS_MODULE - - result = fb_sys_write(info, buf, count, ppos); - - if (result > 0) { - int start = max((int)(offset / info->fix.line_length) - 1, 0); - int lines = min((u32)((result / info->fix.line_length) + 1), - (u32)info->var.yres); - - dlfb_handle_damage(dev, 0, start, info->var.xres, - lines, info->screen_base); - } -#endif - - return result; -} - -/* hardware has native COPY command (see libdlo), but not worth it for fbcon */ -static void dlfb_ops_copyarea(struct fb_info *info, - const struct fb_copyarea *area) -{ - - struct dlfb_data *dev = info->par; - -#if defined CONFIG_FB_SYS_COPYAREA || defined CONFIG_FB_SYS_COPYAREA_MODULE - - sys_copyarea(info, area); - - dlfb_handle_damage(dev, area->dx, area->dy, - area->width, area->height, info->screen_base); -#endif - -} - -static void dlfb_ops_imageblit(struct fb_info *info, - const struct fb_image *image) -{ - struct dlfb_data *dev = info->par; - -#if defined CONFIG_FB_SYS_IMAGEBLIT || defined CONFIG_FB_SYS_IMAGEBLIT_MODULE - - sys_imageblit(info, image); - - dlfb_handle_damage(dev, image->dx, image->dy, - image->width, image->height, info->screen_base); - -#endif - -} - -static void dlfb_ops_fillrect(struct fb_info *info, - const struct fb_fillrect *rect) -{ - struct dlfb_data *dev = info->par; - -#if defined CONFIG_FB_SYS_FILLRECT || defined CONFIG_FB_SYS_FILLRECT_MODULE - - sys_fillrect(info, rect); - - dlfb_handle_damage(dev, rect->dx, rect->dy, rect->width, - rect->height, info->screen_base); -#endif - -} - -#ifdef CONFIG_FB_DEFERRED_IO -/* - * NOTE: fb_defio.c is holding info->fbdefio.mutex - * Touching ANY framebuffer memory that triggers a page fault - * in fb_defio will cause a deadlock, when it also tries to - * grab the same mutex. - */ -static void dlfb_dpy_deferred_io(struct fb_info *info, - struct list_head *pagelist) -{ - struct page *cur; - struct fb_deferred_io *fbdefio = info->fbdefio; - struct dlfb_data *dev = info->par; - struct urb *urb; - char *cmd; - cycles_t start_cycles, end_cycles; - int bytes_sent = 0; - int bytes_identical = 0; - int bytes_rendered = 0; - - if (!fb_defio) - return; - - if (!atomic_read(&dev->usb_active)) - return; - - start_cycles = get_cycles(); - - urb = dlfb_get_urb(dev); - if (!urb) - return; - - cmd = urb->transfer_buffer; - - /* walk the written page list and render each to device */ - list_for_each_entry(cur, &fbdefio->pagelist, lru) { - - if (dlfb_render_hline(dev, &urb, (char *) info->fix.smem_start, - &cmd, cur->index << PAGE_SHIFT, - PAGE_SIZE, &bytes_identical, &bytes_sent)) - goto error; - bytes_rendered += PAGE_SIZE; - } - - if (cmd > (char *) urb->transfer_buffer) { - /* Send partial buffer remaining before exiting */ - int len = cmd - (char *) urb->transfer_buffer; - dlfb_submit_urb(dev, urb, len); - bytes_sent += len; - } else - dlfb_urb_completion(urb); - -error: - atomic_add(bytes_sent, &dev->bytes_sent); - atomic_add(bytes_identical, &dev->bytes_identical); - atomic_add(bytes_rendered, &dev->bytes_rendered); - end_cycles = get_cycles(); - atomic_add(((unsigned int) ((end_cycles - start_cycles) - >> 10)), /* Kcycles */ - &dev->cpu_kcycles_used); -} - -#endif - -static int dlfb_get_edid(struct dlfb_data *dev, char *edid, int len) -{ - int i; - int ret; - char *rbuf; - - rbuf = kmalloc(2, GFP_KERNEL); - if (!rbuf) - return 0; - - for (i = 0; i < len; i++) { - ret = usb_control_msg(dev->udev, - usb_rcvctrlpipe(dev->udev, 0), (0x02), - (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2, - HZ); - if (ret < 1) { - dl_err("Read EDID byte %d failed err %x\n", i, ret); - i--; - break; - } - edid[i] = rbuf[1]; - } - - kfree(rbuf); - - return i; -} - -static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd, - unsigned long arg) -{ - - struct dlfb_data *dev = info->par; - struct dloarea *area = NULL; - - if (!atomic_read(&dev->usb_active)) - return 0; - - /* TODO: Update X server to get this from sysfs instead */ - if (cmd == DLFB_IOCTL_RETURN_EDID) { - char *edid = (char *)arg; - if (copy_to_user(edid, dev->edid, dev->edid_size)) - return -EFAULT; - return 0; - } - - /* TODO: Help propose a standard fb.h ioctl to report mmap damage */ - if (cmd == DLFB_IOCTL_REPORT_DAMAGE) { - - /* - * If we have a damage-aware client, turn fb_defio "off" - * To avoid perf imact of unecessary page fault handling. - * Done by resetting the delay for this fb_info to a very - * long period. Pages will become writable and stay that way. - * Reset to normal value when all clients have closed this fb. - */ - if (info->fbdefio) - info->fbdefio->delay = DL_DEFIO_WRITE_DISABLE; - - area = (struct dloarea *)arg; - - if (area->x < 0) - area->x = 0; - - if (area->x > info->var.xres) - area->x = info->var.xres; - - if (area->y < 0) - area->y = 0; - - if (area->y > info->var.yres) - area->y = info->var.yres; - - dlfb_handle_damage(dev, area->x, area->y, area->w, area->h, - info->screen_base); - } - - return 0; -} - -/* taken from vesafb */ -static int -dlfb_ops_setcolreg(unsigned regno, unsigned red, unsigned green, - unsigned blue, unsigned transp, struct fb_info *info) -{ - int err = 0; - - if (regno >= info->cmap.len) - return 1; - - if (regno < 16) { - if (info->var.red.offset == 10) { - /* 1:5:5:5 */ - ((u32 *) (info->pseudo_palette))[regno] = - ((red & 0xf800) >> 1) | - ((green & 0xf800) >> 6) | ((blue & 0xf800) >> 11); - } else { - /* 0:5:6:5 */ - ((u32 *) (info->pseudo_palette))[regno] = - ((red & 0xf800)) | - ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11); - } - } - - return err; -} - -/* - * It's common for several clients to have framebuffer open simultaneously. - * e.g. both fbcon and X. Makes things interesting. - * Assumes caller is holding info->lock (for open and release at least) - */ -static int dlfb_ops_open(struct fb_info *info, int user) -{ - struct dlfb_data *dev = info->par; - - /* - * fbcon aggressively connects to first framebuffer it finds, - * preventing other clients (X) from working properly. Usually - * not what the user wants. Fail by default with option to enable. - */ - if ((user == 0) & (!console)) - return -EBUSY; - - /* If the USB device is gone, we don't accept new opens */ - if (dev->virtualized) - return -ENODEV; - - dev->fb_count++; - - kref_get(&dev->kref); - -#ifdef CONFIG_FB_DEFERRED_IO - if (fb_defio && (info->fbdefio == NULL)) { - /* enable defio at last moment if not disabled by client */ - - struct fb_deferred_io *fbdefio; - - fbdefio = kmalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); - - if (fbdefio) { - fbdefio->delay = DL_DEFIO_WRITE_DELAY; - fbdefio->deferred_io = dlfb_dpy_deferred_io; - } - - info->fbdefio = fbdefio; - fb_deferred_io_init(info); - } -#endif - - dl_notice("open /dev/fb%d user=%d fb_info=%p count=%d\n", - info->node, user, info, dev->fb_count); - - return 0; -} - -/* - * Called when all client interfaces to start transactions have been disabled, - * and all references to our device instance (dlfb_data) are released. - * Every transaction must have a reference, so we know are fully spun down - */ -static void dlfb_free(struct kref *kref) -{ - struct dlfb_data *dev = container_of(kref, struct dlfb_data, kref); - - /* this function will wait for all in-flight urbs to complete */ - if (dev->urbs.count > 0) - dlfb_free_urb_list(dev); - - if (dev->backing_buffer) - vfree(dev->backing_buffer); - - kfree(dev->edid); - - dl_warn("freeing dlfb_data %p\n", dev); - - kfree(dev); -} - -static void dlfb_release_urb_work(struct work_struct *work) -{ - struct urb_node *unode = container_of(work, struct urb_node, - release_urb_work.work); - - up(&unode->dev->urbs.limit_sem); -} - -static void dlfb_free_framebuffer_work(struct work_struct *work) -{ - struct dlfb_data *dev = container_of(work, struct dlfb_data, - free_framebuffer_work.work); - struct fb_info *info = dev->info; - int node = info->node; - - unregister_framebuffer(info); - - if (info->cmap.len != 0) - fb_dealloc_cmap(&info->cmap); - if (info->monspecs.modedb) - fb_destroy_modedb(info->monspecs.modedb); - if (info->screen_base) - vfree(info->screen_base); - - fb_destroy_modelist(&info->modelist); - - dev->info = 0; - - /* Assume info structure is freed after this point */ - framebuffer_release(info); - - dl_warn("fb_info for /dev/fb%d has been freed\n", node); - - /* ref taken in probe() as part of registering framebfufer */ - kref_put(&dev->kref, dlfb_free); -} - -/* - * Assumes caller is holding info->lock mutex (for open and release at least) - */ -static int dlfb_ops_release(struct fb_info *info, int user) -{ - struct dlfb_data *dev = info->par; - - dev->fb_count--; - - /* We can't free fb_info here - fbmem will touch it when we return */ - if (dev->virtualized && (dev->fb_count == 0)) - schedule_delayed_work(&dev->free_framebuffer_work, HZ); - -#ifdef CONFIG_FB_DEFERRED_IO - if ((dev->fb_count == 0) && (info->fbdefio)) { - fb_deferred_io_cleanup(info); - kfree(info->fbdefio); - info->fbdefio = NULL; - info->fbops->fb_mmap = dlfb_ops_mmap; - } -#endif - - dl_warn("released /dev/fb%d user=%d count=%d\n", - info->node, user, dev->fb_count); - - kref_put(&dev->kref, dlfb_free); - - return 0; -} - -/* - * Check whether a video mode is supported by the DisplayLink chip - * We start from monitor's modes, so don't need to filter that here - */ -static int dlfb_is_valid_mode(struct fb_videomode *mode, - struct fb_info *info) -{ - struct dlfb_data *dev = info->par; - - if (mode->xres * mode->yres > dev->sku_pixel_limit) { - dl_warn("%dx%d beyond chip capabilities\n", - mode->xres, mode->yres); - return 0; - } - - dl_info("%dx%d valid mode\n", mode->xres, mode->yres); - - return 1; -} - -static void dlfb_var_color_format(struct fb_var_screeninfo *var) -{ - const struct fb_bitfield red = { 11, 5, 0 }; - const struct fb_bitfield green = { 5, 6, 0 }; - const struct fb_bitfield blue = { 0, 5, 0 }; - - var->bits_per_pixel = 16; - var->red = red; - var->green = green; - var->blue = blue; -} - -static int dlfb_ops_check_var(struct fb_var_screeninfo *var, - struct fb_info *info) -{ - struct fb_videomode mode; - - /* TODO: support dynamically changing framebuffer size */ - if ((var->xres * var->yres * 2) > info->fix.smem_len) - return -EINVAL; - - /* set device-specific elements of var unrelated to mode */ - dlfb_var_color_format(var); - - fb_var_to_videomode(&mode, var); - - if (!dlfb_is_valid_mode(&mode, info)) - return -EINVAL; - - return 0; -} - -static int dlfb_ops_set_par(struct fb_info *info) -{ - struct dlfb_data *dev = info->par; - int result; - u16 *pix_framebuffer; - int i; - - dl_notice("set_par mode %dx%d\n", info->var.xres, info->var.yres); - - result = dlfb_set_video_mode(dev, &info->var); - - if ((result == 0) && (dev->fb_count == 0)) { - - /* paint greenscreen */ - - pix_framebuffer = (u16 *) info->screen_base; - for (i = 0; i < info->fix.smem_len / 2; i++) - pix_framebuffer[i] = 0x37e6; - - dlfb_handle_damage(dev, 0, 0, info->var.xres, info->var.yres, - info->screen_base); - } - - return result; -} - -/* - * In order to come back from full DPMS off, we need to set the mode again - */ -static int dlfb_ops_blank(int blank_mode, struct fb_info *info) -{ - struct dlfb_data *dev = info->par; - - if (blank_mode != FB_BLANK_UNBLANK) { - char *bufptr; - struct urb *urb; - - urb = dlfb_get_urb(dev); - if (!urb) - return 0; - - bufptr = (char *) urb->transfer_buffer; - bufptr = dlfb_vidreg_lock(bufptr); - bufptr = dlfb_enable_hvsync(bufptr, false); - bufptr = dlfb_vidreg_unlock(bufptr); - - dlfb_submit_urb(dev, urb, bufptr - - (char *) urb->transfer_buffer); - } else { - dlfb_set_video_mode(dev, &info->var); - } - - return 0; -} - -static struct fb_ops dlfb_ops = { - .owner = THIS_MODULE, - .fb_read = dlfb_ops_read, - .fb_write = dlfb_ops_write, - .fb_setcolreg = dlfb_ops_setcolreg, - .fb_fillrect = dlfb_ops_fillrect, - .fb_copyarea = dlfb_ops_copyarea, - .fb_imageblit = dlfb_ops_imageblit, - .fb_mmap = dlfb_ops_mmap, - .fb_ioctl = dlfb_ops_ioctl, - .fb_open = dlfb_ops_open, - .fb_release = dlfb_ops_release, - .fb_blank = dlfb_ops_blank, - .fb_check_var = dlfb_ops_check_var, - .fb_set_par = dlfb_ops_set_par, -}; - - -/* - * Assumes &info->lock held by caller - * Assumes no active clients have framebuffer open - */ -static int dlfb_realloc_framebuffer(struct dlfb_data *dev, struct fb_info *info) -{ - int retval = -ENOMEM; - int old_len = info->fix.smem_len; - int new_len; - unsigned char *old_fb = info->screen_base; - unsigned char *new_fb; - unsigned char *new_back; - - dl_warn("Reallocating framebuffer. Addresses will change!\n"); - - new_len = info->fix.line_length * info->var.yres; - - if (PAGE_ALIGN(new_len) > old_len) { - /* - * Alloc system memory for virtual framebuffer - */ - new_fb = vmalloc(new_len); - if (!new_fb) { - dl_err("Virtual framebuffer alloc failed\n"); - goto error; - } - - if (info->screen_base) { - memcpy(new_fb, old_fb, old_len); - vfree(info->screen_base); - } - - info->screen_base = new_fb; - info->fix.smem_len = PAGE_ALIGN(new_len); - info->fix.smem_start = (unsigned long) new_fb; - info->flags = udlfb_info_flags; - - /* - * Second framebuffer copy to mirror the framebuffer state - * on the physical USB device. We can function without this. - * But with imperfect damage info we may send pixels over USB - * that were, in fact, unchanged - wasting limited USB bandwidth - */ - new_back = vmalloc(new_len); - if (!new_back) - dl_info("No shadow/backing buffer allcoated\n"); - else { - if (dev->backing_buffer) - vfree(dev->backing_buffer); - dev->backing_buffer = new_back; - memset(dev->backing_buffer, 0, new_len); - } - } - - retval = 0; - -error: - return retval; -} - -/* - * 1) Get EDID from hw, or use sw default - * 2) Parse into various fb_info structs - * 3) Allocate virtual framebuffer memory to back highest res mode - * - * Parses EDID into three places used by various parts of fbdev: - * fb_var_screeninfo contains the timing of the monitor's preferred mode - * fb_info.monspecs is full parsed EDID info, including monspecs.modedb - * fb_info.modelist is a linked list of all monitor & VESA modes which work - * - * If EDID is not readable/valid, then modelist is all VESA modes, - * monspecs is NULL, and fb_var_screeninfo is set to safe VESA mode - * Returns 0 if successful - */ -static int dlfb_setup_modes(struct dlfb_data *dev, - struct fb_info *info, - char *default_edid, size_t default_edid_size) -{ - int i; - const struct fb_videomode *default_vmode = NULL; - int result = 0; - char *edid; - int tries = 3; - - if (info->dev) /* only use mutex if info has been registered */ - mutex_lock(&info->lock); - - edid = kmalloc(MAX_EDID_SIZE, GFP_KERNEL); - if (!edid) { - result = -ENOMEM; - goto error; - } - - fb_destroy_modelist(&info->modelist); - memset(&info->monspecs, 0, sizeof(info->monspecs)); - - /* - * Try to (re)read EDID from hardware first - * EDID data may return, but not parse as valid - * Try again a few times, in case of e.g. analog cable noise - */ - while (tries--) { - - i = dlfb_get_edid(dev, edid, MAX_EDID_SIZE); - - if (i >= MIN_EDID_SIZE) - fb_edid_to_monspecs(edid, &info->monspecs); - - if (info->monspecs.modedb_len > 0) { - dev->edid = edid; - dev->edid_size = i; - break; - } - } - - /* If that fails, use a previously returned EDID if available */ - if (info->monspecs.modedb_len == 0) { - - dl_err("Unable to get valid EDID from device/display\n"); - - if (dev->edid) { - fb_edid_to_monspecs(dev->edid, &info->monspecs); - if (info->monspecs.modedb_len > 0) - dl_err("Using previously queried EDID\n"); - } - } - - /* If that fails, use the default EDID we were handed */ - if (info->monspecs.modedb_len == 0) { - if (default_edid_size >= MIN_EDID_SIZE) { - fb_edid_to_monspecs(default_edid, &info->monspecs); - if (info->monspecs.modedb_len > 0) { - memcpy(edid, default_edid, default_edid_size); - dev->edid = edid; - dev->edid_size = default_edid_size; - dl_err("Using default/backup EDID\n"); - } - } - } - - /* If we've got modes, let's pick a best default mode */ - if (info->monspecs.modedb_len > 0) { - - for (i = 0; i < info->monspecs.modedb_len; i++) { - if (dlfb_is_valid_mode(&info->monspecs.modedb[i], info)) - fb_add_videomode(&info->monspecs.modedb[i], - &info->modelist); - else /* if we've removed top/best mode */ - info->monspecs.misc &= ~FB_MISC_1ST_DETAIL; - } - - default_vmode = fb_find_best_display(&info->monspecs, - &info->modelist); - } - - /* If everything else has failed, fall back to safe default mode */ - if (default_vmode == NULL) { - - struct fb_videomode fb_vmode = {0}; - - /* - * Add the standard VESA modes to our modelist - * Since we don't have EDID, there may be modes that - * overspec monitor and/or are incorrect aspect ratio, etc. - * But at least the user has a chance to choose - */ - for (i = 0; i < VESA_MODEDB_SIZE; i++) { - if (dlfb_is_valid_mode((struct fb_videomode *) - &vesa_modes[i], info)) - fb_add_videomode(&vesa_modes[i], - &info->modelist); - } - - /* - * default to resolution safe for projectors - * (since they are most common case without EDID) - */ - fb_vmode.xres = 800; - fb_vmode.yres = 600; - fb_vmode.refresh = 60; - default_vmode = fb_find_nearest_mode(&fb_vmode, - &info->modelist); - } - - /* If we have good mode and no active clients*/ - if ((default_vmode != NULL) && (dev->fb_count == 0)) { - - fb_videomode_to_var(&info->var, default_vmode); - dlfb_var_color_format(&info->var); - - /* - * with mode size info, we can now alloc our framebuffer. - */ - memcpy(&info->fix, &dlfb_fix, sizeof(dlfb_fix)); - info->fix.line_length = info->var.xres * - (info->var.bits_per_pixel / 8); - - result = dlfb_realloc_framebuffer(dev, info); - - } else - result = -EINVAL; - -error: - if (edid && (dev->edid != edid)) - kfree(edid); - - if (info->dev) - mutex_unlock(&info->lock); - - return result; -} - -static ssize_t metrics_bytes_rendered_show(struct device *fbdev, - struct device_attribute *a, char *buf) { - struct fb_info *fb_info = dev_get_drvdata(fbdev); - struct dlfb_data *dev = fb_info->par; - return snprintf(buf, PAGE_SIZE, "%u\n", - atomic_read(&dev->bytes_rendered)); -} - -static ssize_t metrics_bytes_identical_show(struct device *fbdev, - struct device_attribute *a, char *buf) { - struct fb_info *fb_info = dev_get_drvdata(fbdev); - struct dlfb_data *dev = fb_info->par; - return snprintf(buf, PAGE_SIZE, "%u\n", - atomic_read(&dev->bytes_identical)); -} - -static ssize_t metrics_bytes_sent_show(struct device *fbdev, - struct device_attribute *a, char *buf) { - struct fb_info *fb_info = dev_get_drvdata(fbdev); - struct dlfb_data *dev = fb_info->par; - return snprintf(buf, PAGE_SIZE, "%u\n", - atomic_read(&dev->bytes_sent)); -} - -static ssize_t metrics_cpu_kcycles_used_show(struct device *fbdev, - struct device_attribute *a, char *buf) { - struct fb_info *fb_info = dev_get_drvdata(fbdev); - struct dlfb_data *dev = fb_info->par; - return snprintf(buf, PAGE_SIZE, "%u\n", - atomic_read(&dev->cpu_kcycles_used)); -} - -static ssize_t edid_show( - struct file *filp, - struct kobject *kobj, struct bin_attribute *a, - char *buf, loff_t off, size_t count) { - struct device *fbdev = container_of(kobj, struct device, kobj); - struct fb_info *fb_info = dev_get_drvdata(fbdev); - struct dlfb_data *dev = fb_info->par; - - if (dev->edid == NULL) - return 0; - - if ((off >= dev->edid_size) || (count > dev->edid_size)) - return 0; - - if (off + count > dev->edid_size) - count = dev->edid_size - off; - - dl_info("sysfs edid copy %p to %p, %d bytes\n", - dev->edid, buf, (int) count); - - memcpy(buf, dev->edid, count); - - return count; -} - -static ssize_t edid_store( - struct file *filp, - struct kobject *kobj, struct bin_attribute *a, - char *src, loff_t src_off, size_t src_size) { - struct device *fbdev = container_of(kobj, struct device, kobj); - struct fb_info *fb_info = dev_get_drvdata(fbdev); - struct dlfb_data *dev = fb_info->par; - - /* We only support write of entire EDID at once, no offset*/ - if ((src_size < MIN_EDID_SIZE) || - (src_size > MAX_EDID_SIZE) || - (src_off != 0)) - return 0; - - dlfb_setup_modes(dev, fb_info, src, src_size); - - if (dev->edid && (memcmp(src, dev->edid, src_size) == 0)) { - dl_info("sysfs written EDID is new default\n"); - dlfb_ops_set_par(fb_info); - return src_size; - } else - return 0; -} - -static ssize_t metrics_reset_store(struct device *fbdev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct fb_info *fb_info = dev_get_drvdata(fbdev); - struct dlfb_data *dev = fb_info->par; - - atomic_set(&dev->bytes_rendered, 0); - atomic_set(&dev->bytes_identical, 0); - atomic_set(&dev->bytes_sent, 0); - atomic_set(&dev->cpu_kcycles_used, 0); - - return count; -} - -static struct bin_attribute edid_attr = { - .attr.name = "edid", - .attr.mode = 0666, - .size = MAX_EDID_SIZE, - .read = edid_show, - .write = edid_store -}; - -static struct device_attribute fb_device_attrs[] = { - __ATTR_RO(metrics_bytes_rendered), - __ATTR_RO(metrics_bytes_identical), - __ATTR_RO(metrics_bytes_sent), - __ATTR_RO(metrics_cpu_kcycles_used), - __ATTR(metrics_reset, S_IWUGO, NULL, metrics_reset_store), -}; - -/* - * This is necessary before we can communicate with the display controller. - */ -static int dlfb_select_std_channel(struct dlfb_data *dev) -{ - int ret; - u8 set_def_chn[] = { 0x57, 0xCD, 0xDC, 0xA7, - 0x1C, 0x88, 0x5E, 0x15, - 0x60, 0xFE, 0xC6, 0x97, - 0x16, 0x3D, 0x47, 0xF2 }; - - ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), - NR_USB_REQUEST_CHANNEL, - (USB_DIR_OUT | USB_TYPE_VENDOR), 0, 0, - set_def_chn, sizeof(set_def_chn), USB_CTRL_SET_TIMEOUT); - return ret; -} - -static int dlfb_parse_vendor_descriptor(struct dlfb_data *dev, - struct usb_device *usbdev) -{ - char *desc; - char *buf; - char *desc_end; - - u8 total_len = 0; - - buf = kzalloc(MAX_VENDOR_DESCRIPTOR_SIZE, GFP_KERNEL); - if (!buf) - return false; - desc = buf; - - total_len = usb_get_descriptor(usbdev, 0x5f, /* vendor specific */ - 0, desc, MAX_VENDOR_DESCRIPTOR_SIZE); - if (total_len > 5) { - dl_info("vendor descriptor length:%x data:%02x %02x %02x %02x" \ - "%02x %02x %02x %02x %02x %02x %02x\n", - total_len, desc[0], - desc[1], desc[2], desc[3], desc[4], desc[5], desc[6], - desc[7], desc[8], desc[9], desc[10]); - - if ((desc[0] != total_len) || /* descriptor length */ - (desc[1] != 0x5f) || /* vendor descriptor type */ - (desc[2] != 0x01) || /* version (2 bytes) */ - (desc[3] != 0x00) || - (desc[4] != total_len - 2)) /* length after type */ - goto unrecognized; - - desc_end = desc + total_len; - desc += 5; /* the fixed header we've already parsed */ - - while (desc < desc_end) { - u8 length; - u16 key; - - key = *((u16 *) desc); - desc += sizeof(u16); - length = *desc; - desc++; - - switch (key) { - case 0x0200: { /* max_area */ - u32 max_area; - max_area = le32_to_cpu(*((u32 *)desc)); - dl_warn("DL chip limited to %d pixel modes\n", - max_area); - dev->sku_pixel_limit = max_area; - break; - } - default: - break; - } - desc += length; - } - } - - goto success; - -unrecognized: - /* allow udlfb to load for now even if firmware unrecognized */ - dl_err("Unrecognized vendor firmware descriptor\n"); - -success: - kfree(buf); - return true; -} -static int dlfb_usb_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct usb_device *usbdev; - struct dlfb_data *dev = 0; - struct fb_info *info = 0; - int retval = -ENOMEM; - int i; - - /* usb initialization */ - - usbdev = interface_to_usbdev(interface); - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) { - err("dlfb_usb_probe: failed alloc of dev struct\n"); - goto error; - } - - /* we need to wait for both usb and fbdev to spin down on disconnect */ - kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */ - kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */ - - dev->udev = usbdev; - dev->gdev = &usbdev->dev; /* our generic struct device * */ - usb_set_intfdata(interface, dev); - - dl_info("%s %s - serial #%s\n", - usbdev->manufacturer, usbdev->product, usbdev->serial); - dl_info("vid_%04x&pid_%04x&rev_%04x driver's dlfb_data struct at %p\n", - usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, - usbdev->descriptor.bcdDevice, dev); - dl_info("console enable=%d\n", console); - dl_info("fb_defio enable=%d\n", fb_defio); - - dev->sku_pixel_limit = 2048 * 1152; /* default to maximum */ - - if (!dlfb_parse_vendor_descriptor(dev, usbdev)) { - dl_err("firmware not recognized. Assume incompatible device\n"); - goto error; - } - - if (!dlfb_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) { - retval = -ENOMEM; - dl_err("dlfb_alloc_urb_list failed\n"); - goto error; - } - - /* We don't register a new USB class. Our client interface is fbdev */ - - /* allocates framebuffer driver structure, not framebuffer memory */ - info = framebuffer_alloc(0, &usbdev->dev); - if (!info) { - retval = -ENOMEM; - dl_err("framebuffer_alloc failed\n"); - goto error; - } - - dev->info = info; - info->par = dev; - info->pseudo_palette = dev->pseudo_palette; - info->fbops = &dlfb_ops; - - retval = fb_alloc_cmap(&info->cmap, 256, 0); - if (retval < 0) { - dl_err("fb_alloc_cmap failed %x\n", retval); - goto error; - } - - INIT_DELAYED_WORK(&dev->free_framebuffer_work, - dlfb_free_framebuffer_work); - - INIT_LIST_HEAD(&info->modelist); - - retval = dlfb_setup_modes(dev, info, NULL, 0); - if (retval != 0) { - dl_err("unable to find common mode for display and adapter\n"); - goto error; - } - - /* ready to begin using device */ - - atomic_set(&dev->usb_active, 1); - dlfb_select_std_channel(dev); - - dlfb_ops_check_var(&info->var, info); - dlfb_ops_set_par(info); - - retval = register_framebuffer(info); - if (retval < 0) { - dl_err("register_framebuffer failed %d\n", retval); - goto error; - } - - for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) - device_create_file(info->dev, &fb_device_attrs[i]); - - device_create_bin_file(info->dev, &edid_attr); - - dl_info("DisplayLink USB device /dev/fb%d attached. %dx%d resolution." - " Using %dK framebuffer memory\n", info->node, - info->var.xres, info->var.yres, - ((dev->backing_buffer) ? - info->fix.smem_len * 2 : info->fix.smem_len) >> 10); - return 0; - -error: - if (dev) { - - if (info) { - if (info->cmap.len != 0) - fb_dealloc_cmap(&info->cmap); - if (info->monspecs.modedb) - fb_destroy_modedb(info->monspecs.modedb); - if (info->screen_base) - vfree(info->screen_base); - - fb_destroy_modelist(&info->modelist); - - framebuffer_release(info); - } - - if (dev->backing_buffer) - vfree(dev->backing_buffer); - - kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */ - kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */ - - /* dev has been deallocated. Do not dereference */ - } - - return retval; -} - -static void dlfb_usb_disconnect(struct usb_interface *interface) -{ - struct dlfb_data *dev; - struct fb_info *info; - int i; - - dev = usb_get_intfdata(interface); - info = dev->info; - - dl_info("USB disconnect starting\n"); - - /* we virtualize until all fb clients release. Then we free */ - dev->virtualized = true; - - /* When non-active we'll update virtual framebuffer, but no new urbs */ - atomic_set(&dev->usb_active, 0); - - /* remove udlfb's sysfs interfaces */ - for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) - device_remove_file(info->dev, &fb_device_attrs[i]); - device_remove_bin_file(info->dev, &edid_attr); - - usb_set_intfdata(interface, NULL); - - /* if clients still have us open, will be freed on last close */ - if (dev->fb_count == 0) - schedule_delayed_work(&dev->free_framebuffer_work, 0); - - /* release reference taken by kref_init in probe() */ - kref_put(&dev->kref, dlfb_free); - - /* consider dlfb_data freed */ - - return; -} - -static struct usb_driver dlfb_driver = { - .name = "udlfb", - .probe = dlfb_usb_probe, - .disconnect = dlfb_usb_disconnect, - .id_table = id_table, -}; - -static int __init dlfb_module_init(void) -{ - int res; - - res = usb_register(&dlfb_driver); - if (res) - err("usb_register failed. Error number %d", res); - - return res; -} - -static void __exit dlfb_module_exit(void) -{ - usb_deregister(&dlfb_driver); -} - -module_init(dlfb_module_init); -module_exit(dlfb_module_exit); - -static void dlfb_urb_completion(struct urb *urb) -{ - struct urb_node *unode = urb->context; - struct dlfb_data *dev = unode->dev; - unsigned long flags; - - /* sync/async unlink faults aren't errors */ - if (urb->status) { - if (!(urb->status == -ENOENT || - urb->status == -ECONNRESET || - urb->status == -ESHUTDOWN)) { - dl_err("%s - nonzero write bulk status received: %d\n", - __func__, urb->status); - atomic_set(&dev->lost_pixels, 1); - } - } - - urb->transfer_buffer_length = dev->urbs.size; /* reset to actual */ - - spin_lock_irqsave(&dev->urbs.lock, flags); - list_add_tail(&unode->entry, &dev->urbs.list); - dev->urbs.available++; - spin_unlock_irqrestore(&dev->urbs.lock, flags); - - /* - * When using fb_defio, we deadlock if up() is called - * while another is waiting. So queue to another process. - */ - if (fb_defio) - schedule_delayed_work(&unode->release_urb_work, 0); - else - up(&dev->urbs.limit_sem); -} - -static void dlfb_free_urb_list(struct dlfb_data *dev) -{ - int count = dev->urbs.count; - struct list_head *node; - struct urb_node *unode; - struct urb *urb; - int ret; - unsigned long flags; - - dl_notice("Waiting for completes and freeing all render urbs\n"); - - /* keep waiting and freeing, until we've got 'em all */ - while (count--) { - - /* Getting interrupted means a leak, but ok at shutdown*/ - ret = down_interruptible(&dev->urbs.limit_sem); - if (ret) - break; - - spin_lock_irqsave(&dev->urbs.lock, flags); - - node = dev->urbs.list.next; /* have reserved one with sem */ - list_del_init(node); - - spin_unlock_irqrestore(&dev->urbs.lock, flags); - - unode = list_entry(node, struct urb_node, entry); - urb = unode->urb; - - /* Free each separately allocated piece */ - usb_free_coherent(urb->dev, dev->urbs.size, - urb->transfer_buffer, urb->transfer_dma); - usb_free_urb(urb); - kfree(node); - } - -} - -static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size) -{ - int i = 0; - struct urb *urb; - struct urb_node *unode; - char *buf; - - spin_lock_init(&dev->urbs.lock); - - dev->urbs.size = size; - INIT_LIST_HEAD(&dev->urbs.list); - - while (i < count) { - unode = kzalloc(sizeof(struct urb_node), GFP_KERNEL); - if (!unode) - break; - unode->dev = dev; - - INIT_DELAYED_WORK(&unode->release_urb_work, - dlfb_release_urb_work); - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - kfree(unode); - break; - } - unode->urb = urb; - - buf = usb_alloc_coherent(dev->udev, MAX_TRANSFER, GFP_KERNEL, - &urb->transfer_dma); - if (!buf) { - kfree(unode); - usb_free_urb(urb); - break; - } - - /* urb->transfer_buffer_length set to actual before submit */ - usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 1), - buf, size, dlfb_urb_completion, unode); - urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - - list_add_tail(&unode->entry, &dev->urbs.list); - - i++; - } - - sema_init(&dev->urbs.limit_sem, i); - dev->urbs.count = i; - dev->urbs.available = i; - - dl_notice("allocated %d %d byte urbs\n", i, (int) size); - - return i; -} - -static struct urb *dlfb_get_urb(struct dlfb_data *dev) -{ - int ret = 0; - struct list_head *entry; - struct urb_node *unode; - struct urb *urb = NULL; - unsigned long flags; - - /* Wait for an in-flight buffer to complete and get re-queued */ - ret = down_timeout(&dev->urbs.limit_sem, GET_URB_TIMEOUT); - if (ret) { - atomic_set(&dev->lost_pixels, 1); - dl_warn("wait for urb interrupted: %x available: %d\n", - ret, dev->urbs.available); - goto error; - } - - spin_lock_irqsave(&dev->urbs.lock, flags); - - BUG_ON(list_empty(&dev->urbs.list)); /* reserved one with limit_sem */ - entry = dev->urbs.list.next; - list_del_init(entry); - dev->urbs.available--; - - spin_unlock_irqrestore(&dev->urbs.lock, flags); - - unode = list_entry(entry, struct urb_node, entry); - urb = unode->urb; - -error: - return urb; -} - -static int dlfb_submit_urb(struct dlfb_data *dev, struct urb *urb, size_t len) -{ - int ret; - - BUG_ON(len > dev->urbs.size); - - urb->transfer_buffer_length = len; /* set to actual payload len */ - ret = usb_submit_urb(urb, GFP_KERNEL); - if (ret) { - dlfb_urb_completion(urb); /* because no one else will */ - atomic_set(&dev->lost_pixels, 1); - dl_err("usb_submit_urb error %x\n", ret); - } - return ret; -} - -module_param(console, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); -MODULE_PARM_DESC(console, "Allow fbcon to consume first framebuffer found"); - -module_param(fb_defio, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); -MODULE_PARM_DESC(fb_defio, "Enable fb_defio mmap support. *Experimental*"); - -MODULE_AUTHOR("Roberto De Ioris , " - "Jaya Kumar , " - "Bernie Thompson "); -MODULE_DESCRIPTION("DisplayLink kernel framebuffer driver"); -MODULE_LICENSE("GPL"); - diff --git a/drivers/staging/udlfb/udlfb.h b/drivers/staging/udlfb/udlfb.h deleted file mode 100644 index 6f9785e9d62e..000000000000 --- a/drivers/staging/udlfb/udlfb.h +++ /dev/null @@ -1,117 +0,0 @@ -#ifndef UDLFB_H -#define UDLFB_H - -/* - * TODO: Propose standard fb.h ioctl for reporting damage, - * using _IOWR() and one of the existing area structs from fb.h - * Consider these ioctls deprecated, but they're still used by the - * DisplayLink X server as yet - need both to be modified in tandem - * when new ioctl(s) are ready. - */ -#define DLFB_IOCTL_RETURN_EDID 0xAD -#define DLFB_IOCTL_REPORT_DAMAGE 0xAA -struct dloarea { - int x, y; - int w, h; - int x2, y2; -}; - -struct urb_node { - struct list_head entry; - struct dlfb_data *dev; - struct delayed_work release_urb_work; - struct urb *urb; -}; - -struct urb_list { - struct list_head list; - spinlock_t lock; - struct semaphore limit_sem; - int available; - int count; - size_t size; -}; - -struct dlfb_data { - struct usb_device *udev; - struct device *gdev; /* &udev->dev */ - struct fb_info *info; - struct urb_list urbs; - struct kref kref; - char *backing_buffer; - int fb_count; - bool virtualized; /* true when physical usb device not present */ - struct delayed_work free_framebuffer_work; - atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */ - atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */ - char *edid; /* null until we read edid from hw or get from sysfs */ - size_t edid_size; - int sku_pixel_limit; - int base16; - int base8; - u32 pseudo_palette[256]; - /* blit-only rendering path metrics, exposed through sysfs */ - atomic_t bytes_rendered; /* raw pixel-bytes driver asked to render */ - atomic_t bytes_identical; /* saved effort with backbuffer comparison */ - atomic_t bytes_sent; /* to usb, after compression including overhead */ - atomic_t cpu_kcycles_used; /* transpired during pixel processing */ -}; - -#define NR_USB_REQUEST_I2C_SUB_IO 0x02 -#define NR_USB_REQUEST_CHANNEL 0x12 - -/* -BULK_SIZE as per usb-skeleton. Can we get full page and avoid overhead? */ -#define BULK_SIZE 512 -#define MAX_TRANSFER (PAGE_SIZE*16 - BULK_SIZE) -#define WRITES_IN_FLIGHT (4) - -#define MIN_EDID_SIZE 128 -#define MAX_EDID_SIZE 128 - -#define MAX_VENDOR_DESCRIPTOR_SIZE 256 - -#define GET_URB_TIMEOUT HZ -#define FREE_URB_TIMEOUT (HZ*2) - -#define BPP 2 -#define MAX_CMD_PIXELS 255 - -#define RLX_HEADER_BYTES 7 -#define MIN_RLX_PIX_BYTES 4 -#define MIN_RLX_CMD_BYTES (RLX_HEADER_BYTES + MIN_RLX_PIX_BYTES) - -#define RLE_HEADER_BYTES 6 -#define MIN_RLE_PIX_BYTES 3 -#define MIN_RLE_CMD_BYTES (RLE_HEADER_BYTES + MIN_RLE_PIX_BYTES) - -#define RAW_HEADER_BYTES 6 -#define MIN_RAW_PIX_BYTES 2 -#define MIN_RAW_CMD_BYTES (RAW_HEADER_BYTES + MIN_RAW_PIX_BYTES) - -#define DL_DEFIO_WRITE_DELAY 5 /* fb_deferred_io.delay in jiffies */ -#define DL_DEFIO_WRITE_DISABLE (HZ*60) /* "disable" with long delay */ - -/* remove these once align.h patch is taken into kernel */ -#define DL_ALIGN_UP(x, a) ALIGN(x, a) -#define DL_ALIGN_DOWN(x, a) ALIGN(x-(a-1), a) - -/* remove once this gets added to sysfs.h */ -#define __ATTR_RW(attr) __ATTR(attr, 0644, attr##_show, attr##_store) - -/* - * udlfb is both a usb device, and a framebuffer device. - * They may exist at the same time, but during various stages - * inactivity, teardown, or "virtual" operation, only one or the - * other will exist (one will outlive the other). So we can't - * call the dev_*() macros, because we don't have a stable dev object. - */ -#define dl_err(format, arg...) \ - pr_err("udlfb: " format, ## arg) -#define dl_warn(format, arg...) \ - pr_warning("udlfb: " format, ## arg) -#define dl_notice(format, arg...) \ - pr_notice("udlfb: " format, ## arg) -#define dl_info(format, arg...) \ - pr_info("udlfb: " format, ## arg) - -#endif diff --git a/drivers/staging/udlfb/udlfb.txt b/drivers/staging/udlfb/udlfb.txt deleted file mode 100644 index 7fdde2a02a27..000000000000 --- a/drivers/staging/udlfb/udlfb.txt +++ /dev/null @@ -1,144 +0,0 @@ - -What is udlfb? -=============== - -This is a driver for DisplayLink USB 2.0 era graphics chips. - -DisplayLink chips provide simple hline/blit operations with some compression, -pairing that with a hardware framebuffer (16MB) on the other end of the -USB wire. That hardware framebuffer is able to drive the VGA, DVI, or HDMI -monitor with no CPU involvement until a pixel has to change. - -The CPU or other local resource does all the rendering; optinally compares the -result with a local shadow of the remote hardware framebuffer to identify -the minimal set of pixels that have changed; and compresses and sends those -pixels line-by-line via USB bulk transfers. - -Because of the efficiency of bulk transfers and a protocol on top that -does not require any acks - the effect is very low latency that -can support surprisingly high resolutions with good performance for -non-gaming and non-video applications. - -Mode setting, EDID read, etc are other bulk or control transfers. Mode -setting is very flexible - able to set nearly arbitrary modes from any timing. - -Advantages of USB graphics in general: - - * Ability to add a nearly arbitrary number of displays to any USB 2.0 - capable system. On Linux, number of displays is limited by fbdev interface - (FB_MAX is currently 32). Of course, all USB devices on the same - host controller share the same 480Mbs USB 2.0 interface. - -Advantages of supporting DisplayLink chips with kernel framebuffer interface: - - * The actual hardware functionality of DisplayLink chips matches nearly - one-to-one with the fbdev interface, making the driver quite small and - tight relative to the functionality it provides. - * X servers and other applications can use the standard fbdev interface - from user mode to talk to the device, without needing to know anything - about USB or DisplayLink's protocol at all. A "displaylink" X driver - and a slightly modified "fbdev" X driver are among those that already do. - -Disadvantages: - - * Fbdev's mmap interface assumes a real hardware framebuffer is mapped. - In the case of USB graphics, it is just an allocated (virtual) buffer. - Writes need to be detected and encoded into USB bulk transfers by the CPU. - Accurate damage/changed area notifications work around this problem. - In the future, hopefully fbdev will be enhanced with an small standard - interface to allow mmap clients to report damage, for the benefit - of virtual or remote framebuffers. - * Fbdev does not arbitrate client ownership of the framebuffer well. - * Fbcon assumes the first framebuffer it finds should be consumed for console. - * It's not clear what the future of fbdev is, given the rise of KMS/DRM. - -How to use it? -============== - -Udlfb, when loaded as a module, will match against all USB 2.0 generation -DisplayLink chips (Alex and Ollie family). It will then attempt to read the EDID -of the monitor, and set the best common mode between the DisplayLink device -and the monitor's capabilities. - -If the DisplayLink device is successful, it will paint a "green screen" which -means that from a hardware and fbdev software perspective, everything is good. - -At that point, a /dev/fb? interface will be present for user-mode applications -to open and begin writing to the framebuffer of the DisplayLink device using -standard fbdev calls. Note that if mmap() is used, by default the user mode -application must send down damage notifcations to trigger repaints of the -changed regions. Alternatively, udlfb can be recompiled with experimental -defio support enabled, to support a page-fault based detection mechanism -that can work without explicit notifcation. - -The most common client of udlfb is xf86-video-displaylink or a modified -xf86-video-fbdev X server. These servers have no real DisplayLink specific -code. They write to the standard framebuffer interface and rely on udlfb -to do its thing. The one extra feature they have is the ability to report -rectangles from the X DAMAGE protocol extension down to udlfb via udlfb's -damage interface (which will hopefully be standardized for all virtual -framebuffers that need damage info). These damage notifications allow -udlfb to efficiently process the changed pixels. - -Module Options -============== - -Special configuration for udlfb is usually unnecessary. There are a few -options, however. - -From the command line, pass options to modprobe -modprobe udlfb defio=1 console=1 - -Or for permanent option, create file like /etc/modprobe.d/options with text -options udlfb defio=1 console=1 - -Accepted options: - -fb_defio Make use of the fb_defio (CONFIG_FB_DEFERRED_IO) kernel - module to track changed areas of the framebuffer by page faults. - Standard fbdev applications that use mmap but that do not - report damage, may be able to work with this enabled. - Disabled by default because of overhead and other issues. - -console Allow fbcon to attach to udlfb provided framebuffers. This - is disabled by default because fbcon will aggressively consume - the first framebuffer it finds, which isn't usually what the - user wants in the case of USB displays. - -Sysfs Attributes -================ - -Udlfb creates several files in /sys/class/graphics/fb? -Where ? is the sequential framebuffer id of the particular DisplayLink device - -edid If a valid EDID blob is written to this file (typically - by a udev rule), then udlfb will use this EDID as a - backup in case reading the actual EDID of the monitor - attached to the DisplayLink device fails. This is - especially useful for fixed panels, etc. that cannot - communicate their capabilities via EDID. Reading - this file returns the current EDID of the attached - monitor (or last backup value written). This is - useful to get the EDID of the attached monitor, - which can be passed to utilities like parse-edid. - -metrics_bytes_rendered 32-bit count of pixel bytes rendered - -metrics_bytes_identical 32-bit count of how many of those bytes were found to be - unchanged, based on a shadow framebuffer check - -metrics_bytes_sent 32-bit count of how many bytes were transferred over - USB to communicate the resulting changed pixels to the - hardware. Includes compression and protocol overhead - -metrics_cpu_kcycles_used 32-bit count of CPU cycles used in processing the - above pixels (in thousands of cycles). - -metrics_reset Write-only. Any write to this file resets all metrics - above to zero. Note that the 32-bit counters above - roll over very quickly. To get reliable results, design - performance tests to start and finish in a very short - period of time (one minute or less is safe). - --- -Bernie Thompson diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 27c1fb4b1e0d..37771d0916ef 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2034,6 +2034,20 @@ config FB_SM501 If unsure, say N. +config FB_UDL + tristate "Displaylink USB Framebuffer support" + depends on FB && USB + select FB_MODE_HELPERS + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_SYS_FOPS + select FB_DEFERRED_IO + ---help--- + This is a kernel framebuffer driver for DisplayLink USB devices. + Supports fbdev clients like xf86-video-fbdev, kdrive, fbi, and + mplayer -vo fbdev. Supports all USB 2.0 era DisplayLink devices. + To compile as a module, choose M here: the module name is udlfb. config FB_PNX4008_DUM tristate "Display Update Module support on Philips PNX4008 board" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 485e8ed1318c..03678e3021ab 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -122,6 +122,7 @@ obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/ obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o obj-$(CONFIG_FB_PS3) += ps3fb.o obj-$(CONFIG_FB_SM501) += sm501fb.o +obj-$(CONFIG_FB_UDL) += udlfb.o obj-$(CONFIG_FB_XILINX) += xilinxfb.o obj-$(CONFIG_SH_MIPI_DSI) += sh_mipi_dsi.o obj-$(CONFIG_FB_SH_MOBILE_HDMI) += sh_mobile_hdmi.o diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c new file mode 100644 index 000000000000..0cca4873d490 --- /dev/null +++ b/drivers/video/udlfb.c @@ -0,0 +1,1915 @@ +/* + * udlfb.c -- Framebuffer driver for DisplayLink USB controller + * + * Copyright (C) 2009 Roberto De Ioris + * Copyright (C) 2009 Jaya Kumar + * Copyright (C) 2009 Bernie Thompson + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + * + * Layout is based on skeletonfb by James Simmons and Geert Uytterhoeven, + * usb-skeleton by GregKH. + * + * Device-specific portions based on information from Displaylink, with work + * from Florian Echtler, Henrik Bjerregaard Pedersen, and others. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include