From 35edc2a5095efb189e60dc32bbb9d2663aec6d24 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Sun, 20 Nov 2011 20:36:02 +0100 Subject: perf, arch: Rework perf_event_index() Put the logic to compute the event index into a per pmu method. This is required because the x86 rules are weird and wonderful and don't match the capabilities of the current scheme. AFAIK only powerpc actually has a usable userspace read of the PMCs but I'm not at all sure anybody actually used that. ARM is restored to the default since it currently does not support userspace access at all. And all software events are provided with a method that reports their index as 0 (disabled). Signed-off-by: Peter Zijlstra Cc: Michael Cree Cc: Will Deacon Cc: Deng-Cheng Zhu Cc: Anton Blanchard Cc: Eric B Munson Cc: Heiko Carstens Cc: Paul Mundt Cc: David S. Miller Cc: Richard Kuo Cc: Stephane Eranian Cc: Arun Sharma Link: http://lkml.kernel.org/n/tip-dfydxodki16lylkt3gl2j7cw@git.kernel.org Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 08855613ceb3..02545e6df95b 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -680,6 +680,12 @@ struct pmu { * for each successful ->add() during the transaction. */ void (*cancel_txn) (struct pmu *pmu); /* optional */ + + /* + * Will return the value for perf_event_mmap_page::index for this event, + * if no implementation is provided it will default to: event->hw.idx + 1. + */ + int (*event_idx) (struct perf_event *event); /*optional */ }; /** -- cgit From 0c9d42ed4cee2aa1dfc3a260b741baae8615744f Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Sun, 20 Nov 2011 23:30:47 +0100 Subject: perf, x86: Provide means for disabling userspace RDPMC Allow the disabling of RDPMC via a pmu specific attribute: echo 0 > /sys/bus/event_source/devices/cpu/rdpmc Signed-off-by: Peter Zijlstra Cc: Stephane Eranian Cc: Arun Sharma Link: http://lkml.kernel.org/n/tip-pqeog465zo5hsimtkfz73f27@git.kernel.org Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event.c | 55 +++++++++++++++++++++++++++++++++++++++- arch/x86/kernel/cpu/perf_event.h | 8 ++++++ include/linux/perf_event.h | 1 + kernel/events/core.c | 1 + 4 files changed, 64 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 53b569910175..116b040a73a8 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -1210,7 +1211,8 @@ x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) break; case CPU_STARTING: - set_in_cr4(X86_CR4_PCE); + if (x86_pmu.attr_rdpmc) + set_in_cr4(X86_CR4_PCE); if (x86_pmu.cpu_starting) x86_pmu.cpu_starting(cpu); break; @@ -1320,6 +1322,8 @@ static int __init init_hw_perf_events(void) } } + x86_pmu.attr_rdpmc = 1; /* enable userspace RDPMC usage by default */ + pr_info("... version: %d\n", x86_pmu.version); pr_info("... bit width: %d\n", x86_pmu.cntval_bits); pr_info("... generic registers: %d\n", x86_pmu.num_counters); @@ -1555,10 +1559,59 @@ static int x86_pmu_event_idx(struct perf_event *event) return idx + 1; } +static ssize_t get_attr_rdpmc(struct device *cdev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, 40, "%d\n", x86_pmu.attr_rdpmc); +} + +static void change_rdpmc(void *info) +{ + bool enable = !!(unsigned long)info; + + if (enable) + set_in_cr4(X86_CR4_PCE); + else + clear_in_cr4(X86_CR4_PCE); +} + +static ssize_t set_attr_rdpmc(struct device *cdev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long val = simple_strtoul(buf, NULL, 0); + + if (!!val != !!x86_pmu.attr_rdpmc) { + x86_pmu.attr_rdpmc = !!val; + smp_call_function(change_rdpmc, (void *)val, 1); + } + + return count; +} + +static DEVICE_ATTR(rdpmc, S_IRUSR | S_IWUSR, get_attr_rdpmc, set_attr_rdpmc); + +static struct attribute *x86_pmu_attrs[] = { + &dev_attr_rdpmc.attr, + NULL, +}; + +static struct attribute_group x86_pmu_attr_group = { + .attrs = x86_pmu_attrs, +}; + +static const struct attribute_group *x86_pmu_attr_groups[] = { + &x86_pmu_attr_group, + NULL, +}; + static struct pmu pmu = { .pmu_enable = x86_pmu_enable, .pmu_disable = x86_pmu_disable, + .attr_groups = x86_pmu_attr_groups, + .event_init = x86_pmu_event_init, .add = x86_pmu_add, diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index 8944062f46e2..513d617b93c4 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h @@ -307,6 +307,14 @@ struct x86_pmu { struct x86_pmu_quirk *quirks; int perfctr_second_write; + /* + * sysfs attrs + */ + int attr_rdpmc; + + /* + * CPU Hotplug hooks + */ int (*cpu_prepare)(int cpu); void (*cpu_starting)(int cpu); void (*cpu_dying)(int cpu); diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 02545e6df95b..5311b79fe62c 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -615,6 +615,7 @@ struct pmu { struct list_head entry; struct device *dev; + const struct attribute_group **attr_groups; char *name; int type; diff --git a/kernel/events/core.c b/kernel/events/core.c index 05affc3878ff..dcd4049e92fc 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5505,6 +5505,7 @@ static int pmu_dev_alloc(struct pmu *pmu) if (!pmu->dev) goto out; + pmu->dev->groups = pmu->attr_groups; device_initialize(pmu->dev); ret = dev_set_name(pmu->dev, "%s", pmu->name); if (ret) -- cgit From e3f3541c19c89a4daae39300defba68943301949 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 21 Nov 2011 11:43:53 +0100 Subject: perf: Extend the mmap control page with time (TSC) fields Extend the mmap control page with fields so that userspace can compute time deltas relative to the provided time fields. Currently only implemented for x86 with constant and nonstop TSC. Signed-off-by: Peter Zijlstra Cc: Stephane Eranian Cc: Arun Sharma Link: http://lkml.kernel.org/n/tip-3u1jucza77j3wuvs0x2bic0f@git.kernel.org Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event.c | 14 ++++++++++++++ include/linux/perf_event.h | 4 +++- kernel/events/core.c | 21 ++++++++++++++------- 3 files changed, 31 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 116b040a73a8..f8bddb5b0600 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "perf_event.h" @@ -1627,6 +1628,19 @@ static struct pmu pmu = { .event_idx = x86_pmu_event_idx, }; +void perf_update_user_clock(struct perf_event_mmap_page *userpg, u64 now) +{ + if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC)) + return; + + if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) + return; + + userpg->time_mult = this_cpu_read(cyc2ns); + userpg->time_shift = CYC2NS_SCALE_FACTOR; + userpg->time_offset = this_cpu_read(cyc2ns_offset) - now; +} + /* * callchain support */ diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 5311b79fe62c..0b91db2522cc 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -291,12 +291,14 @@ struct perf_event_mmap_page { __s64 offset; /* add to hardware event value */ __u64 time_enabled; /* time event active */ __u64 time_running; /* time event on cpu */ + __u32 time_mult, time_shift; + __u64 time_offset; /* * Hole for extension of the self monitor capabilities */ - __u64 __reserved[123]; /* align to 1k */ + __u64 __reserved[121]; /* align to 1k */ /* * Control data for the mmap() data buffer. diff --git a/kernel/events/core.c b/kernel/events/core.c index dcd4049e92fc..3a9c7d81afbf 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3220,17 +3220,22 @@ static int perf_event_index(struct perf_event *event) } static void calc_timer_values(struct perf_event *event, + u64 *now, u64 *enabled, u64 *running) { - u64 now, ctx_time; + u64 ctx_time; - now = perf_clock(); - ctx_time = event->shadow_ctx_time + now; + *now = perf_clock(); + ctx_time = event->shadow_ctx_time + *now; *enabled = ctx_time - event->tstamp_enabled; *running = ctx_time - event->tstamp_running; } +void __weak perf_update_user_clock(struct perf_event_mmap_page *userpg, u64 now) +{ +} + /* * Callers need to ensure there can be no nesting of this function, otherwise * the seqlock logic goes bad. We can not serialize this because the arch @@ -3240,7 +3245,7 @@ void perf_event_update_userpage(struct perf_event *event) { struct perf_event_mmap_page *userpg; struct ring_buffer *rb; - u64 enabled, running; + u64 enabled, running, now; rcu_read_lock(); /* @@ -3252,7 +3257,7 @@ void perf_event_update_userpage(struct perf_event *event) * because of locking issue as we can be called in * NMI context */ - calc_timer_values(event, &enabled, &running); + calc_timer_values(event, &now, &enabled, &running); rb = rcu_dereference(event->rb); if (!rb) goto unlock; @@ -3277,6 +3282,8 @@ void perf_event_update_userpage(struct perf_event *event) userpg->time_running = running + atomic64_read(&event->child_total_time_running); + perf_update_user_clock(userpg, now); + barrier(); ++userpg->lock; preempt_enable(); @@ -3763,7 +3770,7 @@ static void perf_output_read_group(struct perf_output_handle *handle, static void perf_output_read(struct perf_output_handle *handle, struct perf_event *event) { - u64 enabled = 0, running = 0; + u64 enabled = 0, running = 0, now; u64 read_format = event->attr.read_format; /* @@ -3776,7 +3783,7 @@ static void perf_output_read(struct perf_output_handle *handle, * NMI context */ if (read_format & PERF_FORMAT_TOTAL_TIMES) - calc_timer_values(event, &enabled, &running); + calc_timer_values(event, &now, &enabled, &running); if (event->attr.read_format & PERF_FORMAT_GROUP) perf_output_read_group(handle, event, enabled, running); -- cgit From 6c303d3ab39f0dc69546f179c424ee1124f50906 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Tue, 22 Nov 2011 21:13:48 +0100 Subject: tracing: let trace_signal_generate() report more info, kill overflow_fail/lose_info __send_signal()->trace_signal_generate() doesn't report enough info. The users want to know was the signal actually delivered or not, and they also need the shared/private info. The patch moves trace_signal_generate() at the end of __send_signal() and adds the 2 additional arguments. This also allows us to kill trace_signal_overflow_fail/lose_info, we can simply add the appropriate TRACE_SIGNAL_ "result" codes. Reported-by: Seiji Aguchi Reviewed-by: Seiji Aguchi Acked-by: Steven Rostedt Signed-off-by: Oleg Nesterov --- include/trace/events/signal.h | 85 +++++++++++-------------------------------- kernel/signal.c | 22 +++++++---- 2 files changed, 36 insertions(+), 71 deletions(-) (limited to 'include') diff --git a/include/trace/events/signal.h b/include/trace/events/signal.h index 17df43464df0..39a8a430d90f 100644 --- a/include/trace/events/signal.h +++ b/include/trace/events/signal.h @@ -23,11 +23,23 @@ } \ } while (0) +#ifndef TRACE_HEADER_MULTI_READ +enum { + TRACE_SIGNAL_DELIVERED, + TRACE_SIGNAL_IGNORED, + TRACE_SIGNAL_ALREADY_PENDING, + TRACE_SIGNAL_OVERFLOW_FAIL, + TRACE_SIGNAL_LOSE_INFO, +}; +#endif + /** * signal_generate - called when a signal is generated * @sig: signal number * @info: pointer to struct siginfo * @task: pointer to struct task_struct + * @group: shared or private + * @result: TRACE_SIGNAL_* * * Current process sends a 'sig' signal to 'task' process with * 'info' siginfo. If 'info' is SEND_SIG_NOINFO or SEND_SIG_PRIV, @@ -37,9 +49,10 @@ */ TRACE_EVENT(signal_generate, - TP_PROTO(int sig, struct siginfo *info, struct task_struct *task), + TP_PROTO(int sig, struct siginfo *info, struct task_struct *task, + int group, int result), - TP_ARGS(sig, info, task), + TP_ARGS(sig, info, task, group, result), TP_STRUCT__entry( __field( int, sig ) @@ -47,6 +60,8 @@ TRACE_EVENT(signal_generate, __field( int, code ) __array( char, comm, TASK_COMM_LEN ) __field( pid_t, pid ) + __field( int, group ) + __field( int, result ) ), TP_fast_assign( @@ -54,11 +69,14 @@ TRACE_EVENT(signal_generate, TP_STORE_SIGINFO(__entry, info); memcpy(__entry->comm, task->comm, TASK_COMM_LEN); __entry->pid = task->pid; + __entry->group = group; + __entry->result = result; ), - TP_printk("sig=%d errno=%d code=%d comm=%s pid=%d", + TP_printk("sig=%d errno=%d code=%d comm=%s pid=%d grp=%d res=%d", __entry->sig, __entry->errno, __entry->code, - __entry->comm, __entry->pid) + __entry->comm, __entry->pid, __entry->group, + __entry->result) ); /** @@ -101,65 +119,6 @@ TRACE_EVENT(signal_deliver, __entry->sa_handler, __entry->sa_flags) ); -DECLARE_EVENT_CLASS(signal_queue_overflow, - - TP_PROTO(int sig, int group, struct siginfo *info), - - TP_ARGS(sig, group, info), - - TP_STRUCT__entry( - __field( int, sig ) - __field( int, group ) - __field( int, errno ) - __field( int, code ) - ), - - TP_fast_assign( - __entry->sig = sig; - __entry->group = group; - TP_STORE_SIGINFO(__entry, info); - ), - - TP_printk("sig=%d group=%d errno=%d code=%d", - __entry->sig, __entry->group, __entry->errno, __entry->code) -); - -/** - * signal_overflow_fail - called when signal queue is overflow - * @sig: signal number - * @group: signal to process group or not (bool) - * @info: pointer to struct siginfo - * - * Kernel fails to generate 'sig' signal with 'info' siginfo, because - * siginfo queue is overflow, and the signal is dropped. - * 'group' is not 0 if the signal will be sent to a process group. - * 'sig' is always one of RT signals. - */ -DEFINE_EVENT(signal_queue_overflow, signal_overflow_fail, - - TP_PROTO(int sig, int group, struct siginfo *info), - - TP_ARGS(sig, group, info) -); - -/** - * signal_lose_info - called when siginfo is lost - * @sig: signal number - * @group: signal to process group or not (bool) - * @info: pointer to struct siginfo - * - * Kernel generates 'sig' signal but loses 'info' siginfo, because siginfo - * queue is overflow. - * 'group' is not 0 if the signal will be sent to a process group. - * 'sig' is always one of non-RT signals. - */ -DEFINE_EVENT(signal_queue_overflow, signal_lose_info, - - TP_PROTO(int sig, int group, struct siginfo *info), - - TP_ARGS(sig, group, info) -); - #endif /* _TRACE_SIGNAL_H */ /* This part must be outside protection */ diff --git a/kernel/signal.c b/kernel/signal.c index c73c4284160e..1bd9e86fda1f 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1054,13 +1054,13 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t, struct sigpending *pending; struct sigqueue *q; int override_rlimit; - - trace_signal_generate(sig, info, t); + int ret = 0, result; assert_spin_locked(&t->sighand->siglock); + result = TRACE_SIGNAL_IGNORED; if (!prepare_signal(sig, t, from_ancestor_ns)) - return 0; + goto ret; pending = group ? &t->signal->shared_pending : &t->pending; /* @@ -1068,8 +1068,11 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t, * exactly one non-rt signal, so that we can get more * detailed information about the cause of the signal. */ + result = TRACE_SIGNAL_ALREADY_PENDING; if (legacy_queue(pending, sig)) - return 0; + goto ret; + + result = TRACE_SIGNAL_DELIVERED; /* * fast-pathed signals for kernel-internal things like SIGSTOP * or SIGKILL. @@ -1127,14 +1130,15 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t, * signal was rt and sent by user using something * other than kill(). */ - trace_signal_overflow_fail(sig, group, info); - return -EAGAIN; + result = TRACE_SIGNAL_OVERFLOW_FAIL; + ret = -EAGAIN; + goto ret; } else { /* * This is a silent loss of information. We still * send the signal, but the *info bits are lost. */ - trace_signal_lose_info(sig, group, info); + result = TRACE_SIGNAL_LOSE_INFO; } } @@ -1142,7 +1146,9 @@ out_set: signalfd_notify(t, sig); sigaddset(&pending->signal, sig); complete_signal(sig, t, group); - return 0; +ret: + trace_signal_generate(sig, info, t, group, result); + return ret; } static int send_signal(int sig, struct siginfo *info, struct task_struct *t, -- cgit From a63b9072ea4e32c1fde8b783ccf6e4288414660b Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Fri, 20 Jan 2012 11:58:09 +0800 Subject: cgroup: remove cgroup_attach_task_current_cg() It's just a wrapper of cgroup_attach_task_all(), and it's no longer used after commit 87d6a412bd1ed82c14cabd4b408003b23bbd2880 (vhost: fix attach to cgroups regression) Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 9 --------- 1 file changed, 9 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index e9b602151caf..dee53bdb046d 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -602,11 +602,6 @@ int cgroup_scan_tasks(struct cgroup_scanner *scan); int cgroup_attach_task(struct cgroup *, struct task_struct *); int cgroup_attach_task_all(struct task_struct *from, struct task_struct *); -static inline int cgroup_attach_task_current_cg(struct task_struct *tsk) -{ - return cgroup_attach_task_all(current, tsk); -} - /* * CSS ID is ID for cgroup_subsys_state structs under subsys. This only works * if cgroup_subsys.use_id == true. It can be used for looking up and scanning. @@ -669,10 +664,6 @@ static inline int cgroup_attach_task_all(struct task_struct *from, { return 0; } -static inline int cgroup_attach_task_current_cg(struct task_struct *t) -{ - return 0; -} #endif /* !CONFIG_CGROUPS */ -- cgit From 245282557c49754af3dbcc732316e814340d6bce Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Fri, 20 Jan 2012 11:58:43 +0800 Subject: cgroup: move struct cgroup_pidlist out from the header file It's internally used only. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 32 -------------------------------- kernel/cgroup.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index dee53bdb046d..7da3e745b74c 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -160,38 +160,6 @@ enum { CGRP_CLONE_CHILDREN, }; -/* which pidlist file are we talking about? */ -enum cgroup_filetype { - CGROUP_FILE_PROCS, - CGROUP_FILE_TASKS, -}; - -/* - * A pidlist is a list of pids that virtually represents the contents of one - * of the cgroup files ("procs" or "tasks"). We keep a list of such pidlists, - * a pair (one each for procs, tasks) for each pid namespace that's relevant - * to the cgroup. - */ -struct cgroup_pidlist { - /* - * used to find which pidlist is wanted. doesn't change as long as - * this particular list stays in the list. - */ - struct { enum cgroup_filetype type; struct pid_namespace *ns; } key; - /* array of xids */ - pid_t *list; - /* how many elements the above list has */ - int length; - /* how many files are using the current array */ - int use_count; - /* each of these stored in a list by its cgroup */ - struct list_head links; - /* pointer to the cgroup we belong to, for list removal purposes */ - struct cgroup *owner; - /* protects the other fields */ - struct rw_semaphore mutex; -}; - struct cgroup { unsigned long flags; /* "unsigned long" so bitops work */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index a5d3b5325f77..6ca7acad7c55 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -3043,6 +3043,38 @@ int cgroup_scan_tasks(struct cgroup_scanner *scan) * */ +/* which pidlist file are we talking about? */ +enum cgroup_filetype { + CGROUP_FILE_PROCS, + CGROUP_FILE_TASKS, +}; + +/* + * A pidlist is a list of pids that virtually represents the contents of one + * of the cgroup files ("procs" or "tasks"). We keep a list of such pidlists, + * a pair (one each for procs, tasks) for each pid namespace that's relevant + * to the cgroup. + */ +struct cgroup_pidlist { + /* + * used to find which pidlist is wanted. doesn't change as long as + * this particular list stays in the list. + */ + struct { enum cgroup_filetype type; struct pid_namespace *ns; } key; + /* array of xids */ + pid_t *list; + /* how many elements the above list has */ + int length; + /* how many files are using the current array */ + int use_count; + /* each of these stored in a list by its cgroup */ + struct list_head links; + /* pointer to the cgroup we belong to, for list removal purposes */ + struct cgroup *owner; + /* protects the other fields */ + struct rw_semaphore mutex; +}; + /* * The following two functions "fix" the issue where there are more pids * than kmalloc will give memory for; in such cases, we use vmalloc/vfree. -- cgit From 5ea4399457f730b4614d0632ceefd3e8f53fb1e6 Mon Sep 17 00:00:00 2001 From: "Shimoda, Yoshihiro" Date: Thu, 5 Jan 2012 15:37:22 +0900 Subject: usb: renesas_usbhs: add support for SUDMAC The SUDMAC uses 8-bit width only. So, when the driver uses SUDMAC, we have to clear the MBW_32. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/fifo.c | 6 +++++- include/linux/usb/renesas_usbhs.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 72339bd6fcab..03a9cc529c82 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -23,6 +23,7 @@ #define usbhsf_get_cfifo(p) (&((p)->fifo_info.cfifo)) #define usbhsf_get_d0fifo(p) (&((p)->fifo_info.d0fifo)) #define usbhsf_get_d1fifo(p) (&((p)->fifo_info.d1fifo)) +#define usbhsf_is_cfifo(p, f) (usbhsf_get_cfifo(p) == f) #define usbhsf_fifo_is_busy(f) ((f)->pipe) /* see usbhs_pipe_select_fifo */ @@ -305,7 +306,10 @@ static int usbhsf_fifo_select(struct usbhs_pipe *pipe, } /* "base" will be used below */ - usbhs_write(priv, fifo->sel, base | MBW_32); + if (usbhs_get_dparam(priv, has_sudmac) && !usbhsf_is_cfifo(priv, fifo)) + usbhs_write(priv, fifo->sel, base); + else + usbhs_write(priv, fifo->sel, base | MBW_32); /* check ISEL and CURPIPE value */ while (timeout--) { diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 0d3f98879256..547e59cc00ea 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -149,6 +149,7 @@ struct renesas_usbhs_driver_param { * option: */ u32 has_otg:1; /* for controlling PWEN/EXTLP */ + u32 has_sudmac:1; /* for SUDMAC */ }; /* -- cgit From b0f70292053a0f68f406564a721a7a3f2d66b44f Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Mon, 2 Jan 2012 08:41:23 +0100 Subject: ssb: SPROM: extract each core power info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already extract some basic info but it's incomplete, reads info about the first core only. Used data structure doesn't allow easy adding of more cores. This patch adds new struct and array for storing power info. The plan is to: switch all extractors (including the ones using NVRAM) to new struct, switch drivers, then deprecate and finally drop old SSB fields. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/ssb/pci.c | 40 +++++++++++++++++++++++++++++++++++++++- include/linux/ssb/ssb.h | 8 ++++++++ include/linux/ssb/ssb_regs.h | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index 973223f5de8e..befa89eac6f3 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -523,7 +523,13 @@ static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in) static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in) { int i; - u16 v; + u16 v, o; + u16 pwr_info_offset[] = { + SSB_SROM8_PWR_INFO_CORE0, SSB_SROM8_PWR_INFO_CORE1, + SSB_SROM8_PWR_INFO_CORE2, SSB_SROM8_PWR_INFO_CORE3 + }; + BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) != + ARRAY_SIZE(out->core_pwr_info)); /* extract the MAC address */ for (i = 0; i < 3; i++) { @@ -607,6 +613,38 @@ static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in) memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24, sizeof(out->antenna_gain.ghz5)); + /* Extract cores power info info */ + for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) { + o = pwr_info_offset[i]; + SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI, + SSB_SPROM8_2G_ITSSI, SSB_SPROM8_2G_ITSSI_SHIFT); + SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SROM8_2G_MAXP_ITSSI, + SSB_SPROM8_2G_MAXP, 0); + + SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SROM8_2G_PA_0, ~0, 0); + SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SROM8_2G_PA_1, ~0, 0); + SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SROM8_2G_PA_2, ~0, 0); + + SPEX(core_pwr_info[i].itssi_5g, o + SSB_SROM8_5G_MAXP_ITSSI, + SSB_SPROM8_5G_ITSSI, SSB_SPROM8_5G_ITSSI_SHIFT); + SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SROM8_5G_MAXP_ITSSI, + SSB_SPROM8_5G_MAXP, 0); + SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM8_5GHL_MAXP, + SSB_SPROM8_5GH_MAXP, 0); + SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM8_5GHL_MAXP, + SSB_SPROM8_5GL_MAXP, SSB_SPROM8_5GL_MAXP_SHIFT); + + SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SROM8_5GL_PA_0, ~0, 0); + SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SROM8_5GL_PA_1, ~0, 0); + SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SROM8_5GL_PA_2, ~0, 0); + SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SROM8_5G_PA_0, ~0, 0); + SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SROM8_5G_PA_1, ~0, 0); + SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SROM8_5G_PA_2, ~0, 0); + SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SROM8_5GH_PA_0, ~0, 0); + SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SROM8_5GH_PA_1, ~0, 0); + SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SROM8_5GH_PA_2, ~0, 0); + } + /* Extract FEM info */ SPEX(fem.ghz2.tssipos, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TSSIPOS, SSB_SROM8_FEM_TSSIPOS_SHIFT); diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index dcf35b0f303a..bbc2612cb64a 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -16,6 +16,12 @@ struct pcmcia_device; struct ssb_bus; struct ssb_driver; +struct ssb_sprom_core_pwr_info { + u8 itssi_2g, itssi_5g; + u8 maxpwr_2g, maxpwr_5gl, maxpwr_5g, maxpwr_5gh; + u16 pa_2g[3], pa_5gl[3], pa_5g[3], pa_5gh[3]; +}; + struct ssb_sprom { u8 revision; u8 il0mac[6]; /* MAC address for 802.11b/g */ @@ -82,6 +88,8 @@ struct ssb_sprom { u16 boardflags2_hi; /* Board flags (bits 48-63) */ /* TODO store board flags in a single u64 */ + struct ssb_sprom_core_pwr_info core_pwr_info[4]; + /* Antenna gain values for up to 4 antennas * on each band. Values in dBm/4 (Q5.2). Negative gain means the * loss in the connectors is bigger than the gain. */ diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h index c814ae6eeb22..40b1ef8595ee 100644 --- a/include/linux/ssb/ssb_regs.h +++ b/include/linux/ssb/ssb_regs.h @@ -449,6 +449,39 @@ #define SSB_SPROM8_TS_SLP_OPT_CORRX 0x00B6 #define SSB_SPROM8_FOC_HWIQ_IQSWP 0x00B8 #define SSB_SPROM8_PHYCAL_TEMPDELTA 0x00BA + +/* There are 4 blocks with power info sharing the same layout */ +#define SSB_SROM8_PWR_INFO_CORE0 0x00C0 +#define SSB_SROM8_PWR_INFO_CORE1 0x00E0 +#define SSB_SROM8_PWR_INFO_CORE2 0x0100 +#define SSB_SROM8_PWR_INFO_CORE3 0x0120 + +#define SSB_SROM8_2G_MAXP_ITSSI 0x00 +#define SSB_SPROM8_2G_MAXP 0x00FF +#define SSB_SPROM8_2G_ITSSI 0xFF00 +#define SSB_SPROM8_2G_ITSSI_SHIFT 8 +#define SSB_SROM8_2G_PA_0 0x02 /* 2GHz power amp settings */ +#define SSB_SROM8_2G_PA_1 0x04 +#define SSB_SROM8_2G_PA_2 0x06 +#define SSB_SROM8_5G_MAXP_ITSSI 0x08 /* 5GHz ITSSI and 5.3GHz Max Power */ +#define SSB_SPROM8_5G_MAXP 0x00FF +#define SSB_SPROM8_5G_ITSSI 0xFF00 +#define SSB_SPROM8_5G_ITSSI_SHIFT 8 +#define SSB_SPROM8_5GHL_MAXP 0x0A /* 5.2GHz and 5.8GHz Max Power */ +#define SSB_SPROM8_5GH_MAXP 0x00FF +#define SSB_SPROM8_5GL_MAXP 0xFF00 +#define SSB_SPROM8_5GL_MAXP_SHIFT 8 +#define SSB_SROM8_5G_PA_0 0x0C /* 5.3GHz power amp settings */ +#define SSB_SROM8_5G_PA_1 0x0E +#define SSB_SROM8_5G_PA_2 0x10 +#define SSB_SROM8_5GL_PA_0 0x12 /* 5.2GHz power amp settings */ +#define SSB_SROM8_5GL_PA_1 0x14 +#define SSB_SROM8_5GL_PA_2 0x16 +#define SSB_SROM8_5GH_PA_0 0x18 /* 5.8GHz power amp settings */ +#define SSB_SROM8_5GH_PA_1 0x1A +#define SSB_SROM8_5GH_PA_2 0x1C + +/* TODO: Make it deprecated */ #define SSB_SPROM8_MAXP_BG 0x00C0 /* Max Power 2GHz in path 1 */ #define SSB_SPROM8_MAXP_BG_MASK 0x00FF /* Mask for Max Power 2GHz */ #define SSB_SPROM8_ITSSI_BG 0xFF00 /* Mask for path 1 itssi_bg */ @@ -473,6 +506,7 @@ #define SSB_SPROM8_PA1HIB0 0x00D8 /* 5.8GHz power amp settings */ #define SSB_SPROM8_PA1HIB1 0x00DA #define SSB_SPROM8_PA1HIB2 0x00DC + #define SSB_SPROM8_CCK2GPO 0x0140 /* CCK power offset */ #define SSB_SPROM8_OFDM2GPO 0x0142 /* 2.4GHz OFDM power offset */ #define SSB_SPROM8_OFDM5GPO 0x0146 /* 5.3GHz OFDM power offset */ -- cgit From 11ee51589a61fa836b1540293ad6c7dfd7de544a Mon Sep 17 00:00:00 2001 From: Ilan Elias Date: Sun, 8 Jan 2012 13:43:02 +0200 Subject: NFC: Increase NCI deactivate timeout Increase NCI deactivate timeout from 5 sec to 30 sec. NCI deactivate procedure might take a long time, depending on the local and remote parameters. Signed-off-by: Ilan Elias Acked-by: Samuel Ortiz Signed-off-by: John W. Linville --- include/net/nfc/nci_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index bccd89e9d4c2..f4963ea77947 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -47,7 +47,7 @@ enum { #define NCI_RESET_TIMEOUT 5000 #define NCI_INIT_TIMEOUT 5000 #define NCI_RF_DISC_TIMEOUT 5000 -#define NCI_RF_DEACTIVATE_TIMEOUT 5000 +#define NCI_RF_DEACTIVATE_TIMEOUT 30000 #define NCI_CMD_TIMEOUT 5000 struct nci_dev; -- cgit From eccc068e8e84c8fe997115629925e0422a98e4de Mon Sep 17 00:00:00 2001 From: Hong Wu Date: Wed, 11 Jan 2012 20:33:39 +0200 Subject: wireless: Save original maximum regulatory transmission power for the calucation of the local maximum transmit power The local maximum transmit power is the maximum power a wireless device allowed to transmit. If Power Constraint is presented, the local maximum power equals to the maximum allowed power defined in regulatory domain minus power constraint. The maximum transmit power is maximum power a wireless device capable of transmitting, and should be used in Power Capability element (7.3.2.16 IEEE802.11 2007). The transmit power from a wireless device should not greater than the local maximum transmit power. The maximum transmit power was not calculated correctly in the current Linux wireless/mac80211 when Power Constraint is presented. Signed-off-by: Hong Wu Signed-off-by: John W. Linville --- include/net/cfg80211.h | 2 ++ net/wireless/reg.c | 19 ++----------------- 2 files changed, 4 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 15f4be7d768e..208a7b5f8d49 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -120,6 +120,7 @@ enum ieee80211_channel_flags { * @band: band this channel belongs to. * @max_antenna_gain: maximum antenna gain in dBi * @max_power: maximum transmission power (in dBm) + * @max_reg_power: maximum regulatory transmission power (in dBm) * @beacon_found: helper to regulatory code to indicate when a beacon * has been found on this channel. Use regulatory_hint_found_beacon() * to enable this, this is useful only on 5 GHz band. @@ -133,6 +134,7 @@ struct ieee80211_channel { u32 flags; int max_antenna_gain; int max_power; + int max_reg_power; bool beacon_found; u32 orig_flags; int orig_mag, orig_mpwr; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index f65feaad155f..e9a0ac83b84c 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -882,23 +882,8 @@ static void handle_channel(struct wiphy *wiphy, chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); chan->max_antenna_gain = min(chan->orig_mag, (int) MBI_TO_DBI(power_rule->max_antenna_gain)); - if (chan->orig_mpwr) { - /* - * Devices that have their own custom regulatory domain - * but also use WIPHY_FLAG_STRICT_REGULATORY will follow the - * passed country IE power settings. - */ - if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && - wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && - wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { - chan->max_power = - MBM_TO_DBM(power_rule->max_eirp); - } else { - chan->max_power = min(chan->orig_mpwr, - (int) MBM_TO_DBM(power_rule->max_eirp)); - } - } else - chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); + chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); + chan->max_power = min(chan->max_power, chan->max_reg_power); } static void handle_band(struct wiphy *wiphy, -- cgit From d5a2ca60e41fec4ede7b82d3608278523cffe77b Mon Sep 17 00:00:00 2001 From: Ilan Elias Date: Tue, 17 Jan 2012 11:06:43 +0200 Subject: NFC: Export new attributes sensb_res and sensf_res Export new attributes sensb_res for tech B and sensf_res for tech F in the target info (returned as a response to NFC_CMD_GET_TARGET). The max size of the attributes nfcid1, sensb_res and sensf_res is exported to user space though include/linux/nfc. Signed-off-by: Ilan Elias Acked-by: Samuel Ortiz Signed-off-by: John W. Linville --- include/linux/nfc.h | 7 ++++ include/net/nfc/nci.h | 19 +++++++++ include/net/nfc/nfc.h | 8 +++- net/nfc/nci/ntf.c | 111 ++++++++++++++++++++++++++++++++++++++++++++------ net/nfc/netlink.c | 6 +++ 5 files changed, 136 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/nfc.h b/include/linux/nfc.h index 01d4e5d60325..b4999abcb2a2 100644 --- a/include/linux/nfc.h +++ b/include/linux/nfc.h @@ -89,6 +89,8 @@ enum nfc_commands { * @NFC_ATTR_TARGET_SEL_RES: NFC-A targets extra information (useful if the * target is not NFC-Forum compliant) * @NFC_ATTR_TARGET_NFCID1: NFC-A targets identifier, max 10 bytes + * @NFC_ATTR_TARGET_SENSB_RES: NFC-B targets extra information, max 12 bytes + * @NFC_ATTR_TARGET_SENSF_RES: NFC-F targets extra information, max 18 bytes * @NFC_ATTR_COMM_MODE: Passive or active mode * @NFC_ATTR_RF_MODE: Initiator or target */ @@ -101,6 +103,8 @@ enum nfc_attrs { NFC_ATTR_TARGET_SENS_RES, NFC_ATTR_TARGET_SEL_RES, NFC_ATTR_TARGET_NFCID1, + NFC_ATTR_TARGET_SENSB_RES, + NFC_ATTR_TARGET_SENSF_RES, NFC_ATTR_COMM_MODE, NFC_ATTR_RF_MODE, /* private: internal use only */ @@ -109,6 +113,9 @@ enum nfc_attrs { #define NFC_ATTR_MAX (__NFC_ATTR_AFTER_LAST - 1) #define NFC_DEVICE_NAME_MAXSIZE 8 +#define NFC_NFCID1_MAXSIZE 10 +#define NFC_SENSB_RES_MAXSIZE 12 +#define NFC_SENSF_RES_MAXSIZE 18 /* NFC protocols */ #define NFC_PROTO_JEWEL 1 diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h index 2be95e2626c0..34f5ed29c3c1 100644 --- a/include/net/nfc/nci.h +++ b/include/net/nfc/nci.h @@ -275,11 +275,27 @@ struct rf_tech_specific_params_nfca_poll { __u8 sel_res; } __packed; +struct rf_tech_specific_params_nfcb_poll { + __u8 sensb_res_len; + __u8 sensb_res[12]; /* 11 or 12 Bytes */ +} __packed; + +struct rf_tech_specific_params_nfcf_poll { + __u8 bit_rate; + __u8 sensf_res_len; + __u8 sensf_res[18]; /* 16 or 18 Bytes */ +} __packed; + struct activation_params_nfca_poll_iso_dep { __u8 rats_res_len; __u8 rats_res[20]; }; +struct activation_params_nfcb_poll_iso_dep { + __u8 attrib_res_len; + __u8 attrib_res[50]; +}; + struct nci_rf_intf_activated_ntf { __u8 rf_discovery_id; __u8 rf_interface; @@ -291,6 +307,8 @@ struct nci_rf_intf_activated_ntf { union { struct rf_tech_specific_params_nfca_poll nfca_poll; + struct rf_tech_specific_params_nfcb_poll nfcb_poll; + struct rf_tech_specific_params_nfcf_poll nfcf_poll; } rf_tech_specific_params; __u8 data_exch_rf_tech_and_mode; @@ -300,6 +318,7 @@ struct nci_rf_intf_activated_ntf { union { struct activation_params_nfca_poll_iso_dep nfca_poll_iso_dep; + struct activation_params_nfcb_poll_iso_dep nfcb_poll_iso_dep; } activation_params; } __packed; diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 8696b773a695..819530d0e37f 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -24,6 +24,7 @@ #ifndef __NET_NFC_H #define __NET_NFC_H +#include #include #include @@ -65,7 +66,6 @@ struct nfc_ops { #define NFC_TARGET_IDX_ANY -1 #define NFC_MAX_GT_LEN 48 -#define NFC_MAX_NFCID1_LEN 10 struct nfc_target { u32 idx; @@ -73,7 +73,11 @@ struct nfc_target { u16 sens_res; u8 sel_res; u8 nfcid1_len; - u8 nfcid1[NFC_MAX_NFCID1_LEN]; + u8 nfcid1[NFC_NFCID1_MAXSIZE]; + u8 sensb_res_len; + u8 sensb_res[NFC_SENSB_RES_MAXSIZE]; + u8 sensf_res_len; + u8 sensf_res[NFC_SENSF_RES_MAXSIZE]; }; struct nfc_genl_data { diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 10682bf7029d..a88be91e973f 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -115,15 +115,53 @@ static __u8 *nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev, return data; } +static __u8 *nci_extract_rf_params_nfcb_passive_poll(struct nci_dev *ndev, + struct nci_rf_intf_activated_ntf *ntf, __u8 *data) +{ + struct rf_tech_specific_params_nfcb_poll *nfcb_poll; + + nfcb_poll = &ntf->rf_tech_specific_params.nfcb_poll; + + nfcb_poll->sensb_res_len = *data++; + + pr_debug("sensb_res_len %d\n", nfcb_poll->sensb_res_len); + + memcpy(nfcb_poll->sensb_res, data, nfcb_poll->sensb_res_len); + data += nfcb_poll->sensb_res_len; + + return data; +} + +static __u8 *nci_extract_rf_params_nfcf_passive_poll(struct nci_dev *ndev, + struct nci_rf_intf_activated_ntf *ntf, __u8 *data) +{ + struct rf_tech_specific_params_nfcf_poll *nfcf_poll; + + nfcf_poll = &ntf->rf_tech_specific_params.nfcf_poll; + + nfcf_poll->bit_rate = *data++; + nfcf_poll->sensf_res_len = *data++; + + pr_debug("bit_rate %d, sensf_res_len %d\n", + nfcf_poll->bit_rate, nfcf_poll->sensf_res_len); + + memcpy(nfcf_poll->sensf_res, data, nfcf_poll->sensf_res_len); + data += nfcf_poll->sensf_res_len; + + return data; +} + static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev, struct nci_rf_intf_activated_ntf *ntf, __u8 *data) { struct activation_params_nfca_poll_iso_dep *nfca_poll; + struct activation_params_nfcb_poll_iso_dep *nfcb_poll; switch (ntf->activation_rf_tech_and_mode) { case NCI_NFC_A_PASSIVE_POLL_MODE: nfca_poll = &ntf->activation_params.nfca_poll_iso_dep; nfca_poll->rats_res_len = *data++; + pr_debug("rats_res_len %d\n", nfca_poll->rats_res_len); if (nfca_poll->rats_res_len > 0) { memcpy(nfca_poll->rats_res, data, @@ -131,6 +169,18 @@ static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev, } break; + case NCI_NFC_B_PASSIVE_POLL_MODE: + nfcb_poll = &ntf->activation_params.nfcb_poll_iso_dep; + nfcb_poll->attrib_res_len = *data++; + pr_debug("attrib_res_len %d\n", + nfcb_poll->attrib_res_len); + if (nfcb_poll->attrib_res_len > 0) { + memcpy(nfcb_poll->attrib_res, + data, + nfcb_poll->attrib_res_len); + } + break; + default: pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", ntf->activation_rf_tech_and_mode); @@ -145,21 +195,14 @@ static void nci_target_found(struct nci_dev *ndev, { struct nfc_target nfc_tgt; - if (ntf->rf_protocol == NCI_RF_PROTOCOL_T2T) /* T2T MifareUL */ + memset(&nfc_tgt, 0, sizeof(nfc_tgt)); + + if (ntf->rf_protocol == NCI_RF_PROTOCOL_T2T) nfc_tgt.supported_protocols = NFC_PROTO_MIFARE_MASK; - else if (ntf->rf_protocol == NCI_RF_PROTOCOL_ISO_DEP) /* 4A */ + else if (ntf->rf_protocol == NCI_RF_PROTOCOL_ISO_DEP) nfc_tgt.supported_protocols = NFC_PROTO_ISO14443_MASK; - else - nfc_tgt.supported_protocols = 0; - - nfc_tgt.sens_res = ntf->rf_tech_specific_params.nfca_poll.sens_res; - nfc_tgt.sel_res = ntf->rf_tech_specific_params.nfca_poll.sel_res; - nfc_tgt.nfcid1_len = ntf->rf_tech_specific_params.nfca_poll.nfcid1_len; - if (nfc_tgt.nfcid1_len > 0) { - memcpy(nfc_tgt.nfcid1, - ntf->rf_tech_specific_params.nfca_poll.nfcid1, - nfc_tgt.nfcid1_len); - } + else if (ntf->rf_protocol == NCI_RF_PROTOCOL_T3T) + nfc_tgt.supported_protocols = NFC_PROTO_FELICA_MASK; if (!(nfc_tgt.supported_protocols & ndev->poll_prots)) { pr_debug("the target found does not have the desired protocol\n"); @@ -169,6 +212,38 @@ static void nci_target_found(struct nci_dev *ndev, pr_debug("new target found, supported_protocols 0x%x\n", nfc_tgt.supported_protocols); + if (ntf->activation_rf_tech_and_mode == NCI_NFC_A_PASSIVE_POLL_MODE) { + nfc_tgt.sens_res = + ntf->rf_tech_specific_params.nfca_poll.sens_res; + nfc_tgt.sel_res = + ntf->rf_tech_specific_params.nfca_poll.sel_res; + nfc_tgt.nfcid1_len = + ntf->rf_tech_specific_params.nfca_poll.nfcid1_len; + if (nfc_tgt.nfcid1_len > 0) { + memcpy(nfc_tgt.nfcid1, + ntf->rf_tech_specific_params.nfca_poll.nfcid1, + nfc_tgt.nfcid1_len); + } + } else if (ntf->activation_rf_tech_and_mode == + NCI_NFC_B_PASSIVE_POLL_MODE) { + nfc_tgt.sensb_res_len = + ntf->rf_tech_specific_params.nfcb_poll.sensb_res_len; + if (nfc_tgt.sensb_res_len > 0) { + memcpy(nfc_tgt.sensb_res, + ntf->rf_tech_specific_params.nfcb_poll.sensb_res, + nfc_tgt.sensb_res_len); + } + } else if (ntf->activation_rf_tech_and_mode == + NCI_NFC_F_PASSIVE_POLL_MODE) { + nfc_tgt.sensf_res_len = + ntf->rf_tech_specific_params.nfcf_poll.sensf_res_len; + if (nfc_tgt.sensf_res_len > 0) { + memcpy(nfc_tgt.sensf_res, + ntf->rf_tech_specific_params.nfcf_poll.sensf_res, + nfc_tgt.sensf_res_len); + } + } + ndev->target_available_prots = nfc_tgt.supported_protocols; ndev->max_data_pkt_payload_size = ntf->max_data_pkt_payload_size; ndev->initial_num_credits = ntf->initial_num_credits; @@ -215,6 +290,16 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, &ntf, data); break; + case NCI_NFC_B_PASSIVE_POLL_MODE: + data = nci_extract_rf_params_nfcb_passive_poll(ndev, + &ntf, data); + break; + + case NCI_NFC_F_PASSIVE_POLL_MODE: + data = nci_extract_rf_params_nfcf_passive_poll(ndev, + &ntf, data); + break; + default: pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", ntf.activation_rf_tech_and_mode); diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 6989dfa28ee2..07f0348aabf5 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -70,6 +70,12 @@ static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target, if (target->nfcid1_len > 0) NLA_PUT(msg, NFC_ATTR_TARGET_NFCID1, target->nfcid1_len, target->nfcid1); + if (target->sensb_res_len > 0) + NLA_PUT(msg, NFC_ATTR_TARGET_SENSB_RES, target->sensb_res_len, + target->sensb_res); + if (target->sensf_res_len > 0) + NLA_PUT(msg, NFC_ATTR_TARGET_SENSF_RES, target->sensf_res_len, + target->sensf_res); return genlmsg_end(msg, hdr); -- cgit From c4bf98b220cba7a8618405261d69ee53a265110e Mon Sep 17 00:00:00 2001 From: Ilan Elias Date: Tue, 17 Jan 2012 12:03:50 +0200 Subject: NFC: Add NCI data exchange timer Add NCI data exchange timer to catch timeouts, and call the data exchange callback with an error. Signed-off-by: Ilan Elias Acked-by: Samuel Ortiz Signed-off-by: John W. Linville --- include/net/nfc/nci_core.h | 3 +++ net/nfc/nci/core.c | 24 ++++++++++++++++++++++++ net/nfc/nci/data.c | 4 ++++ 3 files changed, 31 insertions(+) (limited to 'include') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index f4963ea77947..9154663b606b 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -41,6 +41,7 @@ enum { NCI_DISCOVERY, NCI_POLL_ACTIVE, NCI_DATA_EXCHANGE, + NCI_DATA_EXCHANGE_TO, }; /* NCI timeouts */ @@ -49,6 +50,7 @@ enum { #define NCI_RF_DISC_TIMEOUT 5000 #define NCI_RF_DEACTIVATE_TIMEOUT 30000 #define NCI_CMD_TIMEOUT 5000 +#define NCI_DATA_TIMEOUT 700 struct nci_dev; @@ -74,6 +76,7 @@ struct nci_dev { atomic_t credits_cnt; struct timer_list cmd_timer; + struct timer_list data_timer; struct workqueue_struct *cmd_wq; struct work_struct cmd_work; diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 7650139a1a05..815d28a0ed9d 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -286,6 +286,7 @@ static int nci_close_device(struct nci_dev *ndev) if (!test_and_clear_bit(NCI_UP, &ndev->flags)) { del_timer_sync(&ndev->cmd_timer); + del_timer_sync(&ndev->data_timer); mutex_unlock(&ndev->req_lock); return 0; } @@ -331,6 +332,15 @@ static void nci_cmd_timer(unsigned long arg) queue_work(ndev->cmd_wq, &ndev->cmd_work); } +/* NCI data exchange timer function */ +static void nci_data_timer(unsigned long arg) +{ + struct nci_dev *ndev = (void *) arg; + + set_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); + queue_work(ndev->rx_wq, &ndev->rx_work); +} + static int nci_dev_up(struct nfc_dev *nfc_dev) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); @@ -585,6 +595,8 @@ int nci_register_device(struct nci_dev *ndev) setup_timer(&ndev->cmd_timer, nci_cmd_timer, (unsigned long) ndev); + setup_timer(&ndev->data_timer, nci_data_timer, + (unsigned long) ndev); mutex_init(&ndev->req_lock); @@ -722,6 +734,9 @@ static void nci_tx_work(struct work_struct *work) nci_plen(skb->data)); nci_send_frame(skb); + + mod_timer(&ndev->data_timer, + jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT)); } } @@ -753,6 +768,15 @@ static void nci_rx_work(struct work_struct *work) break; } } + + /* check if a data exchange timout has occurred */ + if (test_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags)) { + /* complete the data exchange transaction, if exists */ + if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) + nci_data_exchange_complete(ndev, NULL, -ETIMEDOUT); + + clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); + } } /* ----- NCI TX CMD worker thread ----- */ diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c index e5756b30e602..7880ae924d5e 100644 --- a/net/nfc/nci/data.c +++ b/net/nfc/nci/data.c @@ -44,6 +44,10 @@ void nci_data_exchange_complete(struct nci_dev *ndev, pr_debug("len %d, err %d\n", skb ? skb->len : 0, err); + /* data exchange is complete, stop the data timer */ + del_timer_sync(&ndev->data_timer); + clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); + if (cb) { ndev->data_exchange_cb = NULL; ndev->data_exchange_cb_context = 0; -- cgit From 8939e47fc953cce6ef53e79e9ff9b53319d1a72d Mon Sep 17 00:00:00 2001 From: Ilan Elias Date: Wed, 18 Jan 2012 13:16:12 +0200 Subject: NFC: Clearly separate NCI states from flags Make a clear separation between NCI states and flags. This is required in order to support more NCI states (e.g. for multiple targets support). Signed-off-by: Ilan Elias Signed-off-by: John W. Linville --- include/net/nfc/nci_core.h | 14 ++++++++++---- net/nfc/nci/core.c | 11 ++++++----- net/nfc/nci/ntf.c | 5 ++--- net/nfc/nci/rsp.c | 8 ++++---- 4 files changed, 22 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 9154663b606b..b9c3f8de13dd 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -34,16 +34,21 @@ #include #include -/* NCI device state */ -enum { +/* NCI device flags */ +enum nci_flag { NCI_INIT, NCI_UP, - NCI_DISCOVERY, - NCI_POLL_ACTIVE, NCI_DATA_EXCHANGE, NCI_DATA_EXCHANGE_TO, }; +/* NCI device states */ +enum nci_state { + NCI_IDLE, + NCI_DISCOVERY, + NCI_POLL_ACTIVE, +}; + /* NCI timeouts */ #define NCI_RESET_TIMEOUT 5000 #define NCI_INIT_TIMEOUT 5000 @@ -70,6 +75,7 @@ struct nci_dev { int tx_headroom; int tx_tailroom; + atomic_t state; unsigned long flags; atomic_t cmd_cnt; diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 815d28a0ed9d..629b76845973 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -264,6 +264,7 @@ static int nci_open_device(struct nci_dev *ndev) if (!rc) { set_bit(NCI_UP, &ndev->flags); + atomic_set(&ndev->state, NCI_IDLE); } else { /* Init failed, cleanup */ skb_queue_purge(&ndev->cmd_q); @@ -360,7 +361,7 @@ static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols) struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); int rc; - if (test_bit(NCI_DISCOVERY, &ndev->flags)) { + if (atomic_read(&ndev->state) == NCI_DISCOVERY) { pr_err("unable to start poll, since poll is already active\n"); return -EBUSY; } @@ -370,7 +371,7 @@ static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols) return -EBUSY; } - if (test_bit(NCI_POLL_ACTIVE, &ndev->flags)) { + if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) { pr_debug("target is active, implicitly deactivate...\n"); rc = nci_request(ndev, nci_rf_deactivate_req, 0, @@ -392,7 +393,7 @@ static void nci_stop_poll(struct nfc_dev *nfc_dev) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); - if (!test_bit(NCI_DISCOVERY, &ndev->flags)) { + if (atomic_read(&ndev->state) != NCI_DISCOVERY) { pr_err("unable to stop poll, since poll is not active\n"); return; } @@ -408,7 +409,7 @@ static int nci_activate_target(struct nfc_dev *nfc_dev, __u32 target_idx, pr_debug("target_idx %d, protocol 0x%x\n", target_idx, protocol); - if (!test_bit(NCI_POLL_ACTIVE, &ndev->flags)) { + if (atomic_read(&ndev->state) != NCI_POLL_ACTIVE) { pr_err("there is no available target to activate\n"); return -EINVAL; } @@ -443,7 +444,7 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev, __u32 target_idx) ndev->target_active_prot = 0; - if (test_bit(NCI_POLL_ACTIVE, &ndev->flags)) { + if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) { nci_request(ndev, nci_rf_deactivate_req, 0, msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); } diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index a88be91e973f..8ec39464cea5 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -261,8 +261,7 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, __u8 *data = skb->data; int err = 0; - clear_bit(NCI_DISCOVERY, &ndev->flags); - set_bit(NCI_POLL_ACTIVE, &ndev->flags); + atomic_set(&ndev->state, NCI_POLL_ACTIVE); ntf.rf_discovery_id = *data++; ntf.rf_interface = *data++; @@ -350,7 +349,7 @@ static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, pr_debug("entry, type 0x%x, reason 0x%x\n", ntf->type, ntf->reason); - clear_bit(NCI_POLL_ACTIVE, &ndev->flags); + atomic_set(&ndev->state, NCI_IDLE); ndev->target_active_prot = 0; /* drop tx data queue */ diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index 3c73e92eb625..cb8bce6899cf 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -137,7 +137,7 @@ static void nci_rf_disc_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) pr_debug("status 0x%x\n", status); if (status == NCI_STATUS_OK) - set_bit(NCI_DISCOVERY, &ndev->flags); + atomic_set(&ndev->state, NCI_DISCOVERY); nci_req_complete(ndev, status); } @@ -149,12 +149,12 @@ static void nci_rf_deactivate_rsp_packet(struct nci_dev *ndev, pr_debug("status 0x%x\n", status); - clear_bit(NCI_DISCOVERY, &ndev->flags); - /* If target was active, complete the request only in deactivate_ntf */ if ((status != NCI_STATUS_OK) || - (!test_bit(NCI_POLL_ACTIVE, &ndev->flags))) + (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) { + atomic_set(&ndev->state, NCI_IDLE); nci_req_complete(ndev, status); + } } void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) -- cgit From 25a1d9dc850b1bdcc4760eb625f0a67057f54d26 Mon Sep 17 00:00:00 2001 From: Ilan Elias Date: Wed, 18 Jan 2012 13:16:13 +0200 Subject: NFC: NFC core layer should not set the target_idx The NFC core layer should not set the target_idx. Instead, the driver layer (e.g. NCI, PN533) should set the target_idx, so that it will be able to identify the target when its I/F (e.g. activate_target) is called. This is required in order to support multiple targets. Note that currently supported drivers (PN533 and NCI) don't use the target_idx in their implementation. Signed-off-by: Ilan Elias Signed-off-by: John W. Linville --- include/net/nfc/nfc.h | 1 - net/nfc/core.c | 5 ----- net/nfc/rawsock.c | 12 ------------ 3 files changed, 18 deletions(-) (limited to 'include') diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 819530d0e37f..d253278e5a96 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -87,7 +87,6 @@ struct nfc_genl_data { struct nfc_dev { unsigned idx; - unsigned target_idx; struct nfc_target *targets; int n_targets; int targets_generation; diff --git a/net/nfc/core.c b/net/nfc/core.c index 3ddf6e698df0..6089aca67b14 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -431,15 +431,10 @@ EXPORT_SYMBOL(nfc_alloc_recv_skb); int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets, int n_targets) { - int i; - pr_debug("dev_name=%s n_targets=%d\n", dev_name(&dev->dev), n_targets); dev->polling = false; - for (i = 0; i < n_targets; i++) - targets[i].idx = dev->target_idx++; - spin_lock_bh(&dev->targets_lock); dev->targets_generation++; diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c index 2e2f8c6a61fe..5325439b0c60 100644 --- a/net/nfc/rawsock.c +++ b/net/nfc/rawsock.c @@ -92,18 +92,6 @@ static int rawsock_connect(struct socket *sock, struct sockaddr *_addr, goto error; } - if (addr->target_idx > dev->target_idx - 1 || - addr->target_idx < dev->target_idx - dev->n_targets) { - rc = -EINVAL; - goto error; - } - - if (addr->target_idx > dev->target_idx - 1 || - addr->target_idx < dev->target_idx - dev->n_targets) { - rc = -EINVAL; - goto error; - } - rc = nfc_activate_target(dev, addr->target_idx, addr->nfc_protocol); if (rc) goto put_dev; -- cgit From 019c4fbaa790e2b3f11dab0c8b7d9896d77db3e5 Mon Sep 17 00:00:00 2001 From: Ilan Elias Date: Wed, 18 Jan 2012 13:16:14 +0200 Subject: NFC: Add NCI multiple targets support Add the ability to select between multiple targets in NCI. If only one target is found, it will be auto-activated. If more than one target is found, then DISCOVER_NTF will be generated for each target, and the host should select one by calling DISCOVER_SELECT_CMD. Then, the target will be activated. If the activation fails, GENERIC_ERROR_NTF is generated. Signed-off-by: Ilan Elias Signed-off-by: John W. Linville --- include/net/nfc/nci.h | 34 ++++- include/net/nfc/nci_core.h | 9 +- net/nfc/nci/core.c | 91 +++++++++++-- net/nfc/nci/ntf.c | 323 +++++++++++++++++++++++++++++++++------------ net/nfc/nci/rsp.c | 17 +++ 5 files changed, 378 insertions(+), 96 deletions(-) (limited to 'include') diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h index 34f5ed29c3c1..276094b91d7c 100644 --- a/include/net/nfc/nci.h +++ b/include/net/nfc/nci.h @@ -116,6 +116,11 @@ #define NCI_DISC_MAP_MODE_POLL 0x01 #define NCI_DISC_MAP_MODE_LISTEN 0x02 +/* NCI Discover Notification Type */ +#define NCI_DISCOVER_NTF_TYPE_LAST 0x00 +#define NCI_DISCOVER_NTF_TYPE_LAST_NFCC 0x01 +#define NCI_DISCOVER_NTF_TYPE_MORE 0x02 + /* NCI Deactivation Type */ #define NCI_DEACTIVATE_TYPE_IDLE_MODE 0x00 #define NCI_DEACTIVATE_TYPE_SLEEP_MODE 0x01 @@ -207,6 +212,13 @@ struct nci_rf_disc_cmd { struct disc_config disc_configs[NCI_MAX_NUM_RF_CONFIGS]; } __packed; +#define NCI_OP_RF_DISCOVER_SELECT_CMD nci_opcode_pack(NCI_GID_RF_MGMT, 0x04) +struct nci_rf_discover_select_cmd { + __u8 rf_discovery_id; + __u8 rf_protocol; + __u8 rf_interface; +} __packed; + #define NCI_OP_RF_DEACTIVATE_CMD nci_opcode_pack(NCI_GID_RF_MGMT, 0x06) struct nci_rf_deactivate_cmd { __u8 type; @@ -244,6 +256,8 @@ struct nci_core_init_rsp_2 { #define NCI_OP_RF_DISCOVER_RSP nci_opcode_pack(NCI_GID_RF_MGMT, 0x03) +#define NCI_OP_RF_DISCOVER_SELECT_RSP nci_opcode_pack(NCI_GID_RF_MGMT, 0x04) + #define NCI_OP_RF_DEACTIVATE_RSP nci_opcode_pack(NCI_GID_RF_MGMT, 0x06) /* --------------------------- */ @@ -260,13 +274,15 @@ struct nci_core_conn_credit_ntf { struct conn_credit_entry conn_entries[NCI_MAX_NUM_CONN]; } __packed; +#define NCI_OP_CORE_GENERIC_ERROR_NTF nci_opcode_pack(NCI_GID_CORE, 0x07) + #define NCI_OP_CORE_INTF_ERROR_NTF nci_opcode_pack(NCI_GID_CORE, 0x08) struct nci_core_intf_error_ntf { __u8 status; __u8 conn_id; } __packed; -#define NCI_OP_RF_INTF_ACTIVATED_NTF nci_opcode_pack(NCI_GID_RF_MGMT, 0x05) +#define NCI_OP_RF_DISCOVER_NTF nci_opcode_pack(NCI_GID_RF_MGMT, 0x03) struct rf_tech_specific_params_nfca_poll { __u16 sens_res; __u8 nfcid1_len; /* 0, 4, 7, or 10 Bytes */ @@ -286,6 +302,22 @@ struct rf_tech_specific_params_nfcf_poll { __u8 sensf_res[18]; /* 16 or 18 Bytes */ } __packed; +struct nci_rf_discover_ntf { + __u8 rf_discovery_id; + __u8 rf_protocol; + __u8 rf_tech_and_mode; + __u8 rf_tech_specific_params_len; + + union { + struct rf_tech_specific_params_nfca_poll nfca_poll; + struct rf_tech_specific_params_nfcb_poll nfcb_poll; + struct rf_tech_specific_params_nfcf_poll nfcf_poll; + } rf_tech_specific_params; + + __u8 ntf_type; +} __packed; + +#define NCI_OP_RF_INTF_ACTIVATED_NTF nci_opcode_pack(NCI_GID_RF_MGMT, 0x05) struct activation_params_nfca_poll_iso_dep { __u8 rats_res_len; __u8 rats_res[20]; diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index b9c3f8de13dd..86fee8b5c65c 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -46,6 +46,8 @@ enum nci_flag { enum nci_state { NCI_IDLE, NCI_DISCOVERY, + NCI_W4_ALL_DISCOVERIES, + NCI_W4_HOST_SELECT, NCI_POLL_ACTIVE, }; @@ -53,6 +55,7 @@ enum nci_state { #define NCI_RESET_TIMEOUT 5000 #define NCI_INIT_TIMEOUT 5000 #define NCI_RF_DISC_TIMEOUT 5000 +#define NCI_RF_DISC_SELECT_TIMEOUT 5000 #define NCI_RF_DEACTIVATE_TIMEOUT 30000 #define NCI_CMD_TIMEOUT 5000 #define NCI_DATA_TIMEOUT 700 @@ -66,6 +69,7 @@ struct nci_ops { }; #define NCI_MAX_SUPPORTED_RF_INTERFACES 4 +#define NCI_MAX_DISCOVERED_TARGETS 10 /* NCI Core structures */ struct nci_dev { @@ -105,9 +109,11 @@ struct nci_dev { void *driver_data; __u32 poll_prots; - __u32 target_available_prots; __u32 target_active_prot; + struct nfc_target targets[NCI_MAX_DISCOVERED_TARGETS]; + int n_targets; + /* received during NCI_OP_CORE_RESET_RSP */ __u8 nci_ver; @@ -178,6 +184,7 @@ int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload); int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb); void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb, int err); +void nci_clear_target_list(struct nci_dev *ndev); /* ----- NCI requests ----- */ #define NCI_REQ_DONE 0 diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 629b76845973..12d1d4d62672 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -216,6 +216,39 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) &cmd); } +struct nci_rf_discover_select_param { + __u8 rf_discovery_id; + __u8 rf_protocol; +}; + +static void nci_rf_discover_select_req(struct nci_dev *ndev, unsigned long opt) +{ + struct nci_rf_discover_select_param *param = + (struct nci_rf_discover_select_param *)opt; + struct nci_rf_discover_select_cmd cmd; + + cmd.rf_discovery_id = param->rf_discovery_id; + cmd.rf_protocol = param->rf_protocol; + + switch (cmd.rf_protocol) { + case NCI_RF_PROTOCOL_ISO_DEP: + cmd.rf_interface = NCI_RF_INTERFACE_ISO_DEP; + break; + + case NCI_RF_PROTOCOL_NFC_DEP: + cmd.rf_interface = NCI_RF_INTERFACE_NFC_DEP; + break; + + default: + cmd.rf_interface = NCI_RF_INTERFACE_FRAME; + break; + } + + nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_SELECT_CMD, + sizeof(struct nci_rf_discover_select_cmd), + &cmd); +} + static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt) { struct nci_rf_deactivate_cmd cmd; @@ -264,6 +297,7 @@ static int nci_open_device(struct nci_dev *ndev) if (!rc) { set_bit(NCI_UP, &ndev->flags); + nci_clear_target_list(ndev); atomic_set(&ndev->state, NCI_IDLE); } else { /* Init failed, cleanup */ @@ -361,7 +395,8 @@ static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols) struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); int rc; - if (atomic_read(&ndev->state) == NCI_DISCOVERY) { + if ((atomic_read(&ndev->state) == NCI_DISCOVERY) || + (atomic_read(&ndev->state) == NCI_W4_ALL_DISCOVERIES)) { pr_err("unable to start poll, since poll is already active\n"); return -EBUSY; } @@ -371,8 +406,9 @@ static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols) return -EBUSY; } - if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) { - pr_debug("target is active, implicitly deactivate...\n"); + if ((atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) || + (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) { + pr_debug("target active or w4 select, implicitly deactivate\n"); rc = nci_request(ndev, nci_rf_deactivate_req, 0, msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); @@ -393,7 +429,8 @@ static void nci_stop_poll(struct nfc_dev *nfc_dev) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); - if (atomic_read(&ndev->state) != NCI_DISCOVERY) { + if ((atomic_read(&ndev->state) != NCI_DISCOVERY) && + (atomic_read(&ndev->state) != NCI_W4_ALL_DISCOVERIES)) { pr_err("unable to stop poll, since poll is not active\n"); return; } @@ -406,10 +443,15 @@ static int nci_activate_target(struct nfc_dev *nfc_dev, __u32 target_idx, __u32 protocol) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + struct nci_rf_discover_select_param param; + struct nfc_target *target = 0; + int i; + int rc = 0; pr_debug("target_idx %d, protocol 0x%x\n", target_idx, protocol); - if (atomic_read(&ndev->state) != NCI_POLL_ACTIVE) { + if ((atomic_read(&ndev->state) != NCI_W4_HOST_SELECT) && + (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) { pr_err("there is no available target to activate\n"); return -EINVAL; } @@ -419,16 +461,47 @@ static int nci_activate_target(struct nfc_dev *nfc_dev, __u32 target_idx, return -EBUSY; } - if (!(ndev->target_available_prots & (1 << protocol))) { + for (i = 0; i < ndev->n_targets; i++) { + if (ndev->targets[i].idx == target_idx) { + target = &ndev->targets[i]; + break; + } + } + + if (!target) { + pr_err("unable to find the selected target\n"); + return -EINVAL; + } + + if (!(target->supported_protocols & (1 << protocol))) { pr_err("target does not support the requested protocol 0x%x\n", protocol); return -EINVAL; } - ndev->target_active_prot = protocol; - ndev->target_available_prots = 0; + if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) { + param.rf_discovery_id = target->idx; - return 0; + if (protocol == NFC_PROTO_JEWEL) + param.rf_protocol = NCI_RF_PROTOCOL_T1T; + else if (protocol == NFC_PROTO_MIFARE) + param.rf_protocol = NCI_RF_PROTOCOL_T2T; + else if (protocol == NFC_PROTO_FELICA) + param.rf_protocol = NCI_RF_PROTOCOL_T3T; + else if (protocol == NFC_PROTO_ISO14443) + param.rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; + else + param.rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; + + rc = nci_request(ndev, nci_rf_discover_select_req, + (unsigned long)¶m, + msecs_to_jiffies(NCI_RF_DISC_SELECT_TIMEOUT)); + } + + if (!rc) + ndev->target_active_prot = protocol; + + return rc; } static void nci_deactivate_target(struct nfc_dev *nfc_dev, __u32 target_idx) diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 8ec39464cea5..03e7b4626a3e 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -71,6 +71,20 @@ static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev, queue_work(ndev->tx_wq, &ndev->tx_work); } +static void nci_core_generic_error_ntf_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + __u8 status = skb->data[0]; + + pr_debug("status 0x%x\n", status); + + if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) { + /* Activation failed, so complete the request + (the state remains the same) */ + nci_req_complete(ndev, status); + } +} + static void nci_core_conn_intf_error_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) { @@ -86,12 +100,9 @@ static void nci_core_conn_intf_error_ntf_packet(struct nci_dev *ndev, } static __u8 *nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev, - struct nci_rf_intf_activated_ntf *ntf, __u8 *data) + struct rf_tech_specific_params_nfca_poll *nfca_poll, + __u8 *data) { - struct rf_tech_specific_params_nfca_poll *nfca_poll; - - nfca_poll = &ntf->rf_tech_specific_params.nfca_poll; - nfca_poll->sens_res = __le16_to_cpu(*((__u16 *)data)); data += 2; @@ -116,12 +127,9 @@ static __u8 *nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev, } static __u8 *nci_extract_rf_params_nfcb_passive_poll(struct nci_dev *ndev, - struct nci_rf_intf_activated_ntf *ntf, __u8 *data) + struct rf_tech_specific_params_nfcb_poll *nfcb_poll, + __u8 *data) { - struct rf_tech_specific_params_nfcb_poll *nfcb_poll; - - nfcb_poll = &ntf->rf_tech_specific_params.nfcb_poll; - nfcb_poll->sensb_res_len = *data++; pr_debug("sensb_res_len %d\n", nfcb_poll->sensb_res_len); @@ -133,12 +141,9 @@ static __u8 *nci_extract_rf_params_nfcb_passive_poll(struct nci_dev *ndev, } static __u8 *nci_extract_rf_params_nfcf_passive_poll(struct nci_dev *ndev, - struct nci_rf_intf_activated_ntf *ntf, __u8 *data) + struct rf_tech_specific_params_nfcf_poll *nfcf_poll, + __u8 *data) { - struct rf_tech_specific_params_nfcf_poll *nfcf_poll; - - nfcf_poll = &ntf->rf_tech_specific_params.nfcf_poll; - nfcf_poll->bit_rate = *data++; nfcf_poll->sensf_res_len = *data++; @@ -151,6 +156,172 @@ static __u8 *nci_extract_rf_params_nfcf_passive_poll(struct nci_dev *ndev, return data; } +static int nci_add_new_protocol(struct nci_dev *ndev, + struct nfc_target *target, + __u8 rf_protocol, + __u8 rf_tech_and_mode, + void *params) +{ + struct rf_tech_specific_params_nfca_poll *nfca_poll; + struct rf_tech_specific_params_nfcb_poll *nfcb_poll; + struct rf_tech_specific_params_nfcf_poll *nfcf_poll; + __u32 protocol; + + if (rf_protocol == NCI_RF_PROTOCOL_T2T) + protocol = NFC_PROTO_MIFARE_MASK; + else if (rf_protocol == NCI_RF_PROTOCOL_ISO_DEP) + protocol = NFC_PROTO_ISO14443_MASK; + else if (rf_protocol == NCI_RF_PROTOCOL_T3T) + protocol = NFC_PROTO_FELICA_MASK; + else + protocol = 0; + + if (!(protocol & ndev->poll_prots)) { + pr_err("the target found does not have the desired protocol\n"); + return -EPROTO; + } + + if (rf_tech_and_mode == NCI_NFC_A_PASSIVE_POLL_MODE) { + nfca_poll = (struct rf_tech_specific_params_nfca_poll *)params; + + target->sens_res = nfca_poll->sens_res; + target->sel_res = nfca_poll->sel_res; + target->nfcid1_len = nfca_poll->nfcid1_len; + if (target->nfcid1_len > 0) { + memcpy(target->nfcid1, nfca_poll->nfcid1, + target->nfcid1_len); + } + } else if (rf_tech_and_mode == NCI_NFC_B_PASSIVE_POLL_MODE) { + nfcb_poll = (struct rf_tech_specific_params_nfcb_poll *)params; + + target->sensb_res_len = nfcb_poll->sensb_res_len; + if (target->sensb_res_len > 0) { + memcpy(target->sensb_res, nfcb_poll->sensb_res, + target->sensb_res_len); + } + } else if (rf_tech_and_mode == NCI_NFC_F_PASSIVE_POLL_MODE) { + nfcf_poll = (struct rf_tech_specific_params_nfcf_poll *)params; + + target->sensf_res_len = nfcf_poll->sensf_res_len; + if (target->sensf_res_len > 0) { + memcpy(target->sensf_res, nfcf_poll->sensf_res, + target->sensf_res_len); + } + } else { + pr_err("unsupported rf_tech_and_mode 0x%x\n", rf_tech_and_mode); + return -EPROTO; + } + + target->supported_protocols |= protocol; + + pr_debug("protocol 0x%x\n", protocol); + + return 0; +} + +static void nci_add_new_target(struct nci_dev *ndev, + struct nci_rf_discover_ntf *ntf) +{ + struct nfc_target *target; + int i, rc; + + for (i = 0; i < ndev->n_targets; i++) { + target = &ndev->targets[i]; + if (target->idx == ntf->rf_discovery_id) { + /* This target already exists, add the new protocol */ + nci_add_new_protocol(ndev, target, ntf->rf_protocol, + ntf->rf_tech_and_mode, + &ntf->rf_tech_specific_params); + return; + } + } + + /* This is a new target, check if we've enough room */ + if (ndev->n_targets == NCI_MAX_DISCOVERED_TARGETS) { + pr_debug("not enough room, ignoring new target...\n"); + return; + } + + target = &ndev->targets[ndev->n_targets]; + + rc = nci_add_new_protocol(ndev, target, ntf->rf_protocol, + ntf->rf_tech_and_mode, + &ntf->rf_tech_specific_params); + if (!rc) { + target->idx = ntf->rf_discovery_id; + ndev->n_targets++; + + pr_debug("target_idx %d, n_targets %d\n", target->idx, + ndev->n_targets); + } +} + +void nci_clear_target_list(struct nci_dev *ndev) +{ + memset(ndev->targets, 0, + (sizeof(struct nfc_target)*NCI_MAX_DISCOVERED_TARGETS)); + + ndev->n_targets = 0; +} + +static void nci_rf_discover_ntf_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + struct nci_rf_discover_ntf ntf; + __u8 *data = skb->data; + bool add_target = true; + + ntf.rf_discovery_id = *data++; + ntf.rf_protocol = *data++; + ntf.rf_tech_and_mode = *data++; + ntf.rf_tech_specific_params_len = *data++; + + pr_debug("rf_discovery_id %d\n", ntf.rf_discovery_id); + pr_debug("rf_protocol 0x%x\n", ntf.rf_protocol); + pr_debug("rf_tech_and_mode 0x%x\n", ntf.rf_tech_and_mode); + pr_debug("rf_tech_specific_params_len %d\n", + ntf.rf_tech_specific_params_len); + + if (ntf.rf_tech_specific_params_len > 0) { + switch (ntf.rf_tech_and_mode) { + case NCI_NFC_A_PASSIVE_POLL_MODE: + data = nci_extract_rf_params_nfca_passive_poll(ndev, + &(ntf.rf_tech_specific_params.nfca_poll), data); + break; + + case NCI_NFC_B_PASSIVE_POLL_MODE: + data = nci_extract_rf_params_nfcb_passive_poll(ndev, + &(ntf.rf_tech_specific_params.nfcb_poll), data); + break; + + case NCI_NFC_F_PASSIVE_POLL_MODE: + data = nci_extract_rf_params_nfcf_passive_poll(ndev, + &(ntf.rf_tech_specific_params.nfcf_poll), data); + break; + + default: + pr_err("unsupported rf_tech_and_mode 0x%x\n", + ntf.rf_tech_and_mode); + data += ntf.rf_tech_specific_params_len; + add_target = false; + } + } + + ntf.ntf_type = *data++; + pr_debug("ntf_type %d\n", ntf.ntf_type); + + if (add_target == true) + nci_add_new_target(ndev, &ntf); + + if (ntf.ntf_type == NCI_DISCOVER_NTF_TYPE_MORE) { + atomic_set(&ndev->state, NCI_W4_ALL_DISCOVERIES); + } else { + atomic_set(&ndev->state, NCI_W4_HOST_SELECT); + nfc_targets_found(ndev->nfc_dev, ndev->targets, + ndev->n_targets); + } +} + static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev, struct nci_rf_intf_activated_ntf *ntf, __u8 *data) { @@ -184,74 +355,32 @@ static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev, default: pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", ntf->activation_rf_tech_and_mode); - return -EPROTO; + return NCI_STATUS_RF_PROTOCOL_ERROR; } - return 0; + return NCI_STATUS_OK; } -static void nci_target_found(struct nci_dev *ndev, - struct nci_rf_intf_activated_ntf *ntf) +static void nci_target_auto_activated(struct nci_dev *ndev, + struct nci_rf_intf_activated_ntf *ntf) { - struct nfc_target nfc_tgt; + struct nfc_target *target; + int rc; - memset(&nfc_tgt, 0, sizeof(nfc_tgt)); + target = &ndev->targets[ndev->n_targets]; - if (ntf->rf_protocol == NCI_RF_PROTOCOL_T2T) - nfc_tgt.supported_protocols = NFC_PROTO_MIFARE_MASK; - else if (ntf->rf_protocol == NCI_RF_PROTOCOL_ISO_DEP) - nfc_tgt.supported_protocols = NFC_PROTO_ISO14443_MASK; - else if (ntf->rf_protocol == NCI_RF_PROTOCOL_T3T) - nfc_tgt.supported_protocols = NFC_PROTO_FELICA_MASK; - - if (!(nfc_tgt.supported_protocols & ndev->poll_prots)) { - pr_debug("the target found does not have the desired protocol\n"); + rc = nci_add_new_protocol(ndev, target, ntf->rf_protocol, + ntf->activation_rf_tech_and_mode, + &ntf->rf_tech_specific_params); + if (rc) return; - } - pr_debug("new target found, supported_protocols 0x%x\n", - nfc_tgt.supported_protocols); - - if (ntf->activation_rf_tech_and_mode == NCI_NFC_A_PASSIVE_POLL_MODE) { - nfc_tgt.sens_res = - ntf->rf_tech_specific_params.nfca_poll.sens_res; - nfc_tgt.sel_res = - ntf->rf_tech_specific_params.nfca_poll.sel_res; - nfc_tgt.nfcid1_len = - ntf->rf_tech_specific_params.nfca_poll.nfcid1_len; - if (nfc_tgt.nfcid1_len > 0) { - memcpy(nfc_tgt.nfcid1, - ntf->rf_tech_specific_params.nfca_poll.nfcid1, - nfc_tgt.nfcid1_len); - } - } else if (ntf->activation_rf_tech_and_mode == - NCI_NFC_B_PASSIVE_POLL_MODE) { - nfc_tgt.sensb_res_len = - ntf->rf_tech_specific_params.nfcb_poll.sensb_res_len; - if (nfc_tgt.sensb_res_len > 0) { - memcpy(nfc_tgt.sensb_res, - ntf->rf_tech_specific_params.nfcb_poll.sensb_res, - nfc_tgt.sensb_res_len); - } - } else if (ntf->activation_rf_tech_and_mode == - NCI_NFC_F_PASSIVE_POLL_MODE) { - nfc_tgt.sensf_res_len = - ntf->rf_tech_specific_params.nfcf_poll.sensf_res_len; - if (nfc_tgt.sensf_res_len > 0) { - memcpy(nfc_tgt.sensf_res, - ntf->rf_tech_specific_params.nfcf_poll.sensf_res, - nfc_tgt.sensf_res_len); - } - } + target->idx = ntf->rf_discovery_id; + ndev->n_targets++; - ndev->target_available_prots = nfc_tgt.supported_protocols; - ndev->max_data_pkt_payload_size = ntf->max_data_pkt_payload_size; - ndev->initial_num_credits = ntf->initial_num_credits; + pr_debug("target_idx %d, n_targets %d\n", target->idx, ndev->n_targets); - /* set the available credits to initial value */ - atomic_set(&ndev->credits_cnt, ndev->initial_num_credits); - - nfc_targets_found(ndev->nfc_dev, &nfc_tgt, 1); + nfc_targets_found(ndev->nfc_dev, ndev->targets, ndev->n_targets); } static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, @@ -259,9 +388,7 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, { struct nci_rf_intf_activated_ntf ntf; __u8 *data = skb->data; - int err = 0; - - atomic_set(&ndev->state, NCI_POLL_ACTIVE); + int err = NCI_STATUS_OK; ntf.rf_discovery_id = *data++; ntf.rf_interface = *data++; @@ -286,23 +413,24 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, switch (ntf.activation_rf_tech_and_mode) { case NCI_NFC_A_PASSIVE_POLL_MODE: data = nci_extract_rf_params_nfca_passive_poll(ndev, - &ntf, data); + &(ntf.rf_tech_specific_params.nfca_poll), data); break; case NCI_NFC_B_PASSIVE_POLL_MODE: data = nci_extract_rf_params_nfcb_passive_poll(ndev, - &ntf, data); + &(ntf.rf_tech_specific_params.nfcb_poll), data); break; case NCI_NFC_F_PASSIVE_POLL_MODE: data = nci_extract_rf_params_nfcf_passive_poll(ndev, - &ntf, data); + &(ntf.rf_tech_specific_params.nfcf_poll), data); break; default: pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", ntf.activation_rf_tech_and_mode); - return; + err = NCI_STATUS_RF_PROTOCOL_ERROR; + goto exit; } } @@ -334,12 +462,30 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, default: pr_err("unsupported rf_interface 0x%x\n", ntf.rf_interface); - return; + err = NCI_STATUS_RF_PROTOCOL_ERROR; + break; } } - if (!err) - nci_target_found(ndev, &ntf); +exit: + if (err == NCI_STATUS_OK) { + ndev->max_data_pkt_payload_size = ntf.max_data_pkt_payload_size; + ndev->initial_num_credits = ntf.initial_num_credits; + + /* set the available credits to initial value */ + atomic_set(&ndev->credits_cnt, ndev->initial_num_credits); + } + + if (atomic_read(&ndev->state) == NCI_DISCOVERY) { + /* A single target was found and activated automatically */ + atomic_set(&ndev->state, NCI_POLL_ACTIVE); + if (err == NCI_STATUS_OK) + nci_target_auto_activated(ndev, &ntf); + } else { /* ndev->state == NCI_W4_HOST_SELECT */ + /* A selected target was activated, so complete the request */ + atomic_set(&ndev->state, NCI_POLL_ACTIVE); + nci_req_complete(ndev, err); + } } static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, @@ -349,9 +495,6 @@ static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, pr_debug("entry, type 0x%x, reason 0x%x\n", ntf->type, ntf->reason); - atomic_set(&ndev->state, NCI_IDLE); - ndev->target_active_prot = 0; - /* drop tx data queue */ skb_queue_purge(&ndev->tx_q); @@ -365,6 +508,8 @@ static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) nci_data_exchange_complete(ndev, NULL, -EIO); + nci_clear_target_list(ndev); + atomic_set(&ndev->state, NCI_IDLE); nci_req_complete(ndev, NCI_STATUS_OK); } @@ -386,10 +531,18 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) nci_core_conn_credits_ntf_packet(ndev, skb); break; + case NCI_OP_CORE_GENERIC_ERROR_NTF: + nci_core_generic_error_ntf_packet(ndev, skb); + break; + case NCI_OP_CORE_INTF_ERROR_NTF: nci_core_conn_intf_error_ntf_packet(ndev, skb); break; + case NCI_OP_RF_DISCOVER_NTF: + nci_rf_discover_ntf_packet(ndev, skb); + break; + case NCI_OP_RF_INTF_ACTIVATED_NTF: nci_rf_intf_activated_ntf_packet(ndev, skb); break; diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index cb8bce6899cf..aa63b1e99188 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -142,6 +142,18 @@ static void nci_rf_disc_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) nci_req_complete(ndev, status); } +static void nci_rf_disc_select_rsp_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + __u8 status = skb->data[0]; + + pr_debug("status 0x%x\n", status); + + /* Complete the request on intf_activated_ntf or generic_error_ntf */ + if (status != NCI_STATUS_OK) + nci_req_complete(ndev, status); +} + static void nci_rf_deactivate_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) { @@ -152,6 +164,7 @@ static void nci_rf_deactivate_rsp_packet(struct nci_dev *ndev, /* If target was active, complete the request only in deactivate_ntf */ if ((status != NCI_STATUS_OK) || (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) { + nci_clear_target_list(ndev); atomic_set(&ndev->state, NCI_IDLE); nci_req_complete(ndev, status); } @@ -190,6 +203,10 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) nci_rf_disc_rsp_packet(ndev, skb); break; + case NCI_OP_RF_DISCOVER_SELECT_RSP: + nci_rf_disc_select_rsp_packet(ndev, skb); + break; + case NCI_OP_RF_DEACTIVATE_RSP: nci_rf_deactivate_rsp_packet(ndev, skb); break; -- cgit From 87e6f968339bcdda56b39572c7e63331192296a0 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:11:13 -0500 Subject: dynamic_debug: drop enabled field from struct _ddebug, use _DPRINTK_FLAGS_PRINT Currently any enabled dynamic-debug flag on a pr_debug callsite will enable printing, even if _DPRINTK_FLAGS_PRINT is off. Checking print flag directly allows "-p" to disable callsites without fussing with other flags, so the following disables everything, without altering flags user may have set: echo -p > $DBGFS/dynamic_debug/control Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- include/linux/dynamic_debug.h | 10 ++++------ lib/dynamic_debug.c | 4 ---- 2 files changed, 4 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index 0564e3c39882..f71a6b046245 100644 --- a/include/linux/dynamic_debug.h +++ b/include/linux/dynamic_debug.h @@ -28,7 +28,6 @@ struct _ddebug { #define _DPRINTK_FLAGS_INCL_TID (1<<4) #define _DPRINTK_FLAGS_DEFAULT 0 unsigned int flags:8; - char enabled; } __attribute__((aligned(8))); @@ -62,21 +61,20 @@ int __dynamic_netdev_dbg(struct _ddebug *descriptor, .format = (fmt), \ .lineno = __LINE__, \ .flags = _DPRINTK_FLAGS_DEFAULT, \ - .enabled = false, \ } #define dynamic_pr_debug(fmt, ...) \ do { \ DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \ - if (unlikely(descriptor.enabled)) \ + if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \ __dynamic_pr_debug(&descriptor, pr_fmt(fmt), \ ##__VA_ARGS__); \ } while (0) #define dynamic_dev_dbg(dev, fmt, ...) \ do { \ - DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \ - if (unlikely(descriptor.enabled)) \ + DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \ + if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \ __dynamic_dev_dbg(&descriptor, dev, fmt, \ ##__VA_ARGS__); \ } while (0) @@ -84,7 +82,7 @@ do { \ #define dynamic_netdev_dbg(dev, fmt, ...) \ do { \ DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \ - if (unlikely(descriptor.enabled)) \ + if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \ __dynamic_netdev_dbg(&descriptor, dev, fmt, \ ##__VA_ARGS__); \ } while (0) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index e487d1379298..416c0794ddd3 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -151,10 +151,6 @@ static void ddebug_change(const struct ddebug_query *query, if (newflags == dp->flags) continue; dp->flags = newflags; - if (newflags) - dp->enabled = 1; - else - dp->enabled = 0; if (verbose) pr_info("changed %s:%d [%s]%s %s\n", dp->filename, dp->lineno, -- cgit From b558c96ffa53f4b3dd52b774e4fb7a52982ab52b Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:11:18 -0500 Subject: dynamic_debug: make dynamic-debug supersede DEBUG ccflag If CONFIG_DYNAMIC_DEBUG is defined, honor it over DEBUG, so that pr_debug()s are controllable, instead of always-on. When DEBUG is also defined, change _DPRINTK_FLAGS_DEFAULT to enable printing by default. Also adding _DPRINTK_FLAGS_INCL_MODNAME would be nice, but there are numerous cases of pr_debug(NAME ": ...), which would result in double printing of module-name. So defer this until things settle. Cc: David Miller Cc: Joe Perches Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- include/linux/device.h | 8 ++++---- include/linux/dynamic_debug.h | 4 ++++ include/linux/netdevice.h | 8 ++++---- include/linux/printk.h | 8 ++++---- 4 files changed, 16 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/device.h b/include/linux/device.h index 5b3adb8f9588..a782d7ff9e8b 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -945,14 +945,14 @@ int _dev_info(const struct device *dev, const char *fmt, ...) #define dev_info(dev, fmt, arg...) _dev_info(dev, fmt, ##arg) -#if defined(DEBUG) -#define dev_dbg(dev, format, arg...) \ - dev_printk(KERN_DEBUG, dev, format, ##arg) -#elif defined(CONFIG_DYNAMIC_DEBUG) +#if defined(CONFIG_DYNAMIC_DEBUG) #define dev_dbg(dev, format, ...) \ do { \ dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \ } while (0) +#elif defined(DEBUG) +#define dev_dbg(dev, format, arg...) \ + dev_printk(KERN_DEBUG, dev, format, ##arg) #else #define dev_dbg(dev, format, arg...) \ ({ \ diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index f71a6b046245..29ea09ae30cf 100644 --- a/include/linux/dynamic_debug.h +++ b/include/linux/dynamic_debug.h @@ -26,7 +26,11 @@ struct _ddebug { #define _DPRINTK_FLAGS_INCL_FUNCNAME (1<<2) #define _DPRINTK_FLAGS_INCL_LINENO (1<<3) #define _DPRINTK_FLAGS_INCL_TID (1<<4) +#if defined DEBUG +#define _DPRINTK_FLAGS_DEFAULT _DPRINTK_FLAGS_PRINT +#else #define _DPRINTK_FLAGS_DEFAULT 0 +#endif unsigned int flags:8; } __attribute__((aligned(8))); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0eac07c95255..f486f635e7b5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2687,14 +2687,14 @@ int netdev_info(const struct net_device *dev, const char *format, ...); #define MODULE_ALIAS_NETDEV(device) \ MODULE_ALIAS("netdev-" device) -#if defined(DEBUG) -#define netdev_dbg(__dev, format, args...) \ - netdev_printk(KERN_DEBUG, __dev, format, ##args) -#elif defined(CONFIG_DYNAMIC_DEBUG) +#if defined(CONFIG_DYNAMIC_DEBUG) #define netdev_dbg(__dev, format, args...) \ do { \ dynamic_netdev_dbg(__dev, format, ##args); \ } while (0) +#elif defined(DEBUG) +#define netdev_dbg(__dev, format, args...) \ + netdev_printk(KERN_DEBUG, __dev, format, ##args) #else #define netdev_dbg(__dev, format, args...) \ ({ \ diff --git a/include/linux/printk.h b/include/linux/printk.h index f0e22f75143f..f9abd9357a0c 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -180,13 +180,13 @@ extern void dump_stack(void) __cold; #endif /* If you are writing a driver, please use dev_dbg instead */ -#if defined(DEBUG) -#define pr_debug(fmt, ...) \ - printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) -#elif defined(CONFIG_DYNAMIC_DEBUG) +#if defined(CONFIG_DYNAMIC_DEBUG) /* dynamic_pr_debug() uses pr_fmt() internally so we don't need it here */ #define pr_debug(fmt, ...) \ dynamic_pr_debug(fmt, ##__VA_ARGS__) +#elif defined(DEBUG) +#define pr_debug(fmt, ...) \ + printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) #else #define pr_debug(fmt, ...) \ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) -- cgit From 5ca7d2a6c5e4f24dfe39e8383c6d32e61d95d16a Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:12:44 -0500 Subject: dynamic_debug: describe_flags with '=[pmflt_]*' Change describe_flags() to emit '=[pmflt_]+' for current callsite flags, or just '=_' when they're disabled. Having '=' in output allows a more selective grep expression; in contrast '-' may appear in filenames, line-ranges, and format-strings. '=' also has better mnemonics, saying; "the current setting is equal to ". This allows grep "=_" /dynamic_debug/control to see disabled callsites while avoiding the many occurrences of " = " seen in format strings. Enlarge flagsbufs to handle additional flag char, and alter ddebug_parse_flags() to allow flags=0, so that user can turn off all debug flags via: ~# echo =_ > /dynamic_debug/control Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- include/linux/dynamic_debug.h | 3 ++- lib/dynamic_debug.c | 21 ++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index 29ea09ae30cf..fc39640f2dea 100644 --- a/include/linux/dynamic_debug.h +++ b/include/linux/dynamic_debug.h @@ -21,7 +21,8 @@ struct _ddebug { * The bits here are changed dynamically when the user * writes commands to /dynamic_debug/control */ -#define _DPRINTK_FLAGS_PRINT (1<<0) /* printk() a message using the format */ +#define _DPRINTK_FLAGS_NONE 0 +#define _DPRINTK_FLAGS_PRINT (1<<0) /* printk() a message using the format */ #define _DPRINTK_FLAGS_INCL_MODNAME (1<<1) #define _DPRINTK_FLAGS_INCL_FUNCNAME (1<<2) #define _DPRINTK_FLAGS_INCL_LINENO (1<<3) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index b199e0935053..cde4dfe2b2d5 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -75,6 +75,7 @@ static struct { unsigned flag:8; char opt_char; } opt_array[] = { { _DPRINTK_FLAGS_INCL_FUNCNAME, 'f' }, { _DPRINTK_FLAGS_INCL_LINENO, 'l' }, { _DPRINTK_FLAGS_INCL_TID, 't' }, + { _DPRINTK_FLAGS_NONE, '_' }, }; /* format a string into buf[] which describes the _ddebug's flags */ @@ -84,12 +85,12 @@ static char *ddebug_describe_flags(struct _ddebug *dp, char *buf, char *p = buf; int i; - BUG_ON(maxlen < 4); + BUG_ON(maxlen < 6); for (i = 0; i < ARRAY_SIZE(opt_array); ++i) if (dp->flags & opt_array[i].flag) *p++ = opt_array[i].opt_char; if (p == buf) - *p++ = '-'; + *p++ = '_'; *p = '\0'; return buf; @@ -108,7 +109,7 @@ static void ddebug_change(const struct ddebug_query *query, struct ddebug_table *dt; unsigned int newflags; unsigned int nfound = 0; - char flagbuf[8]; + char flagbuf[10]; /* search for matching ddebugs */ mutex_lock(&ddebug_lock); @@ -152,7 +153,7 @@ static void ddebug_change(const struct ddebug_query *query, continue; dp->flags = newflags; if (verbose) - pr_info("changed %s:%d [%s]%s %s\n", + pr_info("changed %s:%d [%s]%s =%s\n", dp->filename, dp->lineno, dt->mod_name, dp->function, ddebug_describe_flags(dp, flagbuf, @@ -370,8 +371,6 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp, if (i < 0) return -EINVAL; } - if (flags == 0) - return -EINVAL; if (verbose) pr_info("flags=0x%x\n", flags); @@ -666,7 +665,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p) { struct ddebug_iter *iter = m->private; struct _ddebug *dp = p; - char flagsbuf[8]; + char flagsbuf[10]; if (verbose) pr_info("called m=%p p=%p\n", m, p); @@ -677,10 +676,10 @@ static int ddebug_proc_show(struct seq_file *m, void *p) return 0; } - seq_printf(m, "%s:%u [%s]%s %s \"", - dp->filename, dp->lineno, - iter->table->mod_name, dp->function, - ddebug_describe_flags(dp, flagsbuf, sizeof(flagsbuf))); + seq_printf(m, "%s:%u [%s]%s =%s \"", + dp->filename, dp->lineno, + iter->table->mod_name, dp->function, + ddebug_describe_flags(dp, flagsbuf, sizeof(flagsbuf))); seq_escape(m, dp->format, "\t\r\n\""); seq_puts(m, "\"\n"); -- cgit From e703ddae383abb24b1c7f363cb0df7e78a44ea45 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:12:59 -0500 Subject: dynamic_debug: reduce lineno field to a saner 18 bits lineno:24 allows files with 4 million lines, an insane file-size, even for never-to-get-in-tree machine generated code. Reduce this to 18 bits, which still allows 256k lines. This is still insanely big, but its not raving mad. Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- include/linux/dynamic_debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index fc39640f2dea..7e3c53a900d8 100644 --- a/include/linux/dynamic_debug.h +++ b/include/linux/dynamic_debug.h @@ -15,7 +15,7 @@ struct _ddebug { const char *function; const char *filename; const char *format; - unsigned int lineno:24; + unsigned int lineno:18; /* * The flags field controls the behaviour at the callsite. * The bits here are changed dynamically when the user -- cgit From 593a27c4b212e2afdf772a1f8dcb894e91bda0fa Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Thu, 5 Jan 2012 13:04:21 +0400 Subject: tty: cleanup prohibition of direct opening for unix98 pty master cleanup hack added in v2.6.27-3203-g15582d3 comment from that patch: : pty: If the administrator creates a device for a ptmx slave we should not error : : The open path for ptmx slaves is via the ptmx device. Opening them any : other way is not allowed. Vegard Nossum found that previously this was not : the case and mknod foo c 128 42; cat foo would produce nasty diagnostics : : Signed-off-by: Alan Cox : Signed-off-by: Linus Torvalds devpts_get_tty() returns non-null only for inodes on devpts, but there is no inodes for master-devices, /dev/ptmx (/dev/pts/ptmx) is the only way to open them. Thus we can completely forbid lookup for master-devices and eliminate that hack in tty_init_dev() because tty_open() will get EIO from tty_driver_lookup_tty(). Signed-off-by: Konstantin Khlebnikov Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 8 +++----- drivers/tty/tty_io.c | 12 ++---------- include/linux/tty.h | 3 +-- 3 files changed, 6 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index d8653ab6f498..03147fa31d47 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -515,10 +515,8 @@ static int pty_unix98_ioctl(struct tty_struct *tty, static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver, struct inode *ptm_inode, int idx) { - struct tty_struct *tty = devpts_get_tty(ptm_inode, idx); - if (tty) - tty = tty->link; - return tty; + /* Master must be open via /dev/ptmx */ + return ERR_PTR(-EIO); } /** @@ -677,7 +675,7 @@ static int ptmx_open(struct inode *inode, struct file *filp) mutex_lock(&tty_mutex); tty_lock(); - tty = tty_init_dev(ptm_driver, index, 1); + tty = tty_init_dev(ptm_driver, index); mutex_unlock(&tty_mutex); if (IS_ERR(tty)) { diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index e41b9bbc107d..fbcc14063804 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1365,7 +1365,6 @@ static int tty_reopen(struct tty_struct *tty) * @driver: tty driver we are opening a device on * @idx: device index * @ret_tty: returned tty structure - * @first_ok: ok to open a new device (used by ptmx) * * Prepare a tty device. This may not be a "new" clean device but * could also be an active device. The pty drivers require special @@ -1385,18 +1384,11 @@ static int tty_reopen(struct tty_struct *tty) * relaxed for the (most common) case of reopening a tty. */ -struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, - int first_ok) +struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) { struct tty_struct *tty; int retval; - /* Check if pty master is being opened multiple times */ - if (driver->subtype == PTY_TYPE_MASTER && - (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) { - return ERR_PTR(-EIO); - } - /* * First time open is complex, especially for PTY devices. * This code guarantees that either everything succeeds and the @@ -1950,7 +1942,7 @@ retry_open: if (retval) tty = ERR_PTR(retval); } else - tty = tty_init_dev(driver, index, 0); + tty = tty_init_dev(driver, index); mutex_unlock(&tty_mutex); if (driver) diff --git a/include/linux/tty.h b/include/linux/tty.h index 5dbb3cb05a82..d3ebd765b548 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -480,8 +480,7 @@ extern void free_tty_struct(struct tty_struct *tty); extern void initialize_tty_struct(struct tty_struct *tty, struct tty_driver *driver, int idx); extern void deinitialize_tty_struct(struct tty_struct *tty); -extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, - int first_ok); +extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx); extern int tty_release(struct inode *inode, struct file *filp); extern int tty_init_termios(struct tty_struct *tty); -- cgit From e9aba5158a80098447ff207a452a3418ae7ee386 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Thu, 5 Jan 2012 13:06:11 +0400 Subject: tty: rework pty count limiting After adding devpts multiple-insrances sysctl kernel.pty.max limit pty count for each devpts instance independently, while kernel.pty.nr shows total pty count. This patch restores sysctl kernel.pty.max as global limit (4096 by default), adds pty reseve for main devpts (mounted without "newinstance" argument), and new sysctl to tune it: kernel.pty.reserve (1024 by default) Also it adds devpts mount option "max=%d" to limit pty count for each devpts instance independently. (by default NR_UNIX98_PTY_MAX == 2^20) Thus devpts instances in containers cannot eat up all available pty even if we didn't set any limits, while with "max" argument we can adjust limits more precisely. Plus, now open("/dev/ptmx") return -ENOSPC in case lack of pty indexes, this is more informative than -EIO. Signed-off-by: Konstantin Khlebnikov Signed-off-by: Greg Kroah-Hartman --- fs/devpts/inode.c | 34 ++++++++++++++++++++++++++++++---- include/linux/tty.h | 1 + 2 files changed, 31 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index c2c7317d5687..1c6f908e38ca 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -41,8 +41,9 @@ * Otherwise one can eat up all kernel memory by opening /dev/ptmx repeatedly. */ static int pty_limit = NR_UNIX98_PTY_DEFAULT; +static int pty_reserve = NR_UNIX98_PTY_RESERVE; static int pty_limit_min; -static int pty_limit_max = NR_UNIX98_PTY_MAX; +static int pty_limit_max = INT_MAX; static int pty_count; static struct ctl_table pty_table[] = { @@ -54,6 +55,14 @@ static struct ctl_table pty_table[] = { .proc_handler = proc_dointvec_minmax, .extra1 = &pty_limit_min, .extra2 = &pty_limit_max, + }, { + .procname = "reserve", + .maxlen = sizeof(int), + .mode = 0644, + .data = &pty_reserve, + .proc_handler = proc_dointvec_minmax, + .extra1 = &pty_limit_min, + .extra2 = &pty_limit_max, }, { .procname = "nr", .maxlen = sizeof(int), @@ -94,10 +103,11 @@ struct pts_mount_opts { umode_t mode; umode_t ptmxmode; int newinstance; + int max; }; enum { - Opt_uid, Opt_gid, Opt_mode, Opt_ptmxmode, Opt_newinstance, + Opt_uid, Opt_gid, Opt_mode, Opt_ptmxmode, Opt_newinstance, Opt_max, Opt_err }; @@ -108,6 +118,7 @@ static const match_table_t tokens = { #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES {Opt_ptmxmode, "ptmxmode=%o"}, {Opt_newinstance, "newinstance"}, + {Opt_max, "max=%d"}, #endif {Opt_err, NULL} }; @@ -154,6 +165,7 @@ static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts) opts->gid = 0; opts->mode = DEVPTS_DEFAULT_MODE; opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE; + opts->max = NR_UNIX98_PTY_MAX; /* newinstance makes sense only on initial mount */ if (op == PARSE_MOUNT) @@ -197,6 +209,12 @@ static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts) if (op == PARSE_MOUNT) opts->newinstance = 1; break; + case Opt_max: + if (match_int(&args[0], &option) || + option < 0 || option > NR_UNIX98_PTY_MAX) + return -EINVAL; + opts->max = option; + break; #endif default: printk(KERN_ERR "devpts: called with bogus options\n"); @@ -303,6 +321,8 @@ static int devpts_show_options(struct seq_file *seq, struct dentry *root) seq_printf(seq, ",mode=%03o", opts->mode); #ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES seq_printf(seq, ",ptmxmode=%03o", opts->ptmxmode); + if (opts->max < NR_UNIX98_PTY_MAX) + seq_printf(seq, ",max=%d", opts->max); #endif return 0; @@ -483,6 +503,12 @@ retry: return -ENOMEM; mutex_lock(&allocated_ptys_lock); + if (pty_count >= pty_limit - + (fsi->mount_opts.newinstance ? pty_reserve : 0)) { + mutex_unlock(&allocated_ptys_lock); + return -ENOSPC; + } + ida_ret = ida_get_new(&fsi->allocated_ptys, &index); if (ida_ret < 0) { mutex_unlock(&allocated_ptys_lock); @@ -491,10 +517,10 @@ retry: return -EIO; } - if (index >= pty_limit) { + if (index >= fsi->mount_opts.max) { ida_remove(&fsi->allocated_ptys, index); mutex_unlock(&allocated_ptys_lock); - return -EIO; + return -ENOSPC; } pty_count++; mutex_unlock(&allocated_ptys_lock); diff --git a/include/linux/tty.h b/include/linux/tty.h index d3ebd765b548..d40774188203 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -52,6 +52,7 @@ * hardcoded at present.) */ #define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */ +#define NR_UNIX98_PTY_RESERVE 1024 /* Default reserve for main devpts */ #define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */ /* -- cgit From 9875bb480cc89d9b690f7028aadf7e58454f0dae Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 24 Jan 2012 13:35:37 -0500 Subject: Eliminate get_driver() and put_driver() Now that there are no users of get_driver() or put_driver(), this patch (as1513) removes those routines completely. Signed-off-by: Alan Stern CC: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/base/driver.c | 28 ---------------------------- include/linux/device.h | 2 -- 2 files changed, 30 deletions(-) (limited to 'include') diff --git a/drivers/base/driver.c b/drivers/base/driver.c index e979cad75c6e..60e4f77ca662 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -153,34 +153,6 @@ int driver_add_kobj(struct device_driver *drv, struct kobject *kobj, } EXPORT_SYMBOL_GPL(driver_add_kobj); -/** - * get_driver - increment driver reference count. - * @drv: driver. - */ -struct device_driver *get_driver(struct device_driver *drv) -{ - if (drv) { - struct driver_private *priv; - struct kobject *kobj; - - kobj = kobject_get(&drv->p->kobj); - priv = to_driver(kobj); - return priv->driver; - } - return NULL; -} -EXPORT_SYMBOL_GPL(get_driver); - -/** - * put_driver - decrement driver's refcount. - * @drv: driver. - */ -void put_driver(struct device_driver *drv) -{ - kobject_put(&drv->p->kobj); -} -EXPORT_SYMBOL_GPL(put_driver); - static int driver_add_groups(struct device_driver *drv, const struct attribute_group **groups) { diff --git a/include/linux/device.h b/include/linux/device.h index a782d7ff9e8b..d28bd8295677 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -238,8 +238,6 @@ struct device_driver { extern int __must_check driver_register(struct device_driver *drv); extern void driver_unregister(struct device_driver *drv); -extern struct device_driver *get_driver(struct device_driver *drv); -extern void put_driver(struct device_driver *drv); extern struct device_driver *driver_find(const char *name, struct bus_type *bus); extern int driver_probe_done(void); -- cgit From 09e9b813d34d9a09d64a64580a9959d8bae1f4f5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 25 Jan 2012 04:44:20 +0000 Subject: tcp: add LINUX_MIB_TCPRETRANSFAIL counter It might be useful to get a counter of failed tcp_retransmit_skb() calls. Reported-by: Satoru Moriya Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/snmp.h | 1 + net/ipv4/proc.c | 1 + net/ipv4/tcp_output.c | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/snmp.h b/include/linux/snmp.h index c1241c428179..8ee8af4e6da9 100644 --- a/include/linux/snmp.h +++ b/include/linux/snmp.h @@ -232,6 +232,7 @@ enum LINUX_MIB_TCPTIMEWAITOVERFLOW, /* TCPTimeWaitOverflow */ LINUX_MIB_TCPREQQFULLDOCOOKIES, /* TCPReqQFullDoCookies */ LINUX_MIB_TCPREQQFULLDROP, /* TCPReqQFullDrop */ + LINUX_MIB_TCPRETRANSFAIL, /* TCPRetransFail */ __LINUX_MIB_MAX }; diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 6afc807ee2ad..02d61079f08b 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -256,6 +256,7 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPTimeWaitOverflow", LINUX_MIB_TCPTIMEWAITOVERFLOW), SNMP_MIB_ITEM("TCPReqQFullDoCookies", LINUX_MIB_TCPREQQFULLDOCOOKIES), SNMP_MIB_ITEM("TCPReqQFullDrop", LINUX_MIB_TCPREQQFULLDROP), + SNMP_MIB_ITEM("TCPRetransFail", LINUX_MIB_TCPRETRANSFAIL), SNMP_MIB_SENTINEL }; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 8c8de2780c7a..561550ab3c30 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2308,8 +2308,10 @@ begin_fwd: if (sacked & (TCPCB_SACKED_ACKED|TCPCB_SACKED_RETRANS)) continue; - if (tcp_retransmit_skb(sk, skb)) + if (tcp_retransmit_skb(sk, skb)) { + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPRETRANSFAIL); return; + } NET_INC_STATS_BH(sock_net(sk), mib_idx); if (inet_csk(sk)->icsk_ca_state == TCP_CA_Recovery) -- cgit From 3afbd89c9639c344300dcdd7d4e5e18dda559fd4 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 25 Jan 2012 09:05:04 +0100 Subject: serial/efm32: add new driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/tty/serial/efm32-uart.txt | 14 + drivers/tty/serial/Kconfig | 13 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/efm32-uart.c | 830 +++++++++++++++++++++ include/linux/platform_data/efm32-uart.h | 18 + include/linux/serial_core.h | 2 + 6 files changed, 878 insertions(+) create mode 100644 Documentation/devicetree/bindings/tty/serial/efm32-uart.txt create mode 100644 drivers/tty/serial/efm32-uart.c create mode 100644 include/linux/platform_data/efm32-uart.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/tty/serial/efm32-uart.txt b/Documentation/devicetree/bindings/tty/serial/efm32-uart.txt new file mode 100644 index 000000000000..6588b6950a7f --- /dev/null +++ b/Documentation/devicetree/bindings/tty/serial/efm32-uart.txt @@ -0,0 +1,14 @@ +* Energymicro efm32 UART + +Required properties: +- compatible : Should be "efm32,uart" +- reg : Address and length of the register set +- interrupts : Should contain uart interrupt + +Example: + +uart@0x4000c400 { + compatible = "efm32,uart"; + reg = <0x4000c400 0x400>; + interrupts = <15>; +}; diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index aca2386c5ef1..6e24a8f5fd2a 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1628,4 +1628,17 @@ config SERIAL_AR933X_NR_UARTS Set this to the number of serial ports you want the driver to support. +config SERIAL_EFM32_UART + tristate "EFM32 UART/USART port." + depends on ARCH_EFM32 + select SERIAL_CORE + help + This driver support the USART and UART ports on + Energy Micro's efm32 SoCs. + +config SERIAL_EFM32_UART_CONSOLE + bool "EFM32 UART/USART console support" + depends on SERIAL_EFM32_UART=y + select SERIAL_CORE_CONSOLE + endmenu diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index f5b01f2ce525..1997ad4a39a6 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -92,3 +92,4 @@ obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o +obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c new file mode 100644 index 000000000000..615e46470491 --- /dev/null +++ b/drivers/tty/serial/efm32-uart.c @@ -0,0 +1,830 @@ +#if defined(CONFIG_SERIAL_EFM32_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRIVER_NAME "efm32-uart" +#define DEV_NAME "ttyefm" + +#define UARTn_CTRL 0x00 +#define UARTn_CTRL_SYNC 0x0001 +#define UARTn_CTRL_TXBIL 0x1000 + +#define UARTn_FRAME 0x04 +#define UARTn_FRAME_DATABITS__MASK 0x000f +#define UARTn_FRAME_DATABITS(n) ((n) - 3) +#define UARTn_FRAME_PARITY_NONE 0x0000 +#define UARTn_FRAME_PARITY_EVEN 0x0200 +#define UARTn_FRAME_PARITY_ODD 0x0300 +#define UARTn_FRAME_STOPBITS_HALF 0x0000 +#define UARTn_FRAME_STOPBITS_ONE 0x1000 +#define UARTn_FRAME_STOPBITS_TWO 0x3000 + +#define UARTn_CMD 0x0c +#define UARTn_CMD_RXEN 0x0001 +#define UARTn_CMD_RXDIS 0x0002 +#define UARTn_CMD_TXEN 0x0004 +#define UARTn_CMD_TXDIS 0x0008 + +#define UARTn_STATUS 0x10 +#define UARTn_STATUS_TXENS 0x0002 +#define UARTn_STATUS_TXC 0x0020 +#define UARTn_STATUS_TXBL 0x0040 +#define UARTn_STATUS_RXDATAV 0x0080 + +#define UARTn_CLKDIV 0x14 + +#define UARTn_RXDATAX 0x18 +#define UARTn_RXDATAX_RXDATA__MASK 0x01ff +#define UARTn_RXDATAX_PERR 0x4000 +#define UARTn_RXDATAX_FERR 0x8000 +/* + * This is a software only flag used for ignore_status_mask and + * read_status_mask! It's used for breaks that the hardware doesn't report + * explicitly. + */ +#define SW_UARTn_RXDATAX_BERR 0x2000 + +#define UARTn_TXDATA 0x34 + +#define UARTn_IF 0x40 +#define UARTn_IF_TXC 0x0001 +#define UARTn_IF_TXBL 0x0002 +#define UARTn_IF_RXDATAV 0x0004 +#define UARTn_IF_RXOF 0x0010 + +#define UARTn_IFS 0x44 +#define UARTn_IFC 0x48 +#define UARTn_IEN 0x4c + +#define UARTn_ROUTE 0x54 +#define UARTn_ROUTE_LOCATION__MASK 0x0700 +#define UARTn_ROUTE_LOCATION(n) (((n) << 8) & UARTn_ROUTE_LOCATION__MASK) +#define UARTn_ROUTE_RXPEN 0x0001 +#define UARTn_ROUTE_TXPEN 0x0002 + +struct efm32_uart_port { + struct uart_port port; + unsigned int txirq; + struct clk *clk; +}; +#define to_efm_port(_port) container_of(_port, struct efm32_uart_port, port) +#define efm_debug(efm_port, format, arg...) \ + dev_dbg(efm_port->port.dev, format, ##arg) + +static void efm32_uart_write32(struct efm32_uart_port *efm_port, + u32 value, unsigned offset) +{ + writel_relaxed(value, efm_port->port.membase + offset); +} + +static u32 efm32_uart_read32(struct efm32_uart_port *efm_port, + unsigned offset) +{ + return readl_relaxed(efm_port->port.membase + offset); +} + +static unsigned int efm32_uart_tx_empty(struct uart_port *port) +{ + struct efm32_uart_port *efm_port = to_efm_port(port); + u32 status = efm32_uart_read32(efm_port, UARTn_STATUS); + + if (status & UARTn_STATUS_TXC) + return TIOCSER_TEMT; + else + return 0; +} + +static void efm32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* sorry, neither handshaking lines nor loop functionallity */ +} + +static unsigned int efm32_uart_get_mctrl(struct uart_port *port) +{ + /* sorry, no handshaking lines available */ + return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR; +} + +static void efm32_uart_stop_tx(struct uart_port *port) +{ + struct efm32_uart_port *efm_port = to_efm_port(port); + u32 ien = efm32_uart_read32(efm_port, UARTn_IEN); + + efm32_uart_write32(efm_port, UARTn_CMD_TXDIS, UARTn_CMD); + ien &= ~(UARTn_IF_TXC | UARTn_IF_TXBL); + efm32_uart_write32(efm_port, ien, UARTn_IEN); +} + +static void efm32_uart_tx_chars(struct efm32_uart_port *efm_port) +{ + struct uart_port *port = &efm_port->port; + struct circ_buf *xmit = &port->state->xmit; + + while (efm32_uart_read32(efm_port, UARTn_STATUS) & + UARTn_STATUS_TXBL) { + if (port->x_char) { + port->icount.tx++; + efm32_uart_write32(efm_port, port->x_char, + UARTn_TXDATA); + port->x_char = 0; + continue; + } + if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) { + port->icount.tx++; + efm32_uart_write32(efm_port, xmit->buf[xmit->tail], + UARTn_TXDATA); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + } else + break; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (!port->x_char && uart_circ_empty(xmit) && + efm32_uart_read32(efm_port, UARTn_STATUS) & + UARTn_STATUS_TXC) + efm32_uart_stop_tx(port); +} + +static void efm32_uart_start_tx(struct uart_port *port) +{ + struct efm32_uart_port *efm_port = to_efm_port(port); + u32 ien; + + efm32_uart_write32(efm_port, + UARTn_IF_TXBL | UARTn_IF_TXC, UARTn_IFC); + ien = efm32_uart_read32(efm_port, UARTn_IEN); + efm32_uart_write32(efm_port, + ien | UARTn_IF_TXBL | UARTn_IF_TXC, UARTn_IEN); + efm32_uart_write32(efm_port, UARTn_CMD_TXEN, UARTn_CMD); + + efm32_uart_tx_chars(efm_port); +} + +static void efm32_uart_stop_rx(struct uart_port *port) +{ + struct efm32_uart_port *efm_port = to_efm_port(port); + + efm32_uart_write32(efm_port, UARTn_CMD_RXDIS, UARTn_CMD); +} + +static void efm32_uart_enable_ms(struct uart_port *port) +{ + /* no handshake lines, no modem status interrupts */ +} + +static void efm32_uart_break_ctl(struct uart_port *port, int ctl) +{ + /* not possible without fiddling with gpios */ +} + +static void efm32_uart_rx_chars(struct efm32_uart_port *efm_port, + struct tty_struct *tty) +{ + struct uart_port *port = &efm_port->port; + + while (efm32_uart_read32(efm_port, UARTn_STATUS) & + UARTn_STATUS_RXDATAV) { + u32 rxdata = efm32_uart_read32(efm_port, UARTn_RXDATAX); + int flag = 0; + + /* + * This is a reserved bit and I only saw it read as 0. But to be + * sure not to be confused too much by new devices adhere to the + * warning in the reference manual that reserverd bits might + * read as 1 in the future. + */ + rxdata &= ~SW_UARTn_RXDATAX_BERR; + + port->icount.rx++; + + if ((rxdata & UARTn_RXDATAX_FERR) && + !(rxdata & UARTn_RXDATAX_RXDATA__MASK)) { + rxdata |= SW_UARTn_RXDATAX_BERR; + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else if (rxdata & UARTn_RXDATAX_PERR) + port->icount.parity++; + else if (rxdata & UARTn_RXDATAX_FERR) + port->icount.frame++; + + rxdata &= port->read_status_mask; + + if (rxdata & SW_UARTn_RXDATAX_BERR) + flag = TTY_BREAK; + else if (rxdata & UARTn_RXDATAX_PERR) + flag = TTY_PARITY; + else if (rxdata & UARTn_RXDATAX_FERR) + flag = TTY_FRAME; + else if (uart_handle_sysrq_char(port, + rxdata & UARTn_RXDATAX_RXDATA__MASK)) + continue; + + if (tty && (rxdata & port->ignore_status_mask) == 0) + tty_insert_flip_char(tty, + rxdata & UARTn_RXDATAX_RXDATA__MASK, flag); + } +} + +static irqreturn_t efm32_uart_rxirq(int irq, void *data) +{ + struct efm32_uart_port *efm_port = data; + u32 irqflag = efm32_uart_read32(efm_port, UARTn_IF); + int handled = IRQ_NONE; + struct uart_port *port = &efm_port->port; + struct tty_struct *tty; + + spin_lock(&port->lock); + + tty = tty_kref_get(port->state->port.tty); + + if (irqflag & UARTn_IF_RXDATAV) { + efm32_uart_write32(efm_port, UARTn_IF_RXDATAV, UARTn_IFC); + efm32_uart_rx_chars(efm_port, tty); + + handled = IRQ_HANDLED; + } + + if (irqflag & UARTn_IF_RXOF) { + efm32_uart_write32(efm_port, UARTn_IF_RXOF, UARTn_IFC); + port->icount.overrun++; + if (tty) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + + handled = IRQ_HANDLED; + } + + if (tty) { + tty_flip_buffer_push(tty); + tty_kref_put(tty); + } + + spin_unlock(&port->lock); + + return handled; +} + +static irqreturn_t efm32_uart_txirq(int irq, void *data) +{ + struct efm32_uart_port *efm_port = data; + u32 irqflag = efm32_uart_read32(efm_port, UARTn_IF); + + /* TXBL doesn't need to be cleared */ + if (irqflag & UARTn_IF_TXC) + efm32_uart_write32(efm_port, UARTn_IF_TXC, UARTn_IFC); + + if (irqflag & (UARTn_IF_TXC | UARTn_IF_TXBL)) { + efm32_uart_tx_chars(efm_port); + return IRQ_HANDLED; + } else + return IRQ_NONE; +} + +static int efm32_uart_startup(struct uart_port *port) +{ + struct efm32_uart_port *efm_port = to_efm_port(port); + u32 location = 0; + struct efm32_uart_pdata *pdata = dev_get_platdata(port->dev); + int ret; + + if (pdata) + location = UARTn_ROUTE_LOCATION(pdata->location); + + ret = clk_enable(efm_port->clk); + if (ret) { + efm_debug(efm_port, "failed to enable clk\n"); + goto err_clk_enable; + } + port->uartclk = clk_get_rate(efm_port->clk); + + /* Enable pins at configured location */ + efm32_uart_write32(efm_port, location | UARTn_ROUTE_RXPEN | UARTn_ROUTE_TXPEN, + UARTn_ROUTE); + + ret = request_irq(port->irq, efm32_uart_rxirq, 0, + DRIVER_NAME, efm_port); + if (ret) { + efm_debug(efm_port, "failed to register rxirq\n"); + goto err_request_irq_rx; + } + + /* disable all irqs */ + efm32_uart_write32(efm_port, 0, UARTn_IEN); + + ret = request_irq(efm_port->txirq, efm32_uart_txirq, 0, + DRIVER_NAME, efm_port); + if (ret) { + efm_debug(efm_port, "failed to register txirq\n"); + free_irq(port->irq, efm_port); +err_request_irq_rx: + + clk_disable(efm_port->clk); + } else { + efm32_uart_write32(efm_port, + UARTn_IF_RXDATAV | UARTn_IF_RXOF, UARTn_IEN); + efm32_uart_write32(efm_port, UARTn_CMD_RXEN, UARTn_CMD); + } + +err_clk_enable: + return ret; +} + +static void efm32_uart_shutdown(struct uart_port *port) +{ + struct efm32_uart_port *efm_port = to_efm_port(port); + + efm32_uart_write32(efm_port, 0, UARTn_IEN); + free_irq(port->irq, efm_port); + + clk_disable(efm_port->clk); +} + +static void efm32_uart_set_termios(struct uart_port *port, + struct ktermios *new, struct ktermios *old) +{ + struct efm32_uart_port *efm_port = to_efm_port(port); + unsigned long flags; + unsigned baud; + u32 clkdiv; + u32 frame = 0; + + /* no modem control lines */ + new->c_cflag &= ~(CRTSCTS | CMSPAR); + + baud = uart_get_baud_rate(port, new, old, + DIV_ROUND_CLOSEST(port->uartclk, 16 * 8192), + DIV_ROUND_CLOSEST(port->uartclk, 16)); + + switch (new->c_cflag & CSIZE) { + case CS5: + frame |= UARTn_FRAME_DATABITS(5); + break; + case CS6: + frame |= UARTn_FRAME_DATABITS(6); + break; + case CS7: + frame |= UARTn_FRAME_DATABITS(7); + break; + case CS8: + frame |= UARTn_FRAME_DATABITS(8); + break; + } + + if (new->c_cflag & CSTOPB) + /* the receiver only verifies the first stop bit */ + frame |= UARTn_FRAME_STOPBITS_TWO; + else + frame |= UARTn_FRAME_STOPBITS_ONE; + + if (new->c_cflag & PARENB) { + if (new->c_cflag & PARODD) + frame |= UARTn_FRAME_PARITY_ODD; + else + frame |= UARTn_FRAME_PARITY_EVEN; + } else + frame |= UARTn_FRAME_PARITY_NONE; + + /* + * the 6 lowest bits of CLKDIV are dc, bit 6 has value 0.25. + * port->uartclk <= 14e6, so 4 * port->uartclk doesn't overflow. + */ + clkdiv = (DIV_ROUND_CLOSEST(4 * port->uartclk, 16 * baud) - 4) << 6; + + spin_lock_irqsave(&port->lock, flags); + + efm32_uart_write32(efm_port, + UARTn_CMD_TXDIS | UARTn_CMD_RXDIS, UARTn_CMD); + + port->read_status_mask = UARTn_RXDATAX_RXDATA__MASK; + if (new->c_iflag & INPCK) + port->read_status_mask |= + UARTn_RXDATAX_FERR | UARTn_RXDATAX_PERR; + if (new->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= SW_UARTn_RXDATAX_BERR; + + port->ignore_status_mask = 0; + if (new->c_iflag & IGNPAR) + port->ignore_status_mask |= + UARTn_RXDATAX_FERR | UARTn_RXDATAX_PERR; + if (new->c_iflag & IGNBRK) + port->ignore_status_mask |= SW_UARTn_RXDATAX_BERR; + + uart_update_timeout(port, new->c_cflag, baud); + + efm32_uart_write32(efm_port, UARTn_CTRL_TXBIL, UARTn_CTRL); + efm32_uart_write32(efm_port, frame, UARTn_FRAME); + efm32_uart_write32(efm_port, clkdiv, UARTn_CLKDIV); + + efm32_uart_write32(efm_port, UARTn_CMD_TXEN | UARTn_CMD_RXEN, + UARTn_CMD); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *efm32_uart_type(struct uart_port *port) +{ + return port->type == PORT_EFMUART ? "efm32-uart" : NULL; +} + +static void efm32_uart_release_port(struct uart_port *port) +{ + struct efm32_uart_port *efm_port = to_efm_port(port); + + clk_unprepare(efm_port->clk); + clk_put(efm_port->clk); + iounmap(port->membase); +} + +static int efm32_uart_request_port(struct uart_port *port) +{ + struct efm32_uart_port *efm_port = to_efm_port(port); + int ret; + + port->membase = ioremap(port->mapbase, 60); + if (!efm_port->port.membase) { + ret = -ENOMEM; + efm_debug(efm_port, "failed to remap\n"); + goto err_ioremap; + } + + efm_port->clk = clk_get(port->dev, NULL); + if (IS_ERR(efm_port->clk)) { + ret = PTR_ERR(efm_port->clk); + efm_debug(efm_port, "failed to get clock\n"); + goto err_clk_get; + } + + ret = clk_prepare(efm_port->clk); + if (ret) { + clk_put(efm_port->clk); +err_clk_get: + + iounmap(port->membase); +err_ioremap: + return ret; + } + return 0; +} + +static void efm32_uart_config_port(struct uart_port *port, int type) +{ + if (type & UART_CONFIG_TYPE && + !efm32_uart_request_port(port)) + port->type = PORT_EFMUART; +} + +static int efm32_uart_verify_port(struct uart_port *port, + struct serial_struct *serinfo) +{ + int ret = 0; + + if (serinfo->type != PORT_UNKNOWN && serinfo->type != PORT_EFMUART) + ret = -EINVAL; + + return ret; +} + +static struct uart_ops efm32_uart_pops = { + .tx_empty = efm32_uart_tx_empty, + .set_mctrl = efm32_uart_set_mctrl, + .get_mctrl = efm32_uart_get_mctrl, + .stop_tx = efm32_uart_stop_tx, + .start_tx = efm32_uart_start_tx, + .stop_rx = efm32_uart_stop_rx, + .enable_ms = efm32_uart_enable_ms, + .break_ctl = efm32_uart_break_ctl, + .startup = efm32_uart_startup, + .shutdown = efm32_uart_shutdown, + .set_termios = efm32_uart_set_termios, + .type = efm32_uart_type, + .release_port = efm32_uart_release_port, + .request_port = efm32_uart_request_port, + .config_port = efm32_uart_config_port, + .verify_port = efm32_uart_verify_port, +}; + +static struct efm32_uart_port *efm32_uart_ports[5]; + +#ifdef CONFIG_SERIAL_EFM32_UART_CONSOLE +static void efm32_uart_console_putchar(struct uart_port *port, int ch) +{ + struct efm32_uart_port *efm_port = to_efm_port(port); + unsigned int timeout = 0x400; + u32 status; + + while (1) { + status = efm32_uart_read32(efm_port, UARTn_STATUS); + + if (status & UARTn_STATUS_TXBL) + break; + if (!timeout--) + return; + } + efm32_uart_write32(efm_port, ch, UARTn_TXDATA); +} + +static void efm32_uart_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct efm32_uart_port *efm_port = efm32_uart_ports[co->index]; + u32 status = efm32_uart_read32(efm_port, UARTn_STATUS); + unsigned int timeout = 0x400; + + if (!(status & UARTn_STATUS_TXENS)) + efm32_uart_write32(efm_port, UARTn_CMD_TXEN, UARTn_CMD); + + uart_console_write(&efm_port->port, s, count, + efm32_uart_console_putchar); + + /* Wait for the transmitter to become empty */ + while (1) { + u32 status = efm32_uart_read32(efm_port, UARTn_STATUS); + if (status & UARTn_STATUS_TXC) + break; + if (!timeout--) + break; + } + + if (!(status & UARTn_STATUS_TXENS)) + efm32_uart_write32(efm_port, UARTn_CMD_TXDIS, UARTn_CMD); +} + +static void efm32_uart_console_get_options(struct efm32_uart_port *efm_port, + int *baud, int *parity, int *bits) +{ + u32 ctrl = efm32_uart_read32(efm_port, UARTn_CTRL); + u32 route, clkdiv, frame; + + if (ctrl & UARTn_CTRL_SYNC) + /* not operating in async mode */ + return; + + route = efm32_uart_read32(efm_port, UARTn_ROUTE); + if (!(route & UARTn_ROUTE_TXPEN)) + /* tx pin not routed */ + return; + + clkdiv = efm32_uart_read32(efm_port, UARTn_CLKDIV); + + *baud = DIV_ROUND_CLOSEST(4 * efm_port->port.uartclk, + 16 * (4 + (clkdiv >> 6))); + + frame = efm32_uart_read32(efm_port, UARTn_FRAME); + if (frame & UARTn_FRAME_PARITY_ODD) + *parity = 'o'; + else if (frame & UARTn_FRAME_PARITY_EVEN) + *parity = 'e'; + else + *parity = 'n'; + + *bits = (frame & UARTn_FRAME_DATABITS__MASK) - + UARTn_FRAME_DATABITS(4) + 4; + + efm_debug(efm_port, "get_opts: options=%d%c%d\n", + *baud, *parity, *bits); +} + +static int efm32_uart_console_setup(struct console *co, char *options) +{ + struct efm32_uart_port *efm_port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret; + + if (co->index < 0 || co->index >= ARRAY_SIZE(efm32_uart_ports)) { + unsigned i; + for (i = 0; i < ARRAY_SIZE(efm32_uart_ports); ++i) { + if (efm32_uart_ports[i]) { + pr_warn("efm32-console: fall back to console index %u (from %hhi)\n", + i, co->index); + co->index = i; + break; + } + } + } + + efm_port = efm32_uart_ports[co->index]; + if (!efm_port) { + pr_warn("efm32-console: No port at %d\n", co->index); + return -ENODEV; + } + + ret = clk_prepare(efm_port->clk); + if (ret) { + dev_warn(efm_port->port.dev, + "console: clk_prepare failed: %d\n", ret); + return ret; + } + + efm_port->port.uartclk = clk_get_rate(efm_port->clk); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + efm32_uart_console_get_options(efm_port, + &baud, &parity, &bits); + + return uart_set_options(&efm_port->port, co, baud, parity, bits, flow); +} + +static struct uart_driver efm32_uart_reg; + +static struct console efm32_uart_console = { + .name = DEV_NAME, + .write = efm32_uart_console_write, + .device = uart_console_device, + .setup = efm32_uart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &efm32_uart_reg, +}; + +#else +#define efm32_uart_console (*(struct console *)NULL) +#endif /* ifdef CONFIG_SERIAL_EFM32_UART_CONSOLE / else */ + +static struct uart_driver efm32_uart_reg = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = DEV_NAME, + .nr = ARRAY_SIZE(efm32_uart_ports), + .cons = &efm32_uart_console, +}; + +static int efm32_uart_probe_dt(struct platform_device *pdev, + struct efm32_uart_port *efm_port) +{ + struct device_node *np = pdev->dev.of_node; + int ret; + + if (!np) + return 1; + + ret = of_alias_get_id(np, "serial"); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get alias id: %d\n", ret); + return ret; + } else { + efm_port->port.line = ret; + return 0; + } + +} + +static int __devinit efm32_uart_probe(struct platform_device *pdev) +{ + struct efm32_uart_port *efm_port; + struct resource *res; + int ret; + + efm_port = kzalloc(sizeof(*efm_port), GFP_KERNEL); + if (!efm_port) { + dev_dbg(&pdev->dev, "failed to allocate private data\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + dev_dbg(&pdev->dev, "failed to determine base address\n"); + goto err_get_base; + } + + if (resource_size(res) < 60) { + ret = -EINVAL; + dev_dbg(&pdev->dev, "memory resource too small\n"); + goto err_too_small; + } + + ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + dev_dbg(&pdev->dev, "failed to get rx irq\n"); + goto err_get_rxirq; + } + + efm_port->port.irq = ret; + + ret = platform_get_irq(pdev, 1); + if (ret <= 0) + ret = efm_port->port.irq + 1; + + efm_port->txirq = ret; + + efm_port->port.dev = &pdev->dev; + efm_port->port.mapbase = res->start; + efm_port->port.type = PORT_EFMUART; + efm_port->port.iotype = UPIO_MEM32; + efm_port->port.fifosize = 2; + efm_port->port.ops = &efm32_uart_pops; + efm_port->port.flags = UPF_BOOT_AUTOCONF; + + ret = efm32_uart_probe_dt(pdev, efm_port); + if (ret > 0) + /* not created by device tree */ + efm_port->port.line = pdev->id; + + if (efm_port->port.line >= 0 && + efm_port->port.line < ARRAY_SIZE(efm32_uart_ports)) + efm32_uart_ports[efm_port->port.line] = efm_port; + + ret = uart_add_one_port(&efm32_uart_reg, &efm_port->port); + if (ret) { + dev_dbg(&pdev->dev, "failed to add port: %d\n", ret); + + if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports)) + efm32_uart_ports[pdev->id] = NULL; +err_get_rxirq: +err_too_small: +err_get_base: + kfree(efm_port); + } else { + platform_set_drvdata(pdev, efm_port); + dev_dbg(&pdev->dev, "\\o/\n"); + } + + return ret; +} + +static int __devexit efm32_uart_remove(struct platform_device *pdev) +{ + struct efm32_uart_port *efm_port = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + uart_remove_one_port(&efm32_uart_reg, &efm_port->port); + + if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports)) + efm32_uart_ports[pdev->id] = NULL; + + kfree(efm_port); + + return 0; +} + +static struct of_device_id efm32_uart_dt_ids[] = { + { + .compatible = "efm32,uart", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, efm32_uart_dt_ids); + +static struct platform_driver efm32_uart_driver = { + .probe = efm32_uart_probe, + .remove = __devexit_p(efm32_uart_remove), + + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = efm32_uart_dt_ids, + }, +}; + +static int __init efm32_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&efm32_uart_reg); + if (ret) + return ret; + + ret = platform_driver_register(&efm32_uart_driver); + if (ret) + uart_unregister_driver(&efm32_uart_reg); + + pr_info("EFM32 UART/USART driver\n"); + + return ret; +} +module_init(efm32_uart_init); + +static void __exit efm32_uart_exit(void) +{ + platform_driver_unregister(&efm32_uart_driver); + uart_unregister_driver(&efm32_uart_reg); +} + +MODULE_AUTHOR("Uwe Kleine-Koenig "); +MODULE_DESCRIPTION("EFM32 UART/USART driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/include/linux/platform_data/efm32-uart.h b/include/linux/platform_data/efm32-uart.h new file mode 100644 index 000000000000..ed0e975b3c54 --- /dev/null +++ b/include/linux/platform_data/efm32-uart.h @@ -0,0 +1,18 @@ +/* + * + * + */ +#ifndef __LINUX_PLATFORM_DATA_EFM32_UART_H__ +#define __LINUX_PLATFORM_DATA_EFM32_UART_H__ + +#include + +/** + * struct efm32_uart_pdata + * @location: pinmux location for the I/O pins (to be written to the ROUTE + * register) + */ +struct efm32_uart_pdata { + u8 location; +}; +#endif /* ifndef __LINUX_PLATFORM_DATA_EFM32_UART_H__ */ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index c91ace70c21d..585bfd03d2ee 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -210,6 +210,8 @@ /* Atheros AR933X SoC */ #define PORT_AR933X 99 +/* Energy Micro efm32 SoC */ +#define PORT_EFMUART 100 #ifdef __KERNEL__ -- cgit From 644e9cbbe3fc032cc92d0936057e166a994dc246 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 26 Jan 2012 00:09:05 +0100 Subject: Add driver auto probing for x86 features v4 There's a growing number of drivers that support a specific x86 feature or CPU. Currently loading these drivers currently on a generic distribution requires various driver specific hacks and it often doesn't work. This patch adds auto probing for drivers based on the x86 cpuid information, in particular based on vendor/family/model number and also based on CPUID feature bits. For example a common issue is not loading the SSE 4.2 accelerated CRC module: this can significantly lower the performance of BTRFS which relies on fast CRC. Another issue is loading the right CPUFREQ driver for the current CPU. Currently distributions often try all all possible driver until one sticks, which is not really a good way to do this. It works with existing udev without any changes. The code exports the x86 information as a generic string in sysfs that can be matched by udev's pattern matching. This scheme does not support numeric ranges, so if you want to handle e.g. ranges of model numbers they have to be encoded in ASCII or simply all models or families listed. Fixing that would require changing udev. Another issue is that udev will happily load all drivers that match, there is currently no nice way to stop a specific driver from being loaded if it's not needed (e.g. if you don't need fast CRC) But there are not that many cpu specific drivers around and they're all not that bloated, so this isn't a particularly serious issue. Originally this patch added the modalias to the normal cpu sysdevs. However sysdevs don't have all the infrastructure needed for udev, so it couldn't really autoload drivers. This patch instead adds the CPU modaliases to the cpuid devices, which are real devices with full support for udev. This implies that the cpuid driver has to be loaded to use this. This patch just adds infrastructure, some driver conversions in followups. Thanks to Kay for helping with some sysfs magic. v2: Constifcation, some updates v4: (trenn@suse.de): - Use kzalloc instead of kmalloc to terminate modalias buffer - Use uppercase hex values to match correctly against hex values containing letters Cc: Dave Jones Cc: Kay Sievers Cc: Jen Axboe Cc: Herbert Xu Cc: Huang Ying Cc: Len Brown Signed-off-by: Andi Kleen Signed-off-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/cpu_device_id.h | 13 ++++++++ arch/x86/kernel/cpu/Makefile | 1 + arch/x86/kernel/cpu/match.c | 48 +++++++++++++++++++++++++++++ arch/x86/kernel/cpuid.c | 59 +++++++++++++++++++++++++++++++++++- include/linux/mod_devicetable.h | 21 +++++++++++++ scripts/mod/file2alias.c | 24 +++++++++++++++ 6 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 arch/x86/include/asm/cpu_device_id.h create mode 100644 arch/x86/kernel/cpu/match.c (limited to 'include') diff --git a/arch/x86/include/asm/cpu_device_id.h b/arch/x86/include/asm/cpu_device_id.h new file mode 100644 index 000000000000..ff501e511d91 --- /dev/null +++ b/arch/x86/include/asm/cpu_device_id.h @@ -0,0 +1,13 @@ +#ifndef _CPU_DEVICE_ID +#define _CPU_DEVICE_ID 1 + +/* + * Declare drivers belonging to specific x86 CPUs + * Similar in spirit to pci_device_id and related PCI functions + */ + +#include + +extern const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match); + +#endif diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index 25f24dccdcfa..6ab6aa2fdfdd 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile @@ -16,6 +16,7 @@ obj-y := intel_cacheinfo.o scattered.o topology.o obj-y += proc.o capflags.o powerflags.o common.o obj-y += vmware.o hypervisor.o sched.o mshyperv.o obj-y += rdrand.o +obj-y += match.o obj-$(CONFIG_X86_32) += bugs.o obj-$(CONFIG_X86_64) += bugs_64.o diff --git a/arch/x86/kernel/cpu/match.c b/arch/x86/kernel/cpu/match.c new file mode 100644 index 000000000000..7acc961422e7 --- /dev/null +++ b/arch/x86/kernel/cpu/match.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +/** + * x86_match_cpu - match current CPU again an array of x86_cpu_ids + * @match: Pointer to array of x86_cpu_ids. Last entry terminated with + * {}. + * + * Return the entry if the current CPU matches the entries in the + * passed x86_cpu_id match table. Otherwise NULL. The match table + * contains vendor (X86_VENDOR_*), family, model and feature bits or + * respective wildcard entries. + * + * A typical table entry would be to match a specific CPU + * { X86_VENDOR_INTEL, 6, 0x12 } + * or to match a specific CPU feature + * { X86_FEATURE_MATCH(X86_FEATURE_FOOBAR) } + * + * Fields can be wildcarded with %X86_VENDOR_ANY, %X86_FAMILY_ANY, + * %X86_MODEL_ANY, %X86_FEATURE_ANY or 0 (except for vendor) + * + * Arrays used to match for this should also be declared using + * MODULE_DEVICE_TABLE(x86_cpu, ...) + * + * This always matches against the boot cpu, assuming models and features are + * consistent over all CPUs. + */ +const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match) +{ + const struct x86_cpu_id *m; + struct cpuinfo_x86 *c = &boot_cpu_data; + + for (m = match; m->vendor | m->family | m->model | m->feature; m++) { + if (m->vendor != X86_VENDOR_ANY && c->x86_vendor != m->vendor) + continue; + if (m->family != X86_FAMILY_ANY && c->x86 != m->family) + continue; + if (m->model != X86_MODEL_ANY && c->x86_model != m->model) + continue; + if (m->feature != X86_FEATURE_ANY && !cpu_has(c, m->feature)) + continue; + return m; + } + return NULL; +} +EXPORT_SYMBOL(x86_match_cpu); diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c index a524353d93f2..7c89880eefd0 100644 --- a/arch/x86/kernel/cpuid.c +++ b/arch/x86/kernel/cpuid.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -138,13 +139,57 @@ static const struct file_operations cpuid_fops = { .open = cpuid_open, }; +static ssize_t print_cpu_modalias(struct device *dev, + struct device_attribute *attr, + char *bufptr) +{ + int size = PAGE_SIZE; + int i, n; + char *buf = bufptr; + + n = snprintf(buf, size, "x86cpu:vendor:%04X:family:" + "%04X:model:%04X:feature:", + boot_cpu_data.x86_vendor, + boot_cpu_data.x86, + boot_cpu_data.x86_model); + size -= n; + buf += n; + size -= 2; + for (i = 0; i < NCAPINTS*32; i++) { + if (boot_cpu_has(i)) { + n = snprintf(buf, size, ",%04X", i); + if (n < 0) { + WARN(1, "x86 features overflow page\n"); + break; + } + size -= n; + buf += n; + } + } + *buf++ = ','; + *buf++ = '\n'; + return buf - bufptr; +} + +static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL); + static __cpuinit int cpuid_device_create(int cpu) { struct device *dev; + int err; dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL, "cpu%d", cpu); - return IS_ERR(dev) ? PTR_ERR(dev) : 0; + if (IS_ERR(dev)) + return PTR_ERR(dev); + + err = device_create_file(dev, &dev_attr_modalias); + if (err) { + /* keep device around on error. attribute is optional. */ + err = 0; + } + + return 0; } static void cpuid_device_destroy(int cpu) @@ -182,6 +227,17 @@ static char *cpuid_devnode(struct device *dev, umode_t *mode) return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt)); } +static int cpuid_dev_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (buf) { + print_cpu_modalias(NULL, NULL, buf); + add_uevent_var(env, "MODALIAS=%s", buf); + kfree(buf); + } + return 0; +} + static int __init cpuid_init(void) { int i, err = 0; @@ -200,6 +256,7 @@ static int __init cpuid_init(void) goto out_chrdev; } cpuid_class->devnode = cpuid_devnode; + cpuid_class->dev_uevent = cpuid_dev_uevent; for_each_online_cpu(i) { err = cpuid_device_create(i); if (err != 0) diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index b29e7f6f8fa5..cff2cc08f45a 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -571,4 +571,25 @@ struct amba_id { #endif }; +/* + * Match x86 CPUs for CPU specific drivers. + * See documentation of "x86_match_cpu" for details. + */ + +struct x86_cpu_id { + __u16 vendor; + __u16 family; + __u16 model; + __u16 feature; /* bit index */ + kernel_ulong_t driver_data; +}; + +#define X86_FEATURE_MATCH(x) \ + { X86_VENDOR_ANY, X86_FAMILY_ANY, X86_MODEL_ANY, x } + +#define X86_VENDOR_ANY 0xffff +#define X86_FAMILY_ANY 0 +#define X86_MODEL_ANY 0 +#define X86_FEATURE_ANY 0 /* Same as FPU, you can't test for that */ + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index c0e14b3f2306..026ba38759ca 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1013,6 +1013,30 @@ static int do_amba_entry(const char *filename, } ADD_TO_DEVTABLE("amba", struct amba_id, do_amba_entry); +/* LOOKS like x86cpu:vendor:VVVV:family:FFFF:model:MMMM:feature:*,FEAT,* + * All fields are numbers. It would be nicer to use strings for vendor + * and feature, but getting those out of the build system here is too + * complicated. + */ + +static int do_x86cpu_entry(const char *filename, struct x86_cpu_id *id, + char *alias) +{ + id->feature = TO_NATIVE(id->feature); + id->family = TO_NATIVE(id->family); + id->model = TO_NATIVE(id->model); + id->vendor = TO_NATIVE(id->vendor); + + strcpy(alias, "x86cpu:"); + ADD(alias, "vendor:", id->vendor != X86_VENDOR_ANY, id->vendor); + ADD(alias, ":family:", id->family != X86_FAMILY_ANY, id->family); + ADD(alias, ":model:", id->model != X86_MODEL_ANY, id->model); + ADD(alias, ":feature:*,", id->feature != X86_FEATURE_ANY, id->feature); + strcat(alias, ",*"); + return 1; +} +ADD_TO_DEVTABLE("x86cpu", struct x86_cpu_id, do_x86cpu_entry); + /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { -- cgit From 9061e0e16700ef228837e96987ff51794c956197 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 26 Jan 2012 00:09:08 +0100 Subject: ACPI: Load acpi-cpufreq from processor driver automatically The only left over hole in automatic cpufreq driver loading was the loading of ACPI cpufreq. This driver should be loaded when ACPI supports a _PDC method and the CPU vendor wants to use acpi cpufreq. Simply add a request module call to the acpi processor core driver when this is true. This seems like the simplest solution for this. Cc: Len Brown Signed-off-by: Andi Kleen Signed-off-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/processor_driver.c | 1 + drivers/acpi/processor_perflib.c | 22 ++++++++++++++++++++++ include/acpi/processor.h | 1 + 3 files changed, 24 insertions(+) (limited to 'include') diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 0034ede38710..e6920d0aca53 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -497,6 +497,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device) #ifdef CONFIG_CPU_FREQ acpi_processor_ppc_has_changed(pr, 0); + acpi_processor_load_module(pr); #endif acpi_processor_get_throttling_info(pr); acpi_processor_get_limit_info(pr); diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index 85b32376dad7..0af48a8554cd 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -240,6 +240,28 @@ void acpi_processor_ppc_exit(void) acpi_processor_ppc_status &= ~PPC_REGISTERED; } +/* + * Do a quick check if the systems looks like it should use ACPI + * cpufreq. We look at a _PCT method being available, but don't + * do a whole lot of sanity checks. + */ +void acpi_processor_load_module(struct acpi_processor *pr) +{ + static int requested; + acpi_status status = 0; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + + if (!arch_has_acpi_pdc() || requested) + return; + status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer); + if (!ACPI_FAILURE(status)) { + printk(KERN_INFO PREFIX "Requesting acpi_cpufreq\n"); + request_module_nowait("acpi_cpufreq"); + requested = 1; + } + kfree(buffer.pointer); +} + static int acpi_processor_get_performance_control(struct acpi_processor *pr) { int result = 0; diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 610f6fb1bbc2..da57fdcc57d2 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -224,6 +224,7 @@ struct acpi_processor_errata { } piix4; }; +extern void acpi_processor_load_module(struct acpi_processor *pr); extern int acpi_processor_preregister_performance(struct acpi_processor_performance __percpu *performance); -- cgit From fad12ac8c8c2591c7f4e61d19b6a9d76cd49fafa Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Thu, 26 Jan 2012 00:09:14 +0100 Subject: CPU: Introduce ARCH_HAS_CPU_AUTOPROBE and X86 parts This patch is based on Andi Kleen's work: Implement autoprobing/loading of modules serving CPU specific features (x86cpu autoloading). And Kay Siever's work to get rid of sysdev cpu structures and making use of struct device instead. Before, the cpuid driver had to be loaded to get the x86cpu autoloading feature. With this patch autoloading works through the /sys/devices/system/cpu object Cc: Kay Sievers Cc: Dave Jones Cc: Jens Axboe Cc: Herbert Xu Cc: Huang Ying Cc: Len Brown Acked-by: Andi Kleen Signed-off-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/Kconfig | 3 +++ arch/x86/kernel/cpu/match.c | 44 +++++++++++++++++++++++++++++++++ arch/x86/kernel/cpuid.c | 59 +-------------------------------------------- drivers/base/cpu.c | 11 +++++++++ include/linux/cpu.h | 7 ++++++ 5 files changed, 66 insertions(+), 58 deletions(-) (limited to 'include') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 864cc6e6ac8e..6baa1e66e1bc 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -179,6 +179,9 @@ config ARCH_HAS_DEFAULT_IDLE config ARCH_HAS_CACHE_LINE_SIZE def_bool y +config ARCH_HAS_CPU_AUTOPROBE + def_bool y + config HAVE_SETUP_PER_CPU_AREA def_bool y diff --git a/arch/x86/kernel/cpu/match.c b/arch/x86/kernel/cpu/match.c index 7acc961422e7..940e2d483076 100644 --- a/arch/x86/kernel/cpu/match.c +++ b/arch/x86/kernel/cpu/match.c @@ -2,6 +2,7 @@ #include #include #include +#include /** * x86_match_cpu - match current CPU again an array of x86_cpu_ids @@ -46,3 +47,46 @@ const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match) return NULL; } EXPORT_SYMBOL(x86_match_cpu); + +ssize_t arch_print_cpu_modalias(struct device *dev, + struct device_attribute *attr, + char *bufptr) +{ + int size = PAGE_SIZE; + int i, n; + char *buf = bufptr; + + n = snprintf(buf, size, "x86cpu:vendor:%04X:family:%04X:" + "model:%04X:feature:", + boot_cpu_data.x86_vendor, + boot_cpu_data.x86, + boot_cpu_data.x86_model); + size -= n; + buf += n; + size -= 2; + for (i = 0; i < NCAPINTS*32; i++) { + if (boot_cpu_has(i)) { + n = snprintf(buf, size, ",%04X", i); + if (n < 0) { + WARN(1, "x86 features overflow page\n"); + break; + } + size -= n; + buf += n; + } + } + *buf++ = ','; + *buf++ = '\n'; + return buf - bufptr; +} + +int arch_cpu_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (buf) { + arch_print_cpu_modalias(NULL, NULL, buf); + add_uevent_var(env, "MODALIAS=%s", buf); + kfree(buf); + } + return 0; +} diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c index 7c89880eefd0..a524353d93f2 100644 --- a/arch/x86/kernel/cpuid.c +++ b/arch/x86/kernel/cpuid.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include @@ -139,57 +138,13 @@ static const struct file_operations cpuid_fops = { .open = cpuid_open, }; -static ssize_t print_cpu_modalias(struct device *dev, - struct device_attribute *attr, - char *bufptr) -{ - int size = PAGE_SIZE; - int i, n; - char *buf = bufptr; - - n = snprintf(buf, size, "x86cpu:vendor:%04X:family:" - "%04X:model:%04X:feature:", - boot_cpu_data.x86_vendor, - boot_cpu_data.x86, - boot_cpu_data.x86_model); - size -= n; - buf += n; - size -= 2; - for (i = 0; i < NCAPINTS*32; i++) { - if (boot_cpu_has(i)) { - n = snprintf(buf, size, ",%04X", i); - if (n < 0) { - WARN(1, "x86 features overflow page\n"); - break; - } - size -= n; - buf += n; - } - } - *buf++ = ','; - *buf++ = '\n'; - return buf - bufptr; -} - -static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL); - static __cpuinit int cpuid_device_create(int cpu) { struct device *dev; - int err; dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL, "cpu%d", cpu); - if (IS_ERR(dev)) - return PTR_ERR(dev); - - err = device_create_file(dev, &dev_attr_modalias); - if (err) { - /* keep device around on error. attribute is optional. */ - err = 0; - } - - return 0; + return IS_ERR(dev) ? PTR_ERR(dev) : 0; } static void cpuid_device_destroy(int cpu) @@ -227,17 +182,6 @@ static char *cpuid_devnode(struct device *dev, umode_t *mode) return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt)); } -static int cpuid_dev_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (buf) { - print_cpu_modalias(NULL, NULL, buf); - add_uevent_var(env, "MODALIAS=%s", buf); - kfree(buf); - } - return 0; -} - static int __init cpuid_init(void) { int i, err = 0; @@ -256,7 +200,6 @@ static int __init cpuid_init(void) goto out_chrdev; } cpuid_class->devnode = cpuid_devnode; - cpuid_class->dev_uevent = cpuid_dev_uevent; for_each_online_cpu(i) { err = cpuid_device_create(i); if (err != 0) diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index db87e78d7459..2a0c670c281d 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "base.h" @@ -223,6 +224,9 @@ int __cpuinit register_cpu(struct cpu *cpu, int num) cpu->node_id = cpu_to_node(num); cpu->dev.id = num; cpu->dev.bus = &cpu_subsys; +#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE + cpu->dev.bus->uevent = arch_cpu_uevent; +#endif error = device_register(&cpu->dev); if (!error && cpu->hotpluggable) register_cpu_control(cpu); @@ -247,6 +251,10 @@ struct device *get_cpu_device(unsigned cpu) } EXPORT_SYMBOL_GPL(get_cpu_device); +#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE +static DEVICE_ATTR(modalias, 0444, arch_print_cpu_modalias, NULL); +#endif + static struct attribute *cpu_root_attrs[] = { #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE &dev_attr_probe.attr, @@ -257,6 +265,9 @@ static struct attribute *cpu_root_attrs[] = { &cpu_attrs[2].attr.attr, &dev_attr_kernel_max.attr, &dev_attr_offline.attr, +#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE + &dev_attr_modalias.attr, +#endif NULL }; diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 1f6587590a1a..6e53b4823d7f 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -44,6 +44,13 @@ extern ssize_t arch_cpu_release(const char *, size_t); #endif struct notifier_block; +#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE +extern int arch_cpu_uevent(struct device *dev, struct kobj_uevent_env *env); +extern ssize_t arch_print_cpu_modalias(struct device *dev, + struct device_attribute *attr, + char *bufptr); +#endif + /* * CPU notifier priorities. */ -- cgit From 8357929e6ae3661d5a3a7378a717f29873ea18c6 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 14 Nov 2011 13:06:21 -0800 Subject: ntp: Cleanup timex.h Move ntp_sycned to ntp.c and mark time_status as static. Also yank function declaration for non-existant function. CC: Thomas Gleixner CC: Eric Dumazet CC: Richard Cochran Signed-off-by: John Stultz --- include/linux/timex.h | 15 --------------- kernel/time/ntp.c | 13 ++++++++++++- 2 files changed, 12 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/timex.h b/include/linux/timex.h index aa60fe7b6ed6..92e01fc31ca6 100644 --- a/include/linux/timex.h +++ b/include/linux/timex.h @@ -234,23 +234,9 @@ struct timex { extern unsigned long tick_usec; /* USER_HZ period (usec) */ extern unsigned long tick_nsec; /* ACTHZ period (nsec) */ -/* - * phase-lock loop variables - */ -extern int time_status; /* clock synchronization status bits */ - extern void ntp_init(void); extern void ntp_clear(void); -/** - * ntp_synced - Returns 1 if the NTP status is not UNSYNC - * - */ -static inline int ntp_synced(void) -{ - return !(time_status & STA_UNSYNC); -} - /* Required to safely shift negative values */ #define shift_right(x, s) ({ \ __typeof__(x) __x = (x); \ @@ -267,7 +253,6 @@ static inline int ntp_synced(void) extern u64 tick_length; extern void second_overflow(void); -extern void update_ntp_one_tick(void); extern int do_adjtimex(struct timex *); extern void hardpps(const struct timespec *, const struct timespec *); diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index f6117a4c7cb8..ae7e13607d91 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -49,7 +49,7 @@ static struct hrtimer leap_timer; static int time_state = TIME_OK; /* clock status bits: */ -int time_status = STA_UNSYNC; +static int time_status = STA_UNSYNC; /* TAI offset (secs): */ static long time_tai; @@ -233,6 +233,17 @@ static inline void pps_fill_timex(struct timex *txc) #endif /* CONFIG_NTP_PPS */ + +/** + * ntp_synced - Returns 1 if the NTP status is not UNSYNC + * + */ +static inline int ntp_synced(void) +{ + return !(time_status & STA_UNSYNC); +} + + /* * NTP methods: */ -- cgit From ea7cf49a7633c2b70125f59b4e3553d9181cb15d Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 14 Nov 2011 13:18:07 -0800 Subject: ntp: Access tick_length variable via ntp_tick_length() Currently the NTP managed tick_length value is accessed globally, in preparations for locking cleanups, make sure it is accessed via a function and mark it as static. CC: Thomas Gleixner CC: Eric Dumazet CC: Richard Cochran Signed-off-by: John Stultz --- include/linux/timex.h | 2 +- kernel/time/ntp.c | 9 ++++++++- kernel/time/timekeeping.c | 6 +++--- 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/timex.h b/include/linux/timex.h index 92e01fc31ca6..b75e1864ed19 100644 --- a/include/linux/timex.h +++ b/include/linux/timex.h @@ -250,7 +250,7 @@ extern void ntp_clear(void); #define NTP_INTERVAL_LENGTH (NSEC_PER_SEC/NTP_INTERVAL_FREQ) /* Returns how long ticks are at present, in ns / 2^NTP_SCALE_SHIFT. */ -extern u64 tick_length; +extern u64 ntp_tick_length(void); extern void second_overflow(void); extern int do_adjtimex(struct timex *); diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index ae7e13607d91..f131ba62da62 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -28,7 +28,7 @@ unsigned long tick_usec = TICK_USEC; /* ACTHZ period (nsecs): */ unsigned long tick_nsec; -u64 tick_length; +static u64 tick_length; static u64 tick_length_base; static struct hrtimer leap_timer; @@ -360,6 +360,13 @@ void ntp_clear(void) pps_clear(); } + +u64 ntp_tick_length(void) +{ + return tick_length; +} + + /* * Leap second processing. If in leap-insert state at the end of the * day, the system clock is set back one second; if in leap-delete diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index f5d4d226defb..cdae24655c8d 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -811,7 +811,7 @@ static __always_inline int timekeeping_bigadjust(s64 error, s64 *interval, * Now calculate the error in (1 << look_ahead) ticks, but first * remove the single look ahead already included in the error. */ - tick_error = tick_length >> (timekeeper.ntp_error_shift + 1); + tick_error = ntp_tick_length() >> (timekeeper.ntp_error_shift + 1); tick_error -= timekeeper.xtime_interval >> 1; error = ((error - tick_error) >> look_ahead) + tick_error; @@ -994,7 +994,7 @@ static cycle_t logarithmic_accumulation(cycle_t offset, int shift) timekeeper.raw_time.tv_nsec = raw_nsecs; /* Accumulate error between NTP and clock interval */ - timekeeper.ntp_error += tick_length << shift; + timekeeper.ntp_error += ntp_tick_length() << shift; timekeeper.ntp_error -= (timekeeper.xtime_interval + timekeeper.xtime_remainder) << (timekeeper.ntp_error_shift + shift); @@ -1042,7 +1042,7 @@ static void update_wall_time(void) shift = ilog2(offset) - ilog2(timekeeper.cycle_interval); shift = max(0, shift); /* Bound shift to one less then what overflows tick_length */ - maxshift = (8*sizeof(tick_length) - (ilog2(tick_length)+1)) - 1; + maxshift = (64 - (ilog2(ntp_tick_length())+1)) - 1; shift = min(shift, maxshift); while (offset >= timekeeper.cycle_interval) { offset = logarithmic_accumulation(offset, shift); -- cgit From efb3040d481a1594592b1defb4526c406c7a4751 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 26 Jan 2012 13:32:15 +0100 Subject: jump_label: Add some documentation akpm figured we could do with a blub explaining what static_branch() is and why it lives... Grumpily-requested-by: Andrew Morton Cc: Jason Baron Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-h02wu6kabpoojxf03wke704k@git.kernel.org Signed-off-by: Ingo Molnar --- include/linux/jump_label.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'include') diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index 5ce8b140428f..f7c69580fea7 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h @@ -1,6 +1,38 @@ #ifndef _LINUX_JUMP_LABEL_H #define _LINUX_JUMP_LABEL_H +/* + * Jump label support + * + * Copyright (C) 2009-2012 Jason Baron + * Copyright (C) 2011-2012 Peter Zijlstra + * + * Jump labels provide an interface to generate dynamic branches using + * self-modifying code. Assuming toolchain and architecture support the result + * of a "if (static_branch(&key))" statement is a unconditional branch (which + * defaults to false - and the true block is placed out of line). + * + * However at runtime we can change the 'static' branch target using + * jump_label_{inc,dec}(). These function as a 'reference' count on the key + * object and for as long as there are references all branches referring to + * that particular key will point to the (out of line) true block. + * + * Since this relies on modifying code the jump_label_{inc,dec}() functions + * must be considered absolute slow paths (machine wide synchronization etc.). + * OTOH, since the affected branches are unconditional their runtime overhead + * will be absolutely minimal, esp. in the default (off) case where the total + * effect is a single NOP of appropriate size. The on case will patch in a jump + * to the out-of-line block. + * + * When the control is directly exposed to userspace it is prudent to delay the + * decrement to avoid high frequency code modifications which can (and do) + * cause significant performance degradation. Struct jump_label_key_deferred and + * jump_label_dec_deferred() provide for this. + * + * Lacking toolchain and or architecture support, it falls back to a simple + * conditional branch. + */ + #include #include #include -- cgit From 39be350127ec60a078edffe5b4915dafba4ba514 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 26 Jan 2012 12:44:34 +0100 Subject: sched, block: Unify cache detection The block layer has some code trying to determine if two CPUs share a cache, the scheduler has a similar function. Expose the function used by the scheduler and make the block layer use it, thereby removing the block layers usage of CONFIG_SCHED* and topology bits. Signed-off-by: Peter Zijlstra Acked-by: Jens Axboe Link: http://lkml.kernel.org/r/1327579450.2446.95.camel@twins --- block/blk-softirq.c | 16 ++++++++-------- block/blk.h | 16 ---------------- include/linux/sched.h | 8 ++++++++ kernel/sched/core.c | 6 +++--- 4 files changed, 19 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/block/blk-softirq.c b/block/blk-softirq.c index 1366a89d8e66..467c8de88642 100644 --- a/block/blk-softirq.c +++ b/block/blk-softirq.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "blk.h" @@ -103,9 +104,10 @@ static struct notifier_block __cpuinitdata blk_cpu_notifier = { void __blk_complete_request(struct request *req) { - int ccpu, cpu, group_cpu = NR_CPUS; + int ccpu, cpu; struct request_queue *q = req->q; unsigned long flags; + bool shared = false; BUG_ON(!q->softirq_done_fn); @@ -117,22 +119,20 @@ void __blk_complete_request(struct request *req) */ if (req->cpu != -1) { ccpu = req->cpu; - if (!test_bit(QUEUE_FLAG_SAME_FORCE, &q->queue_flags)) { - ccpu = blk_cpu_to_group(ccpu); - group_cpu = blk_cpu_to_group(cpu); - } + if (!test_bit(QUEUE_FLAG_SAME_FORCE, &q->queue_flags)) + shared = cpus_share_cache(cpu, ccpu); } else ccpu = cpu; /* - * If current CPU and requested CPU are in the same group, running - * softirq in current CPU. One might concern this is just like + * If current CPU and requested CPU share a cache, run the softirq on + * the current CPU. One might concern this is just like * QUEUE_FLAG_SAME_FORCE, but actually not. blk_complete_request() is * running in interrupt handler, and currently I/O controller doesn't * support multiple interrupts, so current CPU is unique actually. This * avoids IPI sending from current CPU to the first CPU of a group. */ - if (ccpu == cpu || ccpu == group_cpu) { + if (ccpu == cpu || shared) { struct list_head *list; do_local: list = &__get_cpu_var(blk_cpu_done); diff --git a/block/blk.h b/block/blk.h index 7efd772336de..df5b59acbdff 100644 --- a/block/blk.h +++ b/block/blk.h @@ -164,22 +164,6 @@ static inline int queue_congestion_off_threshold(struct request_queue *q) return q->nr_congestion_off; } -static inline int blk_cpu_to_group(int cpu) -{ - int group = NR_CPUS; -#ifdef CONFIG_SCHED_MC - const struct cpumask *mask = cpu_coregroup_mask(cpu); - group = cpumask_first(mask); -#elif defined(CONFIG_SCHED_SMT) - group = cpumask_first(topology_thread_cpumask(cpu)); -#else - return cpu; -#endif - if (likely(group < NR_CPUS)) - return group; - return cpu; -} - /* * Contribute to IO statistics IFF: * diff --git a/include/linux/sched.h b/include/linux/sched.h index 513f52459872..0e1959568836 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1052,6 +1052,8 @@ static inline int test_sd_parent(struct sched_domain *sd, int flag) unsigned long default_scale_freq_power(struct sched_domain *sd, int cpu); unsigned long default_scale_smt_power(struct sched_domain *sd, int cpu); +bool cpus_share_cache(int this_cpu, int that_cpu); + #else /* CONFIG_SMP */ struct sched_domain_attr; @@ -1061,6 +1063,12 @@ partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[], struct sched_domain_attr *dattr_new) { } + +static inline bool cpus_share_cache(int this_cpu, int that_cpu) +{ + return true; +} + #endif /* !CONFIG_SMP */ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 5255c9d2e053..d7c43227311d 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1507,7 +1507,7 @@ static int ttwu_activate_remote(struct task_struct *p, int wake_flags) } #endif /* __ARCH_WANT_INTERRUPTS_ON_CTXSW */ -static inline int ttwu_share_cache(int this_cpu, int that_cpu) +bool cpus_share_cache(int this_cpu, int that_cpu) { return per_cpu(sd_llc_id, this_cpu) == per_cpu(sd_llc_id, that_cpu); } @@ -1518,7 +1518,7 @@ static void ttwu_queue(struct task_struct *p, int cpu) struct rq *rq = cpu_rq(cpu); #if defined(CONFIG_SMP) - if (sched_feat(TTWU_QUEUE) && !ttwu_share_cache(smp_processor_id(), cpu)) { + if (sched_feat(TTWU_QUEUE) && !cpus_share_cache(smp_processor_id(), cpu)) { sched_clock_cpu(cpu); /* sync clocks x-cpu */ ttwu_queue_remote(p, cpu); return; @@ -5754,7 +5754,7 @@ static void destroy_sched_domains(struct sched_domain *sd, int cpu) * * Also keep a unique ID per domain (we use the first cpu number in * the cpumask of the domain), this allows us to quickly tell if - * two cpus are in the same cache domain, see ttwu_share_cache(). + * two cpus are in the same cache domain, see cpus_share_cache(). */ DEFINE_PER_CPU(struct sched_domain *, sd_llc); DEFINE_PER_CPU(int, sd_llc_id); -- cgit From 4ec4412e1e91f44a3dcb97b6c9172a13fc78bac9 Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Mon, 12 Dec 2011 20:21:08 +0100 Subject: sched: Ensure cpu_power periodic update With a lot of small tasks, the softirq sched is nearly never called when no_hz is enabled. In this case load_balance() is mainly called with the newly_idle mode which doesn't update the cpu_power. Add a next_update field which ensure a maximum update period when there is short activity. Having stale cpu_power information can skew the load-balancing decisions, this is cured by the guaranteed update. Signed-off-by: Vincent Guittot Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1323717668-2143-1-git-send-email-vincent.guittot@linaro.org --- include/linux/sched.h | 1 + kernel/sched/fair.c | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 0e1959568836..92313a3f6f77 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -905,6 +905,7 @@ struct sched_group_power { * single CPU. */ unsigned int power, power_orig; + unsigned long next_update; /* * Number of busy cpus in this group. */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 7c6414fc669d..8e77a6bd597b 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -215,6 +215,8 @@ calc_delta_mine(unsigned long delta_exec, unsigned long weight, const struct sched_class fair_sched_class; +static unsigned long __read_mostly max_load_balance_interval = HZ/10; + /************************************************************** * CFS operations on generic schedulable entities: */ @@ -3776,6 +3778,11 @@ void update_group_power(struct sched_domain *sd, int cpu) struct sched_domain *child = sd->child; struct sched_group *group, *sdg = sd->groups; unsigned long power; + unsigned long interval; + + interval = msecs_to_jiffies(sd->balance_interval); + interval = clamp(interval, 1UL, max_load_balance_interval); + sdg->sgp->next_update = jiffies + interval; if (!child) { update_cpu_power(sd, cpu); @@ -3883,12 +3890,15 @@ static inline void update_sg_lb_stats(struct sched_domain *sd, * domains. In the newly idle case, we will allow all the cpu's * to do the newly idle load balance. */ - if (idle != CPU_NEWLY_IDLE && local_group) { - if (balance_cpu != this_cpu) { - *balance = 0; - return; - } - update_group_power(sd, this_cpu); + if (local_group) { + if (idle != CPU_NEWLY_IDLE) { + if (balance_cpu != this_cpu) { + *balance = 0; + return; + } + update_group_power(sd, this_cpu); + } else if (time_after_eq(jiffies, group->sgp->next_update)) + update_group_power(sd, this_cpu); } /* Adjust by relative CPU power of the group */ @@ -4945,8 +4955,6 @@ static int __cpuinit sched_ilb_notifier(struct notifier_block *nfb, static DEFINE_SPINLOCK(balancing); -static unsigned long __read_mostly max_load_balance_interval = HZ/10; - /* * Scale the max load_balance interval with the number of CPUs in the system. * This trades load-balance latency on larger machines for less cross talk. -- cgit From c1288b1278d00169e12495eb53ad128e09560b69 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 19 Jan 2012 09:29:57 +0100 Subject: mac80211: make beacon filtering per virtual interface Due to firmware limitations, we may not be able to support beacon filtering on all virtual interfaces. To allow this in mac80211, introduce per-interface driver capability flags that the driver sets when an interface is added. Signed-off-by: Johannes Berg Acked-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/p54/main.c | 3 ++- drivers/net/wireless/rtlwifi/base.c | 1 - drivers/net/wireless/rtlwifi/core.c | 2 ++ drivers/net/wireless/wl1251/main.c | 3 ++- drivers/net/wireless/wl12xx/main.c | 3 ++- include/net/mac80211.h | 29 ++++++++++++++++++++--------- net/mac80211/debugfs.c | 2 -- net/mac80211/mlme.c | 2 +- 8 files changed, 29 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index af2ca1a9c7d3..40f4eb7da7b2 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -228,6 +228,8 @@ static int p54_add_interface(struct ieee80211_hw *dev, { struct p54_common *priv = dev->priv; + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + mutex_lock(&priv->conf_mutex); if (priv->mode != NL80211_IFTYPE_MONITOR) { mutex_unlock(&priv->conf_mutex); @@ -734,7 +736,6 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len) IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | - IEEE80211_HW_BEACON_FILTER | IEEE80211_HW_REPORTS_TX_ACK_STATUS; dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 6f80142580fd..ff3e393bc104 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -309,7 +309,6 @@ static void _rtl_init_mac80211(struct ieee80211_hw *hw) /* <5> set hw caps */ hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_RX_INCLUDES_FCS | - IEEE80211_HW_BEACON_FILTER | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_CONNECTION_MONITOR | /* IEEE80211_HW_SUPPORTS_CQM_RSSI | */ diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index d6e37e6a1d53..0ee01ab2e4f6 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -112,6 +112,8 @@ static int rtl_op_add_interface(struct ieee80211_hw *hw, struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); int err = 0; + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + if (mac->vif) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "vif has been set!! mac->vif = 0x%p\n", mac->vif); diff --git a/drivers/net/wireless/wl1251/main.c b/drivers/net/wireless/wl1251/main.c index ba3268ea81fe..86540db6f1a8 100644 --- a/drivers/net/wireless/wl1251/main.c +++ b/drivers/net/wireless/wl1251/main.c @@ -514,6 +514,8 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw, struct wl1251 *wl = hw->priv; int ret = 0; + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + wl1251_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", vif->type, vif->addr); @@ -1338,7 +1340,6 @@ int wl1251_init_ieee80211(struct wl1251 *wl) wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | - IEEE80211_HW_BEACON_FILTER | IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_SUPPORTS_CQM_RSSI; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index d5f55a149de5..afc5381f2870 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2060,6 +2060,8 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, u8 role_type; bool booted = false; + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", ieee80211_vif_type_p2p(vif), vif->addr); @@ -4898,7 +4900,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval; wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_BEACON_FILTER | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_HAS_RATE_CONTROL | diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d49928ba5d09..ae8db247bdeb 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -851,6 +851,16 @@ struct ieee80211_channel_switch { u8 count; }; +/** + * enum ieee80211_vif_flags - virtual interface flags + * + * @IEEE80211_VIF_BEACON_FILTER: the device performs beacon filtering + * on this virtual interface to avoid unnecessary CPU wakeups + */ +enum ieee80211_vif_flags { + IEEE80211_VIF_BEACON_FILTER = BIT(0), +}; + /** * struct ieee80211_vif - per-interface data * @@ -863,6 +873,10 @@ struct ieee80211_channel_switch { * @addr: address of this interface * @p2p: indicates whether this AP or STA interface is a p2p * interface, i.e. a GO or p2p-sta respectively + * @driver_flags: flags/capabilities the driver has for this interface, + * these need to be set (or cleared) when the interface is added + * or, if supported by the driver, the interface type is changed + * at runtime, mac80211 will never touch this field * @drv_priv: data area for driver use, will always be aligned to * sizeof(void *). */ @@ -871,6 +885,7 @@ struct ieee80211_vif { struct ieee80211_bss_conf bss_conf; u8 addr[ETH_ALEN]; bool p2p; + u32 driver_flags; /* must be last */ u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); }; @@ -1079,10 +1094,6 @@ enum sta_notify_cmd { * @IEEE80211_HW_MFP_CAPABLE: * Hardware supports management frame protection (MFP, IEEE 802.11w). * - * @IEEE80211_HW_BEACON_FILTER: - * Hardware supports dropping of irrelevant beacon frames to - * avoid waking up cpu. - * * @IEEE80211_HW_SUPPORTS_STATIC_SMPS: * Hardware supports static spatial multiplexing powersave, * ie. can turn off all but one chain even on HT connections @@ -1150,7 +1161,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_PS_NULLFUNC_STACK = 1<<11, IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12, IEEE80211_HW_MFP_CAPABLE = 1<<13, - IEEE80211_HW_BEACON_FILTER = 1<<14, + /* reuse bit 14 */ IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15, IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16, IEEE80211_HW_SUPPORTS_UAPSD = 1<<17, @@ -1446,8 +1457,8 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); * way the host will only receive beacons where some relevant information * (for example ERP protection or WMM settings) have changed. * - * Beacon filter support is advertised with the %IEEE80211_HW_BEACON_FILTER - * hardware capability. The driver needs to enable beacon filter support + * Beacon filter support is advertised with the %IEEE80211_VIF_BEACON_FILTER + * interface capability. The driver needs to enable beacon filter support * whenever power save is enabled, that is %IEEE80211_CONF_PS is set. When * power save is enabled, the stack will not check for beacon loss and the * driver needs to notify about loss of beacons with ieee80211_beacon_loss(). @@ -3316,7 +3327,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, * * @vif: &struct ieee80211_vif pointer from the add_interface callback. * - * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTER and + * When beacon filtering is enabled with %IEEE80211_VIF_BEACON_FILTER and * %IEEE80211_CONF_PS is set, the driver needs to inform whenever the * hardware is not receiving beacons with this function. */ @@ -3327,7 +3338,7 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif); * * @vif: &struct ieee80211_vif pointer from the add_interface callback. * - * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTER, and + * When beacon filtering is enabled with %IEEE80211_VIF_BEACON_FILTER, and * %IEEE80211_CONF_PS and %IEEE80211_HW_CONNECTION_MONITOR are set, the driver * needs to inform if the connection to the AP has been lost. * diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 90baea53e7c5..e8868dae1c01 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -247,8 +247,6 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf, sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n"); if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE) sf += snprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n"); - if (local->hw.flags & IEEE80211_HW_BEACON_FILTER) - sf += snprintf(buf + sf, mxln - sf, "BEACON_FILTER\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 95d3964fc080..b51eb49d8525 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -127,7 +127,7 @@ static void run_again(struct ieee80211_if_managed *ifmgd, void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata) { - if (sdata->local->hw.flags & IEEE80211_HW_BEACON_FILTER) + if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER) return; mod_timer(&sdata->u.mgd.bcn_mon_timer, -- cgit From ea086359a63bd0dd85c1d784d0425340649613fa Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 19 Jan 2012 09:29:58 +0100 Subject: mac80211: make CQM RSSI support per virtual interface Similar to the previous beacon filtering patch, make CQM RSSI support depend on the flags that the driver set for virtual interfaces. Signed-off-by: Johannes Berg Acked-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl1251/main.c | 6 +++--- drivers/net/wireless/wl12xx/main.c | 4 ++-- include/net/mac80211.h | 14 +++++++------- net/mac80211/cfg.c | 10 ++-------- net/mac80211/debugfs.c | 2 -- net/mac80211/mlme.c | 4 ++-- 6 files changed, 16 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/wl1251/main.c b/drivers/net/wireless/wl1251/main.c index 86540db6f1a8..41302c7b1ad0 100644 --- a/drivers/net/wireless/wl1251/main.c +++ b/drivers/net/wireless/wl1251/main.c @@ -514,7 +514,8 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw, struct wl1251 *wl = hw->priv; int ret = 0; - vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI; wl1251_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", vif->type, vif->addr); @@ -1340,8 +1341,7 @@ int wl1251_init_ieee80211(struct wl1251 *wl) wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | - IEEE80211_HW_SUPPORTS_UAPSD | - IEEE80211_HW_SUPPORTS_CQM_RSSI; + IEEE80211_HW_SUPPORTS_UAPSD; wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index afc5381f2870..f8748cedbae1 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2060,7 +2060,8 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, u8 role_type; bool booted = false; - vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI; wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", ieee80211_vif_type_p2p(vif), vif->addr); @@ -4904,7 +4905,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_HAS_RATE_CONTROL | IEEE80211_HW_CONNECTION_MONITOR | - IEEE80211_HW_SUPPORTS_CQM_RSSI | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_AP_LINK_PS | diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ae8db247bdeb..650185876846 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -856,9 +856,14 @@ struct ieee80211_channel_switch { * * @IEEE80211_VIF_BEACON_FILTER: the device performs beacon filtering * on this virtual interface to avoid unnecessary CPU wakeups + * @IEEE80211_VIF_SUPPORTS_CQM_RSSI: the device can do connection quality + * monitoring on this virtual interface -- i.e. it can monitor + * connection quality related parameters, such as the RSSI level and + * provide notifications if configured trigger levels are reached. */ enum ieee80211_vif_flags { IEEE80211_VIF_BEACON_FILTER = BIT(0), + IEEE80211_VIF_SUPPORTS_CQM_RSSI = BIT(1), }; /** @@ -1119,11 +1124,6 @@ enum sta_notify_cmd { * When this flag is set, signaling beacon-loss will cause an immediate * change to disassociated state. * - * @IEEE80211_HW_SUPPORTS_CQM_RSSI: - * Hardware can do connection quality monitoring - i.e. it can monitor - * connection quality related parameters, such as the RSSI level and - * provide notifications if configured trigger levels are reached. - * * @IEEE80211_HW_NEED_DTIM_PERIOD: * This device needs to know the DTIM period for the BSS before * associating. @@ -1167,7 +1167,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_SUPPORTS_UAPSD = 1<<17, IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18, IEEE80211_HW_CONNECTION_MONITOR = 1<<19, - IEEE80211_HW_SUPPORTS_CQM_RSSI = 1<<20, + /* reuse bit 20 */ IEEE80211_HW_SUPPORTS_PER_STA_GTK = 1<<21, IEEE80211_HW_AP_LINK_PS = 1<<22, IEEE80211_HW_TX_AMPDU_SETUP_IN_HW = 1<<23, @@ -3408,7 +3408,7 @@ void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif); * @rssi_event: the RSSI trigger event type * @gfp: context flags * - * When the %IEEE80211_HW_SUPPORTS_CQM_RSSI is set, and a connection quality + * When the %IEEE80211_VIF_SUPPORTS_CQM_RSSI is set, and a connection quality * monitoring is configured with an rssi threshold, the driver will inform * whenever the rssi level reaches the threshold. */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d86730fe75c8..74c9301681e5 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1873,7 +1873,6 @@ static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy, s32 rssi_thold, u32 rssi_hyst) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_vif *vif = &sdata->vif; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; @@ -1884,14 +1883,9 @@ static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy, bss_conf->cqm_rssi_thold = rssi_thold; bss_conf->cqm_rssi_hyst = rssi_hyst; - if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) { - if (sdata->vif.type != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - return 0; - } - /* tell the driver upon association, unless already associated */ - if (sdata->u.mgd.associated) + if (sdata->u.mgd.associated && + sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM); return 0; diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index e8868dae1c01..affe64be9092 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -257,8 +257,6 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf, sf += snprintf(buf + sf, mxln - sf, "REPORTS_TX_ACK_STATUS\n"); if (local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) sf += snprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n"); - if (local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) - sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_CQM_RSSI\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK) sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n"); if (local->hw.flags & IEEE80211_HW_AP_LINK_PS) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b51eb49d8525..de3268c7be1e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1043,7 +1043,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, bss_info_changed |= BSS_CHANGED_BSSID; /* Tell the driver to monitor connection quality (if supported) */ - if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) && + if (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI && bss_conf->cqm_rssi_thold) bss_info_changed |= BSS_CHANGED_CQM; @@ -1882,7 +1882,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (bss_conf->cqm_rssi_thold && ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT && - !(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) { + !(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) { int sig = ifmgd->ave_beacon_signal / 16; int last_event = ifmgd->last_cqm_event_signal; int thold = bss_conf->cqm_rssi_thold; -- cgit From 94f9065648a2645b28187b44ec7778c30cf58758 Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Sat, 21 Jan 2012 01:02:16 +0800 Subject: {nl,cfg,mac}80211: Add support of setting non-forwarding entity in Mesh A mesh node that joins the mesh network is by default a forwarding entity. This patch allows the mesh node to set as non-forwarding entity. Whenever dot11MeshForwarding is set to 0, the mesh node can prevent itself from forwarding the traffic which is not destined to him. Signed-off-by: Chun-Yeow Yeoh Signed-off-by: John W. Linville --- include/linux/nl80211.h | 4 ++++ include/net/cfg80211.h | 1 + net/mac80211/cfg.c | 2 ++ net/mac80211/debugfs_netdev.c | 1 + net/mac80211/mesh_hwmp.c | 2 +- net/wireless/mesh.c | 1 + net/wireless/nl80211.c | 5 +++++ 7 files changed, 15 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 0f5ff3739820..4f98fae13307 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -2104,6 +2104,9 @@ enum nl80211_mntr_flags { * TUs) during which a mesh STA can send only one Action frame containing a * PERR element. * + * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding + * or forwarding entity (default is TRUE - forwarding entity) + * * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use @@ -2128,6 +2131,7 @@ enum nl80211_meshconf_params { NL80211_MESHCONF_HWMP_RANN_INTERVAL, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, + NL80211_MESHCONF_FORWARDING, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 208a7b5f8d49..281899167d2d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -798,6 +798,7 @@ struct mesh_config { * mesh gate, but not necessarily using the gate announcement protocol. * Still keeping the same nomenclature to be in sync with the spec. */ bool dot11MeshGateAnnouncementProtocol; + bool dot11MeshForwarding; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 74c9301681e5..98460783c2d3 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1346,6 +1346,8 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, conf->dot11MeshHWMPRannInterval = nconf->dot11MeshHWMPRannInterval; } + if (_chg_mesh_attr(NL80211_MESHCONF_FORWARDING, mask)) + conf->dot11MeshForwarding = nconf->dot11MeshForwarding; return 0; } diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 176c08ffb13c..81d12e65a23c 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -422,6 +422,7 @@ IEEE80211_IF_FILE(dot11MeshGateAnnouncementProtocol, u.mesh.mshcfg.dot11MeshGateAnnouncementProtocol, DEC); IEEE80211_IF_FILE(dot11MeshHWMPRannInterval, u.mesh.mshcfg.dot11MeshHWMPRannInterval, DEC); +IEEE80211_IF_FILE(dot11MeshForwarding, u.mesh.mshcfg.dot11MeshForwarding, DEC); #endif diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 73abb7524b2c..cae407136ae0 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -575,7 +575,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, ifmsh->mshstats.dropped_frames_ttl++; } - if (forward) { + if (forward && ifmsh->mshcfg.dot11MeshForwarding) { u32 preq_id; u8 hopcount, flags; diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 8c550df13037..9d3e3b6bfcf4 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -55,6 +55,7 @@ const struct mesh_config default_mesh_config = { .min_discovery_timeout = MESH_MIN_DISCOVERY_TIMEOUT, .dot11MeshHWMPRannInterval = MESH_RANN_INTERVAL, .dot11MeshGateAnnouncementProtocol = false, + .dot11MeshForwarding = true, }; const struct mesh_setup default_mesh_setup = { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5cefd74ab47d..c42173f947ef 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3258,6 +3258,8 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, cur_params.dot11MeshHWMPRannInterval); NLA_PUT_U8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, cur_params.dot11MeshGateAnnouncementProtocol); + NLA_PUT_U8(msg, NL80211_MESHCONF_FORWARDING, + cur_params.dot11MeshForwarding); nla_nest_end(msg, pinfoattr); genlmsg_end(msg, hdr); return genlmsg_reply(msg, info); @@ -3289,6 +3291,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 }, [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 }, [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 }, + [NL80211_MESHCONF_FORWARDING] = { .type = NLA_U8 }, }; static const struct nla_policy @@ -3378,6 +3381,8 @@ do {\ dot11MeshGateAnnouncementProtocol, mask, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, nla_get_u8); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, + mask, NL80211_MESHCONF_FORWARDING, nla_get_u8); if (mask_out) *mask_out = mask; -- cgit From 15f0ebc23b7c68176ee3f9729d4a7f566cc6c311 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 21 Jan 2012 11:03:00 -0800 Subject: kernel-doc: fix new warnings in cfg80211.h Fix new kernel-doc warnings: Warning(include/net/cfg80211.h:1165): No description found for parameter 'channel_type' Warning(include/net/cfg80211.h:2090): No description found for parameter 'probe_resp_offload' Signed-off-by: Randy Dunlap Cc: Johannes Berg Cc: linux-wireless@vger.kernel.org Signed-off-by: John W. Linville --- include/net/cfg80211.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 281899167d2d..5bb3ed4f99f5 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1143,6 +1143,7 @@ struct cfg80211_disassoc_request { * @bssid: Fixed BSSID requested, maybe be %NULL, if set do not * search for IBSSs with a different BSSID. * @channel: The channel to use if no IBSS can be found to join. + * @channel_type: channel type (HT mode) * @channel_fixed: The channel should be fixed -- do not search for * IBSSs to join on other channels. * @ie: information element(s) to include in the beacon @@ -1981,6 +1982,11 @@ struct wiphy_wowlan_support { * configured as RX antennas. Antenna configuration commands will be * rejected unless this or @available_antennas_tx is set. * + * @probe_resp_offload: + * Bitmap of supported protocols for probe response offloading. + * See &enum nl80211_probe_resp_offload_support_attr. Only valid + * when the wiphy flag @WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD is set. + * * @max_remain_on_channel_duration: Maximum time a remain-on-channel operation * may request, if implemented. * -- cgit From 4991969a1027826c3db19dd3e600e145603e6928 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 27 Jan 2012 15:30:48 -0800 Subject: ipv6: Remove neigh argument from ndisc_send_redirect() Instead, compute it as-needed inside of that function using dst_neigh_lookup(). Signed-off-by: David S. Miller --- include/net/ndisc.h | 1 - net/ipv6/ip6_output.c | 2 +- net/ipv6/ndisc.c | 12 ++++++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/ndisc.h b/include/net/ndisc.h index e3133c23980e..6f9c25a76cd1 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -133,7 +133,6 @@ extern void ndisc_send_rs(struct net_device *dev, const struct in6_addr *daddr); extern void ndisc_send_redirect(struct sk_buff *skb, - struct neighbour *neigh, const struct in6_addr *target); extern int ndisc_mc_map(const struct in6_addr *addr, char *buf, diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index d97e07183ce9..604809b89b67 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -486,7 +486,7 @@ int ip6_forward(struct sk_buff *skb) and by source (inside ndisc_send_redirect) */ if (inet_peer_xrlim_allow(rt->rt6i_peer, 1*HZ)) - ndisc_send_redirect(skb, n, target); + ndisc_send_redirect(skb, target); } else { int addrtype = ipv6_addr_type(&hdr->saddr); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index c574ebce3fb5..8d817018c188 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1512,8 +1512,7 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) } } -void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, - const struct in6_addr *target) +void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) { struct net_device *dev = skb->dev; struct net *net = dev_net(dev); @@ -1571,6 +1570,13 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, goto release; if (dev->addr_len) { + struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target); + if (!neigh) { + ND_PRINTK2(KERN_WARNING + "ICMPv6 Redirect: no neigh for target address\n"); + goto release; + } + read_lock_bh(&neigh->lock); if (neigh->nud_state & NUD_VALID) { memcpy(ha_buf, neigh->ha, dev->addr_len); @@ -1579,6 +1585,8 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, len += ndisc_opt_addr_space(dev); } else read_unlock_bh(&neigh->lock); + + neigh_release(neigh); } rd_len = min_t(unsigned int, -- cgit From a46621a3a8f24557201a7ef62de151c812f8985c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 30 Jan 2012 15:22:06 -0500 Subject: net: Deinline __nlmsg_put and genlmsg_put. -7k code on i386 defconfig. text data bss dec hex filename 8455963 532732 1810804 10799499 a4c98b vmlinux.o.before 8448899 532732 1810804 10792435 a4adf3 vmlinux.o This change also removes commented-out copy of __nlmsg_put which was last touched in 2005 with "Enable once all users have been converted" comment on top. Changes in v2: rediffed against net-next. Signed-off-by: Denys Vlasenko Signed-off-by: David S. Miller --- include/linux/netlink.h | 18 ++---------------- include/net/genetlink.h | 31 ++----------------------------- include/net/netlink.h | 35 ----------------------------------- net/netlink/af_netlink.c | 18 ++++++++++++++++++ net/netlink/genetlink.c | 31 +++++++++++++++++++++++++++++++ 5 files changed, 53 insertions(+), 80 deletions(-) (limited to 'include') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 52e48959cfa1..a390e9d54827 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -237,22 +237,8 @@ struct netlink_notify { int protocol; }; -static __inline__ struct nlmsghdr * -__nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags) -{ - struct nlmsghdr *nlh; - int size = NLMSG_LENGTH(len); - - nlh = (struct nlmsghdr*)skb_put(skb, NLMSG_ALIGN(size)); - nlh->nlmsg_type = type; - nlh->nlmsg_len = size; - nlh->nlmsg_flags = flags; - nlh->nlmsg_pid = pid; - nlh->nlmsg_seq = seq; - if (!__builtin_constant_p(size) || NLMSG_ALIGN(size) - size != 0) - memset(NLMSG_DATA(nlh) + len, 0, NLMSG_ALIGN(size) - size); - return nlh; -} +struct nlmsghdr * +__nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags); #define NLMSG_NEW(skb, pid, seq, type, len, flags) \ ({ if (unlikely(skb_tailroom(skb) < (int)NLMSG_SPACE(len))) \ diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 7db32995ccd3..ccb68880abf5 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -131,35 +131,8 @@ extern void genl_unregister_mc_group(struct genl_family *family, extern void genl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group, struct nlmsghdr *nlh, gfp_t flags); -/** - * genlmsg_put - Add generic netlink header to netlink message - * @skb: socket buffer holding the message - * @pid: netlink pid the message is addressed to - * @seq: sequence number (usually the one of the sender) - * @family: generic netlink family - * @flags netlink message flags - * @cmd: generic netlink command - * - * Returns pointer to user specific header - */ -static inline void *genlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, - struct genl_family *family, int flags, u8 cmd) -{ - struct nlmsghdr *nlh; - struct genlmsghdr *hdr; - - nlh = nlmsg_put(skb, pid, seq, family->id, GENL_HDRLEN + - family->hdrsize, flags); - if (nlh == NULL) - return NULL; - - hdr = nlmsg_data(nlh); - hdr->cmd = cmd; - hdr->version = family->version; - hdr->reserved = 0; - - return (char *) hdr + GENL_HDRLEN; -} +void *genlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, + struct genl_family *family, int flags, u8 cmd); /** * genlmsg_nlhdr - Obtain netlink header from user specified header diff --git a/include/net/netlink.h b/include/net/netlink.h index cb1f3504687f..f394fe5d7641 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -441,41 +441,6 @@ static inline int nlmsg_report(const struct nlmsghdr *nlh) nla_for_each_attr(pos, nlmsg_attrdata(nlh, hdrlen), \ nlmsg_attrlen(nlh, hdrlen), rem) -#if 0 -/* FIXME: Enable once all users have been converted */ - -/** - * __nlmsg_put - Add a new netlink message to an skb - * @skb: socket buffer to store message in - * @pid: netlink process id - * @seq: sequence number of message - * @type: message type - * @payload: length of message payload - * @flags: message flags - * - * The caller is responsible to ensure that the skb provides enough - * tailroom for both the netlink header and payload. - */ -static inline struct nlmsghdr *__nlmsg_put(struct sk_buff *skb, u32 pid, - u32 seq, int type, int payload, - int flags) -{ - struct nlmsghdr *nlh; - - nlh = (struct nlmsghdr *) skb_put(skb, nlmsg_total_size(payload)); - nlh->nlmsg_type = type; - nlh->nlmsg_len = nlmsg_msg_size(payload); - nlh->nlmsg_flags = flags; - nlh->nlmsg_pid = pid; - nlh->nlmsg_seq = seq; - - memset((unsigned char *) nlmsg_data(nlh) + payload, 0, - nlmsg_padlen(payload)); - - return nlh; -} -#endif - /** * nlmsg_put - Add a new netlink message to an skb * @skb: socket buffer to store message in diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 629b06182f3f..4d751e3d4b4b 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1645,6 +1645,24 @@ static void netlink_destroy_callback(struct netlink_callback *cb) kfree(cb); } +struct nlmsghdr * +__nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags) +{ + struct nlmsghdr *nlh; + int size = NLMSG_LENGTH(len); + + nlh = (struct nlmsghdr*)skb_put(skb, NLMSG_ALIGN(size)); + nlh->nlmsg_type = type; + nlh->nlmsg_len = size; + nlh->nlmsg_flags = flags; + nlh->nlmsg_pid = pid; + nlh->nlmsg_seq = seq; + if (!__builtin_constant_p(size) || NLMSG_ALIGN(size) - size != 0) + memset(NLMSG_DATA(nlh) + len, 0, NLMSG_ALIGN(size) - size); + return nlh; +} +EXPORT_SYMBOL(__nlmsg_put); + /* * It looks a bit ugly. * It would be better to create kernel thread. diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index c29d2568c9e0..a1154717219e 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -498,6 +498,37 @@ int genl_unregister_family(struct genl_family *family) } EXPORT_SYMBOL(genl_unregister_family); +/** + * genlmsg_put - Add generic netlink header to netlink message + * @skb: socket buffer holding the message + * @pid: netlink pid the message is addressed to + * @seq: sequence number (usually the one of the sender) + * @family: generic netlink family + * @flags netlink message flags + * @cmd: generic netlink command + * + * Returns pointer to user specific header + */ +void *genlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, + struct genl_family *family, int flags, u8 cmd) +{ + struct nlmsghdr *nlh; + struct genlmsghdr *hdr; + + nlh = nlmsg_put(skb, pid, seq, family->id, GENL_HDRLEN + + family->hdrsize, flags); + if (nlh == NULL) + return NULL; + + hdr = nlmsg_data(nlh); + hdr->cmd = cmd; + hdr->version = family->version; + hdr->reserved = 0; + + return (char *) hdr + GENL_HDRLEN; +} +EXPORT_SYMBOL(genlmsg_put); + static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { struct genl_ops *ops; -- cgit From 24db78c05b1e3ccb5a78aedd17aa1008c91dab5a Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Sat, 28 Jan 2012 17:25:32 +0100 Subject: nl80211: add support for mcs masks Allow to set mcs masks through nl80211. We also allow to set MCS rates but no legacy rates (and vice versa). Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: John W. Linville --- include/linux/nl80211.h | 4 ++++ include/net/cfg80211.h | 3 +-- net/wireless/nl80211.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 65 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 4f98fae13307..ad56e21a9f10 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1475,6 +1475,7 @@ enum nl80211_attrs { #define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS #define NL80211_MAX_SUPP_RATES 32 +#define NL80211_MAX_SUPP_HT_RATES 77 #define NL80211_MAX_SUPP_REG_RULES 32 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 @@ -2405,12 +2406,15 @@ enum nl80211_key_attributes { * in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with * 1 = 500 kbps) but without the IE length restriction (at most * %NL80211_MAX_SUPP_RATES in a single array). + * @NL80211_TXRATE_MCS: HT (MCS) rates allowed for TX rate selection + * in an array of MCS numbers. * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute */ enum nl80211_tx_rate_attributes { __NL80211_TXRATE_INVALID, NL80211_TXRATE_LEGACY, + NL80211_TXRATE_MCS, /* keep last */ __NL80211_TXRATE_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 5bb3ed4f99f5..2964205332f4 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1232,8 +1232,7 @@ enum wiphy_params_flags { struct cfg80211_bitrate_mask { struct { u32 legacy; - /* TODO: add support for masking MCS rates; e.g.: */ - /* u8 mcs[IEEE80211_HT_MCS_MASK_LEN]; */ + u8 mcs[IEEE80211_HT_MCS_MASK_LEN]; } control[IEEE80211_NUM_BANDS]; }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c42173f947ef..c910b0750dc2 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5393,9 +5393,39 @@ static u32 rateset_to_mask(struct ieee80211_supported_band *sband, return mask; } +static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband, + u8 *rates, u8 rates_len, + u8 mcs[IEEE80211_HT_MCS_MASK_LEN]) +{ + u8 i; + + memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN); + + for (i = 0; i < rates_len; i++) { + int ridx, rbit; + + ridx = rates[i] / 8; + rbit = BIT(rates[i] % 8); + + /* check validity */ + if ((ridx < 0) || (ridx > IEEE80211_HT_MCS_MASK_LEN)) + return false; + + /* check availability */ + if (sband->ht_cap.mcs.rx_mask[ridx] & rbit) + mcs[ridx] |= rbit; + else + return false; + } + + return true; +} + static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, .len = NL80211_MAX_SUPP_RATES }, + [NL80211_TXRATE_MCS] = { .type = NLA_BINARY, + .len = NL80211_MAX_SUPP_HT_RATES }, }; static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, @@ -5421,12 +5451,20 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, sband = rdev->wiphy.bands[i]; mask.control[i].legacy = sband ? (1 << sband->n_bitrates) - 1 : 0; + if (sband) + memcpy(mask.control[i].mcs, + sband->ht_cap.mcs.rx_mask, + sizeof(mask.control[i].mcs)); + else + memset(mask.control[i].mcs, 0, + sizeof(mask.control[i].mcs)); } /* * The nested attribute uses enum nl80211_band as the index. This maps * directly to the enum ieee80211_band values used in cfg80211. */ + BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8); nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) { enum ieee80211_band band = nla_type(tx_rates); @@ -5442,7 +5480,28 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, sband, nla_data(tb[NL80211_TXRATE_LEGACY]), nla_len(tb[NL80211_TXRATE_LEGACY])); - if (mask.control[band].legacy == 0) + } + if (tb[NL80211_TXRATE_MCS]) { + if (!ht_rateset_to_mask( + sband, + nla_data(tb[NL80211_TXRATE_MCS]), + nla_len(tb[NL80211_TXRATE_MCS]), + mask.control[band].mcs)) + return -EINVAL; + } + + if (mask.control[band].legacy == 0) { + /* don't allow empty legacy rates if HT + * is not even supported. */ + if (!rdev->wiphy.bands[band]->ht_cap.ht_supported) + return -EINVAL; + + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) + if (mask.control[band].mcs[i]) + break; + + /* legacy and mcs rates may not be both empty */ + if (i == IEEE80211_HT_MCS_MASK_LEN) return -EINVAL; } } -- cgit From 19468413e8d98d44be8daf0acaf8d576dfc53fa2 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Sat, 28 Jan 2012 17:25:33 +0100 Subject: mac80211: add support for mcs masks * Handle MCS masks set by the user. * Match rates provided by the rate control algorithm to the mask set, also in HT mode, and switch back to legacy mode if necessary. * add debugfs files to observate the rate selection Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: John W. Linville --- include/net/mac80211.h | 1 + net/mac80211/cfg.c | 5 +- net/mac80211/debugfs_netdev.c | 34 ++++++++++++ net/mac80211/ieee80211_i.h | 1 + net/mac80211/iface.c | 7 +++ net/mac80211/rate.c | 124 +++++++++++++++++++++++++++++++++++++++--- net/mac80211/tx.c | 5 ++ 7 files changed, 168 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 650185876846..520eb4c5e5a2 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3551,6 +3551,7 @@ struct ieee80211_tx_rate_control { bool rts, short_preamble; u8 max_rate_idx; u32 rate_idx_mask; + u8 rate_idx_mcs_mask[IEEE80211_HT_MCS_MASK_LEN]; bool bss; }; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index dc7420441574..d15ba0d0de94 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1902,8 +1902,11 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, return ret; } - for (i = 0; i < IEEE80211_NUM_BANDS; i++) + for (i = 0; i < IEEE80211_NUM_BANDS; i++) { sdata->rc_rateidx_mask[i] = mask->control[i].legacy; + memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].mcs, + sizeof(mask->control[i].mcs)); + } return 0; } diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 81d12e65a23c..510ed1dab3c7 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -87,6 +87,21 @@ static ssize_t ieee80211_if_fmt_##name( \ #define IEEE80211_IF_FMT_SIZE(name, field) \ IEEE80211_IF_FMT(name, field, "%zd\n") +#define IEEE80211_IF_FMT_HEXARRAY(name, field) \ +static ssize_t ieee80211_if_fmt_##name( \ + const struct ieee80211_sub_if_data *sdata, \ + char *buf, int buflen) \ +{ \ + char *p = buf; \ + int i; \ + for (i = 0; i < sizeof(sdata->field); i++) { \ + p += scnprintf(p, buflen + buf - p, "%.2x ", \ + sdata->field[i]); \ + } \ + p += scnprintf(p, buflen + buf - p, "\n"); \ + return p - buf; \ +} + #define IEEE80211_IF_FMT_ATOMIC(name, field) \ static ssize_t ieee80211_if_fmt_##name( \ const struct ieee80211_sub_if_data *sdata, \ @@ -148,6 +163,11 @@ IEEE80211_IF_FILE(rc_rateidx_mask_2ghz, rc_rateidx_mask[IEEE80211_BAND_2GHZ], HEX); IEEE80211_IF_FILE(rc_rateidx_mask_5ghz, rc_rateidx_mask[IEEE80211_BAND_5GHZ], HEX); +IEEE80211_IF_FILE(rc_rateidx_mcs_mask_2ghz, + rc_rateidx_mcs_mask[IEEE80211_BAND_2GHZ], HEXARRAY); +IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz, + rc_rateidx_mcs_mask[IEEE80211_BAND_5GHZ], HEXARRAY); + IEEE80211_IF_FILE(flags, flags, HEX); IEEE80211_IF_FILE(state, state, LHEX); IEEE80211_IF_FILE(channel_type, vif.bss_conf.channel_type, DEC); @@ -442,6 +462,8 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(channel_type); DEBUGFS_ADD(rc_rateidx_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mask_5ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz); DEBUGFS_ADD(bssid); DEBUGFS_ADD(aid); @@ -459,6 +481,8 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(channel_type); DEBUGFS_ADD(rc_rateidx_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mask_5ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz); DEBUGFS_ADD(num_sta_authorized); DEBUGFS_ADD(num_sta_ps); @@ -469,6 +493,12 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata) static void add_ibss_files(struct ieee80211_sub_if_data *sdata) { + DEBUGFS_ADD(channel_type); + DEBUGFS_ADD(rc_rateidx_mask_2ghz); + DEBUGFS_ADD(rc_rateidx_mask_5ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz); + DEBUGFS_ADD_MODE(tsf, 0600); } @@ -480,6 +510,8 @@ static void add_wds_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(channel_type); DEBUGFS_ADD(rc_rateidx_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mask_5ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz); DEBUGFS_ADD(peer); } @@ -492,6 +524,8 @@ static void add_vlan_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(channel_type); DEBUGFS_ADD(rc_rateidx_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mask_5ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); + DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz); } static void add_monitor_files(struct ieee80211_sub_if_data *sdata) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ca6486b941b6..d47e8c110b16 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -646,6 +646,7 @@ struct ieee80211_sub_if_data { /* bitmap of allowed (non-MCS) rate indexes for rate control */ u32 rc_rateidx_mask[IEEE80211_NUM_BANDS]; + u8 rc_rateidx_mcs_mask[IEEE80211_NUM_BANDS][IEEE80211_HT_MCS_MASK_LEN]; union { struct ieee80211_if_ap ap; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 6b0d70e960d4..c33feede5dca 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1181,6 +1181,13 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, sband = local->hw.wiphy->bands[i]; sdata->rc_rateidx_mask[i] = sband ? (1 << sband->n_bitrates) - 1 : 0; + if (sband) + memcpy(sdata->rc_rateidx_mcs_mask[i], + sband->ht_cap.mcs.rx_mask, + sizeof(sdata->rc_rateidx_mcs_mask[i])); + else + memset(sdata->rc_rateidx_mcs_mask[i], 0, + sizeof(sdata->rc_rateidx_mcs_mask[i])); } /* setup type-dependent data */ diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index a21110aecd1a..3fef26d8898a 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -289,8 +289,8 @@ bool rate_control_send_low(struct ieee80211_sta *sta, } EXPORT_SYMBOL(rate_control_send_low); -static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, - int n_bitrates, u32 mask) +static bool rate_idx_match_legacy_mask(struct ieee80211_tx_rate *rate, + int n_bitrates, u32 mask) { int j; @@ -299,7 +299,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, if (mask & (1 << j)) { /* Okay, found a suitable rate. Use it. */ rate->idx = j; - return; + return true; } } @@ -308,6 +308,112 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, if (mask & (1 << j)) { /* Okay, found a suitable rate. Use it. */ rate->idx = j; + return true; + } + } + return false; +} + +static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate, + u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]) +{ + int i, j; + int ridx, rbit; + + ridx = rate->idx / 8; + rbit = rate->idx % 8; + + /* sanity check */ + if (ridx < 0 || ridx > IEEE80211_HT_MCS_MASK_LEN) + return false; + + /* See whether the selected rate or anything below it is allowed. */ + for (i = ridx; i >= 0; i--) { + for (j = rbit; j >= 0; j--) + if (mcs_mask[i] & BIT(j)) { + rate->idx = i * 8 + j; + return true; + } + rbit = 7; + } + + /* Try to find a higher rate that would be allowed */ + ridx = (rate->idx + 1) / 8; + rbit = (rate->idx + 1) % 8; + + for (i = ridx; i < IEEE80211_HT_MCS_MASK_LEN; i++) { + for (j = rbit; j < 8; j++) + if (mcs_mask[i] & BIT(j)) { + rate->idx = i * 8 + j; + return true; + } + rbit = 0; + } + return false; +} + + + +static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, + struct ieee80211_tx_rate_control *txrc, + u32 mask, + u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]) +{ + struct ieee80211_tx_rate alt_rate; + + /* handle HT rates */ + if (rate->flags & IEEE80211_TX_RC_MCS) { + if (rate_idx_match_mcs_mask(rate, mcs_mask)) + return; + + /* also try the legacy rates. */ + alt_rate.idx = 0; + /* keep protection flags */ + alt_rate.flags = rate->flags & + (IEEE80211_TX_RC_USE_RTS_CTS | + IEEE80211_TX_RC_USE_CTS_PROTECT | + IEEE80211_TX_RC_USE_SHORT_PREAMBLE); + alt_rate.count = rate->count; + if (rate_idx_match_legacy_mask(&alt_rate, + txrc->sband->n_bitrates, + mask)) { + *rate = alt_rate; + return; + } + } else { + struct sk_buff *skb = txrc->skb; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + __le16 fc; + + /* handle legacy rates */ + if (rate_idx_match_legacy_mask(rate, txrc->sband->n_bitrates, + mask)) + return; + + /* if HT BSS, and we handle a data frame, also try HT rates */ + if (txrc->bss_conf->channel_type == NL80211_CHAN_NO_HT) + return; + + fc = hdr->frame_control; + if (!ieee80211_is_data(fc)) + return; + + alt_rate.idx = 0; + /* keep protection flags */ + alt_rate.flags = rate->flags & + (IEEE80211_TX_RC_USE_RTS_CTS | + IEEE80211_TX_RC_USE_CTS_PROTECT | + IEEE80211_TX_RC_USE_SHORT_PREAMBLE); + alt_rate.count = rate->count; + + alt_rate.flags |= IEEE80211_TX_RC_MCS; + + if ((txrc->bss_conf->channel_type == NL80211_CHAN_HT40MINUS) || + (txrc->bss_conf->channel_type == NL80211_CHAN_HT40PLUS)) + alt_rate.flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + + if (rate_idx_match_mcs_mask(&alt_rate, mcs_mask)) { + *rate = alt_rate; return; } } @@ -331,6 +437,7 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb); int i; u32 mask; + u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]; if (sta) { ista = &sta->sta; @@ -354,10 +461,14 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, * the common case. */ mask = sdata->rc_rateidx_mask[info->band]; + memcpy(mcs_mask, sdata->rc_rateidx_mcs_mask[info->band], + sizeof(mcs_mask)); if (mask != (1 << txrc->sband->n_bitrates) - 1) { if (sta) { /* Filter out rates that the STA does not support */ mask &= sta->sta.supp_rates[info->band]; + for (i = 0; i < sizeof(mcs_mask); i++) + mcs_mask[i] &= sta->sta.ht_cap.mcs.rx_mask[i]; } /* * Make sure the rate index selected for each TX rate is @@ -368,11 +479,8 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, /* Skip invalid rates */ if (info->control.rates[i].idx < 0) break; - /* Rate masking supports only legacy rates for now */ - if (info->control.rates[i].flags & IEEE80211_TX_RC_MCS) - continue; - rate_idx_match_mask(&info->control.rates[i], - txrc->sband->n_bitrates, mask); + rate_idx_match_mask(&info->control.rates[i], txrc, + mask, mcs_mask); } } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index e05667cd5e76..1be0ca2b5936 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -635,6 +635,9 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) txrc.max_rate_idx = -1; else txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; + memcpy(txrc.rate_idx_mcs_mask, + tx->sdata->rc_rateidx_mcs_mask[tx->channel->band], + sizeof(txrc.rate_idx_mcs_mask)); txrc.bss = (tx->sdata->vif.type == NL80211_IFTYPE_AP || tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT || tx->sdata->vif.type == NL80211_IFTYPE_ADHOC); @@ -2431,6 +2434,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, txrc.max_rate_idx = -1; else txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; + memcpy(txrc.rate_idx_mcs_mask, sdata->rc_rateidx_mcs_mask[band], + sizeof(txrc.rate_idx_mcs_mask)); txrc.bss = true; rate_control_get_rate(sdata, NULL, &txrc); -- cgit From a2d91241a80ec9bbc5ab24b9a2c4d730b3fa5730 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 31 Jan 2012 01:04:42 +0000 Subject: tcp: md5: remove obsolete md5_add() method We no longer use md5_add() method from struct tcp_sock_af_ops Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 4 ---- net/ipv4/tcp_ipv4.c | 8 -------- net/ipv6/tcp_ipv6.c | 9 --------- 3 files changed, 21 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 0118ea999f67..3c903462adf5 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1462,10 +1462,6 @@ struct tcp_sock_af_ops { const struct sock *sk, const struct request_sock *req, const struct sk_buff *skb); - int (*md5_add) (struct sock *sk, - struct sock *addr_sk, - u8 *newkey, - u8 len); int (*md5_parse) (struct sock *sk, char __user *optval, int optlen); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 337ba4cca052..345e24928fa6 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -967,13 +967,6 @@ int tcp_v4_md5_do_add(struct sock *sk, __be32 addr, } EXPORT_SYMBOL(tcp_v4_md5_do_add); -static int tcp_v4_md5_add_func(struct sock *sk, struct sock *addr_sk, - u8 *newkey, u8 newkeylen) -{ - return tcp_v4_md5_do_add(sk, inet_sk(addr_sk)->inet_daddr, - newkey, newkeylen); -} - int tcp_v4_md5_do_del(struct sock *sk, __be32 addr) { struct tcp_sock *tp = tcp_sk(sk); @@ -1853,7 +1846,6 @@ EXPORT_SYMBOL(ipv4_specific); static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = { .md5_lookup = tcp_v4_md5_lookup, .calc_md5_hash = tcp_v4_md5_hash_skb, - .md5_add = tcp_v4_md5_add_func, .md5_parse = tcp_v4_parse_md5_keys, }; #endif diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 3edd05ae4388..f37769ea6375 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -626,13 +626,6 @@ static int tcp_v6_md5_do_add(struct sock *sk, const struct in6_addr *peer, return 0; } -static int tcp_v6_md5_add_func(struct sock *sk, struct sock *addr_sk, - u8 *newkey, __u8 newkeylen) -{ - return tcp_v6_md5_do_add(sk, &inet6_sk(addr_sk)->daddr, - newkey, newkeylen); -} - static int tcp_v6_md5_do_del(struct sock *sk, const struct in6_addr *peer) { struct tcp_sock *tp = tcp_sk(sk); @@ -1898,7 +1891,6 @@ static const struct inet_connection_sock_af_ops ipv6_specific = { static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = { .md5_lookup = tcp_v6_md5_lookup, .calc_md5_hash = tcp_v6_md5_hash_skb, - .md5_add = tcp_v6_md5_add_func, .md5_parse = tcp_v6_parse_md5_keys, }; #endif @@ -1930,7 +1922,6 @@ static const struct inet_connection_sock_af_ops ipv6_mapped = { static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = { .md5_lookup = tcp_v4_md5_lookup, .calc_md5_hash = tcp_v4_md5_hash_skb, - .md5_add = tcp_v6_md5_add_func, .md5_parse = tcp_v6_parse_md5_keys, }; #endif -- cgit From a915da9b69273815527ccb3789421cb7027b545b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 31 Jan 2012 05:18:33 +0000 Subject: tcp: md5: rcu conversion In order to be able to support proper RST messages for TCP MD5 flows, we need to allow access to MD5 keys without locking listener socket. This conversion is a nice cleanup, and shrinks size of timewait sockets by 80 bytes. IPv6 code reuses generic code found in IPv4 instead of duplicating it. Control path uses GFP_KERNEL allocations instead of GFP_ATOMIC. Signed-off-by: Eric Dumazet Cc: Shawn Lu Signed-off-by: David S. Miller --- include/linux/tcp.h | 3 +- include/net/tcp.h | 61 ++++++------- net/ipv4/tcp_ipv4.c | 227 +++++++++++++++++++---------------------------- net/ipv4/tcp_minisocks.c | 12 +-- net/ipv6/tcp_ipv6.c | 173 +++--------------------------------- 5 files changed, 141 insertions(+), 335 deletions(-) (limited to 'include') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 46a85c9e1f25..c2025f159641 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -486,8 +486,7 @@ struct tcp_timewait_sock { u32 tw_ts_recent; long tw_ts_recent_stamp; #ifdef CONFIG_TCP_MD5SIG - u16 tw_md5_keylen; - u8 tw_md5_key[TCP_MD5SIG_MAXKEYLEN]; + struct tcp_md5sig_key *tw_md5_key; #endif /* Few sockets in timewait have cookies; in that case, then this * object holds a reference to them (tw_cookie_values->kref). diff --git a/include/net/tcp.h b/include/net/tcp.h index 3c903462adf5..10ae4c7b6b4f 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1130,35 +1130,26 @@ static inline void tcp_clear_all_retrans_hints(struct tcp_sock *tp) /* MD5 Signature */ struct crypto_hash; +union tcp_md5_addr { + struct in_addr a4; +#if IS_ENABLED(CONFIG_IPV6) + struct in6_addr a6; +#endif +}; + /* - key database */ struct tcp_md5sig_key { - u8 *key; + struct hlist_node node; u8 keylen; -}; - -struct tcp4_md5sig_key { - struct tcp_md5sig_key base; - __be32 addr; -}; - -struct tcp6_md5sig_key { - struct tcp_md5sig_key base; -#if 0 - u32 scope_id; /* XXX */ -#endif - struct in6_addr addr; + u8 family; /* AF_INET or AF_INET6 */ + union tcp_md5_addr addr; + u8 key[TCP_MD5SIG_MAXKEYLEN]; + struct rcu_head rcu; }; /* - sock block */ struct tcp_md5sig_info { - struct tcp4_md5sig_key *keys4; -#if IS_ENABLED(CONFIG_IPV6) - struct tcp6_md5sig_key *keys6; - u32 entries6; - u32 alloced6; -#endif - u32 entries4; - u32 alloced4; + struct hlist_head head; }; /* - pseudo header */ @@ -1195,19 +1186,25 @@ extern int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key, const struct sock *sk, const struct request_sock *req, const struct sk_buff *skb); -extern struct tcp_md5sig_key * tcp_v4_md5_lookup(struct sock *sk, - struct sock *addr_sk); -extern int tcp_v4_md5_do_add(struct sock *sk, __be32 addr, u8 *newkey, - u8 newkeylen); -extern int tcp_v4_md5_do_del(struct sock *sk, __be32 addr); +extern int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, + int family, const u8 *newkey, + u8 newkeylen, gfp_t gfp); +extern int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, + int family); +extern struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk, + struct sock *addr_sk); #ifdef CONFIG_TCP_MD5SIG -#define tcp_twsk_md5_key(twsk) ((twsk)->tw_md5_keylen ? \ - &(struct tcp_md5sig_key) { \ - .key = (twsk)->tw_md5_key, \ - .keylen = (twsk)->tw_md5_keylen, \ - } : NULL) +extern struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk, + const union tcp_md5_addr *addr, int family); +#define tcp_twsk_md5_key(twsk) ((twsk)->tw_md5_key) #else +static inline struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk, + const union tcp_md5_addr *addr, + int family) +{ + return NULL; +} #define tcp_twsk_md5_key(twsk) NULL #endif diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 345e24928fa6..1d5fd82c5c08 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -90,16 +90,8 @@ EXPORT_SYMBOL(sysctl_tcp_low_latency); #ifdef CONFIG_TCP_MD5SIG -static struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, - __be32 addr); -static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key, +static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key, __be32 daddr, __be32 saddr, const struct tcphdr *th); -#else -static inline -struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr) -{ - return NULL; -} #endif struct inet_hashinfo tcp_hashinfo; @@ -631,7 +623,9 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) arg.iov[0].iov_len = sizeof(rep.th); #ifdef CONFIG_TCP_MD5SIG - key = sk ? tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->saddr) : NULL; + key = sk ? tcp_md5_do_lookup(sk, + (union tcp_md5_addr *)&ip_hdr(skb)->saddr, + AF_INET) : NULL; if (key) { rep.opt[0] = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | @@ -759,7 +753,8 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd, req->ts_recent, 0, - tcp_v4_md5_do_lookup(sk, ip_hdr(skb)->daddr), + tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->daddr, + AF_INET), inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0, ip_hdr(skb)->tos); } @@ -876,146 +871,124 @@ static struct ip_options_rcu *tcp_v4_save_options(struct sock *sk, */ /* Find the Key structure for an address. */ -static struct tcp_md5sig_key * - tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr) +struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk, + const union tcp_md5_addr *addr, + int family) { struct tcp_sock *tp = tcp_sk(sk); - int i; + struct tcp_md5sig_key *key; + struct hlist_node *pos; + unsigned int size = sizeof(struct in_addr); - if (!tp->md5sig_info || !tp->md5sig_info->entries4) + if (!tp->md5sig_info) return NULL; - for (i = 0; i < tp->md5sig_info->entries4; i++) { - if (tp->md5sig_info->keys4[i].addr == addr) - return &tp->md5sig_info->keys4[i].base; +#if IS_ENABLED(CONFIG_IPV6) + if (family == AF_INET6) + size = sizeof(struct in6_addr); +#endif + hlist_for_each_entry_rcu(key, pos, &tp->md5sig_info->head, node) { + if (key->family != family) + continue; + if (!memcmp(&key->addr, addr, size)) + return key; } return NULL; } +EXPORT_SYMBOL(tcp_md5_do_lookup); struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk, struct sock *addr_sk) { - return tcp_v4_md5_do_lookup(sk, inet_sk(addr_sk)->inet_daddr); + union tcp_md5_addr *addr; + + addr = (union tcp_md5_addr *)&inet_sk(addr_sk)->inet_daddr; + return tcp_md5_do_lookup(sk, addr, AF_INET); } EXPORT_SYMBOL(tcp_v4_md5_lookup); static struct tcp_md5sig_key *tcp_v4_reqsk_md5_lookup(struct sock *sk, struct request_sock *req) { - return tcp_v4_md5_do_lookup(sk, inet_rsk(req)->rmt_addr); + union tcp_md5_addr *addr; + + addr = (union tcp_md5_addr *)&inet_rsk(req)->rmt_addr; + return tcp_md5_do_lookup(sk, addr, AF_INET); } /* This can be called on a newly created socket, from other files */ -int tcp_v4_md5_do_add(struct sock *sk, __be32 addr, - u8 *newkey, u8 newkeylen) +int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, + int family, const u8 *newkey, u8 newkeylen, gfp_t gfp) { /* Add Key to the list */ struct tcp_md5sig_key *key; struct tcp_sock *tp = tcp_sk(sk); - struct tcp4_md5sig_key *keys; + struct tcp_md5sig_info *md5sig; - key = tcp_v4_md5_do_lookup(sk, addr); + key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET); if (key) { /* Pre-existing entry - just update that one. */ - kfree(key->key); - key->key = newkey; + memcpy(key->key, newkey, newkeylen); key->keylen = newkeylen; - } else { - struct tcp_md5sig_info *md5sig; - - if (!tp->md5sig_info) { - tp->md5sig_info = kzalloc(sizeof(*tp->md5sig_info), - GFP_ATOMIC); - if (!tp->md5sig_info) { - kfree(newkey); - return -ENOMEM; - } - sk_nocaps_add(sk, NETIF_F_GSO_MASK); - } + return 0; + } - md5sig = tp->md5sig_info; - if (md5sig->entries4 == 0 && - tcp_alloc_md5sig_pool(sk) == NULL) { - kfree(newkey); + md5sig = tp->md5sig_info; + if (!md5sig) { + md5sig = kmalloc(sizeof(*md5sig), gfp); + if (!md5sig) return -ENOMEM; - } - - if (md5sig->alloced4 == md5sig->entries4) { - keys = kmalloc((sizeof(*keys) * - (md5sig->entries4 + 1)), GFP_ATOMIC); - if (!keys) { - kfree(newkey); - if (md5sig->entries4 == 0) - tcp_free_md5sig_pool(); - return -ENOMEM; - } - if (md5sig->entries4) - memcpy(keys, md5sig->keys4, - sizeof(*keys) * md5sig->entries4); + sk_nocaps_add(sk, NETIF_F_GSO_MASK); + INIT_HLIST_HEAD(&md5sig->head); + tp->md5sig_info = md5sig; + } - /* Free old key list, and reference new one */ - kfree(md5sig->keys4); - md5sig->keys4 = keys; - md5sig->alloced4++; - } - md5sig->entries4++; - md5sig->keys4[md5sig->entries4 - 1].addr = addr; - md5sig->keys4[md5sig->entries4 - 1].base.key = newkey; - md5sig->keys4[md5sig->entries4 - 1].base.keylen = newkeylen; + key = kmalloc(sizeof(*key), gfp); + if (!key) + return -ENOMEM; + if (hlist_empty(&md5sig->head) && !tcp_alloc_md5sig_pool(sk)) { + kfree(key); + return -ENOMEM; } + + memcpy(key->key, newkey, newkeylen); + key->keylen = newkeylen; + key->family = family; + memcpy(&key->addr, addr, + (family == AF_INET6) ? sizeof(struct in6_addr) : + sizeof(struct in_addr)); + hlist_add_head_rcu(&key->node, &md5sig->head); return 0; } -EXPORT_SYMBOL(tcp_v4_md5_do_add); +EXPORT_SYMBOL(tcp_md5_do_add); -int tcp_v4_md5_do_del(struct sock *sk, __be32 addr) +int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family) { struct tcp_sock *tp = tcp_sk(sk); - int i; - - for (i = 0; i < tp->md5sig_info->entries4; i++) { - if (tp->md5sig_info->keys4[i].addr == addr) { - /* Free the key */ - kfree(tp->md5sig_info->keys4[i].base.key); - tp->md5sig_info->entries4--; - - if (tp->md5sig_info->entries4 == 0) { - kfree(tp->md5sig_info->keys4); - tp->md5sig_info->keys4 = NULL; - tp->md5sig_info->alloced4 = 0; - tcp_free_md5sig_pool(); - } else if (tp->md5sig_info->entries4 != i) { - /* Need to do some manipulation */ - memmove(&tp->md5sig_info->keys4[i], - &tp->md5sig_info->keys4[i+1], - (tp->md5sig_info->entries4 - i) * - sizeof(struct tcp4_md5sig_key)); - } - return 0; - } - } - return -ENOENT; + struct tcp_md5sig_key *key; + + key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET); + if (!key) + return -ENOENT; + hlist_del_rcu(&key->node); + kfree_rcu(key, rcu); + if (hlist_empty(&tp->md5sig_info->head)) + tcp_free_md5sig_pool(); + return 0; } -EXPORT_SYMBOL(tcp_v4_md5_do_del); +EXPORT_SYMBOL(tcp_md5_do_del); -static void tcp_v4_clear_md5_list(struct sock *sk) +void tcp_clear_md5_list(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); + struct tcp_md5sig_key *key; + struct hlist_node *pos, *n; - /* Free each key, then the set of key keys, - * the crypto element, and then decrement our - * hold on the last resort crypto. - */ - if (tp->md5sig_info->entries4) { - int i; - for (i = 0; i < tp->md5sig_info->entries4; i++) - kfree(tp->md5sig_info->keys4[i].base.key); - tp->md5sig_info->entries4 = 0; + if (!hlist_empty(&tp->md5sig_info->head)) tcp_free_md5sig_pool(); - } - if (tp->md5sig_info->keys4) { - kfree(tp->md5sig_info->keys4); - tp->md5sig_info->keys4 = NULL; - tp->md5sig_info->alloced4 = 0; + hlist_for_each_entry_safe(key, pos, n, &tp->md5sig_info->head, node) { + hlist_del_rcu(&key->node); + kfree_rcu(key, rcu); } } @@ -1024,7 +997,6 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval, { struct tcp_md5sig cmd; struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.tcpm_addr; - u8 *newkey; if (optlen < sizeof(cmd)) return -EINVAL; @@ -1038,29 +1010,16 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval, if (!cmd.tcpm_key || !cmd.tcpm_keylen) { if (!tcp_sk(sk)->md5sig_info) return -ENOENT; - return tcp_v4_md5_do_del(sk, sin->sin_addr.s_addr); + return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr, + AF_INET); } if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) return -EINVAL; - if (!tcp_sk(sk)->md5sig_info) { - struct tcp_sock *tp = tcp_sk(sk); - struct tcp_md5sig_info *p; - - p = kzalloc(sizeof(*p), sk->sk_allocation); - if (!p) - return -EINVAL; - - tp->md5sig_info = p; - sk_nocaps_add(sk, NETIF_F_GSO_MASK); - } - - newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, sk->sk_allocation); - if (!newkey) - return -ENOMEM; - return tcp_v4_md5_do_add(sk, sin->sin_addr.s_addr, - newkey, cmd.tcpm_keylen); + return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr, + AF_INET, cmd.tcpm_key, cmd.tcpm_keylen, + GFP_KERNEL); } static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp, @@ -1086,7 +1045,7 @@ static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp, return crypto_hash_update(&hp->md5_desc, &sg, sizeof(*bp)); } -static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key, +static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key, __be32 daddr, __be32 saddr, const struct tcphdr *th) { struct tcp_md5sig_pool *hp; @@ -1186,7 +1145,8 @@ static int tcp_v4_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb) int genhash; unsigned char newhash[16]; - hash_expected = tcp_v4_md5_do_lookup(sk, iph->saddr); + hash_expected = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&iph->saddr, + AF_INET); hash_location = tcp_parse_md5sig_option(th); /* We've parsed the options - do we have a hash? */ @@ -1474,7 +1434,8 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, #ifdef CONFIG_TCP_MD5SIG /* Copy over the MD5 key from the original socket */ - key = tcp_v4_md5_do_lookup(sk, newinet->inet_daddr); + key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&newinet->inet_daddr, + AF_INET); if (key != NULL) { /* * We're using one, so create a matching key @@ -1482,10 +1443,8 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, * memory, then we end up not copying the key * across. Shucks. */ - char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC); - if (newkey != NULL) - tcp_v4_md5_do_add(newsk, newinet->inet_daddr, - newkey, key->keylen); + tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newinet->inet_daddr, + AF_INET, key->key, key->keylen, GFP_ATOMIC); sk_nocaps_add(newsk, NETIF_F_GSO_MASK); } #endif @@ -1934,7 +1893,7 @@ void tcp_v4_destroy_sock(struct sock *sk) #ifdef CONFIG_TCP_MD5SIG /* Clean up the MD5 key list, if any */ if (tp->md5sig_info) { - tcp_v4_clear_md5_list(sk); + tcp_clear_md5_list(sk); kfree(tp->md5sig_info); tp->md5sig_info = NULL; } diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 550e755747e0..3cabafb5cdd1 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -359,13 +359,11 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) */ do { struct tcp_md5sig_key *key; - memset(tcptw->tw_md5_key, 0, sizeof(tcptw->tw_md5_key)); - tcptw->tw_md5_keylen = 0; + tcptw->tw_md5_key = NULL; key = tp->af_specific->md5_lookup(sk, sk); if (key != NULL) { - memcpy(&tcptw->tw_md5_key, key->key, key->keylen); - tcptw->tw_md5_keylen = key->keylen; - if (tcp_alloc_md5sig_pool(sk) == NULL) + tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC); + if (tcptw->tw_md5_key && tcp_alloc_md5sig_pool(sk) == NULL) BUG(); } } while (0); @@ -405,8 +403,10 @@ void tcp_twsk_destructor(struct sock *sk) { #ifdef CONFIG_TCP_MD5SIG struct tcp_timewait_sock *twsk = tcp_twsk(sk); - if (twsk->tw_md5_keylen) + if (twsk->tw_md5_key) { tcp_free_md5sig_pool(); + kfree_rcu(twsk->tw_md5_key, rcu); + } #endif } EXPORT_SYMBOL_GPL(tcp_twsk_destructor); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index f37769ea6375..bec41f9a6413 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -540,19 +540,7 @@ static void tcp_v6_reqsk_destructor(struct request_sock *req) static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk, const struct in6_addr *addr) { - struct tcp_sock *tp = tcp_sk(sk); - int i; - - BUG_ON(tp == NULL); - - if (!tp->md5sig_info || !tp->md5sig_info->entries6) - return NULL; - - for (i = 0; i < tp->md5sig_info->entries6; i++) { - if (ipv6_addr_equal(&tp->md5sig_info->keys6[i].addr, addr)) - return &tp->md5sig_info->keys6[i].base; - } - return NULL; + return tcp_md5_do_lookup(sk, (union tcp_md5_addr *)addr, AF_INET6); } static struct tcp_md5sig_key *tcp_v6_md5_lookup(struct sock *sk, @@ -567,129 +555,11 @@ static struct tcp_md5sig_key *tcp_v6_reqsk_md5_lookup(struct sock *sk, return tcp_v6_md5_do_lookup(sk, &inet6_rsk(req)->rmt_addr); } -static int tcp_v6_md5_do_add(struct sock *sk, const struct in6_addr *peer, - char *newkey, u8 newkeylen) -{ - /* Add key to the list */ - struct tcp_md5sig_key *key; - struct tcp_sock *tp = tcp_sk(sk); - struct tcp6_md5sig_key *keys; - - key = tcp_v6_md5_do_lookup(sk, peer); - if (key) { - /* modify existing entry - just update that one */ - kfree(key->key); - key->key = newkey; - key->keylen = newkeylen; - } else { - /* reallocate new list if current one is full. */ - if (!tp->md5sig_info) { - tp->md5sig_info = kzalloc(sizeof(*tp->md5sig_info), GFP_ATOMIC); - if (!tp->md5sig_info) { - kfree(newkey); - return -ENOMEM; - } - sk_nocaps_add(sk, NETIF_F_GSO_MASK); - } - if (tp->md5sig_info->entries6 == 0 && - tcp_alloc_md5sig_pool(sk) == NULL) { - kfree(newkey); - return -ENOMEM; - } - if (tp->md5sig_info->alloced6 == tp->md5sig_info->entries6) { - keys = kmalloc((sizeof (tp->md5sig_info->keys6[0]) * - (tp->md5sig_info->entries6 + 1)), GFP_ATOMIC); - - if (!keys) { - kfree(newkey); - if (tp->md5sig_info->entries6 == 0) - tcp_free_md5sig_pool(); - return -ENOMEM; - } - - if (tp->md5sig_info->entries6) - memmove(keys, tp->md5sig_info->keys6, - (sizeof (tp->md5sig_info->keys6[0]) * - tp->md5sig_info->entries6)); - - kfree(tp->md5sig_info->keys6); - tp->md5sig_info->keys6 = keys; - tp->md5sig_info->alloced6++; - } - - tp->md5sig_info->keys6[tp->md5sig_info->entries6].addr = *peer; - tp->md5sig_info->keys6[tp->md5sig_info->entries6].base.key = newkey; - tp->md5sig_info->keys6[tp->md5sig_info->entries6].base.keylen = newkeylen; - - tp->md5sig_info->entries6++; - } - return 0; -} - -static int tcp_v6_md5_do_del(struct sock *sk, const struct in6_addr *peer) -{ - struct tcp_sock *tp = tcp_sk(sk); - int i; - - for (i = 0; i < tp->md5sig_info->entries6; i++) { - if (ipv6_addr_equal(&tp->md5sig_info->keys6[i].addr, peer)) { - /* Free the key */ - kfree(tp->md5sig_info->keys6[i].base.key); - tp->md5sig_info->entries6--; - - if (tp->md5sig_info->entries6 == 0) { - kfree(tp->md5sig_info->keys6); - tp->md5sig_info->keys6 = NULL; - tp->md5sig_info->alloced6 = 0; - tcp_free_md5sig_pool(); - } else { - /* shrink the database */ - if (tp->md5sig_info->entries6 != i) - memmove(&tp->md5sig_info->keys6[i], - &tp->md5sig_info->keys6[i+1], - (tp->md5sig_info->entries6 - i) - * sizeof (tp->md5sig_info->keys6[0])); - } - return 0; - } - } - return -ENOENT; -} - -static void tcp_v6_clear_md5_list (struct sock *sk) -{ - struct tcp_sock *tp = tcp_sk(sk); - int i; - - if (tp->md5sig_info->entries6) { - for (i = 0; i < tp->md5sig_info->entries6; i++) - kfree(tp->md5sig_info->keys6[i].base.key); - tp->md5sig_info->entries6 = 0; - tcp_free_md5sig_pool(); - } - - kfree(tp->md5sig_info->keys6); - tp->md5sig_info->keys6 = NULL; - tp->md5sig_info->alloced6 = 0; - - if (tp->md5sig_info->entries4) { - for (i = 0; i < tp->md5sig_info->entries4; i++) - kfree(tp->md5sig_info->keys4[i].base.key); - tp->md5sig_info->entries4 = 0; - tcp_free_md5sig_pool(); - } - - kfree(tp->md5sig_info->keys4); - tp->md5sig_info->keys4 = NULL; - tp->md5sig_info->alloced4 = 0; -} - static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval, int optlen) { struct tcp_md5sig cmd; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr; - u8 *newkey; if (optlen < sizeof(cmd)) return -EINVAL; @@ -704,33 +574,21 @@ static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval, if (!tcp_sk(sk)->md5sig_info) return -ENOENT; if (ipv6_addr_v4mapped(&sin6->sin6_addr)) - return tcp_v4_md5_do_del(sk, sin6->sin6_addr.s6_addr32[3]); - return tcp_v6_md5_do_del(sk, &sin6->sin6_addr); + return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], + AF_INET); + return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr, + AF_INET6); } if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) return -EINVAL; - if (!tcp_sk(sk)->md5sig_info) { - struct tcp_sock *tp = tcp_sk(sk); - struct tcp_md5sig_info *p; - - p = kzalloc(sizeof(struct tcp_md5sig_info), GFP_KERNEL); - if (!p) - return -ENOMEM; - - tp->md5sig_info = p; - sk_nocaps_add(sk, NETIF_F_GSO_MASK); - } + if (ipv6_addr_v4mapped(&sin6->sin6_addr)) + return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], + AF_INET, cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); - newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); - if (!newkey) - return -ENOMEM; - if (ipv6_addr_v4mapped(&sin6->sin6_addr)) { - return tcp_v4_md5_do_add(sk, sin6->sin6_addr.s6_addr32[3], - newkey, cmd.tcpm_keylen); - } - return tcp_v6_md5_do_add(sk, &sin6->sin6_addr, newkey, cmd.tcpm_keylen); + return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr, + AF_INET6, cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); } static int tcp_v6_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp, @@ -1503,10 +1361,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, * memory, then we end up not copying the key * across. Shucks. */ - char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC); - if (newkey != NULL) - tcp_v6_md5_do_add(newsk, &newnp->daddr, - newkey, key->keylen); + tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newnp->daddr, + AF_INET6, key->key, key->keylen, GFP_ATOMIC); } #endif @@ -1995,11 +1851,6 @@ static int tcp_v6_init_sock(struct sock *sk) static void tcp_v6_destroy_sock(struct sock *sk) { -#ifdef CONFIG_TCP_MD5SIG - /* Clean up the MD5 key list */ - if (tcp_sk(sk)->md5sig_info) - tcp_v6_clear_md5_list(sk); -#endif tcp_v4_destroy_sock(sk); inet6_destroy_sock(sk); } -- cgit From a8afca032998850ec63e83d555cdcf0eb5680cd6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 31 Jan 2012 18:45:40 +0000 Subject: tcp: md5: protects md5sig_info with RCU This patch makes sure we use appropriate memory barriers before publishing tp->md5sig_info, allowing tcp_md5_do_lookup() being used from tcp_v4_send_reset() without holding socket lock (upcoming patch from Shawn Lu) Note we also need to respect rcu grace period before its freeing, since we can free socket without this grace period thanks to SLAB_DESTROY_BY_RCU Signed-off-by: Eric Dumazet Cc: Shawn Lu Signed-off-by: David S. Miller --- include/linux/tcp.h | 2 +- include/net/tcp.h | 1 + net/ipv4/tcp_ipv4.c | 32 ++++++++++++++++++++------------ net/ipv6/tcp_ipv6.c | 2 -- 4 files changed, 22 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index c2025f159641..115389e9b945 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -463,7 +463,7 @@ struct tcp_sock { const struct tcp_sock_af_ops *af_specific; /* TCP MD5 Signature Option information */ - struct tcp_md5sig_info *md5sig_info; + struct tcp_md5sig_info __rcu *md5sig_info; #endif /* When the cookie options are generated and exchanged, then this diff --git a/include/net/tcp.h b/include/net/tcp.h index 10ae4c7b6b4f..78880ba0f560 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1150,6 +1150,7 @@ struct tcp_md5sig_key { /* - sock block */ struct tcp_md5sig_info { struct hlist_head head; + struct rcu_head rcu; }; /* - pseudo header */ diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index da5d3226771b..567cca9b30df 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -879,14 +879,18 @@ struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk, struct tcp_md5sig_key *key; struct hlist_node *pos; unsigned int size = sizeof(struct in_addr); + struct tcp_md5sig_info *md5sig; - if (!tp->md5sig_info) + /* caller either holds rcu_read_lock() or socket lock */ + md5sig = rcu_dereference_check(tp->md5sig_info, + sock_owned_by_user(sk)); + if (!md5sig) return NULL; #if IS_ENABLED(CONFIG_IPV6) if (family == AF_INET6) size = sizeof(struct in6_addr); #endif - hlist_for_each_entry_rcu(key, pos, &tp->md5sig_info->head, node) { + hlist_for_each_entry_rcu(key, pos, &md5sig->head, node) { if (key->family != family) continue; if (!memcmp(&key->addr, addr, size)) @@ -932,7 +936,8 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, return 0; } - md5sig = tp->md5sig_info; + md5sig = rcu_dereference_protected(tp->md5sig_info, + sock_owned_by_user(sk)); if (!md5sig) { md5sig = kmalloc(sizeof(*md5sig), gfp); if (!md5sig) @@ -940,7 +945,7 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, sk_nocaps_add(sk, NETIF_F_GSO_MASK); INIT_HLIST_HEAD(&md5sig->head); - tp->md5sig_info = md5sig; + rcu_assign_pointer(tp->md5sig_info, md5sig); } key = sock_kmalloc(sk, sizeof(*key), gfp); @@ -966,6 +971,7 @@ int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family) { struct tcp_sock *tp = tcp_sk(sk); struct tcp_md5sig_key *key; + struct tcp_md5sig_info *md5sig; key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET); if (!key) @@ -973,7 +979,9 @@ int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family) hlist_del_rcu(&key->node); atomic_sub(sizeof(*key), &sk->sk_omem_alloc); kfree_rcu(key, rcu); - if (hlist_empty(&tp->md5sig_info->head)) + md5sig = rcu_dereference_protected(tp->md5sig_info, + sock_owned_by_user(sk)); + if (hlist_empty(&md5sig->head)) tcp_free_md5sig_pool(); return 0; } @@ -984,10 +992,13 @@ void tcp_clear_md5_list(struct sock *sk) struct tcp_sock *tp = tcp_sk(sk); struct tcp_md5sig_key *key; struct hlist_node *pos, *n; + struct tcp_md5sig_info *md5sig; - if (!hlist_empty(&tp->md5sig_info->head)) + md5sig = rcu_dereference_protected(tp->md5sig_info, 1); + + if (!hlist_empty(&md5sig->head)) tcp_free_md5sig_pool(); - hlist_for_each_entry_safe(key, pos, n, &tp->md5sig_info->head, node) { + hlist_for_each_entry_safe(key, pos, n, &md5sig->head, node) { hlist_del_rcu(&key->node); atomic_sub(sizeof(*key), &sk->sk_omem_alloc); kfree_rcu(key, rcu); @@ -1009,12 +1020,9 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval, if (sin->sin_family != AF_INET) return -EINVAL; - if (!cmd.tcpm_key || !cmd.tcpm_keylen) { - if (!tcp_sk(sk)->md5sig_info) - return -ENOENT; + if (!cmd.tcpm_key || !cmd.tcpm_keylen) return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr, AF_INET); - } if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) return -EINVAL; @@ -1896,7 +1904,7 @@ void tcp_v4_destroy_sock(struct sock *sk) /* Clean up the MD5 key list, if any */ if (tp->md5sig_info) { tcp_clear_md5_list(sk); - kfree(tp->md5sig_info); + kfree_rcu(tp->md5sig_info, rcu); tp->md5sig_info = NULL; } #endif diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index bec41f9a6413..c25018106ef2 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -571,8 +571,6 @@ static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval, return -EINVAL; if (!cmd.tcpm_keylen) { - if (!tcp_sk(sk)->md5sig_info) - return -ENOENT; if (ipv6_addr_v4mapped(&sin6->sin6_addr)) return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], AF_INET); -- cgit From f79d52c254e4e2cef3da64dc02ade3bc8f10c539 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 1 Feb 2012 16:14:17 -0500 Subject: ipv6: Remove never used function inet6_ac_check(). It went from unused, to commented out, and never changing after that. Just get rid of it, if someone wants it they can unearth it from the history. Signed-off-by: David S. Miller --- include/net/addrconf.h | 1 - net/ipv6/anycast.c | 29 ----------------------------- 2 files changed, 30 deletions(-) (limited to 'include') diff --git a/include/net/addrconf.h b/include/net/addrconf.h index f68dce2d8d88..757a17638b1b 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -160,7 +160,6 @@ extern void addrconf_prefix_rcv(struct net_device *dev, extern int ipv6_sock_ac_join(struct sock *sk,int ifindex, const struct in6_addr *addr); extern int ipv6_sock_ac_drop(struct sock *sk,int ifindex, const struct in6_addr *addr); extern void ipv6_sock_ac_close(struct sock *sk); -extern int inet6_ac_check(struct sock *sk, const struct in6_addr *addr, int ifindex); extern int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr); extern int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr); diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 59402b4637f9..db00d27ffb16 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c @@ -211,35 +211,6 @@ void ipv6_sock_ac_close(struct sock *sk) rcu_read_unlock(); } -#if 0 -/* The function is not used, which is funny. Apparently, author - * supposed to use it to filter out datagrams inside udp/raw but forgot. - * - * It is OK, anycasts are not special comparing to delivery to unicasts. - */ - -int inet6_ac_check(struct sock *sk, struct in6_addr *addr, int ifindex) -{ - struct ipv6_ac_socklist *pac; - struct ipv6_pinfo *np = inet6_sk(sk); - int found; - - found = 0; - read_lock(&ipv6_sk_ac_lock); - for (pac=np->ipv6_ac_list; pac; pac=pac->acl_next) { - if (ifindex && pac->acl_ifindex != ifindex) - continue; - found = ipv6_addr_equal(&pac->acl_addr, addr); - if (found) - break; - } - read_unlock(&ipv6_sk_ac_lock); - - return found; -} - -#endif - static void aca_put(struct ifacaddr6 *ac) { if (atomic_dec_and_test(&ac->aca_refcnt)) { -- cgit From c7f798e3722610d486e951f04bbc00b6ba124d62 Mon Sep 17 00:00:00 2001 From: Yong Zhang Date: Thu, 1 Dec 2011 11:25:59 -0800 Subject: clocksource: Get rid of clocksource_calc_mult_shift() No one is using this, so encourage the use of clocksource_register_hz/khz(), and drop this helper. Cc: Thomas Gleixner Signed-off-by: Yong Zhang [jstultz: tweaked commit message] Signed-off-by: John Stultz --- include/linux/clocksource.h | 7 ------- 1 file changed, 7 deletions(-) (limited to 'include') diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 081147da0564..fbe89e17124e 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -319,13 +319,6 @@ static inline void __clocksource_updatefreq_khz(struct clocksource *cs, u32 khz) __clocksource_updatefreq_scale(cs, 1000, khz); } -static inline void -clocksource_calc_mult_shift(struct clocksource *cs, u32 freq, u32 minsec) -{ - return clocks_calc_mult_shift(&cs->mult, &cs->shift, freq, - NSEC_PER_SEC, minsec); -} - #ifdef CONFIG_GENERIC_TIME_VSYSCALL extern void update_vsyscall(struct timespec *ts, struct timespec *wtm, -- cgit From 761b3ef50e1c2649cffbfa67a4dcb2dcdb7982ed Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Tue, 31 Jan 2012 13:47:36 +0800 Subject: cgroup: remove cgroup_subsys argument from callbacks The argument is not used at all, and it's not necessary, because a specific callback handler of course knows which subsys it belongs to. Now only ->pupulate() takes this argument, because the handlers of this callback always call cgroup_add_file()/cgroup_add_files(). So we reduce a few lines of code, though the shrinking of object size is minimal. 16 files changed, 113 insertions(+), 162 deletions(-) text data bss dec hex filename 5486240 656987 7039960 13183187 c928d3 vmlinux.o.orig 5486170 656987 7039960 13183117 c9288d vmlinux.o Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- Documentation/cgroups/cgroups.txt | 26 +++++++++------------ block/blk-cgroup.c | 22 +++++++----------- include/linux/cgroup.h | 29 ++++++++++------------- include/net/sock.h | 7 +++--- include/net/tcp_memcontrol.h | 2 +- kernel/cgroup.c | 43 +++++++++++++++++------------------ kernel/cgroup_freezer.c | 11 ++++----- kernel/cpuset.c | 16 ++++--------- kernel/events/core.c | 13 ++++------- kernel/sched/core.c | 20 +++++++--------- mm/memcontrol.c | 48 ++++++++++++++++----------------------- net/core/netprio_cgroup.c | 10 ++++---- net/core/sock.c | 6 ++--- net/ipv4/tcp_memcontrol.c | 2 +- net/sched/cls_cgroup.c | 10 ++++---- security/device_cgroup.c | 10 ++++---- 16 files changed, 113 insertions(+), 162 deletions(-) (limited to 'include') diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt index a7c96ae5557c..8e74980ab385 100644 --- a/Documentation/cgroups/cgroups.txt +++ b/Documentation/cgroups/cgroups.txt @@ -558,8 +558,7 @@ Each subsystem may export the following methods. The only mandatory methods are create/destroy. Any others that are null are presumed to be successful no-ops. -struct cgroup_subsys_state *create(struct cgroup_subsys *ss, - struct cgroup *cgrp) +struct cgroup_subsys_state *create(struct cgroup *cgrp) (cgroup_mutex held by caller) Called to create a subsystem state object for a cgroup. The @@ -574,7 +573,7 @@ identified by the passed cgroup object having a NULL parent (since it's the root of the hierarchy) and may be an appropriate place for initialization code. -void destroy(struct cgroup_subsys *ss, struct cgroup *cgrp) +void destroy(struct cgroup *cgrp) (cgroup_mutex held by caller) The cgroup system is about to destroy the passed cgroup; the subsystem @@ -585,7 +584,7 @@ cgroup->parent is still valid. (Note - can also be called for a newly-created cgroup if an error occurs after this subsystem's create() method has been called for the new cgroup). -int pre_destroy(struct cgroup_subsys *ss, struct cgroup *cgrp); +int pre_destroy(struct cgroup *cgrp); Called before checking the reference count on each subsystem. This may be useful for subsystems which have some extra references even if @@ -593,8 +592,7 @@ there are not tasks in the cgroup. If pre_destroy() returns error code, rmdir() will fail with it. From this behavior, pre_destroy() can be called multiple times against a cgroup. -int can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct cgroup_taskset *tset) +int can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) (cgroup_mutex held by caller) Called prior to moving one or more tasks into a cgroup; if the @@ -615,8 +613,7 @@ fork. If this method returns 0 (success) then this should remain valid while the caller holds cgroup_mutex and it is ensured that either attach() or cancel_attach() will be called in future. -void cancel_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct cgroup_taskset *tset) +void cancel_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) (cgroup_mutex held by caller) Called when a task attach operation has failed after can_attach() has succeeded. @@ -625,23 +622,22 @@ function, so that the subsystem can implement a rollback. If not, not necessary. This will be called only about subsystems whose can_attach() operation have succeeded. The parameters are identical to can_attach(). -void attach(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct cgroup_taskset *tset) +void attach(struct cgroup *cgrp, struct cgroup_taskset *tset) (cgroup_mutex held by caller) Called after the task has been attached to the cgroup, to allow any post-attachment activity that requires memory allocations or blocking. The parameters are identical to can_attach(). -void fork(struct cgroup_subsy *ss, struct task_struct *task) +void fork(struct task_struct *task) Called when a task is forked into a cgroup. -void exit(struct cgroup_subsys *ss, struct task_struct *task) +void exit(struct task_struct *task) Called during task exit. -int populate(struct cgroup_subsys *ss, struct cgroup *cgrp) +int populate(struct cgroup *cgrp) (cgroup_mutex held by caller) Called after creation of a cgroup to allow a subsystem to populate @@ -651,7 +647,7 @@ include/linux/cgroup.h for details). Note that although this method can return an error code, the error code is currently not always handled well. -void post_clone(struct cgroup_subsys *ss, struct cgroup *cgrp) +void post_clone(struct cgroup *cgrp) (cgroup_mutex held by caller) Called during cgroup_create() to do any parameter @@ -659,7 +655,7 @@ initialization which might be required before a task could attach. For example in cpusets, no task may attach before 'cpus' and 'mems' are set up. -void bind(struct cgroup_subsys *ss, struct cgroup *root) +void bind(struct cgroup *root) (cgroup_mutex and ss->hierarchy_mutex held by caller) Called when a cgroup subsystem is rebound to a different hierarchy diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index fa8f26309444..1359d637831f 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -28,13 +28,10 @@ static LIST_HEAD(blkio_list); struct blkio_cgroup blkio_root_cgroup = { .weight = 2*BLKIO_WEIGHT_DEFAULT }; EXPORT_SYMBOL_GPL(blkio_root_cgroup); -static struct cgroup_subsys_state *blkiocg_create(struct cgroup_subsys *, - struct cgroup *); -static int blkiocg_can_attach(struct cgroup_subsys *, struct cgroup *, - struct cgroup_taskset *); -static void blkiocg_attach(struct cgroup_subsys *, struct cgroup *, - struct cgroup_taskset *); -static void blkiocg_destroy(struct cgroup_subsys *, struct cgroup *); +static struct cgroup_subsys_state *blkiocg_create(struct cgroup *); +static int blkiocg_can_attach(struct cgroup *, struct cgroup_taskset *); +static void blkiocg_attach(struct cgroup *, struct cgroup_taskset *); +static void blkiocg_destroy(struct cgroup *); static int blkiocg_populate(struct cgroup_subsys *, struct cgroup *); /* for encoding cft->private value on file */ @@ -1548,7 +1545,7 @@ static int blkiocg_populate(struct cgroup_subsys *subsys, struct cgroup *cgroup) ARRAY_SIZE(blkio_files)); } -static void blkiocg_destroy(struct cgroup_subsys *subsys, struct cgroup *cgroup) +static void blkiocg_destroy(struct cgroup *cgroup) { struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgroup); unsigned long flags; @@ -1598,8 +1595,7 @@ static void blkiocg_destroy(struct cgroup_subsys *subsys, struct cgroup *cgroup) kfree(blkcg); } -static struct cgroup_subsys_state * -blkiocg_create(struct cgroup_subsys *subsys, struct cgroup *cgroup) +static struct cgroup_subsys_state *blkiocg_create(struct cgroup *cgroup) { struct blkio_cgroup *blkcg; struct cgroup *parent = cgroup->parent; @@ -1628,8 +1624,7 @@ done: * of the main cic data structures. For now we allow a task to change * its cgroup only if it's the only owner of its ioc. */ -static int blkiocg_can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct cgroup_taskset *tset) +static int blkiocg_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) { struct task_struct *task; struct io_context *ioc; @@ -1648,8 +1643,7 @@ static int blkiocg_can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, return ret; } -static void blkiocg_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct cgroup_taskset *tset) +static void blkiocg_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) { struct task_struct *task; struct io_context *ioc; diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 7da3e745b74c..501adb1b2f43 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -452,23 +452,18 @@ int cgroup_taskset_size(struct cgroup_taskset *tset); */ struct cgroup_subsys { - struct cgroup_subsys_state *(*create)(struct cgroup_subsys *ss, - struct cgroup *cgrp); - int (*pre_destroy)(struct cgroup_subsys *ss, struct cgroup *cgrp); - void (*destroy)(struct cgroup_subsys *ss, struct cgroup *cgrp); - int (*can_attach)(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct cgroup_taskset *tset); - void (*cancel_attach)(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct cgroup_taskset *tset); - void (*attach)(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct cgroup_taskset *tset); - void (*fork)(struct cgroup_subsys *ss, struct task_struct *task); - void (*exit)(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct cgroup *old_cgrp, struct task_struct *task); - int (*populate)(struct cgroup_subsys *ss, - struct cgroup *cgrp); - void (*post_clone)(struct cgroup_subsys *ss, struct cgroup *cgrp); - void (*bind)(struct cgroup_subsys *ss, struct cgroup *root); + struct cgroup_subsys_state *(*create)(struct cgroup *cgrp); + int (*pre_destroy)(struct cgroup *cgrp); + void (*destroy)(struct cgroup *cgrp); + int (*can_attach)(struct cgroup *cgrp, struct cgroup_taskset *tset); + void (*cancel_attach)(struct cgroup *cgrp, struct cgroup_taskset *tset); + void (*attach)(struct cgroup *cgrp, struct cgroup_taskset *tset); + void (*fork)(struct task_struct *task); + void (*exit)(struct cgroup *cgrp, struct cgroup *old_cgrp, + struct task_struct *task); + int (*populate)(struct cgroup_subsys *ss, struct cgroup *cgrp); + void (*post_clone)(struct cgroup *cgrp); + void (*bind)(struct cgroup *root); int subsys_id; int active; diff --git a/include/net/sock.h b/include/net/sock.h index bb972d254dff..705d1add19a1 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -68,7 +68,7 @@ struct cgroup; struct cgroup_subsys; #ifdef CONFIG_NET int mem_cgroup_sockets_init(struct cgroup *cgrp, struct cgroup_subsys *ss); -void mem_cgroup_sockets_destroy(struct cgroup *cgrp, struct cgroup_subsys *ss); +void mem_cgroup_sockets_destroy(struct cgroup *cgrp); #else static inline int mem_cgroup_sockets_init(struct cgroup *cgrp, struct cgroup_subsys *ss) @@ -76,7 +76,7 @@ int mem_cgroup_sockets_init(struct cgroup *cgrp, struct cgroup_subsys *ss) return 0; } static inline -void mem_cgroup_sockets_destroy(struct cgroup *cgrp, struct cgroup_subsys *ss) +void mem_cgroup_sockets_destroy(struct cgroup *cgrp) { } #endif @@ -869,8 +869,7 @@ struct proto { */ int (*init_cgroup)(struct cgroup *cgrp, struct cgroup_subsys *ss); - void (*destroy_cgroup)(struct cgroup *cgrp, - struct cgroup_subsys *ss); + void (*destroy_cgroup)(struct cgroup *cgrp); struct cg_proto *(*proto_cgroup)(struct mem_cgroup *memcg); #endif }; diff --git a/include/net/tcp_memcontrol.h b/include/net/tcp_memcontrol.h index 3512082fa909..48410ff25c9e 100644 --- a/include/net/tcp_memcontrol.h +++ b/include/net/tcp_memcontrol.h @@ -13,7 +13,7 @@ struct tcp_memcontrol { struct cg_proto *tcp_proto_cgroup(struct mem_cgroup *memcg); int tcp_init_cgroup(struct cgroup *cgrp, struct cgroup_subsys *ss); -void tcp_destroy_cgroup(struct cgroup *cgrp, struct cgroup_subsys *ss); +void tcp_destroy_cgroup(struct cgroup *cgrp); unsigned long long tcp_max_memory(const struct mem_cgroup *memcg); void tcp_prot_mem(struct mem_cgroup *memcg, long val, int idx); #endif /* _TCP_MEMCG_H */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 43a224f167b5..865d89a580c7 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -818,7 +818,7 @@ static int cgroup_call_pre_destroy(struct cgroup *cgrp) for_each_subsys(cgrp->root, ss) if (ss->pre_destroy) { - ret = ss->pre_destroy(ss, cgrp); + ret = ss->pre_destroy(cgrp); if (ret) break; } @@ -846,7 +846,7 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode) * Release the subsystem state objects. */ for_each_subsys(cgrp->root, ss) - ss->destroy(ss, cgrp); + ss->destroy(cgrp); cgrp->root->number_of_cgroups--; mutex_unlock(&cgroup_mutex); @@ -1015,7 +1015,7 @@ static int rebind_subsystems(struct cgroupfs_root *root, list_move(&ss->sibling, &root->subsys_list); ss->root = root; if (ss->bind) - ss->bind(ss, cgrp); + ss->bind(cgrp); mutex_unlock(&ss->hierarchy_mutex); /* refcount was already taken, and we're keeping it */ } else if (bit & removed_bits) { @@ -1025,7 +1025,7 @@ static int rebind_subsystems(struct cgroupfs_root *root, BUG_ON(cgrp->subsys[i]->cgroup != cgrp); mutex_lock(&ss->hierarchy_mutex); if (ss->bind) - ss->bind(ss, dummytop); + ss->bind(dummytop); dummytop->subsys[i]->cgroup = dummytop; cgrp->subsys[i] = NULL; subsys[i]->root = &rootnode; @@ -1908,7 +1908,7 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) for_each_subsys(root, ss) { if (ss->can_attach) { - retval = ss->can_attach(ss, cgrp, &tset); + retval = ss->can_attach(cgrp, &tset); if (retval) { /* * Remember on which subsystem the can_attach() @@ -1932,7 +1932,7 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) for_each_subsys(root, ss) { if (ss->attach) - ss->attach(ss, cgrp, &tset); + ss->attach(cgrp, &tset); } synchronize_rcu(); @@ -1954,7 +1954,7 @@ out: */ break; if (ss->cancel_attach) - ss->cancel_attach(ss, cgrp, &tset); + ss->cancel_attach(cgrp, &tset); } } return retval; @@ -2067,7 +2067,7 @@ static int cgroup_attach_proc(struct cgroup *cgrp, struct task_struct *leader) */ for_each_subsys(root, ss) { if (ss->can_attach) { - retval = ss->can_attach(ss, cgrp, &tset); + retval = ss->can_attach(cgrp, &tset); if (retval) { failed_ss = ss; goto out_cancel_attach; @@ -2104,7 +2104,7 @@ static int cgroup_attach_proc(struct cgroup *cgrp, struct task_struct *leader) */ for_each_subsys(root, ss) { if (ss->attach) - ss->attach(ss, cgrp, &tset); + ss->attach(cgrp, &tset); } /* @@ -2128,7 +2128,7 @@ out_cancel_attach: if (ss == failed_ss) break; if (ss->cancel_attach) - ss->cancel_attach(ss, cgrp, &tset); + ss->cancel_attach(cgrp, &tset); } } out_free_group_list: @@ -3756,7 +3756,7 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, set_bit(CGRP_CLONE_CHILDREN, &cgrp->flags); for_each_subsys(root, ss) { - struct cgroup_subsys_state *css = ss->create(ss, cgrp); + struct cgroup_subsys_state *css = ss->create(cgrp); if (IS_ERR(css)) { err = PTR_ERR(css); @@ -3770,7 +3770,7 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, } /* At error, ->destroy() callback has to free assigned ID. */ if (clone_children(parent) && ss->post_clone) - ss->post_clone(ss, cgrp); + ss->post_clone(cgrp); } cgroup_lock_hierarchy(root); @@ -3804,7 +3804,7 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, for_each_subsys(root, ss) { if (cgrp->subsys[ss->subsys_id]) - ss->destroy(ss, cgrp); + ss->destroy(cgrp); } mutex_unlock(&cgroup_mutex); @@ -4028,7 +4028,7 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss) /* Create the top cgroup state for this subsystem */ list_add(&ss->sibling, &rootnode.subsys_list); ss->root = &rootnode; - css = ss->create(ss, dummytop); + css = ss->create(dummytop); /* We don't handle early failures gracefully */ BUG_ON(IS_ERR(css)); init_cgroup_css(css, ss, dummytop); @@ -4117,7 +4117,7 @@ int __init_or_module cgroup_load_subsys(struct cgroup_subsys *ss) * no ss->create seems to need anything important in the ss struct, so * this can happen first (i.e. before the rootnode attachment). */ - css = ss->create(ss, dummytop); + css = ss->create(dummytop); if (IS_ERR(css)) { /* failure case - need to deassign the subsys[] slot. */ subsys[i] = NULL; @@ -4135,7 +4135,7 @@ int __init_or_module cgroup_load_subsys(struct cgroup_subsys *ss) int ret = cgroup_init_idr(ss, css); if (ret) { dummytop->subsys[ss->subsys_id] = NULL; - ss->destroy(ss, dummytop); + ss->destroy(dummytop); subsys[i] = NULL; mutex_unlock(&cgroup_mutex); return ret; @@ -4233,7 +4233,7 @@ void cgroup_unload_subsys(struct cgroup_subsys *ss) * pointer to find their state. note that this also takes care of * freeing the css_id. */ - ss->destroy(ss, dummytop); + ss->destroy(dummytop); dummytop->subsys[ss->subsys_id] = NULL; mutex_unlock(&cgroup_mutex); @@ -4509,7 +4509,7 @@ void cgroup_fork_callbacks(struct task_struct *child) for (i = 0; i < CGROUP_BUILTIN_SUBSYS_COUNT; i++) { struct cgroup_subsys *ss = subsys[i]; if (ss->fork) - ss->fork(ss, child); + ss->fork(child); } } } @@ -4611,7 +4611,7 @@ void cgroup_exit(struct task_struct *tsk, int run_callbacks) struct cgroup *old_cgrp = rcu_dereference_raw(cg->subsys[i])->cgroup; struct cgroup *cgrp = task_cgroup(tsk, i); - ss->exit(ss, cgrp, old_cgrp, tsk); + ss->exit(cgrp, old_cgrp, tsk); } } } @@ -5066,8 +5066,7 @@ struct cgroup_subsys_state *cgroup_css_from_dir(struct file *f, int id) } #ifdef CONFIG_CGROUP_DEBUG -static struct cgroup_subsys_state *debug_create(struct cgroup_subsys *ss, - struct cgroup *cont) +static struct cgroup_subsys_state *debug_create(struct cgroup *cont) { struct cgroup_subsys_state *css = kzalloc(sizeof(*css), GFP_KERNEL); @@ -5077,7 +5076,7 @@ static struct cgroup_subsys_state *debug_create(struct cgroup_subsys *ss, return css; } -static void debug_destroy(struct cgroup_subsys *ss, struct cgroup *cont) +static void debug_destroy(struct cgroup *cont) { kfree(cont->subsys[debug_subsys_id]); } diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index fc0646b78a64..f86e93920b62 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -128,8 +128,7 @@ struct cgroup_subsys freezer_subsys; * task->alloc_lock (inside __thaw_task(), prevents race with refrigerator()) * sighand->siglock */ -static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss, - struct cgroup *cgroup) +static struct cgroup_subsys_state *freezer_create(struct cgroup *cgroup) { struct freezer *freezer; @@ -142,8 +141,7 @@ static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss, return &freezer->css; } -static void freezer_destroy(struct cgroup_subsys *ss, - struct cgroup *cgroup) +static void freezer_destroy(struct cgroup *cgroup) { struct freezer *freezer = cgroup_freezer(cgroup); @@ -164,8 +162,7 @@ static bool is_task_frozen_enough(struct task_struct *task) * a write to that file racing against an attach, and hence the * can_attach() result will remain valid until the attach completes. */ -static int freezer_can_attach(struct cgroup_subsys *ss, - struct cgroup *new_cgroup, +static int freezer_can_attach(struct cgroup *new_cgroup, struct cgroup_taskset *tset) { struct freezer *freezer; @@ -185,7 +182,7 @@ static int freezer_can_attach(struct cgroup_subsys *ss, return 0; } -static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task) +static void freezer_fork(struct task_struct *task) { struct freezer *freezer; diff --git a/kernel/cpuset.c b/kernel/cpuset.c index a09ac2b9a661..5d575836dba6 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -1399,8 +1399,7 @@ static nodemask_t cpuset_attach_nodemask_from; static nodemask_t cpuset_attach_nodemask_to; /* Called by cgroups to determine if a cpuset is usable; cgroup_mutex held */ -static int cpuset_can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct cgroup_taskset *tset) +static int cpuset_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) { struct cpuset *cs = cgroup_cs(cgrp); struct task_struct *task; @@ -1436,8 +1435,7 @@ static int cpuset_can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, return 0; } -static void cpuset_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct cgroup_taskset *tset) +static void cpuset_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) { struct mm_struct *mm; struct task_struct *task; @@ -1833,8 +1831,7 @@ static int cpuset_populate(struct cgroup_subsys *ss, struct cgroup *cont) * (and likewise for mems) to the new cgroup. Called with cgroup_mutex * held. */ -static void cpuset_post_clone(struct cgroup_subsys *ss, - struct cgroup *cgroup) +static void cpuset_post_clone(struct cgroup *cgroup) { struct cgroup *parent, *child; struct cpuset *cs, *parent_cs; @@ -1857,13 +1854,10 @@ static void cpuset_post_clone(struct cgroup_subsys *ss, /* * cpuset_create - create a cpuset - * ss: cpuset cgroup subsystem * cont: control group that the new cpuset will be part of */ -static struct cgroup_subsys_state *cpuset_create( - struct cgroup_subsys *ss, - struct cgroup *cont) +static struct cgroup_subsys_state *cpuset_create(struct cgroup *cont) { struct cpuset *cs; struct cpuset *parent; @@ -1902,7 +1896,7 @@ static struct cgroup_subsys_state *cpuset_create( * will call async_rebuild_sched_domains(). */ -static void cpuset_destroy(struct cgroup_subsys *ss, struct cgroup *cont) +static void cpuset_destroy(struct cgroup *cont) { struct cpuset *cs = cgroup_cs(cont); diff --git a/kernel/events/core.c b/kernel/events/core.c index a8f4ac001a00..a5d1ee92b0d9 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -6906,8 +6906,7 @@ unlock: device_initcall(perf_event_sysfs_init); #ifdef CONFIG_CGROUP_PERF -static struct cgroup_subsys_state *perf_cgroup_create( - struct cgroup_subsys *ss, struct cgroup *cont) +static struct cgroup_subsys_state *perf_cgroup_create(struct cgroup *cont) { struct perf_cgroup *jc; @@ -6924,8 +6923,7 @@ static struct cgroup_subsys_state *perf_cgroup_create( return &jc->css; } -static void perf_cgroup_destroy(struct cgroup_subsys *ss, - struct cgroup *cont) +static void perf_cgroup_destroy(struct cgroup *cont) { struct perf_cgroup *jc; jc = container_of(cgroup_subsys_state(cont, perf_subsys_id), @@ -6941,8 +6939,7 @@ static int __perf_cgroup_move(void *info) return 0; } -static void perf_cgroup_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct cgroup_taskset *tset) +static void perf_cgroup_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) { struct task_struct *task; @@ -6950,8 +6947,8 @@ static void perf_cgroup_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, task_function_call(task, __perf_cgroup_move, task); } -static void perf_cgroup_exit(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct cgroup *old_cgrp, struct task_struct *task) +static void perf_cgroup_exit(struct cgroup *cgrp, struct cgroup *old_cgrp, + struct task_struct *task) { /* * cgroup_exit() is called in the copy_process() failure path. diff --git a/kernel/sched/core.c b/kernel/sched/core.c index df00cb09263e..ff12f7216062 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7530,8 +7530,7 @@ static inline struct task_group *cgroup_tg(struct cgroup *cgrp) struct task_group, css); } -static struct cgroup_subsys_state * -cpu_cgroup_create(struct cgroup_subsys *ss, struct cgroup *cgrp) +static struct cgroup_subsys_state *cpu_cgroup_create(struct cgroup *cgrp) { struct task_group *tg, *parent; @@ -7548,15 +7547,14 @@ cpu_cgroup_create(struct cgroup_subsys *ss, struct cgroup *cgrp) return &tg->css; } -static void -cpu_cgroup_destroy(struct cgroup_subsys *ss, struct cgroup *cgrp) +static void cpu_cgroup_destroy(struct cgroup *cgrp) { struct task_group *tg = cgroup_tg(cgrp); sched_destroy_group(tg); } -static int cpu_cgroup_can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, +static int cpu_cgroup_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) { struct task_struct *task; @@ -7574,7 +7572,7 @@ static int cpu_cgroup_can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, return 0; } -static void cpu_cgroup_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, +static void cpu_cgroup_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) { struct task_struct *task; @@ -7584,8 +7582,8 @@ static void cpu_cgroup_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, } static void -cpu_cgroup_exit(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct cgroup *old_cgrp, struct task_struct *task) +cpu_cgroup_exit(struct cgroup *cgrp, struct cgroup *old_cgrp, + struct task_struct *task) { /* * cgroup_exit() is called in the copy_process() failure path. @@ -7935,8 +7933,7 @@ struct cgroup_subsys cpu_cgroup_subsys = { */ /* create a new cpu accounting group */ -static struct cgroup_subsys_state *cpuacct_create( - struct cgroup_subsys *ss, struct cgroup *cgrp) +static struct cgroup_subsys_state *cpuacct_create(struct cgroup *cgrp) { struct cpuacct *ca; @@ -7966,8 +7963,7 @@ out: } /* destroy an existing cpu accounting group */ -static void -cpuacct_destroy(struct cgroup_subsys *ss, struct cgroup *cgrp) +static void cpuacct_destroy(struct cgroup *cgrp) { struct cpuacct *ca = cgroup_ca(cgrp); diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 3dbff4dcde35..ae2f0a8ab761 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -4580,10 +4580,9 @@ static int register_kmem_files(struct cgroup *cont, struct cgroup_subsys *ss) return mem_cgroup_sockets_init(cont, ss); }; -static void kmem_cgroup_destroy(struct cgroup_subsys *ss, - struct cgroup *cont) +static void kmem_cgroup_destroy(struct cgroup *cont) { - mem_cgroup_sockets_destroy(cont, ss); + mem_cgroup_sockets_destroy(cont); } #else static int register_kmem_files(struct cgroup *cont, struct cgroup_subsys *ss) @@ -4591,8 +4590,7 @@ static int register_kmem_files(struct cgroup *cont, struct cgroup_subsys *ss) return 0; } -static void kmem_cgroup_destroy(struct cgroup_subsys *ss, - struct cgroup *cont) +static void kmem_cgroup_destroy(struct cgroup *cont) { } #endif @@ -4884,7 +4882,7 @@ err_cleanup: } static struct cgroup_subsys_state * __ref -mem_cgroup_create(struct cgroup_subsys *ss, struct cgroup *cont) +mem_cgroup_create(struct cgroup *cont) { struct mem_cgroup *memcg, *parent; long error = -ENOMEM; @@ -4946,20 +4944,18 @@ free_out: return ERR_PTR(error); } -static int mem_cgroup_pre_destroy(struct cgroup_subsys *ss, - struct cgroup *cont) +static int mem_cgroup_pre_destroy(struct cgroup *cont) { struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); return mem_cgroup_force_empty(memcg, false); } -static void mem_cgroup_destroy(struct cgroup_subsys *ss, - struct cgroup *cont) +static void mem_cgroup_destroy(struct cgroup *cont) { struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); - kmem_cgroup_destroy(ss, cont); + kmem_cgroup_destroy(cont); mem_cgroup_put(memcg); } @@ -5296,9 +5292,8 @@ static void mem_cgroup_clear_mc(void) mem_cgroup_end_move(from); } -static int mem_cgroup_can_attach(struct cgroup_subsys *ss, - struct cgroup *cgroup, - struct cgroup_taskset *tset) +static int mem_cgroup_can_attach(struct cgroup *cgroup, + struct cgroup_taskset *tset) { struct task_struct *p = cgroup_taskset_first(tset); int ret = 0; @@ -5336,9 +5331,8 @@ static int mem_cgroup_can_attach(struct cgroup_subsys *ss, return ret; } -static void mem_cgroup_cancel_attach(struct cgroup_subsys *ss, - struct cgroup *cgroup, - struct cgroup_taskset *tset) +static void mem_cgroup_cancel_attach(struct cgroup *cgroup, + struct cgroup_taskset *tset) { mem_cgroup_clear_mc(); } @@ -5453,9 +5447,8 @@ retry: up_read(&mm->mmap_sem); } -static void mem_cgroup_move_task(struct cgroup_subsys *ss, - struct cgroup *cont, - struct cgroup_taskset *tset) +static void mem_cgroup_move_task(struct cgroup *cont, + struct cgroup_taskset *tset) { struct task_struct *p = cgroup_taskset_first(tset); struct mm_struct *mm = get_task_mm(p); @@ -5470,20 +5463,17 @@ static void mem_cgroup_move_task(struct cgroup_subsys *ss, mem_cgroup_clear_mc(); } #else /* !CONFIG_MMU */ -static int mem_cgroup_can_attach(struct cgroup_subsys *ss, - struct cgroup *cgroup, - struct cgroup_taskset *tset) +static int mem_cgroup_can_attach(struct cgroup *cgroup, + struct cgroup_taskset *tset) { return 0; } -static void mem_cgroup_cancel_attach(struct cgroup_subsys *ss, - struct cgroup *cgroup, - struct cgroup_taskset *tset) +static void mem_cgroup_cancel_attach(struct cgroup *cgroup, + struct cgroup_taskset *tset) { } -static void mem_cgroup_move_task(struct cgroup_subsys *ss, - struct cgroup *cont, - struct cgroup_taskset *tset) +static void mem_cgroup_move_task(struct cgroup *cont, + struct cgroup_taskset *tset) { } #endif diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index 3a9fd4826b75..22036ab732cf 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -23,9 +23,8 @@ #include #include -static struct cgroup_subsys_state *cgrp_create(struct cgroup_subsys *ss, - struct cgroup *cgrp); -static void cgrp_destroy(struct cgroup_subsys *ss, struct cgroup *cgrp); +static struct cgroup_subsys_state *cgrp_create(struct cgroup *cgrp); +static void cgrp_destroy(struct cgroup *cgrp); static int cgrp_populate(struct cgroup_subsys *ss, struct cgroup *cgrp); struct cgroup_subsys net_prio_subsys = { @@ -120,8 +119,7 @@ static void update_netdev_tables(void) rtnl_unlock(); } -static struct cgroup_subsys_state *cgrp_create(struct cgroup_subsys *ss, - struct cgroup *cgrp) +static struct cgroup_subsys_state *cgrp_create(struct cgroup *cgrp) { struct cgroup_netprio_state *cs; int ret; @@ -145,7 +143,7 @@ static struct cgroup_subsys_state *cgrp_create(struct cgroup_subsys *ss, return &cs->css; } -static void cgrp_destroy(struct cgroup_subsys *ss, struct cgroup *cgrp) +static void cgrp_destroy(struct cgroup *cgrp) { struct cgroup_netprio_state *cs; struct net_device *dev; diff --git a/net/core/sock.c b/net/core/sock.c index 5c5af9988f94..688037cb3b6e 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -160,19 +160,19 @@ int mem_cgroup_sockets_init(struct cgroup *cgrp, struct cgroup_subsys *ss) out: list_for_each_entry_continue_reverse(proto, &proto_list, node) if (proto->destroy_cgroup) - proto->destroy_cgroup(cgrp, ss); + proto->destroy_cgroup(cgrp); mutex_unlock(&proto_list_mutex); return ret; } -void mem_cgroup_sockets_destroy(struct cgroup *cgrp, struct cgroup_subsys *ss) +void mem_cgroup_sockets_destroy(struct cgroup *cgrp) { struct proto *proto; mutex_lock(&proto_list_mutex); list_for_each_entry_reverse(proto, &proto_list, node) if (proto->destroy_cgroup) - proto->destroy_cgroup(cgrp, ss); + proto->destroy_cgroup(cgrp); mutex_unlock(&proto_list_mutex); } #endif diff --git a/net/ipv4/tcp_memcontrol.c b/net/ipv4/tcp_memcontrol.c index 49978788a9dc..e714c6834c90 100644 --- a/net/ipv4/tcp_memcontrol.c +++ b/net/ipv4/tcp_memcontrol.c @@ -94,7 +94,7 @@ create_files: } EXPORT_SYMBOL(tcp_init_cgroup); -void tcp_destroy_cgroup(struct cgroup *cgrp, struct cgroup_subsys *ss) +void tcp_destroy_cgroup(struct cgroup *cgrp) { struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp); struct cg_proto *cg_proto; diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index f84fdc3a7f27..1afaa284fcd7 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -22,9 +22,8 @@ #include #include -static struct cgroup_subsys_state *cgrp_create(struct cgroup_subsys *ss, - struct cgroup *cgrp); -static void cgrp_destroy(struct cgroup_subsys *ss, struct cgroup *cgrp); +static struct cgroup_subsys_state *cgrp_create(struct cgroup *cgrp); +static void cgrp_destroy(struct cgroup *cgrp); static int cgrp_populate(struct cgroup_subsys *ss, struct cgroup *cgrp); struct cgroup_subsys net_cls_subsys = { @@ -51,8 +50,7 @@ static inline struct cgroup_cls_state *task_cls_state(struct task_struct *p) struct cgroup_cls_state, css); } -static struct cgroup_subsys_state *cgrp_create(struct cgroup_subsys *ss, - struct cgroup *cgrp) +static struct cgroup_subsys_state *cgrp_create(struct cgroup *cgrp) { struct cgroup_cls_state *cs; @@ -66,7 +64,7 @@ static struct cgroup_subsys_state *cgrp_create(struct cgroup_subsys *ss, return &cs->css; } -static void cgrp_destroy(struct cgroup_subsys *ss, struct cgroup *cgrp) +static void cgrp_destroy(struct cgroup *cgrp) { kfree(cgrp_cls_state(cgrp)); } diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 8b5b5d8612c6..c43a3323feea 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -61,8 +61,8 @@ static inline struct dev_cgroup *task_devcgroup(struct task_struct *task) struct cgroup_subsys devices_subsys; -static int devcgroup_can_attach(struct cgroup_subsys *ss, - struct cgroup *new_cgrp, struct cgroup_taskset *set) +static int devcgroup_can_attach(struct cgroup *new_cgrp, + struct cgroup_taskset *set) { struct task_struct *task = cgroup_taskset_first(set); @@ -156,8 +156,7 @@ remove: /* * called from kernel/cgroup.c with cgroup_lock() held. */ -static struct cgroup_subsys_state *devcgroup_create(struct cgroup_subsys *ss, - struct cgroup *cgroup) +static struct cgroup_subsys_state *devcgroup_create(struct cgroup *cgroup) { struct dev_cgroup *dev_cgroup, *parent_dev_cgroup; struct cgroup *parent_cgroup; @@ -195,8 +194,7 @@ static struct cgroup_subsys_state *devcgroup_create(struct cgroup_subsys *ss, return &dev_cgroup->css; } -static void devcgroup_destroy(struct cgroup_subsys *ss, - struct cgroup *cgroup) +static void devcgroup_destroy(struct cgroup *cgroup) { struct dev_cgroup *dev_cgroup; struct dev_whitelist_item *wh, *tmp; -- cgit From 348748b0e8cccc675e2f3a1456460ffcd540e1a1 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 11 Jan 2012 12:45:56 +0100 Subject: usb/uas: move UAS structs / defines into a header file The protocol specific structures and defines which are used by UAS are moved into a header files by this patch so it can be accessed by the UAS gadget as well. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Sarah Sharp --- drivers/usb/storage/uas.c | 56 +------------------------------------------ include/linux/usb/uas.h | 61 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 55 deletions(-) create mode 100644 include/linux/usb/uas.h (limited to 'include') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index e34c75970ed1..f98ba40352c1 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -23,49 +24,6 @@ #include #include -/* Common header for all IUs */ -struct iu { - __u8 iu_id; - __u8 rsvd1; - __be16 tag; -}; - -enum { - IU_ID_COMMAND = 0x01, - IU_ID_STATUS = 0x03, - IU_ID_RESPONSE = 0x04, - IU_ID_TASK_MGMT = 0x05, - IU_ID_READ_READY = 0x06, - IU_ID_WRITE_READY = 0x07, -}; - -struct command_iu { - __u8 iu_id; - __u8 rsvd1; - __be16 tag; - __u8 prio_attr; - __u8 rsvd5; - __u8 len; - __u8 rsvd7; - struct scsi_lun lun; - __u8 cdb[16]; /* XXX: Overflow-checking tools may misunderstand */ -}; - -/* - * Also used for the Read Ready and Write Ready IUs since they have the - * same first four bytes - */ -struct sense_iu { - __u8 iu_id; - __u8 rsvd1; - __be16 tag; - __be16 status_qual; - __u8 status; - __u8 rsvd7[7]; - __be16 len; - __u8 sense[SCSI_SENSE_BUFFERSIZE]; -}; - /* * The r00-r01c specs define this version of the SENSE IU data structure. * It's still in use by several different firmware releases. @@ -80,18 +38,6 @@ struct sense_iu_old { __u8 sense[SCSI_SENSE_BUFFERSIZE]; }; -enum { - CMD_PIPE_ID = 1, - STATUS_PIPE_ID = 2, - DATA_IN_PIPE_ID = 3, - DATA_OUT_PIPE_ID = 4, - - UAS_SIMPLE_TAG = 0, - UAS_HEAD_TAG = 1, - UAS_ORDERED_TAG = 2, - UAS_ACA = 4, -}; - struct uas_dev_info { struct usb_interface *intf; struct usb_device *udev; diff --git a/include/linux/usb/uas.h b/include/linux/usb/uas.h new file mode 100644 index 000000000000..856be7fcbbd8 --- /dev/null +++ b/include/linux/usb/uas.h @@ -0,0 +1,61 @@ +#ifndef __USB_UAS_H__ +#define __USB_UAS_H__ + +#include +#include + +/* Common header for all IUs */ +struct iu { + __u8 iu_id; + __u8 rsvd1; + __be16 tag; +}; + +enum { + IU_ID_COMMAND = 0x01, + IU_ID_STATUS = 0x03, + IU_ID_RESPONSE = 0x04, + IU_ID_TASK_MGMT = 0x05, + IU_ID_READ_READY = 0x06, + IU_ID_WRITE_READY = 0x07, +}; + +struct command_iu { + __u8 iu_id; + __u8 rsvd1; + __be16 tag; + __u8 prio_attr; + __u8 rsvd5; + __u8 len; + __u8 rsvd7; + struct scsi_lun lun; + __u8 cdb[16]; /* XXX: Overflow-checking tools may misunderstand */ +}; + +/* + * Also used for the Read Ready and Write Ready IUs since they have the + * same first four bytes + */ +struct sense_iu { + __u8 iu_id; + __u8 rsvd1; + __be16 tag; + __be16 status_qual; + __u8 status; + __u8 rsvd7[7]; + __be16 len; + __u8 sense[SCSI_SENSE_BUFFERSIZE]; +}; + +enum { + CMD_PIPE_ID = 1, + STATUS_PIPE_ID = 2, + DATA_IN_PIPE_ID = 3, + DATA_OUT_PIPE_ID = 4, + + UAS_SIMPLE_TAG = 0, + UAS_HEAD_TAG = 1, + UAS_ORDERED_TAG = 2, + UAS_ACA = 4, +}; +#endif -- cgit From ee398b59ec1dc3ce1d60518f4ea644907893414b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 11 Jan 2012 12:45:57 +0100 Subject: usb/uas: add usb_pipe_usage_descriptor usb_pipe_usage_descriptor defines the struct which is used to describe the type of the endpoint in UAS (status/command/data in+out). It will be used by the UAS gadget, the host code is using a char array for the access. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Sarah Sharp --- include/linux/usb/uas.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/linux/usb/uas.h b/include/linux/usb/uas.h index 856be7fcbbd8..9a988e413694 100644 --- a/include/linux/usb/uas.h +++ b/include/linux/usb/uas.h @@ -47,6 +47,14 @@ struct sense_iu { __u8 sense[SCSI_SENSE_BUFFERSIZE]; }; +struct usb_pipe_usage_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __u8 bPipeID; + __u8 Reserved; +} __attribute__((__packed__)); + enum { CMD_PIPE_ID = 1, STATUS_PIPE_ID = 2, -- cgit From 66d450e84ec656ec4b774c41cd8d46b3e48d51df Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 30 Jan 2012 21:14:28 +0100 Subject: TTY: provide tty_standard_install helper There are currently many cut&paste copies of what tty_driver_install_tty does when custom ->install method is not provided. Let's get rid of the copies and create a helper with this setup code. Signed-off-by: Jiri Slaby Cc: Havard Skinnemoen Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 30 +++++++++++++++--------------- include/linux/tty.h | 2 ++ 2 files changed, 17 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index fbcc14063804..44736f9e61d7 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1271,6 +1271,19 @@ int tty_init_termios(struct tty_struct *tty) } EXPORT_SYMBOL_GPL(tty_init_termios); +int tty_standard_install(struct tty_driver *driver, struct tty_struct *tty) +{ + int ret = tty_init_termios(tty); + if (ret) + return ret; + + tty_driver_kref_get(driver); + tty->count++; + driver->ttys[tty->index] = tty; + return 0; +} +EXPORT_SYMBOL_GPL(tty_standard_install); + /** * tty_driver_install_tty() - install a tty entry in the driver * @driver: the driver for the tty @@ -1286,21 +1299,8 @@ EXPORT_SYMBOL_GPL(tty_init_termios); static int tty_driver_install_tty(struct tty_driver *driver, struct tty_struct *tty) { - int idx = tty->index; - int ret; - - if (driver->ops->install) { - ret = driver->ops->install(driver, tty); - return ret; - } - - if (tty_init_termios(tty) == 0) { - tty_driver_kref_get(driver); - tty->count++; - driver->ttys[idx] = tty; - return 0; - } - return -ENOMEM; + return driver->ops->install ? driver->ops->install(driver, tty) : + tty_standard_install(driver, tty); } /** diff --git a/include/linux/tty.h b/include/linux/tty.h index d40774188203..a91ff403b3bf 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -484,6 +484,8 @@ extern void deinitialize_tty_struct(struct tty_struct *tty); extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx); extern int tty_release(struct inode *inode, struct file *filp); extern int tty_init_termios(struct tty_struct *tty); +extern int tty_standard_install(struct tty_driver *driver, + struct tty_struct *tty); extern struct tty_struct *tty_pair_get_tty(struct tty_struct *tty); extern struct tty_struct *tty_pair_get_pty(struct tty_struct *tty); -- cgit From 4f03a2c934894f30a64d397df8c7c4de129c5b30 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 27 Jan 2012 15:55:57 -0800 Subject: drivers: hv: kvp: Add/cleanup connector defines The current KVP code carries some private connector related defines. Update connector.h to have all the KVP defines. As part of this patch get rid of some unused defines. Signed-off-by: K. Y. Srinivasan Signed-off-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.h | 3 --- include/linux/connector.h | 1 + tools/hv/hv_kvp_daemon.c | 4 ---- 3 files changed, 1 insertion(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/hv/hv_kvp.h b/drivers/hv/hv_kvp.h index 9b765d7df838..c2c5bba25a5a 100644 --- a/drivers/hv/hv_kvp.h +++ b/drivers/hv/hv_kvp.h @@ -107,9 +107,6 @@ * the KVP user-mode component. */ -#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */ -#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */ - enum hv_ku_op { KVP_REGISTER = 0, /* Register the user mode component */ KVP_KERNEL_GET, /* Kernel is requesting the value */ diff --git a/include/linux/connector.h b/include/linux/connector.h index 3c9c54fd5690..76384074262d 100644 --- a/include/linux/connector.h +++ b/include/linux/connector.h @@ -43,6 +43,7 @@ #define CN_IDX_DRBD 0x8 #define CN_VAL_DRBD 0x1 #define CN_KVP_IDX 0x9 /* HyperV KVP */ +#define CN_KVP_VAL 0x1 /* queries from the kernel */ #define CN_NETLINK_USERS 10 /* Highest index + 1 */ diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 11224eddcdc2..2b6a2d950b88 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -40,15 +40,11 @@ #include /* - * KYS: TODO. Need to register these in the kernel. * * The following definitions are shared with the in-kernel component; do not * change any of this without making the corresponding changes in * the KVP kernel component. */ -#define CN_KVP_IDX 0x9 /* MSFT KVP functionality */ -#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */ -#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */ /* * KVP protocol: The user mode component first registers with the -- cgit From 2939437ce8f2de07237eb2bcce29b6a699bfe799 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 27 Jan 2012 15:55:58 -0800 Subject: drivers: hv: kvp: Move the contents of hv_kvp.h to hyperv.h In preparation for consolidating all KVP related defines into a single header file that both the kernel and user level components can use, move the contents of hv_kvp.h into hyperv.h. Signed-off-by: K. Y. Srinivasan Signed-off-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.c | 2 - drivers/hv/hv_kvp.h | 181 ------------------------------------------------- drivers/hv/hv_util.c | 3 - include/linux/hyperv.h | 165 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 186 deletions(-) delete mode 100644 drivers/hv/hv_kvp.h (limited to 'include') diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 0e8343f585bb..4a6971e13539 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -28,8 +28,6 @@ #include #include -#include "hv_kvp.h" - /* diff --git a/drivers/hv/hv_kvp.h b/drivers/hv/hv_kvp.h deleted file mode 100644 index c2c5bba25a5a..000000000000 --- a/drivers/hv/hv_kvp.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * An implementation of HyperV key value pair (KVP) functionality for Linux. - * - * - * Copyright (C) 2010, Novell, Inc. - * Author : K. Y. Srinivasan - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ -#ifndef _KVP_H -#define _KVP_H_ - -/* - * Maximum value size - used for both key names and value data, and includes - * any applicable NULL terminators. - * - * Note: This limit is somewhat arbitrary, but falls easily within what is - * supported for all native guests (back to Win 2000) and what is reasonable - * for the IC KVP exchange functionality. Note that Windows Me/98/95 are - * limited to 255 character key names. - * - * MSDN recommends not storing data values larger than 2048 bytes in the - * registry. - * - * Note: This value is used in defining the KVP exchange message - this value - * cannot be modified without affecting the message size and compatibility. - */ - -/* - * bytes, including any null terminators - */ -#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE (2048) - - -/* - * Maximum key size - the registry limit for the length of an entry name - * is 256 characters, including the null terminator - */ - -#define HV_KVP_EXCHANGE_MAX_KEY_SIZE (512) - -/* - * In Linux, we implement the KVP functionality in two components: - * 1) The kernel component which is packaged as part of the hv_utils driver - * is responsible for communicating with the host and responsible for - * implementing the host/guest protocol. 2) A user level daemon that is - * responsible for data gathering. - * - * Host/Guest Protocol: The host iterates over an index and expects the guest - * to assign a key name to the index and also return the value corresponding to - * the key. The host will have atmost one KVP transaction outstanding at any - * given point in time. The host side iteration stops when the guest returns - * an error. Microsoft has specified the following mapping of key names to - * host specified index: - * - * Index Key Name - * 0 FullyQualifiedDomainName - * 1 IntegrationServicesVersion - * 2 NetworkAddressIPv4 - * 3 NetworkAddressIPv6 - * 4 OSBuildNumber - * 5 OSName - * 6 OSMajorVersion - * 7 OSMinorVersion - * 8 OSVersion - * 9 ProcessorArchitecture - * - * The Windows host expects the Key Name and Key Value to be encoded in utf16. - * - * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the - * data gathering functionality in a user mode daemon. The user level daemon - * is also responsible for binding the key name to the index as well. The - * kernel and user-level daemon communicate using a connector channel. - * - * The user mode component first registers with the - * the kernel component. Subsequently, the kernel component requests, data - * for the specified keys. In response to this message the user mode component - * fills in the value corresponding to the specified key. We overload the - * sequence field in the cn_msg header to define our KVP message types. - * - * - * The kernel component simply acts as a conduit for communication between the - * Windows host and the user-level daemon. The kernel component passes up the - * index received from the Host to the user-level daemon. If the index is - * valid (supported), the corresponding key as well as its - * value (both are strings) is returned. If the index is invalid - * (not supported), a NULL key string is returned. - */ - -/* - * - * The following definitions are shared with the user-mode component; do not - * change any of this without making the corresponding changes in - * the KVP user-mode component. - */ - -enum hv_ku_op { - KVP_REGISTER = 0, /* Register the user mode component */ - KVP_KERNEL_GET, /* Kernel is requesting the value */ - KVP_KERNEL_SET, /* Kernel is providing the value */ - KVP_USER_GET, /* User is requesting the value */ - KVP_USER_SET /* User is providing the value */ -}; - -struct hv_ku_msg { - __u32 kvp_index; /* Key index */ - __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ - __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ -}; - - - - -#ifdef __KERNEL__ - -/* - * Registry value types. - */ - -#define REG_SZ 1 - -enum hv_kvp_exchg_op { - KVP_OP_GET = 0, - KVP_OP_SET, - KVP_OP_DELETE, - KVP_OP_ENUMERATE, - KVP_OP_COUNT /* Number of operations, must be last. */ -}; - -enum hv_kvp_exchg_pool { - KVP_POOL_EXTERNAL = 0, - KVP_POOL_GUEST, - KVP_POOL_AUTO, - KVP_POOL_AUTO_EXTERNAL, - KVP_POOL_AUTO_INTERNAL, - KVP_POOL_COUNT /* Number of pools, must be last. */ -}; - -struct hv_kvp_hdr { - u8 operation; - u8 pool; -}; - -struct hv_kvp_exchg_msg_value { - u32 value_type; - u32 key_size; - u32 value_size; - u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; - u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; -}; - -struct hv_kvp_msg_enumerate { - u32 index; - struct hv_kvp_exchg_msg_value data; -}; - -struct hv_kvp_msg { - struct hv_kvp_hdr kvp_hdr; - struct hv_kvp_msg_enumerate kvp_data; -}; - -int hv_kvp_init(struct hv_util_service *); -void hv_kvp_deinit(void); -void hv_kvp_onchannelcallback(void *); - -#endif /* __KERNEL__ */ -#endif /* _KVP_H */ - diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 55d58f21e6d4..dbb8b8eec210 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -28,9 +28,6 @@ #include #include -#include "hv_kvp.h" - - static void shutdown_onchannelcallback(void *context); static struct hv_util_service util_shutdown = { .util_cb = shutdown_onchannelcallback, diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 62b908e0e591..7332b3faecc8 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -25,6 +25,166 @@ #ifndef _HYPERV_H #define _HYPERV_H +#include + +/* + * An implementation of HyperV key value pair (KVP) functionality for Linux. + * + * + * Copyright (C) 2010, Novell, Inc. + * Author : K. Y. Srinivasan + * + */ + +/* + * Maximum value size - used for both key names and value data, and includes + * any applicable NULL terminators. + * + * Note: This limit is somewhat arbitrary, but falls easily within what is + * supported for all native guests (back to Win 2000) and what is reasonable + * for the IC KVP exchange functionality. Note that Windows Me/98/95 are + * limited to 255 character key names. + * + * MSDN recommends not storing data values larger than 2048 bytes in the + * registry. + * + * Note: This value is used in defining the KVP exchange message - this value + * cannot be modified without affecting the message size and compatibility. + */ + +/* + * bytes, including any null terminators + */ +#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE (2048) + + +/* + * Maximum key size - the registry limit for the length of an entry name + * is 256 characters, including the null terminator + */ + +#define HV_KVP_EXCHANGE_MAX_KEY_SIZE (512) + +/* + * In Linux, we implement the KVP functionality in two components: + * 1) The kernel component which is packaged as part of the hv_utils driver + * is responsible for communicating with the host and responsible for + * implementing the host/guest protocol. 2) A user level daemon that is + * responsible for data gathering. + * + * Host/Guest Protocol: The host iterates over an index and expects the guest + * to assign a key name to the index and also return the value corresponding to + * the key. The host will have atmost one KVP transaction outstanding at any + * given point in time. The host side iteration stops when the guest returns + * an error. Microsoft has specified the following mapping of key names to + * host specified index: + * + * Index Key Name + * 0 FullyQualifiedDomainName + * 1 IntegrationServicesVersion + * 2 NetworkAddressIPv4 + * 3 NetworkAddressIPv6 + * 4 OSBuildNumber + * 5 OSName + * 6 OSMajorVersion + * 7 OSMinorVersion + * 8 OSVersion + * 9 ProcessorArchitecture + * + * The Windows host expects the Key Name and Key Value to be encoded in utf16. + * + * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the + * data gathering functionality in a user mode daemon. The user level daemon + * is also responsible for binding the key name to the index as well. The + * kernel and user-level daemon communicate using a connector channel. + * + * The user mode component first registers with the + * the kernel component. Subsequently, the kernel component requests, data + * for the specified keys. In response to this message the user mode component + * fills in the value corresponding to the specified key. We overload the + * sequence field in the cn_msg header to define our KVP message types. + * + * + * The kernel component simply acts as a conduit for communication between the + * Windows host and the user-level daemon. The kernel component passes up the + * index received from the Host to the user-level daemon. If the index is + * valid (supported), the corresponding key as well as its + * value (both are strings) is returned. If the index is invalid + * (not supported), a NULL key string is returned. + */ + +/* + * + * The following definitions are shared with the user-mode component; do not + * change any of this without making the corresponding changes in + * the KVP user-mode component. + */ + +enum hv_ku_op { + KVP_REGISTER = 0, /* Register the user mode component */ + KVP_KERNEL_GET, /* Kernel is requesting the value */ + KVP_KERNEL_SET, /* Kernel is providing the value */ + KVP_USER_GET, /* User is requesting the value */ + KVP_USER_SET /* User is providing the value */ +}; + +struct hv_ku_msg { + __u32 kvp_index; /* Key index */ + __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ + __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ +}; + + + + +#ifdef __KERNEL__ + +/* + * Registry value types. + */ + +#define REG_SZ 1 + +enum hv_kvp_exchg_op { + KVP_OP_GET = 0, + KVP_OP_SET, + KVP_OP_DELETE, + KVP_OP_ENUMERATE, + KVP_OP_COUNT /* Number of operations, must be last. */ +}; + +enum hv_kvp_exchg_pool { + KVP_POOL_EXTERNAL = 0, + KVP_POOL_GUEST, + KVP_POOL_AUTO, + KVP_POOL_AUTO_EXTERNAL, + KVP_POOL_AUTO_INTERNAL, + KVP_POOL_COUNT /* Number of pools, must be last. */ +}; + +struct hv_kvp_hdr { + u8 operation; + u8 pool; +}; + +struct hv_kvp_exchg_msg_value { + u32 value_type; + u32 key_size; + u32 value_size; + u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; + u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; +}; + +struct hv_kvp_msg_enumerate { + u32 index; + struct hv_kvp_exchg_msg_value data; +}; + +struct hv_kvp_msg { + struct hv_kvp_hdr kvp_hdr; + struct hv_kvp_msg_enumerate kvp_data; +}; + #include #include #include @@ -870,4 +1030,9 @@ struct hyperv_service_callback { extern void vmbus_prep_negotiate_resp(struct icmsg_hdr *, struct icmsg_negotiate *, u8 *); +int hv_kvp_init(struct hv_util_service *); +void hv_kvp_deinit(void); +void hv_kvp_onchannelcallback(void *); + +#endif /* __KERNEL__ */ #endif /* _HYPERV_H */ -- cgit From cf5046b309b3a05c444a8edba6b44108510b7d7d Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Mon, 10 Oct 2011 23:43:53 +0200 Subject: can: dev: let can_get_echo_skb() return dlc of CAN frame can_get_echo_skb() is usually called in the TX complete handler. The stats->tx_packets and stats->tx_bytes should be updated there, too. This patch simplifies to figure out the size of the sent CAN frame. Signed-off-by: Marc Kleine-Budde --- drivers/net/can/dev.c | 10 +++++++++- include/linux/can/dev.h | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 120f1ab5a2ce..9d9799cf68cd 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -327,16 +327,24 @@ EXPORT_SYMBOL_GPL(can_put_echo_skb); * is handled in the device driver. The driver must protect * access to priv->echo_skb, if necessary. */ -void can_get_echo_skb(struct net_device *dev, unsigned int idx) +unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx) { struct can_priv *priv = netdev_priv(dev); BUG_ON(idx >= priv->echo_skb_max); if (priv->echo_skb[idx]) { + struct sk_buff *skb = priv->echo_skb[idx]; + struct can_frame *cf = (struct can_frame *)skb->data; + u8 dlc = cf->can_dlc; + netif_rx(priv->echo_skb[idx]); priv->echo_skb[idx] = NULL; + + return dlc; } + + return 0; } EXPORT_SYMBOL_GPL(can_get_echo_skb); diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h index a0969fcb72b9..5d2efe7e3f1b 100644 --- a/include/linux/can/dev.h +++ b/include/linux/can/dev.h @@ -92,7 +92,7 @@ void can_bus_off(struct net_device *dev); void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, unsigned int idx); -void can_get_echo_skb(struct net_device *dev, unsigned int idx); +unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx); void can_free_echo_skb(struct net_device *dev, unsigned int idx); struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf); -- cgit From ac483c446b67870444c9eeaf8325d3d2af9b91bc Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 2 Jan 2012 10:04:14 +0100 Subject: ftrace: Change filter/notrace set functions to return exit code Currently the ftrace_set_filter and ftrace_set_notrace functions do not return any return code. So there's no way for ftrace_ops user to tell wether the filter was correctly applied. The set_ftrace_filter interface returns error in case the filter did not match: # echo krava > set_ftrace_filter bash: echo: write error: Invalid argument Changing both ftrace_set_filter and ftrace_set_notrace functions to return zero if the filter was applied correctly or -E* values in case of error. Link: http://lkml.kernel.org/r/1325495060-6402-2-git-send-email-jolsa@redhat.com Acked-by: Frederic Weisbecker Signed-off-by: Jiri Olsa Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 4 ++-- kernel/trace/ftrace.c | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 028e26f0bf08..f33fb3b041c6 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -178,9 +178,9 @@ struct dyn_ftrace { }; int ftrace_force_update(void); -void ftrace_set_filter(struct ftrace_ops *ops, unsigned char *buf, +int ftrace_set_filter(struct ftrace_ops *ops, unsigned char *buf, int len, int reset); -void ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf, +int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf, int len, int reset); void ftrace_set_global_filter(unsigned char *buf, int len, int reset); void ftrace_set_global_notrace(unsigned char *buf, int len, int reset); diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 683d559a0eef..e2e0597c0845 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -3146,8 +3146,10 @@ ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len, mutex_lock(&ftrace_regex_lock); if (reset) ftrace_filter_reset(hash); - if (buf) - ftrace_match_records(hash, buf, len); + if (buf && !ftrace_match_records(hash, buf, len)) { + ret = -EINVAL; + goto out_regex_unlock; + } mutex_lock(&ftrace_lock); ret = ftrace_hash_move(ops, enable, orig_hash, hash); @@ -3157,6 +3159,7 @@ ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len, mutex_unlock(&ftrace_lock); + out_regex_unlock: mutex_unlock(&ftrace_regex_lock); free_ftrace_hash(hash); @@ -3173,10 +3176,10 @@ ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len, * Filters denote which functions should be enabled when tracing is enabled. * If @buf is NULL and reset is set, all functions will be enabled for tracing. */ -void ftrace_set_filter(struct ftrace_ops *ops, unsigned char *buf, +int ftrace_set_filter(struct ftrace_ops *ops, unsigned char *buf, int len, int reset) { - ftrace_set_regex(ops, buf, len, reset, 1); + return ftrace_set_regex(ops, buf, len, reset, 1); } EXPORT_SYMBOL_GPL(ftrace_set_filter); @@ -3191,10 +3194,10 @@ EXPORT_SYMBOL_GPL(ftrace_set_filter); * is enabled. If @buf is NULL and reset is set, all functions will be enabled * for tracing. */ -void ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf, +int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf, int len, int reset) { - ftrace_set_regex(ops, buf, len, reset, 0); + return ftrace_set_regex(ops, buf, len, reset, 0); } EXPORT_SYMBOL_GPL(ftrace_set_notrace); /** -- cgit From f069686e4bdc60a637d210ea3eea25fcdb82df88 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 25 Jan 2012 20:18:55 -0500 Subject: tracing/softirq: Move __raise_softirq_irqoff() out of header The __raise_softirq_irqoff() contains a tracepoint. As tracepoints in headers can cause issues, and not to mention, bloats the kernel when they are in a static inline, it is best to move the function that contains the tracepoint out of the header and into softirq.c. Link: http://lkml.kernel.org/r/20120118120711.GB14863@elte.hu Suggested-by: Ingo Molnar Signed-off-by: Steven Rostedt --- include/linux/interrupt.h | 7 +------ kernel/irq/chip.c | 2 ++ kernel/softirq.c | 6 ++++++ 3 files changed, 9 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index a64b00e286f5..3f830e005118 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -20,7 +20,6 @@ #include #include #include -#include /* * These correspond to the IORESOURCE_IRQ_* defines in @@ -456,11 +455,7 @@ asmlinkage void do_softirq(void); asmlinkage void __do_softirq(void); extern void open_softirq(int nr, void (*action)(struct softirq_action *)); extern void softirq_init(void); -static inline void __raise_softirq_irqoff(unsigned int nr) -{ - trace_softirq_raise(nr); - or_softirq_pending(1UL << nr); -} +extern void __raise_softirq_irqoff(unsigned int nr); extern void raise_softirq_irqoff(unsigned int nr); extern void raise_softirq(unsigned int nr); diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index f7c543a801d9..fc418249f01f 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -16,6 +16,8 @@ #include #include +#include + #include "internals.h" /** diff --git a/kernel/softirq.c b/kernel/softirq.c index 4eb3a0fa351e..06d40993594a 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -385,6 +385,12 @@ void raise_softirq(unsigned int nr) local_irq_restore(flags); } +void __raise_softirq_irqoff(unsigned int nr) +{ + trace_softirq_raise(nr); + or_softirq_pending(1UL << nr); +} + void open_softirq(int nr, void (*action)(struct softirq_action *)) { softirq_vec[nr].action = action; -- cgit From 60d3369edb03fb4799c45ac594e2b8a76aa9079c Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sun, 15 Jan 2012 21:48:38 +0100 Subject: Fix up version number reference in include/trace/events/power.h What was originally going to be 2.6.41 became 3.1 . Signed-off-by: Jesper Juhl Signed-off-by: Jiri Kosina --- include/trace/events/power.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/trace/events/power.h b/include/trace/events/power.h index 1bcc2a8c00e2..b2626999e580 100644 --- a/include/trace/events/power.h +++ b/include/trace/events/power.h @@ -65,7 +65,7 @@ TRACE_EVENT(machine_suspend, TP_printk("state=%lu", (unsigned long)__entry->state) ); -/* This code will be removed after deprecation time exceeded (2.6.41) */ +/* This code will be removed after deprecation time exceeded (3.1) */ #ifdef CONFIG_EVENT_POWER_TRACING_DEPRECATED /* -- cgit From 332ad43f19848ed653a5e44afa8e2f4a7d34ed44 Mon Sep 17 00:00:00 2001 From: "sjur.brandeland@stericsson.com" Date: Fri, 3 Feb 2012 04:36:21 +0000 Subject: caif-hsi: Add RX flip buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement RX flip buffer in the cfhsi_rx_done function, piggy-backed frames is also supported. This gives a significant performance gain for CAIF over HSI. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 145 +++++++++++++++++++++++++++++++------------- include/net/caif/caif_hsi.h | 1 + 2 files changed, 104 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index 0a4fc62a381d..c8afd62239e9 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -426,6 +426,35 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) return xfer_sz; } +static int cfhsi_rx_desc_len(struct cfhsi_desc *desc) +{ + int xfer_sz = 0; + int nfrms = 0; + u16 *plen; + + if ((desc->header & ~CFHSI_PIGGY_DESC) || + (desc->offset > CFHSI_MAX_EMB_FRM_SZ)) { + + pr_err("Invalid descriptor. %x %x\n", desc->header, + desc->offset); + return -EPROTO; + } + + /* Calculate transfer length. */ + plen = desc->cffrm_len; + while (nfrms < CFHSI_MAX_PKTS && *plen) { + xfer_sz += *plen; + plen++; + nfrms++; + } + + if (xfer_sz % 4) { + pr_err("Invalid payload len: %d, ignored.\n", xfer_sz); + return -EPROTO; + } + return xfer_sz; +} + static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) { int rx_sz = 0; @@ -517,8 +546,10 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) static void cfhsi_rx_done(struct cfhsi *cfhsi) { int res; - int desc_pld_len = 0; + int desc_pld_len = 0, rx_len, rx_state; struct cfhsi_desc *desc = NULL; + u8 *rx_ptr, *rx_buf; + struct cfhsi_desc *piggy_desc = NULL; desc = (struct cfhsi_desc *)cfhsi->rx_buf; @@ -534,65 +565,71 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) spin_unlock_bh(&cfhsi->lock); if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) { - desc_pld_len = cfhsi_rx_desc(desc, cfhsi); - if (desc_pld_len == -ENOMEM) - goto restart; - if (desc_pld_len == -EPROTO) + desc_pld_len = cfhsi_rx_desc_len(desc); + + if (desc_pld_len < 0) goto out_of_sync; + + rx_buf = cfhsi->rx_buf; + rx_len = desc_pld_len; + if (desc_pld_len > 0 && (desc->header & CFHSI_PIGGY_DESC)) + rx_len += CFHSI_DESC_SZ; + if (desc_pld_len == 0) + rx_buf = cfhsi->rx_flip_buf; } else { - int pld_len; + rx_buf = cfhsi->rx_flip_buf; - if (!cfhsi->rx_state.piggy_desc) { - pld_len = cfhsi_rx_pld(desc, cfhsi); - if (pld_len == -ENOMEM) - goto restart; - if (pld_len == -EPROTO) - goto out_of_sync; - cfhsi->rx_state.pld_len = pld_len; - } else { - pld_len = cfhsi->rx_state.pld_len; - } + rx_len = CFHSI_DESC_SZ; + if (cfhsi->rx_state.pld_len > 0 && + (desc->header & CFHSI_PIGGY_DESC)) { - if ((pld_len > 0) && (desc->header & CFHSI_PIGGY_DESC)) { - struct cfhsi_desc *piggy_desc; piggy_desc = (struct cfhsi_desc *) (desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ + - pld_len); + cfhsi->rx_state.pld_len); + cfhsi->rx_state.piggy_desc = true; - /* Extract piggy-backed descriptor. */ - desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi); - if (desc_pld_len == -ENOMEM) - goto restart; + /* Extract payload len from piggy-backed descriptor. */ + desc_pld_len = cfhsi_rx_desc_len(piggy_desc); + if (desc_pld_len < 0) + goto out_of_sync; + + if (desc_pld_len > 0) + rx_len = desc_pld_len; + + if (desc_pld_len > 0 && + (piggy_desc->header & CFHSI_PIGGY_DESC)) + rx_len += CFHSI_DESC_SZ; /* * Copy needed information from the piggy-backed * descriptor to the descriptor in the start. */ - memcpy((u8 *)desc, (u8 *)piggy_desc, + memcpy(rx_buf, (u8 *)piggy_desc, CFHSI_DESC_SHORT_SZ); - + /* Mark no embedded frame here */ + piggy_desc->offset = 0; if (desc_pld_len == -EPROTO) goto out_of_sync; } } - memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state)); if (desc_pld_len) { - cfhsi->rx_state.state = CFHSI_RX_STATE_PAYLOAD; - cfhsi->rx_ptr = cfhsi->rx_buf + CFHSI_DESC_SZ; - cfhsi->rx_len = desc_pld_len; + rx_state = CFHSI_RX_STATE_PAYLOAD; + rx_ptr = rx_buf + CFHSI_DESC_SZ; } else { - cfhsi->rx_state.state = CFHSI_RX_STATE_DESC; - cfhsi->rx_ptr = cfhsi->rx_buf; - cfhsi->rx_len = CFHSI_DESC_SZ; + rx_state = CFHSI_RX_STATE_DESC; + rx_ptr = rx_buf; + rx_len = CFHSI_DESC_SZ; } + /* Initiate next read */ if (test_bit(CFHSI_AWAKE, &cfhsi->bits)) { /* Set up new transfer. */ dev_dbg(&cfhsi->ndev->dev, "%s: Start RX.\n", - __func__); - res = cfhsi->dev->cfhsi_rx(cfhsi->rx_ptr, cfhsi->rx_len, + __func__); + + res = cfhsi->dev->cfhsi_rx(rx_ptr, rx_len, cfhsi->dev); if (WARN_ON(res < 0)) { dev_err(&cfhsi->ndev->dev, "%s: RX error %d.\n", @@ -601,16 +638,32 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) cfhsi->ndev->stats.rx_dropped++; } } - return; -restart: - if (++cfhsi->rx_state.retries > CFHSI_MAX_RX_RETRIES) { - dev_err(&cfhsi->ndev->dev, "%s: No memory available " - "in %d iterations.\n", - __func__, CFHSI_MAX_RX_RETRIES); - BUG(); + if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) { + /* Extract payload from descriptor */ + if (cfhsi_rx_desc(desc, cfhsi) < 0) + goto out_of_sync; + } else { + /* Extract payload */ + if (cfhsi_rx_pld(desc, cfhsi) < 0) + goto out_of_sync; + if (piggy_desc) { + /* Extract any payload in piggyback descriptor. */ + if (cfhsi_rx_desc(piggy_desc, cfhsi) < 0) + goto out_of_sync; + } } - mod_timer(&cfhsi->rx_slowpath_timer, jiffies + 1); + + /* Update state info */ + memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state)); + cfhsi->rx_state.state = rx_state; + cfhsi->rx_ptr = rx_ptr; + cfhsi->rx_len = rx_len; + cfhsi->rx_state.pld_len = desc_pld_len; + cfhsi->rx_state.piggy_desc = desc->header & CFHSI_PIGGY_DESC; + + if (rx_buf != cfhsi->rx_buf) + swap(cfhsi->rx_buf, cfhsi->rx_flip_buf); return; out_of_sync: @@ -1040,6 +1093,12 @@ int cfhsi_probe(struct platform_device *pdev) goto err_alloc_rx; } + cfhsi->rx_flip_buf = kzalloc(CFHSI_BUF_SZ_RX, GFP_KERNEL); + if (!cfhsi->rx_flip_buf) { + res = -ENODEV; + goto err_alloc_rx_flip; + } + /* Pre-calculate inactivity timeout. */ if (inactivity_timeout != -1) { cfhsi->inactivity_timeout = @@ -1138,6 +1197,8 @@ int cfhsi_probe(struct platform_device *pdev) err_activate: destroy_workqueue(cfhsi->wq); err_create_wq: + kfree(cfhsi->rx_flip_buf); + err_alloc_rx_flip: kfree(cfhsi->rx_buf); err_alloc_rx: kfree(cfhsi->tx_buf); diff --git a/include/net/caif/caif_hsi.h b/include/net/caif/caif_hsi.h index 8d552519ff67..6db8ecf52aa2 100644 --- a/include/net/caif/caif_hsi.h +++ b/include/net/caif/caif_hsi.h @@ -138,6 +138,7 @@ struct cfhsi { u8 *rx_ptr; u8 *tx_buf; u8 *rx_buf; + u8 *rx_flip_buf; spinlock_t lock; int flow_off_sent; u32 q_low_mark; -- cgit From 42481ba29033b3e692fe81e6536fc8f59b40ce07 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sat, 4 Feb 2012 23:29:19 +0100 Subject: Remove incorrect comment from include/trace/events/power.h The code is not going to be removed, so remove the comment stating that it will be. Signed-off-by: Jesper Juhl Signed-off-by: Jiri Kosina --- include/trace/events/power.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/trace/events/power.h b/include/trace/events/power.h index b2626999e580..5800eb848827 100644 --- a/include/trace/events/power.h +++ b/include/trace/events/power.h @@ -65,7 +65,6 @@ TRACE_EVENT(machine_suspend, TP_printk("state=%lu", (unsigned long)__entry->state) ); -/* This code will be removed after deprecation time exceeded (3.1) */ #ifdef CONFIG_EVENT_POWER_TRACING_DEPRECATED /* -- cgit From f09603a259ffef69ad4516a04eb06cd65ac522fe Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 20 Jan 2012 13:55:21 +0100 Subject: mac80211: add sta_state callback (based on Eliad's patch) Add a callback to notify the low-level driver whenever the state of a station changes. The driver is only notified when the station is actually in the mac80211 hash table, not for pre-insert state transitions. To allow the driver to replace sta_add/remove calls with this, call extra transitions with the NOTEXIST state. This callback can fail, so we need to be careful in handling it when a station is inserted, particularly in the IBSS case where we still keep the station entry around for mac80211 purposes. Signed-off-by: Eliad Peller Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 30 +++++++++++++ net/mac80211/driver-ops.h | 22 +++++++++ net/mac80211/driver-trace.h | 32 +++++++++++++ net/mac80211/main.c | 3 ++ net/mac80211/pm.c | 10 ++++- net/mac80211/sta_info.c | 106 +++++++++++++++++++++++++++++++++++++------- net/mac80211/sta_info.h | 9 ---- net/mac80211/util.c | 10 ++++- 8 files changed, 194 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 520eb4c5e5a2..922313f0b39b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -981,6 +981,25 @@ enum set_key_cmd { SET_KEY, DISABLE_KEY, }; +/** + * enum ieee80211_sta_state - station state + * + * @IEEE80211_STA_NOTEXIST: station doesn't exist at all, + * this is a special state for add/remove transitions + * @IEEE80211_STA_NONE: station exists without special state + * @IEEE80211_STA_AUTH: station is authenticated + * @IEEE80211_STA_ASSOC: station is associated + * @IEEE80211_STA_AUTHORIZED: station is authorized (802.1X) + */ +enum ieee80211_sta_state { + /* NOTE: These need to be ordered correctly! */ + IEEE80211_STA_NOTEXIST, + IEEE80211_STA_NONE, + IEEE80211_STA_AUTH, + IEEE80211_STA_ASSOC, + IEEE80211_STA_AUTHORIZED, +}; + /** * struct ieee80211_sta - station table entry * @@ -1974,6 +1993,13 @@ enum ieee80211_frame_release_type { * in AP mode, this callback will not be called when the flag * %IEEE80211_HW_AP_LINK_PS is set. Must be atomic. * + * @sta_state: Notifies low level driver about state transition of a + * station (which can be the AP, a client, IBSS/WDS/mesh peer etc.) + * This callback is mutually exclusive with @sta_add/@sta_remove. + * It must not fail for down transitions but may fail for transitions + * up the list of states. + * The callback can sleep. + * * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max), * bursting) for a hardware TX queue. * Returns a negative error code on failure. @@ -2193,6 +2219,10 @@ struct ieee80211_ops { struct ieee80211_sta *sta); void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd, struct ieee80211_sta *sta); + int (*sta_state)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state); int (*conf_tx)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index b8673f6100df..4bd266ec5333 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -478,6 +478,28 @@ static inline void drv_sta_remove(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline __must_check +int drv_sta_state(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + int ret = 0; + + might_sleep(); + + sdata = get_bss_sdata(sdata); + check_sdata_in_driver(sdata); + + trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state); + if (local->ops->sta_state) + ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta, + old_state, new_state); + trace_drv_return_int(local, ret); + return ret; +} + static inline int drv_conf_tx(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, u16 queue, const struct ieee80211_tx_queue_params *params) diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 6e9df8fd8fb8..384e2f08c187 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -635,6 +635,38 @@ TRACE_EVENT(drv_sta_notify, ) ); +TRACE_EVENT(drv_sta_state, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state), + + TP_ARGS(local, sdata, sta, old_state, new_state), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + STA_ENTRY + __field(u32, old_state) + __field(u32, new_state) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + STA_ASSIGN; + __entry->old_state = old_state; + __entry->new_state = new_state; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " state: %d->%d", + LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, + __entry->old_state, __entry->new_state + ) +); + TRACE_EVENT(drv_sta_add, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 6192caadfab9..f4fc540aac17 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -535,6 +535,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, int priv_size, i; struct wiphy *wiphy; + if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove))) + return NULL; + /* Ensure 32-byte alignment of our private data and hw private data. * We use the wiphy priv data for both our ieee80211_local and for * the driver's private data diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index c65ff471acce..af49ac4f0826 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -97,9 +97,17 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* tear down aggregation sessions and remove STAs */ mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { - if (sta->uploaded) + if (sta->uploaded) { + enum ieee80211_sta_state state; + drv_sta_remove(local, sta->sdata, &sta->sta); + state = sta->sta_state; + for (; state > IEEE80211_STA_NOTEXIST; state--) + WARN_ON(drv_sta_state(local, sdata, sta, + state, state - 1)); + } + mesh_plink_quiesce(sta); } mutex_unlock(&local->sta_mtx); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 464bc691644b..fcd9027c6699 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -351,6 +351,38 @@ static int sta_info_insert_check(struct sta_info *sta) return 0; } +static int sta_info_insert_drv_state(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct sta_info *sta) +{ + enum ieee80211_sta_state state; + int err = 0; + + for (state = IEEE80211_STA_NOTEXIST; state < sta->sta_state; state++) { + err = drv_sta_state(local, sdata, sta, state, state + 1); + if (err) + break; + } + + if (!err) { + sta->uploaded = true; + return 0; + } + + if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { + printk(KERN_DEBUG + "%s: failed to move IBSS STA %pM to state %d (%d) - keeping it anyway.\n", + sdata->name, sta->sta.addr, state + 1, err); + err = 0; + } + + /* unwind on error */ + for (; state > IEEE80211_STA_NOTEXIST; state--) + WARN_ON(drv_sta_state(local, sdata, sta, state, state - 1)); + + return err; +} + /* * should be called with sta_mtx locked * this function replaces the mutex lock @@ -392,8 +424,11 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to " "driver (%d) - keeping it anyway.\n", sdata->name, sta->sta.addr, err); - } else - sta->uploaded = true; + } else { + err = sta_info_insert_drv_state(local, sdata, sta); + if (err) + goto out_err; + } } if (!dummy_reinsert) { @@ -759,15 +794,19 @@ int __must_check __sta_info_destroy(struct sta_info *sta) RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); while (sta->sta_state > IEEE80211_STA_NONE) { - int err = sta_info_move_state(sta, sta->sta_state - 1); - if (err) { + ret = sta_info_move_state(sta, sta->sta_state - 1); + if (ret) { WARN_ON_ONCE(1); break; } } - if (sta->uploaded) + if (sta->uploaded) { drv_sta_remove(local, sdata, &sta->sta); + ret = drv_sta_state(local, sdata, sta, IEEE80211_STA_NONE, + IEEE80211_STA_NOTEXIST); + WARN_ON_ONCE(ret != 0); + } /* * At this point, after we wait for an RCU grace period, @@ -1404,20 +1443,58 @@ int sta_info_move_state(struct sta_info *sta, if (sta->sta_state == new_state) return 0; + /* check allowed transitions first */ + + switch (new_state) { + case IEEE80211_STA_NONE: + if (sta->sta_state != IEEE80211_STA_AUTH) + return -EINVAL; + break; + case IEEE80211_STA_AUTH: + if (sta->sta_state != IEEE80211_STA_NONE && + sta->sta_state != IEEE80211_STA_ASSOC) + return -EINVAL; + break; + case IEEE80211_STA_ASSOC: + if (sta->sta_state != IEEE80211_STA_AUTH && + sta->sta_state != IEEE80211_STA_AUTHORIZED) + return -EINVAL; + break; + case IEEE80211_STA_AUTHORIZED: + if (sta->sta_state != IEEE80211_STA_ASSOC) + return -EINVAL; + break; + default: + WARN(1, "invalid state %d", new_state); + return -EINVAL; + } + + printk(KERN_DEBUG "%s: moving STA %pM to state %d\n", + sta->sdata->name, sta->sta.addr, new_state); + + /* + * notify the driver before the actual changes so it can + * fail the transition + */ + if (test_sta_flag(sta, WLAN_STA_INSERTED)) { + int err = drv_sta_state(sta->local, sta->sdata, sta, + sta->sta_state, new_state); + if (err) + return err; + } + + /* reflect the change in all state variables */ + switch (new_state) { case IEEE80211_STA_NONE: if (sta->sta_state == IEEE80211_STA_AUTH) clear_bit(WLAN_STA_AUTH, &sta->_flags); - else - return -EINVAL; break; case IEEE80211_STA_AUTH: if (sta->sta_state == IEEE80211_STA_NONE) set_bit(WLAN_STA_AUTH, &sta->_flags); else if (sta->sta_state == IEEE80211_STA_ASSOC) clear_bit(WLAN_STA_ASSOC, &sta->_flags); - else - return -EINVAL; break; case IEEE80211_STA_ASSOC: if (sta->sta_state == IEEE80211_STA_AUTH) { @@ -1426,24 +1503,19 @@ int sta_info_move_state(struct sta_info *sta, if (sta->sdata->vif.type == NL80211_IFTYPE_AP) atomic_dec(&sta->sdata->u.ap.num_sta_authorized); clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags); - } else - return -EINVAL; + } break; case IEEE80211_STA_AUTHORIZED: if (sta->sta_state == IEEE80211_STA_ASSOC) { if (sta->sdata->vif.type == NL80211_IFTYPE_AP) atomic_inc(&sta->sdata->u.ap.num_sta_authorized); set_bit(WLAN_STA_AUTHORIZED, &sta->_flags); - } else - return -EINVAL; + } break; default: - WARN(1, "invalid state %d", new_state); - return -EINVAL; + break; } - printk(KERN_DEBUG "%s: moving STA %pM to state %d\n", - sta->sdata->name, sta->sta.addr, new_state); sta->sta_state = new_state; return 0; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index da4b03c1c3bc..2ee808860007 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -75,15 +75,6 @@ enum ieee80211_sta_info_flags { WLAN_STA_INSERTED, }; -enum ieee80211_sta_state { - /* NOTE: These need to be ordered correctly! */ - IEEE80211_STA_NOTEXIST, - IEEE80211_STA_NONE, - IEEE80211_STA_AUTH, - IEEE80211_STA_ASSOC, - IEEE80211_STA_AUTHORIZED, -}; - #define STA_TID_NUM 16 #define ADDBA_RESP_INTERVAL HZ #define HT_AGG_MAX_RETRIES 15 diff --git a/net/mac80211/util.c b/net/mac80211/util.c index d4966e88aa49..8f8b4ecc776f 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1184,8 +1184,16 @@ int ieee80211_reconfig(struct ieee80211_local *local) /* add STAs back */ mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { - if (sta->uploaded) + if (sta->uploaded) { + enum ieee80211_sta_state state; + WARN_ON(drv_sta_add(local, sta->sdata, &sta->sta)); + + for (state = IEEE80211_STA_NOTEXIST; + state < sta->sta_state - 1; state++) + WARN_ON(drv_sta_state(local, sta->sdata, sta, + state, state + 1)); + } } mutex_unlock(&local->sta_mtx); -- cgit From 95de817b9034d50860319f6033ec85d25024694c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 20 Jan 2012 13:55:25 +0100 Subject: cfg80211: stop tracking authenticated state To track authenticated state seems to have been a design mistake in cfg80211. It is possible to have out of band authentication (FT), tracking multiple authentications caused more problems than it ever helped, and the implementation in mac80211 is too complex. Remove all this complexity, and let userspace do whatever it wants to, mac80211 can deal with that just fine. Association is still tracked of course, but authentication no longer is. Local auth state changes are thus no longer of value, so ignore them completely. This will also help implement SAE -- asking the driver to do an authentication is now almost equivalent to sending an authentication frame, with the exception of shared key authentication which is still handled completely. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- Documentation/DocBook/80211.tmpl | 1 - include/net/cfg80211.h | 39 ++--- net/mac80211/mlme.c | 23 +-- net/wireless/core.h | 9 +- net/wireless/mlme.c | 322 +++++++-------------------------------- net/wireless/nl80211.c | 18 +-- net/wireless/sme.c | 41 +---- 7 files changed, 91 insertions(+), 362 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl index 2014155c899d..c5ac6929c41c 100644 --- a/Documentation/DocBook/80211.tmpl +++ b/Documentation/DocBook/80211.tmpl @@ -129,7 +129,6 @@ !Finclude/net/cfg80211.h cfg80211_pmksa !Finclude/net/cfg80211.h cfg80211_send_rx_auth !Finclude/net/cfg80211.h cfg80211_send_auth_timeout -!Finclude/net/cfg80211.h __cfg80211_auth_canceled !Finclude/net/cfg80211.h cfg80211_send_rx_assoc !Finclude/net/cfg80211.h cfg80211_send_assoc_timeout !Finclude/net/cfg80211.h cfg80211_send_deauth diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 2964205332f4..6cfecb02a34b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1039,10 +1039,6 @@ const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie); * @key_len: length of WEP key for shared key authentication * @key_idx: index of WEP key for shared key authentication * @key: WEP key for shared key authentication - * @local_state_change: This is a request for a local state only, i.e., no - * Authentication frame is to be transmitted and authentication state is - * to be changed without having to wait for a response from the peer STA - * (AP). */ struct cfg80211_auth_request { struct cfg80211_bss *bss; @@ -1051,7 +1047,6 @@ struct cfg80211_auth_request { enum nl80211_auth_type auth_type; const u8 *key; u8 key_len, key_idx; - bool local_state_change; }; /** @@ -1068,7 +1063,11 @@ enum cfg80211_assoc_req_flags { * * This structure provides information needed to complete IEEE 802.11 * (re)association. - * @bss: The BSS to associate with. + * @bss: The BSS to associate with. If the call is successful the driver + * is given a reference that it must release, normally via a call to + * cfg80211_send_rx_assoc(), or, if association timed out, with a + * call to cfg80211_put_bss() (in addition to calling + * cfg80211_send_assoc_timeout()) * @ie: Extra IEs to add to (Re)Association Request frame or %NULL * @ie_len: Length of ie buffer in octets * @use_mfp: Use management frame protection (IEEE 802.11w) in this association @@ -1096,19 +1095,16 @@ struct cfg80211_assoc_request { * This structure provides information needed to complete IEEE 802.11 * deauthentication. * - * @bss: the BSS to deauthenticate from + * @bssid: the BSSID of the BSS to deauthenticate from * @ie: Extra IEs to add to Deauthentication frame or %NULL * @ie_len: Length of ie buffer in octets * @reason_code: The reason code for the deauthentication - * @local_state_change: This is a request for a local state only, i.e., no - * Deauthentication frame is to be transmitted. */ struct cfg80211_deauth_request { - struct cfg80211_bss *bss; + const u8 *bssid; const u8 *ie; size_t ie_len; u16 reason_code; - bool local_state_change; }; /** @@ -2206,8 +2202,6 @@ struct cfg80211_conn; struct cfg80211_internal_bss; struct cfg80211_cached_keys; -#define MAX_AUTH_BSSES 4 - /** * struct wireless_dev - wireless per-netdev state * @@ -2271,8 +2265,6 @@ struct wireless_dev { struct list_head event_list; spinlock_t event_lock; - struct cfg80211_internal_bss *authtry_bsses[MAX_AUTH_BSSES]; - struct cfg80211_internal_bss *auth_bsses[MAX_AUTH_BSSES]; struct cfg80211_internal_bss *current_bss; /* associated / joined */ struct ieee80211_channel *channel; @@ -2763,21 +2755,11 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len); */ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr); -/** - * __cfg80211_auth_canceled - notify cfg80211 that authentication was canceled - * @dev: network device - * @addr: The MAC address of the device with which the authentication timed out - * - * When a pending authentication had no action yet, the driver may decide - * to not send a deauth frame, but in that case must calls this function - * to tell cfg80211 about this decision. It is only valid to call this - * function within the deauth() callback. - */ -void __cfg80211_auth_canceled(struct net_device *dev, const u8 *addr); - /** * cfg80211_send_rx_assoc - notification of processed association * @dev: network device + * @bss: the BSS struct association was requested for, the struct reference + * is owned by cfg80211 after this call * @buf: (re)association response frame (header + body) * @len: length of the frame data * @@ -2786,7 +2768,8 @@ void __cfg80211_auth_canceled(struct net_device *dev, const u8 *addr); * function or cfg80211_send_assoc_timeout() to indicate the result of * cfg80211_ops::assoc() call. This function may sleep. */ -void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len); +void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss, + const u8 *buf, size_t len); /** * cfg80211_send_assoc_timeout - notification of timed out association diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d04811a29cdf..082fcda57786 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2459,9 +2459,6 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, struct ieee80211_work *wk; u16 auth_alg; - if (req->local_state_change) - return 0; /* no need to update mac80211 state */ - switch (req->auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: auth_alg = WLAN_AUTH_OPEN; @@ -2593,7 +2590,7 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, sta_info_destroy_addr(wk->sdata, cbss->bssid); } - cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len); + cfg80211_send_rx_assoc(wk->sdata->dev, cbss, skb->data, skb->len); destroy: if (wk->assoc.synced) drv_finish_tx_sync(local, wk->sdata, wk->filter_ta, @@ -2750,13 +2747,12 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - u8 bssid[ETH_ALEN]; bool assoc_bss = false; mutex_lock(&ifmgd->mtx); - memcpy(bssid, req->bss->bssid, ETH_ALEN); - if (ifmgd->associated == req->bss) { + if (ifmgd->associated && + memcmp(ifmgd->associated->bssid, req->bssid, ETH_ALEN) == 0) { ieee80211_set_disassoc(sdata, false, true); mutex_unlock(&ifmgd->mtx); assoc_bss = true; @@ -2777,7 +2773,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, tmp->type != IEEE80211_WORK_ASSOC_BEACON_WAIT) continue; - if (memcmp(req->bss->bssid, tmp->filter_ta, ETH_ALEN)) + if (memcmp(req->bssid, tmp->filter_ta, ETH_ALEN)) continue; not_auth_yet = tmp->type == IEEE80211_WORK_DIRECT_PROBE; @@ -2811,18 +2807,15 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, * frame, and if it's IDLE we have completed the auth * process already. */ - if (not_auth_yet) { - __cfg80211_auth_canceled(sdata->dev, bssid); + if (not_auth_yet) return 0; - } } printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n", - sdata->name, bssid, req->reason_code); + sdata->name, req->bssid, req->reason_code); - ieee80211_send_deauth_disassoc(sdata, bssid, IEEE80211_STYPE_DEAUTH, - req->reason_code, cookie, - !req->local_state_change); + ieee80211_send_deauth_disassoc(sdata, req->bssid, IEEE80211_STYPE_DEAUTH, + req->reason_code, cookie, true); if (assoc_bss) sta_info_flush(sdata->local, sdata); diff --git a/net/wireless/core.h b/net/wireless/core.h index 43ad9c81efcf..2b454caf4395 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -325,15 +325,13 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx, - bool local_state_change); + const u8 *key, int key_len, int key_idx); int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx, - bool local_state_change); + const u8 *key, int key_len, int key_idx); int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, @@ -421,7 +419,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap); void cfg80211_sme_scan_done(struct net_device *dev); void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); -void cfg80211_sme_disassoc(struct net_device *dev, int idx); +void cfg80211_sme_disassoc(struct net_device *dev, + struct cfg80211_internal_bss *bss); void __cfg80211_scan_done(struct work_struct *wk); void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); void __cfg80211_sched_scan_results(struct work_struct *wk); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 438dfc105b4a..d553d365e751 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -20,40 +20,18 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; - u8 *bssid = mgmt->bssid; - int i; - u16 status = le16_to_cpu(mgmt->u.auth.status_code); - bool done = false; wdev_lock(wdev); - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->authtry_bsses[i] && - memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, - ETH_ALEN) == 0) { - if (status == WLAN_STATUS_SUCCESS) { - wdev->auth_bsses[i] = wdev->authtry_bsses[i]; - } else { - cfg80211_unhold_bss(wdev->authtry_bsses[i]); - cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); - } - wdev->authtry_bsses[i] = NULL; - done = true; - break; - } - } - - if (done) { - nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL); - cfg80211_sme_rx_auth(dev, buf, len); - } + nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL); + cfg80211_sme_rx_auth(dev, buf, len); wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_rx_auth); -void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) +void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss, + const u8 *buf, size_t len) { u16 status_code; struct wireless_dev *wdev = dev->ieee80211_ptr; @@ -61,8 +39,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; u8 *ie = mgmt->u.assoc_resp.variable; - int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); - struct cfg80211_internal_bss *bss = NULL; + int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); wdev_lock(wdev); @@ -75,43 +52,20 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) * frame instead of reassoc. */ if (status_code != WLAN_STATUS_SUCCESS && wdev->conn && - cfg80211_sme_failed_reassoc(wdev)) + cfg80211_sme_failed_reassoc(wdev)) { + cfg80211_put_bss(bss); goto out; + } nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); - if (status_code == WLAN_STATUS_SUCCESS) { - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (!wdev->auth_bsses[i]) - continue; - if (memcmp(wdev->auth_bsses[i]->pub.bssid, mgmt->bssid, - ETH_ALEN) == 0) { - bss = wdev->auth_bsses[i]; - wdev->auth_bsses[i] = NULL; - /* additional reference to drop hold */ - cfg80211_ref_bss(bss); - break; - } - } - - /* - * We might be coming here because the driver reported - * a successful association at the same time as the - * user requested a deauth. In that case, we will have - * removed the BSS from the auth_bsses list due to the - * deauth request when the assoc response makes it. If - * the two code paths acquire the lock the other way - * around, that's just the standard situation of a - * deauth being requested while connected. - */ - if (!bss) - goto out; - } else if (wdev->conn) { + if (status_code != WLAN_STATUS_SUCCESS && wdev->conn) { cfg80211_sme_failed_assoc(wdev); /* * do not call connect_result() now because the * sme will schedule work that does it later. */ + cfg80211_put_bss(bss); goto out; } @@ -124,17 +78,10 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) wdev->sme_state = CFG80211_SME_CONNECTING; } - /* this consumes one bss reference (unless bss is NULL) */ + /* this consumes the bss reference */ __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, status_code, - status_code == WLAN_STATUS_SUCCESS, - bss ? &bss->pub : NULL); - /* drop hold now, and also reference acquired above */ - if (bss) { - cfg80211_unhold_bss(bss); - cfg80211_put_bss(&bss->pub); - } - + status_code == WLAN_STATUS_SUCCESS, bss); out: wdev_unlock(wdev); } @@ -148,8 +95,7 @@ void __cfg80211_send_deauth(struct net_device *dev, struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; const u8 *bssid = mgmt->bssid; - int i; - bool found = false, was_current = false; + bool was_current = false; ASSERT_WDEV_LOCK(wdev); @@ -158,32 +104,9 @@ void __cfg80211_send_deauth(struct net_device *dev, cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(&wdev->current_bss->pub); wdev->current_bss = NULL; - found = true; was_current = true; - } else for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->auth_bsses[i] && - memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { - cfg80211_unhold_bss(wdev->auth_bsses[i]); - cfg80211_put_bss(&wdev->auth_bsses[i]->pub); - wdev->auth_bsses[i] = NULL; - found = true; - break; - } - if (wdev->authtry_bsses[i] && - memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, - ETH_ALEN) == 0 && - memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) == 0) { - cfg80211_unhold_bss(wdev->authtry_bsses[i]); - cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); - wdev->authtry_bsses[i] = NULL; - found = true; - break; - } } - if (!found) - return; - nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL); if (wdev->sme_state == CFG80211_SME_CONNECTED && was_current) { @@ -220,10 +143,8 @@ void __cfg80211_send_disassoc(struct net_device *dev, struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; const u8 *bssid = mgmt->bssid; - int i; u16 reason_code; bool from_ap; - bool done = false; ASSERT_WDEV_LOCK(wdev); @@ -234,16 +155,10 @@ void __cfg80211_send_disassoc(struct net_device *dev, if (wdev->current_bss && memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->authtry_bsses[i] || wdev->auth_bsses[i]) - continue; - wdev->auth_bsses[i] = wdev->current_bss; - wdev->current_bss = NULL; - done = true; - cfg80211_sme_disassoc(dev, i); - break; - } - WARN_ON(!done); + cfg80211_sme_disassoc(dev, wdev->current_bss); + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; } else WARN_ON(1); @@ -287,34 +202,6 @@ void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf, } EXPORT_SYMBOL(cfg80211_send_unprot_disassoc); -static void __cfg80211_auth_remove(struct wireless_dev *wdev, const u8 *addr) -{ - int i; - bool done = false; - - ASSERT_WDEV_LOCK(wdev); - - for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { - if (wdev->authtry_bsses[i] && - memcmp(wdev->authtry_bsses[i]->pub.bssid, - addr, ETH_ALEN) == 0) { - cfg80211_unhold_bss(wdev->authtry_bsses[i]); - cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); - wdev->authtry_bsses[i] = NULL; - done = true; - break; - } - } - - WARN_ON(!done); -} - -void __cfg80211_auth_canceled(struct net_device *dev, const u8 *addr) -{ - __cfg80211_auth_remove(dev->ieee80211_ptr, addr); -} -EXPORT_SYMBOL(__cfg80211_auth_canceled); - void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) { struct wireless_dev *wdev = dev->ieee80211_ptr; @@ -329,8 +216,6 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) WLAN_STATUS_UNSPECIFIED_FAILURE, false, NULL); - __cfg80211_auth_remove(wdev, addr); - wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_auth_timeout); @@ -340,8 +225,6 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - int i; - bool done = false; wdev_lock(wdev); @@ -351,20 +234,6 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) WLAN_STATUS_UNSPECIFIED_FAILURE, false, NULL); - for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { - if (wdev->auth_bsses[i] && - memcmp(wdev->auth_bsses[i]->pub.bssid, - addr, ETH_ALEN) == 0) { - cfg80211_unhold_bss(wdev->auth_bsses[i]); - cfg80211_put_bss(&wdev->auth_bsses[i]->pub); - wdev->auth_bsses[i] = NULL; - done = true; - break; - } - } - - WARN_ON(!done); - wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_assoc_timeout); @@ -403,13 +272,11 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx, - bool local_state_change) + const u8 *key, int key_len, int key_idx) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_auth_request req; - struct cfg80211_internal_bss *bss; - int i, err, slot = -1, nfree = 0; + int err; ASSERT_WDEV_LOCK(wdev); @@ -421,20 +288,8 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0) return -EALREADY; - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->authtry_bsses[i] && - memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, - ETH_ALEN) == 0) - return -EALREADY; - if (wdev->auth_bsses[i] && - memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, - ETH_ALEN) == 0) - return -EALREADY; - } - memset(&req, 0, sizeof(req)); - req.local_state_change = local_state_change; req.ie = ie; req.ie_len = ie_len; req.auth_type = auth_type; @@ -446,39 +301,9 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, if (!req.bss) return -ENOENT; - bss = bss_from_pub(req.bss); - - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) { - slot = i; - nfree++; - } - } - - /* we need one free slot for disassoc and one for this auth */ - if (nfree < 2) { - err = -ENOSPC; - goto out; - } - - if (local_state_change) - wdev->auth_bsses[slot] = bss; - else - wdev->authtry_bsses[slot] = bss; - cfg80211_hold_bss(bss); - err = rdev->ops->auth(&rdev->wiphy, dev, &req); - if (err) { - if (local_state_change) - wdev->auth_bsses[slot] = NULL; - else - wdev->authtry_bsses[slot] = NULL; - cfg80211_unhold_bss(bss); - } - out: - if (err) - cfg80211_put_bss(req.bss); + cfg80211_put_bss(req.bss); return err; } @@ -487,15 +312,14 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, enum nl80211_auth_type auth_type, const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx, - bool local_state_change) + const u8 *key, int key_len, int key_idx) { int err; wdev_lock(dev->ieee80211_ptr); err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, ssid, ssid_len, ie, ie_len, - key, key_len, key_idx, local_state_change); + key, key_len, key_idx); wdev_unlock(dev->ieee80211_ptr); return err; @@ -530,8 +354,7 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_assoc_request req; - struct cfg80211_internal_bss *bss; - int i, err, slot = -1; + int err; bool was_connected = false; ASSERT_WDEV_LOCK(wdev); @@ -573,26 +396,14 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, return -ENOENT; } - bss = bss_from_pub(req.bss); - - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (bss == wdev->auth_bsses[i]) { - slot = i; - break; - } - } + err = rdev->ops->assoc(&rdev->wiphy, dev, &req); - if (slot < 0) { - err = -ENOTCONN; - goto out; + if (err) { + if (was_connected) + wdev->sme_state = CFG80211_SME_CONNECTED; + cfg80211_put_bss(req.bss); } - err = rdev->ops->assoc(&rdev->wiphy, dev, &req); - out: - if (err && was_connected) - wdev->sme_state = CFG80211_SME_CONNECTED; - /* still a reference in wdev->auth_bsses[slot] */ - cfg80211_put_bss(req.bss); return err; } @@ -624,34 +435,25 @@ int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, bool local_state_change) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_deauth_request req; - int i; + struct cfg80211_deauth_request req = { + .bssid = bssid, + .reason_code = reason, + .ie = ie, + .ie_len = ie_len, + }; ASSERT_WDEV_LOCK(wdev); - memset(&req, 0, sizeof(req)); - req.reason_code = reason; - req.local_state_change = local_state_change; - req.ie = ie; - req.ie_len = ie_len; - if (wdev->current_bss && - memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { - req.bss = &wdev->current_bss->pub; - } else for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->auth_bsses[i] && - memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) { - req.bss = &wdev->auth_bsses[i]->pub; - break; - } - if (wdev->authtry_bsses[i] && - memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) { - req.bss = &wdev->authtry_bsses[i]->pub; - break; + if (local_state_change) { + if (wdev->current_bss && + memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; } - } - if (!req.bss) - return -ENOTCONN; + return 0; + } return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); } @@ -722,7 +524,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_deauth_request req; - int i; + u8 bssid[ETH_ALEN]; ASSERT_WDEV_LOCK(wdev); @@ -734,35 +536,17 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, req.ie = NULL; req.ie_len = 0; - if (wdev->current_bss) { - req.bss = &wdev->current_bss->pub; - rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); - if (wdev->current_bss) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - wdev->current_bss = NULL; - } - } + if (!wdev->current_bss) + return; - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->auth_bsses[i]) { - req.bss = &wdev->auth_bsses[i]->pub; - rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); - if (wdev->auth_bsses[i]) { - cfg80211_unhold_bss(wdev->auth_bsses[i]); - cfg80211_put_bss(&wdev->auth_bsses[i]->pub); - wdev->auth_bsses[i] = NULL; - } - } - if (wdev->authtry_bsses[i]) { - req.bss = &wdev->authtry_bsses[i]->pub; - rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); - if (wdev->authtry_bsses[i]) { - cfg80211_unhold_bss(wdev->authtry_bsses[i]); - cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); - wdev->authtry_bsses[i] = NULL; - } - } + memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN); + req.bssid = bssid; + rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); + + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; } } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c910b0750dc2..e1fd1bf90729 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4083,7 +4083,6 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, struct cfg80211_bss *res = &intbss->pub; void *hdr; struct nlattr *bss; - int i; ASSERT_WDEV_LOCK(wdev); @@ -4136,13 +4135,6 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, if (intbss == wdev->current_bss) NLA_PUT_U32(msg, NL80211_BSS_STATUS, NL80211_BSS_STATUS_ASSOCIATED); - else for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (intbss != wdev->auth_bsses[i]) - continue; - NLA_PUT_U32(msg, NL80211_BSS_STATUS, - NL80211_BSS_STATUS_AUTHENTICATED); - break; - } break; case NL80211_IFTYPE_ADHOC: if (intbss == wdev->current_bss) @@ -4410,10 +4402,16 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; + /* + * Since we no longer track auth state, ignore + * requests to only change local state. + */ + if (local_state_change) + return 0; + return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, ssid, ssid_len, ie, ie_len, - key.p.key, key.p.key_len, key.idx, - local_state_change); + key.p.key, key.p.key_len, key.idx); } static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 7b9ecaed96be..f7e937ff8978 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -179,7 +179,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) params->ssid, params->ssid_len, NULL, 0, params->key, params->key_len, - params->key_idx, false); + params->key_idx); case CFG80211_CONN_ASSOCIATE_NEXT: BUG_ON(!rdev->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; @@ -477,6 +477,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, kfree(wdev->connect_keys); wdev->connect_keys = NULL; wdev->ssid_len = 0; + cfg80211_put_bss(bss); return; } @@ -701,31 +702,10 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, wdev->ssid_len = 0; if (wdev->conn) { - const u8 *bssid; - int ret; - kfree(wdev->conn->ie); wdev->conn->ie = NULL; kfree(wdev->conn); wdev->conn = NULL; - - /* - * If this disconnect was due to a disassoc, we - * we might still have an auth BSS around. For - * the userspace SME that's currently expected, - * but for the kernel SME (nl80211 CONNECT or - * wireless extensions) we want to clear up all - * state. - */ - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (!wdev->auth_bsses[i]) - continue; - bssid = wdev->auth_bsses[i]->pub.bssid; - ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, - WLAN_REASON_DEAUTH_LEAVING, - false); - WARN(ret, "deauth failed: %d\n", ret); - } } nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); @@ -1012,7 +992,8 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, return err; } -void cfg80211_sme_disassoc(struct net_device *dev, int idx) +void cfg80211_sme_disassoc(struct net_device *dev, + struct cfg80211_internal_bss *bss) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); @@ -1031,16 +1012,8 @@ void cfg80211_sme_disassoc(struct net_device *dev, int idx) * want it any more so deauthenticate too. */ - if (!wdev->auth_bsses[idx]) - return; + memcpy(bssid, bss->pub.bssid, ETH_ALEN); - memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN); - if (__cfg80211_mlme_deauth(rdev, dev, bssid, - NULL, 0, WLAN_REASON_DEAUTH_LEAVING, - false)) { - /* whatever -- assume gone anyway */ - cfg80211_unhold_bss(wdev->auth_bsses[idx]); - cfg80211_put_bss(&wdev->auth_bsses[idx]->pub); - wdev->auth_bsses[idx] = NULL; - } + __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, + WLAN_REASON_DEAUTH_LEAVING, false); } -- cgit From 4c0c0b75e0c35ddb8f61c06bcbffede63ab4f4a2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 20 Jan 2012 13:55:26 +0100 Subject: cfg80211: export cfg80211_ref_bss This is needed by mac80211 to keep a reference to a BSS alive for the auth process. Remove the old version of cfg80211_ref_bss() since it's not actually used. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 14 ++++++++++++++ net/wireless/core.h | 5 ----- net/wireless/scan.c | 12 ++++++++++++ 3 files changed, 26 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 6cfecb02a34b..229edc526cf5 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2719,6 +2719,20 @@ struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *meshid, size_t meshidlen, const u8 *meshcfg); +/** + * cfg80211_ref_bss - reference BSS struct + * @bss: the BSS struct to reference + * + * Increments the refcount of the given BSS struct. + */ +void cfg80211_ref_bss(struct cfg80211_bss *bss); + +/** + * cfg80211_put_bss - unref BSS struct + * @bss: the BSS struct + * + * Decrements the refcount of the given BSS struct. + */ void cfg80211_put_bss(struct cfg80211_bss *bss); /** diff --git a/net/wireless/core.h b/net/wireless/core.h index 2b454caf4395..3ac2dd00d714 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -144,11 +144,6 @@ static inline struct cfg80211_internal_bss *bss_from_pub(struct cfg80211_bss *pu return container_of(pub, struct cfg80211_internal_bss, pub); } -static inline void cfg80211_ref_bss(struct cfg80211_internal_bss *bss) -{ - kref_get(&bss->ref); -} - static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss) { atomic_inc(&bss->hold); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 31119e32e092..afde7e5f0010 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -861,6 +861,18 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, } EXPORT_SYMBOL(cfg80211_inform_bss_frame); +void cfg80211_ref_bss(struct cfg80211_bss *pub) +{ + struct cfg80211_internal_bss *bss; + + if (!pub) + return; + + bss = container_of(pub, struct cfg80211_internal_bss, pub); + kref_get(&bss->ref); +} +EXPORT_SYMBOL(cfg80211_ref_bss); + void cfg80211_put_bss(struct cfg80211_bss *pub) { struct cfg80211_internal_bss *bss; -- cgit From 5f2d6171e1e70584b9819771443485750453fd16 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 31 Jan 2012 00:03:31 +0100 Subject: bcma: add the core unit number Some SoCs have two pcie or gmac cores and we need to know the number of the specific core on the bus. This is the case for the BCM4706. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/scan.c | 14 ++++++++++++++ include/linux/bcma/bcma.h | 1 + 2 files changed, 15 insertions(+) (limited to 'include') diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c index cad994857683..e513aa83ec25 100644 --- a/drivers/bcma/scan.c +++ b/drivers/bcma/scan.c @@ -212,6 +212,17 @@ static struct bcma_device *bcma_find_core_by_index(struct bcma_bus *bus, return NULL; } +static struct bcma_device *bcma_find_core_reverse(struct bcma_bus *bus, u16 coreid) +{ + struct bcma_device *core; + + list_for_each_entry_reverse(core, &bus->cores, list) { + if (core->id.id == coreid) + return core; + } + return NULL; +} + static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr, struct bcma_device_id *match, int core_num, struct bcma_device *core) @@ -392,6 +403,7 @@ int bcma_bus_scan(struct bcma_bus *bus) bcma_scan_switch_core(bus, erombase); while (eromptr < eromend) { + struct bcma_device *other_core; struct bcma_device *core = kzalloc(sizeof(*core), GFP_KERNEL); if (!core) return -ENOMEM; @@ -411,6 +423,8 @@ int bcma_bus_scan(struct bcma_bus *bus) core->core_index = core_num++; bus->nr_cores++; + other_core = bcma_find_core_reverse(bus, core->id.id); + core->core_unit = (other_core == NULL) ? 0 : other_core->core_unit + 1; pr_info("Core %d found: %s " "(manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n", diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 83c209f39493..024a6e2a9083 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -136,6 +136,7 @@ struct bcma_device { bool dev_registered; u8 core_index; + u8 core_unit; u32 addr; u32 wrap; -- cgit From 2be25cac8402fab56bb51166f464d1b420bcf744 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 31 Jan 2012 00:03:32 +0100 Subject: bcma: add constants for PCI and use them There are many magic numbers used in the PCIe code. Replace them with some constants from the Broadcom SDK and also use them in the pcie host controller. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_pci.c | 124 ++++++++++++++++++++--------------- include/linux/bcma/bcma_driver_pci.h | 85 ++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c index 4fde6254f04e..fc462b4a0990 100644 --- a/drivers/bcma/driver_pci.c +++ b/drivers/bcma/driver_pci.c @@ -4,6 +4,7 @@ * * Copyright 2005, Broadcom Corporation * Copyright 2006, 2007, Michael Buesch + * Copyright 2011, 2012, Hauke Mehrtens * * Licensed under the GNU/GPL. See COPYING for details. */ @@ -18,38 +19,39 @@ static u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address) { - pcicore_write32(pc, 0x130, address); - pcicore_read32(pc, 0x130); - return pcicore_read32(pc, 0x134); + pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address); + pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR); + return pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_DATA); } #if 0 static void bcma_pcie_write(struct bcma_drv_pci *pc, u32 address, u32 data) { - pcicore_write32(pc, 0x130, address); - pcicore_read32(pc, 0x130); - pcicore_write32(pc, 0x134, data); + pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address); + pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR); + pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_DATA, data); } #endif static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy) { - const u16 mdio_control = 0x128; - const u16 mdio_data = 0x12C; u32 v; int i; - v = (1 << 30); /* Start of Transaction */ - v |= (1 << 28); /* Write Transaction */ - v |= (1 << 17); /* Turnaround */ - v |= (0x1F << 18); + v = BCMA_CORE_PCI_MDIODATA_START; + v |= BCMA_CORE_PCI_MDIODATA_WRITE; + v |= (BCMA_CORE_PCI_MDIODATA_DEV_ADDR << + BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF); + v |= (BCMA_CORE_PCI_MDIODATA_BLK_ADDR << + BCMA_CORE_PCI_MDIODATA_REGADDR_SHF); + v |= BCMA_CORE_PCI_MDIODATA_TA; v |= (phy << 4); - pcicore_write32(pc, mdio_data, v); + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v); udelay(10); for (i = 0; i < 200; i++) { - v = pcicore_read32(pc, mdio_control); - if (v & 0x100 /* Trans complete */) + v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL); + if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) break; msleep(1); } @@ -57,79 +59,84 @@ static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy) static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 device, u8 address) { - const u16 mdio_control = 0x128; - const u16 mdio_data = 0x12C; int max_retries = 10; u16 ret = 0; u32 v; int i; - v = 0x80; /* Enable Preamble Sequence */ - v |= 0x2; /* MDIO Clock Divisor */ - pcicore_write32(pc, mdio_control, v); + /* enable mdio access to SERDES */ + v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN; + v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL; + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v); if (pc->core->id.rev >= 10) { max_retries = 200; bcma_pcie_mdio_set_phy(pc, device); + v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR << + BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF); + v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF); + } else { + v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD); + v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD); } - v = (1 << 30); /* Start of Transaction */ - v |= (1 << 29); /* Read Transaction */ - v |= (1 << 17); /* Turnaround */ - if (pc->core->id.rev < 10) - v |= (u32)device << 22; - v |= (u32)address << 18; - pcicore_write32(pc, mdio_data, v); + v = BCMA_CORE_PCI_MDIODATA_START; + v |= BCMA_CORE_PCI_MDIODATA_READ; + v |= BCMA_CORE_PCI_MDIODATA_TA; + + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v); /* Wait for the device to complete the transaction */ udelay(10); for (i = 0; i < max_retries; i++) { - v = pcicore_read32(pc, mdio_control); - if (v & 0x100 /* Trans complete */) { + v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL); + if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) { udelay(10); - ret = pcicore_read32(pc, mdio_data); + ret = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_DATA); break; } msleep(1); } - pcicore_write32(pc, mdio_control, 0); + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0); return ret; } static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device, u8 address, u16 data) { - const u16 mdio_control = 0x128; - const u16 mdio_data = 0x12C; int max_retries = 10; u32 v; int i; - v = 0x80; /* Enable Preamble Sequence */ - v |= 0x2; /* MDIO Clock Divisor */ - pcicore_write32(pc, mdio_control, v); + /* enable mdio access to SERDES */ + v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN; + v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL; + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v); if (pc->core->id.rev >= 10) { max_retries = 200; bcma_pcie_mdio_set_phy(pc, device); + v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR << + BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF); + v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF); + } else { + v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD); + v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD); } - v = (1 << 30); /* Start of Transaction */ - v |= (1 << 28); /* Write Transaction */ - v |= (1 << 17); /* Turnaround */ - if (pc->core->id.rev < 10) - v |= (u32)device << 22; - v |= (u32)address << 18; + v = BCMA_CORE_PCI_MDIODATA_START; + v |= BCMA_CORE_PCI_MDIODATA_WRITE; + v |= BCMA_CORE_PCI_MDIODATA_TA; v |= data; - pcicore_write32(pc, mdio_data, v); + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v); /* Wait for the device to complete the transaction */ udelay(10); for (i = 0; i < max_retries; i++) { - v = pcicore_read32(pc, mdio_control); - if (v & 0x100 /* Trans complete */) + v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL); + if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) break; msleep(1); } - pcicore_write32(pc, mdio_control, 0); + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0); } /************************************************** @@ -138,20 +145,29 @@ static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device, static u8 bcma_pcicore_polarity_workaround(struct bcma_drv_pci *pc) { - return (bcma_pcie_read(pc, 0x204) & 0x10) ? 0xC0 : 0x80; + u32 tmp; + + tmp = bcma_pcie_read(pc, BCMA_CORE_PCI_PLP_STATUSREG); + if (tmp & BCMA_CORE_PCI_PLP_POLARITYINV_STAT) + return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE | + BCMA_CORE_PCI_SERDES_RX_CTRL_POLARITY; + else + return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE; } static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc) { - const u8 serdes_pll_device = 0x1D; - const u8 serdes_rx_device = 0x1F; u16 tmp; - bcma_pcie_mdio_write(pc, serdes_rx_device, 1 /* Control */, - bcma_pcicore_polarity_workaround(pc)); - tmp = bcma_pcie_mdio_read(pc, serdes_pll_device, 1 /* Control */); - if (tmp & 0x4000) - bcma_pcie_mdio_write(pc, serdes_pll_device, 1, tmp & ~0x4000); + bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_RX, + BCMA_CORE_PCI_SERDES_RX_CTRL, + bcma_pcicore_polarity_workaround(pc)); + tmp = bcma_pcie_mdio_read(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL, + BCMA_CORE_PCI_SERDES_PLL_CTRL); + if (tmp & BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN) + bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL, + BCMA_CORE_PCI_SERDES_PLL_CTRL, + tmp & ~BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN); } /************************************************** diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h index 3871b668caf9..67ea7beff9f3 100644 --- a/include/linux/bcma/bcma_driver_pci.h +++ b/include/linux/bcma/bcma_driver_pci.h @@ -53,6 +53,35 @@ struct pci_dev; #define BCMA_CORE_PCI_SBTOPCI1_MASK 0xFC000000 #define BCMA_CORE_PCI_SBTOPCI2 0x0108 /* Backplane to PCI translation 2 (sbtopci2) */ #define BCMA_CORE_PCI_SBTOPCI2_MASK 0xC0000000 +#define BCMA_CORE_PCI_CONFIG_ADDR 0x0120 /* pcie config space access */ +#define BCMA_CORE_PCI_CONFIG_DATA 0x0124 /* pcie config space access */ +#define BCMA_CORE_PCI_MDIO_CONTROL 0x0128 /* controls the mdio access */ +#define BCMA_CORE_PCI_MDIOCTL_DIVISOR_MASK 0x7f /* clock to be used on MDIO */ +#define BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL 0x2 +#define BCMA_CORE_PCI_MDIOCTL_PREAM_EN 0x80 /* Enable preamble sequnce */ +#define BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE 0x100 /* Tranaction complete */ +#define BCMA_CORE_PCI_MDIO_DATA 0x012c /* Data to the mdio access */ +#define BCMA_CORE_PCI_MDIODATA_MASK 0x0000ffff /* data 2 bytes */ +#define BCMA_CORE_PCI_MDIODATA_TA 0x00020000 /* Turnaround */ +#define BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD 18 /* Regaddr shift (rev < 10) */ +#define BCMA_CORE_PCI_MDIODATA_REGADDR_MASK_OLD 0x003c0000 /* Regaddr Mask (rev < 10) */ +#define BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD 22 /* Physmedia devaddr shift (rev < 10) */ +#define BCMA_CORE_PCI_MDIODATA_DEVADDR_MASK_OLD 0x0fc00000 /* Physmedia devaddr Mask (rev < 10) */ +#define BCMA_CORE_PCI_MDIODATA_REGADDR_SHF 18 /* Regaddr shift */ +#define BCMA_CORE_PCI_MDIODATA_REGADDR_MASK 0x007c0000 /* Regaddr Mask */ +#define BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF 23 /* Physmedia devaddr shift */ +#define BCMA_CORE_PCI_MDIODATA_DEVADDR_MASK 0x0f800000 /* Physmedia devaddr Mask */ +#define BCMA_CORE_PCI_MDIODATA_WRITE 0x10000000 /* write Transaction */ +#define BCMA_CORE_PCI_MDIODATA_READ 0x20000000 /* Read Transaction */ +#define BCMA_CORE_PCI_MDIODATA_START 0x40000000 /* start of Transaction */ +#define BCMA_CORE_PCI_MDIODATA_DEV_ADDR 0x0 /* dev address for serdes */ +#define BCMA_CORE_PCI_MDIODATA_BLK_ADDR 0x1F /* blk address for serdes */ +#define BCMA_CORE_PCI_MDIODATA_DEV_PLL 0x1d /* SERDES PLL Dev */ +#define BCMA_CORE_PCI_MDIODATA_DEV_TX 0x1e /* SERDES TX Dev */ +#define BCMA_CORE_PCI_MDIODATA_DEV_RX 0x1f /* SERDES RX Dev */ +#define BCMA_CORE_PCI_PCIEIND_ADDR 0x0130 /* indirect access to the internal register */ +#define BCMA_CORE_PCI_PCIEIND_DATA 0x0134 /* Data to/from the internal regsiter */ +#define BCMA_CORE_PCI_CLKREQENCTRL 0x0138 /* >= rev 6, Clkreq rdma control */ #define BCMA_CORE_PCI_PCICFG0 0x0400 /* PCI config space 0 (rev >= 8) */ #define BCMA_CORE_PCI_PCICFG1 0x0500 /* PCI config space 1 (rev >= 8) */ #define BCMA_CORE_PCI_PCICFG2 0x0600 /* PCI config space 2 (rev >= 8) */ @@ -72,6 +101,62 @@ struct pci_dev; #define BCMA_CORE_PCI_SBTOPCI_RC_READL 0x00000010 /* Memory read line */ #define BCMA_CORE_PCI_SBTOPCI_RC_READM 0x00000020 /* Memory read multiple */ +/* PCIE protocol PHY diagnostic registers */ +#define BCMA_CORE_PCI_PLP_MODEREG 0x200 /* Mode */ +#define BCMA_CORE_PCI_PLP_STATUSREG 0x204 /* Status */ +#define BCMA_CORE_PCI_PLP_POLARITYINV_STAT 0x10 /* Status reg PCIE_PLP_STATUSREG */ +#define BCMA_CORE_PCI_PLP_LTSSMCTRLREG 0x208 /* LTSSM control */ +#define BCMA_CORE_PCI_PLP_LTLINKNUMREG 0x20c /* Link Training Link number */ +#define BCMA_CORE_PCI_PLP_LTLANENUMREG 0x210 /* Link Training Lane number */ +#define BCMA_CORE_PCI_PLP_LTNFTSREG 0x214 /* Link Training N_FTS */ +#define BCMA_CORE_PCI_PLP_ATTNREG 0x218 /* Attention */ +#define BCMA_CORE_PCI_PLP_ATTNMASKREG 0x21C /* Attention Mask */ +#define BCMA_CORE_PCI_PLP_RXERRCTR 0x220 /* Rx Error */ +#define BCMA_CORE_PCI_PLP_RXFRMERRCTR 0x224 /* Rx Framing Error */ +#define BCMA_CORE_PCI_PLP_RXERRTHRESHREG 0x228 /* Rx Error threshold */ +#define BCMA_CORE_PCI_PLP_TESTCTRLREG 0x22C /* Test Control reg */ +#define BCMA_CORE_PCI_PLP_SERDESCTRLOVRDREG 0x230 /* SERDES Control Override */ +#define BCMA_CORE_PCI_PLP_TIMINGOVRDREG 0x234 /* Timing param override */ +#define BCMA_CORE_PCI_PLP_RXTXSMDIAGREG 0x238 /* RXTX State Machine Diag */ +#define BCMA_CORE_PCI_PLP_LTSSMDIAGREG 0x23C /* LTSSM State Machine Diag */ + +/* PCIE protocol DLLP diagnostic registers */ +#define BCMA_CORE_PCI_DLLP_LCREG 0x100 /* Link Control */ +#define BCMA_CORE_PCI_DLLP_LSREG 0x104 /* Link Status */ +#define BCMA_CORE_PCI_DLLP_LAREG 0x108 /* Link Attention */ +#define BCMA_CORE_PCI_DLLP_LSREG_LINKUP (1 << 16) +#define BCMA_CORE_PCI_DLLP_LAMASKREG 0x10C /* Link Attention Mask */ +#define BCMA_CORE_PCI_DLLP_NEXTTXSEQNUMREG 0x110 /* Next Tx Seq Num */ +#define BCMA_CORE_PCI_DLLP_ACKEDTXSEQNUMREG 0x114 /* Acked Tx Seq Num */ +#define BCMA_CORE_PCI_DLLP_PURGEDTXSEQNUMREG 0x118 /* Purged Tx Seq Num */ +#define BCMA_CORE_PCI_DLLP_RXSEQNUMREG 0x11C /* Rx Sequence Number */ +#define BCMA_CORE_PCI_DLLP_LRREG 0x120 /* Link Replay */ +#define BCMA_CORE_PCI_DLLP_LACKTOREG 0x124 /* Link Ack Timeout */ +#define BCMA_CORE_PCI_DLLP_PMTHRESHREG 0x128 /* Power Management Threshold */ +#define BCMA_CORE_PCI_DLLP_RTRYWPREG 0x12C /* Retry buffer write ptr */ +#define BCMA_CORE_PCI_DLLP_RTRYRPREG 0x130 /* Retry buffer Read ptr */ +#define BCMA_CORE_PCI_DLLP_RTRYPPREG 0x134 /* Retry buffer Purged ptr */ +#define BCMA_CORE_PCI_DLLP_RTRRWREG 0x138 /* Retry buffer Read/Write */ +#define BCMA_CORE_PCI_DLLP_ECTHRESHREG 0x13C /* Error Count Threshold */ +#define BCMA_CORE_PCI_DLLP_TLPERRCTRREG 0x140 /* TLP Error Counter */ +#define BCMA_CORE_PCI_DLLP_ERRCTRREG 0x144 /* Error Counter */ +#define BCMA_CORE_PCI_DLLP_NAKRXCTRREG 0x148 /* NAK Received Counter */ +#define BCMA_CORE_PCI_DLLP_TESTREG 0x14C /* Test */ +#define BCMA_CORE_PCI_DLLP_PKTBIST 0x150 /* Packet BIST */ +#define BCMA_CORE_PCI_DLLP_PCIE11 0x154 /* DLLP PCIE 1.1 reg */ + +/* SERDES RX registers */ +#define BCMA_CORE_PCI_SERDES_RX_CTRL 1 /* Rx cntrl */ +#define BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE 0x80 /* rxpolarity_force */ +#define BCMA_CORE_PCI_SERDES_RX_CTRL_POLARITY 0x40 /* rxpolarity_value */ +#define BCMA_CORE_PCI_SERDES_RX_TIMER1 2 /* Rx Timer1 */ +#define BCMA_CORE_PCI_SERDES_RX_CDR 6 /* CDR */ +#define BCMA_CORE_PCI_SERDES_RX_CDRBW 7 /* CDR BW */ + +/* SERDES PLL registers */ +#define BCMA_CORE_PCI_SERDES_PLL_CTRL 1 /* PLL control reg */ +#define BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN 0x4000 /* bit 14 is FREQDET on */ + /* PCIcore specific boardflags */ #define BCMA_CORE_PCI_BFL_NOPCI 0x00000400 /* Board leaves PCI floating */ -- cgit From d1a7a8e1d367e34e5adce91f48cae07dc08d9e6c Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 31 Jan 2012 00:03:34 +0100 Subject: bcma: make some functions __devinit bcma_core_pci_hostmode_init() has to be in __devinit as it will call a function in that section and so all functions calling it also have to be in __devinit. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/bcma_private.h | 4 ++-- drivers/bcma/driver_pci.c | 6 +++--- drivers/bcma/driver_pci_host.c | 2 +- drivers/bcma/host_pci.c | 4 ++-- drivers/bcma/main.c | 2 +- include/linux/bcma/bcma_driver_pci.h | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h index 6109da5d883c..63c5242159f2 100644 --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -13,7 +13,7 @@ struct bcma_bus; /* main.c */ -int bcma_bus_register(struct bcma_bus *bus); +int __devinit bcma_bus_register(struct bcma_bus *bus); void bcma_bus_unregister(struct bcma_bus *bus); int __init bcma_bus_early_register(struct bcma_bus *bus, struct bcma_device *core_cc, @@ -52,7 +52,7 @@ extern void __exit bcma_host_pci_exit(void); u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address); #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE -void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc); +void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc); #endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */ #endif diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c index 22dc9fca6dcb..155d953dba74 100644 --- a/drivers/bcma/driver_pci.c +++ b/drivers/bcma/driver_pci.c @@ -174,12 +174,12 @@ static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc) * Init. **************************************************/ -static void bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc) +static void __devinit bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc) { bcma_pcicore_serdes_workaround(pc); } -static bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc) +static bool __devinit bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc) { struct bcma_bus *bus = pc->core->bus; u16 chipid_top; @@ -204,7 +204,7 @@ static bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc) return true; } -void bcma_core_pci_init(struct bcma_drv_pci *pc) +void __devinit bcma_core_pci_init(struct bcma_drv_pci *pc) { if (pc->setup_done) return; diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c index eb332b75ce83..99e040b607a2 100644 --- a/drivers/bcma/driver_pci_host.c +++ b/drivers/bcma/driver_pci_host.c @@ -8,7 +8,7 @@ #include "bcma_private.h" #include -void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc) +void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc) { pr_err("No support for PCI core in hostmode yet\n"); } diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index f59244e33971..e3928d68802b 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -154,8 +154,8 @@ const struct bcma_host_ops bcma_host_pci_ops = { .awrite32 = bcma_host_pci_awrite32, }; -static int bcma_host_pci_probe(struct pci_dev *dev, - const struct pci_device_id *id) +static int __devinit bcma_host_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) { struct bcma_bus *bus; int err = -ENOMEM; diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index febbc0a1222a..3363036f23dc 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -132,7 +132,7 @@ static void bcma_unregister_cores(struct bcma_bus *bus) } } -int bcma_bus_register(struct bcma_bus *bus) +int __devinit bcma_bus_register(struct bcma_bus *bus) { int err; struct bcma_device *core; diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h index 67ea7beff9f3..679d4cab4547 100644 --- a/include/linux/bcma/bcma_driver_pci.h +++ b/include/linux/bcma/bcma_driver_pci.h @@ -169,7 +169,7 @@ struct bcma_drv_pci { #define pcicore_read32(pc, offset) bcma_read32((pc)->core, offset) #define pcicore_write32(pc, offset, val) bcma_write32((pc)->core, offset, val) -extern void bcma_core_pci_init(struct bcma_drv_pci *pc); +extern void __devinit bcma_core_pci_init(struct bcma_drv_pci *pc); extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core, bool enable); -- cgit From 49dc9577155576b10ff79f0c1486c816b01f58bf Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 31 Jan 2012 00:03:35 +0100 Subject: bcma: add PCIe host controller Some SoCs have a PCIe host controller to make it possible to attach some other devices to it, like an other Wifi card. This code was tested with an Netgear WNDR3400 (bcm4716 based), but should work with all bcma based SoCs. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- arch/mips/pci/pci-bcm47xx.c | 49 ++- drivers/bcma/bcma_private.h | 1 + drivers/bcma/driver_pci.c | 38 +-- drivers/bcma/driver_pci_host.c | 576 ++++++++++++++++++++++++++++++++++- include/linux/bcma/bcma_driver_pci.h | 38 +++ include/linux/bcma/bcma_regs.h | 27 ++ 6 files changed, 690 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/arch/mips/pci/pci-bcm47xx.c b/arch/mips/pci/pci-bcm47xx.c index 400535a955d0..c682468010c5 100644 --- a/arch/mips/pci/pci-bcm47xx.c +++ b/arch/mips/pci/pci-bcm47xx.c @@ -25,6 +25,7 @@ #include #include #include +#include #include int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) @@ -32,15 +33,12 @@ int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) return 0; } -int pcibios_plat_dev_init(struct pci_dev *dev) -{ #ifdef CONFIG_BCM47XX_SSB +static int bcm47xx_pcibios_plat_dev_init_ssb(struct pci_dev *dev) +{ int res; u8 slot, pin; - if (bcm47xx_bus_type != BCM47XX_BUS_TYPE_SSB) - return 0; - res = ssb_pcibios_plat_dev_init(dev); if (res < 0) { printk(KERN_ALERT "PCI: Failed to init device %s\n", @@ -60,6 +58,47 @@ int pcibios_plat_dev_init(struct pci_dev *dev) } dev->irq = res; + return 0; +} #endif + +#ifdef CONFIG_BCM47XX_BCMA +static int bcm47xx_pcibios_plat_dev_init_bcma(struct pci_dev *dev) +{ + int res; + + res = bcma_core_pci_plat_dev_init(dev); + if (res < 0) { + printk(KERN_ALERT "PCI: Failed to init device %s\n", + pci_name(dev)); + return res; + } + + res = bcma_core_pci_pcibios_map_irq(dev); + + /* IRQ-0 and IRQ-1 are software interrupts. */ + if (res < 2) { + printk(KERN_ALERT "PCI: Failed to map IRQ of device %s\n", + pci_name(dev)); + return res; + } + + dev->irq = res; return 0; } +#endif + +int pcibios_plat_dev_init(struct pci_dev *dev) +{ +#ifdef CONFIG_BCM47XX_SSB + if (bcm47xx_bus_type == BCM47XX_BUS_TYPE_SSB) + return bcm47xx_pcibios_plat_dev_init_ssb(dev); + else +#endif +#ifdef CONFIG_BCM47XX_BCMA + if (bcm47xx_bus_type == BCM47XX_BUS_TYPE_BCMA) + return bcm47xx_pcibios_plat_dev_init_bcma(dev); + else +#endif + return 0; +} diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h index 63c5242159f2..b81755bb4798 100644 --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -52,6 +52,7 @@ extern void __exit bcma_host_pci_exit(void); u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address); #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE +bool __devinit bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc); void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc); #endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */ diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c index 155d953dba74..4d38ae179b48 100644 --- a/drivers/bcma/driver_pci.c +++ b/drivers/bcma/driver_pci.c @@ -2,7 +2,7 @@ * Broadcom specific AMBA * PCI Core * - * Copyright 2005, Broadcom Corporation + * Copyright 2005, 2011, Broadcom Corporation * Copyright 2006, 2007, Michael Buesch * Copyright 2011, 2012, Hauke Mehrtens * @@ -179,47 +179,19 @@ static void __devinit bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc) bcma_pcicore_serdes_workaround(pc); } -static bool __devinit bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc) -{ - struct bcma_bus *bus = pc->core->bus; - u16 chipid_top; - - chipid_top = (bus->chipinfo.id & 0xFF00); - if (chipid_top != 0x4700 && - chipid_top != 0x5300) - return false; - -#ifdef CONFIG_SSB_DRIVER_PCICORE - if (bus->sprom.boardflags_lo & SSB_BFL_NOPCI) - return false; -#endif /* CONFIG_SSB_DRIVER_PCICORE */ - -#if 0 - /* TODO: on BCMA we use address from EROM instead of magic formula */ - u32 tmp; - return !mips_busprobe32(tmp, (bus->mmio + - (pc->core->core_index * BCMA_CORE_SIZE))); -#endif - - return true; -} - void __devinit bcma_core_pci_init(struct bcma_drv_pci *pc) { if (pc->setup_done) return; - if (bcma_core_pci_is_in_hostmode(pc)) { #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE + pc->hostmode = bcma_core_pci_is_in_hostmode(pc); + if (pc->hostmode) bcma_core_pci_hostmode_init(pc); -#else - pr_err("Driver compiled without support for hostmode PCI\n"); #endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */ - } else { - bcma_core_pci_clientmode_init(pc); - } - pc->setup_done = true; + if (!pc->hostmode) + bcma_core_pci_clientmode_init(pc); } int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core, diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c index 99e040b607a2..4e20bcfa7ec5 100644 --- a/drivers/bcma/driver_pci_host.c +++ b/drivers/bcma/driver_pci_host.c @@ -2,13 +2,587 @@ * Broadcom specific AMBA * PCI Core in hostmode * + * Copyright 2005 - 2011, Broadcom Corporation + * Copyright 2006, 2007, Michael Buesch + * Copyright 2011, 2012, Hauke Mehrtens + * * Licensed under the GNU/GPL. See COPYING for details. */ #include "bcma_private.h" +#include #include +#include + +/* Probe a 32bit value on the bus and catch bus exceptions. + * Returns nonzero on a bus exception. + * This is MIPS specific */ +#define mips_busprobe32(val, addr) get_dbe((val), ((u32 *)(addr))) + +/* Assume one-hot slot wiring */ +#define BCMA_PCI_SLOT_MAX 16 +#define PCI_CONFIG_SPACE_SIZE 256 + +bool __devinit bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc) +{ + struct bcma_bus *bus = pc->core->bus; + u16 chipid_top; + u32 tmp; + + chipid_top = (bus->chipinfo.id & 0xFF00); + if (chipid_top != 0x4700 && + chipid_top != 0x5300) + return false; + + if (bus->sprom.boardflags_lo & BCMA_CORE_PCI_BFL_NOPCI) { + pr_info("This PCI core is disabled and not working\n"); + return false; + } + + bcma_core_enable(pc->core, 0); + + return !mips_busprobe32(tmp, pc->core->io_addr); +} + +static u32 bcma_pcie_read_config(struct bcma_drv_pci *pc, u32 address) +{ + pcicore_write32(pc, BCMA_CORE_PCI_CONFIG_ADDR, address); + pcicore_read32(pc, BCMA_CORE_PCI_CONFIG_ADDR); + return pcicore_read32(pc, BCMA_CORE_PCI_CONFIG_DATA); +} + +static void bcma_pcie_write_config(struct bcma_drv_pci *pc, u32 address, + u32 data) +{ + pcicore_write32(pc, BCMA_CORE_PCI_CONFIG_ADDR, address); + pcicore_read32(pc, BCMA_CORE_PCI_CONFIG_ADDR); + pcicore_write32(pc, BCMA_CORE_PCI_CONFIG_DATA, data); +} + +static u32 bcma_get_cfgspace_addr(struct bcma_drv_pci *pc, unsigned int dev, + unsigned int func, unsigned int off) +{ + u32 addr = 0; + + /* Issue config commands only when the data link is up (atleast + * one external pcie device is present). + */ + if (dev >= 2 || !(bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_LSREG) + & BCMA_CORE_PCI_DLLP_LSREG_LINKUP)) + goto out; + + /* Type 0 transaction */ + /* Slide the PCI window to the appropriate slot */ + pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI1, BCMA_CORE_PCI_SBTOPCI_CFG0); + /* Calculate the address */ + addr = pc->host_controller->host_cfg_addr; + addr |= (dev << BCMA_CORE_PCI_CFG_SLOT_SHIFT); + addr |= (func << BCMA_CORE_PCI_CFG_FUN_SHIFT); + addr |= (off & ~3); + +out: + return addr; +} + +static int bcma_extpci_read_config(struct bcma_drv_pci *pc, unsigned int dev, + unsigned int func, unsigned int off, + void *buf, int len) +{ + int err = -EINVAL; + u32 addr, val; + void __iomem *mmio = 0; + + WARN_ON(!pc->hostmode); + if (unlikely(len != 1 && len != 2 && len != 4)) + goto out; + if (dev == 0) { + /* we support only two functions on device 0 */ + if (func > 1) + return -EINVAL; + + /* accesses to config registers with offsets >= 256 + * requires indirect access. + */ + if (off >= PCI_CONFIG_SPACE_SIZE) { + addr = (func << 12); + addr |= (off & 0x0FFF); + val = bcma_pcie_read_config(pc, addr); + } else { + addr = BCMA_CORE_PCI_PCICFG0; + addr |= (func << 8); + addr |= (off & 0xfc); + val = pcicore_read32(pc, addr); + } + } else { + addr = bcma_get_cfgspace_addr(pc, dev, func, off); + if (unlikely(!addr)) + goto out; + err = -ENOMEM; + mmio = ioremap_nocache(addr, len); + if (!mmio) + goto out; + + if (mips_busprobe32(val, mmio)) { + val = 0xffffffff; + goto unmap; + } + + val = readl(mmio); + } + val >>= (8 * (off & 3)); + + switch (len) { + case 1: + *((u8 *)buf) = (u8)val; + break; + case 2: + *((u16 *)buf) = (u16)val; + break; + case 4: + *((u32 *)buf) = (u32)val; + break; + } + err = 0; +unmap: + if (mmio) + iounmap(mmio); +out: + return err; +} + +static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev, + unsigned int func, unsigned int off, + const void *buf, int len) +{ + int err = -EINVAL; + u32 addr = 0, val = 0; + void __iomem *mmio = 0; + u16 chipid = pc->core->bus->chipinfo.id; + + WARN_ON(!pc->hostmode); + if (unlikely(len != 1 && len != 2 && len != 4)) + goto out; + if (dev == 0) { + /* accesses to config registers with offsets >= 256 + * requires indirect access. + */ + if (off < PCI_CONFIG_SPACE_SIZE) { + addr = pc->core->addr + BCMA_CORE_PCI_PCICFG0; + addr |= (func << 8); + addr |= (off & 0xfc); + mmio = ioremap_nocache(addr, len); + if (!mmio) + goto out; + } + } else { + addr = bcma_get_cfgspace_addr(pc, dev, func, off); + if (unlikely(!addr)) + goto out; + err = -ENOMEM; + mmio = ioremap_nocache(addr, len); + if (!mmio) + goto out; + + if (mips_busprobe32(val, mmio)) { + val = 0xffffffff; + goto unmap; + } + } + + switch (len) { + case 1: + val = readl(mmio); + val &= ~(0xFF << (8 * (off & 3))); + val |= *((const u8 *)buf) << (8 * (off & 3)); + break; + case 2: + val = readl(mmio); + val &= ~(0xFFFF << (8 * (off & 3))); + val |= *((const u16 *)buf) << (8 * (off & 3)); + break; + case 4: + val = *((const u32 *)buf); + break; + } + if (dev == 0 && !addr) { + /* accesses to config registers with offsets >= 256 + * requires indirect access. + */ + addr = (func << 12); + addr |= (off & 0x0FFF); + bcma_pcie_write_config(pc, addr, val); + } else { + writel(val, mmio); + + if (chipid == 0x4716 || chipid == 0x4748) + readl(mmio); + } + + err = 0; +unmap: + if (mmio) + iounmap(mmio); +out: + return err; +} + +static int bcma_core_pci_hostmode_read_config(struct pci_bus *bus, + unsigned int devfn, + int reg, int size, u32 *val) +{ + unsigned long flags; + int err; + struct bcma_drv_pci *pc; + struct bcma_drv_pci_host *pc_host; + + pc_host = container_of(bus->ops, struct bcma_drv_pci_host, pci_ops); + pc = pc_host->pdev; + + spin_lock_irqsave(&pc_host->cfgspace_lock, flags); + err = bcma_extpci_read_config(pc, PCI_SLOT(devfn), + PCI_FUNC(devfn), reg, val, size); + spin_unlock_irqrestore(&pc_host->cfgspace_lock, flags); + + return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; +} + +static int bcma_core_pci_hostmode_write_config(struct pci_bus *bus, + unsigned int devfn, + int reg, int size, u32 val) +{ + unsigned long flags; + int err; + struct bcma_drv_pci *pc; + struct bcma_drv_pci_host *pc_host; + + pc_host = container_of(bus->ops, struct bcma_drv_pci_host, pci_ops); + pc = pc_host->pdev; + + spin_lock_irqsave(&pc_host->cfgspace_lock, flags); + err = bcma_extpci_write_config(pc, PCI_SLOT(devfn), + PCI_FUNC(devfn), reg, &val, size); + spin_unlock_irqrestore(&pc_host->cfgspace_lock, flags); + + return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; +} + +/* return cap_offset if requested capability exists in the PCI config space */ +static u8 __devinit bcma_find_pci_capability(struct bcma_drv_pci *pc, + unsigned int dev, + unsigned int func, u8 req_cap_id, + unsigned char *buf, u32 *buflen) +{ + u8 cap_id; + u8 cap_ptr = 0; + u32 bufsize; + u8 byte_val; + + /* check for Header type 0 */ + bcma_extpci_read_config(pc, dev, func, PCI_HEADER_TYPE, &byte_val, + sizeof(u8)); + if ((byte_val & 0x7f) != PCI_HEADER_TYPE_NORMAL) + return cap_ptr; + + /* check if the capability pointer field exists */ + bcma_extpci_read_config(pc, dev, func, PCI_STATUS, &byte_val, + sizeof(u8)); + if (!(byte_val & PCI_STATUS_CAP_LIST)) + return cap_ptr; + + /* check if the capability pointer is 0x00 */ + bcma_extpci_read_config(pc, dev, func, PCI_CAPABILITY_LIST, &cap_ptr, + sizeof(u8)); + if (cap_ptr == 0x00) + return cap_ptr; + + /* loop thr'u the capability list and see if the requested capabilty + * exists */ + bcma_extpci_read_config(pc, dev, func, cap_ptr, &cap_id, sizeof(u8)); + while (cap_id != req_cap_id) { + bcma_extpci_read_config(pc, dev, func, cap_ptr + 1, &cap_ptr, + sizeof(u8)); + if (cap_ptr == 0x00) + return cap_ptr; + bcma_extpci_read_config(pc, dev, func, cap_ptr, &cap_id, + sizeof(u8)); + } + + /* found the caller requested capability */ + if ((buf != NULL) && (buflen != NULL)) { + u8 cap_data; + + bufsize = *buflen; + if (!bufsize) + return cap_ptr; + + *buflen = 0; + + /* copy the cpability data excluding cap ID and next ptr */ + cap_data = cap_ptr + 2; + if ((bufsize + cap_data) > PCI_CONFIG_SPACE_SIZE) + bufsize = PCI_CONFIG_SPACE_SIZE - cap_data; + *buflen = bufsize; + while (bufsize--) { + bcma_extpci_read_config(pc, dev, func, cap_data, buf, + sizeof(u8)); + cap_data++; + buf++; + } + } + + return cap_ptr; +} + +/* If the root port is capable of returning Config Request + * Retry Status (CRS) Completion Status to software then + * enable the feature. + */ +static void __devinit bcma_core_pci_enable_crs(struct bcma_drv_pci *pc) +{ + u8 cap_ptr, root_ctrl, root_cap, dev; + u16 val16; + int i; + + cap_ptr = bcma_find_pci_capability(pc, 0, 0, PCI_CAP_ID_EXP, NULL, + NULL); + root_cap = cap_ptr + PCI_EXP_RTCAP; + bcma_extpci_read_config(pc, 0, 0, root_cap, &val16, sizeof(u16)); + if (val16 & BCMA_CORE_PCI_RC_CRS_VISIBILITY) { + /* Enable CRS software visibility */ + root_ctrl = cap_ptr + PCI_EXP_RTCTL; + val16 = PCI_EXP_RTCTL_CRSSVE; + bcma_extpci_read_config(pc, 0, 0, root_ctrl, &val16, + sizeof(u16)); + + /* Initiate a configuration request to read the vendor id + * field of the device function's config space header after + * 100 ms wait time from the end of Reset. If the device is + * not done with its internal initialization, it must at + * least return a completion TLP, with a completion status + * of "Configuration Request Retry Status (CRS)". The root + * complex must complete the request to the host by returning + * a read-data value of 0001h for the Vendor ID field and + * all 1s for any additional bytes included in the request. + * Poll using the config reads for max wait time of 1 sec or + * until we receive the successful completion status. Repeat + * the procedure for all the devices. + */ + for (dev = 1; dev < BCMA_PCI_SLOT_MAX; dev++) { + for (i = 0; i < 100000; i++) { + bcma_extpci_read_config(pc, dev, 0, + PCI_VENDOR_ID, &val16, + sizeof(val16)); + if (val16 != 0x1) + break; + udelay(10); + } + if (val16 == 0x1) + pr_err("PCI: Broken device in slot %d\n", dev); + } + } +} void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc) { - pr_err("No support for PCI core in hostmode yet\n"); + struct bcma_bus *bus = pc->core->bus; + struct bcma_drv_pci_host *pc_host; + u32 tmp; + u32 pci_membase_1G; + unsigned long io_map_base; + + pr_info("PCIEcore in host mode found\n"); + + pc_host = kzalloc(sizeof(*pc_host), GFP_KERNEL); + if (!pc_host) { + pr_err("can not allocate memory"); + return; + } + + pc->host_controller = pc_host; + pc_host->pci_controller.io_resource = &pc_host->io_resource; + pc_host->pci_controller.mem_resource = &pc_host->mem_resource; + pc_host->pci_controller.pci_ops = &pc_host->pci_ops; + pc_host->pdev = pc; + + pci_membase_1G = BCMA_SOC_PCI_DMA; + pc_host->host_cfg_addr = BCMA_SOC_PCI_CFG; + + pc_host->pci_ops.read = bcma_core_pci_hostmode_read_config; + pc_host->pci_ops.write = bcma_core_pci_hostmode_write_config; + + pc_host->mem_resource.name = "BCMA PCIcore external memory", + pc_host->mem_resource.start = BCMA_SOC_PCI_DMA; + pc_host->mem_resource.end = BCMA_SOC_PCI_DMA + BCMA_SOC_PCI_DMA_SZ - 1; + pc_host->mem_resource.flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED; + + pc_host->io_resource.name = "BCMA PCIcore external I/O", + pc_host->io_resource.start = 0x100; + pc_host->io_resource.end = 0x7FF; + pc_host->io_resource.flags = IORESOURCE_IO | IORESOURCE_PCI_FIXED; + + /* Reset RC */ + udelay(3000); + pcicore_write32(pc, BCMA_CORE_PCI_CTL, BCMA_CORE_PCI_CTL_RST_OE); + udelay(1000); + pcicore_write32(pc, BCMA_CORE_PCI_CTL, BCMA_CORE_PCI_CTL_RST | + BCMA_CORE_PCI_CTL_RST_OE); + + /* 64 MB I/O access window. On 4716, use + * sbtopcie0 to access the device registers. We + * can't use address match 2 (1 GB window) region + * as mips can't generate 64-bit address on the + * backplane. + */ + if (bus->chipinfo.id == 0x4716 || bus->chipinfo.id == 0x4748) { + pc_host->mem_resource.start = BCMA_SOC_PCI_MEM; + pc_host->mem_resource.end = BCMA_SOC_PCI_MEM + + BCMA_SOC_PCI_MEM_SZ - 1; + pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI0, + BCMA_CORE_PCI_SBTOPCI_MEM | BCMA_SOC_PCI_MEM); + } else if (bus->chipinfo.id == 0x5300) { + tmp = BCMA_CORE_PCI_SBTOPCI_MEM; + tmp |= BCMA_CORE_PCI_SBTOPCI_PREF; + tmp |= BCMA_CORE_PCI_SBTOPCI_BURST; + if (pc->core->core_unit == 0) { + pc_host->mem_resource.start = BCMA_SOC_PCI_MEM; + pc_host->mem_resource.end = BCMA_SOC_PCI_MEM + + BCMA_SOC_PCI_MEM_SZ - 1; + pci_membase_1G = BCMA_SOC_PCIE_DMA_H32; + pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI0, + tmp | BCMA_SOC_PCI_MEM); + } else if (pc->core->core_unit == 1) { + pc_host->mem_resource.start = BCMA_SOC_PCI1_MEM; + pc_host->mem_resource.end = BCMA_SOC_PCI1_MEM + + BCMA_SOC_PCI_MEM_SZ - 1; + pci_membase_1G = BCMA_SOC_PCIE1_DMA_H32; + pc_host->host_cfg_addr = BCMA_SOC_PCI1_CFG; + pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI0, + tmp | BCMA_SOC_PCI1_MEM); + } + } else + pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI0, + BCMA_CORE_PCI_SBTOPCI_IO); + + /* 64 MB configuration access window */ + pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI1, BCMA_CORE_PCI_SBTOPCI_CFG0); + + /* 1 GB memory access window */ + pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI2, + BCMA_CORE_PCI_SBTOPCI_MEM | pci_membase_1G); + + + /* As per PCI Express Base Spec 1.1 we need to wait for + * at least 100 ms from the end of a reset (cold/warm/hot) + * before issuing configuration requests to PCI Express + * devices. + */ + udelay(100000); + + bcma_core_pci_enable_crs(pc); + + /* Enable PCI bridge BAR0 memory & master access */ + tmp = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + bcma_extpci_write_config(pc, 0, 0, PCI_COMMAND, &tmp, sizeof(tmp)); + + /* Enable PCI interrupts */ + pcicore_write32(pc, BCMA_CORE_PCI_IMASK, BCMA_CORE_PCI_IMASK_INTA); + + /* Ok, ready to run, register it to the system. + * The following needs change, if we want to port hostmode + * to non-MIPS platform. */ + io_map_base = (unsigned long)ioremap_nocache(BCMA_SOC_PCI_MEM, + 0x04000000); + pc_host->pci_controller.io_map_base = io_map_base; + set_io_port_base(pc_host->pci_controller.io_map_base); + /* Give some time to the PCI controller to configure itself with the new + * values. Not waiting at this point causes crashes of the machine. */ + mdelay(10); + register_pci_controller(&pc_host->pci_controller); + return; +} + +/* Early PCI fixup for a device on the PCI-core bridge. */ +static void bcma_core_pci_fixup_pcibridge(struct pci_dev *dev) +{ + if (dev->bus->ops->read != bcma_core_pci_hostmode_read_config) { + /* This is not a device on the PCI-core bridge. */ + return; + } + if (PCI_SLOT(dev->devfn) != 0) + return; + + pr_info("PCI: Fixing up bridge %s\n", pci_name(dev)); + + /* Enable PCI bridge bus mastering and memory space */ + pci_set_master(dev); + if (pcibios_enable_device(dev, ~0) < 0) { + pr_err("PCI: BCMA bridge enable failed\n"); + return; + } + + /* Enable PCI bridge BAR1 prefetch and burst */ + pci_write_config_dword(dev, BCMA_PCI_BAR1_CONTROL, 3); +} +DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, bcma_core_pci_fixup_pcibridge); + +/* Early PCI fixup for all PCI-cores to set the correct memory address. */ +static void bcma_core_pci_fixup_addresses(struct pci_dev *dev) +{ + struct resource *res; + int pos; + + if (dev->bus->ops->read != bcma_core_pci_hostmode_read_config) { + /* This is not a device on the PCI-core bridge. */ + return; + } + if (PCI_SLOT(dev->devfn) == 0) + return; + + pr_info("PCI: Fixing up addresses %s\n", pci_name(dev)); + + for (pos = 0; pos < 6; pos++) { + res = &dev->resource[pos]; + if (res->flags & (IORESOURCE_IO | IORESOURCE_MEM)) + pci_assign_resource(dev, pos); + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, bcma_core_pci_fixup_addresses); + +/* This function is called when doing a pci_enable_device(). + * We must first check if the device is a device on the PCI-core bridge. */ +int bcma_core_pci_plat_dev_init(struct pci_dev *dev) +{ + struct bcma_drv_pci_host *pc_host; + + if (dev->bus->ops->read != bcma_core_pci_hostmode_read_config) { + /* This is not a device on the PCI-core bridge. */ + return -ENODEV; + } + pc_host = container_of(dev->bus->ops, struct bcma_drv_pci_host, + pci_ops); + + pr_info("PCI: Fixing up device %s\n", pci_name(dev)); + + /* Fix up interrupt lines */ + dev->irq = bcma_core_mips_irq(pc_host->pdev->core) + 2; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + + return 0; +} +EXPORT_SYMBOL(bcma_core_pci_plat_dev_init); + +/* PCI device IRQ mapping. */ +int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev) +{ + struct bcma_drv_pci_host *pc_host; + + if (dev->bus->ops->read != bcma_core_pci_hostmode_read_config) { + /* This is not a device on the PCI-core bridge. */ + return -ENODEV; + } + + pc_host = container_of(dev->bus->ops, struct bcma_drv_pci_host, + pci_ops); + return bcma_core_mips_irq(pc_host->pdev->core) + 2; } +EXPORT_SYMBOL(bcma_core_pci_pcibios_map_irq); diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h index 679d4cab4547..46c71e27d31f 100644 --- a/include/linux/bcma/bcma_driver_pci.h +++ b/include/linux/bcma/bcma_driver_pci.h @@ -160,9 +160,44 @@ struct pci_dev; /* PCIcore specific boardflags */ #define BCMA_CORE_PCI_BFL_NOPCI 0x00000400 /* Board leaves PCI floating */ +/* PCIE Config space accessing MACROS */ +#define BCMA_CORE_PCI_CFG_BUS_SHIFT 24 /* Bus shift */ +#define BCMA_CORE_PCI_CFG_SLOT_SHIFT 19 /* Slot/Device shift */ +#define BCMA_CORE_PCI_CFG_FUN_SHIFT 16 /* Function shift */ +#define BCMA_CORE_PCI_CFG_OFF_SHIFT 0 /* Register shift */ + +#define BCMA_CORE_PCI_CFG_BUS_MASK 0xff /* Bus mask */ +#define BCMA_CORE_PCI_CFG_SLOT_MASK 0x1f /* Slot/Device mask */ +#define BCMA_CORE_PCI_CFG_FUN_MASK 7 /* Function mask */ +#define BCMA_CORE_PCI_CFG_OFF_MASK 0xfff /* Register mask */ + +/* PCIE Root Capability Register bits (Host mode only) */ +#define BCMA_CORE_PCI_RC_CRS_VISIBILITY 0x0001 + +struct bcma_drv_pci; + +#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE +struct bcma_drv_pci_host { + struct bcma_drv_pci *pdev; + + u32 host_cfg_addr; + spinlock_t cfgspace_lock; + + struct pci_controller pci_controller; + struct pci_ops pci_ops; + struct resource mem_resource; + struct resource io_resource; +}; +#endif + struct bcma_drv_pci { struct bcma_device *core; u8 setup_done:1; + u8 hostmode:1; + +#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE + struct bcma_drv_pci_host *host_controller; +#endif }; /* Register access */ @@ -173,4 +208,7 @@ extern void __devinit bcma_core_pci_init(struct bcma_drv_pci *pc); extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core, bool enable); +extern int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev); +extern int bcma_core_pci_plat_dev_init(struct pci_dev *dev); + #endif /* LINUX_BCMA_DRIVER_PCI_H_ */ diff --git a/include/linux/bcma/bcma_regs.h b/include/linux/bcma/bcma_regs.h index 9faae2ae02e8..5a71d5719640 100644 --- a/include/linux/bcma/bcma_regs.h +++ b/include/linux/bcma/bcma_regs.h @@ -56,4 +56,31 @@ #define BCMA_PCI_GPIO_XTAL 0x40 /* PCI config space GPIO 14 for Xtal powerup */ #define BCMA_PCI_GPIO_PLL 0x80 /* PCI config space GPIO 15 for PLL powerdown */ +/* SiliconBackplane Address Map. + * All regions may not exist on all chips. + */ +#define BCMA_SOC_SDRAM_BASE 0x00000000U /* Physical SDRAM */ +#define BCMA_SOC_PCI_MEM 0x08000000U /* Host Mode sb2pcitranslation0 (64 MB) */ +#define BCMA_SOC_PCI_MEM_SZ (64 * 1024 * 1024) +#define BCMA_SOC_PCI_CFG 0x0c000000U /* Host Mode sb2pcitranslation1 (64 MB) */ +#define BCMA_SOC_SDRAM_SWAPPED 0x10000000U /* Byteswapped Physical SDRAM */ +#define BCMA_SOC_SDRAM_R2 0x80000000U /* Region 2 for sdram (512 MB) */ + + +#define BCMA_SOC_PCI_DMA 0x40000000U /* Client Mode sb2pcitranslation2 (1 GB) */ +#define BCMA_SOC_PCI_DMA2 0x80000000U /* Client Mode sb2pcitranslation2 (1 GB) */ +#define BCMA_SOC_PCI_DMA_SZ 0x40000000U /* Client Mode sb2pcitranslation2 size in bytes */ +#define BCMA_SOC_PCIE_DMA_L32 0x00000000U /* PCIE Client Mode sb2pcitranslation2 + * (2 ZettaBytes), low 32 bits + */ +#define BCMA_SOC_PCIE_DMA_H32 0x80000000U /* PCIE Client Mode sb2pcitranslation2 + * (2 ZettaBytes), high 32 bits + */ + +#define BCMA_SOC_PCI1_MEM 0x40000000U /* Host Mode sb2pcitranslation0 (64 MB) */ +#define BCMA_SOC_PCI1_CFG 0x44000000U /* Host Mode sb2pcitranslation1 (64 MB) */ +#define BCMA_SOC_PCIE1_DMA_H32 0xc0000000U /* PCIE Client Mode sb2pcitranslation2 + * (2 ZettaBytes), high 32 bits + */ + #endif /* LINUX_BCMA_REGS_H_ */ -- cgit From 8f9ada4fa1926e540b1562cb9bacb3e51a698c35 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 31 Jan 2012 00:03:36 +0100 Subject: bcma: add bus num counter If we have two bcma buses on one computer the second will not work without this patch. Now each bus gets an own number. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/main.c | 12 +++++++++++- include/linux/bcma/bcma.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 3363036f23dc..bcd1c01cde9e 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -13,6 +13,12 @@ MODULE_DESCRIPTION("Broadcom's specific AMBA driver"); MODULE_LICENSE("GPL"); +/* contains the number the next bus should get. */ +static unsigned int bcma_bus_next_num = 0; + +/* bcma_buses_mutex locks the bcma_bus_next_num */ +static DEFINE_MUTEX(bcma_buses_mutex); + static int bcma_bus_match(struct device *dev, struct device_driver *drv); static int bcma_device_probe(struct device *dev); static int bcma_device_remove(struct device *dev); @@ -93,7 +99,7 @@ static int bcma_register_cores(struct bcma_bus *bus) core->dev.release = bcma_release_core_dev; core->dev.bus = &bcma_bus_type; - dev_set_name(&core->dev, "bcma%d:%d", 0/*bus->num*/, dev_id); + dev_set_name(&core->dev, "bcma%d:%d", bus->num, dev_id); switch (bus->hosttype) { case BCMA_HOSTTYPE_PCI: @@ -137,6 +143,10 @@ int __devinit bcma_bus_register(struct bcma_bus *bus) int err; struct bcma_device *core; + mutex_lock(&bcma_buses_mutex); + bus->num = bcma_bus_next_num++; + mutex_unlock(&bcma_buses_mutex); + /* Scan for devices (cores) */ err = bcma_bus_scan(bus); if (err) { diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 024a6e2a9083..b9f65fbee42f 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -196,6 +196,7 @@ struct bcma_bus { struct list_head cores; u8 nr_cores; u8 init_done:1; + u8 num; struct bcma_drv_cc drv_cc; struct bcma_drv_pci drv_pci; -- cgit From d6865dcc58f252480515101fd13532f0fc420b53 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 31 Jan 2012 00:03:37 +0100 Subject: bcma: add extra sprom check This check is needed on the BCM43224 device as it says in the capabilities it has an sprom but is extra check says it has not. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/sprom.c | 7 +++++++ include/linux/bcma/bcma_driver_chipcommon.h | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index e35134f724f6..ca7752510d5b 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -250,6 +250,7 @@ int bcma_sprom_get(struct bcma_bus *bus) { u16 offset; u16 *sprom; + u32 sromctrl; int err = 0; if (!bus->drv_cc.core) @@ -258,6 +259,12 @@ int bcma_sprom_get(struct bcma_bus *bus) if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM)) return -ENOENT; + if (bus->drv_cc.core->id.rev >= 32) { + sromctrl = bcma_read32(bus->drv_cc.core, BCMA_CC_SROM_CONTROL); + if (!(sromctrl & BCMA_CC_SROM_CONTROL_PRESENT)) + return -ENOENT; + } + sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), GFP_KERNEL); if (!sprom) diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index a33086a7530b..e72938b10714 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -181,6 +181,22 @@ #define BCMA_CC_FLASH_CFG 0x0128 #define BCMA_CC_FLASH_CFG_DS 0x0010 /* Data size, 0=8bit, 1=16bit */ #define BCMA_CC_FLASH_WAITCNT 0x012C +#define BCMA_CC_SROM_CONTROL 0x0190 +#define BCMA_CC_SROM_CONTROL_START 0x80000000 +#define BCMA_CC_SROM_CONTROL_BUSY 0x80000000 +#define BCMA_CC_SROM_CONTROL_OPCODE 0x60000000 +#define BCMA_CC_SROM_CONTROL_OP_READ 0x00000000 +#define BCMA_CC_SROM_CONTROL_OP_WRITE 0x20000000 +#define BCMA_CC_SROM_CONTROL_OP_WRDIS 0x40000000 +#define BCMA_CC_SROM_CONTROL_OP_WREN 0x60000000 +#define BCMA_CC_SROM_CONTROL_OTPSEL 0x00000010 +#define BCMA_CC_SROM_CONTROL_LOCK 0x00000008 +#define BCMA_CC_SROM_CONTROL_SIZE_MASK 0x00000006 +#define BCMA_CC_SROM_CONTROL_SIZE_1K 0x00000000 +#define BCMA_CC_SROM_CONTROL_SIZE_4K 0x00000002 +#define BCMA_CC_SROM_CONTROL_SIZE_16K 0x00000004 +#define BCMA_CC_SROM_CONTROL_SIZE_SHIFT 1 +#define BCMA_CC_SROM_CONTROL_PRESENT 0x00000001 /* 0x1E0 is defined as shared BCMA_CLKCTLST */ #define BCMA_CC_HW_WORKAROUND 0x01E4 /* Hardware workaround (rev >= 20) */ #define BCMA_CC_UART0_DATA 0x0300 -- cgit From 267335d63b808dc861f3a4dc81a605489a8a13ac Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 31 Jan 2012 20:25:47 +0100 Subject: cfg80211/mac80211: userspace peer authorization in IBSS If the IBSS network is RSN-protected, let userspace authorize the stations instead of adding them as AUTHORIZED by default. Signed-off-by: Antonio Quartulli Signed-off-by: John W. Linville --- include/net/cfg80211.h | 5 +++++ net/mac80211/ibss.c | 6 +++++- net/mac80211/ieee80211_i.h | 2 ++ net/wireless/nl80211.c | 20 ++++++++++++-------- 4 files changed, 24 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 229edc526cf5..e0c9ff3a1977 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1147,6 +1147,10 @@ struct cfg80211_disassoc_request { * @beacon_interval: beacon interval to use * @privacy: this is a protected network, keys will be configured * after joining + * @control_port: whether user space controls IEEE 802.1X port, i.e., + * sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is + * required to assume that the port is unauthorized until authorized by + * user space. Otherwise, port is marked authorized by default. * @basic_rates: bitmap of basic rates to use when creating the IBSS * @mcast_rate: per-band multicast rate index + 1 (0: disabled) */ @@ -1161,6 +1165,7 @@ struct cfg80211_ibss_params { u32 basic_rates; bool channel_fixed; bool privacy; + bool control_port; int mcast_rate[IEEE80211_NUM_BANDS]; }; diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 7b3a0b0aa246..8361da4b36ab 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -268,7 +268,10 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta, sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); - sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); + /* authorize the station only if the network is not RSN protected. If + * not wait for the userspace to authorize it */ + if (!sta->sdata->u.ibss.control_port) + sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); rate_control_rate_init(sta); @@ -1075,6 +1078,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, sdata->u.ibss.fixed_bssid = false; sdata->u.ibss.privacy = params->privacy; + sdata->u.ibss.control_port = params->control_port; sdata->u.ibss.basic_rates = params->basic_rates; memcpy(sdata->vif.bss_conf.mcast_rate, params->mcast_rate, sizeof(params->mcast_rate)); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a146b1177cb0..74594f012cd3 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -478,6 +478,8 @@ struct ieee80211_if_ibss { bool fixed_channel; bool privacy; + bool control_port; + u8 bssid[ETH_ALEN]; u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid_len, ie_len; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e1fd1bf90729..f1681e2c5949 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2654,13 +2654,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: - /* disallow things sta doesn't support */ - if (params.plink_action) - return -EINVAL; - if (params.ht_capa) - return -EINVAL; - if (params.listen_interval >= 0) - return -EINVAL; /* * Don't allow userspace to change the TDLS_PEER flag, * but silently ignore attempts to change it since we @@ -2668,7 +2661,15 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) * to change the flag. */ params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); - + /* fall through */ + case NL80211_IFTYPE_ADHOC: + /* disallow things sta doesn't support */ + if (params.plink_action) + return -EINVAL; + if (params.ht_capa) + return -EINVAL; + if (params.listen_interval >= 0) + return -EINVAL; /* reject any changes other than AUTHORIZED */ if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) return -EINVAL; @@ -4802,6 +4803,9 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) return PTR_ERR(connkeys); } + ibss.control_port = + nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]); + err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys); if (err) kfree(connkeys); -- cgit From 885bd8eca6ac172e299750d99bd5c9fddbed89b9 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 2 Feb 2012 17:44:55 +0200 Subject: mac80211: support hw scan while idle Currently, mac80211 goes to idle-off before starting a scan. However, some devices that implement hw scan might not need going idle-off in order to perform a hw scan, and thus saving some energy and simplifying their state machine. (Note that this is also the case for sched scan - it currently doesn't make mac80211 go idle-off) Add a new flag to indicate support for hw scan while idle. Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- include/net/mac80211.h | 5 +++++ net/mac80211/debugfs.c | 2 ++ net/mac80211/iface.c | 3 ++- net/mac80211/main.c | 3 +++ 4 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 922313f0b39b..cbff4f94a200 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1164,6 +1164,10 @@ enum sta_notify_cmd { * @IEEE80211_HW_TX_AMPDU_SETUP_IN_HW: The device handles TX A-MPDU session * setup strictly in HW. mac80211 should not attempt to do this in * software. + * + * @IEEE80211_HW_SCAN_WHILE_IDLE: The device can do hw scan while + * being idle (i.e. mac80211 doesn't have to go idle-off during the + * the scan). */ enum ieee80211_hw_flags { IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, @@ -1190,6 +1194,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_SUPPORTS_PER_STA_GTK = 1<<21, IEEE80211_HW_AP_LINK_PS = 1<<22, IEEE80211_HW_TX_AMPDU_SETUP_IN_HW = 1<<23, + IEEE80211_HW_SCAN_WHILE_IDLE = 1<<24, }; /** diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index affe64be9092..483e96ed95c1 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -263,6 +263,8 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf, sf += snprintf(buf + sf, mxln - sf, "AP_LINK_PS\n"); if (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW) sf += snprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n"); + if (local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE) + sf += snprintf(buf + sf, mxln - sf, "SCAN_WHILE_IDLE\n"); rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); kfree(buf); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 19f0818eba78..6b3cd65d1e07 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1332,7 +1332,8 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) wk->sdata->vif.bss_conf.idle = false; } - if (local->scan_sdata) { + if (local->scan_sdata && + !(local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE)) { scanning = true; local->scan_sdata->vif.bss_conf.idle = false; } diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 362e89d6d467..831a5bd44fd0 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -697,6 +697,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ) return -EINVAL; + if ((hw->flags & IEEE80211_HW_SCAN_WHILE_IDLE) && !local->ops->hw_scan) + return -EINVAL; + if (hw->max_report_rates == 0) hw->max_report_rates = hw->max_rates; -- cgit From a0417fa3a18a14be1f4d9cffcf378a7c42d92a91 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 6 Feb 2012 15:14:37 -0500 Subject: net: Make qdisc_skb_cb upper size bound explicit. Just like skb->cb[], so that qdisc_skb_cb can be encapsulated inside of other data structures. This is intended to be used by IPoIB so that it can remember addressing information stored at hard_header_ops->create() time that it can fetch when the packet gets to the transmit routine. Signed-off-by: David S. Miller --- include/net/sch_generic.h | 9 ++++++++- net/sched/sch_choke.c | 3 +-- net/sched/sch_netem.c | 3 +-- net/sched/sch_sfb.c | 3 +-- net/sched/sch_sfq.c | 5 ++--- 5 files changed, 13 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index f6bb08b73ca4..55ce96b53b09 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -220,9 +220,16 @@ struct tcf_proto { struct qdisc_skb_cb { unsigned int pkt_len; - long data[]; + unsigned char data[24]; }; +static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz) +{ + struct qdisc_skb_cb *qcb; + BUILD_BUG_ON(sizeof(skb->cb) < sizeof(unsigned int) + sz); + BUILD_BUG_ON(sizeof(qcb->data) < sz); +} + static inline int qdisc_qlen(const struct Qdisc *q) { return q->q.qlen; diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c index e465064d39a3..7e267d7b9c75 100644 --- a/net/sched/sch_choke.c +++ b/net/sched/sch_choke.c @@ -148,8 +148,7 @@ struct choke_skb_cb { static inline struct choke_skb_cb *choke_skb_cb(const struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(skb->cb) < - sizeof(struct qdisc_skb_cb) + sizeof(struct choke_skb_cb)); + qdisc_cb_private_validate(skb, sizeof(struct choke_skb_cb)); return (struct choke_skb_cb *)qdisc_skb_cb(skb)->data; } diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 2776012132ea..e83d61ca78ca 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -130,8 +130,7 @@ struct netem_skb_cb { static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(skb->cb) < - sizeof(struct qdisc_skb_cb) + sizeof(struct netem_skb_cb)); + qdisc_cb_private_validate(skb, sizeof(struct netem_skb_cb)); return (struct netem_skb_cb *)qdisc_skb_cb(skb)->data; } diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c index 96e42cae4c7a..d7eea99333e9 100644 --- a/net/sched/sch_sfb.c +++ b/net/sched/sch_sfb.c @@ -94,8 +94,7 @@ struct sfb_skb_cb { static inline struct sfb_skb_cb *sfb_skb_cb(const struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(skb->cb) < - sizeof(struct qdisc_skb_cb) + sizeof(struct sfb_skb_cb)); + qdisc_cb_private_validate(skb, sizeof(struct sfb_skb_cb)); return (struct sfb_skb_cb *)qdisc_skb_cb(skb)->data; } diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 67494aef9acf..60d47180f043 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -166,9 +166,8 @@ struct sfq_skb_cb { static inline struct sfq_skb_cb *sfq_skb_cb(const struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(skb->cb) < - sizeof(struct qdisc_skb_cb) + sizeof(struct sfq_skb_cb)); - return (struct sfq_skb_cb *)qdisc_skb_cb(skb)->data; + qdisc_cb_private_validate(skb, sizeof(struct sfq_skb_cb)); + return (struct sfq_skb_cb *)qdisc_skb_cb(skb)->data; } static unsigned int sfq_hash(const struct sfq_sched_data *q, -- cgit From c3059be16c9ef29c05f0876a9df5fea21f29724f Mon Sep 17 00:00:00 2001 From: Shriram Rajagopalan Date: Sun, 5 Feb 2012 13:51:32 +0000 Subject: net/sched: sch_plug - Queue traffic until an explicit release command The qdisc supports two operations - plug and unplug. When the qdisc receives a plug command via netlink request, packets arriving henceforth are buffered until a corresponding unplug command is received. Depending on the type of unplug command, the queue can be unplugged indefinitely or selectively. This qdisc can be used to implement output buffering, an essential functionality required for consistent recovery in checkpoint based fault-tolerance systems. Output buffering enables speculative execution by allowing generated network traffic to be rolled back. It is used to provide network protection for Xen Guests in the Remus high availability project, available as part of Xen. This module is generic enough to be used by any other system that wishes to add speculative execution and output buffering to its applications. This module was originally available in the linux 2.6.32 PV-OPS tree, used as dom0 for Xen. For more information, please refer to http://nss.cs.ubc.ca/remus/ and http://wiki.xensource.com/xenwiki/Remus Changes in V3: * Removed debug output (printk) on queue overflow * Added TCQ_PLUG_RELEASE_INDEFINITE - that allows the user to use this qdisc, for simple plug/unplug operations. * Use of packet counts instead of pointers to keep track of the buffers in the queue. Signed-off-by: Shriram Rajagopalan Signed-off-by: Brendan Cully [author of the code in the linux 2.6.32 pvops tree] Signed-off-by: David S. Miller --- include/linux/pkt_sched.h | 21 +++++ net/sched/Kconfig | 26 ++++++ net/sched/Makefile | 1 + net/sched/sch_plug.c | 233 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 281 insertions(+) create mode 100644 net/sched/sch_plug.c (limited to 'include') diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h index 0d5b79365d03..410b33d014d2 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -127,6 +127,27 @@ struct tc_multiq_qopt { __u16 max_bands; /* Maximum number of queues */ }; +/* PLUG section */ + +#define TCQ_PLUG_BUFFER 0 +#define TCQ_PLUG_RELEASE_ONE 1 +#define TCQ_PLUG_RELEASE_INDEFINITE 2 +#define TCQ_PLUG_LIMIT 3 + +struct tc_plug_qopt { + /* TCQ_PLUG_BUFFER: Inset a plug into the queue and + * buffer any incoming packets + * TCQ_PLUG_RELEASE_ONE: Dequeue packets from queue head + * to beginning of the next plug. + * TCQ_PLUG_RELEASE_INDEFINITE: Dequeue all packets from queue. + * Stop buffering packets until the next TCQ_PLUG_BUFFER + * command is received (just act as a pass-thru queue). + * TCQ_PLUG_LIMIT: Increase/decrease queue size + */ + int action; + __u32 limit; +}; + /* TBF section */ struct tc_tbf_qopt { diff --git a/net/sched/Kconfig b/net/sched/Kconfig index 2590e91b3289..75b58f81d53d 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -260,6 +260,32 @@ config NET_SCH_INGRESS To compile this code as a module, choose M here: the module will be called sch_ingress. +config NET_SCH_PLUG + tristate "Plug network traffic until release (PLUG)" + ---help--- + + This queuing discipline allows userspace to plug/unplug a network + output queue, using the netlink interface. When it receives an + enqueue command it inserts a plug into the outbound queue that + causes following packets to enqueue until a dequeue command arrives + over netlink, causing the plug to be removed and resuming the normal + packet flow. + + This module also provides a generic "network output buffering" + functionality (aka output commit), wherein upon arrival of a dequeue + command, only packets up to the first plug are released for delivery. + The Remus HA project uses this module to enable speculative execution + of virtual machines by allowing the generated network output to be rolled + back if needed. + + For more information, please refer to http://wiki.xensource.com/xenwiki/Remus + + Say Y here if you are using this kernel for Xen dom0 and + want to protect Xen guests with Remus. + + To compile this code as a module, choose M here: the + module will be called sch_plug. + comment "Classification" config NET_CLS diff --git a/net/sched/Makefile b/net/sched/Makefile index dc5889c0a15a..8cdf4e2b51d3 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_NET_SCH_MULTIQ) += sch_multiq.o obj-$(CONFIG_NET_SCH_ATM) += sch_atm.o obj-$(CONFIG_NET_SCH_NETEM) += sch_netem.o obj-$(CONFIG_NET_SCH_DRR) += sch_drr.o +obj-$(CONFIG_NET_SCH_PLUG) += sch_plug.o obj-$(CONFIG_NET_SCH_MQPRIO) += sch_mqprio.o obj-$(CONFIG_NET_SCH_CHOKE) += sch_choke.o obj-$(CONFIG_NET_SCH_QFQ) += sch_qfq.o diff --git a/net/sched/sch_plug.c b/net/sched/sch_plug.c new file mode 100644 index 000000000000..ba7b737e4055 --- /dev/null +++ b/net/sched/sch_plug.c @@ -0,0 +1,233 @@ +/* + * sch_plug.c Queue traffic until an explicit release command + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * There are two ways to use this qdisc: + * 1. A simple "instantaneous" plug/unplug operation, by issuing an alternating + * sequence of TCQ_PLUG_BUFFER & TCQ_PLUG_RELEASE_INDEFINITE commands. + * + * 2. For network output buffering (a.k.a output commit) functionality. + * Output commit property is commonly used by applications using checkpoint + * based fault-tolerance to ensure that the checkpoint from which a system + * is being restored is consistent w.r.t outside world. + * + * Consider for e.g. Remus - a Virtual Machine checkpointing system, + * wherein a VM is checkpointed, say every 50ms. The checkpoint is replicated + * asynchronously to the backup host, while the VM continues executing the + * next epoch speculatively. + * + * The following is a typical sequence of output buffer operations: + * 1.At epoch i, start_buffer(i) + * 2. At end of epoch i (i.e. after 50ms): + * 2.1 Stop VM and take checkpoint(i). + * 2.2 start_buffer(i+1) and Resume VM + * 3. While speculatively executing epoch(i+1), asynchronously replicate + * checkpoint(i) to backup host. + * 4. When checkpoint_ack(i) is received from backup, release_buffer(i) + * Thus, this Qdisc would receive the following sequence of commands: + * TCQ_PLUG_BUFFER (epoch i) + * .. TCQ_PLUG_BUFFER (epoch i+1) + * ....TCQ_PLUG_RELEASE_ONE (epoch i) + * ......TCQ_PLUG_BUFFER (epoch i+2) + * ........ + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * State of the queue, when used for network output buffering: + * + * plug(i+1) plug(i) head + * ------------------+--------------------+----------------> + * | | + * | | + * pkts_current_epoch| pkts_last_epoch |pkts_to_release + * ----------------->|<--------+--------->|+---------------> + * v v + * + */ + +struct plug_sched_data { + /* If true, the dequeue function releases all packets + * from head to end of the queue. The queue turns into + * a pass-through queue for newly arriving packets. + */ + bool unplug_indefinite; + + /* Queue Limit in bytes */ + u32 limit; + + /* Number of packets (output) from the current speculatively + * executing epoch. + */ + u32 pkts_current_epoch; + + /* Number of packets corresponding to the recently finished + * epoch. These will be released when we receive a + * TCQ_PLUG_RELEASE_ONE command. This command is typically + * issued after committing a checkpoint at the target. + */ + u32 pkts_last_epoch; + + /* + * Number of packets from the head of the queue, that can + * be released (committed checkpoint). + */ + u32 pkts_to_release; +}; + +static int plug_enqueue(struct sk_buff *skb, struct Qdisc *sch) +{ + struct plug_sched_data *q = qdisc_priv(sch); + + if (likely(sch->qstats.backlog + skb->len <= q->limit)) { + if (!q->unplug_indefinite) + q->pkts_current_epoch++; + return qdisc_enqueue_tail(skb, sch); + } + + return qdisc_reshape_fail(skb, sch); +} + +static struct sk_buff *plug_dequeue(struct Qdisc *sch) +{ + struct plug_sched_data *q = qdisc_priv(sch); + + if (qdisc_is_throttled(sch)) + return NULL; + + if (!q->unplug_indefinite) { + if (!q->pkts_to_release) { + /* No more packets to dequeue. Block the queue + * and wait for the next release command. + */ + qdisc_throttled(sch); + return NULL; + } + q->pkts_to_release--; + } + + return qdisc_dequeue_head(sch); +} + +static int plug_init(struct Qdisc *sch, struct nlattr *opt) +{ + struct plug_sched_data *q = qdisc_priv(sch); + + q->pkts_current_epoch = 0; + q->pkts_last_epoch = 0; + q->pkts_to_release = 0; + q->unplug_indefinite = false; + + if (opt == NULL) { + /* We will set a default limit of 100 pkts (~150kB) + * in case tx_queue_len is not available. The + * default value is completely arbitrary. + */ + u32 pkt_limit = qdisc_dev(sch)->tx_queue_len ? : 100; + q->limit = pkt_limit * psched_mtu(qdisc_dev(sch)); + } else { + struct tc_plug_qopt *ctl = nla_data(opt); + + if (nla_len(opt) < sizeof(*ctl)) + return -EINVAL; + + q->limit = ctl->limit; + } + + qdisc_throttled(sch); + return 0; +} + +/* Receives 4 types of messages: + * TCQ_PLUG_BUFFER: Inset a plug into the queue and + * buffer any incoming packets + * TCQ_PLUG_RELEASE_ONE: Dequeue packets from queue head + * to beginning of the next plug. + * TCQ_PLUG_RELEASE_INDEFINITE: Dequeue all packets from queue. + * Stop buffering packets until the next TCQ_PLUG_BUFFER + * command is received (just act as a pass-thru queue). + * TCQ_PLUG_LIMIT: Increase/decrease queue size + */ +static int plug_change(struct Qdisc *sch, struct nlattr *opt) +{ + struct plug_sched_data *q = qdisc_priv(sch); + struct tc_plug_qopt *msg; + + if (opt == NULL) + return -EINVAL; + + msg = nla_data(opt); + if (nla_len(opt) < sizeof(*msg)) + return -EINVAL; + + switch (msg->action) { + case TCQ_PLUG_BUFFER: + /* Save size of the current buffer */ + q->pkts_last_epoch = q->pkts_current_epoch; + q->pkts_current_epoch = 0; + if (q->unplug_indefinite) + qdisc_throttled(sch); + q->unplug_indefinite = false; + break; + case TCQ_PLUG_RELEASE_ONE: + /* Add packets from the last complete buffer to the + * packets to be released set. + */ + q->pkts_to_release += q->pkts_last_epoch; + q->pkts_last_epoch = 0; + qdisc_unthrottled(sch); + netif_schedule_queue(sch->dev_queue); + break; + case TCQ_PLUG_RELEASE_INDEFINITE: + q->unplug_indefinite = true; + q->pkts_to_release = 0; + q->pkts_last_epoch = 0; + q->pkts_current_epoch = 0; + qdisc_unthrottled(sch); + netif_schedule_queue(sch->dev_queue); + break; + case TCQ_PLUG_LIMIT: + /* Limit is supplied in bytes */ + q->limit = msg->limit; + break; + default: + return -EINVAL; + } + + return 0; +} + +struct Qdisc_ops plug_qdisc_ops = { + .id = "plug", + .priv_size = sizeof(struct plug_sched_data), + .enqueue = plug_enqueue, + .dequeue = plug_dequeue, + .peek = qdisc_peek_head, + .init = plug_init, + .change = plug_change, + .owner = THIS_MODULE, +}; + +static int __init plug_module_init(void) +{ + return register_qdisc(&plug_qdisc_ops); +} + +static void __exit plug_module_exit(void) +{ + unregister_qdisc(&plug_qdisc_ops); +} +module_init(plug_module_init) +module_exit(plug_module_exit) +MODULE_LICENSE("GPL"); -- cgit From 76e21053b5bf33a07c76f99d27a74238310e3c71 Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Wed, 8 Feb 2012 09:11:07 +0000 Subject: ipv4: Implement IP_UNICAST_IF socket option. The IP_UNICAST_IF feature is needed by the Wine project. This patch implements the feature by setting the outgoing interface in a similar fashion to that of IP_MULTICAST_IF. A separate option is needed to handle this feature since the existing options do not provide all of the characteristics required by IP_UNICAST_IF, a summary is provided below. SO_BINDTODEVICE: * SO_BINDTODEVICE requires administrative privileges, IP_UNICAST_IF does not. From reading some old mailing list articles my understanding is that SO_BINDTODEVICE requires administrative privileges because it can override the administrator's routing settings. * The SO_BINDTODEVICE option restricts both outbound and inbound traffic, IP_UNICAST_IF only impacts outbound traffic. IP_PKTINFO: * Since IP_PKTINFO and IP_UNICAST_IF are independent options, implementing IP_UNICAST_IF with IP_PKTINFO will likely break some applications. * Implementing IP_UNICAST_IF on top of IP_PKTINFO significantly complicates the Wine codebase and reduces the socket performance (doing this requires a lot of extra communication between the "server" and "user" layers). bind(): * bind() does not work on broadcast packets, IP_UNICAST_IF is specifically intended to work with broadcast packets. * Like SO_BINDTODEVICE, bind() restricts both outbound and inbound traffic. Signed-off-by: Erich E. Hoover Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/in.h | 1 + include/net/inet_sock.h | 2 ++ net/ipv4/ip_sockglue.c | 33 +++++++++++++++++++++++++++++++++ net/ipv4/ping.c | 3 ++- net/ipv4/raw.c | 3 ++- net/ipv4/udp.c | 3 ++- 6 files changed, 42 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/in.h b/include/linux/in.h index 01129c0ea87c..e0337f11d92e 100644 --- a/include/linux/in.h +++ b/include/linux/in.h @@ -111,6 +111,7 @@ struct in_addr { #define MCAST_LEAVE_SOURCE_GROUP 47 #define MCAST_MSFILTER 48 #define IP_MULTICAST_ALL 49 +#define IP_UNICAST_IF 50 #define MCAST_EXCLUDE 0 #define MCAST_INCLUDE 1 diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index e3e405106afe..022f772c0ebe 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -132,6 +132,7 @@ struct rtable; * @tos - TOS * @mc_ttl - Multicasting TTL * @is_icsk - is this an inet_connection_sock? + * @uc_index - Unicast outgoing device index * @mc_index - Multicast device index * @mc_list - Group array * @cork - info to build ip hdr on each ip frag while socket is corked @@ -167,6 +168,7 @@ struct inet_sock { transparent:1, mc_all:1, nodefrag:1; + int uc_index; int mc_index; __be32 mc_addr; struct ip_mc_socklist __rcu *mc_list; diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 8aa87c19fa00..9125529dab95 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -469,6 +469,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, (1<mc_loop = !!val; break; + case IP_UNICAST_IF: + { + struct net_device *dev = NULL; + int ifindex; + + if (optlen != sizeof(int)) + goto e_inval; + + ifindex = (__force int)ntohl((__force __be32)val); + if (ifindex == 0) { + inet->uc_index = 0; + err = 0; + break; + } + + dev = dev_get_by_index(sock_net(sk), ifindex); + err = -EADDRNOTAVAIL; + if (!dev) + break; + dev_put(dev); + + err = -EINVAL; + if (sk->sk_bound_dev_if) + break; + + inet->uc_index = ifindex; + err = 0; + break; + } case IP_MULTICAST_IF: { struct ip_mreqn mreq; @@ -1178,6 +1208,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname, case IP_MULTICAST_LOOP: val = inet->mc_loop; break; + case IP_UNICAST_IF: + val = (__force int)htonl((__u32) inet->uc_index); + break; case IP_MULTICAST_IF: { struct in_addr addr; diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index aea5a199c37a..cfc82cf339f6 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -556,7 +556,8 @@ static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ipc.oif = inet->mc_index; if (!saddr) saddr = inet->mc_addr; - } + } else if (!ipc.oif) + ipc.oif = inet->uc_index; flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 3ccda5ae8a27..ab466305b629 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -563,7 +563,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ipc.oif = inet->mc_index; if (!saddr) saddr = inet->mc_addr; - } + } else if (!ipc.oif) + ipc.oif = inet->uc_index; flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 5d075b5f70fc..cd99f1a0f59f 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -917,7 +917,8 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, if (!saddr) saddr = inet->mc_addr; connected = 0; - } + } else if (!ipc.oif) + ipc.oif = inet->uc_index; if (connected) rt = (struct rtable *)sk_dst_check(sk, 0); -- cgit From c4062dfc425e94290ac427a98d6b4721dd2bc91f Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Wed, 8 Feb 2012 09:11:08 +0000 Subject: ipv6: Implement IPV6_UNICAST_IF socket option. The IPV6_UNICAST_IF feature is the IPv6 compliment to IP_UNICAST_IF. Signed-off-by: Erich E. Hoover Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/in6.h | 1 + include/linux/ipv6.h | 1 + net/ipv6/icmp.c | 4 ++++ net/ipv6/ipv6_sockglue.c | 34 ++++++++++++++++++++++++++++++++++ net/ipv6/raw.c | 2 ++ net/ipv6/udp.c | 3 ++- 6 files changed, 44 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/in6.h b/include/linux/in6.h index 097a34b55560..5c83d9e3eb8f 100644 --- a/include/linux/in6.h +++ b/include/linux/in6.h @@ -271,6 +271,7 @@ struct in6_flowlabel_req { #define IPV6_ORIGDSTADDR 74 #define IPV6_RECVORIGDSTADDR IPV6_ORIGDSTADDR #define IPV6_TRANSPARENT 75 +#define IPV6_UNICAST_IF 76 /* * Multicast Routing: diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 6318268dcaf5..743a16a41040 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -324,6 +324,7 @@ struct ipv6_pinfo { __unused_2:6; __s16 mcast_hops:9; #endif + int ucast_oif; int mcast_oif; /* pktoption flags */ diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 01d46bff63c3..af88934e4d79 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -468,6 +468,8 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) fl6.flowi6_oif = np->mcast_oif; + else if (!fl6.flowi6_oif) + fl6.flowi6_oif = np->ucast_oif; dst = icmpv6_route_lookup(net, skb, sk, &fl6); if (IS_ERR(dst)) @@ -553,6 +555,8 @@ static void icmpv6_echo_reply(struct sk_buff *skb) if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) fl6.flowi6_oif = np->mcast_oif; + else if (!fl6.flowi6_oif) + fl6.flowi6_oif = np->ucast_oif; err = ip6_dst_lookup(sk, &dst, &fl6); if (err) diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 18a2719003c3..6d6b65fdaa1a 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -516,6 +516,36 @@ done: retv = 0; break; + case IPV6_UNICAST_IF: + { + struct net_device *dev = NULL; + int ifindex; + + if (optlen != sizeof(int)) + goto e_inval; + + ifindex = (__force int)ntohl((__force __be32)val); + if (ifindex == 0) { + np->ucast_oif = 0; + retv = 0; + break; + } + + dev = dev_get_by_index(net, ifindex); + retv = -EADDRNOTAVAIL; + if (!dev) + break; + dev_put(dev); + + retv = -EINVAL; + if (sk->sk_bound_dev_if) + break; + + np->ucast_oif = ifindex; + retv = 0; + break; + } + case IPV6_MULTICAST_IF: if (sk->sk_type == SOCK_STREAM) break; @@ -1160,6 +1190,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, val = np->mcast_oif; break; + case IPV6_UNICAST_IF: + val = (__force int)htonl((__u32) np->ucast_oif); + break; + case IPV6_MTU_DISCOVER: val = np->pmtudisc; break; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index d02f7e4dd611..5bddea778840 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -856,6 +856,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) fl6.flowi6_oif = np->mcast_oif; + else if (!fl6.flowi6_oif) + fl6.flowi6_oif = np->ucast_oif; security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); dst = ip6_dst_lookup_flow(sk, &fl6, final_p, true); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 4f96b5c63685..8aebf8f90436 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1130,7 +1130,8 @@ do_udp_sendmsg: if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) { fl6.flowi6_oif = np->mcast_oif; connected = 0; - } + } else if (!fl6.flowi6_oif) + fl6.flowi6_oif = np->ucast_oif; security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); -- cgit From 800c5eb7b5eba6cb2a32738d763fd59f0fbcdde4 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Wed, 8 Feb 2012 00:19:47 +0000 Subject: af_iucv: change net_device handling for HS transport This patch saves the net_device in the iucv_sock structure during bind in order to fasten skb sending. In addition some other small improvements are made for HS transport: - error checking when sending skbs - locking changes in afiucv_hs_callback_txnotify - skb freeing in afiucv_hs_callback_txnotify And finally it contains code cleanup to get rid of iucv_skb_queue_purge. Signed-off-by: Ursula Braun Signed-off-by: Frank Blaschka Signed-off-by: David S. Miller --- include/net/iucv/af_iucv.h | 1 + net/iucv/af_iucv.c | 119 +++++++++++++++++++++++---------------------- 2 files changed, 63 insertions(+), 57 deletions(-) (limited to 'include') diff --git a/include/net/iucv/af_iucv.h b/include/net/iucv/af_iucv.h index 0954ec959159..a1517887aeac 100644 --- a/include/net/iucv/af_iucv.h +++ b/include/net/iucv/af_iucv.h @@ -113,6 +113,7 @@ struct iucv_sock { spinlock_t accept_q_lock; struct sock *parent; struct iucv_path *path; + struct net_device *hs_dev; struct sk_buff_head send_skb_q; struct sk_buff_head backlog_skb_q; struct sock_msg_q message_q; diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index ef6ab71921fc..fbce4a3126de 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -131,17 +131,6 @@ static inline void low_nmcpy(unsigned char *dst, char *src) memcpy(&dst[8], src, 8); } -static void iucv_skb_queue_purge(struct sk_buff_head *list) -{ - struct sk_buff *skb; - - while ((skb = skb_dequeue(list)) != NULL) { - if (skb->dev) - dev_put(skb->dev); - kfree_skb(skb); - } -} - static int afiucv_pm_prepare(struct device *dev) { #ifdef CONFIG_PM_DEBUG @@ -176,7 +165,7 @@ static int afiucv_pm_freeze(struct device *dev) read_lock(&iucv_sk_list.lock); sk_for_each(sk, node, &iucv_sk_list.head) { iucv = iucv_sk(sk); - iucv_skb_queue_purge(&iucv->send_skb_q); + skb_queue_purge(&iucv->send_skb_q); skb_queue_purge(&iucv->backlog_skb_q); switch (sk->sk_state) { case IUCV_DISCONN: @@ -337,7 +326,6 @@ static void iucv_sock_wake_msglim(struct sock *sk) static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock, struct sk_buff *skb, u8 flags) { - struct net *net = sock_net(sock); struct iucv_sock *iucv = iucv_sk(sock); struct af_iucv_trans_hdr *phs_hdr; struct sk_buff *nskb; @@ -374,10 +362,10 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock, if (imsg) memcpy(&phs_hdr->iucv_hdr, imsg, sizeof(struct iucv_message)); - skb->dev = dev_get_by_index(net, sock->sk_bound_dev_if); + skb->dev = iucv->hs_dev; if (!skb->dev) return -ENODEV; - if (!(skb->dev->flags & IFF_UP)) + if (!(skb->dev->flags & IFF_UP) || !netif_carrier_ok(skb->dev)) return -ENETDOWN; if (skb->len > skb->dev->mtu) { if (sock->sk_type == SOCK_SEQPACKET) @@ -392,15 +380,14 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock, return -ENOMEM; skb_queue_tail(&iucv->send_skb_q, nskb); err = dev_queue_xmit(skb); - if (err) { + if (net_xmit_eval(err)) { skb_unlink(nskb, &iucv->send_skb_q); - dev_put(nskb->dev); kfree_skb(nskb); } else { atomic_sub(confirm_recv, &iucv->msg_recv); WARN_ON(atomic_read(&iucv->msg_recv) < 0); } - return err; + return net_xmit_eval(err); } static struct sock *__iucv_get_sock_by_name(char *nm) @@ -471,7 +458,8 @@ static void iucv_sock_close(struct sock *sk) { struct iucv_sock *iucv = iucv_sk(sk); unsigned long timeo; - int err, blen; + int err = 0; + int blen; struct sk_buff *skb; lock_sock(sk); @@ -498,7 +486,7 @@ static void iucv_sock_close(struct sock *sk) sk->sk_state = IUCV_CLOSING; sk->sk_state_change(sk); - if (!skb_queue_empty(&iucv->send_skb_q)) { + if (!err && !skb_queue_empty(&iucv->send_skb_q)) { if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) timeo = sk->sk_lingertime; else @@ -515,13 +503,19 @@ static void iucv_sock_close(struct sock *sk) sk->sk_err = ECONNRESET; sk->sk_state_change(sk); - iucv_skb_queue_purge(&iucv->send_skb_q); + skb_queue_purge(&iucv->send_skb_q); skb_queue_purge(&iucv->backlog_skb_q); default: /* fall through */ iucv_sever_path(sk, 1); } + if (iucv->hs_dev) { + dev_put(iucv->hs_dev); + iucv->hs_dev = NULL; + sk->sk_bound_dev_if = 0; + } + /* mark socket for deletion by iucv_sock_kill() */ sock_set_flag(sk, SOCK_ZAPPED); @@ -713,7 +707,6 @@ static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr, goto done_unlock; /* Bind the socket */ - if (pr_iucv) if (!memcmp(sa->siucv_user_id, iucv_userid, 8)) goto vm_bind; /* VM IUCV transport */ @@ -727,6 +720,8 @@ static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr, memcpy(iucv->src_name, sa->siucv_name, 8); memcpy(iucv->src_user_id, sa->siucv_user_id, 8); sk->sk_bound_dev_if = dev->ifindex; + iucv->hs_dev = dev; + dev_hold(dev); sk->sk_state = IUCV_BOUND; iucv->transport = AF_IUCV_TRANS_HIPER; if (!iucv->msglimit) @@ -1128,8 +1123,10 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock, noblock, &err); else skb = sock_alloc_send_skb(sk, len, noblock, &err); - if (!skb) + if (!skb) { + err = -ENOMEM; goto out; + } if (iucv->transport == AF_IUCV_TRANS_HIPER) skb_reserve(skb, sizeof(struct af_iucv_trans_hdr) + ETH_HLEN); if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { @@ -1152,6 +1149,7 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock, /* increment and save iucv message tag for msg_completion cbk */ txmsg.tag = iucv->send_tag++; memcpy(CB_TAG(skb), &txmsg.tag, CB_TAG_LEN); + if (iucv->transport == AF_IUCV_TRANS_HIPER) { atomic_inc(&iucv->msg_sent); err = afiucv_hs_send(&txmsg, sk, skb, 0); @@ -1206,8 +1204,6 @@ release: return len; fail: - if (skb->dev) - dev_put(skb->dev); kfree_skb(skb); out: release_sock(sk); @@ -1400,7 +1396,14 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock, } kfree_skb(skb); - atomic_inc(&iucv->msg_recv); + if (iucv->transport == AF_IUCV_TRANS_HIPER) { + atomic_inc(&iucv->msg_recv); + if (atomic_read(&iucv->msg_recv) > iucv->msglimit) { + WARN_ON(1); + iucv_sock_close(sk); + return -EFAULT; + } + } /* Queue backlog skbs */ spin_lock_bh(&iucv->message_q.lock); @@ -1957,6 +1960,8 @@ static int afiucv_hs_callback_syn(struct sock *sk, struct sk_buff *skb) memcpy(niucv->src_name, iucv->src_name, 8); memcpy(niucv->src_user_id, iucv->src_user_id, 8); nsk->sk_bound_dev_if = sk->sk_bound_dev_if; + niucv->hs_dev = iucv->hs_dev; + dev_hold(niucv->hs_dev); afiucv_swap_src_dest(skb); trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_ACK; trans_hdr->window = niucv->msglimit; @@ -2025,12 +2030,15 @@ static int afiucv_hs_callback_fin(struct sock *sk, struct sk_buff *skb) struct iucv_sock *iucv = iucv_sk(sk); /* other end of connection closed */ - if (iucv) { - bh_lock_sock(sk); + if (!iucv) + goto out; + bh_lock_sock(sk); + if (sk->sk_state == IUCV_CONNECTED) { sk->sk_state = IUCV_DISCONN; sk->sk_state_change(sk); - bh_unlock_sock(sk); } + bh_unlock_sock(sk); +out: kfree_skb(skb); return NET_RX_SUCCESS; } @@ -2175,11 +2183,11 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, break; case (AF_IUCV_FLAG_WIN): err = afiucv_hs_callback_win(sk, skb); - if (skb->len > sizeof(struct af_iucv_trans_hdr)) - err = afiucv_hs_callback_rx(sk, skb); - else - kfree(skb); - break; + if (skb->len == sizeof(struct af_iucv_trans_hdr)) { + kfree_skb(skb); + break; + } + /* fall through */ case 0: /* plain data frame */ memcpy(CB_TRGCLS(skb), &trans_hdr->iucv_hdr.class, @@ -2205,65 +2213,64 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *skb, struct iucv_sock *iucv = NULL; struct sk_buff_head *list; struct sk_buff *list_skb; - struct sk_buff *this = NULL; + struct sk_buff *nskb; unsigned long flags; struct hlist_node *node; - read_lock(&iucv_sk_list.lock); + read_lock_irqsave(&iucv_sk_list.lock, flags); sk_for_each(sk, node, &iucv_sk_list.head) if (sk == isk) { iucv = iucv_sk(sk); break; } - read_unlock(&iucv_sk_list.lock); + read_unlock_irqrestore(&iucv_sk_list.lock, flags); - if (!iucv) + if (!iucv || sock_flag(sk, SOCK_ZAPPED)) return; - bh_lock_sock(sk); list = &iucv->send_skb_q; - list_skb = list->next; + spin_lock_irqsave(&list->lock, flags); if (skb_queue_empty(list)) goto out_unlock; - - spin_lock_irqsave(&list->lock, flags); + list_skb = list->next; + nskb = list_skb->next; while (list_skb != (struct sk_buff *)list) { if (skb_shinfo(list_skb) == skb_shinfo(skb)) { - this = list_skb; switch (n) { case TX_NOTIFY_OK: - __skb_unlink(this, list); + __skb_unlink(list_skb, list); + kfree_skb(list_skb); iucv_sock_wake_msglim(sk); - dev_put(this->dev); - kfree_skb(this); break; case TX_NOTIFY_PENDING: atomic_inc(&iucv->pendings); break; case TX_NOTIFY_DELAYED_OK: - __skb_unlink(this, list); + __skb_unlink(list_skb, list); atomic_dec(&iucv->pendings); if (atomic_read(&iucv->pendings) <= 0) iucv_sock_wake_msglim(sk); - dev_put(this->dev); - kfree_skb(this); + kfree_skb(list_skb); break; case TX_NOTIFY_UNREACHABLE: case TX_NOTIFY_DELAYED_UNREACHABLE: case TX_NOTIFY_TPQFULL: /* not yet used */ case TX_NOTIFY_GENERALERROR: case TX_NOTIFY_DELAYED_GENERALERROR: - __skb_unlink(this, list); - dev_put(this->dev); - kfree_skb(this); - sk->sk_state = IUCV_DISCONN; - sk->sk_state_change(sk); + __skb_unlink(list_skb, list); + kfree_skb(list_skb); + if (sk->sk_state == IUCV_CONNECTED) { + sk->sk_state = IUCV_DISCONN; + sk->sk_state_change(sk); + } break; } break; } - list_skb = list_skb->next; + list_skb = nskb; + nskb = nskb->next; } +out_unlock: spin_unlock_irqrestore(&list->lock, flags); if (sk->sk_state == IUCV_CLOSING) { @@ -2273,8 +2280,6 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *skb, } } -out_unlock: - bh_unlock_sock(sk); } static const struct proto_ops iucv_sock_ops = { .family = PF_IUCV, -- cgit From 51363b8751a673a00ad48eea895266396d53fa52 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Wed, 8 Feb 2012 00:19:48 +0000 Subject: af_iucv: allow retrieval of maximum message size For HS transport the maximum message size depends on the MTU-size of the HS-device bound to the AF_IUCV socket. This patch adds a getsockopt option MSGSIZE returning the maximum message size that can be handled for this AF_IUCV socket. Signed-off-by: Ursula Braun Signed-off-by: Frank Blaschka Signed-off-by: David S. Miller --- include/net/iucv/af_iucv.h | 1 + net/iucv/af_iucv.c | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/iucv/af_iucv.h b/include/net/iucv/af_iucv.h index a1517887aeac..2e1d5ecc2d1b 100644 --- a/include/net/iucv/af_iucv.h +++ b/include/net/iucv/af_iucv.h @@ -132,6 +132,7 @@ struct iucv_sock { /* iucv socket options (SOL_IUCV) */ #define SO_IPRMDATA_MSG 0x0080 /* send/recv IPRM_DATA msgs */ #define SO_MSGLIMIT 0x1000 /* get/set IUCV MSGLIMIT */ +#define SO_MSGSIZE 0x0800 /* get maximum msgsize */ /* iucv related control messages (scm) */ #define SCM_IUCV_TRGCLS 0x0001 /* target class control message */ diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index fbce4a3126de..98d1f0ba7fe9 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -1633,7 +1633,8 @@ static int iucv_sock_getsockopt(struct socket *sock, int level, int optname, { struct sock *sk = sock->sk; struct iucv_sock *iucv = iucv_sk(sk); - int val, len; + unsigned int val; + int len; if (level != SOL_IUCV) return -ENOPROTOOPT; @@ -1656,6 +1657,13 @@ static int iucv_sock_getsockopt(struct socket *sock, int level, int optname, : iucv->msglimit; /* default */ release_sock(sk); break; + case SO_MSGSIZE: + if (sk->sk_state == IUCV_OPEN) + return -EBADFD; + val = (iucv->hs_dev) ? iucv->hs_dev->mtu - + sizeof(struct af_iucv_trans_hdr) - ETH_HLEN : + 0x7fffffff; + break; default: return -ENOPROTOOPT; } -- cgit From de8c46bfc032fbdf490cfb67f534d2a0188ebeb0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 10 Jan 2012 13:43:04 -0500 Subject: SCSI: fix typo in definition of struct scsi_target This patch (as1506) corrects a typo in the definition of the scsi_target structure. pdt_1f_for_no_lun is supposed to be a single-bit flag, not a full-sized integer. Signed-off-by: Alan Stern Cc: James Bottomley Signed-off-by: Greg Kroah-Hartman --- include/scsi/scsi_device.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 77273f2fdd80..01cb3c4cb74d 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -246,8 +246,8 @@ struct scsi_target { unsigned int single_lun:1; /* Indicates we should only * allow I/O to one of the luns * for the device at a time. */ - unsigned int pdt_1f_for_no_lun; /* PDT = 0x1f */ - /* means no lun present */ + unsigned int pdt_1f_for_no_lun:1; /* PDT = 0x1f + * means no lun present. */ /* commands actually active on LLD. protected by host lock. */ unsigned int target_busy; /* -- cgit From 09b6b51b0b6c1b9bb61815baf205e4d74c89ff04 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 10 Jan 2012 13:43:30 -0500 Subject: SCSI & usb-storage: add flags for VPD pages and REPORT LUNS This patch (as1507) adds a skip_vpd_pages flag to struct scsi_device and a no_report_luns flag to struct scsi_target. The first is used to control whether sd will look at VPD pages for information on block provisioning, limits, and characteristics. The second prevents scsi_report_lun_scan() from issuing a REPORT LUNS command. The patch also modifies usb-storage to set the new flag bits for all USB devices and targets, and to stop adjusting the scsi_level value. Historically we have seen that USB mass-storage devices often don't support VPD pages or REPORT LUNS properly. Until now we have avoided these things by setting the scsi_level to SCSI_2 for all USB devices. But this has the side effect of storing the LUN bits into the second byte of each CDB, and now we have a report of a device which doesn't like that. The best solution is to stop abusing scsi_level and instead have separate flags for VPD pages and REPORT LUNS. Signed-off-by: Alan Stern Reported-by: Perry Wagle CC: Matthew Dharm Cc: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_scan.c | 4 ++++ drivers/scsi/sd.c | 2 +- drivers/usb/storage/scsiglue.c | 26 ++++++++++++++++---------- include/scsi/scsi_device.h | 3 +++ 4 files changed, 24 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 89da43f73c00..fd37bfbfbcdb 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1295,6 +1295,7 @@ EXPORT_SYMBOL(int_to_scsilun); * LUNs even if it's older than SCSI-3. * If BLIST_NOREPORTLUN is set, return 1 always. * If BLIST_NOLUN is set, return 0 always. + * If starget->no_report_luns is set, return 1 always. * * Return: * 0: scan completed (or no memory, so further scanning is futile) @@ -1321,6 +1322,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, * Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set. * Also allow SCSI-2 if BLIST_REPORTLUN2 is set and host adapter does * support more than 8 LUNs. + * Don't attempt if the target doesn't support REPORT LUNS. */ if (bflags & BLIST_NOREPORTLUN) return 1; @@ -1332,6 +1334,8 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, return 1; if (bflags & BLIST_NOLUN) return 0; + if (starget->no_report_luns) + return 1; if (!(sdev = scsi_device_lookup_by_target(starget, 0))) { sdev = scsi_alloc_sdev(starget, 0, NULL); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index c691fb50e6cb..d173b90b25e9 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2349,7 +2349,7 @@ static int sd_try_extended_inquiry(struct scsi_device *sdp) * some USB ones crash on receiving them, and the pages * we currently ask for are for SPC-3 and beyond */ - if (sdp->scsi_level > SCSI_SPC_2) + if (sdp->scsi_level > SCSI_SPC_2 && !sdp->skip_vpd_pages) return 1; return 0; } diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 13b8bcdf3dba..dc68cc9fef5d 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -197,6 +197,9 @@ static int slave_configure(struct scsi_device *sdev) * page x08, so we will skip it. */ sdev->skip_ms_page_8 = 1; + /* Some devices don't handle VPD pages correctly */ + sdev->skip_vpd_pages = 1; + /* Some disks return the total number of blocks in response * to READ CAPACITY rather than the highest block number. * If this device makes that mistake, tell the sd driver. */ @@ -217,16 +220,6 @@ static int slave_configure(struct scsi_device *sdev) if (sdev->scsi_level > SCSI_SPC_2) us->fflags |= US_FL_SANE_SENSE; - /* Some devices report a SCSI revision level above 2 but are - * unable to handle the REPORT LUNS command (for which - * support is mandatory at level 3). Since we already have - * a Get-Max-LUN request, we won't lose much by setting the - * revision level down to 2. The only devices that would be - * affected are those with sparse LUNs. */ - if (sdev->scsi_level > SCSI_2) - sdev->sdev_target->scsi_level = - sdev->scsi_level = SCSI_2; - /* USB-IDE bridges tend to report SK = 0x04 (Non-recoverable * Hardware Error) when any low-level error occurs, * recoverable or not. Setting this flag tells the SCSI @@ -283,6 +276,18 @@ static int slave_configure(struct scsi_device *sdev) return 0; } +static int target_alloc(struct scsi_target *starget) +{ + /* + * Some USB drives don't support REPORT LUNS, even though they + * report a SCSI revision level above 2. Tell the SCSI layer + * not to issue that command; it will perform a normal sequential + * scan instead. + */ + starget->no_report_luns = 1; + return 0; +} + /* queue a command */ /* This is always called with scsi_lock(host) held */ static int queuecommand_lck(struct scsi_cmnd *srb, @@ -546,6 +551,7 @@ struct scsi_host_template usb_stor_host_template = { .slave_alloc = slave_alloc, .slave_configure = slave_configure, + .target_alloc = target_alloc, /* lots of sg segments can be handled */ .sg_tablesize = SCSI_MAX_SG_CHAIN_SEGMENTS, diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 01cb3c4cb74d..b3a1c2daf6cc 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -136,6 +136,7 @@ struct scsi_device { unsigned use_10_for_ms:1; /* first try 10-byte mode sense/select */ unsigned skip_ms_page_8:1; /* do not use MODE SENSE page 0x08 */ unsigned skip_ms_page_3f:1; /* do not use MODE SENSE page 0x3f */ + unsigned skip_vpd_pages:1; /* do not read VPD pages */ unsigned use_192_bytes_for_3f:1; /* ask for 192 bytes from page 0x3f */ unsigned no_start_on_add:1; /* do not issue start on add */ unsigned allow_restart:1; /* issue START_UNIT in error handler */ @@ -248,6 +249,8 @@ struct scsi_target { * for the device at a time. */ unsigned int pdt_1f_for_no_lun:1; /* PDT = 0x1f * means no lun present. */ + unsigned int no_report_luns:1; /* Don't use + * REPORT LUNS for scanning. */ /* commands actually active on LLD. protected by host lock. */ unsigned int target_busy; /* -- cgit From 59a084a70afa0678bda2a23a7bc7cc59664945c7 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 2 Feb 2012 16:56:48 -0800 Subject: drivers: hv: Cleanup the kvp related state in hyperv.h Now cleanup the hyperv.h with regards to KVP definitions. Signed-off-by: K. Y. Srinivasan Signed-off-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- include/linux/hyperv.h | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 7332b3faecc8..b822978ecbc8 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -137,7 +137,6 @@ struct hv_ku_msg { -#ifdef __KERNEL__ /* * Registry value types. @@ -163,28 +162,30 @@ enum hv_kvp_exchg_pool { }; struct hv_kvp_hdr { - u8 operation; - u8 pool; -}; + __u8 operation; + __u8 pool; + __u16 pad; +} __attribute__((packed)); struct hv_kvp_exchg_msg_value { - u32 value_type; - u32 key_size; - u32 value_size; - u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; - u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; -}; + __u32 value_type; + __u32 key_size; + __u32 value_size; + __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; + __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; +} __attribute__((packed)); struct hv_kvp_msg_enumerate { - u32 index; + __u32 index; struct hv_kvp_exchg_msg_value data; -}; +} __attribute__((packed)); struct hv_kvp_msg { struct hv_kvp_hdr kvp_hdr; struct hv_kvp_msg_enumerate kvp_data; -}; +} __attribute__((packed)); +#ifdef __KERNEL__ #include #include #include -- cgit From 2640335438ca4d7b139e114dae5f0d80e740e106 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 2 Feb 2012 16:56:50 -0800 Subject: drivers: hv: kvp: Cleanup the kernel/user protocol Now, cleanup the user/kernel KVP protocol by using the same structure definition that is used for host/guest KVP protocol. This simplifies the code. Signed-off-by: K. Y. Srinivasan Signed-off-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.c | 41 +++++++++++++++++++++++++---------------- include/linux/hyperv.h | 30 +++++------------------------- tools/hv/hv_kvp_daemon.c | 30 +++++++++++++++--------------- 3 files changed, 45 insertions(+), 56 deletions(-) (limited to 'include') diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 4a6971e13539..0ef4c1f6ca54 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -71,15 +71,20 @@ kvp_register(void) { struct cn_msg *msg; + struct hv_kvp_msg *kvp_msg; + char *version; - msg = kzalloc(sizeof(*msg) + strlen(HV_DRV_VERSION) + 1 , GFP_ATOMIC); + msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg), GFP_ATOMIC); if (msg) { + kvp_msg = (struct hv_kvp_msg *)msg->data; + version = kvp_msg->body.kvp_version; msg->id.idx = CN_KVP_IDX; msg->id.val = CN_KVP_VAL; - msg->seq = KVP_REGISTER; - strcpy(msg->data, HV_DRV_VERSION); - msg->len = strlen(HV_DRV_VERSION) + 1; + + kvp_msg->kvp_hdr.operation = KVP_OP_REGISTER; + strcpy(version, HV_DRV_VERSION); + msg->len = sizeof(struct hv_kvp_msg); cn_netlink_send(msg, 0, GFP_ATOMIC); kfree(msg); } @@ -101,23 +106,24 @@ kvp_work_func(struct work_struct *dummy) static void kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { - struct hv_ku_msg *message; + struct hv_kvp_msg *message; + struct hv_kvp_msg_enumerate *data; - message = (struct hv_ku_msg *)msg->data; - if (msg->seq == KVP_REGISTER) { + message = (struct hv_kvp_msg *)msg->data; + if (message->kvp_hdr.operation == KVP_OP_REGISTER) { pr_info("KVP: user-mode registering done.\n"); kvp_register(); } - if (msg->seq == KVP_USER_SET) { + if (message->kvp_hdr.operation == KVP_OP_ENUMERATE) { + data = &message->body.kvp_enum_data; /* * Complete the transaction by forwarding the key value * to the host. But first, cancel the timeout. */ if (cancel_delayed_work_sync(&kvp_work)) - kvp_respond_to_host(message->kvp_key, - message->kvp_value, - !strlen(message->kvp_key)); + kvp_respond_to_host(data->data.key, data->data.value, + !strlen(data->data.key)); } } @@ -125,6 +131,7 @@ static void kvp_send_key(struct work_struct *dummy) { struct cn_msg *msg; + struct hv_kvp_msg *message; int index = kvp_transaction.index; msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC); @@ -132,9 +139,11 @@ kvp_send_key(struct work_struct *dummy) if (msg) { msg->id.idx = CN_KVP_IDX; msg->id.val = CN_KVP_VAL; - msg->seq = KVP_KERNEL_GET; - ((struct hv_ku_msg *)msg->data)->kvp_index = index; - msg->len = sizeof(struct hv_ku_msg); + + message = (struct hv_kvp_msg *)msg->data; + message->kvp_hdr.operation = KVP_OP_ENUMERATE; + message->body.kvp_enum_data.index = index; + msg->len = sizeof(struct hv_kvp_msg); cn_netlink_send(msg, 0, GFP_ATOMIC); kfree(msg); } @@ -191,7 +200,7 @@ kvp_respond_to_host(char *key, char *value, int error) kvp_msg = (struct hv_kvp_msg *) &recv_buffer[sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; - kvp_data = &kvp_msg->kvp_data; + kvp_data = &kvp_msg->body.kvp_enum_data; key_name = key; /* @@ -266,7 +275,7 @@ void hv_kvp_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; - kvp_data = &kvp_msg->kvp_data; + kvp_data = &kvp_msg->body.kvp_enum_data; /* * We only support the "get" operation on diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index b822978ecbc8..75aee6720c1b 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -113,30 +113,6 @@ * (not supported), a NULL key string is returned. */ -/* - * - * The following definitions are shared with the user-mode component; do not - * change any of this without making the corresponding changes in - * the KVP user-mode component. - */ - -enum hv_ku_op { - KVP_REGISTER = 0, /* Register the user mode component */ - KVP_KERNEL_GET, /* Kernel is requesting the value */ - KVP_KERNEL_SET, /* Kernel is providing the value */ - KVP_USER_GET, /* User is requesting the value */ - KVP_USER_SET /* User is providing the value */ -}; - -struct hv_ku_msg { - __u32 kvp_index; /* Key index */ - __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ - __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ -}; - - - - /* * Registry value types. @@ -149,6 +125,7 @@ enum hv_kvp_exchg_op { KVP_OP_SET, KVP_OP_DELETE, KVP_OP_ENUMERATE, + KVP_OP_REGISTER, KVP_OP_COUNT /* Number of operations, must be last. */ }; @@ -182,7 +159,10 @@ struct hv_kvp_msg_enumerate { struct hv_kvp_msg { struct hv_kvp_hdr kvp_hdr; - struct hv_kvp_msg_enumerate kvp_data; + union { + struct hv_kvp_msg_enumerate kvp_enum_data; + char kvp_version[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; + } body; } __attribute__((packed)); #ifdef __KERNEL__ diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index b75523cde2cd..4ebf70380582 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -302,7 +302,7 @@ int main(void) struct pollfd pfd; struct nlmsghdr *incoming_msg; struct cn_msg *incoming_cn_msg; - struct hv_ku_msg *hv_msg; + struct hv_kvp_msg *hv_msg; char *p; char *key_value; char *key_name; @@ -340,9 +340,11 @@ int main(void) message = (struct cn_msg *)kvp_send_buffer; message->id.idx = CN_KVP_IDX; message->id.val = CN_KVP_VAL; - message->seq = KVP_REGISTER; + + hv_msg = (struct hv_kvp_msg *)message->data; + hv_msg->kvp_hdr.operation = KVP_OP_REGISTER; message->ack = 0; - message->len = 0; + message->len = sizeof(struct hv_kvp_msg); len = netlink_send(fd, message); if (len < 0) { @@ -368,14 +370,15 @@ int main(void) incoming_msg = (struct nlmsghdr *)kvp_recv_buffer; incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); + hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; - switch (incoming_cn_msg->seq) { - case KVP_REGISTER: + switch (hv_msg->kvp_hdr.operation) { + case KVP_OP_REGISTER: /* * Driver is registering with us; stash away the version * information. */ - p = (char *)incoming_cn_msg->data; + p = (char *)hv_msg->body.kvp_version; lic_version = malloc(strlen(p) + 1); if (lic_version) { strcpy(lic_version, p); @@ -386,17 +389,15 @@ int main(void) } continue; - case KVP_KERNEL_GET: - break; default: - continue; + break; } - hv_msg = (struct hv_ku_msg *)incoming_cn_msg->data; - key_name = (char *)hv_msg->kvp_key; - key_value = (char *)hv_msg->kvp_value; + hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; + key_name = (char *)hv_msg->body.kvp_enum_data.data.key; + key_value = (char *)hv_msg->body.kvp_enum_data.data.value; - switch (hv_msg->kvp_index) { + switch (hv_msg->body.kvp_enum_data.index) { case FullyQualifiedDomainName: kvp_get_domain_name(key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); @@ -456,9 +457,8 @@ int main(void) incoming_cn_msg->id.idx = CN_KVP_IDX; incoming_cn_msg->id.val = CN_KVP_VAL; - incoming_cn_msg->seq = KVP_USER_SET; incoming_cn_msg->ack = 0; - incoming_cn_msg->len = sizeof(struct hv_ku_msg); + incoming_cn_msg->len = sizeof(struct hv_kvp_msg); len = netlink_send(fd, incoming_cn_msg); if (len < 0) { -- cgit From aad4f4000cecec9c80b5f9aff91043dc104d61a0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 18 Nov 2011 10:12:49 -0800 Subject: PCI: Add helper macro for pci_register_driver boilerplate This patch introduces the module_pci_driver macro which is a convenience macro for PCI driver modules similar to module_platform_driver. It is intended to be used by drivers which init/exit section does nothing but register/unregister the PCI driver. By using this macro it is possible to eliminate a few lines of boilerplate code per PCI driver. Based on work done by Lars-Peter Clausen for other busses (i2c and spi). Cc: Lars-Peter Clausen Signed-off-by: Greg Kroah-Hartman Acked-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- include/linux/pci.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include') diff --git a/include/linux/pci.h b/include/linux/pci.h index a16b1df3deff..d4afd703e948 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -946,6 +946,19 @@ int __must_check __pci_register_driver(struct pci_driver *, struct module *, __pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) void pci_unregister_driver(struct pci_driver *dev); + +/** + * module_pci_driver() - Helper macro for registering a PCI driver + * @__pci_driver: pci_driver struct + * + * Helper macro for PCI drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_pci_driver(__pci_driver) \ + module_driver(__pci_driver, pci_register_driver, \ + pci_unregister_driver) + void pci_remove_behind_bridge(struct pci_dev *dev); struct pci_driver *pci_dev_driver(const struct pci_dev *dev); int pci_add_dynid(struct pci_driver *drv, -- cgit From 0846e7e9856c0928223447d9349a877202a63f24 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 3 Feb 2012 17:11:54 -0500 Subject: usb: Add support for indicating whether a port is removable Userspace may want to make policy decisions based on whether or not a given USB device is removable. Add a per-device member and support for exposing it in sysfs. Information sources to populate it will be added later. Signed-off-by: Matthew Garrett Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-usb | 11 +++++++++++ drivers/usb/core/sysfs.c | 23 +++++++++++++++++++++++ include/linux/usb.h | 8 ++++++++ 3 files changed, 42 insertions(+) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index b4f548792e32..7c22a532fdfb 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -182,3 +182,14 @@ Description: USB2 hardware LPM is enabled for the device. Developer can write y/Y/1 or n/N/0 to the file to enable/disable the feature. + +What: /sys/bus/usb/devices/.../removable +Date: February 2012 +Contact: Matthew Garrett +Description: + Some information about whether a given USB device is + physically fixed to the platform can be inferred from a + combination of hub decriptor bits and platform-specific data + such as ACPI. This file will read either "removable" or + "fixed" if the information is available, and "unknown" + otherwise. \ No newline at end of file diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 9e491ca2e5c4..566d9f94f735 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -230,6 +230,28 @@ show_urbnum(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL); +static ssize_t +show_removable(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev; + char *state; + + udev = to_usb_device(dev); + + switch (udev->removable) { + case USB_DEVICE_REMOVABLE: + state = "removable"; + break; + case USB_DEVICE_FIXED: + state = "fixed"; + break; + default: + state = "unknown"; + } + + return sprintf(buf, "%s\n", state); +} +static DEVICE_ATTR(removable, S_IRUGO, show_removable, NULL); #ifdef CONFIG_PM @@ -626,6 +648,7 @@ static struct attribute *dev_attrs[] = { &dev_attr_avoid_reset_quirk.attr, &dev_attr_authorized.attr, &dev_attr_remove.attr, + &dev_attr_removable.attr, NULL, }; static struct attribute_group dev_attr_grp = { diff --git a/include/linux/usb.h b/include/linux/usb.h index 27a4e16d2bf1..b2eb3a47caf5 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -376,6 +376,12 @@ struct usb_bus { struct usb_tt; +enum usb_device_removable { + USB_DEVICE_REMOVABLE_UNKNOWN = 0, + USB_DEVICE_REMOVABLE, + USB_DEVICE_FIXED, +}; + /** * struct usb_device - kernel's representation of a USB device * @devnum: device number; address on a USB bus @@ -432,6 +438,7 @@ struct usb_tt; * @wusb_dev: if this is a Wireless USB device, link to the WUSB * specific data for the device. * @slot_id: Slot ID assigned by xHCI + * @removable: Device can be physically removed from this port * * Notes: * Usbcore drivers should not set usbdev->state directly. Instead use @@ -509,6 +516,7 @@ struct usb_device { #endif struct wusb_dev *wusb_dev; int slot_id; + enum usb_device_removable removable; }; #define to_usb_device(d) container_of(d, struct usb_device, dev) -- cgit From e688355bfeadf17ef522b1e62cc12f8e88e69667 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 6 Feb 2012 19:22:19 +0100 Subject: USB: serial: add macro for console error reporting Add macro which prints an error message only once if port is used a console. Reporting errors in a write path when port is used as a console could otherwise result in an infinite loop. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/serial.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'include') diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 4267a9c717ba..10cb74d2ad1d 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -389,5 +389,20 @@ do { \ printk(KERN_DEBUG "%s: " format "\n", __FILE__, ##arg); \ } while (0) +/* + * Macro for reporting errors in write path to avoid inifinite loop + * when port is used as a console. + */ +#define dev_err_console(usport, fmt, ...) \ +do { \ + static bool __print_once; \ + struct usb_serial_port *__port = (usport); \ + \ + if (!__port->port.console || !__print_once) { \ + __print_once = true; \ + dev_err(&__port->dev, fmt, ##__VA_ARGS__); \ + } \ +} while (0) + #endif /* __LINUX_USB_SERIAL_H */ -- cgit From 3f5dc70721af68b76ad544b0c61fea365d67c041 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 8 Feb 2012 14:35:46 +0100 Subject: tty: serial: altera_uart: remove early_altera_uart_setup The function has no users inside the tree and the nios2 (out-of-mainline) port doesn't use it either (anymore). Signed-off-by: Tobias Klauser Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/altera_uart.c | 23 ----------------------- include/linux/altera_uart.h | 4 ---- 2 files changed, 27 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 1d04c5037f25..217833e94b5b 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -403,29 +403,6 @@ static struct altera_uart altera_uart_ports[CONFIG_SERIAL_ALTERA_UART_MAXPORTS]; #if defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) -int __init early_altera_uart_setup(struct altera_uart_platform_uart *platp) -{ - struct uart_port *port; - int i; - - for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS && platp[i].mapbase; i++) { - port = &altera_uart_ports[i].port; - - port->line = i; - port->type = PORT_ALTERA_UART; - port->mapbase = platp[i].mapbase; - port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE); - port->iotype = SERIAL_IO_MEM; - port->irq = platp[i].irq; - port->uartclk = platp[i].uartclk; - port->flags = UPF_BOOT_AUTOCONF; - port->ops = &altera_uart_ops; - port->private_data = platp; - } - - return 0; -} - static void altera_uart_console_putc(struct uart_port *port, const char c) { while (!(altera_uart_readl(port, ALTERA_UART_STATUS_REG) & diff --git a/include/linux/altera_uart.h b/include/linux/altera_uart.h index a10a90791976..c022c82db7ca 100644 --- a/include/linux/altera_uart.h +++ b/include/linux/altera_uart.h @@ -5,8 +5,6 @@ #ifndef __ALTUART_H #define __ALTUART_H -#include - struct altera_uart_platform_uart { unsigned long mapbase; /* Physical address base */ unsigned int irq; /* Interrupt vector */ @@ -14,6 +12,4 @@ struct altera_uart_platform_uart { unsigned int bus_shift; /* Bus shift (address stride) */ }; -int __init early_altera_uart_setup(struct altera_uart_platform_uart *platp); - #endif /* __ALTUART_H */ -- cgit From 6816383a09b5be8d35f14f4c25dedb64498e4959 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 9 Feb 2012 18:48:19 -0500 Subject: tty: sparc: rename drivers/tty/serial/suncore.h -> include/linux/sunserialcore.h There are multiple users of this file from different source paths now, and rather than have ../ paths in include statements, just move the file to the linux header dir. Suggested-by: David S. Miller Signed-off-by: Paul Gortmaker Acked-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 2 +- drivers/tty/serial/8250/8250.c | 7 +++---- drivers/tty/serial/suncore.c | 2 +- drivers/tty/serial/suncore.h | 33 --------------------------------- drivers/tty/serial/sunhv.c | 3 +-- drivers/tty/serial/sunsab.c | 2 +- drivers/tty/serial/sunsu.c | 3 +-- drivers/tty/serial/sunzilog.c | 2 +- include/linux/sunserialcore.h | 33 +++++++++++++++++++++++++++++++++ 9 files changed, 42 insertions(+), 45 deletions(-) delete mode 100644 drivers/tty/serial/suncore.h create mode 100644 include/linux/sunserialcore.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 9a648eb8e213..27c052cc8835 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6187,8 +6187,8 @@ L: sparclinux@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-next-2.6.git S: Maintained +F: include/linux/sunserialcore.h F: drivers/tty/serial/suncore.c -F: drivers/tty/serial/suncore.h F: drivers/tty/serial/sunhv.c F: drivers/tty/serial/sunsab.c F: drivers/tty/serial/sunsab.h diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index b423d0f962a2..917ab8452746 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -38,16 +38,15 @@ #include #include #include +#ifdef CONFIG_SPARC +#include +#endif #include #include #include "8250.h" -#ifdef CONFIG_SPARC -#include "../suncore.h" -#endif - /* * Configuration: * share_irqs - whether we pass IRQF_SHARED to request_irq(). This option diff --git a/drivers/tty/serial/suncore.c b/drivers/tty/serial/suncore.c index 6381a0282ee7..6e4ac8db2d79 100644 --- a/drivers/tty/serial/suncore.c +++ b/drivers/tty/serial/suncore.c @@ -17,11 +17,11 @@ #include #include #include +#include #include #include -#include "suncore.h" static int sunserial_current_minor = 64; diff --git a/drivers/tty/serial/suncore.h b/drivers/tty/serial/suncore.h deleted file mode 100644 index db2057936c31..000000000000 --- a/drivers/tty/serial/suncore.h +++ /dev/null @@ -1,33 +0,0 @@ -/* suncore.h - * - * Generic SUN serial/kbd/ms layer. Based entirely - * upon drivers/sbus/char/sunserial.h which is: - * - * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - * - * Port to new UART layer is: - * - * Copyright (C) 2002 David S. Miller (davem@redhat.com) - */ - -#ifndef _SERIAL_SUN_H -#define _SERIAL_SUN_H - -/* Serial keyboard defines for L1-A processing... */ -#define SUNKBD_RESET 0xff -#define SUNKBD_L1 0x01 -#define SUNKBD_UP 0x80 -#define SUNKBD_A 0x4d - -extern unsigned int suncore_mouse_baud_cflag_next(unsigned int, int *); -extern int suncore_mouse_baud_detection(unsigned char, int); - -extern int sunserial_register_minors(struct uart_driver *, int); -extern void sunserial_unregister_minors(struct uart_driver *, int); - -extern int sunserial_console_match(struct console *, struct device_node *, - struct uart_driver *, int, bool); -extern void sunserial_console_termios(struct console *, - struct device_node *); - -#endif /* !(_SERIAL_SUN_H) */ diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index c0b7246d7339..3ba5d285c2d0 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -29,8 +29,7 @@ #endif #include - -#include "suncore.h" +#include #define CON_BREAK ((long)-1) #define CON_HUP ((long)-2) diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index b5fa2a57b9da..62dacd0ba526 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -43,8 +43,8 @@ #endif #include +#include -#include "suncore.h" #include "sunsab.h" struct uart_sunsab_port { diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index ad0f8f5f6ea1..d3ca6da129fe 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -47,8 +47,7 @@ #endif #include - -#include "suncore.h" +#include /* We are on a NS PC87303 clocked with 24.0 MHz, which results * in a UART clock of 1.8462 MHz. diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index 5a47d1b196d8..da4415842a43 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -43,8 +43,8 @@ #endif #include +#include -#include "suncore.h" #include "sunzilog.h" /* On 32-bit sparcs we need to delay after register accesses diff --git a/include/linux/sunserialcore.h b/include/linux/sunserialcore.h new file mode 100644 index 000000000000..68e7430bb0fe --- /dev/null +++ b/include/linux/sunserialcore.h @@ -0,0 +1,33 @@ +/* sunserialcore.h + * + * Generic SUN serial/kbd/ms layer. Based entirely + * upon drivers/sbus/char/sunserial.h which is: + * + * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) + * + * Port to new UART layer is: + * + * Copyright (C) 2002 David S. Miller (davem@redhat.com) + */ + +#ifndef _SERIAL_SUN_H +#define _SERIAL_SUN_H + +/* Serial keyboard defines for L1-A processing... */ +#define SUNKBD_RESET 0xff +#define SUNKBD_L1 0x01 +#define SUNKBD_UP 0x80 +#define SUNKBD_A 0x4d + +extern unsigned int suncore_mouse_baud_cflag_next(unsigned int, int *); +extern int suncore_mouse_baud_detection(unsigned char, int); + +extern int sunserial_register_minors(struct uart_driver *, int); +extern void sunserial_unregister_minors(struct uart_driver *, int); + +extern int sunserial_console_match(struct console *, struct device_node *, + struct uart_driver *, int, bool); +extern void sunserial_console_termios(struct console *, + struct device_node *); + +#endif /* !(_SERIAL_SUN_H) */ -- cgit From 74d1d82cdaaec727f5072eb1c9f49b7e920e076f Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 6 Feb 2012 11:22:22 -0800 Subject: drivers/base: add bus for System-on-Chip devices Traditionally, any System-on-Chip based platform creates a flat list of platform_devices directly under /sys/devices/platform. In order to give these some better structure, this introduces a new bus type for soc_devices that are registered with the new soc_device_register() function. All devices that are on the same chip should then be registered as child devices of the soc device. The soc bus also exports a few standardised device attributes which allow user space to query the specific type of soc. Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/base/Kconfig | 3 + drivers/base/Makefile | 1 + drivers/base/soc.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sys_soc.h | 37 ++++++++++ 4 files changed, 224 insertions(+) create mode 100644 drivers/base/soc.c create mode 100644 include/linux/sys_soc.h (limited to 'include') diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 7be9f79018e9..9aa618acfe97 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -176,6 +176,9 @@ config GENERIC_CPU_DEVICES bool default n +config SOC_BUS + bool + source "drivers/base/regmap/Kconfig" config DMA_SHARED_BUFFER diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 610f9997a403..b6d1b9c4200c 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_MODULES) += module.o endif obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o obj-$(CONFIG_REGMAP) += regmap/ +obj-$(CONFIG_SOC_BUS) += soc.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG diff --git a/drivers/base/soc.c b/drivers/base/soc.c new file mode 100644 index 000000000000..05f150382da8 --- /dev/null +++ b/drivers/base/soc.c @@ -0,0 +1,183 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Lee Jones for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_IDR(soc_ida); +static DEFINE_SPINLOCK(soc_lock); + +static ssize_t soc_info_get(struct device *dev, + struct device_attribute *attr, + char *buf); + +struct soc_device { + struct device dev; + struct soc_device_attribute *attr; + int soc_dev_num; +}; + +static struct bus_type soc_bus_type = { + .name = "soc", +}; + +static DEVICE_ATTR(machine, S_IRUGO, soc_info_get, NULL); +static DEVICE_ATTR(family, S_IRUGO, soc_info_get, NULL); +static DEVICE_ATTR(soc_id, S_IRUGO, soc_info_get, NULL); +static DEVICE_ATTR(revision, S_IRUGO, soc_info_get, NULL); + +struct device *soc_device_to_device(struct soc_device *soc_dev) +{ + return &soc_dev->dev; +} + +static mode_t soc_attribute_mode(struct kobject *kobj, + struct attribute *attr, + int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); + + if ((attr == &dev_attr_machine.attr) + && (soc_dev->attr->machine != NULL)) + return attr->mode; + if ((attr == &dev_attr_family.attr) + && (soc_dev->attr->family != NULL)) + return attr->mode; + if ((attr == &dev_attr_revision.attr) + && (soc_dev->attr->revision != NULL)) + return attr->mode; + if ((attr == &dev_attr_soc_id.attr) + && (soc_dev->attr->soc_id != NULL)) + return attr->mode; + + /* Unknown or unfilled attribute. */ + return 0; +} + +static ssize_t soc_info_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); + + if (attr == &dev_attr_machine) + return sprintf(buf, "%s\n", soc_dev->attr->machine); + if (attr == &dev_attr_family) + return sprintf(buf, "%s\n", soc_dev->attr->family); + if (attr == &dev_attr_revision) + return sprintf(buf, "%s\n", soc_dev->attr->revision); + if (attr == &dev_attr_soc_id) + return sprintf(buf, "%s\n", soc_dev->attr->soc_id); + + return -EINVAL; + +} + +static struct attribute *soc_attr[] = { + &dev_attr_machine.attr, + &dev_attr_family.attr, + &dev_attr_soc_id.attr, + &dev_attr_revision.attr, + NULL, +}; + +static const struct attribute_group soc_attr_group = { + .attrs = soc_attr, + .is_visible = soc_attribute_mode, +}; + +static const struct attribute_group *soc_attr_groups[] = { + &soc_attr_group, + NULL, +}; + +static void soc_release(struct device *dev) +{ + struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); + + kfree(soc_dev); +} + +struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr) +{ + struct soc_device *soc_dev; + int ret; + + soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL); + if (!soc_dev) { + ret = -ENOMEM; + goto out1; + } + + /* Fetch a unique (reclaimable) SOC ID. */ + do { + if (!ida_pre_get(&soc_ida, GFP_KERNEL)) { + ret = -ENOMEM; + goto out2; + } + + spin_lock(&soc_lock); + ret = ida_get_new(&soc_ida, &soc_dev->soc_dev_num); + spin_unlock(&soc_lock); + + } while (ret == -EAGAIN); + + if (ret) + goto out2; + + soc_dev->attr = soc_dev_attr; + soc_dev->dev.bus = &soc_bus_type; + soc_dev->dev.groups = soc_attr_groups; + soc_dev->dev.release = soc_release; + + dev_set_name(&soc_dev->dev, "soc%d", soc_dev->soc_dev_num); + + ret = device_register(&soc_dev->dev); + if (ret) + goto out3; + + return soc_dev; + +out3: + ida_remove(&soc_ida, soc_dev->soc_dev_num); +out2: + kfree(soc_dev); +out1: + return ERR_PTR(ret); +} + +/* Ensure soc_dev->attr is freed prior to calling soc_device_unregister. */ +void soc_device_unregister(struct soc_device *soc_dev) +{ + ida_remove(&soc_ida, soc_dev->soc_dev_num); + + device_unregister(&soc_dev->dev); +} + +static int __init soc_bus_register(void) +{ + spin_lock_init(&soc_lock); + + return bus_register(&soc_bus_type); +} +core_initcall(soc_bus_register); + +static void __exit soc_bus_unregister(void) +{ + ida_destroy(&soc_ida); + + bus_unregister(&soc_bus_type); +} +module_exit(soc_bus_unregister); diff --git a/include/linux/sys_soc.h b/include/linux/sys_soc.h new file mode 100644 index 000000000000..2739ccb69571 --- /dev/null +++ b/include/linux/sys_soc.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * Author: Lee Jones for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2 + */ +#ifndef __SOC_BUS_H +#define __SOC_BUS_H + +#include + +struct soc_device_attribute { + const char *machine; + const char *family; + const char *revision; + const char *soc_id; +}; + +/** + * soc_device_register - register SoC as a device + * @soc_plat_dev_attr: Attributes passed from platform to be attributed to a SoC + */ +struct soc_device *soc_device_register( + struct soc_device_attribute *soc_plat_dev_attr); + +/** + * soc_device_unregister - unregister SoC device + * @dev: SoC device to be unregistered + */ +void soc_device_unregister(struct soc_device *soc_dev); + +/** + * soc_device_to_device - helper function to fetch struct device + * @soc: Previously registered SoC device container + */ +struct device *soc_device_to_device(struct soc_device *soc); + +#endif /* __SOC_BUS_H */ -- cgit From 4031ae6edb92f7e0aade76357813211ae0520a5c Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 27 Jan 2012 06:22:53 +0000 Subject: skbuff: Move rxhash and vlan_tci to consolidate holes in sk_buff This change helps to reduce the overall size of the sk_buff by moving rxhash and vlan_tci so that the u16 values and u8 bitfields can be better combined to create only one hole instead of multiple. Signed-off-by: Alexander Duyck Tested-by: Stephen Ko Signed-off-by: Jeff Kirsher --- include/linux/skbuff.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 50db9b04a552..2b7317ff297f 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -438,6 +438,11 @@ struct sk_buff { #endif int skb_iif; + + __u32 rxhash; + + __u16 vlan_tci; + #ifdef CONFIG_NET_SCHED __u16 tc_index; /* traffic control index */ #ifdef CONFIG_NET_CLS_ACT @@ -445,8 +450,6 @@ struct sk_buff { #endif #endif - __u32 rxhash; - __u16 queue_mapping; kmemcheck_bitfield_begin(flags2); #ifdef CONFIG_IPV6_NDISC_NODETYPE @@ -470,8 +473,6 @@ struct sk_buff { __u32 dropcount; }; - __u16 vlan_tci; - sk_buff_data_t transport_header; sk_buff_data_t network_header; sk_buff_data_t mac_header; -- cgit From 9388dc3047a88bedfd867e9ba3e1980c815ac524 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 9 Feb 2012 20:45:19 +0400 Subject: sched: Turn lock_task_sighand() into a static inline It appears that sparse tool understands static inline functions for context balance checking, so let's turn the macros into an inline func. This makes the code a little bit more robust. Suggested-by: Oleg Nesterov Signed-off-by: Anton Vorontsov Cc: KOSAKI Motohiro Cc: Greg KH Cc: Arve Cc: San Mehat Cc: Colin Cross Cc: Eric W. Biederman Cc: Paul E. McKenney Cc: kernel-team@android.com Cc: linaro-kernel@lists.linaro.org Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120209164519.GA10266@oksana.dev.rtsoft.ru Signed-off-by: Ingo Molnar --- include/linux/sched.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 92313a3f6f77..5dba2ad52431 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2393,12 +2393,15 @@ static inline void task_unlock(struct task_struct *p) extern struct sighand_struct *__lock_task_sighand(struct task_struct *tsk, unsigned long *flags); -#define lock_task_sighand(tsk, flags) \ -({ struct sighand_struct *__ss; \ - __cond_lock(&(tsk)->sighand->siglock, \ - (__ss = __lock_task_sighand(tsk, flags))); \ - __ss; \ -}) \ +static inline struct sighand_struct *lock_task_sighand(struct task_struct *tsk, + unsigned long *flags) +{ + struct sighand_struct *ret; + + ret = __lock_task_sighand(tsk, flags); + (void)__cond_lock(&tsk->sighand->siglock, ret); + return ret; +} static inline void unlock_task_sighand(struct task_struct *tsk, unsigned long *flags) -- cgit From 7a3198a89722ad9521d22b05938d357eac7460fa Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Thu, 9 Feb 2012 09:34:41 +0000 Subject: ipv6: helper function to get tclass Implement helper inline function to get traffic class from IPv6 header. Signed-off-by: Jiri Benc Signed-off-by: David S. Miller --- include/linux/ipv6.h | 5 +++++ net/ipv6/datagram.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 743a16a41040..4847a64d3c0a 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -233,6 +233,11 @@ static inline struct ipv6hdr *ipipv6_hdr(const struct sk_buff *skb) return (struct ipv6hdr *)skb_transport_header(skb); } +static inline __u8 ipv6_tclass(const struct ipv6hdr *iph) +{ + return (ntohl(*(__be32 *)iph) >> 20) & 0xff; +} + /* This structure contains results of exthdrs parsing as offsets from skb->nh. diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 251e7cd75e89..76832c8dc89d 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -485,7 +485,7 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) } if (np->rxopt.bits.rxtclass) { - int tclass = (ntohl(*(__be32 *)ipv6_hdr(skb)) >> 20) & 0xff; + int tclass = ipv6_tclass(ipv6_hdr(skb)); put_cmsg(msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass); } -- cgit From 4c507d2897bd9be810b3403ade73b04cf6fdfd4a Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Thu, 9 Feb 2012 09:35:49 +0000 Subject: net: implement IP_RECVTOS for IP_PKTOPTIONS Currently, it is not easily possible to get TOS/DSCP value of packets from an incoming TCP stream. The mechanism is there, IP_PKTOPTIONS getsockopt with IP_RECVTOS set, the same way as incoming TTL can be queried. This is not actually implemented for TOS, though. This patch adds this functionality, both for IPv4 (IP_PKTOPTIONS) and IPv6 (IPV6_2292PKTOPTIONS). For IPv4, like in the IP_RECVTTL case, the value of the TOS field is stored from the other party's ACK. This is needed for proxies which require DSCP transparency. One such example is at http://zph.bratcheda.org/. Signed-off-by: Jiri Benc Signed-off-by: David S. Miller --- include/linux/ipv6.h | 2 +- include/net/inet_sock.h | 1 + net/ipv4/af_inet.c | 1 + net/ipv4/ip_sockglue.c | 4 ++++ net/ipv4/tcp_ipv4.c | 1 + net/ipv6/af_inet6.c | 1 + net/ipv6/ipv6_sockglue.c | 4 ++++ net/ipv6/tcp_ipv6.c | 4 ++++ 8 files changed, 17 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 4847a64d3c0a..8260ef779762 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -366,7 +366,7 @@ struct ipv6_pinfo { dontfrag:1; __u8 min_hopcount; __u8 tclass; - __u8 padding; + __u8 rcv_tclass; __u32 dst_cookie; diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 022f772c0ebe..ae17e1352d7e 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -168,6 +168,7 @@ struct inet_sock { transparent:1, mc_all:1, nodefrag:1; + __u8 rcv_tos; int uc_index; int mc_index; __be32 mc_addr; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index f7b5670744f0..e588a34e85c2 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -381,6 +381,7 @@ lookup_protocol: inet->mc_all = 1; inet->mc_index = 0; inet->mc_list = NULL; + inet->rcv_tos = 0; sk_refcnt_debug_inc(sk); diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 9125529dab95..ca50d9f9f8c1 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -1289,6 +1289,10 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname, int hlim = inet->mc_ttl; put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); } + if (inet->cmsg_flags & IP_CMSG_TOS) { + int tos = inet->rcv_tos; + put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos); + } len -= msg.msg_controllen; return put_user(len, optlen); } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 4d6f81c818dc..94abee8cf563 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1463,6 +1463,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, ireq->opt = NULL; newinet->mc_index = inet_iif(skb); newinet->mc_ttl = ip_hdr(skb)->ttl; + newinet->rcv_tos = ip_hdr(skb)->tos; inet_csk(newsk)->icsk_ext_hdr_len = 0; if (inet_opt) inet_csk(newsk)->icsk_ext_hdr_len = inet_opt->opt.optlen; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 273f48d1df2e..5605f9dca87e 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -214,6 +214,7 @@ lookup_protocol: inet->mc_ttl = 1; inet->mc_index = 0; inet->mc_list = NULL; + inet->rcv_tos = 0; if (ipv4_config.no_pmtu_disc) inet->pmtudisc = IP_PMTUDISC_DONT; diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 6d6b65fdaa1a..63dd1f89ed7d 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -1017,6 +1017,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, int hlim = np->mcast_hops; put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim); } + if (np->rxopt.bits.rxtclass) { + int tclass = np->rcv_tclass; + put_cmsg(&msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass); + } if (np->rxopt.bits.rxoinfo) { struct in6_pktinfo src_info; src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif : diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index d16414cb3421..12c6ece67f39 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1282,6 +1282,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newnp->opt = NULL; newnp->mcast_oif = inet6_iif(skb); newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; + newnp->rcv_tclass = ipv6_tclass(ipv6_hdr(skb)); /* * No need to charge this sock to the relevant IPv6 refcnt debug socks count @@ -1360,6 +1361,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newnp->opt = NULL; newnp->mcast_oif = inet6_iif(skb); newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; + newnp->rcv_tclass = ipv6_tclass(ipv6_hdr(skb)); /* Clone native IPv6 options from listening socket (if any) @@ -1562,6 +1564,8 @@ ipv6_pktoptions: np->mcast_oif = inet6_iif(opt_skb); if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) np->mcast_hops = ipv6_hdr(opt_skb)->hop_limit; + if (np->rxopt.bits.rxtclass) + np->rcv_tclass = ipv6_tclass(ipv6_hdr(skb)); if (ipv6_opt_accepted(sk, opt_skb)) { skb_set_owner_r(opt_skb, sk); opt_skb = xchg(&np->pktoptions, opt_skb); -- cgit From 1a0d6ae5795c376bae6d012fb25e8341e4c6d5f2 Mon Sep 17 00:00:00 2001 From: Danny Kukawka Date: Thu, 9 Feb 2012 09:48:54 +0000 Subject: rename dev_hw_addr_random and remove redundant second Renamed dev_hw_addr_random to eth_hw_addr_random() to reflect that this function only assign a random ethernet address (MAC). Removed the second parameter (u8 *hwaddr), it's redundant since the also given net_device already contains net_device->dev_addr. Set it directly. Adapt igbvf and ixgbevf to the changed function. Small fix for ixgbevf_probe(): if ixgbevf_sw_init() fails (which means the device got no dev_addr) handle the error and jump to err_sw_init as already done by igbvf in similar case. Signed-off-by: Danny Kukawka Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/igbvf/netdev.c | 11 +++++---- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 28 ++++++++++++++--------- include/linux/etherdevice.h | 13 ++++++----- 3 files changed, 31 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 446297ff0104..92956e80fd1b 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -2712,18 +2712,19 @@ static int __devinit igbvf_probe(struct pci_dev *pdev, dev_info(&pdev->dev, "PF still in reset state, assigning new address." " Is the PF interface up?\n"); - dev_hw_addr_random(adapter->netdev, hw->mac.addr); + eth_hw_addr_random(netdev); + memcpy(adapter->hw.mac.addr, netdev->dev_addr, + netdev->addr_len); } else { err = hw->mac.ops.read_mac_addr(hw); if (err) { dev_err(&pdev->dev, "Error reading MAC address\n"); goto err_hw_init; } + memcpy(netdev->dev_addr, adapter->hw.mac.addr, + netdev->addr_len); } - memcpy(netdev->dev_addr, adapter->hw.mac.addr, netdev->addr_len); - memcpy(netdev->perm_addr, adapter->hw.mac.addr, netdev->addr_len); - if (!is_valid_ether_addr(netdev->perm_addr)) { dev_err(&pdev->dev, "Invalid MAC Address: %pM\n", netdev->dev_addr); @@ -2731,6 +2732,8 @@ static int __devinit igbvf_probe(struct pci_dev *pdev, goto err_hw_init; } + memcpy(netdev->perm_addr, adapter->hw.mac.addr, netdev->addr_len); + setup_timer(&adapter->watchdog_timer, &igbvf_watchdog, (unsigned long) adapter); diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 58c04b69ce70..e10221dcebb1 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -2199,13 +2199,17 @@ static int __devinit ixgbevf_sw_init(struct ixgbevf_adapter *adapter) if (err) { dev_info(&pdev->dev, "PF still in reset state, assigning new address\n"); - dev_hw_addr_random(adapter->netdev, hw->mac.addr); + eth_hw_addr_random(adapter->netdev); + memcpy(adapter->hw.mac.addr, adapter->netdev->dev_addr, + adapter->netdev->addr_len); } else { err = hw->mac.ops.init_hw(hw); if (err) { pr_err("init_shared_code failed: %d\n", err); goto out; } + memcpy(adapter->netdev->dev_addr, adapter->hw.mac.addr, + adapter->netdev->addr_len); } /* Enable dynamic interrupt throttling rates */ @@ -2224,6 +2228,7 @@ static int __devinit ixgbevf_sw_init(struct ixgbevf_adapter *adapter) adapter->flags |= IXGBE_FLAG_RX_CSUM_ENABLED; set_bit(__IXGBEVF_DOWN, &adapter->state); + return 0; out: return err; @@ -3394,6 +3399,17 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, /* setup the private structure */ err = ixgbevf_sw_init(adapter); + if (err) + goto err_sw_init; + + /* The HW MAC address was set and/or determined in sw_init */ + memcpy(netdev->perm_addr, adapter->hw.mac.addr, netdev->addr_len); + + if (!is_valid_ether_addr(netdev->dev_addr)) { + pr_err("invalid MAC address\n"); + err = -EIO; + goto err_sw_init; + } netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | @@ -3418,16 +3434,6 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev, netdev->priv_flags |= IFF_UNICAST_FLT; - /* The HW MAC address was set and/or determined in sw_init */ - memcpy(netdev->dev_addr, adapter->hw.mac.addr, netdev->addr_len); - memcpy(netdev->perm_addr, adapter->hw.mac.addr, netdev->addr_len); - - if (!is_valid_ether_addr(netdev->dev_addr)) { - pr_err("invalid MAC address\n"); - err = -EIO; - goto err_sw_init; - } - init_timer(&adapter->watchdog_timer); adapter->watchdog_timer.function = ixgbevf_watchdog; adapter->watchdog_timer.data = (unsigned long)adapter; diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index 05955cf09937..8a1835855faa 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -140,17 +140,18 @@ static inline void random_ether_addr(u8 *addr) } /** - * dev_hw_addr_random - Create random MAC and set device flag + * eth_hw_addr_random - Generate software assigned random Ethernet and + * set device flag * @dev: pointer to net_device structure - * @hwaddr: Pointer to a six-byte array containing the Ethernet address * - * Generate random MAC to be used by a device and set addr_assign_type - * so the state can be read by sysfs and be used by udev. + * Generate a random Ethernet address (MAC) to be used by a net device + * and set addr_assign_type so the state can be read by sysfs and be + * used by userspace. */ -static inline void dev_hw_addr_random(struct net_device *dev, u8 *hwaddr) +static inline void eth_hw_addr_random(struct net_device *dev) { dev->addr_assign_type |= NET_ADDR_RANDOM; - random_ether_addr(hwaddr); + random_ether_addr(dev->dev_addr); } /** -- cgit From 8675381109b0eb1c948a423c2b35e3f4509cb25e Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 13 Feb 2012 13:24:02 +0200 Subject: usb: otg: Rename otg_transceiver to usb_phy This is the first step in separating USB transceivers from USB OTG utilities. Includes fixes to IMX code from Sascha Hauer. Signed-off-by: Heikki Krogerus Acked-by: Sascha Hauer Acked-by: Pavankumar Kondeti Acked-by: Li Yang Acked-by: Alan Stern Acked-by: Igor Grinberg Reviewed-by: Marek Vasut Signed-off-by: Felipe Balbi --- arch/arm/mach-imx/mx31moboard-devboard.c | 6 +- arch/arm/mach-imx/mx31moboard-marxbot.c | 6 +- arch/arm/mach-pxa/pxa3xx-ulpi.c | 6 +- arch/arm/mach-tegra/include/mach/usb_phy.h | 2 +- arch/arm/plat-mxc/include/mach/mxc_ehci.h | 2 +- arch/arm/plat-mxc/include/mach/ulpi.h | 4 +- arch/arm/plat-mxc/ulpi.c | 6 +- drivers/power/isp1704_charger.c | 2 +- drivers/power/pda_power.c | 2 +- drivers/power/twl4030_charger.c | 2 +- drivers/usb/gadget/ci13xxx_udc.h | 2 +- drivers/usb/gadget/fsl_usb2_udc.h | 2 +- drivers/usb/gadget/langwell_udc.h | 2 +- drivers/usb/gadget/mv_udc.h | 2 +- drivers/usb/gadget/omap_udc.c | 4 +- drivers/usb/gadget/omap_udc.h | 2 +- drivers/usb/gadget/pxa25x_udc.h | 2 +- drivers/usb/gadget/pxa27x_udc.h | 2 +- drivers/usb/gadget/s3c-hsudc.c | 2 +- drivers/usb/host/ehci-msm.c | 2 +- drivers/usb/host/ehci-mv.c | 2 +- drivers/usb/host/ehci-tegra.c | 2 +- drivers/usb/host/ehci.h | 2 +- drivers/usb/host/ohci.h | 2 +- drivers/usb/musb/blackfin.c | 2 +- drivers/usb/musb/musb_core.c | 4 +- drivers/usb/musb/musb_core.h | 2 +- drivers/usb/musb/tusb6010.c | 2 +- drivers/usb/otg/Kconfig | 2 +- drivers/usb/otg/ab8500-usb.c | 12 ++-- drivers/usb/otg/fsl_otg.c | 18 +++--- drivers/usb/otg/fsl_otg.h | 2 +- drivers/usb/otg/gpio_vbus.c | 8 +-- drivers/usb/otg/isp1301_omap.c | 12 ++-- drivers/usb/otg/msm_otg.c | 54 ++++++++-------- drivers/usb/otg/mv_otg.c | 14 ++--- drivers/usb/otg/mv_otg.h | 2 +- drivers/usb/otg/nop-usb-xceiv.c | 10 +-- drivers/usb/otg/otg.c | 8 +-- drivers/usb/otg/otg_fsm.h | 2 +- drivers/usb/otg/twl4030-usb.c | 8 +-- drivers/usb/otg/twl6030-usb.c | 18 +++--- drivers/usb/otg/ulpi.c | 20 +++--- drivers/usb/otg/ulpi_viewport.c | 4 +- include/linux/usb/intel_mid_otg.h | 6 +- include/linux/usb/msm_hsusb.h | 2 +- include/linux/usb/otg.h | 98 +++++++++++++++--------------- include/linux/usb/ulpi.h | 2 +- 48 files changed, 190 insertions(+), 190 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-imx/mx31moboard-devboard.c b/arch/arm/mach-imx/mx31moboard-devboard.c index 0aa25364360d..9cd4a97efa52 100644 --- a/arch/arm/mach-imx/mx31moboard-devboard.c +++ b/arch/arm/mach-imx/mx31moboard-devboard.c @@ -158,7 +158,7 @@ static int devboard_usbh1_hw_init(struct platform_device *pdev) #define USBH1_VBUSEN_B IOMUX_TO_GPIO(MX31_PIN_NFRE_B) #define USBH1_MODE IOMUX_TO_GPIO(MX31_PIN_NFALE) -static int devboard_isp1105_init(struct otg_transceiver *otg) +static int devboard_isp1105_init(struct usb_phy *otg) { int ret = gpio_request(USBH1_MODE, "usbh1-mode"); if (ret) @@ -177,7 +177,7 @@ static int devboard_isp1105_init(struct otg_transceiver *otg) } -static int devboard_isp1105_set_vbus(struct otg_transceiver *otg, bool on) +static int devboard_isp1105_set_vbus(struct usb_phy *otg, bool on) { if (on) gpio_set_value(USBH1_VBUSEN_B, 0); @@ -194,7 +194,7 @@ static struct mxc_usbh_platform_data usbh1_pdata __initdata = { static int __init devboard_usbh1_init(void) { - struct otg_transceiver *otg; + struct usb_phy *otg; struct platform_device *pdev; otg = kzalloc(sizeof(*otg), GFP_KERNEL); diff --git a/arch/arm/mach-imx/mx31moboard-marxbot.c b/arch/arm/mach-imx/mx31moboard-marxbot.c index bb639cbda4e5..2be769bbe05e 100644 --- a/arch/arm/mach-imx/mx31moboard-marxbot.c +++ b/arch/arm/mach-imx/mx31moboard-marxbot.c @@ -272,7 +272,7 @@ static int marxbot_usbh1_hw_init(struct platform_device *pdev) #define USBH1_VBUSEN_B IOMUX_TO_GPIO(MX31_PIN_NFRE_B) #define USBH1_MODE IOMUX_TO_GPIO(MX31_PIN_NFALE) -static int marxbot_isp1105_init(struct otg_transceiver *otg) +static int marxbot_isp1105_init(struct usb_phy *otg) { int ret = gpio_request(USBH1_MODE, "usbh1-mode"); if (ret) @@ -291,7 +291,7 @@ static int marxbot_isp1105_init(struct otg_transceiver *otg) } -static int marxbot_isp1105_set_vbus(struct otg_transceiver *otg, bool on) +static int marxbot_isp1105_set_vbus(struct usb_phy *otg, bool on) { if (on) gpio_set_value(USBH1_VBUSEN_B, 0); @@ -308,7 +308,7 @@ static struct mxc_usbh_platform_data usbh1_pdata __initdata = { static int __init marxbot_usbh1_init(void) { - struct otg_transceiver *otg; + struct usb_phy *otg; struct platform_device *pdev; otg = kzalloc(sizeof(*otg), GFP_KERNEL); diff --git a/arch/arm/mach-pxa/pxa3xx-ulpi.c b/arch/arm/mach-pxa/pxa3xx-ulpi.c index e28dfb88827f..960d0ac50418 100644 --- a/arch/arm/mach-pxa/pxa3xx-ulpi.c +++ b/arch/arm/mach-pxa/pxa3xx-ulpi.c @@ -33,7 +33,7 @@ struct pxa3xx_u2d_ulpi { struct clk *clk; void __iomem *mmio_base; - struct otg_transceiver *otg; + struct usb_phy *otg; unsigned int ulpi_mode; }; @@ -79,7 +79,7 @@ static int pxa310_ulpi_poll(void) return -ETIMEDOUT; } -static int pxa310_ulpi_read(struct otg_transceiver *otg, u32 reg) +static int pxa310_ulpi_read(struct usb_phy *otg, u32 reg) { int err; @@ -98,7 +98,7 @@ static int pxa310_ulpi_read(struct otg_transceiver *otg, u32 reg) return u2d_readl(U2DOTGUCR) & U2DOTGUCR_RDATA; } -static int pxa310_ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg) +static int pxa310_ulpi_write(struct usb_phy *otg, u32 val, u32 reg) { if (pxa310_ulpi_get_phymode() != SYNCH) { pr_warning("%s: PHY is not in SYNCH mode!\n", __func__); diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h index d4b8f9e298a8..de1a0f602b28 100644 --- a/arch/arm/mach-tegra/include/mach/usb_phy.h +++ b/arch/arm/mach-tegra/include/mach/usb_phy.h @@ -58,7 +58,7 @@ struct tegra_usb_phy { struct clk *pad_clk; enum tegra_usb_phy_mode mode; void *config; - struct otg_transceiver *ulpi; + struct usb_phy *ulpi; }; struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, diff --git a/arch/arm/plat-mxc/include/mach/mxc_ehci.h b/arch/arm/plat-mxc/include/mach/mxc_ehci.h index 2c159dc2398b..9ffd1bbe615f 100644 --- a/arch/arm/plat-mxc/include/mach/mxc_ehci.h +++ b/arch/arm/plat-mxc/include/mach/mxc_ehci.h @@ -44,7 +44,7 @@ struct mxc_usbh_platform_data { int (*exit)(struct platform_device *pdev); unsigned int portsc; - struct otg_transceiver *otg; + struct usb_phy *otg; }; int mx51_initialize_usb_hw(int port, unsigned int flags); diff --git a/arch/arm/plat-mxc/include/mach/ulpi.h b/arch/arm/plat-mxc/include/mach/ulpi.h index f9161c96d7bd..d39d94a170e7 100644 --- a/arch/arm/plat-mxc/include/mach/ulpi.h +++ b/arch/arm/plat-mxc/include/mach/ulpi.h @@ -2,9 +2,9 @@ #define __MACH_ULPI_H #ifdef CONFIG_USB_ULPI -struct otg_transceiver *imx_otg_ulpi_create(unsigned int flags); +struct usb_phy *imx_otg_ulpi_create(unsigned int flags); #else -static inline struct otg_transceiver *imx_otg_ulpi_create(unsigned int flags) +static inline struct usb_phy *imx_otg_ulpi_create(unsigned int flags) { return NULL; } diff --git a/arch/arm/plat-mxc/ulpi.c b/arch/arm/plat-mxc/ulpi.c index 477e45bea1be..8eeeb6b979c4 100644 --- a/arch/arm/plat-mxc/ulpi.c +++ b/arch/arm/plat-mxc/ulpi.c @@ -58,7 +58,7 @@ static int ulpi_poll(void __iomem *view, u32 bit) return -ETIMEDOUT; } -static int ulpi_read(struct otg_transceiver *otg, u32 reg) +static int ulpi_read(struct usb_phy *otg, u32 reg) { int ret; void __iomem *view = otg->io_priv; @@ -84,7 +84,7 @@ static int ulpi_read(struct otg_transceiver *otg, u32 reg) return (__raw_readl(view) >> ULPIVW_RDATA_SHIFT) & ULPIVW_RDATA_MASK; } -static int ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg) +static int ulpi_write(struct usb_phy *otg, u32 val, u32 reg) { int ret; void __iomem *view = otg->io_priv; @@ -112,7 +112,7 @@ struct otg_io_access_ops mxc_ulpi_access_ops = { }; EXPORT_SYMBOL_GPL(mxc_ulpi_access_ops); -struct otg_transceiver *imx_otg_ulpi_create(unsigned int flags) +struct usb_phy *imx_otg_ulpi_create(unsigned int flags) { return otg_ulpi_create(&mxc_ulpi_access_ops, flags); } diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index b806667b59ae..51cdea4fc034 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -56,7 +56,7 @@ static u16 isp170x_id[] = { struct isp1704_charger { struct device *dev; struct power_supply psy; - struct otg_transceiver *otg; + struct usb_phy *otg; struct notifier_block nb; struct work_struct work; diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index fd49689738af..dcf07f55eb04 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -40,7 +40,7 @@ static struct timer_list polling_timer; static int polling; #ifdef CONFIG_USB_OTG_UTILS -static struct otg_transceiver *transceiver; +static struct usb_phy *transceiver; static struct notifier_block otg_nb; #endif diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 54b9198fa576..b3ead2305b4c 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -69,7 +69,7 @@ struct twl4030_bci { struct device *dev; struct power_supply ac; struct power_supply usb; - struct otg_transceiver *transceiver; + struct usb_phy *transceiver; struct notifier_block otg_nb; struct work_struct work; int irq_chg; diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h index f4871e1fac59..0d31af56c989 100644 --- a/drivers/usb/gadget/ci13xxx_udc.h +++ b/drivers/usb/gadget/ci13xxx_udc.h @@ -136,7 +136,7 @@ struct ci13xxx { struct usb_gadget_driver *driver; /* 3rd party gadget driver */ struct ci13xxx_udc_driver *udc_driver; /* device controller driver */ int vbus_active; /* is VBUS active */ - struct otg_transceiver *transceiver; /* Transceiver struct */ + struct usb_phy *transceiver; /* Transceiver struct */ }; /****************************************************************************** diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index f781f5dec417..e651469fd39b 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -471,7 +471,7 @@ struct fsl_udc { struct usb_ctrlrequest local_setup_buff; spinlock_t lock; - struct otg_transceiver *transceiver; + struct usb_phy *transceiver; unsigned softconnect:1; unsigned vbus_active:1; unsigned stopped:1; diff --git a/drivers/usb/gadget/langwell_udc.h b/drivers/usb/gadget/langwell_udc.h index d6e78accaffe..8c8087abb481 100644 --- a/drivers/usb/gadget/langwell_udc.h +++ b/drivers/usb/gadget/langwell_udc.h @@ -162,7 +162,7 @@ struct langwell_udc { spinlock_t lock; /* device lock */ struct langwell_ep *ep; struct usb_gadget_driver *driver; - struct otg_transceiver *transceiver; + struct usb_phy *transceiver; u8 dev_addr; u32 usb_state; u32 resume_state; diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/mv_udc.h index 34aadfae723d..e2be9519abbe 100644 --- a/drivers/usb/gadget/mv_udc.h +++ b/drivers/usb/gadget/mv_udc.h @@ -217,7 +217,7 @@ struct mv_udc { struct work_struct vbus_work; struct workqueue_struct *qwork; - struct otg_transceiver *transceiver; + struct usb_phy *transceiver; struct mv_usb_platform_data *pdata; diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 576cd8578b45..d3529787351d 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2650,7 +2650,7 @@ static void omap_udc_release(struct device *dev) } static int __init -omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) +omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv) { unsigned tmp, buf; @@ -2790,7 +2790,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) { int status = -ENODEV; int hmc; - struct otg_transceiver *xceiv = NULL; + struct usb_phy *xceiv = NULL; const char *type = NULL; struct omap_usb_config *config = pdev->dev.platform_data; struct clk *dc_clk; diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h index 29edc51b6b22..59d3b2213cb1 100644 --- a/drivers/usb/gadget/omap_udc.h +++ b/drivers/usb/gadget/omap_udc.h @@ -164,7 +164,7 @@ struct omap_udc { struct omap_ep ep[32]; u16 devstat; u16 clr_halt; - struct otg_transceiver *transceiver; + struct usb_phy *transceiver; struct list_head iso; unsigned softconnect:1; unsigned vbus_active:1; diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h index 8eaf4e43726b..893e917f048e 100644 --- a/drivers/usb/gadget/pxa25x_udc.h +++ b/drivers/usb/gadget/pxa25x_udc.h @@ -119,7 +119,7 @@ struct pxa25x_udc { struct device *dev; struct clk *clk; struct pxa2xx_udc_mach_info *mach; - struct otg_transceiver *transceiver; + struct usb_phy *transceiver; u64 dma_mask; struct pxa25x_ep ep [PXA_UDC_NUM_ENDPOINTS]; diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h index 7f4e8f424e80..a1d268c6f2cc 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/pxa27x_udc.h @@ -447,7 +447,7 @@ struct pxa_udc { struct usb_gadget_driver *driver; struct device *dev; struct pxa2xx_udc_mach_info *mach; - struct otg_transceiver *transceiver; + struct usb_phy *transceiver; enum ep0_state ep0state; struct udc_stats stats; diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index df8661d266cb..dea475a61fd3 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -145,7 +145,7 @@ struct s3c_hsudc { struct usb_gadget_driver *driver; struct device *dev; struct s3c24xx_hsudc_platdata *pd; - struct otg_transceiver *transceiver; + struct usb_phy *transceiver; struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsudc_supply_names)]; spinlock_t lock; void __iomem *regs; diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 592d5f76803e..ef7c4319c6e4 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -32,7 +32,7 @@ #define MSM_USB_BASE (hcd->regs) -static struct otg_transceiver *otg; +static struct usb_phy *otg; static int ehci_msm_reset(struct usb_hcd *hcd) { diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 52a604fb9321..39ca79e008d9 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -28,7 +28,7 @@ struct ehci_hcd_mv { void __iomem *cap_regs; void __iomem *op_regs; - struct otg_transceiver *otg; + struct usb_phy *otg; struct mv_usb_platform_data *pdata; diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index dbc7fe8ca9e7..2e157144dcf6 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -35,7 +35,7 @@ struct tegra_ehci_hcd { struct tegra_usb_phy *phy; struct clk *clk; struct clk *emc_clk; - struct otg_transceiver *transceiver; + struct usb_phy *transceiver; int host_resumed; int bus_suspended; int port_resuming; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 0a5fda73b3f2..8f9acbc96fde 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -176,7 +176,7 @@ struct ehci_hcd { /* one per controller */ /* * OTG controllers and transceivers need software interaction */ - struct otg_transceiver *transceiver; + struct usb_phy *transceiver; }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 8ff6f7ea96fd..1b19aea25a2b 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -376,7 +376,7 @@ struct ohci_hcd { * OTG controllers and transceivers need software interaction; * other external transceivers should be software-transparent */ - struct otg_transceiver *transceiver; + struct usb_phy *transceiver; void (*start_hnp)(struct ohci_hcd *ohci); /* diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 5e7cfba5b079..fc8e9edbcb82 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -317,7 +317,7 @@ static void bfin_musb_set_vbus(struct musb *musb, int is_on) musb_readb(musb->mregs, MUSB_DEVCTL)); } -static int bfin_musb_set_power(struct otg_transceiver *x, unsigned mA) +static int bfin_musb_set_power(struct usb_phy *x, unsigned mA) { return 0; } diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 3d11cf64ebd1..7b7ecf6c65b7 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -131,7 +131,7 @@ static inline struct musb *dev_to_musb(struct device *dev) /*-------------------------------------------------------------------------*/ #ifndef CONFIG_BLACKFIN -static int musb_ulpi_read(struct otg_transceiver *otg, u32 offset) +static int musb_ulpi_read(struct usb_phy *otg, u32 offset) { void __iomem *addr = otg->io_priv; int i = 0; @@ -165,7 +165,7 @@ static int musb_ulpi_read(struct otg_transceiver *otg, u32 offset) return musb_readb(addr, MUSB_ULPI_REG_DATA); } -static int musb_ulpi_write(struct otg_transceiver *otg, +static int musb_ulpi_write(struct usb_phy *otg, u32 offset, u32 data) { void __iomem *addr = otg->io_priv; diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 3d28fb8a2dc9..93de517a32a0 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -372,7 +372,7 @@ struct musb { u16 int_rx; u16 int_tx; - struct otg_transceiver *xceiv; + struct usb_phy *xceiv; u8 xceiv_event; int nIrq; diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 1f405616e6cd..5ce01bdb1951 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -277,7 +277,7 @@ static struct musb *the_musb; * mode), or low power Default-B sessions, something else supplies power. * Caller must take care of locking. */ -static int tusb_draw_power(struct otg_transceiver *x, unsigned mA) +static int tusb_draw_power(struct usb_phy *x, unsigned mA) { struct musb *musb = the_musb; void __iomem *tbase = musb->ctrl_base; diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 76d629345418..b353b393448a 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -23,7 +23,7 @@ config USB_GPIO_VBUS select USB_OTG_UTILS help Provides simple GPIO VBUS sensing for controllers with an - internal transceiver via the otg_transceiver interface, and + internal transceiver via the usb_phy interface, and optionally control of a D+ pullup GPIO as well as a VBUS current limit regulator. diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index 74fe6e62e0f7..5f06dda7db61 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -68,7 +68,7 @@ enum ab8500_usb_link_status { }; struct ab8500_usb { - struct otg_transceiver otg; + struct usb_phy otg; struct device *dev; int irq_num_id_rise; int irq_num_id_fall; @@ -82,7 +82,7 @@ struct ab8500_usb { int rev; }; -static inline struct ab8500_usb *xceiv_to_ab(struct otg_transceiver *x) +static inline struct ab8500_usb *xceiv_to_ab(struct usb_phy *x) { return container_of(x, struct ab8500_usb, otg); } @@ -269,7 +269,7 @@ static void ab8500_usb_phy_disable_work(struct work_struct *work) ab8500_usb_peri_phy_dis(ab); } -static int ab8500_usb_set_power(struct otg_transceiver *otg, unsigned mA) +static int ab8500_usb_set_power(struct usb_phy *otg, unsigned mA) { struct ab8500_usb *ab; @@ -290,13 +290,13 @@ static int ab8500_usb_set_power(struct otg_transceiver *otg, unsigned mA) * ab->vbus_draw. */ -static int ab8500_usb_set_suspend(struct otg_transceiver *x, int suspend) +static int ab8500_usb_set_suspend(struct usb_phy *x, int suspend) { /* TODO */ return 0; } -static int ab8500_usb_set_peripheral(struct otg_transceiver *otg, +static int ab8500_usb_set_peripheral(struct usb_phy *otg, struct usb_gadget *gadget) { struct ab8500_usb *ab; @@ -329,7 +329,7 @@ static int ab8500_usb_set_peripheral(struct otg_transceiver *otg, return 0; } -static int ab8500_usb_set_host(struct otg_transceiver *otg, +static int ab8500_usb_set_host(struct usb_phy *otg, struct usb_bus *host) { struct ab8500_usb *ab; diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c index a190850d2d3b..66db2a117444 100644 --- a/drivers/usb/otg/fsl_otg.c +++ b/drivers/usb/otg/fsl_otg.c @@ -452,7 +452,7 @@ void otg_reset_controller(void) /* Call suspend/resume routines in host driver */ int fsl_otg_start_host(struct otg_fsm *fsm, int on) { - struct otg_transceiver *xceiv = fsm->transceiver; + struct usb_phy *xceiv = fsm->transceiver; struct device *dev; struct fsl_otg *otg_dev = container_of(xceiv, struct fsl_otg, otg); u32 retval = 0; @@ -518,7 +518,7 @@ end: */ int fsl_otg_start_gadget(struct otg_fsm *fsm, int on) { - struct otg_transceiver *xceiv = fsm->transceiver; + struct usb_phy *xceiv = fsm->transceiver; struct device *dev; if (!xceiv->gadget || !xceiv->gadget->dev.parent) @@ -542,7 +542,7 @@ int fsl_otg_start_gadget(struct otg_fsm *fsm, int on) * Called by initialization code of host driver. Register host controller * to the OTG. Suspend host for OTG role detection. */ -static int fsl_otg_set_host(struct otg_transceiver *otg_p, struct usb_bus *host) +static int fsl_otg_set_host(struct usb_phy *otg_p, struct usb_bus *host) { struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); @@ -587,7 +587,7 @@ static int fsl_otg_set_host(struct otg_transceiver *otg_p, struct usb_bus *host) } /* Called by initialization code of udc. Register udc to OTG. */ -static int fsl_otg_set_peripheral(struct otg_transceiver *otg_p, +static int fsl_otg_set_peripheral(struct usb_phy *otg_p, struct usb_gadget *gadget) { struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); @@ -625,7 +625,7 @@ static int fsl_otg_set_peripheral(struct otg_transceiver *otg_p, } /* Set OTG port power, only for B-device */ -static int fsl_otg_set_power(struct otg_transceiver *otg_p, unsigned mA) +static int fsl_otg_set_power(struct usb_phy *otg_p, unsigned mA) { if (!fsl_otg_dev) return -ENODEV; @@ -658,7 +658,7 @@ static void fsl_otg_event(struct work_struct *work) } /* B-device start SRP */ -static int fsl_otg_start_srp(struct otg_transceiver *otg_p) +static int fsl_otg_start_srp(struct usb_phy *otg_p) { struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); @@ -673,7 +673,7 @@ static int fsl_otg_start_srp(struct otg_transceiver *otg_p) } /* A_host suspend will call this function to start hnp */ -static int fsl_otg_start_hnp(struct otg_transceiver *otg_p) +static int fsl_otg_start_hnp(struct usb_phy *otg_p) { struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); @@ -698,7 +698,7 @@ static int fsl_otg_start_hnp(struct otg_transceiver *otg_p) irqreturn_t fsl_otg_isr(int irq, void *dev_id) { struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm; - struct otg_transceiver *otg = &((struct fsl_otg *)dev_id)->otg; + struct usb_phy *otg = &((struct fsl_otg *)dev_id)->otg; u32 otg_int_src, otg_sc; otg_sc = fsl_readl(&usb_dr_regs->otgsc); @@ -815,7 +815,7 @@ err: int usb_otg_start(struct platform_device *pdev) { struct fsl_otg *p_otg; - struct otg_transceiver *otg_trans = otg_get_transceiver(); + struct usb_phy *otg_trans = otg_get_transceiver(); struct otg_fsm *fsm; int status; struct resource *res; diff --git a/drivers/usb/otg/fsl_otg.h b/drivers/usb/otg/fsl_otg.h index 3f8ef731aac9..caec254ad819 100644 --- a/drivers/usb/otg/fsl_otg.h +++ b/drivers/usb/otg/fsl_otg.h @@ -369,7 +369,7 @@ inline struct fsl_otg_timer *otg_timer_initializer } struct fsl_otg { - struct otg_transceiver otg; + struct usb_phy otg; struct otg_fsm fsm; struct usb_dr_mmap *dr_mem_map; struct delayed_work otg_event; diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c index fb644c107ded..4e1b1384dc89 100644 --- a/drivers/usb/otg/gpio_vbus.c +++ b/drivers/usb/otg/gpio_vbus.c @@ -32,7 +32,7 @@ * Needs to be loaded before the UDC driver that will use it. */ struct gpio_vbus_data { - struct otg_transceiver otg; + struct usb_phy otg; struct device *dev; struct regulator *vbus_draw; int vbus_draw_enabled; @@ -149,7 +149,7 @@ static irqreturn_t gpio_vbus_irq(int irq, void *data) /* OTG transceiver interface */ /* bind/unbind the peripheral controller */ -static int gpio_vbus_set_peripheral(struct otg_transceiver *otg, +static int gpio_vbus_set_peripheral(struct usb_phy *otg, struct usb_gadget *gadget) { struct gpio_vbus_data *gpio_vbus; @@ -189,7 +189,7 @@ static int gpio_vbus_set_peripheral(struct otg_transceiver *otg, } /* effective for B devices, ignored for A-peripheral */ -static int gpio_vbus_set_power(struct otg_transceiver *otg, unsigned mA) +static int gpio_vbus_set_power(struct usb_phy *otg, unsigned mA) { struct gpio_vbus_data *gpio_vbus; @@ -201,7 +201,7 @@ static int gpio_vbus_set_power(struct otg_transceiver *otg, unsigned mA) } /* for non-OTG B devices: set/clear transceiver suspend mode */ -static int gpio_vbus_set_suspend(struct otg_transceiver *otg, int suspend) +static int gpio_vbus_set_suspend(struct usb_phy *otg, int suspend) { struct gpio_vbus_data *gpio_vbus; diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index 8c86787c2f09..1bc27948ac22 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -52,7 +52,7 @@ MODULE_DESCRIPTION("ISP1301 USB OTG Transceiver Driver"); MODULE_LICENSE("GPL"); struct isp1301 { - struct otg_transceiver otg; + struct usb_phy otg; struct i2c_client *client; void (*i2c_release)(struct device *dev); @@ -1274,7 +1274,7 @@ static int isp1301_otg_enable(struct isp1301 *isp) /* add or disable the host device+driver */ static int -isp1301_set_host(struct otg_transceiver *otg, struct usb_bus *host) +isp1301_set_host(struct usb_phy *otg, struct usb_bus *host) { struct isp1301 *isp = container_of(otg, struct isp1301, otg); @@ -1330,7 +1330,7 @@ isp1301_set_host(struct otg_transceiver *otg, struct usb_bus *host) } static int -isp1301_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget) +isp1301_set_peripheral(struct usb_phy *otg, struct usb_gadget *gadget) { struct isp1301 *isp = container_of(otg, struct isp1301, otg); #ifndef CONFIG_USB_OTG @@ -1399,7 +1399,7 @@ isp1301_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget) /*-------------------------------------------------------------------------*/ static int -isp1301_set_power(struct otg_transceiver *dev, unsigned mA) +isp1301_set_power(struct usb_phy *dev, unsigned mA) { if (!the_transceiver) return -ENODEV; @@ -1409,7 +1409,7 @@ isp1301_set_power(struct otg_transceiver *dev, unsigned mA) } static int -isp1301_start_srp(struct otg_transceiver *dev) +isp1301_start_srp(struct usb_phy *dev) { struct isp1301 *isp = container_of(dev, struct isp1301, otg); u32 otg_ctrl; @@ -1436,7 +1436,7 @@ isp1301_start_srp(struct otg_transceiver *dev) } static int -isp1301_start_hnp(struct otg_transceiver *dev) +isp1301_start_hnp(struct usb_phy *dev) { #ifdef CONFIG_USB_OTG struct isp1301 *isp = container_of(dev, struct isp1301, otg); diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index b276f8fcdeba..cba4737a04f4 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -235,7 +235,7 @@ static int msm_hsusb_ldo_set_mode(int on) return ret < 0 ? ret : 0; } -static int ulpi_read(struct otg_transceiver *otg, u32 reg) +static int ulpi_read(struct usb_phy *otg, u32 reg) { struct msm_otg *motg = container_of(otg, struct msm_otg, otg); int cnt = 0; @@ -260,7 +260,7 @@ static int ulpi_read(struct otg_transceiver *otg, u32 reg) return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); } -static int ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg) +static int ulpi_write(struct usb_phy *otg, u32 val, u32 reg) { struct msm_otg *motg = container_of(otg, struct msm_otg, otg); int cnt = 0; @@ -390,7 +390,7 @@ static int msm_otg_phy_reset(struct msm_otg *motg) } #define LINK_RESET_TIMEOUT_USEC (250 * 1000) -static int msm_otg_reset(struct otg_transceiver *otg) +static int msm_otg_reset(struct usb_phy *otg) { struct msm_otg *motg = container_of(otg, struct msm_otg, otg); struct msm_otg_platform_data *pdata = motg->pdata; @@ -448,7 +448,7 @@ static int msm_otg_reset(struct otg_transceiver *otg) #ifdef CONFIG_PM_SLEEP static int msm_otg_suspend(struct msm_otg *motg) { - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; struct usb_bus *bus = otg->host; struct msm_otg_platform_data *pdata = motg->pdata; int cnt = 0; @@ -543,7 +543,7 @@ static int msm_otg_suspend(struct msm_otg *motg) static int msm_otg_resume(struct msm_otg *motg) { - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; struct usb_bus *bus = otg->host; int cnt = 0; unsigned temp; @@ -627,7 +627,7 @@ static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA) motg->cur_power = mA; } -static int msm_otg_set_power(struct otg_transceiver *otg, unsigned mA) +static int msm_otg_set_power(struct usb_phy *otg, unsigned mA) { struct msm_otg *motg = container_of(otg, struct msm_otg, otg); @@ -644,7 +644,7 @@ static int msm_otg_set_power(struct otg_transceiver *otg, unsigned mA) return 0; } -static void msm_otg_start_host(struct otg_transceiver *otg, int on) +static void msm_otg_start_host(struct usb_phy *otg, int on) { struct msm_otg *motg = container_of(otg, struct msm_otg, otg); struct msm_otg_platform_data *pdata = motg->pdata; @@ -683,7 +683,7 @@ static void msm_otg_start_host(struct otg_transceiver *otg, int on) } } -static int msm_otg_set_host(struct otg_transceiver *otg, struct usb_bus *host) +static int msm_otg_set_host(struct usb_phy *otg, struct usb_bus *host) { struct msm_otg *motg = container_of(otg, struct msm_otg, otg); struct usb_hcd *hcd; @@ -729,7 +729,7 @@ static int msm_otg_set_host(struct otg_transceiver *otg, struct usb_bus *host) return 0; } -static void msm_otg_start_peripheral(struct otg_transceiver *otg, int on) +static void msm_otg_start_peripheral(struct usb_phy *otg, int on) { struct msm_otg *motg = container_of(otg, struct msm_otg, otg); struct msm_otg_platform_data *pdata = motg->pdata; @@ -756,7 +756,7 @@ static void msm_otg_start_peripheral(struct otg_transceiver *otg, int on) } -static int msm_otg_set_peripheral(struct otg_transceiver *otg, +static int msm_otg_set_peripheral(struct usb_phy *otg, struct usb_gadget *gadget) { struct msm_otg *motg = container_of(otg, struct msm_otg, otg); @@ -800,7 +800,7 @@ static int msm_otg_set_peripheral(struct otg_transceiver *otg, static bool msm_chg_check_secondary_det(struct msm_otg *motg) { - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; u32 chg_det; bool ret = false; @@ -821,7 +821,7 @@ static bool msm_chg_check_secondary_det(struct msm_otg *motg) static void msm_chg_enable_secondary_det(struct msm_otg *motg) { - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; u32 chg_det; switch (motg->pdata->phy_type) { @@ -861,7 +861,7 @@ static void msm_chg_enable_secondary_det(struct msm_otg *motg) static bool msm_chg_check_primary_det(struct msm_otg *motg) { - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; u32 chg_det; bool ret = false; @@ -882,7 +882,7 @@ static bool msm_chg_check_primary_det(struct msm_otg *motg) static void msm_chg_enable_primary_det(struct msm_otg *motg) { - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; u32 chg_det; switch (motg->pdata->phy_type) { @@ -907,7 +907,7 @@ static void msm_chg_enable_primary_det(struct msm_otg *motg) static bool msm_chg_check_dcd(struct msm_otg *motg) { - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; u32 line_state; bool ret = false; @@ -928,7 +928,7 @@ static bool msm_chg_check_dcd(struct msm_otg *motg) static void msm_chg_disable_dcd(struct msm_otg *motg) { - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; u32 chg_det; switch (motg->pdata->phy_type) { @@ -947,7 +947,7 @@ static void msm_chg_disable_dcd(struct msm_otg *motg) static void msm_chg_enable_dcd(struct msm_otg *motg) { - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; u32 chg_det; switch (motg->pdata->phy_type) { @@ -968,7 +968,7 @@ static void msm_chg_enable_dcd(struct msm_otg *motg) static void msm_chg_block_on(struct msm_otg *motg) { - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; u32 func_ctrl, chg_det; /* put the controller in non-driving mode */ @@ -1003,7 +1003,7 @@ static void msm_chg_block_on(struct msm_otg *motg) static void msm_chg_block_off(struct msm_otg *motg) { - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; u32 func_ctrl, chg_det; switch (motg->pdata->phy_type) { @@ -1038,7 +1038,7 @@ static void msm_chg_block_off(struct msm_otg *motg) static void msm_chg_detect_work(struct work_struct *w) { struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work); - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; bool is_dcd, tmout, vout; unsigned long delay; @@ -1152,7 +1152,7 @@ static void msm_otg_init_sm(struct msm_otg *motg) static void msm_otg_sm_work(struct work_struct *w) { struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; switch (otg->state) { case OTG_STATE_UNDEFINED: @@ -1243,7 +1243,7 @@ static void msm_otg_sm_work(struct work_struct *w) static irqreturn_t msm_otg_irq(int irq, void *data) { struct msm_otg *motg = data; - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; u32 otgsc = 0; if (atomic_read(&motg->in_lpm)) { @@ -1281,7 +1281,7 @@ static irqreturn_t msm_otg_irq(int irq, void *data) static int msm_otg_mode_show(struct seq_file *s, void *unused) { struct msm_otg *motg = s->private; - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; switch (otg->state) { case OTG_STATE_A_HOST: @@ -1309,7 +1309,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, struct seq_file *s = file->private_data; struct msm_otg *motg = s->private; char buf[16]; - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; int status = count; enum usb_mode_type req_mode; @@ -1414,7 +1414,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) int ret = 0; struct resource *res; struct msm_otg *motg; - struct otg_transceiver *otg; + struct usb_phy *otg; dev_info(&pdev->dev, "msm_otg probe\n"); if (!pdev->dev.platform_data) { @@ -1598,7 +1598,7 @@ free_motg: static int __devexit msm_otg_remove(struct platform_device *pdev) { struct msm_otg *motg = platform_get_drvdata(pdev); - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; int cnt = 0; if (otg->host || otg->gadget) @@ -1660,7 +1660,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) static int msm_otg_runtime_idle(struct device *dev) { struct msm_otg *motg = dev_get_drvdata(dev); - struct otg_transceiver *otg = &motg->otg; + struct usb_phy *otg = &motg->otg; dev_dbg(dev, "OTG runtime idle\n"); diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c index b5fbe1452ab0..6e05d58effe1 100644 --- a/drivers/usb/otg/mv_otg.c +++ b/drivers/usb/otg/mv_otg.c @@ -55,7 +55,7 @@ static char *state_string[] = { "a_vbus_err" }; -static int mv_otg_set_vbus(struct otg_transceiver *otg, bool on) +static int mv_otg_set_vbus(struct usb_phy *otg, bool on) { struct mv_otg *mvotg = container_of(otg, struct mv_otg, otg); if (mvotg->pdata->set_vbus == NULL) @@ -64,7 +64,7 @@ static int mv_otg_set_vbus(struct otg_transceiver *otg, bool on) return mvotg->pdata->set_vbus(on); } -static int mv_otg_set_host(struct otg_transceiver *otg, +static int mv_otg_set_host(struct usb_phy *otg, struct usb_bus *host) { otg->host = host; @@ -72,7 +72,7 @@ static int mv_otg_set_host(struct otg_transceiver *otg, return 0; } -static int mv_otg_set_peripheral(struct otg_transceiver *otg, +static int mv_otg_set_peripheral(struct usb_phy *otg, struct usb_gadget *gadget) { otg->gadget = gadget; @@ -203,7 +203,7 @@ static void mv_otg_init_irq(struct mv_otg *mvotg) static void mv_otg_start_host(struct mv_otg *mvotg, int on) { #ifdef CONFIG_USB - struct otg_transceiver *otg = &mvotg->otg; + struct usb_phy *otg = &mvotg->otg; struct usb_hcd *hcd; if (!otg->host) @@ -222,7 +222,7 @@ static void mv_otg_start_host(struct mv_otg *mvotg, int on) static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on) { - struct otg_transceiver *otg = &mvotg->otg; + struct usb_phy *otg = &mvotg->otg; if (!otg->gadget) return; @@ -343,7 +343,7 @@ static void mv_otg_update_inputs(struct mv_otg *mvotg) static void mv_otg_update_state(struct mv_otg *mvotg) { struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl; - struct otg_transceiver *otg = &mvotg->otg; + struct usb_phy *otg = &mvotg->otg; int old_state = otg->state; switch (old_state) { @@ -416,7 +416,7 @@ static void mv_otg_update_state(struct mv_otg *mvotg) static void mv_otg_work(struct work_struct *work) { struct mv_otg *mvotg; - struct otg_transceiver *otg; + struct usb_phy *otg; int old_state; mvotg = container_of((struct delayed_work *)work, struct mv_otg, work); diff --git a/drivers/usb/otg/mv_otg.h b/drivers/usb/otg/mv_otg.h index be6ca1437645..a74ee07a2727 100644 --- a/drivers/usb/otg/mv_otg.h +++ b/drivers/usb/otg/mv_otg.h @@ -136,7 +136,7 @@ struct mv_otg_regs { }; struct mv_otg { - struct otg_transceiver otg; + struct usb_phy otg; struct mv_otg_ctrl otg_ctrl; /* base address */ diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index c1e360046435..2ab027997060 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -33,7 +33,7 @@ #include struct nop_usb_xceiv { - struct otg_transceiver otg; + struct usb_phy otg; struct device *dev; }; @@ -58,17 +58,17 @@ void usb_nop_xceiv_unregister(void) } EXPORT_SYMBOL(usb_nop_xceiv_unregister); -static inline struct nop_usb_xceiv *xceiv_to_nop(struct otg_transceiver *x) +static inline struct nop_usb_xceiv *xceiv_to_nop(struct usb_phy *x) { return container_of(x, struct nop_usb_xceiv, otg); } -static int nop_set_suspend(struct otg_transceiver *x, int suspend) +static int nop_set_suspend(struct usb_phy *x, int suspend) { return 0; } -static int nop_set_peripheral(struct otg_transceiver *x, +static int nop_set_peripheral(struct usb_phy *x, struct usb_gadget *gadget) { struct nop_usb_xceiv *nop; @@ -88,7 +88,7 @@ static int nop_set_peripheral(struct otg_transceiver *x, return 0; } -static int nop_set_host(struct otg_transceiver *x, struct usb_bus *host) +static int nop_set_host(struct usb_phy *x, struct usb_bus *host) { struct nop_usb_xceiv *nop; diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 307c27bc51eb..56c0f1781d8c 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -15,7 +15,7 @@ #include -static struct otg_transceiver *xceiv; +static struct usb_phy *xceiv; /** * otg_get_transceiver - find the (single) OTG transceiver @@ -26,7 +26,7 @@ static struct otg_transceiver *xceiv; * * For use by USB host and peripheral drivers. */ -struct otg_transceiver *otg_get_transceiver(void) +struct usb_phy *otg_get_transceiver(void) { if (xceiv) get_device(xceiv->dev); @@ -42,7 +42,7 @@ EXPORT_SYMBOL(otg_get_transceiver); * * For use by USB host and peripheral drivers. */ -void otg_put_transceiver(struct otg_transceiver *x) +void otg_put_transceiver(struct usb_phy *x) { if (x) put_device(x->dev); @@ -57,7 +57,7 @@ EXPORT_SYMBOL(otg_put_transceiver); * coordinate the activities of drivers for host and peripheral * controllers, and in some cases for VBUS current regulation. */ -int otg_set_transceiver(struct otg_transceiver *x) +int otg_set_transceiver(struct usb_phy *x) { if (xceiv && x) return -EBUSY; diff --git a/drivers/usb/otg/otg_fsm.h b/drivers/usb/otg/otg_fsm.h index 0cecf1d593a0..5e589ae9a199 100644 --- a/drivers/usb/otg/otg_fsm.h +++ b/drivers/usb/otg/otg_fsm.h @@ -82,7 +82,7 @@ struct otg_fsm { int loc_sof; struct otg_fsm_ops *ops; - struct otg_transceiver *transceiver; + struct usb_phy *transceiver; /* Current usb protocol used: 0:undefine; 1:host; 2:client */ int protocol; diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 14f66c358629..beeecc239d18 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -144,7 +144,7 @@ #define GPIO_USB_4PIN_ULPI_2430C (3 << 0) struct twl4030_usb { - struct otg_transceiver otg; + struct usb_phy otg; struct device *dev; /* TWL4030 internal USB regulator supplies */ @@ -548,7 +548,7 @@ static void twl4030_usb_phy_init(struct twl4030_usb *twl) sysfs_notify(&twl->dev->kobj, NULL, "vbus"); } -static int twl4030_set_suspend(struct otg_transceiver *x, int suspend) +static int twl4030_set_suspend(struct usb_phy *x, int suspend) { struct twl4030_usb *twl = xceiv_to_twl(x); @@ -560,7 +560,7 @@ static int twl4030_set_suspend(struct otg_transceiver *x, int suspend) return 0; } -static int twl4030_set_peripheral(struct otg_transceiver *x, +static int twl4030_set_peripheral(struct usb_phy *x, struct usb_gadget *gadget) { struct twl4030_usb *twl; @@ -576,7 +576,7 @@ static int twl4030_set_peripheral(struct otg_transceiver *x, return 0; } -static int twl4030_set_host(struct otg_transceiver *x, struct usb_bus *host) +static int twl4030_set_host(struct usb_phy *x, struct usb_bus *host) { struct twl4030_usb *twl; diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index ed2b26cfe814..56c4d3d50ebe 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -87,7 +87,7 @@ #define VBUS_DET BIT(2) struct twl6030_usb { - struct otg_transceiver otg; + struct usb_phy otg; struct device *dev; /* for vbus reporting with irqs disabled */ @@ -137,7 +137,7 @@ static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address) return ret; } -static int twl6030_phy_init(struct otg_transceiver *x) +static int twl6030_phy_init(struct usb_phy *x) { struct twl6030_usb *twl; struct device *dev; @@ -155,7 +155,7 @@ static int twl6030_phy_init(struct otg_transceiver *x) return 0; } -static void twl6030_phy_shutdown(struct otg_transceiver *x) +static void twl6030_phy_shutdown(struct usb_phy *x) { struct twl6030_usb *twl; struct device *dev; @@ -167,7 +167,7 @@ static void twl6030_phy_shutdown(struct otg_transceiver *x) pdata->phy_power(twl->dev, 0, 0); } -static int twl6030_phy_suspend(struct otg_transceiver *x, int suspend) +static int twl6030_phy_suspend(struct usb_phy *x, int suspend) { struct twl6030_usb *twl = xceiv_to_twl(x); struct device *dev = twl->dev; @@ -178,7 +178,7 @@ static int twl6030_phy_suspend(struct otg_transceiver *x, int suspend) return 0; } -static int twl6030_start_srp(struct otg_transceiver *x) +static int twl6030_start_srp(struct usb_phy *x) { struct twl6030_usb *twl = xceiv_to_twl(x); @@ -324,7 +324,7 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) return IRQ_HANDLED; } -static int twl6030_set_peripheral(struct otg_transceiver *x, +static int twl6030_set_peripheral(struct usb_phy *x, struct usb_gadget *gadget) { struct twl6030_usb *twl; @@ -340,7 +340,7 @@ static int twl6030_set_peripheral(struct otg_transceiver *x, return 0; } -static int twl6030_enable_irq(struct otg_transceiver *x) +static int twl6030_enable_irq(struct usb_phy *x) { struct twl6030_usb *twl = xceiv_to_twl(x); @@ -376,7 +376,7 @@ static void otg_set_vbus_work(struct work_struct *data) CHARGERUSB_CTRL1); } -static int twl6030_set_vbus(struct otg_transceiver *x, bool enabled) +static int twl6030_set_vbus(struct usb_phy *x, bool enabled) { struct twl6030_usb *twl = xceiv_to_twl(x); @@ -386,7 +386,7 @@ static int twl6030_set_vbus(struct otg_transceiver *x, bool enabled) return 0; } -static int twl6030_set_host(struct otg_transceiver *x, struct usb_bus *host) +static int twl6030_set_host(struct usb_phy *x, struct usb_bus *host) { struct twl6030_usb *twl; diff --git a/drivers/usb/otg/ulpi.c b/drivers/usb/otg/ulpi.c index 0b0466728fdc..51841ed829ab 100644 --- a/drivers/usb/otg/ulpi.c +++ b/drivers/usb/otg/ulpi.c @@ -49,7 +49,7 @@ static struct ulpi_info ulpi_ids[] = { ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"), }; -static int ulpi_set_otg_flags(struct otg_transceiver *otg) +static int ulpi_set_otg_flags(struct usb_phy *otg) { unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN; @@ -73,7 +73,7 @@ static int ulpi_set_otg_flags(struct otg_transceiver *otg) return otg_io_write(otg, flags, ULPI_OTG_CTRL); } -static int ulpi_set_fc_flags(struct otg_transceiver *otg) +static int ulpi_set_fc_flags(struct usb_phy *otg) { unsigned int flags = 0; @@ -115,7 +115,7 @@ static int ulpi_set_fc_flags(struct otg_transceiver *otg) return otg_io_write(otg, flags, ULPI_FUNC_CTRL); } -static int ulpi_set_ic_flags(struct otg_transceiver *otg) +static int ulpi_set_ic_flags(struct usb_phy *otg) { unsigned int flags = 0; @@ -134,7 +134,7 @@ static int ulpi_set_ic_flags(struct otg_transceiver *otg) return otg_io_write(otg, flags, ULPI_IFC_CTRL); } -static int ulpi_set_flags(struct otg_transceiver *otg) +static int ulpi_set_flags(struct usb_phy *otg) { int ret; @@ -149,7 +149,7 @@ static int ulpi_set_flags(struct otg_transceiver *otg) return ulpi_set_fc_flags(otg); } -static int ulpi_check_integrity(struct otg_transceiver *otg) +static int ulpi_check_integrity(struct usb_phy *otg) { int ret, i; unsigned int val = 0x55; @@ -175,7 +175,7 @@ static int ulpi_check_integrity(struct otg_transceiver *otg) return 0; } -static int ulpi_init(struct otg_transceiver *otg) +static int ulpi_init(struct usb_phy *otg) { int i, vid, pid, ret; u32 ulpi_id = 0; @@ -206,7 +206,7 @@ static int ulpi_init(struct otg_transceiver *otg) return ulpi_set_flags(otg); } -static int ulpi_set_host(struct otg_transceiver *otg, struct usb_bus *host) +static int ulpi_set_host(struct usb_phy *otg, struct usb_bus *host) { unsigned int flags = otg_io_read(otg, ULPI_IFC_CTRL); @@ -231,7 +231,7 @@ static int ulpi_set_host(struct otg_transceiver *otg, struct usb_bus *host) return otg_io_write(otg, flags, ULPI_IFC_CTRL); } -static int ulpi_set_vbus(struct otg_transceiver *otg, bool on) +static int ulpi_set_vbus(struct usb_phy *otg, bool on) { unsigned int flags = otg_io_read(otg, ULPI_OTG_CTRL); @@ -248,11 +248,11 @@ static int ulpi_set_vbus(struct otg_transceiver *otg, bool on) return otg_io_write(otg, flags, ULPI_OTG_CTRL); } -struct otg_transceiver * +struct usb_phy * otg_ulpi_create(struct otg_io_access_ops *ops, unsigned int flags) { - struct otg_transceiver *otg; + struct usb_phy *otg; otg = kzalloc(sizeof(*otg), GFP_KERNEL); if (!otg) diff --git a/drivers/usb/otg/ulpi_viewport.c b/drivers/usb/otg/ulpi_viewport.c index e9a37f90994f..e7b311b960bd 100644 --- a/drivers/usb/otg/ulpi_viewport.c +++ b/drivers/usb/otg/ulpi_viewport.c @@ -40,7 +40,7 @@ static int ulpi_viewport_wait(void __iomem *view, u32 mask) return -ETIMEDOUT; } -static int ulpi_viewport_read(struct otg_transceiver *otg, u32 reg) +static int ulpi_viewport_read(struct usb_phy *otg, u32 reg) { int ret; void __iomem *view = otg->io_priv; @@ -58,7 +58,7 @@ static int ulpi_viewport_read(struct otg_transceiver *otg, u32 reg) return ULPI_VIEW_DATA_READ(readl(view)); } -static int ulpi_viewport_write(struct otg_transceiver *otg, u32 val, u32 reg) +static int ulpi_viewport_write(struct usb_phy *otg, u32 val, u32 reg) { int ret; void __iomem *view = otg->io_priv; diff --git a/include/linux/usb/intel_mid_otg.h b/include/linux/usb/intel_mid_otg.h index a0ccf795f362..756cf5543ffd 100644 --- a/include/linux/usb/intel_mid_otg.h +++ b/include/linux/usb/intel_mid_otg.h @@ -104,11 +104,11 @@ struct iotg_ulpi_access_ops { /* * the Intel MID (Langwell/Penwell) otg transceiver driver needs to interact * with device and host drivers to implement the USB OTG related feature. More - * function members are added based on otg_transceiver data structure for this + * function members are added based on usb_phy data structure for this * purpose. */ struct intel_mid_otg_xceiv { - struct otg_transceiver otg; + struct usb_phy otg; struct otg_hsm hsm; /* base address */ @@ -147,7 +147,7 @@ struct intel_mid_otg_xceiv { }; static inline -struct intel_mid_otg_xceiv *otg_to_mid_xceiv(struct otg_transceiver *otg) +struct intel_mid_otg_xceiv *otg_to_mid_xceiv(struct usb_phy *otg) { return container_of(otg, struct intel_mid_otg_xceiv, otg); } diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 00311fe9d0df..2d3547ae9562 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -160,7 +160,7 @@ struct msm_otg_platform_data { * detection process. */ struct msm_otg { - struct otg_transceiver otg; + struct usb_phy otg; struct msm_otg_platform_data *pdata; int irq; struct clk *clk; diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index d87f44f5b04e..e0bc55702a89 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -43,14 +43,14 @@ enum usb_xceiv_events { USB_EVENT_ENUMERATED, /* gadget driver enumerated */ }; -struct otg_transceiver; +struct usb_phy; /* for transceivers connected thru an ULPI interface, the user must * provide access ops */ struct otg_io_access_ops { - int (*read)(struct otg_transceiver *otg, u32 reg); - int (*write)(struct otg_transceiver *otg, u32 val, u32 reg); + int (*read)(struct usb_phy *x, u32 reg); + int (*write)(struct usb_phy *x, u32 val, u32 reg); }; /* @@ -59,7 +59,7 @@ struct otg_io_access_ops { * moment, using the transceiver, ID signal, HNP and sometimes static * configuration information (including "board isn't wired for otg"). */ -struct otg_transceiver { +struct usb_phy { struct device *dev; const char *label; unsigned int flags; @@ -82,40 +82,40 @@ struct otg_transceiver { u16 port_change; /* initialize/shutdown the OTG controller */ - int (*init)(struct otg_transceiver *otg); - void (*shutdown)(struct otg_transceiver *otg); + int (*init)(struct usb_phy *x); + void (*shutdown)(struct usb_phy *x); /* bind/unbind the host controller */ - int (*set_host)(struct otg_transceiver *otg, + int (*set_host)(struct usb_phy *x, struct usb_bus *host); /* bind/unbind the peripheral controller */ - int (*set_peripheral)(struct otg_transceiver *otg, + int (*set_peripheral)(struct usb_phy *x, struct usb_gadget *gadget); /* effective for B devices, ignored for A-peripheral */ - int (*set_power)(struct otg_transceiver *otg, + int (*set_power)(struct usb_phy *x, unsigned mA); /* effective for A-peripheral, ignored for B devices */ - int (*set_vbus)(struct otg_transceiver *otg, + int (*set_vbus)(struct usb_phy *x, bool enabled); /* for non-OTG B devices: set transceiver into suspend mode */ - int (*set_suspend)(struct otg_transceiver *otg, + int (*set_suspend)(struct usb_phy *x, int suspend); /* for B devices only: start session with A-Host */ - int (*start_srp)(struct otg_transceiver *otg); + int (*start_srp)(struct usb_phy *x); /* start or continue HNP role switch */ - int (*start_hnp)(struct otg_transceiver *otg); + int (*start_hnp)(struct usb_phy *x); }; /* for board-specific init logic */ -extern int otg_set_transceiver(struct otg_transceiver *); +extern int otg_set_transceiver(struct usb_phy *); #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) /* sometimes transceivers are accessed only through e.g. ULPI */ @@ -132,50 +132,50 @@ static inline void usb_nop_xceiv_unregister(void) #endif /* helpers for direct access thru low-level io interface */ -static inline int otg_io_read(struct otg_transceiver *otg, u32 reg) +static inline int otg_io_read(struct usb_phy *x, u32 reg) { - if (otg->io_ops && otg->io_ops->read) - return otg->io_ops->read(otg, reg); + if (x->io_ops && x->io_ops->read) + return x->io_ops->read(x, reg); return -EINVAL; } -static inline int otg_io_write(struct otg_transceiver *otg, u32 val, u32 reg) +static inline int otg_io_write(struct usb_phy *x, u32 val, u32 reg) { - if (otg->io_ops && otg->io_ops->write) - return otg->io_ops->write(otg, val, reg); + if (x->io_ops && x->io_ops->write) + return x->io_ops->write(x, val, reg); return -EINVAL; } static inline int -otg_init(struct otg_transceiver *otg) +otg_init(struct usb_phy *x) { - if (otg->init) - return otg->init(otg); + if (x->init) + return x->init(x); return 0; } static inline void -otg_shutdown(struct otg_transceiver *otg) +otg_shutdown(struct usb_phy *x) { - if (otg->shutdown) - otg->shutdown(otg); + if (x->shutdown) + x->shutdown(x); } /* for usb host and peripheral controller drivers */ #ifdef CONFIG_USB_OTG_UTILS -extern struct otg_transceiver *otg_get_transceiver(void); -extern void otg_put_transceiver(struct otg_transceiver *); +extern struct usb_phy *otg_get_transceiver(void); +extern void otg_put_transceiver(struct usb_phy *); extern const char *otg_state_string(enum usb_otg_state state); #else -static inline struct otg_transceiver *otg_get_transceiver(void) +static inline struct usb_phy *otg_get_transceiver(void) { return NULL; } -static inline void otg_put_transceiver(struct otg_transceiver *x) +static inline void otg_put_transceiver(struct usb_phy *x) { } @@ -187,67 +187,67 @@ static inline const char *otg_state_string(enum usb_otg_state state) /* Context: can sleep */ static inline int -otg_start_hnp(struct otg_transceiver *otg) +otg_start_hnp(struct usb_phy *x) { - return otg->start_hnp(otg); + return x->start_hnp(x); } /* Context: can sleep */ static inline int -otg_set_vbus(struct otg_transceiver *otg, bool enabled) +otg_set_vbus(struct usb_phy *x, bool enabled) { - return otg->set_vbus(otg, enabled); + return x->set_vbus(x, enabled); } /* for HCDs */ static inline int -otg_set_host(struct otg_transceiver *otg, struct usb_bus *host) +otg_set_host(struct usb_phy *x, struct usb_bus *host) { - return otg->set_host(otg, host); + return x->set_host(x, host); } /* for usb peripheral controller drivers */ /* Context: can sleep */ static inline int -otg_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *periph) +otg_set_peripheral(struct usb_phy *x, struct usb_gadget *periph) { - return otg->set_peripheral(otg, periph); + return x->set_peripheral(x, periph); } static inline int -otg_set_power(struct otg_transceiver *otg, unsigned mA) +otg_set_power(struct usb_phy *x, unsigned mA) { - return otg->set_power(otg, mA); + return x->set_power(x, mA); } /* Context: can sleep */ static inline int -otg_set_suspend(struct otg_transceiver *otg, int suspend) +otg_set_suspend(struct usb_phy *x, int suspend) { - if (otg->set_suspend != NULL) - return otg->set_suspend(otg, suspend); + if (x->set_suspend != NULL) + return x->set_suspend(x, suspend); else return 0; } static inline int -otg_start_srp(struct otg_transceiver *otg) +otg_start_srp(struct usb_phy *x) { - return otg->start_srp(otg); + return x->start_srp(x); } /* notifiers */ static inline int -otg_register_notifier(struct otg_transceiver *otg, struct notifier_block *nb) +otg_register_notifier(struct usb_phy *x, struct notifier_block *nb) { - return atomic_notifier_chain_register(&otg->notifier, nb); + return atomic_notifier_chain_register(&x->notifier, nb); } static inline void -otg_unregister_notifier(struct otg_transceiver *otg, struct notifier_block *nb) +otg_unregister_notifier(struct usb_phy *x, struct notifier_block *nb) { - atomic_notifier_chain_unregister(&otg->notifier, nb); + atomic_notifier_chain_unregister(&x->notifier, nb); } /* for OTG controller drivers (and maybe other stuff) */ diff --git a/include/linux/usb/ulpi.h b/include/linux/usb/ulpi.h index 9595796d62ed..51ebf72bc449 100644 --- a/include/linux/usb/ulpi.h +++ b/include/linux/usb/ulpi.h @@ -181,7 +181,7 @@ /*-------------------------------------------------------------------------*/ -struct otg_transceiver *otg_ulpi_create(struct otg_io_access_ops *ops, +struct usb_phy *otg_ulpi_create(struct otg_io_access_ops *ops, unsigned int flags); #ifdef CONFIG_USB_ULPI_VIEWPORT -- cgit From de07e18c00c403d5076a5a697d83fe3ced73bc30 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 13 Feb 2012 13:24:03 +0200 Subject: usb: otg: Rename usb_xceiv_event to usb_phy_event Convert all users. Signed-off-by: Heikki Krogerus Reviewed-by: Marek Vasut Signed-off-by: Felipe Balbi --- drivers/usb/otg/ab8500-usb.c | 2 +- drivers/usb/otg/twl4030-usb.c | 2 +- include/linux/usb/otg.h | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index 5f06dda7db61..b46a5fe178ef 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -153,7 +153,7 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab) u8 reg; enum ab8500_usb_link_status lsts; void *v = NULL; - enum usb_xceiv_events event; + enum usb_phy_events event; abx500_get_register_interruptible(ab->dev, AB8500_USB, diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index beeecc239d18..906d524b94bb 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -246,7 +246,7 @@ twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) /*-------------------------------------------------------------------------*/ -static enum usb_xceiv_events twl4030_usb_linkstat(struct twl4030_usb *twl) +static enum usb_phy_events twl4030_usb_linkstat(struct twl4030_usb *twl) { int status; int linkstat = USB_EVENT_NONE; diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index e0bc55702a89..723a000146f8 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -35,7 +35,7 @@ enum usb_otg_state { OTG_STATE_A_VBUS_ERR, }; -enum usb_xceiv_events { +enum usb_phy_events { USB_EVENT_NONE, /* no events or cable disconnected */ USB_EVENT_VBUS, /* vbus valid event */ USB_EVENT_ID, /* id was grounded */ @@ -66,7 +66,7 @@ struct usb_phy { u8 default_a; enum usb_otg_state state; - enum usb_xceiv_events last_event; + enum usb_phy_events last_event; struct usb_bus *host; struct usb_gadget *gadget; @@ -74,7 +74,7 @@ struct usb_phy { struct otg_io_access_ops *io_ops; void __iomem *io_priv; - /* for notification of usb_xceiv_events */ + /* for notification of usb_phy_events */ struct atomic_notifier_head notifier; /* to pass extra port status to the root hub */ -- cgit From 7a8a3a9bec7432eedcb32b54a3940d0593246060 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 13 Feb 2012 13:24:04 +0200 Subject: usb: otg: Separate otg members from usb_phy Introducing struct otg and collecting otg specific members to it from struct usb_phy. There are no changes to struct usb_phy at this stage. This also renames transceiver specific functions, and offers aliases for the old otg ones. Signed-off-by: Heikki Krogerus Reviewed-by: Marek Vasut Signed-off-by: Felipe Balbi --- drivers/usb/otg/otg.c | 38 ++++++++++---------- include/linux/usb/otg.h | 96 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 98 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 56c0f1781d8c..801e597a1541 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -15,56 +15,56 @@ #include -static struct usb_phy *xceiv; +static struct usb_phy *phy; /** - * otg_get_transceiver - find the (single) OTG transceiver + * usb_get_transceiver - find the (single) USB transceiver * * Returns the transceiver driver, after getting a refcount to it; or * null if there is no such transceiver. The caller is responsible for - * calling otg_put_transceiver() to release that count. + * calling usb_put_transceiver() to release that count. * * For use by USB host and peripheral drivers. */ -struct usb_phy *otg_get_transceiver(void) +struct usb_phy *usb_get_transceiver(void) { - if (xceiv) - get_device(xceiv->dev); - return xceiv; + if (phy) + get_device(phy->dev); + return phy; } -EXPORT_SYMBOL(otg_get_transceiver); +EXPORT_SYMBOL(usb_get_transceiver); /** - * otg_put_transceiver - release the (single) OTG transceiver - * @x: the transceiver returned by otg_get_transceiver() + * usb_put_transceiver - release the (single) USB transceiver + * @x: the transceiver returned by usb_get_transceiver() * - * Releases a refcount the caller received from otg_get_transceiver(). + * Releases a refcount the caller received from usb_get_transceiver(). * * For use by USB host and peripheral drivers. */ -void otg_put_transceiver(struct usb_phy *x) +void usb_put_transceiver(struct usb_phy *x) { if (x) put_device(x->dev); } -EXPORT_SYMBOL(otg_put_transceiver); +EXPORT_SYMBOL(usb_put_transceiver); /** - * otg_set_transceiver - declare the (single) OTG transceiver - * @x: the USB OTG transceiver to be used; or NULL + * usb_set_transceiver - declare the (single) USB transceiver + * @x: the USB transceiver to be used; or NULL * * This call is exclusively for use by transceiver drivers, which * coordinate the activities of drivers for host and peripheral * controllers, and in some cases for VBUS current regulation. */ -int otg_set_transceiver(struct usb_phy *x) +int usb_set_transceiver(struct usb_phy *x) { - if (xceiv && x) + if (phy && x) return -EBUSY; - xceiv = x; + phy = x; return 0; } -EXPORT_SYMBOL(otg_set_transceiver); +EXPORT_SYMBOL(usb_set_transceiver); const char *otg_state_string(enum usb_otg_state state) { diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 723a000146f8..5c1cfbc73555 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -48,11 +48,36 @@ struct usb_phy; /* for transceivers connected thru an ULPI interface, the user must * provide access ops */ -struct otg_io_access_ops { +struct usb_phy_io_ops { int (*read)(struct usb_phy *x, u32 reg); int (*write)(struct usb_phy *x, u32 val, u32 reg); }; +struct usb_otg { + u8 default_a; + + struct usb_phy *phy; + struct usb_bus *host; + struct usb_gadget *gadget; + + /* bind/unbind the host controller */ + int (*set_host)(struct usb_otg *otg, struct usb_bus *host); + + /* bind/unbind the peripheral controller */ + int (*set_peripheral)(struct usb_otg *otg, + struct usb_gadget *gadget); + + /* effective for A-peripheral, ignored for B devices */ + int (*set_vbus)(struct usb_otg *otg, bool enabled); + + /* for B devices only: start session with A-Host */ + int (*start_srp)(struct usb_otg *otg); + + /* start or continue HNP role switch */ + int (*start_hnp)(struct usb_otg *otg); + +}; + /* * the otg driver needs to interact with both device side and host side * usb controllers. it decides which controller is active at a given @@ -68,11 +93,13 @@ struct usb_phy { enum usb_otg_state state; enum usb_phy_events last_event; + struct usb_otg *otg; + struct usb_bus *host; struct usb_gadget *gadget; - struct otg_io_access_ops *io_ops; - void __iomem *io_priv; + struct usb_phy_io_ops *io_ops; + void __iomem *io_priv; /* for notification of usb_phy_events */ struct atomic_notifier_head notifier; @@ -115,7 +142,7 @@ struct usb_phy { /* for board-specific init logic */ -extern int otg_set_transceiver(struct usb_phy *); +extern int usb_set_transceiver(struct usb_phy *); #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) /* sometimes transceivers are accessed only through e.g. ULPI */ @@ -132,7 +159,7 @@ static inline void usb_nop_xceiv_unregister(void) #endif /* helpers for direct access thru low-level io interface */ -static inline int otg_io_read(struct usb_phy *x, u32 reg) +static inline int usb_phy_io_read(struct usb_phy *x, u32 reg) { if (x->io_ops && x->io_ops->read) return x->io_ops->read(x, reg); @@ -140,7 +167,7 @@ static inline int otg_io_read(struct usb_phy *x, u32 reg) return -EINVAL; } -static inline int otg_io_write(struct usb_phy *x, u32 val, u32 reg) +static inline int usb_phy_io_write(struct usb_phy *x, u32 val, u32 reg) { if (x->io_ops && x->io_ops->write) return x->io_ops->write(x, val, reg); @@ -149,7 +176,7 @@ static inline int otg_io_write(struct usb_phy *x, u32 val, u32 reg) } static inline int -otg_init(struct usb_phy *x) +usb_phy_init(struct usb_phy *x) { if (x->init) return x->init(x); @@ -158,7 +185,7 @@ otg_init(struct usb_phy *x) } static inline void -otg_shutdown(struct usb_phy *x) +usb_phy_shutdown(struct usb_phy *x) { if (x->shutdown) x->shutdown(x); @@ -166,16 +193,16 @@ otg_shutdown(struct usb_phy *x) /* for usb host and peripheral controller drivers */ #ifdef CONFIG_USB_OTG_UTILS -extern struct usb_phy *otg_get_transceiver(void); -extern void otg_put_transceiver(struct usb_phy *); +extern struct usb_phy *usb_get_transceiver(void); +extern void usb_put_transceiver(struct usb_phy *); extern const char *otg_state_string(enum usb_otg_state state); #else -static inline struct usb_phy *otg_get_transceiver(void) +static inline struct usb_phy *usb_get_transceiver(void) { return NULL; } -static inline void otg_put_transceiver(struct usb_phy *x) +static inline void usb_put_transceiver(struct usb_phy *x) { } @@ -189,6 +216,9 @@ static inline const char *otg_state_string(enum usb_otg_state state) static inline int otg_start_hnp(struct usb_phy *x) { + if (x->otg && x->otg->start_hnp) + return x->otg->start_hnp(x->otg); + return x->start_hnp(x); } @@ -196,6 +226,9 @@ otg_start_hnp(struct usb_phy *x) static inline int otg_set_vbus(struct usb_phy *x, bool enabled) { + if (x->otg && x->otg->set_vbus) + return x->otg->set_vbus(x->otg, enabled); + return x->set_vbus(x, enabled); } @@ -203,6 +236,9 @@ otg_set_vbus(struct usb_phy *x, bool enabled) static inline int otg_set_host(struct usb_phy *x, struct usb_bus *host) { + if (x->otg && x->otg->set_host) + return x->otg->set_host(x->otg, host); + return x->set_host(x, host); } @@ -212,18 +248,23 @@ otg_set_host(struct usb_phy *x, struct usb_bus *host) static inline int otg_set_peripheral(struct usb_phy *x, struct usb_gadget *periph) { + if (x->otg && x->otg->set_peripheral) + return x->otg->set_peripheral(x->otg, periph); + return x->set_peripheral(x, periph); } static inline int -otg_set_power(struct usb_phy *x, unsigned mA) +usb_phy_set_power(struct usb_phy *x, unsigned mA) { - return x->set_power(x, mA); + if (x && x->set_power) + return x->set_power(x, mA); + return 0; } /* Context: can sleep */ static inline int -otg_set_suspend(struct usb_phy *x, int suspend) +usb_phy_set_suspend(struct usb_phy *x, int suspend) { if (x->set_suspend != NULL) return x->set_suspend(x, suspend); @@ -234,18 +275,21 @@ otg_set_suspend(struct usb_phy *x, int suspend) static inline int otg_start_srp(struct usb_phy *x) { + if (x->otg && x->otg->start_srp) + return x->otg->start_srp(x->otg); + return x->start_srp(x); } /* notifiers */ static inline int -otg_register_notifier(struct usb_phy *x, struct notifier_block *nb) +usb_register_notifier(struct usb_phy *x, struct notifier_block *nb) { return atomic_notifier_chain_register(&x->notifier, nb); } static inline void -otg_unregister_notifier(struct usb_phy *x, struct notifier_block *nb) +usb_unregister_notifier(struct usb_phy *x, struct notifier_block *nb) { atomic_notifier_chain_unregister(&x->notifier, nb); } @@ -253,4 +297,22 @@ otg_unregister_notifier(struct usb_phy *x, struct notifier_block *nb) /* for OTG controller drivers (and maybe other stuff) */ extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num); +/* Temporary aliases for transceiver functions */ +#define otg_set_transceiver(x) usb_set_transceiver(x) +#define otg_get_transceiver() usb_get_transceiver() +#define otg_put_transceiver(x) usb_put_transceiver(x) + +#define otg_io_read(x, a) usb_phy_io_read(x, a) +#define otg_io_write(x, a, b) usb_phy_io_write(x, a, b) + +#define otg_init(x) usb_phy_init(x) +#define otg_shutdown(x) usb_phy_shutdown(x) +#define otg_set_power(x, a) usb_phy_set_power(x, a) +#define otg_set_suspend(x, a) usb_phy_set_suspend(x, a) + +#define otg_register_notifier(x, a) usb_register_notifier(x, a) +#define otg_unregister_notifier(x, a) usb_unregiser_notifier(x, a) + +#define otg_io_access_ops usb_phy_io_ops + #endif /* __LINUX_USB_OTG_H */ -- cgit From 1d4c9293ae3555f2dcf9f394d1e2a14fd9421c4f Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 13 Feb 2012 13:24:09 +0200 Subject: usb: otg: msm: Start using struct usb_otg Use struct usb_otg members with OTG specific functions instead of usb_phy members. Signed-off-by: Heikki Krogerus Acked-by: Pavankumar Kondeti Reviewed-by: Marek Vasut Signed-off-by: Felipe Balbi --- drivers/usb/otg/msm_otg.c | 398 ++++++++++++++++++++++-------------------- include/linux/usb/msm_hsusb.h | 2 +- 2 files changed, 206 insertions(+), 194 deletions(-) (limited to 'include') diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index cba4737a04f4..1d0347c247d1 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -69,9 +69,9 @@ static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) int ret = 0; if (init) { - hsusb_vddcx = regulator_get(motg->otg.dev, "HSUSB_VDDCX"); + hsusb_vddcx = regulator_get(motg->phy.dev, "HSUSB_VDDCX"); if (IS_ERR(hsusb_vddcx)) { - dev_err(motg->otg.dev, "unable to get hsusb vddcx\n"); + dev_err(motg->phy.dev, "unable to get hsusb vddcx\n"); return PTR_ERR(hsusb_vddcx); } @@ -79,7 +79,7 @@ static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) USB_PHY_VDD_DIG_VOL_MIN, USB_PHY_VDD_DIG_VOL_MAX); if (ret) { - dev_err(motg->otg.dev, "unable to set the voltage " + dev_err(motg->phy.dev, "unable to set the voltage " "for hsusb vddcx\n"); regulator_put(hsusb_vddcx); return ret; @@ -87,18 +87,18 @@ static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) ret = regulator_enable(hsusb_vddcx); if (ret) { - dev_err(motg->otg.dev, "unable to enable hsusb vddcx\n"); + dev_err(motg->phy.dev, "unable to enable hsusb vddcx\n"); regulator_put(hsusb_vddcx); } } else { ret = regulator_set_voltage(hsusb_vddcx, 0, USB_PHY_VDD_DIG_VOL_MAX); if (ret) - dev_err(motg->otg.dev, "unable to set the voltage " + dev_err(motg->phy.dev, "unable to set the voltage " "for hsusb vddcx\n"); ret = regulator_disable(hsusb_vddcx); if (ret) - dev_err(motg->otg.dev, "unable to disable hsusb vddcx\n"); + dev_err(motg->phy.dev, "unable to disable hsusb vddcx\n"); regulator_put(hsusb_vddcx); } @@ -111,40 +111,40 @@ static int msm_hsusb_ldo_init(struct msm_otg *motg, int init) int rc = 0; if (init) { - hsusb_3p3 = regulator_get(motg->otg.dev, "HSUSB_3p3"); + hsusb_3p3 = regulator_get(motg->phy.dev, "HSUSB_3p3"); if (IS_ERR(hsusb_3p3)) { - dev_err(motg->otg.dev, "unable to get hsusb 3p3\n"); + dev_err(motg->phy.dev, "unable to get hsusb 3p3\n"); return PTR_ERR(hsusb_3p3); } rc = regulator_set_voltage(hsusb_3p3, USB_PHY_3P3_VOL_MIN, USB_PHY_3P3_VOL_MAX); if (rc) { - dev_err(motg->otg.dev, "unable to set voltage level " + dev_err(motg->phy.dev, "unable to set voltage level " "for hsusb 3p3\n"); goto put_3p3; } rc = regulator_enable(hsusb_3p3); if (rc) { - dev_err(motg->otg.dev, "unable to enable the hsusb 3p3\n"); + dev_err(motg->phy.dev, "unable to enable the hsusb 3p3\n"); goto put_3p3; } - hsusb_1p8 = regulator_get(motg->otg.dev, "HSUSB_1p8"); + hsusb_1p8 = regulator_get(motg->phy.dev, "HSUSB_1p8"); if (IS_ERR(hsusb_1p8)) { - dev_err(motg->otg.dev, "unable to get hsusb 1p8\n"); + dev_err(motg->phy.dev, "unable to get hsusb 1p8\n"); rc = PTR_ERR(hsusb_1p8); goto disable_3p3; } rc = regulator_set_voltage(hsusb_1p8, USB_PHY_1P8_VOL_MIN, USB_PHY_1P8_VOL_MAX); if (rc) { - dev_err(motg->otg.dev, "unable to set voltage level " + dev_err(motg->phy.dev, "unable to set voltage level " "for hsusb 1p8\n"); goto put_1p8; } rc = regulator_enable(hsusb_1p8); if (rc) { - dev_err(motg->otg.dev, "unable to enable the hsusb 1p8\n"); + dev_err(motg->phy.dev, "unable to enable the hsusb 1p8\n"); goto put_1p8; } @@ -235,9 +235,9 @@ static int msm_hsusb_ldo_set_mode(int on) return ret < 0 ? ret : 0; } -static int ulpi_read(struct usb_phy *otg, u32 reg) +static int ulpi_read(struct usb_phy *phy, u32 reg) { - struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); int cnt = 0; /* initiate read operation */ @@ -253,16 +253,16 @@ static int ulpi_read(struct usb_phy *otg, u32 reg) } if (cnt >= ULPI_IO_TIMEOUT_USEC) { - dev_err(otg->dev, "ulpi_read: timeout %08x\n", + dev_err(phy->dev, "ulpi_read: timeout %08x\n", readl(USB_ULPI_VIEWPORT)); return -ETIMEDOUT; } return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); } -static int ulpi_write(struct usb_phy *otg, u32 val, u32 reg) +static int ulpi_write(struct usb_phy *phy, u32 val, u32 reg) { - struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); int cnt = 0; /* initiate write operation */ @@ -279,13 +279,13 @@ static int ulpi_write(struct usb_phy *otg, u32 val, u32 reg) } if (cnt >= ULPI_IO_TIMEOUT_USEC) { - dev_err(otg->dev, "ulpi_write: timeout\n"); + dev_err(phy->dev, "ulpi_write: timeout\n"); return -ETIMEDOUT; } return 0; } -static struct otg_io_access_ops msm_otg_io_ops = { +static struct usb_phy_io_ops msm_otg_io_ops = { .read = ulpi_read, .write = ulpi_write, }; @@ -299,9 +299,9 @@ static void ulpi_init(struct msm_otg *motg) return; while (seq[0] >= 0) { - dev_vdbg(motg->otg.dev, "ulpi: write 0x%02x to 0x%02x\n", + dev_vdbg(motg->phy.dev, "ulpi: write 0x%02x to 0x%02x\n", seq[0], seq[1]); - ulpi_write(&motg->otg, seq[0], seq[1]); + ulpi_write(&motg->phy, seq[0], seq[1]); seq += 2; } } @@ -313,11 +313,11 @@ static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) if (assert) { ret = clk_reset(motg->clk, CLK_RESET_ASSERT); if (ret) - dev_err(motg->otg.dev, "usb hs_clk assert failed\n"); + dev_err(motg->phy.dev, "usb hs_clk assert failed\n"); } else { ret = clk_reset(motg->clk, CLK_RESET_DEASSERT); if (ret) - dev_err(motg->otg.dev, "usb hs_clk deassert failed\n"); + dev_err(motg->phy.dev, "usb hs_clk deassert failed\n"); } return ret; } @@ -328,13 +328,13 @@ static int msm_otg_phy_clk_reset(struct msm_otg *motg) ret = clk_reset(motg->phy_reset_clk, CLK_RESET_ASSERT); if (ret) { - dev_err(motg->otg.dev, "usb phy clk assert failed\n"); + dev_err(motg->phy.dev, "usb phy clk assert failed\n"); return ret; } usleep_range(10000, 12000); ret = clk_reset(motg->phy_reset_clk, CLK_RESET_DEASSERT); if (ret) - dev_err(motg->otg.dev, "usb phy clk deassert failed\n"); + dev_err(motg->phy.dev, "usb phy clk deassert failed\n"); return ret; } @@ -358,7 +358,7 @@ static int msm_otg_phy_reset(struct msm_otg *motg) writel(val | PORTSC_PTS_ULPI, USB_PORTSC); for (retries = 3; retries > 0; retries--) { - ret = ulpi_write(&motg->otg, ULPI_FUNC_CTRL_SUSPENDM, + ret = ulpi_write(&motg->phy, ULPI_FUNC_CTRL_SUSPENDM, ULPI_CLR(ULPI_FUNC_CTRL)); if (!ret) break; @@ -375,7 +375,7 @@ static int msm_otg_phy_reset(struct msm_otg *motg) return ret; for (retries = 3; retries > 0; retries--) { - ret = ulpi_read(&motg->otg, ULPI_DEBUG); + ret = ulpi_read(&motg->phy, ULPI_DEBUG); if (ret != -ETIMEDOUT) break; ret = msm_otg_phy_clk_reset(motg); @@ -385,14 +385,14 @@ static int msm_otg_phy_reset(struct msm_otg *motg) if (!retries) return -ETIMEDOUT; - dev_info(motg->otg.dev, "phy_reset: success\n"); + dev_info(motg->phy.dev, "phy_reset: success\n"); return 0; } #define LINK_RESET_TIMEOUT_USEC (250 * 1000) -static int msm_otg_reset(struct usb_phy *otg) +static int msm_otg_reset(struct usb_phy *phy) { - struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); struct msm_otg_platform_data *pdata = motg->pdata; int cnt = 0; int ret; @@ -401,7 +401,7 @@ static int msm_otg_reset(struct usb_phy *otg) ret = msm_otg_phy_reset(motg); if (ret) { - dev_err(otg->dev, "phy_reset failed\n"); + dev_err(phy->dev, "phy_reset failed\n"); return ret; } @@ -435,8 +435,8 @@ static int msm_otg_reset(struct usb_phy *otg) val |= OTGSC_BSVIE; } writel(val, USB_OTGSC); - ulpi_write(otg, ulpi_val, ULPI_USB_INT_EN_RISE); - ulpi_write(otg, ulpi_val, ULPI_USB_INT_EN_FALL); + ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_RISE); + ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL); } return 0; @@ -448,8 +448,8 @@ static int msm_otg_reset(struct usb_phy *otg) #ifdef CONFIG_PM_SLEEP static int msm_otg_suspend(struct msm_otg *motg) { - struct usb_phy *otg = &motg->otg; - struct usb_bus *bus = otg->host; + struct usb_phy *phy = &motg->phy; + struct usb_bus *bus = phy->otg->host; struct msm_otg_platform_data *pdata = motg->pdata; int cnt = 0; @@ -475,10 +475,10 @@ static int msm_otg_suspend(struct msm_otg *motg) */ if (motg->pdata->phy_type == CI_45NM_INTEGRATED_PHY) { - ulpi_read(otg, 0x14); + ulpi_read(phy, 0x14); if (pdata->otg_control == OTG_PHY_CONTROL) - ulpi_write(otg, 0x01, 0x30); - ulpi_write(otg, 0x08, 0x09); + ulpi_write(phy, 0x01, 0x30); + ulpi_write(phy, 0x08, 0x09); } /* @@ -495,8 +495,8 @@ static int msm_otg_suspend(struct msm_otg *motg) } if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) { - dev_err(otg->dev, "Unable to suspend PHY\n"); - msm_otg_reset(otg); + dev_err(phy->dev, "Unable to suspend PHY\n"); + msm_otg_reset(phy); enable_irq(motg->irq); return -ETIMEDOUT; } @@ -528,7 +528,7 @@ static int msm_otg_suspend(struct msm_otg *motg) msm_hsusb_config_vddcx(0); } - if (device_may_wakeup(otg->dev)) + if (device_may_wakeup(phy->dev)) enable_irq_wake(motg->irq); if (bus) clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); @@ -536,15 +536,15 @@ static int msm_otg_suspend(struct msm_otg *motg) atomic_set(&motg->in_lpm, 1); enable_irq(motg->irq); - dev_info(otg->dev, "USB in low power mode\n"); + dev_info(phy->dev, "USB in low power mode\n"); return 0; } static int msm_otg_resume(struct msm_otg *motg) { - struct usb_phy *otg = &motg->otg; - struct usb_bus *bus = otg->host; + struct usb_phy *phy = &motg->phy; + struct usb_bus *bus = phy->otg->host; int cnt = 0; unsigned temp; @@ -592,13 +592,13 @@ static int msm_otg_resume(struct msm_otg *motg) * PHY. USB state can not be restored. Re-insertion * of USB cable is the only way to get USB working. */ - dev_err(otg->dev, "Unable to resume USB." + dev_err(phy->dev, "Unable to resume USB." "Re-plugin the cable\n"); - msm_otg_reset(otg); + msm_otg_reset(phy); } skip_phy_resume: - if (device_may_wakeup(otg->dev)) + if (device_may_wakeup(phy->dev)) disable_irq_wake(motg->irq); if (bus) set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); @@ -607,11 +607,11 @@ skip_phy_resume: if (motg->async_int) { motg->async_int = 0; - pm_runtime_put(otg->dev); + pm_runtime_put(phy->dev); enable_irq(motg->irq); } - dev_info(otg->dev, "USB exited from low power mode\n"); + dev_info(phy->dev, "USB exited from low power mode\n"); return 0; } @@ -623,13 +623,13 @@ static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA) return; /* TODO: Notify PMIC about available current */ - dev_info(motg->otg.dev, "Avail curr from USB = %u\n", mA); + dev_info(motg->phy.dev, "Avail curr from USB = %u\n", mA); motg->cur_power = mA; } -static int msm_otg_set_power(struct usb_phy *otg, unsigned mA) +static int msm_otg_set_power(struct usb_phy *phy, unsigned mA) { - struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); /* * Gadget driver uses set_power method to notify about the @@ -644,19 +644,19 @@ static int msm_otg_set_power(struct usb_phy *otg, unsigned mA) return 0; } -static void msm_otg_start_host(struct usb_phy *otg, int on) +static void msm_otg_start_host(struct usb_phy *phy, int on) { - struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); struct msm_otg_platform_data *pdata = motg->pdata; struct usb_hcd *hcd; - if (!otg->host) + if (!phy->otg->host) return; - hcd = bus_to_hcd(otg->host); + hcd = bus_to_hcd(phy->otg->host); if (on) { - dev_dbg(otg->dev, "host on\n"); + dev_dbg(phy->dev, "host on\n"); if (pdata->vbus_power) pdata->vbus_power(1); @@ -671,7 +671,7 @@ static void msm_otg_start_host(struct usb_phy *otg, int on) usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); #endif } else { - dev_dbg(otg->dev, "host off\n"); + dev_dbg(phy->dev, "host off\n"); #ifdef CONFIG_USB usb_remove_hcd(hcd); @@ -683,9 +683,9 @@ static void msm_otg_start_host(struct usb_phy *otg, int on) } } -static int msm_otg_set_host(struct usb_phy *otg, struct usb_bus *host) +static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host) { - struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy); struct usb_hcd *hcd; /* @@ -693,16 +693,16 @@ static int msm_otg_set_host(struct usb_phy *otg, struct usb_bus *host) * only peripheral configuration. */ if (motg->pdata->mode == USB_PERIPHERAL) { - dev_info(otg->dev, "Host mode is not supported\n"); + dev_info(otg->phy->dev, "Host mode is not supported\n"); return -ENODEV; } if (!host) { - if (otg->state == OTG_STATE_A_HOST) { - pm_runtime_get_sync(otg->dev); - msm_otg_start_host(otg, 0); + if (otg->phy->state == OTG_STATE_A_HOST) { + pm_runtime_get_sync(otg->phy->dev); + msm_otg_start_host(otg->phy, 0); otg->host = NULL; - otg->state = OTG_STATE_UNDEFINED; + otg->phy->state = OTG_STATE_UNDEFINED; schedule_work(&motg->sm_work); } else { otg->host = NULL; @@ -715,30 +715,30 @@ static int msm_otg_set_host(struct usb_phy *otg, struct usb_bus *host) hcd->power_budget = motg->pdata->power_budget; otg->host = host; - dev_dbg(otg->dev, "host driver registered w/ tranceiver\n"); + dev_dbg(otg->phy->dev, "host driver registered w/ tranceiver\n"); /* * Kick the state machine work, if peripheral is not supported * or peripheral is already registered with us. */ if (motg->pdata->mode == USB_HOST || otg->gadget) { - pm_runtime_get_sync(otg->dev); + pm_runtime_get_sync(otg->phy->dev); schedule_work(&motg->sm_work); } return 0; } -static void msm_otg_start_peripheral(struct usb_phy *otg, int on) +static void msm_otg_start_peripheral(struct usb_phy *phy, int on) { - struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); struct msm_otg_platform_data *pdata = motg->pdata; - if (!otg->gadget) + if (!phy->otg->gadget) return; if (on) { - dev_dbg(otg->dev, "gadget on\n"); + dev_dbg(phy->dev, "gadget on\n"); /* * Some boards have a switch cotrolled by gpio * to enable/disable internal HUB. Disable internal @@ -746,36 +746,36 @@ static void msm_otg_start_peripheral(struct usb_phy *otg, int on) */ if (pdata->setup_gpio) pdata->setup_gpio(OTG_STATE_B_PERIPHERAL); - usb_gadget_vbus_connect(otg->gadget); + usb_gadget_vbus_connect(phy->otg->gadget); } else { - dev_dbg(otg->dev, "gadget off\n"); - usb_gadget_vbus_disconnect(otg->gadget); + dev_dbg(phy->dev, "gadget off\n"); + usb_gadget_vbus_disconnect(phy->otg->gadget); if (pdata->setup_gpio) pdata->setup_gpio(OTG_STATE_UNDEFINED); } } -static int msm_otg_set_peripheral(struct usb_phy *otg, - struct usb_gadget *gadget) +static int msm_otg_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) { - struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy); /* * Fail peripheral registration if this board can support * only host configuration. */ if (motg->pdata->mode == USB_HOST) { - dev_info(otg->dev, "Peripheral mode is not supported\n"); + dev_info(otg->phy->dev, "Peripheral mode is not supported\n"); return -ENODEV; } if (!gadget) { - if (otg->state == OTG_STATE_B_PERIPHERAL) { - pm_runtime_get_sync(otg->dev); - msm_otg_start_peripheral(otg, 0); + if (otg->phy->state == OTG_STATE_B_PERIPHERAL) { + pm_runtime_get_sync(otg->phy->dev); + msm_otg_start_peripheral(otg->phy, 0); otg->gadget = NULL; - otg->state = OTG_STATE_UNDEFINED; + otg->phy->state = OTG_STATE_UNDEFINED; schedule_work(&motg->sm_work); } else { otg->gadget = NULL; @@ -784,14 +784,14 @@ static int msm_otg_set_peripheral(struct usb_phy *otg, return 0; } otg->gadget = gadget; - dev_dbg(otg->dev, "peripheral driver registered w/ tranceiver\n"); + dev_dbg(otg->phy->dev, "peripheral driver registered w/ tranceiver\n"); /* * Kick the state machine work, if host is not supported * or host is already registered with us. */ if (motg->pdata->mode == USB_PERIPHERAL || otg->host) { - pm_runtime_get_sync(otg->dev); + pm_runtime_get_sync(otg->phy->dev); schedule_work(&motg->sm_work); } @@ -800,17 +800,17 @@ static int msm_otg_set_peripheral(struct usb_phy *otg, static bool msm_chg_check_secondary_det(struct msm_otg *motg) { - struct usb_phy *otg = &motg->otg; + struct usb_phy *phy = &motg->phy; u32 chg_det; bool ret = false; switch (motg->pdata->phy_type) { case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(otg, 0x34); + chg_det = ulpi_read(phy, 0x34); ret = chg_det & (1 << 4); break; case SNPS_28NM_INTEGRATED_PHY: - chg_det = ulpi_read(otg, 0x87); + chg_det = ulpi_read(phy, 0x87); ret = chg_det & 1; break; default: @@ -821,38 +821,38 @@ static bool msm_chg_check_secondary_det(struct msm_otg *motg) static void msm_chg_enable_secondary_det(struct msm_otg *motg) { - struct usb_phy *otg = &motg->otg; + struct usb_phy *phy = &motg->phy; u32 chg_det; switch (motg->pdata->phy_type) { case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(otg, 0x34); + chg_det = ulpi_read(phy, 0x34); /* Turn off charger block */ chg_det |= ~(1 << 1); - ulpi_write(otg, chg_det, 0x34); + ulpi_write(phy, chg_det, 0x34); udelay(20); /* control chg block via ULPI */ chg_det &= ~(1 << 3); - ulpi_write(otg, chg_det, 0x34); + ulpi_write(phy, chg_det, 0x34); /* put it in host mode for enabling D- source */ chg_det &= ~(1 << 2); - ulpi_write(otg, chg_det, 0x34); + ulpi_write(phy, chg_det, 0x34); /* Turn on chg detect block */ chg_det &= ~(1 << 1); - ulpi_write(otg, chg_det, 0x34); + ulpi_write(phy, chg_det, 0x34); udelay(20); /* enable chg detection */ chg_det &= ~(1 << 0); - ulpi_write(otg, chg_det, 0x34); + ulpi_write(phy, chg_det, 0x34); break; case SNPS_28NM_INTEGRATED_PHY: /* * Configure DM as current source, DP as current sink * and enable battery charging comparators. */ - ulpi_write(otg, 0x8, 0x85); - ulpi_write(otg, 0x2, 0x85); - ulpi_write(otg, 0x1, 0x85); + ulpi_write(phy, 0x8, 0x85); + ulpi_write(phy, 0x2, 0x85); + ulpi_write(phy, 0x1, 0x85); break; default: break; @@ -861,17 +861,17 @@ static void msm_chg_enable_secondary_det(struct msm_otg *motg) static bool msm_chg_check_primary_det(struct msm_otg *motg) { - struct usb_phy *otg = &motg->otg; + struct usb_phy *phy = &motg->phy; u32 chg_det; bool ret = false; switch (motg->pdata->phy_type) { case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(otg, 0x34); + chg_det = ulpi_read(phy, 0x34); ret = chg_det & (1 << 4); break; case SNPS_28NM_INTEGRATED_PHY: - chg_det = ulpi_read(otg, 0x87); + chg_det = ulpi_read(phy, 0x87); ret = chg_det & 1; break; default: @@ -882,23 +882,23 @@ static bool msm_chg_check_primary_det(struct msm_otg *motg) static void msm_chg_enable_primary_det(struct msm_otg *motg) { - struct usb_phy *otg = &motg->otg; + struct usb_phy *phy = &motg->phy; u32 chg_det; switch (motg->pdata->phy_type) { case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(otg, 0x34); + chg_det = ulpi_read(phy, 0x34); /* enable chg detection */ chg_det &= ~(1 << 0); - ulpi_write(otg, chg_det, 0x34); + ulpi_write(phy, chg_det, 0x34); break; case SNPS_28NM_INTEGRATED_PHY: /* * Configure DP as current source, DM as current sink * and enable battery charging comparators. */ - ulpi_write(otg, 0x2, 0x85); - ulpi_write(otg, 0x1, 0x85); + ulpi_write(phy, 0x2, 0x85); + ulpi_write(phy, 0x1, 0x85); break; default: break; @@ -907,17 +907,17 @@ static void msm_chg_enable_primary_det(struct msm_otg *motg) static bool msm_chg_check_dcd(struct msm_otg *motg) { - struct usb_phy *otg = &motg->otg; + struct usb_phy *phy = &motg->phy; u32 line_state; bool ret = false; switch (motg->pdata->phy_type) { case CI_45NM_INTEGRATED_PHY: - line_state = ulpi_read(otg, 0x15); + line_state = ulpi_read(phy, 0x15); ret = !(line_state & 1); break; case SNPS_28NM_INTEGRATED_PHY: - line_state = ulpi_read(otg, 0x87); + line_state = ulpi_read(phy, 0x87); ret = line_state & 2; break; default: @@ -928,17 +928,17 @@ static bool msm_chg_check_dcd(struct msm_otg *motg) static void msm_chg_disable_dcd(struct msm_otg *motg) { - struct usb_phy *otg = &motg->otg; + struct usb_phy *phy = &motg->phy; u32 chg_det; switch (motg->pdata->phy_type) { case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(otg, 0x34); + chg_det = ulpi_read(phy, 0x34); chg_det &= ~(1 << 5); - ulpi_write(otg, chg_det, 0x34); + ulpi_write(phy, chg_det, 0x34); break; case SNPS_28NM_INTEGRATED_PHY: - ulpi_write(otg, 0x10, 0x86); + ulpi_write(phy, 0x10, 0x86); break; default: break; @@ -947,19 +947,19 @@ static void msm_chg_disable_dcd(struct msm_otg *motg) static void msm_chg_enable_dcd(struct msm_otg *motg) { - struct usb_phy *otg = &motg->otg; + struct usb_phy *phy = &motg->phy; u32 chg_det; switch (motg->pdata->phy_type) { case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(otg, 0x34); + chg_det = ulpi_read(phy, 0x34); /* Turn on D+ current source */ chg_det |= (1 << 5); - ulpi_write(otg, chg_det, 0x34); + ulpi_write(phy, chg_det, 0x34); break; case SNPS_28NM_INTEGRATED_PHY: /* Data contact detection enable */ - ulpi_write(otg, 0x10, 0x85); + ulpi_write(phy, 0x10, 0x85); break; default: break; @@ -968,32 +968,32 @@ static void msm_chg_enable_dcd(struct msm_otg *motg) static void msm_chg_block_on(struct msm_otg *motg) { - struct usb_phy *otg = &motg->otg; + struct usb_phy *phy = &motg->phy; u32 func_ctrl, chg_det; /* put the controller in non-driving mode */ - func_ctrl = ulpi_read(otg, ULPI_FUNC_CTRL); + func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL); func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; - ulpi_write(otg, func_ctrl, ULPI_FUNC_CTRL); + ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL); switch (motg->pdata->phy_type) { case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(otg, 0x34); + chg_det = ulpi_read(phy, 0x34); /* control chg block via ULPI */ chg_det &= ~(1 << 3); - ulpi_write(otg, chg_det, 0x34); + ulpi_write(phy, chg_det, 0x34); /* Turn on chg detect block */ chg_det &= ~(1 << 1); - ulpi_write(otg, chg_det, 0x34); + ulpi_write(phy, chg_det, 0x34); udelay(20); break; case SNPS_28NM_INTEGRATED_PHY: /* Clear charger detecting control bits */ - ulpi_write(otg, 0x3F, 0x86); + ulpi_write(phy, 0x3F, 0x86); /* Clear alt interrupt latch and enable bits */ - ulpi_write(otg, 0x1F, 0x92); - ulpi_write(otg, 0x1F, 0x95); + ulpi_write(phy, 0x1F, 0x92); + ulpi_write(phy, 0x1F, 0x95); udelay(100); break; default: @@ -1003,32 +1003,32 @@ static void msm_chg_block_on(struct msm_otg *motg) static void msm_chg_block_off(struct msm_otg *motg) { - struct usb_phy *otg = &motg->otg; + struct usb_phy *phy = &motg->phy; u32 func_ctrl, chg_det; switch (motg->pdata->phy_type) { case CI_45NM_INTEGRATED_PHY: - chg_det = ulpi_read(otg, 0x34); + chg_det = ulpi_read(phy, 0x34); /* Turn off charger block */ chg_det |= ~(1 << 1); - ulpi_write(otg, chg_det, 0x34); + ulpi_write(phy, chg_det, 0x34); break; case SNPS_28NM_INTEGRATED_PHY: /* Clear charger detecting control bits */ - ulpi_write(otg, 0x3F, 0x86); + ulpi_write(phy, 0x3F, 0x86); /* Clear alt interrupt latch and enable bits */ - ulpi_write(otg, 0x1F, 0x92); - ulpi_write(otg, 0x1F, 0x95); + ulpi_write(phy, 0x1F, 0x92); + ulpi_write(phy, 0x1F, 0x95); break; default: break; } /* put the controller in normal mode */ - func_ctrl = ulpi_read(otg, ULPI_FUNC_CTRL); + func_ctrl = ulpi_read(phy, ULPI_FUNC_CTRL); func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL; - ulpi_write(otg, func_ctrl, ULPI_FUNC_CTRL); + ulpi_write(phy, func_ctrl, ULPI_FUNC_CTRL); } #define MSM_CHG_DCD_POLL_TIME (100 * HZ/1000) /* 100 msec */ @@ -1038,14 +1038,14 @@ static void msm_chg_block_off(struct msm_otg *motg) static void msm_chg_detect_work(struct work_struct *w) { struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work); - struct usb_phy *otg = &motg->otg; + struct usb_phy *phy = &motg->phy; bool is_dcd, tmout, vout; unsigned long delay; - dev_dbg(otg->dev, "chg detection work\n"); + dev_dbg(phy->dev, "chg detection work\n"); switch (motg->chg_state) { case USB_CHG_STATE_UNDEFINED: - pm_runtime_get_sync(otg->dev); + pm_runtime_get_sync(phy->dev); msm_chg_block_on(motg); msm_chg_enable_dcd(motg); motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; @@ -1088,7 +1088,7 @@ static void msm_chg_detect_work(struct work_struct *w) motg->chg_state = USB_CHG_STATE_DETECTED; case USB_CHG_STATE_DETECTED: msm_chg_block_off(motg); - dev_dbg(otg->dev, "charger = %d\n", motg->chg_type); + dev_dbg(phy->dev, "charger = %d\n", motg->chg_type); schedule_work(&motg->sm_work); return; default: @@ -1152,22 +1152,22 @@ static void msm_otg_init_sm(struct msm_otg *motg) static void msm_otg_sm_work(struct work_struct *w) { struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); - struct usb_phy *otg = &motg->otg; + struct usb_otg *otg = motg->phy.otg; - switch (otg->state) { + switch (otg->phy->state) { case OTG_STATE_UNDEFINED: - dev_dbg(otg->dev, "OTG_STATE_UNDEFINED state\n"); - msm_otg_reset(otg); + dev_dbg(otg->phy->dev, "OTG_STATE_UNDEFINED state\n"); + msm_otg_reset(otg->phy); msm_otg_init_sm(motg); - otg->state = OTG_STATE_B_IDLE; + otg->phy->state = OTG_STATE_B_IDLE; /* FALL THROUGH */ case OTG_STATE_B_IDLE: - dev_dbg(otg->dev, "OTG_STATE_B_IDLE state\n"); + dev_dbg(otg->phy->dev, "OTG_STATE_B_IDLE state\n"); if (!test_bit(ID, &motg->inputs) && otg->host) { /* disable BSV bit */ writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC); - msm_otg_start_host(otg, 1); - otg->state = OTG_STATE_A_HOST; + msm_otg_start_host(otg->phy, 1); + otg->phy->state = OTG_STATE_A_HOST; } else if (test_bit(B_SESS_VLD, &motg->inputs)) { switch (motg->chg_state) { case USB_CHG_STATE_UNDEFINED: @@ -1182,13 +1182,15 @@ static void msm_otg_sm_work(struct work_struct *w) case USB_CDP_CHARGER: msm_otg_notify_charger(motg, IDEV_CHG_MAX); - msm_otg_start_peripheral(otg, 1); - otg->state = OTG_STATE_B_PERIPHERAL; + msm_otg_start_peripheral(otg->phy, 1); + otg->phy->state + = OTG_STATE_B_PERIPHERAL; break; case USB_SDP_CHARGER: msm_otg_notify_charger(motg, IUNIT); - msm_otg_start_peripheral(otg, 1); - otg->state = OTG_STATE_B_PERIPHERAL; + msm_otg_start_peripheral(otg->phy, 1); + otg->phy->state + = OTG_STATE_B_PERIPHERAL; break; default: break; @@ -1204,34 +1206,34 @@ static void msm_otg_sm_work(struct work_struct *w) * is incremented in charger detection work. */ if (cancel_delayed_work_sync(&motg->chg_work)) { - pm_runtime_put_sync(otg->dev); - msm_otg_reset(otg); + pm_runtime_put_sync(otg->phy->dev); + msm_otg_reset(otg->phy); } msm_otg_notify_charger(motg, 0); motg->chg_state = USB_CHG_STATE_UNDEFINED; motg->chg_type = USB_INVALID_CHARGER; } - pm_runtime_put_sync(otg->dev); + pm_runtime_put_sync(otg->phy->dev); break; case OTG_STATE_B_PERIPHERAL: - dev_dbg(otg->dev, "OTG_STATE_B_PERIPHERAL state\n"); + dev_dbg(otg->phy->dev, "OTG_STATE_B_PERIPHERAL state\n"); if (!test_bit(B_SESS_VLD, &motg->inputs) || !test_bit(ID, &motg->inputs)) { msm_otg_notify_charger(motg, 0); - msm_otg_start_peripheral(otg, 0); + msm_otg_start_peripheral(otg->phy, 0); motg->chg_state = USB_CHG_STATE_UNDEFINED; motg->chg_type = USB_INVALID_CHARGER; - otg->state = OTG_STATE_B_IDLE; - msm_otg_reset(otg); + otg->phy->state = OTG_STATE_B_IDLE; + msm_otg_reset(otg->phy); schedule_work(w); } break; case OTG_STATE_A_HOST: - dev_dbg(otg->dev, "OTG_STATE_A_HOST state\n"); + dev_dbg(otg->phy->dev, "OTG_STATE_A_HOST state\n"); if (test_bit(ID, &motg->inputs)) { - msm_otg_start_host(otg, 0); - otg->state = OTG_STATE_B_IDLE; - msm_otg_reset(otg); + msm_otg_start_host(otg->phy, 0); + otg->phy->state = OTG_STATE_B_IDLE; + msm_otg_reset(otg->phy); schedule_work(w); } break; @@ -1243,13 +1245,13 @@ static void msm_otg_sm_work(struct work_struct *w) static irqreturn_t msm_otg_irq(int irq, void *data) { struct msm_otg *motg = data; - struct usb_phy *otg = &motg->otg; + struct usb_phy *phy = &motg->phy; u32 otgsc = 0; if (atomic_read(&motg->in_lpm)) { disable_irq_nosync(irq); motg->async_int = 1; - pm_runtime_get(otg->dev); + pm_runtime_get(phy->dev); return IRQ_HANDLED; } @@ -1262,15 +1264,15 @@ static irqreturn_t msm_otg_irq(int irq, void *data) set_bit(ID, &motg->inputs); else clear_bit(ID, &motg->inputs); - dev_dbg(otg->dev, "ID set/clear\n"); - pm_runtime_get_noresume(otg->dev); + dev_dbg(phy->dev, "ID set/clear\n"); + pm_runtime_get_noresume(phy->dev); } else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) { if (otgsc & OTGSC_BSV) set_bit(B_SESS_VLD, &motg->inputs); else clear_bit(B_SESS_VLD, &motg->inputs); - dev_dbg(otg->dev, "BSV set/clear\n"); - pm_runtime_get_noresume(otg->dev); + dev_dbg(phy->dev, "BSV set/clear\n"); + pm_runtime_get_noresume(phy->dev); } writel(otgsc, USB_OTGSC); @@ -1281,9 +1283,9 @@ static irqreturn_t msm_otg_irq(int irq, void *data) static int msm_otg_mode_show(struct seq_file *s, void *unused) { struct msm_otg *motg = s->private; - struct usb_phy *otg = &motg->otg; + struct usb_otg *otg = motg->phy.otg; - switch (otg->state) { + switch (otg->phy->state) { case OTG_STATE_A_HOST: seq_printf(s, "host\n"); break; @@ -1309,7 +1311,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, struct seq_file *s = file->private_data; struct msm_otg *motg = s->private; char buf[16]; - struct usb_phy *otg = &motg->otg; + struct usb_otg *otg = motg->phy.otg; int status = count; enum usb_mode_type req_mode; @@ -1333,7 +1335,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, switch (req_mode) { case USB_NONE: - switch (otg->state) { + switch (otg->phy->state) { case OTG_STATE_A_HOST: case OTG_STATE_B_PERIPHERAL: set_bit(ID, &motg->inputs); @@ -1344,7 +1346,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, } break; case USB_PERIPHERAL: - switch (otg->state) { + switch (otg->phy->state) { case OTG_STATE_B_IDLE: case OTG_STATE_A_HOST: set_bit(ID, &motg->inputs); @@ -1355,7 +1357,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, } break; case USB_HOST: - switch (otg->state) { + switch (otg->phy->state) { case OTG_STATE_B_IDLE: case OTG_STATE_B_PERIPHERAL: clear_bit(ID, &motg->inputs); @@ -1368,7 +1370,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, goto out; } - pm_runtime_get_sync(otg->dev); + pm_runtime_get_sync(otg->phy->dev); schedule_work(&motg->sm_work); out: return status; @@ -1414,7 +1416,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) int ret = 0; struct resource *res; struct msm_otg *motg; - struct usb_phy *otg; + struct usb_phy *phy; dev_info(&pdev->dev, "msm_otg probe\n"); if (!pdev->dev.platform_data) { @@ -1428,9 +1430,15 @@ static int __init msm_otg_probe(struct platform_device *pdev) return -ENOMEM; } + motg->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); + if (!motg->phy.otg) { + dev_err(&pdev->dev, "unable to allocate msm_otg\n"); + return -ENOMEM; + } + motg->pdata = pdev->dev.platform_data; - otg = &motg->otg; - otg->dev = &pdev->dev; + phy = &motg->phy; + phy->dev = &pdev->dev; motg->phy_reset_clk = clk_get(&pdev->dev, "usb_phy_clk"); if (IS_ERR(motg->phy_reset_clk)) { @@ -1538,16 +1546,18 @@ static int __init msm_otg_probe(struct platform_device *pdev) goto disable_clks; } - otg->init = msm_otg_reset; - otg->set_host = msm_otg_set_host; - otg->set_peripheral = msm_otg_set_peripheral; - otg->set_power = msm_otg_set_power; + phy->init = msm_otg_reset; + phy->set_power = msm_otg_set_power; + + phy->io_ops = &msm_otg_io_ops; - otg->io_ops = &msm_otg_io_ops; + phy->otg->phy = &motg->phy; + phy->otg->set_host = msm_otg_set_host; + phy->otg->set_peripheral = msm_otg_set_peripheral; - ret = otg_set_transceiver(&motg->otg); + ret = usb_set_transceiver(&motg->phy); if (ret) { - dev_err(&pdev->dev, "otg_set_transceiver failed\n"); + dev_err(&pdev->dev, "usb_set_transceiver failed\n"); goto free_irq; } @@ -1591,6 +1601,7 @@ put_clk: put_phy_reset_clk: clk_put(motg->phy_reset_clk); free_motg: + kfree(motg->phy.otg); kfree(motg); return ret; } @@ -1598,10 +1609,10 @@ free_motg: static int __devexit msm_otg_remove(struct platform_device *pdev) { struct msm_otg *motg = platform_get_drvdata(pdev); - struct usb_phy *otg = &motg->otg; + struct usb_phy *phy = &motg->phy; int cnt = 0; - if (otg->host || otg->gadget) + if (phy->otg->host || phy->otg->gadget) return -EBUSY; msm_otg_debugfs_cleanup(); @@ -1613,14 +1624,14 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); pm_runtime_disable(&pdev->dev); - otg_set_transceiver(NULL); + usb_set_transceiver(NULL); free_irq(motg->irq, motg); /* * Put PHY in low power mode. */ - ulpi_read(otg, 0x14); - ulpi_write(otg, 0x08, 0x09); + ulpi_read(phy, 0x14); + ulpi_write(phy, 0x08, 0x09); writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { @@ -1630,7 +1641,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) cnt++; } if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) - dev_err(otg->dev, "Unable to suspend PHY\n"); + dev_err(phy->dev, "Unable to suspend PHY\n"); clk_disable(motg->pclk); clk_disable(motg->clk); @@ -1651,6 +1662,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) if (motg->core_clk) clk_put(motg->core_clk); + kfree(motg->phy.otg); kfree(motg); return 0; @@ -1660,7 +1672,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) static int msm_otg_runtime_idle(struct device *dev) { struct msm_otg *motg = dev_get_drvdata(dev); - struct usb_phy *otg = &motg->otg; + struct usb_otg *otg = motg->phy.otg; dev_dbg(dev, "OTG runtime idle\n"); @@ -1670,7 +1682,7 @@ static int msm_otg_runtime_idle(struct device *dev) * This 1 sec delay also prevents entering into LPM immediately * after asynchronous interrupt. */ - if (otg->state != OTG_STATE_UNDEFINED) + if (otg->phy->state != OTG_STATE_UNDEFINED) pm_schedule_suspend(dev, 1000); return -EAGAIN; diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 2d3547ae9562..22a396c13f3a 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -160,7 +160,7 @@ struct msm_otg_platform_data { * detection process. */ struct msm_otg { - struct usb_phy otg; + struct usb_phy phy; struct msm_otg_platform_data *pdata; int irq; struct clk *clk; -- cgit From 298b083cf9dd2efd9bb7020107ab0077135051e0 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 13 Feb 2012 13:24:13 +0200 Subject: usb: otg: ulpi: Start using struct usb_otg Use struct usb_otg members with OTG specific functions instead of usb_phy members. Signed-off-by: Heikki Krogerus Acked-by: Igor Grinberg Acked-by: Sascha Hauer Reviewed-by: Marek Vasut Signed-off-by: Felipe Balbi --- arch/arm/mach-pxa/pxa3xx-ulpi.c | 6 +- arch/arm/plat-mxc/include/mach/ulpi.h | 2 +- arch/arm/plat-mxc/ulpi.c | 2 +- drivers/usb/otg/ulpi.c | 114 +++++++++++++++++++--------------- drivers/usb/otg/ulpi_viewport.c | 2 +- include/linux/usb/ulpi.h | 4 +- 6 files changed, 71 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-pxa/pxa3xx-ulpi.c b/arch/arm/mach-pxa/pxa3xx-ulpi.c index 960d0ac50418..a13f33565623 100644 --- a/arch/arm/mach-pxa/pxa3xx-ulpi.c +++ b/arch/arm/mach-pxa/pxa3xx-ulpi.c @@ -111,7 +111,7 @@ static int pxa310_ulpi_write(struct usb_phy *otg, u32 val, u32 reg) return pxa310_ulpi_poll(); } -struct otg_io_access_ops pxa310_ulpi_access_ops = { +struct usb_phy_io_ops pxa310_ulpi_access_ops = { .read = pxa310_ulpi_read, .write = pxa310_ulpi_write, }; @@ -139,7 +139,7 @@ static int pxa310_start_otg_host_transcvr(struct usb_bus *host) pxa310_otg_transceiver_rtsm(); - err = otg_init(u2d->otg); + err = usb_phy_init(u2d->otg); if (err) { pr_err("OTG transceiver init failed"); return err; @@ -191,7 +191,7 @@ static void pxa310_stop_otg_hc(void) otg_set_host(u2d->otg, NULL); otg_set_vbus(u2d->otg, 0); - otg_shutdown(u2d->otg); + usb_phy_shutdown(u2d->otg); } static void pxa310_u2d_setup_otg_hc(void) diff --git a/arch/arm/plat-mxc/include/mach/ulpi.h b/arch/arm/plat-mxc/include/mach/ulpi.h index d39d94a170e7..42bdaca6d7d9 100644 --- a/arch/arm/plat-mxc/include/mach/ulpi.h +++ b/arch/arm/plat-mxc/include/mach/ulpi.h @@ -10,7 +10,7 @@ static inline struct usb_phy *imx_otg_ulpi_create(unsigned int flags) } #endif -extern struct otg_io_access_ops mxc_ulpi_access_ops; +extern struct usb_phy_io_ops mxc_ulpi_access_ops; #endif /* __MACH_ULPI_H */ diff --git a/arch/arm/plat-mxc/ulpi.c b/arch/arm/plat-mxc/ulpi.c index 8eeeb6b979c4..d2963427184f 100644 --- a/arch/arm/plat-mxc/ulpi.c +++ b/arch/arm/plat-mxc/ulpi.c @@ -106,7 +106,7 @@ static int ulpi_write(struct usb_phy *otg, u32 val, u32 reg) return ulpi_poll(view, ULPIVW_RUN); } -struct otg_io_access_ops mxc_ulpi_access_ops = { +struct usb_phy_io_ops mxc_ulpi_access_ops = { .read = ulpi_read, .write = ulpi_write, }; diff --git a/drivers/usb/otg/ulpi.c b/drivers/usb/otg/ulpi.c index 51841ed829ab..217339dd7a90 100644 --- a/drivers/usb/otg/ulpi.c +++ b/drivers/usb/otg/ulpi.c @@ -49,31 +49,31 @@ static struct ulpi_info ulpi_ids[] = { ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"), }; -static int ulpi_set_otg_flags(struct usb_phy *otg) +static int ulpi_set_otg_flags(struct usb_phy *phy) { unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN; - if (otg->flags & ULPI_OTG_ID_PULLUP) + if (phy->flags & ULPI_OTG_ID_PULLUP) flags |= ULPI_OTG_CTRL_ID_PULLUP; /* * ULPI Specification rev.1.1 default * for Dp/DmPulldown is enabled. */ - if (otg->flags & ULPI_OTG_DP_PULLDOWN_DIS) + if (phy->flags & ULPI_OTG_DP_PULLDOWN_DIS) flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN; - if (otg->flags & ULPI_OTG_DM_PULLDOWN_DIS) + if (phy->flags & ULPI_OTG_DM_PULLDOWN_DIS) flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN; - if (otg->flags & ULPI_OTG_EXTVBUSIND) + if (phy->flags & ULPI_OTG_EXTVBUSIND) flags |= ULPI_OTG_CTRL_EXTVBUSIND; - return otg_io_write(otg, flags, ULPI_OTG_CTRL); + return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); } -static int ulpi_set_fc_flags(struct usb_phy *otg) +static int ulpi_set_fc_flags(struct usb_phy *phy) { unsigned int flags = 0; @@ -81,27 +81,27 @@ static int ulpi_set_fc_flags(struct usb_phy *otg) * ULPI Specification rev.1.1 default * for XcvrSelect is Full Speed. */ - if (otg->flags & ULPI_FC_HS) + if (phy->flags & ULPI_FC_HS) flags |= ULPI_FUNC_CTRL_HIGH_SPEED; - else if (otg->flags & ULPI_FC_LS) + else if (phy->flags & ULPI_FC_LS) flags |= ULPI_FUNC_CTRL_LOW_SPEED; - else if (otg->flags & ULPI_FC_FS4LS) + else if (phy->flags & ULPI_FC_FS4LS) flags |= ULPI_FUNC_CTRL_FS4LS; else flags |= ULPI_FUNC_CTRL_FULL_SPEED; - if (otg->flags & ULPI_FC_TERMSEL) + if (phy->flags & ULPI_FC_TERMSEL) flags |= ULPI_FUNC_CTRL_TERMSELECT; /* * ULPI Specification rev.1.1 default * for OpMode is Normal Operation. */ - if (otg->flags & ULPI_FC_OP_NODRV) + if (phy->flags & ULPI_FC_OP_NODRV) flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; - else if (otg->flags & ULPI_FC_OP_DIS_NRZI) + else if (phy->flags & ULPI_FC_OP_DIS_NRZI) flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI; - else if (otg->flags & ULPI_FC_OP_NSYNC_NEOP) + else if (phy->flags & ULPI_FC_OP_NSYNC_NEOP) flags |= ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP; else flags |= ULPI_FUNC_CTRL_OPMODE_NORMAL; @@ -112,54 +112,54 @@ static int ulpi_set_fc_flags(struct usb_phy *otg) */ flags |= ULPI_FUNC_CTRL_SUSPENDM; - return otg_io_write(otg, flags, ULPI_FUNC_CTRL); + return usb_phy_io_write(phy, flags, ULPI_FUNC_CTRL); } -static int ulpi_set_ic_flags(struct usb_phy *otg) +static int ulpi_set_ic_flags(struct usb_phy *phy) { unsigned int flags = 0; - if (otg->flags & ULPI_IC_AUTORESUME) + if (phy->flags & ULPI_IC_AUTORESUME) flags |= ULPI_IFC_CTRL_AUTORESUME; - if (otg->flags & ULPI_IC_EXTVBUS_INDINV) + if (phy->flags & ULPI_IC_EXTVBUS_INDINV) flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS; - if (otg->flags & ULPI_IC_IND_PASSTHRU) + if (phy->flags & ULPI_IC_IND_PASSTHRU) flags |= ULPI_IFC_CTRL_PASSTHRU; - if (otg->flags & ULPI_IC_PROTECT_DIS) + if (phy->flags & ULPI_IC_PROTECT_DIS) flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE; - return otg_io_write(otg, flags, ULPI_IFC_CTRL); + return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL); } -static int ulpi_set_flags(struct usb_phy *otg) +static int ulpi_set_flags(struct usb_phy *phy) { int ret; - ret = ulpi_set_otg_flags(otg); + ret = ulpi_set_otg_flags(phy); if (ret) return ret; - ret = ulpi_set_ic_flags(otg); + ret = ulpi_set_ic_flags(phy); if (ret) return ret; - return ulpi_set_fc_flags(otg); + return ulpi_set_fc_flags(phy); } -static int ulpi_check_integrity(struct usb_phy *otg) +static int ulpi_check_integrity(struct usb_phy *phy) { int ret, i; unsigned int val = 0x55; for (i = 0; i < 2; i++) { - ret = otg_io_write(otg, val, ULPI_SCRATCH); + ret = usb_phy_io_write(phy, val, ULPI_SCRATCH); if (ret < 0) return ret; - ret = otg_io_read(otg, ULPI_SCRATCH); + ret = usb_phy_io_read(phy, ULPI_SCRATCH); if (ret < 0) return ret; @@ -175,13 +175,13 @@ static int ulpi_check_integrity(struct usb_phy *otg) return 0; } -static int ulpi_init(struct usb_phy *otg) +static int ulpi_init(struct usb_phy *phy) { int i, vid, pid, ret; u32 ulpi_id = 0; for (i = 0; i < 4; i++) { - ret = otg_io_read(otg, ULPI_PRODUCT_ID_HIGH - i); + ret = usb_phy_io_read(phy, ULPI_PRODUCT_ID_HIGH - i); if (ret < 0) return ret; ulpi_id = (ulpi_id << 8) | ret; @@ -199,16 +199,17 @@ static int ulpi_init(struct usb_phy *otg) } } - ret = ulpi_check_integrity(otg); + ret = ulpi_check_integrity(phy); if (ret) return ret; - return ulpi_set_flags(otg); + return ulpi_set_flags(phy); } -static int ulpi_set_host(struct usb_phy *otg, struct usb_bus *host) +static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host) { - unsigned int flags = otg_io_read(otg, ULPI_IFC_CTRL); + struct usb_phy *phy = otg->phy; + unsigned int flags = usb_phy_io_read(phy, ULPI_IFC_CTRL); if (!host) { otg->host = NULL; @@ -221,51 +222,62 @@ static int ulpi_set_host(struct usb_phy *otg, struct usb_bus *host) ULPI_IFC_CTRL_3_PIN_SERIAL_MODE | ULPI_IFC_CTRL_CARKITMODE); - if (otg->flags & ULPI_IC_6PIN_SERIAL) + if (phy->flags & ULPI_IC_6PIN_SERIAL) flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE; - else if (otg->flags & ULPI_IC_3PIN_SERIAL) + else if (phy->flags & ULPI_IC_3PIN_SERIAL) flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE; - else if (otg->flags & ULPI_IC_CARKIT) + else if (phy->flags & ULPI_IC_CARKIT) flags |= ULPI_IFC_CTRL_CARKITMODE; - return otg_io_write(otg, flags, ULPI_IFC_CTRL); + return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL); } -static int ulpi_set_vbus(struct usb_phy *otg, bool on) +static int ulpi_set_vbus(struct usb_otg *otg, bool on) { - unsigned int flags = otg_io_read(otg, ULPI_OTG_CTRL); + struct usb_phy *phy = otg->phy; + unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL); flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT); if (on) { - if (otg->flags & ULPI_OTG_DRVVBUS) + if (phy->flags & ULPI_OTG_DRVVBUS) flags |= ULPI_OTG_CTRL_DRVVBUS; - if (otg->flags & ULPI_OTG_DRVVBUS_EXT) + if (phy->flags & ULPI_OTG_DRVVBUS_EXT) flags |= ULPI_OTG_CTRL_DRVVBUS_EXT; } - return otg_io_write(otg, flags, ULPI_OTG_CTRL); + return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL); } struct usb_phy * -otg_ulpi_create(struct otg_io_access_ops *ops, +otg_ulpi_create(struct usb_phy_io_ops *ops, unsigned int flags) { - struct usb_phy *otg; + struct usb_phy *phy; + struct usb_otg *otg; + + phy = kzalloc(sizeof(*phy), GFP_KERNEL); + if (!phy) + return NULL; otg = kzalloc(sizeof(*otg), GFP_KERNEL); - if (!otg) + if (!otg) { + kfree(phy); return NULL; + } + + phy->label = "ULPI"; + phy->flags = flags; + phy->io_ops = ops; + phy->otg = otg; + phy->init = ulpi_init; - otg->label = "ULPI"; - otg->flags = flags; - otg->io_ops = ops; - otg->init = ulpi_init; + otg->phy = phy; otg->set_host = ulpi_set_host; otg->set_vbus = ulpi_set_vbus; - return otg; + return phy; } EXPORT_SYMBOL_GPL(otg_ulpi_create); diff --git a/drivers/usb/otg/ulpi_viewport.c b/drivers/usb/otg/ulpi_viewport.c index e7b311b960bd..c5ba7e5423fc 100644 --- a/drivers/usb/otg/ulpi_viewport.c +++ b/drivers/usb/otg/ulpi_viewport.c @@ -74,7 +74,7 @@ static int ulpi_viewport_write(struct usb_phy *otg, u32 val, u32 reg) return ulpi_viewport_wait(view, ULPI_VIEW_RUN); } -struct otg_io_access_ops ulpi_viewport_access_ops = { +struct usb_phy_io_ops ulpi_viewport_access_ops = { .read = ulpi_viewport_read, .write = ulpi_viewport_write, }; diff --git a/include/linux/usb/ulpi.h b/include/linux/usb/ulpi.h index 51ebf72bc449..6f033a415ecb 100644 --- a/include/linux/usb/ulpi.h +++ b/include/linux/usb/ulpi.h @@ -181,12 +181,12 @@ /*-------------------------------------------------------------------------*/ -struct usb_phy *otg_ulpi_create(struct otg_io_access_ops *ops, +struct usb_phy *otg_ulpi_create(struct usb_phy_io_ops *ops, unsigned int flags); #ifdef CONFIG_USB_ULPI_VIEWPORT /* access ops for controllers with a viewport register */ -extern struct otg_io_access_ops ulpi_viewport_access_ops; +extern struct usb_phy_io_ops ulpi_viewport_access_ops; #endif #endif /* __LINUX_USB_ULPI_H */ -- cgit From 2fbb90db1b8fcc78f43830f1a009f3af243c5f42 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 7 Feb 2012 09:32:43 -0500 Subject: tracing/rcu: Add trace_##name##__rcuidle() static tracepoint for inside rcu_idle_exit() sections Added is a new static inline function that lets *any* tracepoint be used inside a rcu_idle_exit() section. And this also solves the problem where the same tracepoint may be used inside a rcu_idle_exit() section as well as outside of one. I added a new tracepoint function with a "_rcuidle" extension. All tracepoints can be used with either the normal "trace_foobar()" function, or the "trace_foobar_rcuidle()" function when inside a rcu_idle_exit() section. All tracepoints defined by TRACE_EVENT() or any of the derivatives will have a "_rcuidle()" function also defined. When a tracepoint is used within an rcu_idle_exit() section, the "_rcuidle()" version must be used. This denotes that the tracepoint is within rcu_idle_exit() and it allows the rcu read locks within the tracepoint to still be valid, as this version takes us out of rcu_idle_exit(). Another nice aspect about this patch is that "static inline"s are not compiled into text when not used. So only the tracepoints that actually use the _rcuidle() version will have them defined in the actual text that is booted. Link: http://lkml.kernel.org/r/1328563113.2200.39.camel@gandalf.stny.rr.com> Acked-by: Paul E. McKenney Reviewed-by: Josh Triplett Signed-off-by: Steven Rostedt --- include/linux/tracepoint.h | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index df0a779c1bbd..fc36da97ff7e 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -114,7 +114,7 @@ static inline void tracepoint_synchronize_unregister(void) * as "(void *, void)". The DECLARE_TRACE_NOARGS() will pass in just * "void *data", where as the DECLARE_TRACE() will pass in "void *data, proto". */ -#define __DO_TRACE(tp, proto, args, cond) \ +#define __DO_TRACE(tp, proto, args, cond, prercu, postrcu) \ do { \ struct tracepoint_func *it_func_ptr; \ void *it_func; \ @@ -122,6 +122,7 @@ static inline void tracepoint_synchronize_unregister(void) \ if (!(cond)) \ return; \ + prercu; \ rcu_read_lock_sched_notrace(); \ it_func_ptr = rcu_dereference_sched((tp)->funcs); \ if (it_func_ptr) { \ @@ -132,6 +133,7 @@ static inline void tracepoint_synchronize_unregister(void) } while ((++it_func_ptr)->func); \ } \ rcu_read_unlock_sched_notrace(); \ + postrcu; \ } while (0) /* @@ -139,7 +141,7 @@ static inline void tracepoint_synchronize_unregister(void) * not add unwanted padding between the beginning of the section and the * structure. Force alignment to the same alignment as the section start. */ -#define __DECLARE_TRACE(name, proto, args, cond, data_proto, data_args) \ +#define __DECLARE_TRACE(name, proto, args, cond, data_proto, data_args) \ extern struct tracepoint __tracepoint_##name; \ static inline void trace_##name(proto) \ { \ @@ -147,7 +149,17 @@ static inline void tracepoint_synchronize_unregister(void) __DO_TRACE(&__tracepoint_##name, \ TP_PROTO(data_proto), \ TP_ARGS(data_args), \ - TP_CONDITION(cond)); \ + TP_CONDITION(cond),,); \ + } \ + static inline void trace_##name##_rcuidle(proto) \ + { \ + if (static_branch(&__tracepoint_##name.key)) \ + __DO_TRACE(&__tracepoint_##name, \ + TP_PROTO(data_proto), \ + TP_ARGS(data_args), \ + TP_CONDITION(cond), \ + rcu_idle_exit(), \ + rcu_idle_enter()); \ } \ static inline int \ register_trace_##name(void (*probe)(data_proto), void *data) \ @@ -190,9 +202,11 @@ static inline void tracepoint_synchronize_unregister(void) EXPORT_SYMBOL(__tracepoint_##name) #else /* !CONFIG_TRACEPOINTS */ -#define __DECLARE_TRACE(name, proto, args, cond, data_proto, data_args) \ +#define __DECLARE_TRACE(name, proto, args, cond, data_proto, data_args) \ static inline void trace_##name(proto) \ { } \ + static inline void trace_##name##_rcuidle(proto) \ + { } \ static inline int \ register_trace_##name(void (*probe)(data_proto), \ void *data) \ -- cgit From 484546509ce5d49d43ec0a6eb2141c6bf3362bfc Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 7 Feb 2012 09:40:30 -0500 Subject: x86/tracing: Denote the power and cpuidle tracepoints as _rcuidle() The power and cpuidle tracepoints are called within a rcu_idle_exit() section, and must be denoted with the _rcuidle() version of the tracepoint. Acked-by: Paul E. McKenney Reviewed-by: Josh Triplett Signed-off-by: Steven Rostedt --- arch/x86/kernel/process.c | 24 ++++++++++++------------ include/trace/events/power.h | 2 ++ 2 files changed, 14 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 15763af7bfe3..44eefde92109 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -377,8 +377,8 @@ static inline int hlt_use_halt(void) void default_idle(void) { if (hlt_use_halt()) { - trace_power_start(POWER_CSTATE, 1, smp_processor_id()); - trace_cpu_idle(1, smp_processor_id()); + trace_power_start_rcuidle(POWER_CSTATE, 1, smp_processor_id()); + trace_cpu_idle_rcuidle(1, smp_processor_id()); current_thread_info()->status &= ~TS_POLLING; /* * TS_POLLING-cleared state must be visible before we @@ -391,8 +391,8 @@ void default_idle(void) else local_irq_enable(); current_thread_info()->status |= TS_POLLING; - trace_power_end(smp_processor_id()); - trace_cpu_idle(PWR_EVENT_EXIT, smp_processor_id()); + trace_power_end_rcuidle(smp_processor_id()); + trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id()); } else { local_irq_enable(); /* loop is done by the caller */ @@ -450,8 +450,8 @@ EXPORT_SYMBOL_GPL(cpu_idle_wait); static void mwait_idle(void) { if (!need_resched()) { - trace_power_start(POWER_CSTATE, 1, smp_processor_id()); - trace_cpu_idle(1, smp_processor_id()); + trace_power_start_rcuidle(POWER_CSTATE, 1, smp_processor_id()); + trace_cpu_idle_rcuidle(1, smp_processor_id()); if (this_cpu_has(X86_FEATURE_CLFLUSH_MONITOR)) clflush((void *)¤t_thread_info()->flags); @@ -461,8 +461,8 @@ static void mwait_idle(void) __sti_mwait(0, 0); else local_irq_enable(); - trace_power_end(smp_processor_id()); - trace_cpu_idle(PWR_EVENT_EXIT, smp_processor_id()); + trace_power_end_rcuidle(smp_processor_id()); + trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id()); } else local_irq_enable(); } @@ -474,13 +474,13 @@ static void mwait_idle(void) */ static void poll_idle(void) { - trace_power_start(POWER_CSTATE, 0, smp_processor_id()); - trace_cpu_idle(0, smp_processor_id()); + trace_power_start_rcuidle(POWER_CSTATE, 0, smp_processor_id()); + trace_cpu_idle_rcuidle(0, smp_processor_id()); local_irq_enable(); while (!need_resched()) cpu_relax(); - trace_power_end(smp_processor_id()); - trace_cpu_idle(PWR_EVENT_EXIT, smp_processor_id()); + trace_power_end_rcuidle(smp_processor_id()); + trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id()); } /* diff --git a/include/trace/events/power.h b/include/trace/events/power.h index 1bcc2a8c00e2..14b38940062b 100644 --- a/include/trace/events/power.h +++ b/include/trace/events/power.h @@ -151,6 +151,8 @@ enum { events get removed */ static inline void trace_power_start(u64 type, u64 state, u64 cpuid) {}; static inline void trace_power_end(u64 cpuid) {}; +static inline void trace_power_start_rcuidle(u64 type, u64 state, u64 cpuid) {}; +static inline void trace_power_end_rcuidle(u64 cpuid) {}; static inline void trace_power_frequency(u64 type, u64 state, u64 cpuid) {}; #endif /* _PWR_EVENT_AVOID_DOUBLE_DEFINING_DEPRECATED */ -- cgit From b57c1a5646739bfc273245dc738f2f12a2d4d3ec Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 3 Jan 2012 16:03:00 +0200 Subject: Bluetooth: Convert inquiry cache to use standard list types This makes it possible to use the convenience functions provided for standard kernel list types and it also makes it easier to extend the use of the cache for the management interface where e.g. name resolving control will be needed. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 10 ++++------ net/bluetooth/hci_core.c | 31 +++++++++++++++++-------------- net/bluetooth/hci_sysfs.c | 2 +- 3 files changed, 22 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ea9231f4935f..91d1baf05077 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -44,14 +44,14 @@ struct inquiry_data { }; struct inquiry_entry { - struct inquiry_entry *next; + struct list_head list; __u32 timestamp; struct inquiry_data data; }; struct inquiry_cache { + struct list_head list; __u32 timestamp; - struct inquiry_entry *list; }; struct hci_conn_hash { @@ -350,14 +350,12 @@ extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb); static inline void inquiry_cache_init(struct hci_dev *hdev) { - struct inquiry_cache *c = &hdev->inq_cache; - c->list = NULL; + INIT_LIST_HEAD(&hdev->inq_cache.list); } static inline int inquiry_cache_empty(struct hci_dev *hdev) { - struct inquiry_cache *c = &hdev->inq_cache; - return c->list == NULL; + return list_empty(&hdev->inq_cache.list); } static inline long inquiry_cache_age(struct hci_dev *hdev) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 845da3ee56a0..feeea4df2529 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -357,15 +357,11 @@ struct hci_dev *hci_dev_get(int index) /* ---- Inquiry support ---- */ static void inquiry_cache_flush(struct hci_dev *hdev) { - struct inquiry_cache *cache = &hdev->inq_cache; - struct inquiry_entry *next = cache->list, *e; - - BT_DBG("cache %p", cache); + struct inquiry_entry *p, *n; - cache->list = NULL; - while ((e = next)) { - next = e->next; - kfree(e); + list_for_each_entry_safe(p, n, &hdev->inq_cache.list, list) { + list_del(&p->list); + kfree(p); } } @@ -376,10 +372,12 @@ struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *b BT_DBG("cache %p, %s", cache, batostr(bdaddr)); - for (e = cache->list; e; e = e->next) + list_for_each_entry(e, &cache->list, list) { if (!bacmp(&e->data.bdaddr, bdaddr)) - break; - return e; + return e; + } + + return NULL; } void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data) @@ -396,8 +394,7 @@ void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data) if (!ie) return; - ie->next = cache->list; - cache->list = ie; + list_add(&ie->list, &cache->list); } memcpy(&ie->data, data, sizeof(*data)); @@ -412,15 +409,21 @@ static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf) struct inquiry_entry *e; int copied = 0; - for (e = cache->list; e && copied < num; e = e->next, copied++) { + list_for_each_entry(e, &cache->list, list) { struct inquiry_data *data = &e->data; + + if (copied >= num) + break; + bacpy(&info->bdaddr, &data->bdaddr); info->pscan_rep_mode = data->pscan_rep_mode; info->pscan_period_mode = data->pscan_period_mode; info->pscan_mode = data->pscan_mode; memcpy(info->dev_class, data->dev_class, 3); info->clock_offset = data->clock_offset; + info++; + copied++; } BT_DBG("cache %p, copied %d", cache, copied); diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 521095614235..ed9cceeec7be 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -388,7 +388,7 @@ static int inquiry_cache_show(struct seq_file *f, void *p) hci_dev_lock(hdev); - for (e = cache->list; e; e = e->next) { + list_for_each_entry(e, &cache->list, list) { struct inquiry_data *data = &e->data; seq_printf(f, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", batostr(&data->bdaddr), -- cgit From 32748db00228b67a5315a91e1a6dd2c54864d87b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 30 Dec 2011 14:57:23 +0200 Subject: Bluetooth: Move Extended Inquiry Response defines to hci.h The EIR defines are needed also outside of mgmt.c (e.g. in hci_event.c to check if EIR data has the complete name) so it's better to have them in a single public place, i.e. hci.h. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 13 +++++++++++++ net/bluetooth/mgmt.c | 12 ------------ 2 files changed, 13 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 5b2fed5eebf2..ce5133b22445 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -284,6 +284,19 @@ enum { #define HCI_FLOW_CTL_MODE_PACKET_BASED 0x00 #define HCI_FLOW_CTL_MODE_BLOCK_BASED 0x01 +/* Extended Inquiry Response field types */ +#define EIR_FLAGS 0x01 /* flags */ +#define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */ +#define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */ +#define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */ +#define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */ +#define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */ +#define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */ +#define EIR_NAME_SHORT 0x08 /* shortened local name */ +#define EIR_NAME_COMPLETE 0x09 /* complete local name */ +#define EIR_TX_POWER 0x0A /* transmit power level */ +#define EIR_DEVICE_ID 0x10 /* device ID */ + /* ----- HCI Commands ---- */ #define HCI_OP_NOP 0x0000 diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bc8e59dda78e..851cb19c55b1 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -303,18 +303,6 @@ static u32 get_current_settings(struct hci_dev *hdev) return settings; } -#define EIR_FLAGS 0x01 /* flags */ -#define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */ -#define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */ -#define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */ -#define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */ -#define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */ -#define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */ -#define EIR_NAME_SHORT 0x08 /* shortened local name */ -#define EIR_NAME_COMPLETE 0x09 /* complete local name */ -#define EIR_TX_POWER 0x0A /* transmit power level */ -#define EIR_DEVICE_ID 0x10 /* device ID */ - #define PNP_INFO_SVCLASS_ID 0x1200 static u8 bluetooth_base_uuid[] = { -- cgit From 561aafbcb2e3f8fee11d3781f866c7b4c4f93a28 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 4 Jan 2012 13:31:59 +0200 Subject: Bluetooth: Add initial mgmt_confirm_name support This patch adds initial support for mgmt_confirm_name. It adds the necessary tracking of the name state by extending the inquiry cache. The actual name resolving operation (to be done once inquiry is finished) is not yet part of this patch. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 29 +++++++++++++++----- net/bluetooth/hci_core.c | 58 ++++++++++++++++++++++++++++++++-------- net/bluetooth/hci_event.c | 51 +++++++++++++++++++++++++++++------ net/bluetooth/hci_sysfs.c | 2 +- net/bluetooth/mgmt.c | 51 ++++++++++++++++++++++++++++++++++- 5 files changed, 163 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 91d1baf05077..2999b6e2c3f0 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -44,14 +44,23 @@ struct inquiry_data { }; struct inquiry_entry { - struct list_head list; + struct list_head all; /* inq_cache.all */ + struct list_head list; /* unknown or resolve */ + enum { + NAME_NOT_KNOWN, + NAME_NEEDED, + NAME_PENDING, + NAME_KNOWN, + } name_state; __u32 timestamp; struct inquiry_data data; }; struct inquiry_cache { - struct list_head list; - __u32 timestamp; + struct list_head all; /* All devices found during inquiry */ + struct list_head unknown; /* Name state not known */ + struct list_head resolve; /* Name needs to be resolved */ + __u32 timestamp; }; struct hci_conn_hash { @@ -350,12 +359,14 @@ extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb); static inline void inquiry_cache_init(struct hci_dev *hdev) { - INIT_LIST_HEAD(&hdev->inq_cache.list); + INIT_LIST_HEAD(&hdev->inq_cache.all); + INIT_LIST_HEAD(&hdev->inq_cache.unknown); + INIT_LIST_HEAD(&hdev->inq_cache.resolve); } static inline int inquiry_cache_empty(struct hci_dev *hdev) { - return list_empty(&hdev->inq_cache.list); + return list_empty(&hdev->inq_cache.all); } static inline long inquiry_cache_age(struct hci_dev *hdev) @@ -371,7 +382,10 @@ static inline long inquiry_entry_age(struct inquiry_entry *e) struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); -void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data); +struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, + bdaddr_t *bdaddr); +void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, + bool name_known); /* ----- HCI Connections ----- */ enum { @@ -913,7 +927,8 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, u8 *randomizer, u8 status); int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 *dev_class, s8 rssi, u8 *eir); + u8 addr_type, u8 *dev_class, s8 rssi, + u8 cfm_name, u8 *eir); int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name); int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status); int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index feeea4df2529..fc09a3cbe20c 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -357,12 +357,16 @@ struct hci_dev *hci_dev_get(int index) /* ---- Inquiry support ---- */ static void inquiry_cache_flush(struct hci_dev *hdev) { + struct inquiry_cache *cache = &hdev->inq_cache; struct inquiry_entry *p, *n; - list_for_each_entry_safe(p, n, &hdev->inq_cache.list, list) { - list_del(&p->list); + list_for_each_entry_safe(p, n, &cache->all, all) { + list_del(&p->all); kfree(p); } + + INIT_LIST_HEAD(&cache->unknown); + INIT_LIST_HEAD(&cache->resolve); } struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) @@ -372,7 +376,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *b BT_DBG("cache %p, %s", cache, batostr(bdaddr)); - list_for_each_entry(e, &cache->list, list) { + list_for_each_entry(e, &cache->all, all) { if (!bacmp(&e->data.bdaddr, bdaddr)) return e; } @@ -380,7 +384,24 @@ struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *b return NULL; } -void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data) +struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, + bdaddr_t *bdaddr) +{ + struct inquiry_cache *cache = &hdev->inq_cache; + struct inquiry_entry *e; + + BT_DBG("cache %p, %s", cache, batostr(bdaddr)); + + list_for_each_entry(e, &cache->unknown, list) { + if (!bacmp(&e->data.bdaddr, bdaddr)) + return e; + } + + return NULL; +} + +void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, + bool name_known) { struct inquiry_cache *cache = &hdev->inq_cache; struct inquiry_entry *ie; @@ -388,13 +409,28 @@ void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data) BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr)); ie = hci_inquiry_cache_lookup(hdev, &data->bdaddr); - if (!ie) { - /* Entry not in the cache. Add new one. */ - ie = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC); - if (!ie) - return; + if (ie) + goto update; + + /* Entry not in the cache. Add new one. */ + ie = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC); + if (!ie) + return; + + list_add(&ie->all, &cache->all); + + if (name_known) { + ie->name_state = NAME_KNOWN; + } else { + ie->name_state = NAME_NOT_KNOWN; + list_add(&ie->list, &cache->unknown); + } - list_add(&ie->list, &cache->list); +update: + if (name_known && ie->name_state != NAME_KNOWN && + ie->name_state != NAME_PENDING) { + ie->name_state = NAME_KNOWN; + list_del(&ie->list); } memcpy(&ie->data, data, sizeof(*data)); @@ -409,7 +445,7 @@ static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf) struct inquiry_entry *e; int copied = 0; - list_for_each_entry(e, &cache->list, list) { + list_for_each_entry(e, &cache->all, all) { struct inquiry_data *data = &e->data; if (copied >= num) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 001307f81057..9302c3c25568 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1533,9 +1533,9 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff * data.clock_offset = info->clock_offset; data.rssi = 0x00; data.ssp_mode = 0x00; - hci_inquiry_cache_update(hdev, &data); + hci_inquiry_cache_update(hdev, &data, false); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, 0, NULL); + info->dev_class, 0, 1, NULL); } hci_dev_unlock(hdev); @@ -2572,10 +2572,10 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct data.clock_offset = info->clock_offset; data.rssi = info->rssi; data.ssp_mode = 0x00; - hci_inquiry_cache_update(hdev, &data); + hci_inquiry_cache_update(hdev, &data, false); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, - NULL); + 1, NULL); } } else { struct inquiry_info_with_rssi *info = (void *) (skb->data + 1); @@ -2589,10 +2589,10 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct data.clock_offset = info->clock_offset; data.rssi = info->rssi; data.ssp_mode = 0x00; - hci_inquiry_cache_update(hdev, &data); + hci_inquiry_cache_update(hdev, &data, false); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, - NULL); + 1, NULL); } } @@ -2710,6 +2710,31 @@ static inline void hci_sniff_subrate_evt(struct hci_dev *hdev, struct sk_buff *s BT_DBG("%s status %d", hdev->name, ev->status); } +static inline bool eir_has_complete_name(u8 *data, size_t data_len) +{ + u8 field_len; + size_t parsed; + + for (parsed = 0; parsed < data_len - 1; parsed += field_len) { + field_len = data[0]; + + if (field_len == 0) + break; + + parsed += field_len + 1; + + if (parsed > data_len) + break; + + if (data[1] == EIR_NAME_COMPLETE) + return true; + + data += field_len + 1; + } + + return false; +} + static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct inquiry_data data; @@ -2724,6 +2749,8 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct hci_dev_lock(hdev); for (; num_rsp; num_rsp--, info++) { + bool name_known; + bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; data.pscan_period_mode = info->pscan_period_mode; @@ -2732,9 +2759,17 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct data.clock_offset = info->clock_offset; data.rssi = info->rssi; data.ssp_mode = 0x01; - hci_inquiry_cache_update(hdev, &data); + + if (test_bit(HCI_MGMT, &hdev->flags)) + name_known = eir_has_complete_name(info->data, + sizeof(info->data)); + else + name_known = true; + + hci_inquiry_cache_update(hdev, &data, name_known); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, info->rssi, info->data); + info->dev_class, info->rssi, + !name_known, info->data); } hci_dev_unlock(hdev); diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index ed9cceeec7be..3600d78c2f25 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -388,7 +388,7 @@ static int inquiry_cache_show(struct seq_file *f, void *p) hci_dev_lock(hdev); - list_for_each_entry(e, &cache->list, list) { + list_for_each_entry(e, &cache->all, all) { struct inquiry_data *data = &e->data; seq_printf(f, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", batostr(&data->bdaddr), diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 851cb19c55b1..39775119585a 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1967,6 +1967,50 @@ failed: return err; } +static int confirm_name(struct sock *sk, u16 index, unsigned char *data, + u16 len) +{ + struct mgmt_cp_confirm_name *cp = (void *) data; + struct inquiry_entry *e; + struct hci_dev *hdev; + int err; + + BT_DBG("hci%u", index); + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_CONFIRM_NAME, + MGMT_STATUS_INVALID_PARAMS); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_CONFIRM_NAME, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + e = hci_inquiry_cache_lookup_unknown(hdev, &cp->bdaddr); + if (!e) { + err = cmd_status (sk, index, MGMT_OP_CONFIRM_NAME, + MGMT_STATUS_INVALID_PARAMS); + goto failed; + } + + if (cp->name_known) { + e->name_state = NAME_KNOWN; + list_del(&e->list); + } else { + e->name_state = NAME_NEEDED; + list_move(&e->list, &hdev->inq_cache.resolve); + } + + err = 0; + +failed: + hci_dev_unlock(hdev); + + return err; +} + static int block_device(struct sock *sk, u16 index, unsigned char *data, u16 len) { @@ -2215,6 +2259,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_STOP_DISCOVERY: err = stop_discovery(sk, index); break; + case MGMT_OP_CONFIRM_NAME: + err = confirm_name(sk, index, buf + sizeof(*hdr), len); + break; case MGMT_OP_BLOCK_DEVICE: err = block_device(sk, index, buf + sizeof(*hdr), len); break; @@ -2689,7 +2736,8 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, } int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 *dev_class, s8 rssi, u8 *eir) + u8 addr_type, u8 *dev_class, s8 rssi, + u8 cfm_name, u8 *eir) { struct mgmt_ev_device_found ev; @@ -2698,6 +2746,7 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, bacpy(&ev.addr.bdaddr, bdaddr); ev.addr.type = link_to_mgmt(link_type, addr_type); ev.rssi = rssi; + ev.confirm_name = cfm_name; if (eir) memcpy(ev.eir, eir, sizeof(ev.eir)); -- cgit From 3175405b906a85ed2bad21e09c444266e4a05a8e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 4 Jan 2012 13:39:52 +0200 Subject: Bluetooth: Return updated name state with hci_inquiry_cache_update If user-space has already confirmed the name for a remote device we shouldn't request confirmation again. The simplest way to do this is to return the name state from hci_inquiry_cache_update (if it is anything else than unknown then we do not need confirmation from user-space). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 9 +++++++-- net/bluetooth/hci_event.c | 21 ++++++++++++++------- 3 files changed, 22 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 2999b6e2c3f0..236f7f0e596e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -384,7 +384,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, bdaddr_t *bdaddr); -void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, +bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, bool name_known); /* ----- HCI Connections ----- */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index fc09a3cbe20c..162176151db9 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -400,7 +400,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, return NULL; } -void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, +bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, bool name_known) { struct inquiry_cache *cache = &hdev->inq_cache; @@ -415,7 +415,7 @@ void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, /* Entry not in the cache. Add new one. */ ie = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC); if (!ie) - return; + return false; list_add(&ie->all, &cache->all); @@ -436,6 +436,11 @@ update: memcpy(&ie->data, data, sizeof(*data)); ie->timestamp = jiffies; cache->timestamp = jiffies; + + if (ie->name_state == NAME_NOT_KNOWN) + return false; + + return true; } static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 9302c3c25568..d4d20df9fbbf 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1525,6 +1525,8 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff * hci_dev_lock(hdev); for (; num_rsp; num_rsp--, info++) { + bool name_known; + bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; data.pscan_period_mode = info->pscan_period_mode; @@ -1533,9 +1535,10 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff * data.clock_offset = info->clock_offset; data.rssi = 0x00; data.ssp_mode = 0x00; - hci_inquiry_cache_update(hdev, &data, false); + + name_known = hci_inquiry_cache_update(hdev, &data, false); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, 0, 1, NULL); + info->dev_class, 0, !name_known, NULL); } hci_dev_unlock(hdev); @@ -2551,6 +2554,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct { struct inquiry_data data; int num_rsp = *((__u8 *) skb->data); + bool name_known; BT_DBG("%s num_rsp %d", hdev->name, num_rsp); @@ -2572,10 +2576,12 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct data.clock_offset = info->clock_offset; data.rssi = info->rssi; data.ssp_mode = 0x00; - hci_inquiry_cache_update(hdev, &data, false); + + name_known = hci_inquiry_cache_update(hdev, &data, + false); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, - 1, NULL); + !name_known, NULL); } } else { struct inquiry_info_with_rssi *info = (void *) (skb->data + 1); @@ -2589,10 +2595,11 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct data.clock_offset = info->clock_offset; data.rssi = info->rssi; data.ssp_mode = 0x00; - hci_inquiry_cache_update(hdev, &data, false); + name_known = hci_inquiry_cache_update(hdev, &data, + false); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, - 1, NULL); + !name_known, NULL); } } @@ -2766,7 +2773,7 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct else name_known = true; - hci_inquiry_cache_update(hdev, &data, name_known); + name_known = hci_inquiry_cache_update(hdev, &data, name_known); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, !name_known, info->data); -- cgit From 30883512be0839349d29c7b0bc31016e0498cf8c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 4 Jan 2012 14:16:21 +0200 Subject: Bluetooth: Rename hdev->inq_cache to hdev->discovery This struct is used for not just inquiry caching but also for general device discovery state tracking so it's better to rename it to something more appropriate. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 16 ++++++++-------- net/bluetooth/hci_core.c | 12 ++++++------ net/bluetooth/hci_sysfs.c | 2 +- net/bluetooth/mgmt.c | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 236f7f0e596e..5a566fd5e2a6 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -56,7 +56,7 @@ struct inquiry_entry { struct inquiry_data data; }; -struct inquiry_cache { +struct discovery_state { struct list_head all; /* All devices found during inquiry */ struct list_head unknown; /* Name state not known */ struct list_head resolve; /* Name needs to be resolved */ @@ -226,7 +226,7 @@ struct hci_dev { struct list_head mgmt_pending; - struct inquiry_cache inq_cache; + struct discovery_state discovery; struct hci_conn_hash conn_hash; struct list_head blacklist; @@ -357,21 +357,21 @@ extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb); #define INQUIRY_CACHE_AGE_MAX (HZ*30) /* 30 seconds */ #define INQUIRY_ENTRY_AGE_MAX (HZ*60) /* 60 seconds */ -static inline void inquiry_cache_init(struct hci_dev *hdev) +static inline void discovery_init(struct hci_dev *hdev) { - INIT_LIST_HEAD(&hdev->inq_cache.all); - INIT_LIST_HEAD(&hdev->inq_cache.unknown); - INIT_LIST_HEAD(&hdev->inq_cache.resolve); + INIT_LIST_HEAD(&hdev->discovery.all); + INIT_LIST_HEAD(&hdev->discovery.unknown); + INIT_LIST_HEAD(&hdev->discovery.resolve); } static inline int inquiry_cache_empty(struct hci_dev *hdev) { - return list_empty(&hdev->inq_cache.all); + return list_empty(&hdev->discovery.all); } static inline long inquiry_cache_age(struct hci_dev *hdev) { - struct inquiry_cache *c = &hdev->inq_cache; + struct discovery_state *c = &hdev->discovery; return jiffies - c->timestamp; } diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 5dbfb276edf2..55509b0a810a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -357,7 +357,7 @@ struct hci_dev *hci_dev_get(int index) /* ---- Inquiry support ---- */ static void inquiry_cache_flush(struct hci_dev *hdev) { - struct inquiry_cache *cache = &hdev->inq_cache; + struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *p, *n; list_for_each_entry_safe(p, n, &cache->all, all) { @@ -371,7 +371,7 @@ static void inquiry_cache_flush(struct hci_dev *hdev) struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) { - struct inquiry_cache *cache = &hdev->inq_cache; + struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *e; BT_DBG("cache %p, %s", cache, batostr(bdaddr)); @@ -387,7 +387,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *b struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, bdaddr_t *bdaddr) { - struct inquiry_cache *cache = &hdev->inq_cache; + struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *e; BT_DBG("cache %p, %s", cache, batostr(bdaddr)); @@ -403,7 +403,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, bool name_known) { - struct inquiry_cache *cache = &hdev->inq_cache; + struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *ie; BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr)); @@ -445,7 +445,7 @@ update: static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf) { - struct inquiry_cache *cache = &hdev->inq_cache; + struct discovery_state *cache = &hdev->discovery; struct inquiry_info *info = (struct inquiry_info *) buf; struct inquiry_entry *e; int copied = 0; @@ -1546,7 +1546,7 @@ int hci_register_dev(struct hci_dev *hdev) init_waitqueue_head(&hdev->req_wait_q); mutex_init(&hdev->req_lock); - inquiry_cache_init(hdev); + discovery_init(hdev); hci_conn_hash_init(hdev); diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 3600d78c2f25..74b49e330a1d 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -383,7 +383,7 @@ static struct device_type bt_host = { static int inquiry_cache_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; - struct inquiry_cache *cache = &hdev->inq_cache; + struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *e; hci_dev_lock(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 39775119585a..894f11bc571d 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2000,7 +2000,7 @@ static int confirm_name(struct sock *sk, u16 index, unsigned char *data, list_del(&e->list); } else { e->name_state = NAME_NEEDED; - list_move(&e->list, &hdev->inq_cache.resolve); + list_move(&e->list, &hdev->discovery.resolve); } err = 0; -- cgit From ff9ef5787046c3fd20cf9f7ca1cd70260c1eedb9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 4 Jan 2012 14:23:45 +0200 Subject: Bluetooth: Add discovery state tracking This patch adds proper state tracking to the device discovery process. This makes it possible to return appropriate errors when trying to stop a non-active discovery or start discovery when it is already ongoing. Once name resolving is implemented this also makes it possible to know what the right action to do is when a remote name lookup is cancelled. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 9 +++++++++ net/bluetooth/hci_core.c | 25 +++++++++++++++++++++++++ net/bluetooth/hci_event.c | 6 +++--- net/bluetooth/mgmt.c | 16 ++++++++++++++++ 4 files changed, 53 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5a566fd5e2a6..2f19de4770b6 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -57,6 +57,12 @@ struct inquiry_entry { }; struct discovery_state { + enum { + DISCOVERY_STOPPED, + DISCOVERY_STARTING, + DISCOVERY_ACTIVE, + DISCOVERY_STOPPING, + } state; struct list_head all; /* All devices found during inquiry */ struct list_head unknown; /* Name state not known */ struct list_head resolve; /* Name needs to be resolved */ @@ -359,11 +365,14 @@ extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb); static inline void discovery_init(struct hci_dev *hdev) { + hdev->discovery.state = DISCOVERY_STOPPED; INIT_LIST_HEAD(&hdev->discovery.all); INIT_LIST_HEAD(&hdev->discovery.unknown); INIT_LIST_HEAD(&hdev->discovery.resolve); } +void hci_discovery_set_state(struct hci_dev *hdev, int state); + static inline int inquiry_cache_empty(struct hci_dev *hdev) { return list_empty(&hdev->discovery.all); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 55509b0a810a..b68719230601 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -355,6 +355,30 @@ struct hci_dev *hci_dev_get(int index) } /* ---- Inquiry support ---- */ + +void hci_discovery_set_state(struct hci_dev *hdev, int state) +{ + BT_DBG("%s state %u -> %u", hdev->name, hdev->discovery.state, state); + + if (hdev->discovery.state == state) + return; + + switch (state) { + case DISCOVERY_STOPPED: + mgmt_discovering(hdev, 0); + break; + case DISCOVERY_STARTING: + break; + case DISCOVERY_ACTIVE: + mgmt_discovering(hdev, 1); + break; + case DISCOVERY_STOPPING: + break; + } + + hdev->discovery.state = state; +} + static void inquiry_cache_flush(struct hci_dev *hdev) { struct discovery_state *cache = &hdev->discovery; @@ -367,6 +391,7 @@ static void inquiry_cache_flush(struct hci_dev *hdev) INIT_LIST_HEAD(&cache->unknown); INIT_LIST_HEAD(&cache->resolve); + cache->state = DISCOVERY_STOPPED; } struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d4d20df9fbbf..43d69569a0d5 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -65,7 +65,7 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_INQUIRY, &hdev->flags); hci_dev_lock(hdev); - mgmt_discovering(hdev, 0); + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); hci_dev_unlock(hdev); hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status); @@ -1119,7 +1119,7 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) set_bit(HCI_INQUIRY, &hdev->flags); hci_dev_lock(hdev); - mgmt_discovering(hdev, 1); + hci_discovery_set_state(hdev, DISCOVERY_ACTIVE); hci_dev_unlock(hdev); } @@ -1507,7 +1507,7 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff return; hci_dev_lock(hdev); - mgmt_discovering(hdev, 0); + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); hci_dev_unlock(hdev); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 894f11bc571d..590966ddfa63 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1918,6 +1918,12 @@ static int start_discovery(struct sock *sk, u16 index, goto failed; } + if (hdev->discovery.state != DISCOVERY_STOPPED) { + err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY, + MGMT_STATUS_BUSY); + goto failed; + } + cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0); if (!cmd) { err = -ENOMEM; @@ -1927,6 +1933,8 @@ static int start_discovery(struct sock *sk, u16 index, err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR); if (err < 0) mgmt_pending_remove(cmd); + else + hci_discovery_set_state(hdev, DISCOVERY_STARTING); failed: hci_dev_unlock(hdev); @@ -1950,6 +1958,12 @@ static int stop_discovery(struct sock *sk, u16 index) hci_dev_lock(hdev); + if (hdev->discovery.state != DISCOVERY_ACTIVE) { + err = cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, + MGMT_STATUS_REJECTED); + goto failed; + } + cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0); if (!cmd) { err = -ENOMEM; @@ -1959,6 +1973,8 @@ static int stop_discovery(struct sock *sk, u16 index) err = hci_cancel_inquiry(hdev); if (err < 0) mgmt_pending_remove(cmd); + else + hci_discovery_set_state(hdev, DISCOVERY_STOPPING); failed: hci_dev_unlock(hdev); -- cgit From 30dc78e1a2bcbe2a0fca7aa44dfded4bb0db6148 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 4 Jan 2012 15:44:20 +0200 Subject: Bluetooth: Add name resolving support for mgmt based discovery This patch adds the necessary logic to perform name lookups after inquiry completes. This is done by checking for entries in the resolve list after each inquiry complete and remote name complete HCI event. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 8 +++- net/bluetooth/hci_core.c | 34 ++++++++++++++++- net/bluetooth/hci_event.c | 81 ++++++++++++++++++++++++++++++++++++++-- net/bluetooth/mgmt.c | 37 +++++++++++++++--- 4 files changed, 149 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 2f19de4770b6..a8680da7f400 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -60,7 +60,8 @@ struct discovery_state { enum { DISCOVERY_STOPPED, DISCOVERY_STARTING, - DISCOVERY_ACTIVE, + DISCOVERY_INQUIRY, + DISCOVERY_RESOLVING, DISCOVERY_STOPPING, } state; struct list_head all; /* All devices found during inquiry */ @@ -371,6 +372,8 @@ static inline void discovery_init(struct hci_dev *hdev) INIT_LIST_HEAD(&hdev->discovery.resolve); } +bool hci_discovery_active(struct hci_dev *hdev); + void hci_discovery_set_state(struct hci_dev *hdev, int state); static inline int inquiry_cache_empty(struct hci_dev *hdev) @@ -393,6 +396,9 @@ struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, bdaddr_t *bdaddr); +struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev, + bdaddr_t *bdaddr, + int state); bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, bool name_known); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b68719230601..546a42941477 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -356,6 +356,17 @@ struct hci_dev *hci_dev_get(int index) /* ---- Inquiry support ---- */ +bool hci_discovery_active(struct hci_dev *hdev) +{ + struct discovery_state *discov = &hdev->discovery; + + if (discov->state == DISCOVERY_INQUIRY || + discov->state == DISCOVERY_RESOLVING) + return true; + + return false; +} + void hci_discovery_set_state(struct hci_dev *hdev, int state) { BT_DBG("%s state %u -> %u", hdev->name, hdev->discovery.state, state); @@ -369,9 +380,11 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state) break; case DISCOVERY_STARTING: break; - case DISCOVERY_ACTIVE: + case DISCOVERY_INQUIRY: mgmt_discovering(hdev, 1); break; + case DISCOVERY_RESOLVING: + break; case DISCOVERY_STOPPING: break; } @@ -425,6 +438,25 @@ struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, return NULL; } +struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev, + bdaddr_t *bdaddr, + int state) +{ + struct discovery_state *cache = &hdev->discovery; + struct inquiry_entry *e; + + BT_DBG("cache %p bdaddr %s state %d", cache, batostr(bdaddr), state); + + list_for_each_entry(e, &cache->resolve, list) { + if (!bacmp(bdaddr, BDADDR_ANY) && e->name_state == state) + return e; + if (!bacmp(&e->data.bdaddr, bdaddr)) + return e; + } + + return NULL; +} + bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, bool name_known) { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 43d69569a0d5..089dff80ccb0 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1119,7 +1119,7 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) set_bit(HCI_INQUIRY, &hdev->flags); hci_dev_lock(hdev); - hci_discovery_set_state(hdev, DISCOVERY_ACTIVE); + hci_discovery_set_state(hdev, DISCOVERY_INQUIRY); hci_dev_unlock(hdev); } @@ -1271,6 +1271,50 @@ static int hci_outgoing_auth_needed(struct hci_dev *hdev, return 1; } +static inline int hci_resolve_name(struct hci_dev *hdev, struct inquiry_entry *e) +{ + struct hci_cp_remote_name_req cp; + + memset(&cp, 0, sizeof(cp)); + + bacpy(&cp.bdaddr, &e->data.bdaddr); + cp.pscan_rep_mode = e->data.pscan_rep_mode; + cp.pscan_mode = e->data.pscan_mode; + cp.clock_offset = e->data.clock_offset; + + return hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); +} + +static void hci_resolve_next_name(struct hci_dev *hdev, bdaddr_t *bdaddr) +{ + struct discovery_state *discov = &hdev->discovery; + struct inquiry_entry *e; + + if (discov->state == DISCOVERY_STOPPING) + goto discov_complete; + + if (discov->state != DISCOVERY_RESOLVING) + return; + + e = hci_inquiry_cache_lookup_resolve(hdev, bdaddr, NAME_PENDING); + if (e) { + e->name_state = NAME_KNOWN; + list_del(&e->list); + } + + if (list_empty(&discov->resolve)) + goto discov_complete; + + e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_NEEDED); + if (hci_resolve_name(hdev, e) == 0) { + e->name_state = NAME_PENDING; + return; + } + +discov_complete: + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); +} + static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status) { struct hci_cp_remote_name_req *cp; @@ -1289,6 +1333,9 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status) hci_dev_lock(hdev); + if (test_bit(HCI_MGMT, &hdev->flags)) + hci_resolve_next_name(hdev, &cp->bdaddr); + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); if (!conn) goto unlock; @@ -1496,6 +1543,8 @@ static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status) static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); + struct discovery_state *discov = &hdev->discovery; + struct inquiry_entry *e; BT_DBG("%s status %d", hdev->name, status); @@ -1506,8 +1555,28 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags)) return; + if (!test_bit(HCI_MGMT, &hdev->flags)) + return; + hci_dev_lock(hdev); - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + + if (discov->state != DISCOVERY_INQUIRY) + goto unlock; + + if (list_empty(&discov->resolve)) { + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + goto unlock; + } + + e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_NEEDED); + if (e && hci_resolve_name(hdev, e) == 0) { + e->name_state = NAME_PENDING; + hci_discovery_set_state(hdev, DISCOVERY_RESOLVING); + } else { + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + } + +unlock: hci_dev_unlock(hdev); } @@ -1807,8 +1876,12 @@ static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb hci_dev_lock(hdev); - if (ev->status == 0 && test_bit(HCI_MGMT, &hdev->flags)) - mgmt_remote_name(hdev, &ev->bdaddr, ev->name); + if (test_bit(HCI_MGMT, &hdev->flags)) { + if (ev->status == 0) + mgmt_remote_name(hdev, &ev->bdaddr, ev->name); + + hci_resolve_next_name(hdev, &ev->bdaddr); + } conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (!conn) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 590966ddfa63..295cfc8a3076 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1947,6 +1947,8 @@ static int stop_discovery(struct sock *sk, u16 index) { struct hci_dev *hdev; struct pending_cmd *cmd; + struct hci_cp_remote_name_req_cancel cp; + struct inquiry_entry *e; int err; BT_DBG("hci%u", index); @@ -1958,25 +1960,44 @@ static int stop_discovery(struct sock *sk, u16 index) hci_dev_lock(hdev); - if (hdev->discovery.state != DISCOVERY_ACTIVE) { + if (!hci_discovery_active(hdev)) { err = cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, MGMT_STATUS_REJECTED); - goto failed; + goto unlock; } cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0); if (!cmd) { err = -ENOMEM; - goto failed; + goto unlock; + } + + if (hdev->discovery.state == DISCOVERY_INQUIRY) { + err = hci_cancel_inquiry(hdev); + if (err < 0) + mgmt_pending_remove(cmd); + else + hci_discovery_set_state(hdev, DISCOVERY_STOPPING); + goto unlock; + } + + e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_PENDING); + if (!e) { + mgmt_pending_remove(cmd); + err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY, NULL, 0); + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + goto unlock; } - err = hci_cancel_inquiry(hdev); + bacpy(&cp.bdaddr, &e->data.bdaddr); + err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL, + sizeof(cp), &cp); if (err < 0) mgmt_pending_remove(cmd); else hci_discovery_set_state(hdev, DISCOVERY_STOPPING); -failed: +unlock: hci_dev_unlock(hdev); hci_dev_put(hdev); @@ -2004,6 +2025,12 @@ static int confirm_name(struct sock *sk, u16 index, unsigned char *data, hci_dev_lock(hdev); + if (!hci_discovery_active(hdev)) { + err = cmd_status(sk, index, MGMT_OP_CONFIRM_NAME, + MGMT_STATUS_FAILED); + goto failed; + } + e = hci_inquiry_cache_lookup_unknown(hdev, &cp->bdaddr); if (!e) { err = cmd_status (sk, index, MGMT_OP_CONFIRM_NAME, -- cgit From 25e89e99b4a54a2cb6e27b4675cd71a3d8a9b3fc Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 4 Jan 2012 12:41:58 +0200 Subject: Bluetooth: Process num completed data blocks event Adds support for Number Of Completed Data Blocks Event. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 13 +++++++++++ net/bluetooth/hci_event.c | 54 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index ce5133b22445..6a9d316fb977 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1168,6 +1168,19 @@ struct hci_ev_le_meta { __u8 subevent; } __packed; +#define HCI_EV_NUM_COMP_BLOCKS 0x48 +struct hci_comp_blocks_info { + __le16 handle; + __le16 pkts; + __le16 blocks; +} __packed; + +struct hci_ev_num_comp_blocks { + __le16 num_blocks; + __u8 num_hndl; + struct hci_comp_blocks_info handles[0]; +} __packed; + /* Low energy meta events */ #define HCI_EV_LE_CONN_COMPLETE 0x01 struct hci_ev_le_conn_complete { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 089dff80ccb0..0466ed9c1b47 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2408,6 +2408,56 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s queue_work(hdev->workqueue, &hdev->tx_work); } +static inline void hci_num_comp_blocks_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_num_comp_blocks *ev = (void *) skb->data; + int i; + + if (hdev->flow_ctl_mode != HCI_FLOW_CTL_MODE_BLOCK_BASED) { + BT_ERR("Wrong event for mode %d", hdev->flow_ctl_mode); + return; + } + + if (skb->len < sizeof(*ev) || skb->len < sizeof(*ev) + + ev->num_hndl * sizeof(struct hci_comp_blocks_info)) { + BT_DBG("%s bad parameters", hdev->name); + return; + } + + BT_DBG("%s num_blocks %d num_hndl %d", hdev->name, ev->num_blocks, + ev->num_hndl); + + for (i = 0; i < ev->num_hndl; i++) { + struct hci_comp_blocks_info *info = &ev->handles[i]; + struct hci_conn *conn; + __u16 handle, block_count; + + handle = __le16_to_cpu(info->handle); + block_count = __le16_to_cpu(info->blocks); + + conn = hci_conn_hash_lookup_handle(hdev, handle); + if (!conn) + continue; + + conn->sent -= block_count; + + switch (conn->type) { + case ACL_LINK: + hdev->block_cnt += block_count; + if (hdev->block_cnt > hdev->num_blocks) + hdev->block_cnt = hdev->num_blocks; + break; + + default: + BT_ERR("Unknown type %d conn %p", conn->type, conn); + break; + } + } + + queue_work(hdev->workqueue, &hdev->tx_work); +} + static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_mode_change *ev = (void *) skb->data; @@ -3386,6 +3436,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_remote_oob_data_request_evt(hdev, skb); break; + case HCI_EV_NUM_COMP_BLOCKS: + hci_num_comp_blocks_evt(hdev, skb); + break; + default: BT_DBG("%s event 0x%x", hdev->name, event); break; -- cgit From b83ddfe2ac670392c27a799371bb1a7e14a7e3d3 Mon Sep 17 00:00:00 2001 From: Andrzej Kaczmarek Date: Wed, 4 Jan 2012 12:10:42 +0100 Subject: Bluetooth: l2cap_set_timer needs jiffies as timeout value After moving L2CAP timers to workqueues l2cap_set_timer expects timeout value to be specified in jiffies but constants defined in miliseconds are used. This makes timeouts unreliable when CONFIG_HZ is not set to 1000. __set_chan_timer macro still uses jiffies as input to avoid multiple conversions from/to jiffies for sk_sndtimeo value which is already specified in jiffies. Signed-off-by: Andrzej Kaczmarek Ackec-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 6 +++--- net/bluetooth/l2cap_core.c | 12 ++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 68f589150692..ab8f7d83079a 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -626,13 +626,13 @@ static inline void l2cap_clear_timer(struct l2cap_chan *chan, #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) #define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer) #define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \ - L2CAP_DEFAULT_RETRANS_TO); + msecs_to_jiffies(L2CAP_DEFAULT_RETRANS_TO)); #define __clear_retrans_timer(c) l2cap_clear_timer(c, &c->retrans_timer) #define __set_monitor_timer(c) l2cap_set_timer(c, &c->monitor_timer, \ - L2CAP_DEFAULT_MONITOR_TO); + msecs_to_jiffies(L2CAP_DEFAULT_MONITOR_TO)); #define __clear_monitor_timer(c) l2cap_clear_timer(c, &c->monitor_timer) #define __set_ack_timer(c) l2cap_set_timer(c, &chan->ack_timer, \ - L2CAP_DEFAULT_ACK_TO); + msecs_to_jiffies(L2CAP_DEFAULT_ACK_TO)); #define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer) static inline int __seq_offset(struct l2cap_chan *chan, __u16 seq1, __u16 seq2) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index faf0b11ac1d3..e35ff4cd33ce 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2970,7 +2970,8 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr default: sk->sk_err = ECONNRESET; - __set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT); + __set_chan_timer(chan, + msecs_to_jiffies(L2CAP_DISC_REJ_TIMEOUT)); l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; } @@ -4478,7 +4479,8 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) if (encrypt == 0x00) { if (chan->sec_level == BT_SECURITY_MEDIUM) { __clear_chan_timer(chan); - __set_chan_timer(chan, L2CAP_ENC_TIMEOUT); + __set_chan_timer(chan, + msecs_to_jiffies(L2CAP_ENC_TIMEOUT)); } else if (chan->sec_level == BT_SECURITY_HIGH) l2cap_chan_close(chan, ECONNREFUSED); } else { @@ -4546,7 +4548,8 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) L2CAP_CONN_REQ, sizeof(req), &req); } else { __clear_chan_timer(chan); - __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); + __set_chan_timer(chan, + msecs_to_jiffies(L2CAP_DISC_TIMEOUT)); } } else if (chan->state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; @@ -4566,7 +4569,8 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) } } else { l2cap_state_change(chan, BT_DISCONN); - __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); + __set_chan_timer(chan, + msecs_to_jiffies(L2CAP_DISC_TIMEOUT)); res = L2CAP_CR_SEC_BLOCK; stat = L2CAP_CS_NO_INFO; } -- cgit From cc48dc0a996af6ae20e91c551d71e7f72768860f Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 4 Jan 2012 16:42:26 +0200 Subject: Bluetooth: Remove magic number from ACL TO Adds HCI_ACL_TX_TIMEOUT and clear conversion from msec to jiffies Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_core.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 6a9d316fb977..4202c9cb497e 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -130,6 +130,7 @@ enum { #define HCI_IDLE_TIMEOUT (6000) /* 6 seconds */ #define HCI_INIT_TIMEOUT (10000) /* 10 seconds */ #define HCI_CMD_TIMEOUT (1000) /* 1 seconds */ +#define HCI_ACL_TX_TIMEOUT (45000) /* 45 seconds */ /* HCI data types */ #define HCI_COMMAND_PKT 0x01 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 546a42941477..f84935e5cbab 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2350,7 +2350,8 @@ static inline void hci_sched_acl(struct hci_dev *hdev) if (!test_bit(HCI_RAW, &hdev->flags)) { /* ACL tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ - if (!hdev->acl_cnt && time_after(jiffies, hdev->acl_last_tx + HZ * 45)) + if (!hdev->acl_cnt && time_after(jiffies, hdev->acl_last_tx + + msecs_to_jiffies(HCI_ACL_TX_TIMEOUT))) hci_link_tx_to(hdev, ACL_LINK); } -- cgit From 1931782b67bd1b81007022e8311669c40bb7d5b1 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Wed, 4 Jan 2012 11:57:17 -0300 Subject: Bluetooth: Fix using an absolute timeout on hci_conn_put() queue_delayed_work() expects a relative time for when that work should be scheduled. Signed-off-by: Vinicius Costa Gomes Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index a8680da7f400..25a6c3fd7d1a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -588,7 +588,7 @@ static inline void hci_conn_put(struct hci_conn *conn) } cancel_delayed_work_sync(&conn->disc_work); queue_delayed_work(conn->hdev->workqueue, - &conn->disc_work, jiffies + timeo); + &conn->disc_work, timeo); } } -- cgit From 010666a126fce7b9ecdda7209c558db21d771c56 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sat, 7 Jan 2012 15:47:07 +0100 Subject: Bluetooth: Make hci-destruct callback optional Several drivers already provide an empty callback so we can actually make this optional and then remove all those empty callbacks in the drivers. This callback isn't needed at all by most drivers as they can remove their allocated structures on device disconnect and not on hci destruction. Signed-off-by: David Herrmann Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 6 ++++-- net/bluetooth/hci_core.c | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 25a6c3fd7d1a..86c74cc563ff 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -595,8 +595,10 @@ static inline void hci_conn_put(struct hci_conn *conn) /* ----- HCI Devices ----- */ static inline void __hci_dev_put(struct hci_dev *d) { - if (atomic_dec_and_test(&d->refcnt)) - d->destruct(d); + if (atomic_dec_and_test(&d->refcnt)) { + if (d->destruct) + d->destruct(d); + } } /* diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f84935e5cbab..f23e32a645c8 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1551,7 +1551,7 @@ int hci_register_dev(struct hci_dev *hdev) BT_DBG("%p name %s bus %d owner %p", hdev, hdev->name, hdev->bus, hdev->owner); - if (!hdev->open || !hdev->close || !hdev->destruct) + if (!hdev->open || !hdev->close) return -EINVAL; /* Do not allow HCI_AMP devices to register at index 0, -- cgit From 587ae086f6e44c3f1d313e3efdfc8c2866784bc3 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sat, 7 Jan 2012 15:47:20 +0100 Subject: Bluetooth: Remove unused hci-destruct cb The hci-destruct callback is not used by any driver so we can remove it. There is no reason to keep it alive, anymore. Drivers can free their internal data on driver-release and we do not need to provide a public destruct callback. Internally, we still use a destruct callback inside of hci_sysfs.c. This one is used to correctly free our hci_dev data structure if no more users have a reference to it. Signed-off-by: David Herrmann Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 86c74cc563ff..935aca8783c4 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -270,7 +270,6 @@ struct hci_dev { int (*close)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev); int (*send)(struct sk_buff *skb); - void (*destruct)(struct hci_dev *hdev); void (*notify)(struct hci_dev *hdev, unsigned int evt); int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg); }; @@ -595,10 +594,7 @@ static inline void hci_conn_put(struct hci_conn *conn) /* ----- HCI Devices ----- */ static inline void __hci_dev_put(struct hci_dev *d) { - if (atomic_dec_and_test(&d->refcnt)) { - if (d->destruct) - d->destruct(d); - } + atomic_dec(&d->refcnt); } /* -- cgit From e9b9cfa1575e37cb2dbb5534aeaaa16814228887 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sat, 7 Jan 2012 15:47:22 +0100 Subject: Bluetooth: Remove HCI-owner field After unregistering an hci_dev object a bluetooth driver does not have any callbacks in the hci_dev structure left over. Therefore, there is no need to keep a reference to the module. Previously, we needed this to protect the hci-destruct callback. However, this callback is no longer available so we do not need this owner field, anymore. Drivers now call hci_unregister_dev() and they are done with the object. Signed-off-by: David Herrmann Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/bfusb.c | 2 -- drivers/bluetooth/bluecard_cs.c | 2 -- drivers/bluetooth/bpa10x.c | 2 -- drivers/bluetooth/bt3c_cs.c | 2 -- drivers/bluetooth/btmrvl_main.c | 1 - drivers/bluetooth/btsdio.c | 2 -- drivers/bluetooth/btuart_cs.c | 2 -- drivers/bluetooth/btusb.c | 2 -- drivers/bluetooth/btwilink.c | 1 - drivers/bluetooth/dtl1_cs.c | 2 -- drivers/bluetooth/hci_ldisc.c | 2 -- drivers/bluetooth/hci_vhci.c | 2 -- include/net/bluetooth/hci_core.h | 13 ++----------- net/bluetooth/hci_core.c | 3 +-- 14 files changed, 3 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index e99ce89e1cad..c7d6ff0ffcf1 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -705,8 +705,6 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i hdev->send = bfusb_send_frame; hdev->ioctl = bfusb_ioctl; - hdev->owner = THIS_MODULE; - if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); hci_free_dev(hdev); diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 5cb325a13745..6b1261f9deb0 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -738,8 +738,6 @@ static int bluecard_open(bluecard_info_t *info) hdev->send = bluecard_hci_send_frame; hdev->ioctl = bluecard_hci_ioctl; - hdev->owner = THIS_MODULE; - id = inb(iobase + 0x30); if ((id & 0x0f) == 0x02) diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 29cd11d401e1..9d635148104c 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -470,8 +470,6 @@ static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id * hdev->flush = bpa10x_flush; hdev->send = bpa10x_send_frame; - hdev->owner = THIS_MODULE; - set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); err = hci_register_dev(hdev); diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index e74334dfc776..0e304cb4bdea 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -584,8 +584,6 @@ static int bt3c_open(bt3c_info_t *info) hdev->send = bt3c_hci_send_frame; hdev->ioctl = bt3c_hci_ioctl; - hdev->owner = THIS_MODULE; - /* Load firmware */ err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev); if (err < 0) { diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index 995cf43f4bd2..66b58fd09fbe 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -550,7 +550,6 @@ int btmrvl_register_hdev(struct btmrvl_private *priv) hdev->flush = btmrvl_flush; hdev->send = btmrvl_send_frame; hdev->ioctl = btmrvl_ioctl; - hdev->owner = THIS_MODULE; btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c index d38945cc9f05..2d6e4ed1637f 100644 --- a/drivers/bluetooth/btsdio.c +++ b/drivers/bluetooth/btsdio.c @@ -337,8 +337,6 @@ static int btsdio_probe(struct sdio_func *func, hdev->flush = btsdio_flush; hdev->send = btsdio_send_frame; - hdev->owner = THIS_MODULE; - err = hci_register_dev(hdev); if (err < 0) { hci_free_dev(hdev); diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index 84e02f1a45b9..80ad2b9b352e 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -502,8 +502,6 @@ static int btuart_open(btuart_info_t *info) hdev->send = btuart_hci_send_frame; hdev->ioctl = btuart_hci_ioctl; - hdev->owner = THIS_MODULE; - spin_lock_irqsave(&(info->lock), flags); /* Reset UART */ diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 44b5e736ddc3..a36888a9c205 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -997,8 +997,6 @@ static int btusb_probe(struct usb_interface *intf, hdev->send = btusb_send_frame; hdev->notify = btusb_notify; - hdev->owner = THIS_MODULE; - /* Interface numbers are hardcoded in the specification */ data->isoc = usb_ifnum_to_if(data->udev, 1); diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c index da9cf6a6e8ac..b81b32e4fa12 100644 --- a/drivers/bluetooth/btwilink.c +++ b/drivers/bluetooth/btwilink.c @@ -317,7 +317,6 @@ static int bt_ti_probe(struct platform_device *pdev) hdev->close = ti_st_close; hdev->flush = NULL; hdev->send = ti_st_send_frame; - hdev->owner = THIS_MODULE; err = hci_register_dev(hdev); if (err < 0) { diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index aae40caaa188..295cf1b4a052 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -484,8 +484,6 @@ static int dtl1_open(dtl1_info_t *info) hdev->send = dtl1_hci_send_frame; hdev->ioctl = dtl1_hci_ioctl; - hdev->owner = THIS_MODULE; - spin_lock_irqsave(&(info->lock), flags); /* Reset UART */ diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 5ea49df3462b..459ff0ba5a42 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -392,8 +392,6 @@ static int hci_uart_register_dev(struct hci_uart *hu) hdev->send = hci_uart_send_frame; hdev->parent = hu->tty->dev; - hdev->owner = THIS_MODULE; - if (!reset) set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 44a801292d62..5f305c131a0d 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -244,8 +244,6 @@ static int vhci_open(struct inode *inode, struct file *file) hdev->flush = vhci_flush; hdev->send = vhci_send_frame; - hdev->owner = THIS_MODULE; - if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); kfree(data); diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 935aca8783c4..99984688ccdd 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -262,8 +262,6 @@ struct hci_dev { struct rfkill *rfkill; - struct module *owner; - unsigned long dev_flags; int (*open)(struct hci_dev *hdev); @@ -601,11 +599,7 @@ static inline void __hci_dev_put(struct hci_dev *d) * hci_dev_put and hci_dev_hold are macros to avoid dragging all the * overhead of all the modular infrastructure into this header. */ -#define hci_dev_put(d) \ -do { \ - __hci_dev_put(d); \ - module_put(d->owner); \ -} while (0) +#define hci_dev_put(d) __hci_dev_put(d) static inline struct hci_dev *__hci_dev_hold(struct hci_dev *d) { @@ -613,10 +607,7 @@ static inline struct hci_dev *__hci_dev_hold(struct hci_dev *d) return d; } -#define hci_dev_hold(d) \ -({ \ - try_module_get(d->owner) ? __hci_dev_hold(d) : NULL; \ -}) +#define hci_dev_hold(d) __hci_dev_hold(d) #define hci_dev_lock(d) mutex_lock(&d->lock) #define hci_dev_unlock(d) mutex_unlock(&d->lock) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f23e32a645c8..58392a6b48b5 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1548,8 +1548,7 @@ int hci_register_dev(struct hci_dev *hdev) struct list_head *head = &hci_dev_list, *p; int i, id, error; - BT_DBG("%p name %s bus %d owner %p", hdev, hdev->name, - hdev->bus, hdev->owner); + BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); if (!hdev->open || !hdev->close) return -EINVAL; -- cgit From 4c724c7135ca2b407bd318b4267456a7b5723825 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sat, 7 Jan 2012 15:47:23 +0100 Subject: Bluetooth: Correctly take hci_dev->dev refcount The hci_dev->dev device structure has an internal refcount. This refcount is used to protect the whole hci_dev object. However, we currently do not use it. Therefore, if someone calls hci_free_dev() we currently immediately destroy the hci_dev object because we never took the device refcount. This even happens if the hci_dev->refcnt is not 0. In fact, the hci_dev->refcnt is totally useless in its current state. Therefore, we simply remove hci_dev->refcnt and instead use hci_dev->dev refcnt. This fixes all the symptoms and also correctly integrates the device structure into our bluetooth bus system. Signed-off-by: David Herrmann Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 5 ++--- net/bluetooth/hci_core.c | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 99984688ccdd..4ccf3749a9a7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -129,7 +129,6 @@ struct adv_entry { struct hci_dev { struct list_head list; struct mutex lock; - atomic_t refcnt; char name[8]; unsigned long flags; @@ -592,7 +591,7 @@ static inline void hci_conn_put(struct hci_conn *conn) /* ----- HCI Devices ----- */ static inline void __hci_dev_put(struct hci_dev *d) { - atomic_dec(&d->refcnt); + put_device(&d->dev); } /* @@ -603,7 +602,7 @@ static inline void __hci_dev_put(struct hci_dev *d) static inline struct hci_dev *__hci_dev_hold(struct hci_dev *d) { - atomic_inc(&d->refcnt); + get_device(&d->dev); return d; } diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 58392a6b48b5..f5fba65a9e59 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1571,7 +1571,6 @@ int hci_register_dev(struct hci_dev *hdev) hdev->id = id; list_add_tail(&hdev->list, head); - atomic_set(&hdev->refcnt, 1); mutex_init(&hdev->lock); hdev->flags = 0; @@ -1655,6 +1654,7 @@ int hci_register_dev(struct hci_dev *hdev) schedule_work(&hdev->power_on); hci_notify(hdev, HCI_DEV_REG); + __hci_dev_hold(hdev); return id; -- cgit From dc946bd86f725c42c3ab1caf9966d29f5b364fea Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sat, 7 Jan 2012 15:47:24 +0100 Subject: Bluetooth: Remove __hci_dev_put/hold Since we remove the owner field of hci_dev hci_dev_put and __hci_dev_put do the same so we can merge them into one function. Same for hci_dev_hold and __hci_dev_hold. Signed-off-by: David Herrmann Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 4 ++-- include/net/bluetooth/hci_core.h | 12 ++---------- net/bluetooth/hci_core.c | 4 ++-- 3 files changed, 6 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index a36888a9c205..d7664ffc5183 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1079,7 +1079,7 @@ static void btusb_disconnect(struct usb_interface *intf) hdev = data->hdev; - __hci_dev_hold(hdev); + hci_dev_hold(hdev); usb_set_intfdata(data->intf, NULL); @@ -1093,7 +1093,7 @@ static void btusb_disconnect(struct usb_interface *intf) else if (data->isoc) usb_driver_release_interface(&btusb_driver, data->isoc); - __hci_dev_put(hdev); + hci_dev_put(hdev); hci_free_dev(hdev); kfree(data); diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4ccf3749a9a7..f9c88251fe1a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -589,25 +589,17 @@ static inline void hci_conn_put(struct hci_conn *conn) } /* ----- HCI Devices ----- */ -static inline void __hci_dev_put(struct hci_dev *d) +static inline void hci_dev_put(struct hci_dev *d) { put_device(&d->dev); } -/* - * hci_dev_put and hci_dev_hold are macros to avoid dragging all the - * overhead of all the modular infrastructure into this header. - */ -#define hci_dev_put(d) __hci_dev_put(d) - -static inline struct hci_dev *__hci_dev_hold(struct hci_dev *d) +static inline struct hci_dev *hci_dev_hold(struct hci_dev *d) { get_device(&d->dev); return d; } -#define hci_dev_hold(d) __hci_dev_hold(d) - #define hci_dev_lock(d) mutex_lock(&d->lock) #define hci_dev_unlock(d) mutex_unlock(&d->lock) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f5fba65a9e59..a3113f8c1f93 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1654,7 +1654,7 @@ int hci_register_dev(struct hci_dev *hdev) schedule_work(&hdev->power_on); hci_notify(hdev, HCI_DEV_REG); - __hci_dev_hold(hdev); + hci_dev_hold(hdev); return id; @@ -1717,7 +1717,7 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_adv_entries_clear(hdev); hci_dev_unlock(hdev); - __hci_dev_put(hdev); + hci_dev_put(hdev); } EXPORT_SYMBOL(hci_unregister_dev); -- cgit From cbe8fed490601862310f035b9973509b8634998e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 8 Jan 2012 22:51:16 +0200 Subject: Bluetooth: Remove bogus inline declaration from l2cap_chan_connect As reported by Dan Carpenter this function causes a Sparse warning and shouldn't be declared inline: include/net/bluetooth/l2cap.h:837:30 error: marked inline, but without a definition" Reported-by: Dan Carpenter Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 2 +- net/bluetooth/l2cap_core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index ab8f7d83079a..26486e182f55 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -834,7 +834,7 @@ int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid); struct l2cap_chan *l2cap_chan_create(struct sock *sk); void l2cap_chan_close(struct l2cap_chan *chan, int reason); void l2cap_chan_destroy(struct l2cap_chan *chan); -inline int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, +int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst); int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, u32 priority); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index e35ff4cd33ce..dcccae04ae08 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1120,7 +1120,7 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr return c1; } -inline int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst) +int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst) { struct sock *sk = chan->sk; bdaddr_t *src = &bt_sk(sk)->src; -- cgit From a8b2d5c2cfe1c6398e3fdd4372c4ae7f74fb4493 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 8 Jan 2012 23:11:15 +0200 Subject: Bluetooth: Move mgmt related flags from hdev->flags to hdev->dev_flags There's no point in exposing these to user-space (which is what happens to everything in hdev->flags) so move them to dev_flags instead. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 16 ++++++++-------- net/bluetooth/hci_core.c | 30 +++++++++++++++--------------- net/bluetooth/hci_event.c | 44 ++++++++++++++++++++++---------------------- net/bluetooth/mgmt.c | 30 +++++++++++++++--------------- net/bluetooth/smp.c | 2 +- 5 files changed, 61 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 4202c9cb497e..3ee39ed9c29b 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -77,14 +77,6 @@ enum { HCI_RAW, - HCI_SETUP, - HCI_AUTO_OFF, - HCI_MGMT, - HCI_PAIRABLE, - HCI_SERVICE_CACHE, - HCI_LINK_KEYS, - HCI_DEBUG_KEYS, - HCI_RESET, }; @@ -93,6 +85,14 @@ enum { * states from the controller. */ enum { + HCI_SETUP, + HCI_AUTO_OFF, + HCI_MGMT, + HCI_PAIRABLE, + HCI_SERVICE_CACHE, + HCI_LINK_KEYS, + HCI_DEBUG_KEYS, + HCI_LE_SCAN, }; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index a3113f8c1f93..3acb23cf6ee4 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -668,7 +668,7 @@ int hci_dev_open(__u16 dev) hci_dev_hold(hdev); set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); - if (!test_bit(HCI_SETUP, &hdev->flags)) { + if (!test_bit(HCI_SETUP, &hdev->dev_flags)) { hci_dev_lock(hdev); mgmt_powered(hdev, 1); hci_dev_unlock(hdev); @@ -722,10 +722,10 @@ static int hci_dev_do_close(struct hci_dev *hdev) hdev->discov_timeout = 0; } - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags)) + if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) cancel_delayed_work(&hdev->power_off); - if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->flags)) + if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) cancel_delayed_work(&hdev->service_cache); hci_dev_lock(hdev); @@ -947,11 +947,11 @@ int hci_get_dev_list(void __user *arg) read_lock(&hci_dev_list_lock); list_for_each_entry(hdev, &hci_dev_list, list) { - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags)) + if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) cancel_delayed_work(&hdev->power_off); - if (!test_bit(HCI_MGMT, &hdev->flags)) - set_bit(HCI_PAIRABLE, &hdev->flags); + if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + set_bit(HCI_PAIRABLE, &hdev->dev_flags); (dr + n)->dev_id = hdev->id; (dr + n)->dev_opt = hdev->flags; @@ -983,11 +983,11 @@ int hci_get_dev_info(void __user *arg) if (!hdev) return -ENODEV; - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags)) + if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) cancel_delayed_work_sync(&hdev->power_off); - if (!test_bit(HCI_MGMT, &hdev->flags)) - set_bit(HCI_PAIRABLE, &hdev->flags); + if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + set_bit(HCI_PAIRABLE, &hdev->dev_flags); strcpy(di.name, hdev->name); di.bdaddr = hdev->bdaddr; @@ -1067,11 +1067,11 @@ static void hci_power_on(struct work_struct *work) if (hci_dev_open(hdev->id) < 0) return; - if (test_bit(HCI_AUTO_OFF, &hdev->flags)) + if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) schedule_delayed_work(&hdev->power_off, msecs_to_jiffies(AUTO_OFF_TIMEOUT)); - if (test_and_clear_bit(HCI_SETUP, &hdev->flags)) + if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) mgmt_index_added(hdev); } @@ -1082,7 +1082,7 @@ static void hci_power_off(struct work_struct *work) BT_DBG("%s", hdev->name); - clear_bit(HCI_AUTO_OFF, &hdev->flags); + clear_bit(HCI_AUTO_OFF, &hdev->dev_flags); hci_dev_close(hdev->id); } @@ -1649,8 +1649,8 @@ int hci_register_dev(struct hci_dev *hdev) } } - set_bit(HCI_AUTO_OFF, &hdev->flags); - set_bit(HCI_SETUP, &hdev->flags); + set_bit(HCI_AUTO_OFF, &hdev->dev_flags); + set_bit(HCI_SETUP, &hdev->dev_flags); schedule_work(&hdev->power_on); hci_notify(hdev, HCI_DEV_REG); @@ -1686,7 +1686,7 @@ void hci_unregister_dev(struct hci_dev *hdev) kfree_skb(hdev->reassembly[i]); if (!test_bit(HCI_INIT, &hdev->flags) && - !test_bit(HCI_SETUP, &hdev->flags)) { + !test_bit(HCI_SETUP, &hdev->dev_flags)) { hci_dev_lock(hdev); mgmt_index_removed(hdev); hci_dev_unlock(hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0466ed9c1b47..2d39ede1f202 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -211,7 +211,7 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->flags)) + if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_set_local_name_complete(hdev, sent, status); if (status == 0) @@ -890,7 +890,7 @@ static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->flags)) + if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status); if (rp->status != 0) @@ -916,7 +916,7 @@ static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->flags)) + if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_pin_code_neg_reply_complete(hdev, &rp->bdaddr, rp->status); @@ -951,7 +951,7 @@ static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->flags)) + if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, rp->status); @@ -967,7 +967,7 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev, hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->flags)) + if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr, rp->status); @@ -982,7 +982,7 @@ static void hci_cc_user_passkey_reply(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->flags)) + if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr, rp->status); @@ -998,7 +998,7 @@ static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev, hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->flags)) + if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr, rp->status); @@ -1110,7 +1110,7 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) hci_req_complete(hdev, HCI_OP_INQUIRY, status); hci_conn_check_pending(hdev); hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->flags)) + if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_start_discovery_failed(hdev, status); hci_dev_unlock(hdev); return; @@ -1333,7 +1333,7 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status) hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->flags)) + if (test_bit(HCI_MGMT, &hdev->dev_flags)) hci_resolve_next_name(hdev, &cp->bdaddr); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); @@ -1555,7 +1555,7 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags)) return; - if (!test_bit(HCI_MGMT, &hdev->flags)) + if (!test_bit(HCI_MGMT, &hdev->dev_flags)) return; hci_dev_lock(hdev); @@ -1876,7 +1876,7 @@ static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->flags)) { + if (test_bit(HCI_MGMT, &hdev->dev_flags)) { if (ev->status == 0) mgmt_remote_name(hdev, &ev->bdaddr, ev->name); @@ -2505,10 +2505,10 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff hci_conn_put(conn); } - if (!test_bit(HCI_PAIRABLE, &hdev->flags)) + if (!test_bit(HCI_PAIRABLE, &hdev->dev_flags)) hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(ev->bdaddr), &ev->bdaddr); - else if (test_bit(HCI_MGMT, &hdev->flags)) { + else if (test_bit(HCI_MGMT, &hdev->dev_flags)) { u8 secure; if (conn->pending_sec_level == BT_SECURITY_HIGH) @@ -2532,7 +2532,7 @@ static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff BT_DBG("%s", hdev->name); - if (!test_bit(HCI_LINK_KEYS, &hdev->flags)) + if (!test_bit(HCI_LINK_KEYS, &hdev->dev_flags)) return; hci_dev_lock(hdev); @@ -2547,7 +2547,7 @@ static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff BT_DBG("%s found key type %u for %s", hdev->name, key->type, batostr(&ev->bdaddr)); - if (!test_bit(HCI_DEBUG_KEYS, &hdev->flags) && + if (!test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) && key->type == HCI_LK_DEBUG_COMBINATION) { BT_DBG("%s ignoring debug key", hdev->name); goto not_found; @@ -2609,7 +2609,7 @@ static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff hci_conn_put(conn); } - if (test_bit(HCI_LINK_KEYS, &hdev->flags)) + if (test_bit(HCI_LINK_KEYS, &hdev->dev_flags)) hci_add_link_key(hdev, conn, 1, &ev->bdaddr, ev->link_key, ev->key_type, pin_len); @@ -2890,7 +2890,7 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct data.rssi = info->rssi; data.ssp_mode = 0x01; - if (test_bit(HCI_MGMT, &hdev->flags)) + if (test_bit(HCI_MGMT, &hdev->dev_flags)) name_known = eir_has_complete_name(info->data, sizeof(info->data)); else @@ -2939,10 +2939,10 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff hci_conn_hold(conn); - if (!test_bit(HCI_MGMT, &hdev->flags)) + if (!test_bit(HCI_MGMT, &hdev->dev_flags)) goto unlock; - if (test_bit(HCI_PAIRABLE, &hdev->flags) || + if (test_bit(HCI_PAIRABLE, &hdev->dev_flags) || (conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) { struct hci_cp_io_capability_reply cp; @@ -3005,7 +3005,7 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev, hci_dev_lock(hdev); - if (!test_bit(HCI_MGMT, &hdev->flags)) + if (!test_bit(HCI_MGMT, &hdev->dev_flags)) goto unlock; conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); @@ -3071,7 +3071,7 @@ static inline void hci_user_passkey_request_evt(struct hci_dev *hdev, hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->flags)) + if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_user_passkey_request(hdev, &ev->bdaddr); hci_dev_unlock(hdev); @@ -3130,7 +3130,7 @@ static inline void hci_remote_oob_data_request_evt(struct hci_dev *hdev, hci_dev_lock(hdev); - if (!test_bit(HCI_MGMT, &hdev->flags)) + if (!test_bit(HCI_MGMT, &hdev->dev_flags)) goto unlock; data = hci_find_remote_oob_data(hdev, &ev->bdaddr); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 295cfc8a3076..3de1e909471a 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -226,10 +226,10 @@ static int read_index_list(struct sock *sk) i = 0; list_for_each_entry(d, &hci_dev_list, list) { - if (test_and_clear_bit(HCI_AUTO_OFF, &d->flags)) + if (test_and_clear_bit(HCI_AUTO_OFF, &d->dev_flags)) cancel_delayed_work(&d->power_off); - if (test_bit(HCI_SETUP, &d->flags)) + if (test_bit(HCI_SETUP, &d->dev_flags)) continue; put_unaligned_le16(d->id, &rp->index[i++]); @@ -285,7 +285,7 @@ static u32 get_current_settings(struct hci_dev *hdev) if (test_bit(HCI_ISCAN, &hdev->flags)) settings |= MGMT_SETTING_DISCOVERABLE; - if (test_bit(HCI_PAIRABLE, &hdev->flags)) + if (test_bit(HCI_PAIRABLE, &hdev->dev_flags)) settings |= MGMT_SETTING_PAIRABLE; if (!(hdev->features[4] & LMP_NO_BREDR)) @@ -419,7 +419,7 @@ static int update_eir(struct hci_dev *hdev) if (hdev->ssp_mode == 0) return 0; - if (test_bit(HCI_SERVICE_CACHE, &hdev->flags)) + if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) return 0; memset(&cp, 0, sizeof(cp)); @@ -451,7 +451,7 @@ static int update_class(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - if (test_bit(HCI_SERVICE_CACHE, &hdev->flags)) + if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) return 0; cod[0] = hdev->minor_class; @@ -469,7 +469,7 @@ static void service_cache_off(struct work_struct *work) struct hci_dev *hdev = container_of(work, struct hci_dev, service_cache.work); - if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->flags)) + if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) return; hci_dev_lock(hdev); @@ -482,10 +482,10 @@ static void service_cache_off(struct work_struct *work) static void mgmt_init_hdev(struct hci_dev *hdev) { - if (!test_and_set_bit(HCI_MGMT, &hdev->flags)) + if (!test_and_set_bit(HCI_MGMT, &hdev->dev_flags)) INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off); - if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->flags)) + if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) schedule_delayed_work(&hdev->service_cache, msecs_to_jiffies(SERVICE_CACHE_TIMEOUT)); } @@ -502,7 +502,7 @@ static int read_controller_info(struct sock *sk, u16 index) return cmd_status(sk, index, MGMT_OP_READ_INFO, MGMT_STATUS_INVALID_PARAMS); - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags)) + if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) cancel_delayed_work_sync(&hdev->power_off); hci_dev_lock(hdev); @@ -851,9 +851,9 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data, hci_dev_lock(hdev); if (cp->val) - set_bit(HCI_PAIRABLE, &hdev->flags); + set_bit(HCI_PAIRABLE, &hdev->dev_flags); else - clear_bit(HCI_PAIRABLE, &hdev->flags); + clear_bit(HCI_PAIRABLE, &hdev->dev_flags); err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev); if (err < 0) @@ -1008,7 +1008,7 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data, hdev->major_class = cp->major; hdev->minor_class = cp->minor; - if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->flags)) { + if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) { hci_dev_unlock(hdev); cancel_delayed_work_sync(&hdev->service_cache); hci_dev_lock(hdev); @@ -1063,12 +1063,12 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data, hci_link_keys_clear(hdev); - set_bit(HCI_LINK_KEYS, &hdev->flags); + set_bit(HCI_LINK_KEYS, &hdev->dev_flags); if (cp->debug_keys) - set_bit(HCI_DEBUG_KEYS, &hdev->flags); + set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); else - clear_bit(HCI_DEBUG_KEYS, &hdev->flags); + clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); for (i = 0; i < key_count; i++) { struct mgmt_link_key_info *key = &cp->keys[i]; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 32c47de30344..65a90242d990 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -217,7 +217,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, { u8 dist_keys = 0; - if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->flags)) { + if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->dev_flags)) { dist_keys = SMP_DIST_ENC_KEY; authreq |= SMP_AUTH_BONDING; } else { -- cgit From a3d4e20a88f54571d794cca365f232bfed0669bb Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 9 Jan 2012 00:53:02 +0200 Subject: Bluetooth: Sort to-be-resolved devices by RSSI during discovery This patch makes sure that devices with stronger signal (RSSI closer to 0) are sorted first in the resolve list and will therefore get their names resolved first during device discovery. Since it's more likely that the device the user is trying to discover has a strong signal due to its proximity this ensures that the user gets the "device found" event for it more quickly. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_core.c | 28 +++++++++++++++++++++++++++- net/bluetooth/mgmt.c | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f9c88251fe1a..59e3541e9fc7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -395,6 +395,8 @@ struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev, bdaddr_t *bdaddr, int state); +void hci_inquiry_cache_update_resolve(struct hci_dev *hdev, + struct inquiry_entry *ie); bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, bool name_known); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3acb23cf6ee4..9963121028e4 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -457,6 +457,25 @@ struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev, return NULL; } +void hci_inquiry_cache_update_resolve(struct hci_dev *hdev, + struct inquiry_entry *ie) +{ + struct discovery_state *cache = &hdev->discovery; + struct list_head *pos = &cache->resolve; + struct inquiry_entry *p; + + list_del(&ie->list); + + list_for_each_entry(p, &cache->resolve, list) { + if (p->name_state != NAME_PENDING && + abs(p->data.rssi) >= abs(ie->data.rssi)) + break; + pos = &p->list; + } + + list_add(&ie->list, pos); +} + bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, bool name_known) { @@ -466,8 +485,15 @@ bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr)); ie = hci_inquiry_cache_lookup(hdev, &data->bdaddr); - if (ie) + if (ie) { + if (ie->name_state == NAME_NEEDED && + data->rssi != ie->data.rssi) { + ie->data.rssi = data->rssi; + hci_inquiry_cache_update_resolve(hdev, ie); + } + goto update; + } /* Entry not in the cache. Add new one. */ ie = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 3de1e909471a..2dae2e8f6234 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2043,7 +2043,7 @@ static int confirm_name(struct sock *sk, u16 index, unsigned char *data, list_del(&e->list); } else { e->name_state = NAME_NEEDED; - list_move(&e->list, &hdev->discovery.resolve); + hci_inquiry_cache_update_resolve(hdev, e); } err = 0; -- cgit From 8b281b9c7820b054d15cf471c418fd884cbbec78 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 10 Jan 2012 18:33:50 -0200 Subject: Bluetooth: Fix 'enable_hs' type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following build warning: CC [M] net/bluetooth/hci_core.o net/bluetooth/hci_core.c: In function ‘__check_enable_hs’: net/bluetooth/hci_core.c:2587: warning: return from incompatible pointer type module_param in hci_core.c passes 'enable_hs' as bool format, so fix this variable definition type. Signed-off-by: Fabio Estevam Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 2 +- net/bluetooth/hci_core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 3ee39ed9c29b..84993073c2f1 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1415,6 +1415,6 @@ struct hci_inquiry_req { }; #define IREQ_CACHE_FLUSH 0x0001 -extern int enable_hs; +extern bool enable_hs; #endif /* __HCI_H */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9963121028e4..91166dbbe35c 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -55,7 +55,7 @@ #define AUTO_OFF_TIMEOUT 2000 -int enable_hs; +bool enable_hs; static void hci_rx_work(struct work_struct *work); static void hci_cmd_work(struct work_struct *work); -- cgit From 75b93b59ad625d734b1c7d7e05bf6b9898c8c28f Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Wed, 11 Jan 2012 10:59:45 +0100 Subject: Bluetooth: Make l2cap_clear_timer return if timer was running or not This is usefull when need to make action after timer was cleared depending on if it was running or not. Signed-off-by: Szymon Janc Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 26486e182f55..238daf84184c 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -616,11 +616,16 @@ static inline void l2cap_set_timer(struct l2cap_chan *chan, schedule_delayed_work(work, timeout); } -static inline void l2cap_clear_timer(struct l2cap_chan *chan, +static inline bool l2cap_clear_timer(struct l2cap_chan *chan, struct delayed_work *work) { - if (__cancel_delayed_work(work)) + bool ret; + + ret = __cancel_delayed_work(work); + if (ret) l2cap_chan_put(chan); + + return ret; } #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) -- cgit From 7d262f86f6b73efb500be9d9242ef0673221493d Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Tue, 10 Jan 2012 18:20:49 -0300 Subject: Bluetooth: Add 'eir_len' param to mgmt_device_found() This patch adds a new parameter to mgmt_device_found() to inform the length of 'eir' pointer. EIR data from LE advertising report event doesn't have a fixed length as EIR data from extended inquiry result event does. We needed to change mgmt_device_found() so it copies 'eir_len' bytes instead of HCI_MAX_EIR_LENGTH. Signed-off-by: Andre Guedes Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_event.c | 10 ++++++---- net/bluetooth/mgmt.c | 7 +++++-- 3 files changed, 12 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 59e3541e9fc7..393acd071cb6 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -925,7 +925,7 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, u8 *randomizer, u8 status); int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *dev_class, s8 rssi, - u8 cfm_name, u8 *eir); + u8 cfm_name, u8 *eir, u8 eir_len); int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name); int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status); int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f3dafae6e1db..3323dc6c9868 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1608,7 +1608,8 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff * name_known = hci_inquiry_cache_update(hdev, &data, false); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, 0, !name_known, NULL); + info->dev_class, 0, !name_known, + NULL, 0); } hci_dev_unlock(hdev); @@ -2705,7 +2706,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct false); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, - !name_known, NULL); + !name_known, NULL, 0); } } else { struct inquiry_info_with_rssi *info = (void *) (skb->data + 1); @@ -2723,7 +2724,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct false); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, - !name_known, NULL); + !name_known, NULL, 0); } } @@ -2900,7 +2901,8 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct name_known = hci_inquiry_cache_update(hdev, &data, name_known); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, - !name_known, info->data); + !name_known, info->data, + sizeof(info->data)); } hci_dev_unlock(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 2dae2e8f6234..e7bbad80fa7e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2780,10 +2780,13 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *dev_class, s8 rssi, - u8 cfm_name, u8 *eir) + u8 cfm_name, u8 *eir, u8 eir_len) { struct mgmt_ev_device_found ev; + if (eir_len > sizeof(ev.eir)) + return -EINVAL; + memset(&ev, 0, sizeof(ev)); bacpy(&ev.addr.bdaddr, bdaddr); @@ -2792,7 +2795,7 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, ev.confirm_name = cfm_name; if (eir) - memcpy(ev.eir, eir, sizeof(ev.eir)); + memcpy(ev.eir, eir, eir_len); if (dev_class) memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class)); -- cgit From afc747a600ff2e3a4eef8f312fc766608a1360e2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 15 Jan 2012 18:11:07 +0200 Subject: Bluetooth: Rename mgmt connected events to match user space User space uses device_(dis)connected instead of just (dis)connected so rename the defines and functions to match this. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 6 +++--- include/net/bluetooth/mgmt.h | 4 ++-- net/bluetooth/hci_event.c | 6 +++--- net/bluetooth/mgmt.c | 12 +++++++----- 4 files changed, 15 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 393acd071cb6..f3fbfd6f6c3b 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -896,10 +896,10 @@ int mgmt_connectable(struct hci_dev *hdev, u8 connectable); int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, u8 persistent); -int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type); -int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, +int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type); +int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type); int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status); int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index be65d3417883..d1d13dc0cca8 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -328,9 +328,9 @@ struct mgmt_ev_new_link_key { struct mgmt_link_key_info key; } __packed; -#define MGMT_EV_CONNECTED 0x000A +#define MGMT_EV_DEVICE_CONNECTED 0x000A -#define MGMT_EV_DISCONNECTED 0x000B +#define MGMT_EV_DEVICE_DISCONNECTED 0x000B #define MGMT_EV_CONNECT_FAILED 0x000C struct mgmt_ev_connect_failed { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 54132a909ea5..e13ce945afc4 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1643,7 +1643,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s conn->state = BT_CONFIG; hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; - mgmt_connected(hdev, &ev->bdaddr, conn->type, + mgmt_device_connected(hdev, &ev->bdaddr, conn->type, conn->dst_type); } else conn->state = BT_CONNECTED; @@ -1789,7 +1789,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff if (ev->status != 0) mgmt_disconnect_failed(hdev, &conn->dst, ev->status); else - mgmt_disconnected(hdev, &conn->dst, conn->type, + mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type); } @@ -3188,7 +3188,7 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff goto unlock; } - mgmt_connected(hdev, &ev->bdaddr, conn->type, conn->dst_type); + mgmt_device_connected(hdev, &ev->bdaddr, conn->type, conn->dst_type); conn->sec_level = BT_SECURITY_LOW; conn->handle = __le16_to_cpu(ev->handle); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e7bbad80fa7e..c8042c6e2b46 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2464,7 +2464,7 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL); } -int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, +int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type) { struct mgmt_addr_info ev; @@ -2472,7 +2472,8 @@ int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, bacpy(&ev.bdaddr, bdaddr); ev.type = link_to_mgmt(link_type, addr_type); - return mgmt_event(MGMT_EV_CONNECTED, hdev, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, &ev, sizeof(ev), + NULL); } static void disconnect_rsp(struct pending_cmd *cmd, void *data) @@ -2509,8 +2510,8 @@ static void remove_keys_rsp(struct pending_cmd *cmd, void *data) mgmt_pending_remove(cmd); } -int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type) +int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type) { struct mgmt_addr_info ev; struct sock *sk = NULL; @@ -2521,7 +2522,8 @@ int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, bacpy(&ev.bdaddr, bdaddr); ev.type = link_to_mgmt(link_type, addr_type); - err = mgmt_event(MGMT_EV_DISCONNECTED, hdev, &ev, sizeof(ev), sk); + err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev), + sk); if (sk) sock_put(sk); -- cgit From e319d2e74378660c5e09a1b8703663ba97f0f62a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 15 Jan 2012 19:51:59 +0200 Subject: Bluetooth: Add eir_len parameter to mgmt_ev_device_found This patch add a two byte eir_len parameter mgmt_ev_device_found. Since it's unlikely that the data will in the short term be much bigger than conventional EIR lengths just use a small stack based buffer for now to avoid dynamic memory allocation & freeing. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- include/net/bluetooth/mgmt.h | 3 ++- net/bluetooth/mgmt.c | 28 +++++++++++++++------------- 3 files changed, 18 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f3fbfd6f6c3b..33dff8ef2e08 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -925,7 +925,7 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, u8 *randomizer, u8 status); int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *dev_class, s8 rssi, - u8 cfm_name, u8 *eir, u8 eir_len); + u8 cfm_name, u8 *eir, u16 eir_len); int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name); int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status); int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index d1d13dc0cca8..4f166c834ddb 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -368,7 +368,8 @@ struct mgmt_ev_device_found { __u8 dev_class[3]; __s8 rssi; __u8 confirm_name; - __u8 eir[HCI_MAX_EIR_LENGTH]; + __le16 eir_len; + __u8 eir[0]; } __packed; #define MGMT_EV_REMOTE_NAME 0x0012 diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c8042c6e2b46..b7e7fdfaee38 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2782,27 +2782,29 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *dev_class, s8 rssi, - u8 cfm_name, u8 *eir, u8 eir_len) + u8 cfm_name, u8 *eir, u16 eir_len) { - struct mgmt_ev_device_found ev; + char buf[512]; + struct mgmt_ev_device_found *ev = (void *) buf; + size_t ev_size = sizeof(*ev) + eir_len; - if (eir_len > sizeof(ev.eir)) + if (ev_size > sizeof(buf)) return -EINVAL; - memset(&ev, 0, sizeof(ev)); + bacpy(&ev->addr.bdaddr, bdaddr); + ev->addr.type = link_to_mgmt(link_type, addr_type); + ev->rssi = rssi; + ev->confirm_name = cfm_name; - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = link_to_mgmt(link_type, addr_type); - ev.rssi = rssi; - ev.confirm_name = cfm_name; - - if (eir) - memcpy(ev.eir, eir, eir_len); + if (eir_len > 0) { + put_unaligned_le16(eir_len, &ev->eir_len); + memcpy(ev->eir, eir, eir_len); + } if (dev_class) - memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class)); + memcpy(ev->dev_class, dev_class, sizeof(ev->dev_class)); - return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL); } int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name) -- cgit From 9ec9fc8a99acc8a521337612b5e49521c49cb4c5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 15 Jan 2012 20:46:33 +0200 Subject: Bluetooth: Add missing EIR defines to hci.h This patch adds missing EIR defines (as specified in the Bluetooth Assigned Numbers document) to hci.h. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 84993073c2f1..dd2cc6cb35b3 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -296,6 +296,9 @@ enum { #define EIR_NAME_SHORT 0x08 /* shortened local name */ #define EIR_NAME_COMPLETE 0x09 /* complete local name */ #define EIR_TX_POWER 0x0A /* transmit power level */ +#define EIR_CLASS_OF_DEV 0x0D /* Class of Device */ +#define EIR_SSP_HASH_C 0x0E /* Simple Pairing Hash C */ +#define EIR_SSP_RAND_R 0x0F /* Simple Pairing Randomizer R */ #define EIR_DEVICE_ID 0x10 /* device ID */ /* ----- HCI Commands ---- */ -- cgit From 6759a67579a927f2a92f398cf67555e6cc92d0ff Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 15 Jan 2012 20:51:14 +0200 Subject: Bluetooth: Move eir_has_data_field to hci_core.h This makes the function accessible from all places it's needed (e.g. mgmt.c and hci_event.c). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 25 +++++++++++++++++++++++++ net/bluetooth/hci_event.c | 25 ------------------------- 2 files changed, 25 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 33dff8ef2e08..393bb73fc999 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -868,6 +868,31 @@ static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status, read_unlock(&hci_cb_list_lock); } +static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type) +{ + u8 field_len; + size_t parsed; + + for (parsed = 0; parsed < data_len - 1; parsed += field_len) { + field_len = data[0]; + + if (field_len == 0) + break; + + parsed += field_len + 1; + + if (parsed > data_len) + break; + + if (data[1] == type) + return true; + + data += field_len + 1; + } + + return false; +} + int hci_register_cb(struct hci_cb *hcb); int hci_unregister_cb(struct hci_cb *hcb); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d256042a83cf..e93afebdcf84 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2842,31 +2842,6 @@ static inline void hci_sniff_subrate_evt(struct hci_dev *hdev, struct sk_buff *s BT_DBG("%s status %d", hdev->name, ev->status); } -static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type) -{ - u8 field_len; - size_t parsed; - - for (parsed = 0; parsed < data_len - 1; parsed += field_len) { - field_len = data[0]; - - if (field_len == 0) - break; - - parsed += field_len + 1; - - if (parsed > data_len) - break; - - if (data[1] == type) - return true; - - data += field_len + 1; - } - - return false; -} - static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct inquiry_data data; -- cgit From 1dc06093a9f353ef19b7b5180602884d0ce065c5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 15 Jan 2012 21:01:23 +0200 Subject: Bluetooth: Merge device class into the EIR data in mgmt_ev_device_found There's no need to have a separate device class field since the same information can be encoded into the EIR data. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 11 +++++++++++ include/net/bluetooth/mgmt.h | 1 - net/bluetooth/mgmt.c | 20 +++++++++++++------- 3 files changed, 24 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 393bb73fc999..a0311018a4d0 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -893,6 +893,17 @@ static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type) return false; } +static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, + u8 data_len) +{ + eir[eir_len++] = sizeof(type) + data_len; + eir[eir_len++] = type; + memcpy(&eir[eir_len], data, data_len); + eir_len += data_len; + + return eir_len; +} + int hci_register_cb(struct hci_cb *hcb); int hci_unregister_cb(struct hci_cb *hcb); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 4f166c834ddb..bdace523b910 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -365,7 +365,6 @@ struct mgmt_ev_auth_failed { #define MGMT_EV_DEVICE_FOUND 0x0011 struct mgmt_ev_device_found { struct mgmt_addr_info addr; - __u8 dev_class[3]; __s8 rssi; __u8 confirm_name; __le16 eir_len; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b7e7fdfaee38..bec64c98b6a9 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2786,23 +2786,29 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, { char buf[512]; struct mgmt_ev_device_found *ev = (void *) buf; - size_t ev_size = sizeof(*ev) + eir_len; + size_t ev_size; - if (ev_size > sizeof(buf)) + /* Leave 5 bytes for a potential CoD field */ + if (sizeof(*ev) + eir_len + 5 > sizeof(buf)) return -EINVAL; + memset(buf, 0, sizeof(buf)); + bacpy(&ev->addr.bdaddr, bdaddr); ev->addr.type = link_to_mgmt(link_type, addr_type); ev->rssi = rssi; ev->confirm_name = cfm_name; - if (eir_len > 0) { - put_unaligned_le16(eir_len, &ev->eir_len); + if (eir_len > 0) memcpy(ev->eir, eir, eir_len); - } - if (dev_class) - memcpy(ev->dev_class, dev_class, sizeof(ev->dev_class)); + if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV)) + eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, + dev_class, 3); + + put_unaligned_le16(eir_len, &ev->eir_len); + + ev_size = sizeof(*ev) + eir_len; return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL); } -- cgit From 51a8efd7d02c13cb1c6fdd1cd66788792a3fcc7c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Jan 2012 06:10:31 +0200 Subject: Bluetooth: Rename conn->pend to conn->flags These flags can and will be used for more general purpose values than just pending state transitions so the more common name "flags" makes more sense than "pend". Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 6 +++--- net/bluetooth/hci_conn.c | 20 ++++++++++---------- net/bluetooth/hci_event.c | 36 ++++++++++++++++++------------------ net/bluetooth/l2cap_core.c | 2 +- net/bluetooth/smp.c | 19 +++++++++---------- 5 files changed, 41 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index a0311018a4d0..0938168ce602 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -300,7 +300,7 @@ struct hci_conn { __u8 io_capability; __u8 power_save; __u16 disc_timeout; - unsigned long pend; + unsigned long flags; __u8 remote_cap; __u8 remote_oob; @@ -764,7 +764,7 @@ static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status) if (conn->type != ACL_LINK && conn->type != LE_LINK) return; - if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) + if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) return; encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00; @@ -805,7 +805,7 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) hci_proto_auth_cfm(conn, status); - if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) + if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) return; encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 3db432473ad5..8a4678a2c982 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -337,7 +337,7 @@ static void hci_conn_enter_sniff_mode(struct hci_conn *conn) hci_send_cmd(hdev, HCI_OP_SNIFF_SUBRATE, sizeof(cp), &cp); } - if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { + if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) { struct hci_cp_sniff_mode cp; cp.handle = cpu_to_le16(conn->handle); cp.max_interval = cpu_to_le16(hdev->sniff_max_interval); @@ -589,9 +589,9 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 acl->power_save = 1; hci_conn_enter_active_mode(acl, BT_POWER_FORCE_ACTIVE_ON); - if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->pend)) { + if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->flags)) { /* defer SCO setup until mode change completed */ - set_bit(HCI_CONN_SCO_SETUP_PEND, &acl->pend); + set_bit(HCI_CONN_SCO_SETUP_PEND, &acl->flags); return sco; } @@ -633,13 +633,13 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) conn->auth_type = auth_type; - if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { + if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { struct hci_cp_auth_requested cp; cp.handle = cpu_to_le16(conn->handle); hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); if (conn->key_type != 0xff) - set_bit(HCI_CONN_REAUTH_PEND, &conn->pend); + set_bit(HCI_CONN_REAUTH_PEND, &conn->flags); } return 0; @@ -650,7 +650,7 @@ static void hci_conn_encrypt(struct hci_conn *conn) { BT_DBG("conn %p", conn); - if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { + if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) { struct hci_cp_set_conn_encrypt cp; cp.handle = cpu_to_le16(conn->handle); cp.encrypt = 0x01; @@ -700,7 +700,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) goto encrypt; auth: - if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) + if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) return 0; if (!hci_conn_auth(conn, sec_level, auth_type)) @@ -735,7 +735,7 @@ int hci_conn_change_link_key(struct hci_conn *conn) { BT_DBG("conn %p", conn); - if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { + if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { struct hci_cp_change_conn_link_key cp; cp.handle = cpu_to_le16(conn->handle); hci_send_cmd(conn->hdev, HCI_OP_CHANGE_CONN_LINK_KEY, @@ -754,7 +754,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role) if (!role && conn->link_mode & HCI_LM_MASTER) return 1; - if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->pend)) { + if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->flags)) { struct hci_cp_switch_role cp; bacpy(&cp.bdaddr, &conn->dst); cp.role = role; @@ -781,7 +781,7 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active) if (!conn->power_save && !force_active) goto timer; - if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { + if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) { struct hci_cp_exit_sniff_mode cp; cp.handle = cpu_to_le16(conn->handle); hci_send_cmd(hdev, HCI_OP_EXIT_SNIFF_MODE, sizeof(cp), &cp); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e93afebdcf84..d9a8618ae156 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1344,7 +1344,7 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status) if (!hci_outgoing_auth_needed(hdev, conn)) goto unlock; - if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { + if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { struct hci_cp_auth_requested cp; cp.handle = __cpu_to_le16(conn->handle); hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); @@ -1461,9 +1461,9 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status) conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); if (conn) { - clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend); + clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags); - if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend)) + if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags)) hci_sco_setup(conn, status); } @@ -1488,9 +1488,9 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status) conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); if (conn) { - clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend); + clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags); - if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend)) + if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags)) hci_sco_setup(conn, status); } @@ -1817,7 +1817,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s if (!ev->status) { if (!(conn->ssp_mode > 0 && hdev->ssp_mode > 0) && - test_bit(HCI_CONN_REAUTH_PEND, &conn->pend)) { + test_bit(HCI_CONN_REAUTH_PEND, &conn->flags)) { BT_INFO("re-auth of legacy device is not possible."); } else { conn->link_mode |= HCI_LM_AUTH; @@ -1827,8 +1827,8 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s mgmt_auth_failed(hdev, &conn->dst, ev->status); } - clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); - clear_bit(HCI_CONN_REAUTH_PEND, &conn->pend); + clear_bit(HCI_CONN_AUTH_PEND, &conn->flags); + clear_bit(HCI_CONN_REAUTH_PEND, &conn->flags); if (conn->state == BT_CONFIG) { if (!ev->status && hdev->ssp_mode > 0 && conn->ssp_mode > 0) { @@ -1850,7 +1850,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s hci_conn_put(conn); } - if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { + if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) { if (!ev->status) { struct hci_cp_set_conn_encrypt cp; cp.handle = ev->handle; @@ -1858,7 +1858,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), &cp); } else { - clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); + clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); hci_encrypt_cfm(conn, ev->status, 0x00); } } @@ -1892,7 +1892,7 @@ static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb if (!hci_outgoing_auth_needed(hdev, conn)) goto unlock; - if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { + if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { struct hci_cp_auth_requested cp; cp.handle = __cpu_to_le16(conn->handle); hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); @@ -1923,7 +1923,7 @@ static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff * conn->link_mode &= ~HCI_LM_ENCRYPT; } - clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); + clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); if (conn->state == BT_CONFIG) { if (!ev->status) @@ -1952,7 +1952,7 @@ static inline void hci_change_link_key_complete_evt(struct hci_dev *hdev, struct if (!ev->status) conn->link_mode |= HCI_LM_SECURE; - clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); + clear_bit(HCI_CONN_AUTH_PEND, &conn->flags); hci_key_change_cfm(conn, ev->status); } @@ -2336,7 +2336,7 @@ static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb conn->link_mode |= HCI_LM_MASTER; } - clear_bit(HCI_CONN_RSWITCH_PEND, &conn->pend); + clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags); hci_role_switch_cfm(conn, ev->status, ev->role); } @@ -2474,14 +2474,14 @@ static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb conn->mode = ev->mode; conn->interval = __le16_to_cpu(ev->interval); - if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { + if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) { if (conn->mode == HCI_CM_ACTIVE) conn->power_save = 1; else conn->power_save = 0; } - if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend)) + if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags)) hci_sco_setup(conn, ev->status); } @@ -3013,7 +3013,7 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev, /* If we're not the initiators request authorization to * proceed from user space (mgmt_user_confirm with * confirm_hint set to 1). */ - if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { + if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { BT_DBG("Confirming auto-accept as acceptor"); confirm_hint = 1; goto confirm; @@ -3074,7 +3074,7 @@ static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_ * initiated the authentication. A traditional auth_complete * event gets always produced as initiator and is also mapped to * the mgmt_auth_failed event */ - if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend) && ev->status != 0) + if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status != 0) mgmt_auth_failed(hdev, &conn->dst, ev->status); hci_conn_put(conn); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a66e5ec9adef..27b72d954436 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1020,7 +1020,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) __cancel_delayed_work(&conn->info_timer); - if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) { + if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) { __cancel_delayed_work(&conn->security_timer); smp_chan_destroy(conn); } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 65a90242d990..e08fe6c9c9c9 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -261,7 +261,7 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send) smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason); - clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->pend); + clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->flags); mgmt_auth_failed(conn->hcon->hdev, conn->dst, reason); cancel_delayed_work_sync(&conn->security_timer); smp_chan_destroy(conn); @@ -449,7 +449,7 @@ static void random_work(struct work_struct *work) memset(stk + smp->smp_key_size, 0, SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size); - if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) { + if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) { reason = SMP_UNSPECIFIED; goto error; } @@ -506,7 +506,7 @@ void smp_chan_destroy(struct l2cap_conn *conn) { struct smp_chan *smp = conn->smp_chan; - clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend); + clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags); if (smp->tfm) crypto_free_blkcipher(smp->tfm); @@ -571,7 +571,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) if (conn->hcon->link_mode & HCI_LM_MASTER) return SMP_CMD_NOTSUPP; - if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend)) + if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) smp = smp_chan_create(conn); smp = conn->smp_chan; @@ -707,8 +707,7 @@ static u8 smp_ltk_encrypt(struct l2cap_conn *conn) if (!key) return 0; - if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, - &hcon->pend)) + if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) return 1; master = (void *) key->data; @@ -733,7 +732,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) if (smp_ltk_encrypt(conn)) return 0; - if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) + if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) return 0; smp = smp_chan_create(conn); @@ -772,7 +771,7 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) if (smp_ltk_encrypt(conn)) goto done; - if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) + if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) return 0; smp = smp_chan_create(conn); @@ -908,7 +907,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) BT_DBG("conn %p force %d", conn, force); - if (!test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend)) + if (!test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) return 0; rsp = (void *) &smp->prsp[1]; @@ -982,7 +981,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) } if (conn->hcon->out || force) { - clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend); + clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags); cancel_delayed_work_sync(&conn->security_timer); smp_chan_destroy(conn); } -- cgit From a0c808b373e89aecc3ecae4cbdcdeff68aa12e3e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Jan 2012 09:49:58 +0200 Subject: Bluetooth: Convert hdev->out to a bool type The hdev->out variable is essentially a boolean so the type 'bool' makes more sense than u8. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_conn.c | 8 ++++---- net/bluetooth/hci_event.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 0938168ce602..520da44940e9 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -282,7 +282,7 @@ struct hci_conn { __u16 state; __u8 mode; __u8 type; - __u8 out; + bool out; __u8 attempt; __u8 dev_class[3]; __u8 features[8]; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 8a4678a2c982..a707d19ee44e 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -51,7 +51,7 @@ static void hci_le_connect(struct hci_conn *conn) struct hci_cp_le_create_conn cp; conn->state = BT_CONNECT; - conn->out = 1; + conn->out = true; conn->link_mode |= HCI_LM_MASTER; conn->sec_level = BT_SECURITY_LOW; @@ -83,7 +83,7 @@ void hci_acl_connect(struct hci_conn *conn) BT_DBG("%p", conn); conn->state = BT_CONNECT; - conn->out = 1; + conn->out = true; conn->link_mode = HCI_LM_MASTER; @@ -151,7 +151,7 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle) BT_DBG("%p", conn); conn->state = BT_CONNECT; - conn->out = 1; + conn->out = true; conn->attempt++; @@ -169,7 +169,7 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle) BT_DBG("%p", conn); conn->state = BT_CONNECT; - conn->out = 1; + conn->out = true; conn->attempt++; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d9a8618ae156..f6c13153a5e7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1154,7 +1154,7 @@ static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) if (!conn) { conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr); if (conn) { - conn->out = 1; + conn->out = true; conn->link_mode |= HCI_LM_MASTER; } else BT_ERR("No memory for new connection"); @@ -1526,7 +1526,7 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr); if (conn) { conn->dst_type = cp->peer_addr_type; - conn->out = 1; + conn->out = true; } else { BT_ERR("No memory for new connection"); } -- cgit From b644ba33699711630099efc58a4efc225560aceb Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 17 Jan 2012 21:48:47 +0200 Subject: Bluetooth: Update device_connected and device_found events to latest API This patch updates mgmt_ev_device_connected and mgmt_ev_device found to include an EIR-encoded remote name and class whenever possible. With this addition the mgmt_ev_remote_name event becomes unnecessary and can be removed. Since the connected event doesn't map to hci_conn_complete anymore a HCI_CONN_MGMT_CONNECTED flag is added to track when mgmt has been notified about a connection. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 7 ++-- include/net/bluetooth/mgmt.h | 17 +++++---- net/bluetooth/hci_event.c | 78 ++++++++++++++++++++++++++++------------ net/bluetooth/mgmt.c | 56 +++++++++++++++++++++-------- 4 files changed, 110 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 520da44940e9..18af5427fd0c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -409,6 +409,7 @@ enum { HCI_CONN_MODE_CHANGE_PEND, HCI_CONN_SCO_SETUP_PEND, HCI_CONN_LE_SMP_PEND, + HCI_CONN_MGMT_CONNECTED, }; static inline void hci_conn_hash_init(struct hci_dev *hdev) @@ -933,7 +934,8 @@ int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, u8 persistent); int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type); + u8 addr_type, u8 *name, u8 name_len, + u8 *dev_class); int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type); int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status); @@ -962,7 +964,8 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 *eir, u16 eir_len); -int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name); +int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, s8 rssi, u8 *name, u8 name_len); int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status); int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status); int mgmt_discovering(struct hci_dev *hdev, u8 discovering); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index bdace523b910..6f37983c8775 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -329,6 +329,11 @@ struct mgmt_ev_new_link_key { } __packed; #define MGMT_EV_DEVICE_CONNECTED 0x000A +struct mgmt_ev_device_connected { + struct mgmt_addr_info addr; + __le16 eir_len; + __u8 eir[0]; +} __packed; #define MGMT_EV_DEVICE_DISCONNECTED 0x000B @@ -371,20 +376,14 @@ struct mgmt_ev_device_found { __u8 eir[0]; } __packed; -#define MGMT_EV_REMOTE_NAME 0x0012 -struct mgmt_ev_remote_name { - bdaddr_t bdaddr; - __u8 name[MGMT_MAX_NAME_LENGTH]; -} __packed; - -#define MGMT_EV_DISCOVERING 0x0013 +#define MGMT_EV_DISCOVERING 0x0012 -#define MGMT_EV_DEVICE_BLOCKED 0x0014 +#define MGMT_EV_DEVICE_BLOCKED 0x0013 struct mgmt_ev_device_blocked { bdaddr_t bdaddr; } __packed; -#define MGMT_EV_DEVICE_UNBLOCKED 0x0015 +#define MGMT_EV_DEVICE_UNBLOCKED 0x0014 struct mgmt_ev_device_unblocked { bdaddr_t bdaddr; } __packed; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f6c13153a5e7..f0b08ab734d7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1286,11 +1286,36 @@ static inline int hci_resolve_name(struct hci_dev *hdev, struct inquiry_entry *e return hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); } -static void hci_resolve_next_name(struct hci_dev *hdev, bdaddr_t *bdaddr) +static bool hci_resolve_next_name(struct hci_dev *hdev) { struct discovery_state *discov = &hdev->discovery; struct inquiry_entry *e; + if (list_empty(&discov->resolve)) + return false; + + e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_NEEDED); + if (hci_resolve_name(hdev, e) == 0) { + e->name_state = NAME_PENDING; + return true; + } + + return false; +} + +static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn, + bdaddr_t *bdaddr, u8 *name, u8 name_len) +{ + struct discovery_state *discov = &hdev->discovery; + struct inquiry_entry *e; + + if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) + mgmt_device_connected(hdev, bdaddr, ACL_LINK, 0x00, + name, name_len, conn->dev_class); + + if (discov->state == DISCOVERY_STOPPED) + return; + if (discov->state == DISCOVERY_STOPPING) goto discov_complete; @@ -1301,16 +1326,13 @@ static void hci_resolve_next_name(struct hci_dev *hdev, bdaddr_t *bdaddr) if (e) { e->name_state = NAME_KNOWN; list_del(&e->list); + if (name) + mgmt_remote_name(hdev, bdaddr, ACL_LINK, 0x00, + e->data.rssi, name, name_len); } - if (list_empty(&discov->resolve)) - goto discov_complete; - - e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_NEEDED); - if (hci_resolve_name(hdev, e) == 0) { - e->name_state = NAME_PENDING; + if (hci_resolve_next_name(hdev)) return; - } discov_complete: hci_discovery_set_state(hdev, DISCOVERY_STOPPED); @@ -1334,10 +1356,11 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status) hci_dev_lock(hdev); + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); + if (test_bit(HCI_MGMT, &hdev->dev_flags)) - hci_resolve_next_name(hdev, &cp->bdaddr); + hci_check_pending_name(hdev, conn, &cp->bdaddr, NULL, 0); - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); if (!conn) goto unlock; @@ -1643,8 +1666,6 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s conn->state = BT_CONFIG; hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; - mgmt_device_connected(hdev, &ev->bdaddr, conn->type, - conn->dst_type); } else conn->state = BT_CONNECTED; @@ -1785,7 +1806,8 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff if (ev->status == 0) conn->state = BT_CLOSED; - if (conn->type == ACL_LINK || conn->type == LE_LINK) { + if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags) && + (conn->type == ACL_LINK || conn->type == LE_LINK)) { if (ev->status != 0) mgmt_disconnect_failed(hdev, &conn->dst, ev->status); else @@ -1878,14 +1900,18 @@ static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->dev_flags)) { - if (ev->status == 0) - mgmt_remote_name(hdev, &ev->bdaddr, ev->name); + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - hci_resolve_next_name(hdev, &ev->bdaddr); - } + if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + goto check_auth; - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); + if (ev->status == 0) + hci_check_pending_name(hdev, conn, &ev->bdaddr, ev->name, + strnlen(ev->name, HCI_MAX_NAME_LENGTH)); + else + hci_check_pending_name(hdev, conn, &ev->bdaddr, NULL, 0); + +check_auth: if (!conn) goto unlock; @@ -1994,7 +2020,10 @@ static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff bacpy(&cp.bdaddr, &conn->dst); cp.pscan_rep_mode = 0x02; hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); - } + } else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) + mgmt_device_connected(hdev, &conn->dst, conn->type, + conn->dst_type, NULL, 0, + conn->dev_class); if (!hci_outgoing_auth_needed(hdev, conn)) { conn->state = BT_CONNECTED; @@ -2763,7 +2792,10 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b bacpy(&cp.bdaddr, &conn->dst); cp.pscan_rep_mode = 0x02; hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); - } + } else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) + mgmt_device_connected(hdev, &conn->dst, conn->type, + conn->dst_type, NULL, 0, + conn->dev_class); if (!hci_outgoing_auth_needed(hdev, conn)) { conn->state = BT_CONNECTED; @@ -3164,7 +3196,9 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff goto unlock; } - mgmt_device_connected(hdev, &ev->bdaddr, conn->type, conn->dst_type); + if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) + mgmt_device_connected(hdev, &ev->bdaddr, conn->type, + conn->dst_type, NULL, 0, 0); conn->sec_level = BT_SECURITY_LOW; conn->handle = __le16_to_cpu(ev->handle); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bec64c98b6a9..ae9283d47e65 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1244,7 +1244,6 @@ static int get_connections(struct sock *sk, u16 index) struct mgmt_rp_get_connections *rp; struct hci_dev *hdev; struct hci_conn *c; - struct list_head *p; size_t rp_len; u16 count; int i, err; @@ -1259,8 +1258,9 @@ static int get_connections(struct sock *sk, u16 index) hci_dev_lock(hdev); count = 0; - list_for_each(p, &hdev->conn_hash.list) { - count++; + list_for_each_entry(c, &hdev->conn_hash.list, list) { + if (test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags)) + count++; } rp_len = sizeof(*rp) + (count * sizeof(struct mgmt_addr_info)); @@ -1274,6 +1274,8 @@ static int get_connections(struct sock *sk, u16 index) i = 0; list_for_each_entry(c, &hdev->conn_hash.list, list) { + if (!test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags)) + continue; bacpy(&rp->addr[i].bdaddr, &c->dst); rp->addr[i].type = link_to_mgmt(c->type, c->dst_type); if (rp->addr[i].type == MGMT_ADDR_INVALID) @@ -2465,15 +2467,28 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, } int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type) + u8 addr_type, u8 *name, u8 name_len, + u8 *dev_class) { - struct mgmt_addr_info ev; + char buf[512]; + struct mgmt_ev_device_connected *ev = (void *) buf; + u16 eir_len = 0; - bacpy(&ev.bdaddr, bdaddr); - ev.type = link_to_mgmt(link_type, addr_type); + bacpy(&ev->addr.bdaddr, bdaddr); + ev->addr.type = link_to_mgmt(link_type, addr_type); - return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, &ev, sizeof(ev), - NULL); + if (name_len > 0) + eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, + name, name_len); + + if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0) + eir_len = eir_append_data(&ev->eir[eir_len], eir_len, + EIR_CLASS_OF_DEV, dev_class, 3); + + put_unaligned_le16(eir_len, &ev->eir_len); + + return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf, + sizeof(*ev) + eir_len, NULL); } static void disconnect_rsp(struct pending_cmd *cmd, void *data) @@ -2813,16 +2828,27 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL); } -int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name) +int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, s8 rssi, u8 *name, u8 name_len) { - struct mgmt_ev_remote_name ev; + struct mgmt_ev_device_found *ev; + char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2]; + u16 eir_len; - memset(&ev, 0, sizeof(ev)); + ev = (struct mgmt_ev_device_found *) buf; - bacpy(&ev.bdaddr, bdaddr); - memcpy(ev.name, name, HCI_MAX_NAME_LENGTH); + memset(buf, 0, sizeof(buf)); + + bacpy(&ev->addr.bdaddr, bdaddr); + ev->addr.type = link_to_mgmt(link_type, addr_type); + ev->rssi = rssi; + + eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name, + name_len); + + put_unaligned_le16(eir_len, &ev->eir_len); - return mgmt_event(MGMT_EV_REMOTE_NAME, hdev, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, &ev, sizeof(ev), NULL); } int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status) -- cgit From 58a681ef1455aef9caad1d41073868fb399373f6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Jan 2012 06:47:28 +0200 Subject: Bluetooth: Merge boolean members of struct hci_conn into flags Now that the flags member of struct hci_conn is supposed to accommodate any boolean type values we can easily merge all boolean members into it. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 6 +++--- net/bluetooth/hci_conn.c | 15 +++++++++------ net/bluetooth/hci_event.c | 21 +++++++++++++-------- 3 files changed, 25 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 18af5427fd0c..7a033111c98f 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -286,7 +286,6 @@ struct hci_conn { __u8 attempt; __u8 dev_class[3]; __u8 features[8]; - __u8 ssp_mode; __u16 interval; __u16 pkt_type; __u16 link_policy; @@ -298,12 +297,10 @@ struct hci_conn { __u8 pin_length; __u8 enc_key_size; __u8 io_capability; - __u8 power_save; __u16 disc_timeout; unsigned long flags; __u8 remote_cap; - __u8 remote_oob; __u8 remote_auth; unsigned int sent; @@ -410,6 +407,9 @@ enum { HCI_CONN_SCO_SETUP_PEND, HCI_CONN_LE_SMP_PEND, HCI_CONN_MGMT_CONNECTED, + HCI_CONN_SSP_ENABLED, + HCI_CONN_POWER_SAVE, + HCI_CONN_REMOTE_OOB, }; static inline void hci_conn_hash_init(struct hci_dev *hdev) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index a707d19ee44e..8288e303621a 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -105,7 +105,8 @@ void hci_acl_connect(struct hci_conn *conn) } memcpy(conn->dev_class, ie->data.dev_class, 3); - conn->ssp_mode = ie->data.ssp_mode; + if (ie->data.ssp_mode > 0) + set_bit(HCI_CONN_SSP_ENABLED, &conn->flags); } cp.pkt_type = cpu_to_le16(conn->pkt_type); @@ -386,7 +387,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) conn->remote_auth = 0xff; conn->key_type = 0xff; - conn->power_save = 1; + set_bit(HCI_CONN_POWER_SAVE, &conn->flags); conn->disc_timeout = HCI_DISCONN_TIMEOUT; switch (type) { @@ -586,7 +587,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 if (acl->state == BT_CONNECTED && (sco->state == BT_OPEN || sco->state == BT_CLOSED)) { - acl->power_save = 1; + set_bit(HCI_CONN_POWER_SAVE, &acl->flags); hci_conn_enter_active_mode(acl, BT_POWER_FORCE_ACTIVE_ON); if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->flags)) { @@ -607,7 +608,8 @@ int hci_conn_check_link_mode(struct hci_conn *conn) { BT_DBG("conn %p", conn); - if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0 && + if (test_bit(HCI_CONN_SSP_ENABLED, &conn->flags) && + conn->hdev->ssp_mode > 0 && !(conn->link_mode & HCI_LM_ENCRYPT)) return 0; @@ -671,7 +673,8 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) /* For non 2.1 devices and low security level we don't need the link key. */ if (sec_level == BT_SECURITY_LOW && - (!conn->ssp_mode || !conn->hdev->ssp_mode)) + (!test_bit(HCI_CONN_SSP_ENABLED, &conn->flags) || + !conn->hdev->ssp_mode)) return 1; /* For other security levels we need the link key. */ @@ -778,7 +781,7 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active) if (conn->mode != HCI_CM_SNIFF) goto timer; - if (!conn->power_save && !force_active) + if (!test_bit(HCI_CONN_POWER_SAVE, &conn->flags) && !force_active) goto timer; if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f0b08ab734d7..02ad53801732 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1264,7 +1264,8 @@ static int hci_outgoing_auth_needed(struct hci_dev *hdev, /* Only request authentication for SSP connections or non-SSP * devices with sec_level HIGH or if MITM protection is requested */ - if (!(hdev->ssp_mode > 0 && conn->ssp_mode > 0) && + if (!(hdev->ssp_mode > 0 && + test_bit(HCI_CONN_SSP_ENABLED, &conn->flags)) && conn->pending_sec_level != BT_SECURITY_HIGH && !(conn->auth_type & 0x01)) return 0; @@ -1838,7 +1839,8 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s goto unlock; if (!ev->status) { - if (!(conn->ssp_mode > 0 && hdev->ssp_mode > 0) && + if (!(test_bit(HCI_CONN_SSP_ENABLED, &conn->flags) && + hdev->ssp_mode > 0) && test_bit(HCI_CONN_REAUTH_PEND, &conn->flags)) { BT_INFO("re-auth of legacy device is not possible."); } else { @@ -1853,7 +1855,8 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s clear_bit(HCI_CONN_REAUTH_PEND, &conn->flags); if (conn->state == BT_CONFIG) { - if (!ev->status && hdev->ssp_mode > 0 && conn->ssp_mode > 0) { + if (!ev->status && hdev->ssp_mode > 0 && + test_bit(HCI_CONN_SSP_ENABLED, &conn->flags)) { struct hci_cp_set_conn_encrypt cp; cp.handle = ev->handle; cp.encrypt = 0x01; @@ -2505,9 +2508,9 @@ static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) { if (conn->mode == HCI_CM_ACTIVE) - conn->power_save = 1; + set_bit(HCI_CONN_POWER_SAVE, &conn->flags); else - conn->power_save = 0; + clear_bit(HCI_CONN_POWER_SAVE, &conn->flags); } if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags)) @@ -2780,7 +2783,8 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b if (ie) ie->data.ssp_mode = (ev->features[0] & 0x01); - conn->ssp_mode = (ev->features[0] & 0x01); + if (ev->features[0] & 0x01) + set_bit(HCI_CONN_SSP_ENABLED, &conn->flags); } if (conn->state != BT_CONFIG) @@ -2962,7 +2966,7 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff conn->auth_type = hci_get_auth_req(conn); cp.authentication = conn->auth_type; - if ((conn->out == 0x01 || conn->remote_oob == 0x01) && + if ((conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)) && hci_find_remote_oob_data(hdev, &conn->dst)) cp.oob_data = 0x01; else @@ -2998,8 +3002,9 @@ static inline void hci_io_capa_reply_evt(struct hci_dev *hdev, struct sk_buff *s goto unlock; conn->remote_cap = ev->capability; - conn->remote_oob = ev->oob_data; conn->remote_auth = ev->authentication; + if (ev->oob_data) + set_bit(HCI_CONN_REMOTE_OOB, &conn->flags); unlock: hci_dev_unlock(hdev); -- cgit From 84bde9d6c0e6830f4a8685a5d237965053118bf9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 25 Jan 2012 14:21:06 +0200 Subject: Bluetooth: Convert hdev->ssp_mode to a flag The ssp_mode is essentially just a boolean so it's more appropriate to have it simply as a flag in hdev->dev_flags. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_conn.c | 6 +++--- net/bluetooth/hci_event.c | 17 ++++++++++++----- net/bluetooth/mgmt.c | 4 ++-- 5 files changed, 18 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index dd2cc6cb35b3..cb9097acbf44 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -94,6 +94,7 @@ enum { HCI_DEBUG_KEYS, HCI_LE_SCAN, + HCI_SSP_ENABLED, }; /* HCI ioctl defines */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7a033111c98f..94ba8693e9d1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -144,7 +144,6 @@ struct hci_dev { __u8 features[8]; __u8 host_features[8]; __u8 commands[64]; - __u8 ssp_mode; __u8 hci_ver; __u16 hci_rev; __u8 lmp_ver; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 8288e303621a..6ec259e84b95 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -609,8 +609,8 @@ int hci_conn_check_link_mode(struct hci_conn *conn) BT_DBG("conn %p", conn); if (test_bit(HCI_CONN_SSP_ENABLED, &conn->flags) && - conn->hdev->ssp_mode > 0 && - !(conn->link_mode & HCI_LM_ENCRYPT)) + test_bit(HCI_SSP_ENABLED, &conn->hdev->dev_flags) && + !(conn->link_mode & HCI_LM_ENCRYPT)) return 0; return 1; @@ -674,7 +674,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) key. */ if (sec_level == BT_SECURITY_LOW && (!test_bit(HCI_CONN_SSP_ENABLED, &conn->flags) || - !conn->hdev->ssp_mode)) + !test_bit(HCI_SSP_ENABLED, &conn->hdev->dev_flags))) return 1; /* For other security levels we need the link key. */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 02ad53801732..eb198ccbc10d 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -429,7 +429,10 @@ static void hci_cc_read_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) if (rp->status) return; - hdev->ssp_mode = rp->mode; + if (rp->mode) + set_bit(HCI_SSP_ENABLED, &hdev->dev_flags); + else + clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); } static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) @@ -446,7 +449,10 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) if (!sent) return; - hdev->ssp_mode = *((__u8 *) sent); + if (*((u8 *) sent)) + set_bit(HCI_SSP_ENABLED, &hdev->dev_flags); + else + clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); } static u8 hci_get_inquiry_mode(struct hci_dev *hdev) @@ -1264,7 +1270,7 @@ static int hci_outgoing_auth_needed(struct hci_dev *hdev, /* Only request authentication for SSP connections or non-SSP * devices with sec_level HIGH or if MITM protection is requested */ - if (!(hdev->ssp_mode > 0 && + if (!(test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && test_bit(HCI_CONN_SSP_ENABLED, &conn->flags)) && conn->pending_sec_level != BT_SECURITY_HIGH && !(conn->auth_type & 0x01)) @@ -1840,7 +1846,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s if (!ev->status) { if (!(test_bit(HCI_CONN_SSP_ENABLED, &conn->flags) && - hdev->ssp_mode > 0) && + test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) && test_bit(HCI_CONN_REAUTH_PEND, &conn->flags)) { BT_INFO("re-auth of legacy device is not possible."); } else { @@ -1855,7 +1861,8 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s clear_bit(HCI_CONN_REAUTH_PEND, &conn->flags); if (conn->state == BT_CONFIG) { - if (!ev->status && hdev->ssp_mode > 0 && + if (!ev->status && + test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && test_bit(HCI_CONN_SSP_ENABLED, &conn->flags)) { struct hci_cp_set_conn_encrypt cp; cp.handle = ev->handle; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ae9283d47e65..89707996d352 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -297,7 +297,7 @@ static u32 get_current_settings(struct hci_dev *hdev) if (test_bit(HCI_AUTH, &hdev->flags)) settings |= MGMT_SETTING_LINK_SECURITY; - if (hdev->ssp_mode > 0) + if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) settings |= MGMT_SETTING_SSP; return settings; @@ -416,7 +416,7 @@ static int update_eir(struct hci_dev *hdev) if (!(hdev->features[6] & LMP_EXT_INQ)) return 0; - if (hdev->ssp_mode == 0) + if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) return 0; if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) -- cgit From aa64a8b500e61c33c17f1d5e7de0cc154489c59e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 18 Jan 2012 21:33:12 +0200 Subject: Bluetooth: Add a convenience function to check for SSP enabled It's a very common test to see if both the local and the remote device have SSP enabled. By creating a simple function to test this we can shorten many if-statements in the code. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 7 +++++++ net/bluetooth/hci_conn.c | 8 ++------ net/bluetooth/hci_event.c | 12 ++++-------- 3 files changed, 13 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 94ba8693e9d1..25f449fcd693 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -411,6 +411,13 @@ enum { HCI_CONN_REMOTE_OOB, }; +static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + return (test_bit(HCI_SSP_ENABLED, &hdev->flags) && + test_bit(HCI_CONN_SSP_ENABLED, &conn->flags)); +} + static inline void hci_conn_hash_init(struct hci_dev *hdev) { struct hci_conn_hash *h = &hdev->conn_hash; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 6ec259e84b95..eae7a53467ef 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -608,9 +608,7 @@ int hci_conn_check_link_mode(struct hci_conn *conn) { BT_DBG("conn %p", conn); - if (test_bit(HCI_CONN_SSP_ENABLED, &conn->flags) && - test_bit(HCI_SSP_ENABLED, &conn->hdev->dev_flags) && - !(conn->link_mode & HCI_LM_ENCRYPT)) + if (hci_conn_ssp_enabled(conn) && !(conn->link_mode & HCI_LM_ENCRYPT)) return 0; return 1; @@ -672,9 +670,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) /* For non 2.1 devices and low security level we don't need the link key. */ - if (sec_level == BT_SECURITY_LOW && - (!test_bit(HCI_CONN_SSP_ENABLED, &conn->flags) || - !test_bit(HCI_SSP_ENABLED, &conn->hdev->dev_flags))) + if (sec_level == BT_SECURITY_LOW && !hci_conn_ssp_enabled(conn)) return 1; /* For other security levels we need the link key. */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index eb198ccbc10d..6fb9016652b7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1270,8 +1270,7 @@ static int hci_outgoing_auth_needed(struct hci_dev *hdev, /* Only request authentication for SSP connections or non-SSP * devices with sec_level HIGH or if MITM protection is requested */ - if (!(test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && - test_bit(HCI_CONN_SSP_ENABLED, &conn->flags)) && + if (!hci_conn_ssp_enabled(conn) && conn->pending_sec_level != BT_SECURITY_HIGH && !(conn->auth_type & 0x01)) return 0; @@ -1845,9 +1844,8 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s goto unlock; if (!ev->status) { - if (!(test_bit(HCI_CONN_SSP_ENABLED, &conn->flags) && - test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) && - test_bit(HCI_CONN_REAUTH_PEND, &conn->flags)) { + if (!hci_conn_ssp_enabled(conn) && + test_bit(HCI_CONN_REAUTH_PEND, &conn->flags)) { BT_INFO("re-auth of legacy device is not possible."); } else { conn->link_mode |= HCI_LM_AUTH; @@ -1861,9 +1859,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s clear_bit(HCI_CONN_REAUTH_PEND, &conn->flags); if (conn->state == BT_CONFIG) { - if (!ev->status && - test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && - test_bit(HCI_CONN_SSP_ENABLED, &conn->flags)) { + if (!ev->status && hci_conn_ssp_enabled(conn)) { struct hci_cp_set_conn_encrypt cp; cp.handle = ev->handle; cp.encrypt = 0x01; -- cgit From d22015aad40b4316f0f74c8e410debca44c3e6e2 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Sun, 22 Jan 2012 00:28:34 +0200 Subject: Bluetooth: silence lockdep warning Since bluetooth uses multiple protocols types, to avoid lockdep warnings, we need to use different lockdep classes (one for each protocol type). This is already done in bt_sock_create but it misses a couple of cases when new connections are created. This patch corrects that to fix the following warning: <4>[ 1864.732366] ======================================================= <4>[ 1864.733030] [ INFO: possible circular locking dependency detected ] <4>[ 1864.733544] 3.0.16-mid3-00007-gc9a0f62 #3 <4>[ 1864.733883] ------------------------------------------------------- <4>[ 1864.734408] t.android.btclc/4204 is trying to acquire lock: <4>[ 1864.734869] (rfcomm_mutex){+.+.+.}, at: [] rfcomm_dlc_close+0x15/0x30 <4>[ 1864.735541] <4>[ 1864.735549] but task is already holding lock: <4>[ 1864.736045] (sk_lock-AF_BLUETOOTH){+.+.+.}, at: [] lock_sock+0xa/0xc <4>[ 1864.736732] <4>[ 1864.736740] which lock already depends on the new lock. <4>[ 1864.736750] <4>[ 1864.737428] <4>[ 1864.737437] the existing dependency chain (in reverse order) is: <4>[ 1864.738016] <4>[ 1864.738023] -> #1 (sk_lock-AF_BLUETOOTH){+.+.+.}: <4>[ 1864.738549] [] lock_acquire+0x104/0x140 <4>[ 1864.738977] [] lock_sock_nested+0x58/0x68 <4>[ 1864.739411] [] l2cap_sock_sendmsg+0x3e/0x76 <4>[ 1864.739858] [] __sock_sendmsg+0x50/0x59 <4>[ 1864.740279] [] sock_sendmsg+0x94/0xa8 <4>[ 1864.740687] [] kernel_sendmsg+0x28/0x37 <4>[ 1864.741106] [] rfcomm_send_frame+0x30/0x38 <4>[ 1864.741542] [] rfcomm_send_ua+0x58/0x5a <4>[ 1864.741959] [] rfcomm_run+0x441/0xb52 <4>[ 1864.742365] [] kthread+0x63/0x68 <4>[ 1864.742742] [] kernel_thread_helper+0x6/0xd <4>[ 1864.743187] <4>[ 1864.743193] -> #0 (rfcomm_mutex){+.+.+.}: <4>[ 1864.743667] [] __lock_acquire+0x988/0xc00 <4>[ 1864.744100] [] lock_acquire+0x104/0x140 <4>[ 1864.744519] [] __mutex_lock_common+0x3b/0x33f <4>[ 1864.744975] [] mutex_lock_nested+0x2d/0x36 <4>[ 1864.745412] [] rfcomm_dlc_close+0x15/0x30 <4>[ 1864.745842] [] __rfcomm_sock_close+0x5f/0x6b <4>[ 1864.746288] [] rfcomm_sock_shutdown+0x2f/0x62 <4>[ 1864.746737] [] sys_socketcall+0x1db/0x422 <4>[ 1864.747165] [] syscall_call+0x7/0xb Signed-off-by: Octavian Purdila Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/bluetooth.h | 2 ++ net/bluetooth/af_bluetooth.c | 12 +++++------- net/bluetooth/l2cap_sock.c | 2 ++ net/bluetooth/rfcomm/sock.c | 2 ++ 4 files changed, 11 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index abaad6ed9b83..4a82ca0bb0b2 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -256,4 +256,6 @@ void l2cap_exit(void); int sco_init(void); void sco_exit(void); +void bt_sock_reclassify_lock(struct sock *sk, int proto); + #endif /* __BLUETOOTH_H */ diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index ef92864ac625..72eb187a5f60 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -71,19 +71,16 @@ static const char *const bt_slock_key_strings[BT_MAX_PROTO] = { "slock-AF_BLUETOOTH-BTPROTO_AVDTP", }; -static inline void bt_sock_reclassify_lock(struct socket *sock, int proto) +void bt_sock_reclassify_lock(struct sock *sk, int proto) { - struct sock *sk = sock->sk; - - if (!sk) - return; - + BUG_ON(!sk); BUG_ON(sock_owned_by_user(sk)); sock_lock_init_class_and_name(sk, bt_slock_key_strings[proto], &bt_slock_key[proto], bt_key_strings[proto], &bt_lock_key[proto]); } +EXPORT_SYMBOL(bt_sock_reclassify_lock); int bt_sock_register(int proto, const struct net_proto_family *ops) { @@ -145,7 +142,8 @@ static int bt_sock_create(struct net *net, struct socket *sock, int proto, if (bt_proto[proto] && try_module_get(bt_proto[proto]->owner)) { err = bt_proto[proto]->create(net, sock, proto, kern); - bt_sock_reclassify_lock(sock, proto); + if (!err) + bt_sock_reclassify_lock(sock->sk, proto); module_put(bt_proto[proto]->owner); } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index c57027f7606f..401d9428ae4c 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -849,6 +849,8 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data) if (!sk) return NULL; + bt_sock_reclassify_lock(sk, BTPROTO_L2CAP); + l2cap_sock_init(sk, parent); return l2cap_pi(sk)->chan; diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index f066678faeee..22169c3f1482 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -956,6 +956,8 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc * if (!sk) goto done; + bt_sock_reclassify_lock(sk, BTPROTO_RFCOMM); + rfcomm_sock_init(sk, parent); bacpy(&bt_sk(sk)->src, &src); bacpy(&bt_sk(sk)->dst, &dst); -- cgit From 2f7719ce54bf6e877987f6ef578b580a51d8c2e3 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 20 Jan 2012 14:08:03 +0200 Subject: Bluetooth: Add alloc_skb chan operator Add channel-specific skb allocation method Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 3 +++ net/bluetooth/l2cap_core.c | 23 +++++++++++++++-------- net/bluetooth/l2cap_sock.c | 9 +++++++++ 3 files changed, 27 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 238daf84184c..e7a8cc7d07f4 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -506,6 +506,9 @@ struct l2cap_ops { int (*recv) (void *data, struct sk_buff *skb); void (*close) (void *data); void (*state_change) (void *data, int state); + struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan, + unsigned long len, int nb, int *err); + }; struct l2cap_conn { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index cba1f683b649..b2f52f87b98e 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1520,7 +1520,6 @@ static void l2cap_send_srejtail(struct l2cap_chan *chan) static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, struct msghdr *msg, int len, int count, struct sk_buff *skb) { - struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; struct sk_buff **frag; int err, sent = 0; @@ -1536,7 +1535,9 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, struct msghdr while (len) { count = min_t(unsigned int, conn->mtu, len); - *frag = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err); + *frag = chan->ops->alloc_skb(chan, count, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!*frag) return err; if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) @@ -1566,8 +1567,10 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, BT_DBG("sk %p len %d priority %u", sk, (int)len, priority); count = min_t(unsigned int, (conn->mtu - hlen), len); - skb = bt_skb_send_alloc(sk, count + hlen, - msg->msg_flags & MSG_DONTWAIT, &err); + + skb = chan->ops->alloc_skb(chan, count + hlen, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) return ERR_PTR(err); @@ -1600,8 +1603,10 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, BT_DBG("sk %p len %d", sk, (int)len); count = min_t(unsigned int, (conn->mtu - hlen), len); - skb = bt_skb_send_alloc(sk, count + hlen, - msg->msg_flags & MSG_DONTWAIT, &err); + + skb = chan->ops->alloc_skb(chan, count + hlen, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) return ERR_PTR(err); @@ -1647,8 +1652,10 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, hlen += L2CAP_FCS_SIZE; count = min_t(unsigned int, (conn->mtu - hlen), len); - skb = bt_skb_send_alloc(sk, count + hlen, - msg->msg_flags & MSG_DONTWAIT, &err); + + skb = chan->ops->alloc_skb(chan, count + hlen, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) return ERR_PTR(err); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 401d9428ae4c..16360298590f 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -899,12 +899,21 @@ static void l2cap_sock_state_change_cb(void *data, int state) sk->sk_state = state; } +static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, + unsigned long len, int nb, int *err) +{ + struct sock *sk = chan->sk; + + return bt_skb_send_alloc(sk, len, nb, err); +} + static struct l2cap_ops l2cap_chan_ops = { .name = "L2CAP Socket Interface", .new_connection = l2cap_sock_new_connection_cb, .recv = l2cap_sock_recv_cb, .close = l2cap_sock_close_cb, .state_change = l2cap_sock_state_change_cb, + .alloc_skb = l2cap_sock_alloc_skb_cb, }; static void l2cap_sock_destruct(struct sock *sk) -- cgit From 2f304d1e8bf89f2258a6c2e08c0bc2c5debd08f7 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 27 Jan 2012 19:42:02 -0300 Subject: Bluetooth: Fix potential deadlock We don't need to use the _sync variant in hci_conn_hold and hci_conn_put to cancel conn->disc_work delayed work. This way we avoid potential deadlocks like this one reported by lockdep. ====================================================== [ INFO: possible circular locking dependency detected ] 3.2.0+ #1 Not tainted ------------------------------------------------------- kworker/u:1/17 is trying to acquire lock: (&hdev->lock){+.+.+.}, at: [] hci_conn_timeout+0x62/0x158 [bluetooth] but task is already holding lock: ((&(&conn->disc_work)->work)){+.+...}, at: [] process_one_work+0x11a/0x2bf which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #2 ((&(&conn->disc_work)->work)){+.+...}: [] lock_acquire+0x8a/0xa7 [] wait_on_work+0x3d/0xaa [] __cancel_work_timer+0xac/0xef [] cancel_delayed_work_sync+0xd/0xf [] smp_chan_create+0xde/0xe6 [bluetooth] [] smp_conn_security+0xa3/0x12d [bluetooth] [] l2cap_connect_cfm+0x237/0x2e8 [bluetooth] [] hci_proto_connect_cfm+0x2d/0x6f [bluetooth] [] hci_event_packet+0x29d1/0x2d60 [bluetooth] [] hci_rx_work+0xd0/0x2e1 [bluetooth] [] process_one_work+0x178/0x2bf [] worker_thread+0xce/0x152 [] kthread+0x95/0x9d [] kernel_thread_helper+0x4/0x10 -> #1 (slock-AF_BLUETOOTH-BTPROTO_L2CAP){+.+...}: [] lock_acquire+0x8a/0xa7 [] _raw_spin_lock_bh+0x36/0x6a [] lock_sock_nested+0x24/0x7f [] lock_sock+0xb/0xd [bluetooth] [] l2cap_chan_connect+0xa9/0x26f [bluetooth] [] l2cap_sock_connect+0xb3/0xff [bluetooth] [] sys_connect+0x69/0x8a [] system_call_fastpath+0x16/0x1b -> #0 (&hdev->lock){+.+.+.}: [] __lock_acquire+0xa80/0xd74 [] lock_acquire+0x8a/0xa7 [] __mutex_lock_common+0x48/0x38e [] mutex_lock_nested+0x2a/0x31 [] hci_conn_timeout+0x62/0x158 [bluetooth] [] process_one_work+0x178/0x2bf [] worker_thread+0xce/0x152 [] kthread+0x95/0x9d [] kernel_thread_helper+0x4/0x10 other info that might help us debug this: Chain exists of: &hdev->lock --> slock-AF_BLUETOOTH-BTPROTO_L2CAP --> (&(&conn->disc_work)->work) Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock((&(&conn->disc_work)->work)); lock(slock-AF_BLUETOOTH-BTPROTO_L2CAP); lock((&(&conn->disc_work)->work)); lock(&hdev->lock); *** DEADLOCK *** 2 locks held by kworker/u:1/17: #0: (hdev->name){.+.+.+}, at: [] process_one_work+0x11a/0x2bf #1: ((&(&conn->disc_work)->work)){+.+...}, at: [] process_one_work+0x11a/0x2bf stack backtrace: Pid: 17, comm: kworker/u:1 Not tainted 3.2.0+ #1 Call Trace: [] print_circular_bug+0x1f8/0x209 [] __lock_acquire+0xa80/0xd74 [] ? arch_local_irq_restore+0x6/0xd [] ? vprintk+0x3f9/0x41e [] lock_acquire+0x8a/0xa7 [] ? hci_conn_timeout+0x62/0x158 [bluetooth] [] __mutex_lock_common+0x48/0x38e [] ? hci_conn_timeout+0x62/0x158 [bluetooth] [] ? __dynamic_pr_debug+0x6d/0x6f [] ? hci_conn_timeout+0x62/0x158 [bluetooth] [] ? trace_hardirqs_off+0xd/0xf [] mutex_lock_nested+0x2a/0x31 [] hci_conn_timeout+0x62/0x158 [bluetooth] [] process_one_work+0x178/0x2bf [] ? process_one_work+0x11a/0x2bf [] ? lock_acquired+0x1d0/0x1df [] ? hci_acl_disconn+0x65/0x65 [bluetooth] [] worker_thread+0xce/0x152 [] ? finish_task_switch+0x45/0xc5 [] ? manage_workers.isra.25+0x16a/0x16a [] kthread+0x95/0x9d [] kernel_thread_helper+0x4/0x10 [] ? retint_restore_args+0x13/0x13 [] ? __init_kthread_worker+0x55/0x55 [] ? gs_change+0x13/0x13 Signed-off-by: Andre Guedes Signed-off-by: Vinicius Costa Gomes Reviewed-by: Ulisses Furquim Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 25f449fcd693..896d9e4955fc 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -572,7 +572,7 @@ void hci_conn_put_device(struct hci_conn *conn); static inline void hci_conn_hold(struct hci_conn *conn) { atomic_inc(&conn->refcnt); - cancel_delayed_work_sync(&conn->disc_work); + cancel_delayed_work(&conn->disc_work); } static inline void hci_conn_put(struct hci_conn *conn) @@ -591,7 +591,7 @@ static inline void hci_conn_put(struct hci_conn *conn) } else { timeo = msecs_to_jiffies(10); } - cancel_delayed_work_sync(&conn->disc_work); + cancel_delayed_work(&conn->disc_work); queue_delayed_work(conn->hdev->workqueue, &conn->disc_work, timeo); } -- cgit From 17cd3f374be6648bd46c86ff8f2a2511d3f416ee Mon Sep 17 00:00:00 2001 From: Ulisses Furquim Date: Mon, 30 Jan 2012 18:26:28 -0200 Subject: Bluetooth: Remove usage of __cancel_delayed_work() __cancel_delayed_work() is being used in some paths where we cannot sleep waiting for the delayed work to finish. However, that function might return while the timer is running and the work will be queued again. Replace the calls with safer cancel_delayed_work() version which spins until the timer handler finishes on other CPUs and cancels the delayed work. Signed-off-by: Ulisses Furquim Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 4 ++-- net/bluetooth/l2cap_core.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index e7a8cc7d07f4..42fdbb833254 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -614,7 +614,7 @@ static inline void l2cap_set_timer(struct l2cap_chan *chan, { BT_DBG("chan %p state %d timeout %ld", chan, chan->state, timeout); - if (!__cancel_delayed_work(work)) + if (!cancel_delayed_work(work)) l2cap_chan_hold(chan); schedule_delayed_work(work, timeout); } @@ -624,7 +624,7 @@ static inline bool l2cap_clear_timer(struct l2cap_chan *chan, { bool ret; - ret = __cancel_delayed_work(work); + ret = cancel_delayed_work(work); if (ret) l2cap_chan_put(chan); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 942ba1d1a38c..ae7fb27525d3 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2588,7 +2588,7 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) && cmd->ident == conn->info_ident) { - __cancel_delayed_work(&conn->info_timer); + cancel_delayed_work(&conn->info_timer); conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_ident = 0; @@ -3135,7 +3135,7 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) return 0; - __cancel_delayed_work(&conn->info_timer); + cancel_delayed_work(&conn->info_timer); if (result != L2CAP_IR_SUCCESS) { conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; @@ -4509,7 +4509,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) if (hcon->type == LE_LINK) { smp_distribute_keys(conn, 0); - __cancel_delayed_work(&conn->security_timer); + cancel_delayed_work(&conn->security_timer); } rcu_read_lock(); -- cgit From 27f27ed8f0f7330337297a6dbdb49472dfeef59b Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Mon, 30 Jan 2012 19:29:11 -0300 Subject: Bluetooth: Add structures for the new LTK exchange messages This defines two new messages, one event that will inform userspace that a new Long Term Key was exchanged and one that will allow userspace to load LTKs into the kernel. Besides the information necessary for the restablishement of the secure link, we added some extra information: "authenticated" that informs if the key can be used to establish an authenticated link, and "master" that informs the role in that the key should be used. Signed-off-by: Vinicius Costa Gomes Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 6f37983c8775..0a694480c27e 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -288,6 +288,22 @@ struct mgmt_cp_unblock_device { bdaddr_t bdaddr; } __packed; +struct mgmt_ltk_info { + struct mgmt_addr_info addr; + __u8 authenticated; + __u8 master; + __u8 enc_size; + __le16 ediv; + __u8 rand[8]; + __u8 val[16]; +} __packed; + +#define MGMT_OP_LOAD_LONG_TERM_KEYS 0x0026 +struct mgmt_cp_load_long_term_keys { + __le16 key_count; + struct mgmt_ltk_info keys[0]; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; @@ -387,3 +403,9 @@ struct mgmt_ev_device_blocked { struct mgmt_ev_device_unblocked { bdaddr_t bdaddr; } __packed; + +#define MGMT_EV_NEW_LONG_TERM_KEY 0x0015 +struct mgmt_ev_new_long_term_key { + __u8 store_hint; + struct mgmt_ltk_info key; +} __packed; -- cgit From f7aa611a0ecf1d22f21e26279e1a3baf1db6b973 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Mon, 30 Jan 2012 19:29:12 -0300 Subject: Bluetooth: Rename smp_key_size to enc_key_size This makes clear that this is the size of the key used to encrypt the link. Signed-off-by: Vinicius Costa Gomes Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/smp.h | 2 +- net/bluetooth/smp.c | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h index aeaf5fa2b9f1..7b3acdd29134 100644 --- a/include/net/bluetooth/smp.h +++ b/include/net/bluetooth/smp.h @@ -127,7 +127,7 @@ struct smp_chan { u8 rrnd[16]; /* SMP Pairing Random (remote) */ u8 pcnf[16]; /* SMP Pairing Confirm */ u8 tk[16]; /* SMP Temporary Key */ - u8 smp_key_size; + u8 enc_key_size; unsigned long smp_flags; struct crypto_blkcipher *tfm; struct work_struct confirm; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index e08fe6c9c9c9..581833436afa 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -250,7 +250,7 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) (max_key_size < SMP_MIN_ENC_KEY_SIZE)) return SMP_ENC_KEY_SIZE; - smp->smp_key_size = max_key_size; + smp->enc_key_size = max_key_size; return 0; } @@ -446,8 +446,8 @@ static void random_work(struct work_struct *work) smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, key); swap128(key, stk); - memset(stk + smp->smp_key_size, 0, - SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size); + memset(stk + smp->enc_key_size, 0, + SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) { reason = SMP_UNSPECIFIED; @@ -455,7 +455,7 @@ static void random_work(struct work_struct *work) } hci_le_start_enc(hcon, ediv, rand, stk); - hcon->enc_key_size = smp->smp_key_size; + hcon->enc_key_size = smp->enc_key_size; } else { u8 stk[16], r[16], rand[8]; __le16 ediv; @@ -469,10 +469,10 @@ static void random_work(struct work_struct *work) smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, key); swap128(key, stk); - memset(stk + smp->smp_key_size, 0, - SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size); + memset(stk + smp->enc_key_size, 0, + SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); - hci_add_ltk(hcon->hdev, 0, conn->dst, smp->smp_key_size, + hci_add_ltk(hcon->hdev, 0, conn->dst, smp->enc_key_size, ediv, rand, stk); } @@ -819,7 +819,7 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) skb_pull(skb, sizeof(*rp)); - hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->smp_key_size, + hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->enc_key_size, rp->ediv, rp->rand, smp->tk); smp_distribute_keys(conn, 1); @@ -940,7 +940,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc); - hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->smp_key_size, + hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->enc_key_size, ediv, ident.rand, enc.ltk); ident.ediv = cpu_to_le16(ediv); -- cgit From 9ef866adf9602238c2e83e951a72a1037d4179de Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 1 Feb 2012 23:42:38 +0200 Subject: Bluetooth: Update mgmt.h to match latest API spec This patch updates the opcodes for mgmt commands and events to match the latest user space API specification. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 126 ++++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 62 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 0a694480c27e..42eb48bb2c3b 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -46,6 +46,16 @@ struct mgmt_hdr { __le16 len; } __packed; +#define MGMT_ADDR_BREDR 0x00 +#define MGMT_ADDR_LE_PUBLIC 0x01 +#define MGMT_ADDR_LE_RANDOM 0x02 +#define MGMT_ADDR_INVALID 0xff + +struct mgmt_addr_info { + bdaddr_t bdaddr; + __u8 type; +} __packed; + #define MGMT_OP_READ_VERSION 0x0001 struct mgmt_rp_read_version { __u8 version; @@ -148,7 +158,23 @@ struct mgmt_cp_load_link_keys { struct mgmt_link_key_info keys[0]; } __packed; -#define MGMT_OP_REMOVE_KEYS 0x0013 +struct mgmt_ltk_info { + struct mgmt_addr_info addr; + __u8 authenticated; + __u8 master; + __u8 enc_size; + __le16 ediv; + __u8 rand[8]; + __u8 val[16]; +} __packed; + +#define MGMT_OP_LOAD_LONG_TERM_KEYS 0x0013 +struct mgmt_cp_load_long_term_keys { + __le16 key_count; + struct mgmt_ltk_info keys[0]; +} __packed; + +#define MGMT_OP_REMOVE_KEYS 0x0014 struct mgmt_cp_remove_keys { bdaddr_t bdaddr; __u8 disconnect; @@ -158,7 +184,7 @@ struct mgmt_rp_remove_keys { __u8 status; }; -#define MGMT_OP_DISCONNECT 0x0014 +#define MGMT_OP_DISCONNECT 0x0015 struct mgmt_cp_disconnect { bdaddr_t bdaddr; } __packed; @@ -167,23 +193,13 @@ struct mgmt_rp_disconnect { __u8 status; } __packed; -#define MGMT_ADDR_BREDR 0x00 -#define MGMT_ADDR_LE_PUBLIC 0x01 -#define MGMT_ADDR_LE_RANDOM 0x02 -#define MGMT_ADDR_INVALID 0xff - -struct mgmt_addr_info { - bdaddr_t bdaddr; - __u8 type; -} __packed; - -#define MGMT_OP_GET_CONNECTIONS 0x0015 +#define MGMT_OP_GET_CONNECTIONS 0x0016 struct mgmt_rp_get_connections { __le16 conn_count; struct mgmt_addr_info addr[0]; } __packed; -#define MGMT_OP_PIN_CODE_REPLY 0x0016 +#define MGMT_OP_PIN_CODE_REPLY 0x0017 struct mgmt_cp_pin_code_reply { bdaddr_t bdaddr; __u8 pin_len; @@ -194,17 +210,17 @@ struct mgmt_rp_pin_code_reply { uint8_t status; } __packed; -#define MGMT_OP_PIN_CODE_NEG_REPLY 0x0017 +#define MGMT_OP_PIN_CODE_NEG_REPLY 0x0018 struct mgmt_cp_pin_code_neg_reply { bdaddr_t bdaddr; } __packed; -#define MGMT_OP_SET_IO_CAPABILITY 0x0018 +#define MGMT_OP_SET_IO_CAPABILITY 0x0019 struct mgmt_cp_set_io_capability { __u8 io_capability; } __packed; -#define MGMT_OP_PAIR_DEVICE 0x0019 +#define MGMT_OP_PAIR_DEVICE 0x001A struct mgmt_cp_pair_device { struct mgmt_addr_info addr; __u8 io_cap; @@ -214,7 +230,9 @@ struct mgmt_rp_pair_device { __u8 status; } __packed; -#define MGMT_OP_USER_CONFIRM_REPLY 0x001A +#define MGMT_OP_CANCEL_PAIR_DEVICE 0x001B + +#define MGMT_OP_USER_CONFIRM_REPLY 0x001C struct mgmt_cp_user_confirm_reply { bdaddr_t bdaddr; } __packed; @@ -223,12 +241,12 @@ struct mgmt_rp_user_confirm_reply { __u8 status; } __packed; -#define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x001B +#define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x001D struct mgmt_cp_user_confirm_neg_reply { bdaddr_t bdaddr; } __packed; -#define MGMT_OP_USER_PASSKEY_REPLY 0x001C +#define MGMT_OP_USER_PASSKEY_REPLY 0x001E struct mgmt_cp_user_passkey_reply { bdaddr_t bdaddr; __le32 passkey; @@ -238,37 +256,37 @@ struct mgmt_rp_user_passkey_reply { __u8 status; } __packed; -#define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x001D +#define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x001F struct mgmt_cp_user_passkey_neg_reply { bdaddr_t bdaddr; } __packed; -#define MGMT_OP_READ_LOCAL_OOB_DATA 0x001E +#define MGMT_OP_READ_LOCAL_OOB_DATA 0x0020 struct mgmt_rp_read_local_oob_data { __u8 hash[16]; __u8 randomizer[16]; } __packed; -#define MGMT_OP_ADD_REMOTE_OOB_DATA 0x001F +#define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0021 struct mgmt_cp_add_remote_oob_data { bdaddr_t bdaddr; __u8 hash[16]; __u8 randomizer[16]; } __packed; -#define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x0020 +#define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x0022 struct mgmt_cp_remove_remote_oob_data { bdaddr_t bdaddr; } __packed; -#define MGMT_OP_START_DISCOVERY 0x0021 +#define MGMT_OP_START_DISCOVERY 0x0023 struct mgmt_cp_start_discovery { __u8 type; } __packed; -#define MGMT_OP_STOP_DISCOVERY 0x0022 +#define MGMT_OP_STOP_DISCOVERY 0x0024 -#define MGMT_OP_CONFIRM_NAME 0x0023 +#define MGMT_OP_CONFIRM_NAME 0x0025 struct mgmt_cp_confirm_name { bdaddr_t bdaddr; __u8 name_known; @@ -278,32 +296,16 @@ struct mgmt_rp_confirm_name { __u8 status; } __packed; -#define MGMT_OP_BLOCK_DEVICE 0x0024 +#define MGMT_OP_BLOCK_DEVICE 0x0026 struct mgmt_cp_block_device { bdaddr_t bdaddr; } __packed; -#define MGMT_OP_UNBLOCK_DEVICE 0x0025 +#define MGMT_OP_UNBLOCK_DEVICE 0x0027 struct mgmt_cp_unblock_device { bdaddr_t bdaddr; } __packed; -struct mgmt_ltk_info { - struct mgmt_addr_info addr; - __u8 authenticated; - __u8 master; - __u8 enc_size; - __le16 ediv; - __u8 rand[8]; - __u8 val[16]; -} __packed; - -#define MGMT_OP_LOAD_LONG_TERM_KEYS 0x0026 -struct mgmt_cp_load_long_term_keys { - __le16 key_count; - struct mgmt_ltk_info keys[0]; -} __packed; - #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; @@ -344,46 +346,52 @@ struct mgmt_ev_new_link_key { struct mgmt_link_key_info key; } __packed; -#define MGMT_EV_DEVICE_CONNECTED 0x000A +#define MGMT_EV_NEW_LONG_TERM_KEY 0x000A +struct mgmt_ev_new_long_term_key { + __u8 store_hint; + struct mgmt_ltk_info key; +} __packed; + +#define MGMT_EV_DEVICE_CONNECTED 0x000B struct mgmt_ev_device_connected { struct mgmt_addr_info addr; __le16 eir_len; __u8 eir[0]; } __packed; -#define MGMT_EV_DEVICE_DISCONNECTED 0x000B +#define MGMT_EV_DEVICE_DISCONNECTED 0x000C -#define MGMT_EV_CONNECT_FAILED 0x000C +#define MGMT_EV_CONNECT_FAILED 0x000D struct mgmt_ev_connect_failed { struct mgmt_addr_info addr; __u8 status; } __packed; -#define MGMT_EV_PIN_CODE_REQUEST 0x000D +#define MGMT_EV_PIN_CODE_REQUEST 0x000E struct mgmt_ev_pin_code_request { bdaddr_t bdaddr; __u8 secure; } __packed; -#define MGMT_EV_USER_CONFIRM_REQUEST 0x000E +#define MGMT_EV_USER_CONFIRM_REQUEST 0x000F struct mgmt_ev_user_confirm_request { bdaddr_t bdaddr; __u8 confirm_hint; __le32 value; } __packed; -#define MGMT_EV_USER_PASSKEY_REQUEST 0x000F +#define MGMT_EV_USER_PASSKEY_REQUEST 0x0010 struct mgmt_ev_user_passkey_request { bdaddr_t bdaddr; } __packed; -#define MGMT_EV_AUTH_FAILED 0x0010 +#define MGMT_EV_AUTH_FAILED 0x0011 struct mgmt_ev_auth_failed { bdaddr_t bdaddr; __u8 status; } __packed; -#define MGMT_EV_DEVICE_FOUND 0x0011 +#define MGMT_EV_DEVICE_FOUND 0x0012 struct mgmt_ev_device_found { struct mgmt_addr_info addr; __s8 rssi; @@ -392,20 +400,14 @@ struct mgmt_ev_device_found { __u8 eir[0]; } __packed; -#define MGMT_EV_DISCOVERING 0x0012 +#define MGMT_EV_DISCOVERING 0x0013 -#define MGMT_EV_DEVICE_BLOCKED 0x0013 +#define MGMT_EV_DEVICE_BLOCKED 0x0014 struct mgmt_ev_device_blocked { bdaddr_t bdaddr; } __packed; -#define MGMT_EV_DEVICE_UNBLOCKED 0x0014 +#define MGMT_EV_DEVICE_UNBLOCKED 0x0015 struct mgmt_ev_device_unblocked { bdaddr_t bdaddr; } __packed; - -#define MGMT_EV_NEW_LONG_TERM_KEY 0x0015 -struct mgmt_ev_new_long_term_key { - __u8 store_hint; - struct mgmt_ltk_info key; -} __packed; -- cgit From 28424707a2e4ad38ab546d2ed5e3d6b035a84258 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 2 Feb 2012 04:02:29 +0200 Subject: Bluetooth: mgmt: Implement Cancel Pair Device command This patch implements the Cancel Pair Device command for mgmt. It's used by user space to cancel an ongoing pairing attempt which was triggered by the Pair Device command. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 1 + net/bluetooth/mgmt.c | 51 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 42eb48bb2c3b..72975fd53988 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -39,6 +39,7 @@ #define MGMT_STATUS_INVALID_PARAMS 0x0d #define MGMT_STATUS_DISCONNECTED 0x0e #define MGMT_STATUS_NOT_POWERED 0x0f +#define MGMT_STATUS_CANCELLED 0x10 struct mgmt_hdr { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 89707996d352..00ab083749eb 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1594,6 +1594,54 @@ unlock: return err; } +static int cancel_pair_device(struct sock *sk, u16 index, + unsigned char *data, u16 len) +{ + struct mgmt_addr_info *addr = (void *) data; + struct hci_dev *hdev; + struct pending_cmd *cmd; + struct hci_conn *conn; + int err; + + BT_DBG(""); + + if (len != sizeof(*addr)) + return cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE, + MGMT_STATUS_INVALID_PARAMS); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + cmd = mgmt_pending_find(MGMT_OP_PAIR_DEVICE, hdev); + if (!cmd) { + err = cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE, + MGMT_STATUS_INVALID_PARAMS); + goto unlock; + } + + conn = cmd->user_data; + + if (bacmp(&addr->bdaddr, &conn->dst) != 0) { + err = cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE, + MGMT_STATUS_INVALID_PARAMS); + goto unlock; + } + + pairing_complete(cmd, MGMT_STATUS_CANCELLED); + + err = cmd_complete(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE, addr, + sizeof(*addr)); +unlock: + hci_dev_unlock(hdev); + hci_dev_put(hdev); + + return err; +} + static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr, u16 mgmt_op, u16 hci_op, __le32 passkey) { @@ -2271,6 +2319,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_PAIR_DEVICE: err = pair_device(sk, index, buf + sizeof(*hdr), len); break; + case MGMT_OP_CANCEL_PAIR_DEVICE: + err = cancel_pair_device(sk, index, buf + sizeof(*hdr), len); + break; case MGMT_OP_USER_CONFIRM_REPLY: err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len); break; -- cgit From b899efaf9b26cadb084752862490b4fc44bc3169 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 2 Feb 2012 21:08:00 -0300 Subject: Bluetooth: Add new structures for handling SMP Long Term Keys This includes a new list for storing the keys and a new structure used to represent each key. Some notes: authenticated is used to identify that the key may be used to setup a HIGH security link. As the same list is used to store both the STK's and the LTK's the type field is used so we can separate between those two types of keys and if the key should be used when in the master or slave role. Signed-off-by: Vinicius Costa Gomes Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 5 +++++ include/net/bluetooth/hci_core.h | 16 ++++++++++++++++ net/bluetooth/hci_core.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index cb9097acbf44..83f045a515a0 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -274,6 +274,11 @@ enum { #define HCI_LK_SMP_LTK 0x81 #define HCI_LK_SMP_IRK 0x82 #define HCI_LK_SMP_CSRK 0x83 +/* The spec doesn't define types for SMP keys, the _MASTER suffix is implied */ +#define HCI_SMP_STK 0x80 +#define HCI_SMP_STK_SLAVE 0x81 +#define HCI_SMP_LTK 0x82 +#define HCI_SMP_LTK_SLAVE 0x83 /* ---- HCI Error Codes ---- */ #define HCI_ERROR_AUTH_FAILURE 0x05 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 896d9e4955fc..c998176a503d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -88,6 +88,18 @@ struct bt_uuid { u8 svc_hint; }; +struct smp_ltk { + struct list_head list; + bdaddr_t bdaddr; + u8 bdaddr_type; + u8 authenticated; + u8 type; + u8 enc_size; + __le16 ediv; + u8 rand[8]; + u8 val[16]; +} __packed; + struct key_master_id { __le16 ediv; u8 rand[8]; @@ -239,6 +251,8 @@ struct hci_dev { struct list_head link_keys; + struct list_head long_term_keys; + struct list_head remote_oob_data; struct list_head adv_entries; @@ -647,8 +661,10 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, struct link_key *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]); struct link_key *hci_find_link_key_type(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); +int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16]); +int hci_smp_ltks_clear(struct hci_dev *hdev); int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_remote_oob_data_clear(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 45e2d2a72b15..a28e637152fe 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1163,6 +1163,18 @@ int hci_link_keys_clear(struct hci_dev *hdev) return 0; } +int hci_smp_ltks_clear(struct hci_dev *hdev) +{ + struct smp_ltk *k, *tmp; + + list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) { + list_del(&k->list); + kfree(k); + } + + return 0; +} + struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct link_key *k; @@ -1355,6 +1367,23 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) return 0; } +int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr) +{ + struct smp_ltk *k, *tmp; + + list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) { + if (bacmp(bdaddr, &k->bdaddr)) + continue; + + BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); + + list_del(&k->list); + kfree(k); + } + + return 0; +} + /* HCI command timer function */ static void hci_cmd_timer(unsigned long arg) { @@ -1638,6 +1667,7 @@ int hci_register_dev(struct hci_dev *hdev) INIT_LIST_HEAD(&hdev->uuids); INIT_LIST_HEAD(&hdev->link_keys); + INIT_LIST_HEAD(&hdev->long_term_keys); INIT_LIST_HEAD(&hdev->remote_oob_data); @@ -1739,6 +1769,7 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_blacklist_clear(hdev); hci_uuids_clear(hdev); hci_link_keys_clear(hdev); + hci_smp_ltks_clear(hdev); hci_remote_oob_data_clear(hdev); hci_adv_entries_clear(hdev); hci_dev_unlock(hdev); -- cgit From c9839a11c0e460a2457e7cac76650d07773e6c3b Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 2 Feb 2012 21:08:01 -0300 Subject: Bluetooth: Use the updated key structures for handling LTKs This updates all the users of the older way, that was using the link_keys list to store the SMP keys, to use the new way. This includes defining new types for the keys, we have a type for each combination of STK/LTK and Master/Slave. Signed-off-by: Vinicius Costa Gomes Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 11 +++--- net/bluetooth/hci_core.c | 76 +++++++++++++++++----------------------- net/bluetooth/hci_event.c | 11 ++++-- net/bluetooth/smp.c | 38 ++++++++++++-------- 4 files changed, 71 insertions(+), 65 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c998176a503d..2649caf4db96 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -658,12 +658,13 @@ int hci_link_keys_clear(struct hci_dev *hdev); struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len); -struct link_key *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]); -struct link_key *hci_find_link_key_type(struct hci_dev *hdev, - bdaddr_t *bdaddr, u8 type); +struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]); +int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, + int new_key, u8 authenticated, u8 tk[16], + u8 enc_size, u16 ediv, u8 rand[8]); +struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 addr_type); int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr); -int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, - u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16]); int hci_smp_ltks_clear(struct hci_dev *hdev); int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index a28e637152fe..84b7a9e4f2f6 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1222,41 +1222,35 @@ static int hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn, return 0; } -struct link_key *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]) +struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]) { - struct link_key *k; - - list_for_each_entry(k, &hdev->link_keys, list) { - struct key_master_id *id; + struct smp_ltk *k; - if (k->type != HCI_LK_SMP_LTK) + list_for_each_entry(k, &hdev->long_term_keys, list) { + if (k->ediv != ediv || + memcmp(rand, k->rand, sizeof(k->rand))) continue; - if (k->dlen != sizeof(*id)) - continue; - - id = (void *) &k->data; - if (id->ediv == ediv && - (memcmp(rand, id->rand, sizeof(id->rand)) == 0)) - return k; + return k; } return NULL; } EXPORT_SYMBOL(hci_find_ltk); -struct link_key *hci_find_link_key_type(struct hci_dev *hdev, - bdaddr_t *bdaddr, u8 type) +struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 addr_type) { - struct link_key *k; + struct smp_ltk *k; - list_for_each_entry(k, &hdev->link_keys, list) - if (k->type == type && bacmp(bdaddr, &k->bdaddr) == 0) + list_for_each_entry(k, &hdev->long_term_keys, list) + if (addr_type == k->bdaddr_type && + bacmp(bdaddr, &k->bdaddr) == 0) return k; return NULL; } -EXPORT_SYMBOL(hci_find_link_key_type); +EXPORT_SYMBOL(hci_find_ltk_by_addr); int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len) @@ -1313,40 +1307,36 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, return 0; } -int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, - u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16]) +int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, + int new_key, u8 authenticated, u8 tk[16], + u8 enc_size, u16 ediv, u8 rand[8]) { - struct link_key *key, *old_key; - struct key_master_id *id; - u8 old_key_type; + struct smp_ltk *key, *old_key; - BT_DBG("%s addr %s", hdev->name, batostr(bdaddr)); + if (!(type & HCI_SMP_STK) && !(type & HCI_SMP_LTK)) + return 0; - old_key = hci_find_link_key_type(hdev, bdaddr, HCI_LK_SMP_LTK); - if (old_key) { + old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type); + if (old_key) key = old_key; - old_key_type = old_key->type; - } else { - key = kzalloc(sizeof(*key) + sizeof(*id), GFP_ATOMIC); + else { + key = kzalloc(sizeof(*key), GFP_ATOMIC); if (!key) return -ENOMEM; - list_add(&key->list, &hdev->link_keys); - old_key_type = 0xff; + list_add(&key->list, &hdev->long_term_keys); } - key->dlen = sizeof(*id); - bacpy(&key->bdaddr, bdaddr); - memcpy(key->val, ltk, sizeof(key->val)); - key->type = HCI_LK_SMP_LTK; - key->pin_len = key_size; - - id = (void *) &key->data; - id->ediv = ediv; - memcpy(id->rand, rand, sizeof(id->rand)); + key->bdaddr_type = addr_type; + memcpy(key->val, tk, sizeof(key->val)); + key->authenticated = authenticated; + key->ediv = ediv; + key->enc_size = enc_size; + key->type = type; + memcpy(key->rand, rand, sizeof(key->rand)); - if (new_key) - mgmt_new_link_key(hdev, key, old_key_type); + if (!new_key) + return 0; return 0; } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a86f82b11316..23dbb31f0423 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3256,7 +3256,7 @@ static inline void hci_le_ltk_request_evt(struct hci_dev *hdev, struct hci_cp_le_ltk_reply cp; struct hci_cp_le_ltk_neg_reply neg; struct hci_conn *conn; - struct link_key *ltk; + struct smp_ltk *ltk; BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle)); @@ -3272,10 +3272,17 @@ static inline void hci_le_ltk_request_evt(struct hci_dev *hdev, memcpy(cp.ltk, ltk->val, sizeof(ltk->val)); cp.handle = cpu_to_le16(conn->handle); - conn->pin_length = ltk->pin_len; + + if (ltk->authenticated) + conn->sec_level = BT_SECURITY_HIGH; hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp); + if (ltk->type & HCI_SMP_STK) { + list_del(<k->list); + kfree(ltk); + } + hci_dev_unlock(hdev); return; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 9ff56e18d99b..0563f737779a 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -475,8 +475,9 @@ static void random_work(struct work_struct *work) memset(stk + smp->enc_key_size, 0, SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); - hci_add_ltk(hcon->hdev, 0, conn->dst, smp->enc_key_size, - ediv, rand, stk); + hci_add_ltk(hcon->hdev, conn->dst, hcon->dst_type, + HCI_SMP_STK_SLAVE, 0, 0, stk, + smp->enc_key_size, ediv, rand); } return; @@ -701,22 +702,18 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) static u8 smp_ltk_encrypt(struct l2cap_conn *conn) { - struct link_key *key; - struct key_master_id *master; + struct smp_ltk *key; struct hci_conn *hcon = conn->hcon; - key = hci_find_link_key_type(hcon->hdev, conn->dst, - HCI_LK_SMP_LTK); + key = hci_find_ltk_by_addr(hcon->hdev, conn->dst, hcon->dst_type); if (!key) return 0; if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) return 1; - master = (void *) key->data; - hci_le_start_enc(hcon, master->ediv, master->rand, - key->val); - hcon->enc_key_size = key->pin_len; + hci_le_start_enc(hcon, key->ediv, key->rand, key->val); + hcon->enc_key_size = key->enc_size; return 1; @@ -819,13 +816,19 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_master_ident *rp = (void *) skb->data; struct smp_chan *smp = conn->smp_chan; + struct hci_dev *hdev = conn->hcon->hdev; + struct hci_conn *hcon = conn->hcon; + u8 authenticated; skb_pull(skb, sizeof(*rp)); - hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->enc_key_size, - rp->ediv, rp->rand, smp->tk); - + hci_dev_lock(hdev); + authenticated = (conn->hcon->sec_level == BT_SECURITY_HIGH); + hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type, + HCI_SMP_LTK, 1, authenticated, smp->tk, + smp->enc_key_size, rp->ediv, rp->rand); smp_distribute_keys(conn, 1); + hci_dev_unlock(hdev); return 0; } @@ -935,6 +938,8 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) if (*keydist & SMP_DIST_ENC_KEY) { struct smp_cmd_encrypt_info enc; struct smp_cmd_master_ident ident; + struct hci_conn *hcon = conn->hcon; + u8 authenticated; __le16 ediv; get_random_bytes(enc.ltk, sizeof(enc.ltk)); @@ -943,8 +948,11 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc); - hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->enc_key_size, - ediv, ident.rand, enc.ltk); + authenticated = hcon->sec_level == BT_SECURITY_HIGH; + hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type, + HCI_SMP_LTK_SLAVE, 1, authenticated, + enc.ltk, smp->enc_key_size, + ediv, ident.rand); ident.ediv = cpu_to_le16(ediv); -- cgit From 346af67b8d116f01ef696fd47959a55deb2db8b6 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 2 Feb 2012 21:08:02 -0300 Subject: Bluetooth: Add MGMT handlers for dealing with SMP LTK's This adds a method to notify that a new LTK is available and a handler to store keys coming from userspace into the kernel LTK list. Signed-off-by: Vinicius Costa Gomes Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 + net/bluetooth/mgmt.c | 80 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 2649caf4db96..7793fc644b87 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -995,6 +995,8 @@ int mgmt_discovering(struct hci_dev *hdev, u8 discovering); int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr); int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr); +int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent); + /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ad8986276848..fd0b08115f2e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2191,6 +2191,60 @@ done: return err; } +static int load_long_term_keys(struct sock *sk, u16 index, + void *cp_data, u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_load_long_term_keys *cp = cp_data; + u16 key_count, expected_len; + int i; + + if (len < sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS, + EINVAL); + + key_count = get_unaligned_le16(&cp->key_count); + + expected_len = sizeof(*cp) + key_count * + sizeof(struct mgmt_ltk_info); + if (expected_len != len) { + BT_ERR("load_keys: expected %u bytes, got %u bytes", + len, expected_len); + return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS, + EINVAL); + } + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS, + ENODEV); + + BT_DBG("hci%u key_count %u", index, key_count); + + hci_dev_lock(hdev); + + hci_smp_ltks_clear(hdev); + + for (i = 0; i < key_count; i++) { + struct mgmt_ltk_info *key = &cp->keys[i]; + u8 type; + + if (key->master) + type = HCI_SMP_LTK; + else + type = HCI_SMP_LTK_SLAVE; + + hci_add_ltk(hdev, &key->addr.bdaddr, key->addr.type, + type, 0, key->authenticated, key->val, + key->enc_size, key->ediv, key->rand); + } + + hci_dev_unlock(hdev); + hci_dev_put(hdev); + + return 0; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { void *buf; @@ -2325,6 +2379,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_UNBLOCK_DEVICE: err = unblock_device(sk, index, cp, len); break; + case MGMT_OP_LOAD_LONG_TERM_KEYS: + err = load_long_term_keys(sk, index, cp, len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, index, opcode, @@ -2478,6 +2535,29 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL); } +int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent) +{ + struct mgmt_ev_new_long_term_key ev; + + memset(&ev, 0, sizeof(ev)); + + ev.store_hint = persistent; + bacpy(&ev.key.addr.bdaddr, &key->bdaddr); + ev.key.addr.type = key->bdaddr_type; + ev.key.authenticated = key->authenticated; + ev.key.enc_size = key->enc_size; + ev.key.ediv = key->ediv; + + if (key->type == HCI_SMP_LTK) + ev.key.master = 1; + + memcpy(ev.key.rand, key->rand, sizeof(key->rand)); + memcpy(ev.key.val, key->val, sizeof(key->val)); + + return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, + &ev, sizeof(ev), NULL); +} + int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *name, u8 name_len, u8 *dev_class) -- cgit From f9c5f9ddcfc404b5d4c079452755337d84c97707 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 2 Feb 2012 21:08:04 -0300 Subject: Bluetooth: Clean up structures left unused With the use of the new structures and lists for the SMP LTK's we may remove some code that is now unused. No need to have extra fields of information inside link_key now that it is only used for Link Keys. Signed-off-by: Vinicius Costa Gomes Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 4 ---- include/net/bluetooth/hci_core.h | 16 ---------------- 2 files changed, 20 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 83f045a515a0..1b634e126878 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -270,10 +270,6 @@ enum { #define HCI_LK_UNAUTH_COMBINATION 0x04 #define HCI_LK_AUTH_COMBINATION 0x05 #define HCI_LK_CHANGED_COMBINATION 0x06 -/* The spec doesn't define types for SMP keys */ -#define HCI_LK_SMP_LTK 0x81 -#define HCI_LK_SMP_IRK 0x82 -#define HCI_LK_SMP_CSRK 0x83 /* The spec doesn't define types for SMP keys, the _MASTER suffix is implied */ #define HCI_SMP_STK 0x80 #define HCI_SMP_STK_SLAVE 0x81 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7793fc644b87..9751da78341d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -100,28 +100,12 @@ struct smp_ltk { u8 val[16]; } __packed; -struct key_master_id { - __le16 ediv; - u8 rand[8]; -} __packed; - -struct link_key_data { - bdaddr_t bdaddr; - u8 type; - u8 val[16]; - u8 pin_len; - u8 dlen; - u8 data[0]; -} __packed; - struct link_key { struct list_head list; bdaddr_t bdaddr; u8 type; u8 val[16]; u8 pin_len; - u8 dlen; - u8 data[0]; }; struct oob_data { -- cgit From c599008f8f999dab8cb4a6404be99bdc4716ba15 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 3 Feb 2012 17:47:57 -0300 Subject: Bluetooth: LE scan should send Discovering events Send MGMT Discovering events once LE scan starts/stops so the userspace can track when local adapters are discovering LE devices. This way, we also keep the same behavior of inquiry which sends MGMT Discovering events once inquiry starts/stops even if it is triggered by an external tool (e.g. hcitool). Signed-off-by: Andre Guedes Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 2 ++ net/bluetooth/hci_event.c | 5 +++++ 3 files changed, 8 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9751da78341d..bf2ef5667887 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -61,6 +61,7 @@ struct discovery_state { DISCOVERY_STOPPED, DISCOVERY_STARTING, DISCOVERY_INQUIRY, + DISCOVERY_LE_SCAN, DISCOVERY_RESOLVING, DISCOVERY_STOPPING, } state; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8bffd3eb344d..1705d9372725 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -361,6 +361,7 @@ bool hci_discovery_active(struct hci_dev *hdev) struct discovery_state *discov = &hdev->discovery; if (discov->state == DISCOVERY_INQUIRY || + discov->state == DISCOVERY_LE_SCAN || discov->state == DISCOVERY_RESOLVING) return true; @@ -381,6 +382,7 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state) case DISCOVERY_STARTING: break; case DISCOVERY_INQUIRY: + case DISCOVERY_LE_SCAN: mgmt_discovering(hdev, 1); break; case DISCOVERY_RESOLVING: diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 23dbb31f0423..8971c18205c0 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1056,12 +1056,17 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, hci_dev_lock(hdev); hci_adv_entries_clear(hdev); + hci_discovery_set_state(hdev, DISCOVERY_LE_SCAN); hci_dev_unlock(hdev); break; case LE_SCANNING_DISABLED: clear_bit(HCI_LE_SCAN, &hdev->dev_flags); + hci_dev_lock(hdev); + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + hci_dev_unlock(hdev); + schedule_delayed_work(&hdev->adv_work, ADV_CLEAR_TIMEOUT); break; -- cgit From 7ba8b4be38e7c83b2b13333a82a0ecde921a7390 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 3 Feb 2012 17:47:59 -0300 Subject: Bluetooth: Add hci_do_le_scan() This patch adds to hci_core the hci_do_le_scan function which should be used to scan LE devices. In order to enable LE scan, hci_do_le_scan() sends commands (Set LE Scan Parameters and Set LE Scan Enable) to the controller and waits for its results. If commands were executed successfully a delayed work is scheduled to disable the ongoing scanning after some amount of time. This function blocks. Signed-off-by: Andre Guedes Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 8 +++++ net/bluetooth/hci_core.c | 74 ++++++++++++++++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 13 +++++-- 3 files changed, 92 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index bf2ef5667887..3e70872bffea 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -122,6 +122,12 @@ struct adv_entry { u8 bdaddr_type; }; +struct le_scan_params { + u8 type; + u16 interval; + u16 window; +}; + #define NUM_REASSEMBLY 4 struct hci_dev { struct list_head list; @@ -261,6 +267,8 @@ struct hci_dev { unsigned long dev_flags; + struct delayed_work le_scan_disable; + int (*open)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 1175f27bd445..ae86cdd80ac0 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -759,6 +759,8 @@ static int hci_dev_do_close(struct hci_dev *hdev) if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) cancel_delayed_work(&hdev->service_cache); + cancel_delayed_work_sync(&hdev->le_scan_disable); + hci_dev_lock(hdev); inquiry_cache_flush(hdev); hci_conn_hash_flush(hdev); @@ -1596,6 +1598,76 @@ int hci_add_adv_entry(struct hci_dev *hdev, return 0; } +static void le_scan_param_req(struct hci_dev *hdev, unsigned long opt) +{ + struct le_scan_params *param = (struct le_scan_params *) opt; + struct hci_cp_le_set_scan_param cp; + + memset(&cp, 0, sizeof(cp)); + cp.type = param->type; + cp.interval = cpu_to_le16(param->interval); + cp.window = cpu_to_le16(param->window); + + hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_PARAM, sizeof(cp), &cp); +} + +static void le_scan_enable_req(struct hci_dev *hdev, unsigned long opt) +{ + struct hci_cp_le_set_scan_enable cp; + + memset(&cp, 0, sizeof(cp)); + cp.enable = 1; + + hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); +} + +static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval, + u16 window, int timeout) +{ + long timeo = msecs_to_jiffies(3000); + struct le_scan_params param; + int err; + + BT_DBG("%s", hdev->name); + + if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) + return -EINPROGRESS; + + param.type = type; + param.interval = interval; + param.window = window; + + hci_req_lock(hdev); + + err = __hci_request(hdev, le_scan_param_req, (unsigned long) ¶m, + timeo); + if (!err) + err = __hci_request(hdev, le_scan_enable_req, 0, timeo); + + hci_req_unlock(hdev); + + if (err < 0) + return err; + + schedule_delayed_work(&hdev->le_scan_disable, + msecs_to_jiffies(timeout)); + + return 0; +} + +static void le_scan_disable_work(struct work_struct *work) +{ + struct hci_dev *hdev = container_of(work, struct hci_dev, + le_scan_disable.work); + struct hci_cp_le_set_scan_enable cp; + + BT_DBG("%s", hdev->name); + + memset(&cp, 0, sizeof(cp)); + + hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); +} + /* Register HCI device */ int hci_register_dev(struct hci_dev *hdev) { @@ -1682,6 +1754,8 @@ int hci_register_dev(struct hci_dev *hdev) atomic_set(&hdev->promisc, 0); + INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work); + write_unlock(&hci_dev_list_lock); hdev->workqueue = alloc_workqueue(hdev->name, WQ_HIGHPRI | WQ_UNBOUND | diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 8971c18205c0..97152d9d7116 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1031,6 +1031,8 @@ static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb) __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%x", hdev->name, status); + + hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_PARAM, status); } static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, @@ -1041,15 +1043,17 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, BT_DBG("%s status 0x%x", hdev->name, status); - if (status) - return; - cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_ENABLE); if (!cp) return; switch (cp->enable) { case LE_SCANNING_ENABLED: + hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_ENABLE, status); + + if (status) + return; + set_bit(HCI_LE_SCAN, &hdev->dev_flags); cancel_delayed_work_sync(&hdev->adv_work); @@ -1061,6 +1065,9 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, break; case LE_SCANNING_DISABLED: + if (status) + return; + clear_bit(HCI_LE_SCAN, &hdev->dev_flags); hci_dev_lock(hdev); -- cgit From 28b75a89480df99a17c8facd5c33985847d06bb6 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 3 Feb 2012 17:48:00 -0300 Subject: Bluetooth: Add hci_le_scan() We are not supposed to block in start_discovery() because start_discovery code is running in write() syscall context and this would block the write operation on the mgmt socket. This way, we cannot directly call hci_do_le_scan() to scan LE devices in start_discovery(). To overcome this issue a derefered work (hdev->le_scan) was created so we can properly call hci_do_le_scan(). The helper function hci_le_scan() simply set LE scan parameters and queue hdev->le_scan work. The work is queued on system_long_wq since it can sleep for a few seconds in the worst case (timeout). Signed-off-by: Andre Guedes Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 6 ++++++ net/bluetooth/hci_core.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3e70872bffea..7107790817a5 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -126,6 +126,7 @@ struct le_scan_params { u8 type; u16 interval; u16 window; + int timeout; }; #define NUM_REASSEMBLY 4 @@ -269,6 +270,9 @@ struct hci_dev { struct delayed_work le_scan_disable; + struct work_struct le_scan; + struct le_scan_params le_scan_params; + int (*open)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev); @@ -1033,5 +1037,7 @@ void hci_le_ltk_neg_reply(struct hci_conn *conn); int hci_do_inquiry(struct hci_dev *hdev, u8 length); int hci_cancel_inquiry(struct hci_dev *hdev); +int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window, + int timeout); #endif /* __HCI_CORE_H */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ae86cdd80ac0..3d09f4b4ca68 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -735,6 +735,8 @@ static int hci_dev_do_close(struct hci_dev *hdev) { BT_DBG("%s %p", hdev->name, hdev); + cancel_work_sync(&hdev->le_scan); + hci_req_cancel(hdev, ENODEV); hci_req_lock(hdev); @@ -1668,6 +1670,37 @@ static void le_scan_disable_work(struct work_struct *work) hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); } +static void le_scan_work(struct work_struct *work) +{ + struct hci_dev *hdev = container_of(work, struct hci_dev, le_scan); + struct le_scan_params *param = &hdev->le_scan_params; + + BT_DBG("%s", hdev->name); + + hci_do_le_scan(hdev, param->type, param->interval, + param->window, param->timeout); +} + +int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window, + int timeout) +{ + struct le_scan_params *param = &hdev->le_scan_params; + + BT_DBG("%s", hdev->name); + + if (work_busy(&hdev->le_scan)) + return -EINPROGRESS; + + param->type = type; + param->interval = interval; + param->window = window; + param->timeout = timeout; + + queue_work(system_long_wq, &hdev->le_scan); + + return 0; +} + /* Register HCI device */ int hci_register_dev(struct hci_dev *hdev) { @@ -1754,6 +1787,8 @@ int hci_register_dev(struct hci_dev *hdev) atomic_set(&hdev->promisc, 0); + INIT_WORK(&hdev->le_scan, le_scan_work); + INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work); write_unlock(&hci_dev_list_lock); -- cgit From 124f6e35286c9d8dc96f147a9026081256136615 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 9 Feb 2012 13:50:12 +0200 Subject: Bluetooth: Update and rename mgmt_remove_keys to mgmt_unpair_device This patch renames the mgmt_remove_keys command to mgmt_unpair_device and updates its parameters to match the latest API (specifically, it adds an address type parameter to the command and its response). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 34 ++++++++++++------------- net/bluetooth/mgmt.c | 60 +++++++++++++++++++++++++------------------- 2 files changed, 51 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 72975fd53988..4c18cd5fb8c1 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -175,17 +175,7 @@ struct mgmt_cp_load_long_term_keys { struct mgmt_ltk_info keys[0]; } __packed; -#define MGMT_OP_REMOVE_KEYS 0x0014 -struct mgmt_cp_remove_keys { - bdaddr_t bdaddr; - __u8 disconnect; -} __packed; -struct mgmt_rp_remove_keys { - bdaddr_t bdaddr; - __u8 status; -}; - -#define MGMT_OP_DISCONNECT 0x0015 +#define MGMT_OP_DISCONNECT 0x0014 struct mgmt_cp_disconnect { bdaddr_t bdaddr; } __packed; @@ -194,13 +184,13 @@ struct mgmt_rp_disconnect { __u8 status; } __packed; -#define MGMT_OP_GET_CONNECTIONS 0x0016 +#define MGMT_OP_GET_CONNECTIONS 0x0015 struct mgmt_rp_get_connections { __le16 conn_count; struct mgmt_addr_info addr[0]; } __packed; -#define MGMT_OP_PIN_CODE_REPLY 0x0017 +#define MGMT_OP_PIN_CODE_REPLY 0x0016 struct mgmt_cp_pin_code_reply { bdaddr_t bdaddr; __u8 pin_len; @@ -211,17 +201,17 @@ struct mgmt_rp_pin_code_reply { uint8_t status; } __packed; -#define MGMT_OP_PIN_CODE_NEG_REPLY 0x0018 +#define MGMT_OP_PIN_CODE_NEG_REPLY 0x0017 struct mgmt_cp_pin_code_neg_reply { bdaddr_t bdaddr; } __packed; -#define MGMT_OP_SET_IO_CAPABILITY 0x0019 +#define MGMT_OP_SET_IO_CAPABILITY 0x0018 struct mgmt_cp_set_io_capability { __u8 io_capability; } __packed; -#define MGMT_OP_PAIR_DEVICE 0x001A +#define MGMT_OP_PAIR_DEVICE 0x0019 struct mgmt_cp_pair_device { struct mgmt_addr_info addr; __u8 io_cap; @@ -231,7 +221,17 @@ struct mgmt_rp_pair_device { __u8 status; } __packed; -#define MGMT_OP_CANCEL_PAIR_DEVICE 0x001B +#define MGMT_OP_CANCEL_PAIR_DEVICE 0x001A + +#define MGMT_OP_UNPAIR_DEVICE 0x001B +struct mgmt_cp_unpair_device { + struct mgmt_addr_info addr; + __u8 disconnect; +} __packed; +struct mgmt_rp_unpair_device { + struct mgmt_addr_info addr; + __u8 status; +}; #define MGMT_OP_USER_CONFIRM_REPLY 0x001C struct mgmt_cp_user_confirm_reply { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 77bc5a4b026c..c64e5db7f596 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1073,57 +1073,63 @@ static int load_link_keys(struct sock *sk, u16 index, void *data, u16 len) return 0; } -static int remove_keys(struct sock *sk, u16 index, void *data, u16 len) +static int unpair_device(struct sock *sk, u16 index, void *data, u16 len) { struct hci_dev *hdev; - struct mgmt_cp_remove_keys *cp = data; - struct mgmt_rp_remove_keys rp; + struct mgmt_cp_unpair_device *cp = data; + struct mgmt_rp_unpair_device rp; struct hci_cp_disconnect dc; struct pending_cmd *cmd; struct hci_conn *conn; int err; if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, + return cmd_status(sk, index, MGMT_OP_UNPAIR_DEVICE, MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, + return cmd_status(sk, index, MGMT_OP_UNPAIR_DEVICE, MGMT_STATUS_INVALID_PARAMS); hci_dev_lock(hdev); memset(&rp, 0, sizeof(rp)); - bacpy(&rp.bdaddr, &cp->bdaddr); + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); + rp.addr.type = cp->addr.type; rp.status = MGMT_STATUS_FAILED; - err = hci_remove_ltk(hdev, &cp->bdaddr); - if (err < 0) { - err = cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, -err); - goto unlock; - } + if (cp->addr.type == MGMT_ADDR_BREDR) + err = hci_remove_link_key(hdev, &cp->addr.bdaddr); + else + err = hci_remove_ltk(hdev, &cp->addr.bdaddr); - err = hci_remove_link_key(hdev, &cp->bdaddr); if (err < 0) { rp.status = MGMT_STATUS_NOT_PAIRED; goto unlock; } if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) { - err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp, + err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp, sizeof(rp)); goto unlock; } - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); + if (cp->addr.type == MGMT_ADDR_BREDR) + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, + &cp->addr.bdaddr); + else + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, + &cp->addr.bdaddr); + if (!conn) { - err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp, + err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp, sizeof(rp)); goto unlock; } - cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_KEYS, hdev, cp, sizeof(*cp)); + cmd = mgmt_pending_add(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp, + sizeof(*cp)); if (!cmd) { err = -ENOMEM; goto unlock; @@ -1137,7 +1143,7 @@ static int remove_keys(struct sock *sk, u16 index, void *data, u16 len) unlock: if (err < 0) - err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp, + err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp, sizeof(rp)); hci_dev_unlock(hdev); hci_dev_put(hdev); @@ -2340,9 +2346,6 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_LOAD_LINK_KEYS: err = load_link_keys(sk, index, cp, len); break; - case MGMT_OP_REMOVE_KEYS: - err = remove_keys(sk, index, cp, len); - break; case MGMT_OP_DISCONNECT: err = disconnect(sk, index, cp, len); break; @@ -2364,6 +2367,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_CANCEL_PAIR_DEVICE: err = cancel_pair_device(sk, index, buf + sizeof(*hdr), len); break; + case MGMT_OP_UNPAIR_DEVICE: + err = unpair_device(sk, index, cp, len); + break; case MGMT_OP_USER_CONFIRM_REPLY: err = user_confirm_reply(sk, index, cp, len); break; @@ -2624,18 +2630,19 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data) mgmt_pending_remove(cmd); } -static void remove_keys_rsp(struct pending_cmd *cmd, void *data) +static void unpair_device_rsp(struct pending_cmd *cmd, void *data) { u8 *status = data; - struct mgmt_cp_remove_keys *cp = cmd->param; - struct mgmt_rp_remove_keys rp; + struct mgmt_cp_unpair_device *cp = cmd->param; + struct mgmt_rp_unpair_device rp; memset(&rp, 0, sizeof(rp)); - bacpy(&rp.bdaddr, &cp->bdaddr); + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); + rp.addr.type = cp->addr.type; if (status != NULL) rp.status = *status; - cmd_complete(cmd->sk, cmd->index, MGMT_OP_REMOVE_KEYS, &rp, + cmd_complete(cmd->sk, cmd->index, MGMT_OP_UNPAIR_DEVICE, &rp, sizeof(rp)); mgmt_pending_remove(cmd); @@ -2659,7 +2666,8 @@ int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, if (sk) sock_put(sk); - mgmt_pending_foreach(MGMT_OP_REMOVE_KEYS, hdev, remove_keys_rsp, NULL); + mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, + NULL); return err; } -- cgit From 88c3df13ca06718e5a8f509ae9cbb1228c10d537 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 9 Feb 2012 14:27:38 +0200 Subject: Bluetooth: Update mgmt_disconnect to match latest API This patch adds an address type parameter to the disconnect command and response in order to match the latest mgmt API specification. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 ++- include/net/bluetooth/mgmt.h | 4 ++-- net/bluetooth/hci_event.c | 28 +++++++++++++++++++++++++--- net/bluetooth/mgmt.c | 28 +++++++++++++--------------- 4 files changed, 42 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7107790817a5..634a0cdcdad6 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -958,7 +958,8 @@ int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 *dev_class); int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type); -int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status); +int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type, u8 status); int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 4c18cd5fb8c1..735e547e3448 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -177,10 +177,10 @@ struct mgmt_cp_load_long_term_keys { #define MGMT_OP_DISCONNECT 0x0014 struct mgmt_cp_disconnect { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; } __packed; struct mgmt_rp_disconnect { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __u8 status; } __packed; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ad5f37b13f77..f0c822db28d9 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1550,6 +1550,28 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status) hci_dev_unlock(hdev); } +static void hci_cs_disconnect(struct hci_dev *hdev, u8 status) +{ + struct hci_cp_disconnect *cp; + struct hci_conn *conn; + + if (!status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_DISCONNECT); + if (!cp) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); + if (conn) + mgmt_disconnect_failed(hdev, &conn->dst, conn->type, + conn->dst_type, status); + + hci_dev_unlock(hdev); +} + static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) { struct hci_cp_le_create_conn *cp; @@ -1839,7 +1861,8 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags) && (conn->type == ACL_LINK || conn->type == LE_LINK)) { if (ev->status != 0) - mgmt_disconnect_failed(hdev, &conn->dst, ev->status); + mgmt_disconnect_failed(hdev, &conn->dst, conn->type, + conn->dst_type, ev->status); else mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type); @@ -2350,8 +2373,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) break; case HCI_OP_DISCONNECT: - if (ev->status != 0) - mgmt_disconnect_failed(hdev, NULL, ev->status); + hci_cs_disconnect(hdev, ev->status); break; case HCI_OP_LE_CREATE_CONN: diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c64e5db7f596..f1257ee5afbc 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1185,9 +1185,10 @@ static int disconnect(struct sock *sk, u16 index, void *data, u16 len) goto failed; } - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - if (!conn) - conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr); + if (cp->addr.type == MGMT_ADDR_BREDR) + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr); + else + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr); if (!conn) { err = cmd_status(sk, index, MGMT_OP_DISCONNECT, @@ -2619,7 +2620,8 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data) struct sock **sk = data; struct mgmt_rp_disconnect rp; - bacpy(&rp.bdaddr, &cp->bdaddr); + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); + rp.addr.type = cp->addr.type; rp.status = 0; cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, &rp, sizeof(rp)); @@ -2672,27 +2674,23 @@ int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, return err; } -int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status) +int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type, u8 status) { + struct mgmt_rp_disconnect rp; struct pending_cmd *cmd; - u8 mgmt_err = mgmt_status(status); int err; cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev); if (!cmd) return -ENOENT; - if (bdaddr) { - struct mgmt_rp_disconnect rp; - - bacpy(&rp.bdaddr, bdaddr); - rp.status = status; + bacpy(&rp.addr.bdaddr, bdaddr); + rp.addr.type = link_to_mgmt(link_type, addr_type); + rp.status = mgmt_status(status); - err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, + err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, &rp, sizeof(rp)); - } else - err = cmd_status(cmd->sk, hdev->id, MGMT_OP_DISCONNECT, - mgmt_err); mgmt_pending_remove(cmd); -- cgit From 272d90df2d4d065e782cafb08358bd8918bf703a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 9 Feb 2012 15:26:12 +0200 Subject: Bluetooth: Add address type to user_confirm and user_passkey messages This patch upadate the user confirm and user passkey mgmt messages to match the latest API specification by adding an address type parameter to them. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 18 ++++---- include/net/bluetooth/mgmt.h | 16 +++---- net/bluetooth/hci_event.c | 14 +++--- net/bluetooth/mgmt.c | 98 ++++++++++++++++++++-------------------- net/bluetooth/smp.c | 4 +- 5 files changed, 79 insertions(+), 71 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 634a0cdcdad6..5f27694068f2 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -968,16 +968,18 @@ int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status); int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, - __le32 value, u8 confirm_hint); + u8 link_type, u8 addr_type, __le32 value, + u8 confirm_hint); int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status); -int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, - bdaddr_t *bdaddr, u8 status); -int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr); + u8 link_type, u8 addr_type, u8 status); +int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type, u8 status); +int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type); int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status); -int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, - bdaddr_t *bdaddr, u8 status); + u8 link_type, u8 addr_type, u8 status); +int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type, u8 status); int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status); int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 735e547e3448..378d498896b3 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -235,31 +235,31 @@ struct mgmt_rp_unpair_device { #define MGMT_OP_USER_CONFIRM_REPLY 0x001C struct mgmt_cp_user_confirm_reply { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; } __packed; struct mgmt_rp_user_confirm_reply { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __u8 status; } __packed; #define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x001D struct mgmt_cp_user_confirm_neg_reply { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; } __packed; #define MGMT_OP_USER_PASSKEY_REPLY 0x001E struct mgmt_cp_user_passkey_reply { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __le32 passkey; } __packed; struct mgmt_rp_user_passkey_reply { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __u8 status; } __packed; #define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x001F struct mgmt_cp_user_passkey_neg_reply { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; } __packed; #define MGMT_OP_READ_LOCAL_OOB_DATA 0x0020 @@ -376,14 +376,14 @@ struct mgmt_ev_pin_code_request { #define MGMT_EV_USER_CONFIRM_REQUEST 0x000F struct mgmt_ev_user_confirm_request { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __u8 confirm_hint; __le32 value; } __packed; #define MGMT_EV_USER_PASSKEY_REQUEST 0x0010 struct mgmt_ev_user_passkey_request { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; } __packed; #define MGMT_EV_AUTH_FAILED 0x0011 diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f0c822db28d9..3bf3f4d59bcc 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -960,8 +960,8 @@ static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, - rp->status); + mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, ACL_LINK, + 0, rp->status); hci_dev_unlock(hdev); } @@ -977,6 +977,7 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev, if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr, + ACL_LINK, 0, rp->status); hci_dev_unlock(hdev); @@ -991,8 +992,8 @@ static void hci_cc_user_passkey_reply(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr, - rp->status); + mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr, ACL_LINK, + 0, rp->status); hci_dev_unlock(hdev); } @@ -1008,6 +1009,7 @@ static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev, if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr, + ACL_LINK, 0, rp->status); hci_dev_unlock(hdev); @@ -3123,7 +3125,7 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev, } confirm: - mgmt_user_confirm_request(hdev, &ev->bdaddr, ev->passkey, + mgmt_user_confirm_request(hdev, &ev->bdaddr, ACL_LINK, 0, ev->passkey, confirm_hint); unlock: @@ -3140,7 +3142,7 @@ static inline void hci_user_passkey_request_evt(struct hci_dev *hdev, hci_dev_lock(hdev); if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_user_passkey_request(hdev, &ev->bdaddr); + mgmt_user_passkey_request(hdev, &ev->bdaddr, ACL_LINK, 0); hci_dev_unlock(hdev); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f1257ee5afbc..16fc828096f6 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1629,7 +1629,8 @@ unlock: } static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr, - u16 mgmt_op, u16 hci_op, __le32 passkey) + u8 type, u16 mgmt_op, u16 hci_op, + __le32 passkey) { struct pending_cmd *cmd; struct hci_dev *hdev; @@ -1648,24 +1649,18 @@ static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr, goto done; } - /* - * Check for an existing ACL link, if present pair via - * HCI commands. - * - * If no ACL link is present, check for an LE link and if - * present, pair via the SMP engine. - * - * If neither ACL nor LE links are present, fail with error. - */ - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr); - if (!conn) { + if (type == MGMT_ADDR_BREDR) + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr); + else conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr); - if (!conn) { - err = cmd_status(sk, index, mgmt_op, + + if (!conn) { + err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_NOT_CONNECTED); - goto done; - } + goto done; + } + if (type == MGMT_ADDR_LE_PUBLIC || type == MGMT_ADDR_LE_RANDOM) { /* Continue with pairing via SMP */ err = smp_user_confirm_reply(conn, mgmt_op, passkey); @@ -1715,9 +1710,9 @@ static int user_confirm_reply(struct sock *sk, u16 index, void *data, u16 len) return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_REPLY, MGMT_STATUS_INVALID_PARAMS); - return user_pairing_resp(sk, index, &cp->bdaddr, - MGMT_OP_USER_CONFIRM_REPLY, - HCI_OP_USER_CONFIRM_REPLY, 0); + return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type, + MGMT_OP_USER_CONFIRM_REPLY, + HCI_OP_USER_CONFIRM_REPLY, 0); } static int user_confirm_neg_reply(struct sock *sk, u16 index, void *data, @@ -1731,9 +1726,9 @@ static int user_confirm_neg_reply(struct sock *sk, u16 index, void *data, return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_NEG_REPLY, MGMT_STATUS_INVALID_PARAMS); - return user_pairing_resp(sk, index, &cp->bdaddr, - MGMT_OP_USER_CONFIRM_NEG_REPLY, - HCI_OP_USER_CONFIRM_NEG_REPLY, 0); + return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type, + MGMT_OP_USER_CONFIRM_NEG_REPLY, + HCI_OP_USER_CONFIRM_NEG_REPLY, 0); } static int user_passkey_reply(struct sock *sk, u16 index, void *data, u16 len) @@ -1746,9 +1741,10 @@ static int user_passkey_reply(struct sock *sk, u16 index, void *data, u16 len) return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_REPLY, EINVAL); - return user_pairing_resp(sk, index, &cp->bdaddr, - MGMT_OP_USER_PASSKEY_REPLY, - HCI_OP_USER_PASSKEY_REPLY, cp->passkey); + return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type, + MGMT_OP_USER_PASSKEY_REPLY, + HCI_OP_USER_PASSKEY_REPLY, + cp->passkey); } static int user_passkey_neg_reply(struct sock *sk, u16 index, void *data, @@ -1762,9 +1758,9 @@ static int user_passkey_neg_reply(struct sock *sk, u16 index, void *data, return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_NEG_REPLY, EINVAL); - return user_pairing_resp(sk, index, &cp->bdaddr, - MGMT_OP_USER_PASSKEY_NEG_REPLY, - HCI_OP_USER_PASSKEY_NEG_REPLY, 0); + return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type, + MGMT_OP_USER_PASSKEY_NEG_REPLY, + HCI_OP_USER_PASSKEY_NEG_REPLY, 0); } static int set_local_name(struct sock *sk, u16 index, void *data, @@ -2765,13 +2761,15 @@ int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, } int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, - __le32 value, u8 confirm_hint) + u8 link_type, u8 addr_type, __le32 value, + u8 confirm_hint) { struct mgmt_ev_user_confirm_request ev; BT_DBG("%s", hdev->name); - bacpy(&ev.bdaddr, bdaddr); + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = link_to_mgmt(link_type, addr_type); ev.confirm_hint = confirm_hint; put_unaligned_le32(value, &ev.value); @@ -2779,20 +2777,23 @@ int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, NULL); } -int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr) +int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type) { struct mgmt_ev_user_passkey_request ev; BT_DBG("%s", hdev->name); - bacpy(&ev.bdaddr, bdaddr); + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = link_to_mgmt(link_type, addr_type); return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev), NULL); } static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status, u8 opcode) + u8 link_type, u8 addr_type, u8 status, + u8 opcode) { struct pending_cmd *cmd; struct mgmt_rp_user_confirm_reply rp; @@ -2802,7 +2803,8 @@ static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, if (!cmd) return -ENOENT; - bacpy(&rp.bdaddr, bdaddr); + bacpy(&rp.addr.bdaddr, bdaddr); + rp.addr.type = link_to_mgmt(link_type, addr_type); rp.status = mgmt_status(status); err = cmd_complete(cmd->sk, hdev->id, opcode, &rp, sizeof(rp)); @@ -2812,31 +2814,31 @@ static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, } int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status) + u8 link_type, u8 addr_type, u8 status) { - return user_pairing_resp_complete(hdev, bdaddr, status, - MGMT_OP_USER_CONFIRM_REPLY); + return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, + status, MGMT_OP_USER_CONFIRM_REPLY); } -int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, - bdaddr_t *bdaddr, u8 status) +int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type, u8 status) { - return user_pairing_resp_complete(hdev, bdaddr, status, - MGMT_OP_USER_CONFIRM_NEG_REPLY); + return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, + status, MGMT_OP_USER_CONFIRM_NEG_REPLY); } int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status) + u8 link_type, u8 addr_type, u8 status) { - return user_pairing_resp_complete(hdev, bdaddr, status, - MGMT_OP_USER_PASSKEY_REPLY); + return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, + status, MGMT_OP_USER_PASSKEY_REPLY); } -int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, - bdaddr_t *bdaddr, u8 status) +int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type, u8 status) { - return user_pairing_resp_complete(hdev, bdaddr, status, - MGMT_OP_USER_PASSKEY_NEG_REPLY); + return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, + status, MGMT_OP_USER_PASSKEY_NEG_REPLY); } int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 0563f737779a..589766d06f22 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -349,9 +349,11 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, hci_dev_lock(hcon->hdev); if (method == REQ_PASSKEY) - ret = mgmt_user_passkey_request(hcon->hdev, conn->dst); + ret = mgmt_user_passkey_request(hcon->hdev, conn->dst, + hcon->type, hcon->dst_type); else ret = mgmt_user_confirm_request(hcon->hdev, conn->dst, + hcon->type, hcon->dst_type, cpu_to_le32(passkey), 0); hci_dev_unlock(hcon->hdev); -- cgit From 664ce4cc293cd6c76236617f78689d0e03e69287 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 9 Feb 2012 15:44:09 +0200 Subject: Bluetooth: Add address type to Out Of Band mgmt messages This patch updates the implementation for these mgmt to be up to date with the latest API specification. Right now the address type isn't actually used for anything but that might change in the future. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 4 ++-- net/bluetooth/mgmt.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 378d498896b3..f284499b5f7f 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -270,14 +270,14 @@ struct mgmt_rp_read_local_oob_data { #define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0021 struct mgmt_cp_add_remote_oob_data { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __u8 hash[16]; __u8 randomizer[16]; } __packed; #define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x0022 struct mgmt_cp_remove_remote_oob_data { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; } __packed; #define MGMT_OP_START_DISCOVERY 0x0023 diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 16fc828096f6..763a447b2532 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1875,7 +1875,7 @@ static int add_remote_oob_data(struct sock *sk, u16 index, void *data, hci_dev_lock(hdev); - err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash, + err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash, cp->randomizer); if (err < 0) err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, @@ -1910,7 +1910,7 @@ static int remove_remote_oob_data(struct sock *sk, u16 index, hci_dev_lock(hdev); - err = hci_remove_remote_oob_data(hdev, &cp->bdaddr); + err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr); if (err < 0) err = cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, MGMT_STATUS_INVALID_PARAMS); -- cgit From 88c1fe4ba55c7245ad2f3c81689f854287875121 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 9 Feb 2012 15:56:11 +0200 Subject: Bluetooth: Add address type to mgmt blacklist messages This patch updates the implmentation for mgmt_block_device and mgmt_unblock_device and their corresponding events to match the latest API specification. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 8 ++++---- include/net/bluetooth/mgmt.h | 8 ++++---- net/bluetooth/hci_core.c | 8 ++++---- net/bluetooth/hci_sock.c | 4 ++-- net/bluetooth/mgmt.c | 14 ++++++++------ 5 files changed, 22 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5f27694068f2..14a655f3929c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -646,8 +646,8 @@ int hci_inquiry(void __user *arg); struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_blacklist_clear(struct hci_dev *hdev); -int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr); -int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr); +int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); +int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int hci_uuids_clear(struct hci_dev *hdev); @@ -992,8 +992,8 @@ int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status); int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status); int mgmt_discovering(struct hci_dev *hdev, u8 discovering); -int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr); -int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr); +int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); +int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index f284499b5f7f..92f85c834677 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -299,12 +299,12 @@ struct mgmt_rp_confirm_name { #define MGMT_OP_BLOCK_DEVICE 0x0026 struct mgmt_cp_block_device { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; } __packed; #define MGMT_OP_UNBLOCK_DEVICE 0x0027 struct mgmt_cp_unblock_device { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; } __packed; #define MGMT_EV_CMD_COMPLETE 0x0001 @@ -405,10 +405,10 @@ struct mgmt_ev_device_found { #define MGMT_EV_DEVICE_BLOCKED 0x0014 struct mgmt_ev_device_blocked { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; } __packed; #define MGMT_EV_DEVICE_UNBLOCKED 0x0015 struct mgmt_ev_device_unblocked { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; } __packed; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3d09f4b4ca68..9ada16449aed 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1489,7 +1489,7 @@ int hci_blacklist_clear(struct hci_dev *hdev) return 0; } -int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr) +int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) { struct bdaddr_list *entry; @@ -1507,10 +1507,10 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr) list_add(&entry->list, &hdev->blacklist); - return mgmt_device_blocked(hdev, bdaddr); + return mgmt_device_blocked(hdev, bdaddr, type); } -int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) +int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) { struct bdaddr_list *entry; @@ -1524,7 +1524,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) list_del(&entry->list); kfree(entry); - return mgmt_device_unblocked(hdev, bdaddr); + return mgmt_device_unblocked(hdev, bdaddr, type); } static void hci_clear_adv_cache(struct work_struct *work) diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 0dcc96266779..9e854d9fb460 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -190,7 +190,7 @@ static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg) hci_dev_lock(hdev); - err = hci_blacklist_add(hdev, &bdaddr); + err = hci_blacklist_add(hdev, &bdaddr, 0); hci_dev_unlock(hdev); @@ -207,7 +207,7 @@ static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg) hci_dev_lock(hdev); - err = hci_blacklist_del(hdev, &bdaddr); + err = hci_blacklist_del(hdev, &bdaddr, 0); hci_dev_unlock(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 763a447b2532..413a0b97c533 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2114,7 +2114,7 @@ static int block_device(struct sock *sk, u16 index, void *data, u16 len) hci_dev_lock(hdev); - err = hci_blacklist_add(hdev, &cp->bdaddr); + err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type); if (err < 0) err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, MGMT_STATUS_FAILED); @@ -2147,7 +2147,7 @@ static int unblock_device(struct sock *sk, u16 index, void *data, u16 len) hci_dev_lock(hdev); - err = hci_blacklist_del(hdev, &cp->bdaddr); + err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type); if (err < 0) err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, @@ -3026,27 +3026,29 @@ int mgmt_discovering(struct hci_dev *hdev, u8 discovering) sizeof(discovering), NULL); } -int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr) +int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) { struct pending_cmd *cmd; struct mgmt_ev_device_blocked ev; cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev); - bacpy(&ev.bdaddr, bdaddr); + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = type; return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev), cmd ? cmd->sk : NULL); } -int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr) +int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) { struct pending_cmd *cmd; struct mgmt_ev_device_unblocked ev; cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev); - bacpy(&ev.bdaddr, bdaddr); + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = type; return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev), cmd ? cmd->sk : NULL); -- cgit From bab73cb68435232ba78a4bd1ac1a85862e3be0bb Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 9 Feb 2012 16:07:29 +0200 Subject: Bluetooth: Add address type to mgmt_ev_auth_failed This patch updates the Authentication Failed mgmt event to match the latest API specification by adding an address type to it. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 ++- include/net/bluetooth/mgmt.h | 2 +- net/bluetooth/hci_event.c | 6 ++++-- net/bluetooth/mgmt.c | 6 ++++-- net/bluetooth/smp.c | 5 ++++- 5 files changed, 15 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 14a655f3929c..ccb24a4212cd 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -980,7 +980,8 @@ int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); -int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status); +int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u8 status); int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, u8 *randomizer, u8 status); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 92f85c834677..17bbf8bf04ae 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -388,7 +388,7 @@ struct mgmt_ev_user_passkey_request { #define MGMT_EV_AUTH_FAILED 0x0011 struct mgmt_ev_auth_failed { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __u8 status; } __packed; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3bf3f4d59bcc..b0784ee5f8b9 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1901,7 +1901,8 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s conn->sec_level = conn->pending_sec_level; } } else { - mgmt_auth_failed(hdev, &conn->dst, ev->status); + mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type, + ev->status); } clear_bit(HCI_CONN_AUTH_PEND, &conn->flags); @@ -3166,7 +3167,8 @@ static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_ * event gets always produced as initiator and is also mapped to * the mgmt_auth_failed event */ if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status != 0) - mgmt_auth_failed(hdev, &conn->dst, ev->status); + mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type, + ev->status); hci_conn_put(conn); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 413a0b97c533..545919828562 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2841,11 +2841,13 @@ int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, status, MGMT_OP_USER_PASSKEY_NEG_REPLY); } -int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status) +int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u8 status) { struct mgmt_ev_auth_failed ev; - bacpy(&ev.bdaddr, bdaddr); + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = link_to_mgmt(link_type, addr_type); ev.status = mgmt_status(status); return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 589766d06f22..f6a6d8be3051 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -257,12 +257,15 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send) { + struct hci_conn *hcon = conn->hcon; + if (send) smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason); clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->flags); - mgmt_auth_failed(conn->hcon->hdev, conn->dst, reason); + mgmt_auth_failed(conn->hcon->hdev, conn->dst, hcon->type, + hcon->dst_type, reason); if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) { cancel_delayed_work_sync(&conn->security_timer); -- cgit From b1078ad0be344e7bec6e7991f33df17565d24e08 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 9 Feb 2012 17:21:16 +0200 Subject: Bluetooth: Add Device Unpaired mgmt event This patch add a new Device Unpaired mgmt event. This will be sent to all mgmt sockets except the one that requested unpairing (that socket will get a command complete instead). The event is also reserved for future SMP updates where a remote device will be able to request pairing revocation from us. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 5 +++++ net/bluetooth/mgmt.c | 27 +++++++++++++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 17bbf8bf04ae..5b5edeed59e2 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -412,3 +412,8 @@ struct mgmt_ev_device_blocked { struct mgmt_ev_device_unblocked { struct mgmt_addr_info addr; } __packed; + +#define MGMT_EV_DEVICE_UNPAIRED 0x0016 +struct mgmt_ev_device_unpaired { + struct mgmt_addr_info addr; +} __packed; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0cf0f4dc8213..a2c2e12516c6 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1073,6 +1073,18 @@ static int load_link_keys(struct sock *sk, u16 index, void *data, u16 len) return 0; } +static int device_unpaired(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 addr_type, struct sock *skip_sk) +{ + struct mgmt_ev_device_unpaired ev; + + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = addr_type; + + return mgmt_event(MGMT_EV_DEVICE_UNPAIRED, hdev, &ev, sizeof(ev), + skip_sk); +} + static int unpair_device(struct sock *sk, u16 index, void *data, u16 len) { struct hci_dev *hdev; @@ -1111,6 +1123,7 @@ static int unpair_device(struct sock *sk, u16 index, void *data, u16 len) if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) { err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp, sizeof(rp)); + device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk); goto unlock; } @@ -1124,6 +1137,7 @@ static int unpair_device(struct sock *sk, u16 index, void *data, u16 len) if (!conn) { err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp, sizeof(rp)); + device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk); goto unlock; } @@ -2629,18 +2643,17 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data) static void unpair_device_rsp(struct pending_cmd *cmd, void *data) { - u8 *status = data; + struct hci_dev *hdev = data; struct mgmt_cp_unpair_device *cp = cmd->param; struct mgmt_rp_unpair_device rp; memset(&rp, 0, sizeof(rp)); bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); rp.addr.type = cp->addr.type; - if (status != NULL) - rp.status = *status; - cmd_complete(cmd->sk, cmd->index, MGMT_OP_UNPAIR_DEVICE, &rp, - sizeof(rp)); + device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk); + + cmd_complete(cmd->sk, cmd->index, cmd->opcode, &rp, sizeof(rp)); mgmt_pending_remove(cmd); } @@ -2664,7 +2677,7 @@ int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, sock_put(sk); mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, - NULL); + hdev); return err; } @@ -2689,6 +2702,8 @@ int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, mgmt_pending_remove(cmd); + mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, + hdev); return err; } -- cgit From aa2b86d761a95068354511de755695ef6b53afc7 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 9 Feb 2012 21:58:30 +0100 Subject: Bluetooth: Introduce to_hci_dev() We currently use dev_set_drvdata to keep a pointer to ourself. This doesn't make sense as we are the bus and not a driver. Therefore, introduce to_hci_dev() so we can get a struct hci_dev pointer from a struct device pointer. dev_set/get_drvdata() is reserved for drivers that provide a device and not for the bus using the device. The bus can use simple pointer arithmetic to retrieve its private data. Signed-off-by: David Herrmann Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_sysfs.c | 33 ++++++++++++++++----------------- 2 files changed, 18 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ccb24a4212cd..221d772ded55 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -623,6 +623,8 @@ static inline struct hci_dev *hci_dev_hold(struct hci_dev *d) #define hci_dev_lock(d) mutex_lock(&d->lock) #define hci_dev_unlock(d) mutex_unlock(&d->lock) +#define to_hci_dev(d) container_of(d, struct hci_dev, dev) + struct hci_dev *hci_dev_get(int index); struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst); diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index ec03ee2b301e..2a0243add1e4 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -189,19 +189,19 @@ static inline char *host_typetostr(int type) static ssize_t show_bus(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = dev_get_drvdata(dev); + struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%s\n", host_bustostr(hdev->bus)); } static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = dev_get_drvdata(dev); + struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%s\n", host_typetostr(hdev->dev_type)); } static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = dev_get_drvdata(dev); + struct hci_dev *hdev = to_hci_dev(dev); char name[HCI_MAX_NAME_LENGTH + 1]; int i; @@ -214,20 +214,20 @@ static ssize_t show_name(struct device *dev, struct device_attribute *attr, char static ssize_t show_class(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = dev_get_drvdata(dev); + struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "0x%.2x%.2x%.2x\n", hdev->dev_class[2], hdev->dev_class[1], hdev->dev_class[0]); } static ssize_t show_address(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = dev_get_drvdata(dev); + struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%s\n", batostr(&hdev->bdaddr)); } static ssize_t show_features(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = dev_get_drvdata(dev); + struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n", hdev->features[0], hdev->features[1], @@ -238,31 +238,31 @@ static ssize_t show_features(struct device *dev, struct device_attribute *attr, static ssize_t show_manufacturer(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = dev_get_drvdata(dev); + struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%d\n", hdev->manufacturer); } static ssize_t show_hci_version(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = dev_get_drvdata(dev); + struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%d\n", hdev->hci_ver); } static ssize_t show_hci_revision(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = dev_get_drvdata(dev); + struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%d\n", hdev->hci_rev); } static ssize_t show_idle_timeout(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = dev_get_drvdata(dev); + struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%d\n", hdev->idle_timeout); } static ssize_t store_idle_timeout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct hci_dev *hdev = dev_get_drvdata(dev); + struct hci_dev *hdev = to_hci_dev(dev); unsigned int val; int rv; @@ -280,13 +280,13 @@ static ssize_t store_idle_timeout(struct device *dev, struct device_attribute *a static ssize_t show_sniff_max_interval(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = dev_get_drvdata(dev); + struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%d\n", hdev->sniff_max_interval); } static ssize_t store_sniff_max_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct hci_dev *hdev = dev_get_drvdata(dev); + struct hci_dev *hdev = to_hci_dev(dev); u16 val; int rv; @@ -304,13 +304,13 @@ static ssize_t store_sniff_max_interval(struct device *dev, struct device_attrib static ssize_t show_sniff_min_interval(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = dev_get_drvdata(dev); + struct hci_dev *hdev = to_hci_dev(dev); return sprintf(buf, "%d\n", hdev->sniff_min_interval); } static ssize_t store_sniff_min_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct hci_dev *hdev = dev_get_drvdata(dev); + struct hci_dev *hdev = to_hci_dev(dev); u16 val; int rv; @@ -370,7 +370,7 @@ static const struct attribute_group *bt_host_groups[] = { static void bt_host_release(struct device *dev) { - void *data = dev_get_drvdata(dev); + void *data = to_hci_dev(dev); kfree(data); module_put(THIS_MODULE); } @@ -525,7 +525,6 @@ void hci_init_sysfs(struct hci_dev *hdev) dev->class = bt_class; __module_get(THIS_MODULE); - dev_set_drvdata(dev, hdev); device_initialize(dev); } -- cgit From 155961e8001719af6d87cbcc961111e8ce477843 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 9 Feb 2012 21:58:32 +0100 Subject: Bluetooth: Remove hci_dev->driver_data The linux device model provides dev_set/get_drvdata so we can use this to save private driver data. This also removes several unnecessary casts. Signed-off-by: David Herrmann Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/bfusb.c | 10 +++++----- drivers/bluetooth/bluecard_cs.c | 12 ++++++------ drivers/bluetooth/bpa10x.c | 18 +++++++++--------- drivers/bluetooth/bt3c_cs.c | 6 +++--- drivers/bluetooth/btmrvl_debugfs.c | 26 ++++++++++++-------------- drivers/bluetooth/btmrvl_main.c | 11 ++++++----- drivers/bluetooth/btsdio.c | 10 +++++----- drivers/bluetooth/btuart_cs.c | 6 +++--- drivers/bluetooth/btusb.c | 28 ++++++++++++++-------------- drivers/bluetooth/btwilink.c | 8 ++++---- drivers/bluetooth/dtl1_cs.c | 6 +++--- drivers/bluetooth/hci_ldisc.c | 6 +++--- drivers/bluetooth/hci_vhci.c | 8 ++++---- include/net/bluetooth/hci_core.h | 11 ++++++++++- 14 files changed, 87 insertions(+), 79 deletions(-) (limited to 'include') diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index c7d6ff0ffcf1..b8ac1c549a1c 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -411,7 +411,7 @@ unlock: static int bfusb_open(struct hci_dev *hdev) { - struct bfusb_data *data = hdev->driver_data; + struct bfusb_data *data = hci_get_drvdata(hdev); unsigned long flags; int i, err; @@ -437,7 +437,7 @@ static int bfusb_open(struct hci_dev *hdev) static int bfusb_flush(struct hci_dev *hdev) { - struct bfusb_data *data = hdev->driver_data; + struct bfusb_data *data = hci_get_drvdata(hdev); BT_DBG("hdev %p bfusb %p", hdev, data); @@ -448,7 +448,7 @@ static int bfusb_flush(struct hci_dev *hdev) static int bfusb_close(struct hci_dev *hdev) { - struct bfusb_data *data = hdev->driver_data; + struct bfusb_data *data = hci_get_drvdata(hdev); unsigned long flags; BT_DBG("hdev %p bfusb %p", hdev, data); @@ -483,7 +483,7 @@ static int bfusb_send_frame(struct sk_buff *skb) if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; - data = hdev->driver_data; + data = hci_get_drvdata(hdev); switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: @@ -696,7 +696,7 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i data->hdev = hdev; hdev->bus = HCI_USB; - hdev->driver_data = data; + hci_set_drvdata(hdev, data); SET_HCIDEV_DEV(hdev, &intf->dev); hdev->open = bfusb_open; diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 6b1261f9deb0..1fcd92380356 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -561,7 +561,7 @@ static irqreturn_t bluecard_interrupt(int irq, void *dev_inst) static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud) { - bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data); + bluecard_info_t *info = hci_get_drvdata(hdev); struct sk_buff *skb; /* Ericsson baud rate command */ @@ -609,7 +609,7 @@ static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud) static int bluecard_hci_flush(struct hci_dev *hdev) { - bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data); + bluecard_info_t *info = hci_get_drvdata(hdev); /* Drop TX queue */ skb_queue_purge(&(info->txq)); @@ -620,7 +620,7 @@ static int bluecard_hci_flush(struct hci_dev *hdev) static int bluecard_hci_open(struct hci_dev *hdev) { - bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data); + bluecard_info_t *info = hci_get_drvdata(hdev); unsigned int iobase = info->p_dev->resource[0]->start; if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) @@ -640,7 +640,7 @@ static int bluecard_hci_open(struct hci_dev *hdev) static int bluecard_hci_close(struct hci_dev *hdev) { - bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data); + bluecard_info_t *info = hci_get_drvdata(hdev); unsigned int iobase = info->p_dev->resource[0]->start; if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) @@ -667,7 +667,7 @@ static int bluecard_hci_send_frame(struct sk_buff *skb) return -ENODEV; } - info = (bluecard_info_t *)(hdev->driver_data); + info = hci_get_drvdata(hdev); switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: @@ -729,7 +729,7 @@ static int bluecard_open(bluecard_info_t *info) info->hdev = hdev; hdev->bus = HCI_PCCARD; - hdev->driver_data = info; + hci_set_drvdata(hdev, info); SET_HCIDEV_DEV(hdev, &info->p_dev->dev); hdev->open = bluecard_hci_open; diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 9d635148104c..d894340a7601 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -66,7 +66,7 @@ struct hci_vendor_hdr { static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count) { - struct bpa10x_data *data = hdev->driver_data; + struct bpa10x_data *data = hci_get_drvdata(hdev); BT_DBG("%s queue %d buffer %p count %d", hdev->name, queue, buf, count); @@ -189,7 +189,7 @@ done: static void bpa10x_rx_complete(struct urb *urb) { struct hci_dev *hdev = urb->context; - struct bpa10x_data *data = hdev->driver_data; + struct bpa10x_data *data = hci_get_drvdata(hdev); int err; BT_DBG("%s urb %p status %d count %d", hdev->name, @@ -219,7 +219,7 @@ static void bpa10x_rx_complete(struct urb *urb) static inline int bpa10x_submit_intr_urb(struct hci_dev *hdev) { - struct bpa10x_data *data = hdev->driver_data; + struct bpa10x_data *data = hci_get_drvdata(hdev); struct urb *urb; unsigned char *buf; unsigned int pipe; @@ -260,7 +260,7 @@ static inline int bpa10x_submit_intr_urb(struct hci_dev *hdev) static inline int bpa10x_submit_bulk_urb(struct hci_dev *hdev) { - struct bpa10x_data *data = hdev->driver_data; + struct bpa10x_data *data = hci_get_drvdata(hdev); struct urb *urb; unsigned char *buf; unsigned int pipe; @@ -301,7 +301,7 @@ static inline int bpa10x_submit_bulk_urb(struct hci_dev *hdev) static int bpa10x_open(struct hci_dev *hdev) { - struct bpa10x_data *data = hdev->driver_data; + struct bpa10x_data *data = hci_get_drvdata(hdev); int err; BT_DBG("%s", hdev->name); @@ -329,7 +329,7 @@ error: static int bpa10x_close(struct hci_dev *hdev) { - struct bpa10x_data *data = hdev->driver_data; + struct bpa10x_data *data = hci_get_drvdata(hdev); BT_DBG("%s", hdev->name); @@ -343,7 +343,7 @@ static int bpa10x_close(struct hci_dev *hdev) static int bpa10x_flush(struct hci_dev *hdev) { - struct bpa10x_data *data = hdev->driver_data; + struct bpa10x_data *data = hci_get_drvdata(hdev); BT_DBG("%s", hdev->name); @@ -355,7 +355,7 @@ static int bpa10x_flush(struct hci_dev *hdev) static int bpa10x_send_frame(struct sk_buff *skb) { struct hci_dev *hdev = (struct hci_dev *) skb->dev; - struct bpa10x_data *data = hdev->driver_data; + struct bpa10x_data *data = hci_get_drvdata(hdev); struct usb_ctrlrequest *dr; struct urb *urb; unsigned int pipe; @@ -459,7 +459,7 @@ static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id * } hdev->bus = HCI_USB; - hdev->driver_data = data; + hci_set_drvdata(hdev, data); data->hdev = hdev; diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 0e304cb4bdea..9c09d6f05dc9 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -389,7 +389,7 @@ static irqreturn_t bt3c_interrupt(int irq, void *dev_inst) static int bt3c_hci_flush(struct hci_dev *hdev) { - bt3c_info_t *info = (bt3c_info_t *)(hdev->driver_data); + bt3c_info_t *info = hci_get_drvdata(hdev); /* Drop TX queue */ skb_queue_purge(&(info->txq)); @@ -428,7 +428,7 @@ static int bt3c_hci_send_frame(struct sk_buff *skb) return -ENODEV; } - info = (bt3c_info_t *) (hdev->driver_data); + info = hci_get_drvdata(hdev); switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: @@ -575,7 +575,7 @@ static int bt3c_open(bt3c_info_t *info) info->hdev = hdev; hdev->bus = HCI_PCCARD; - hdev->driver_data = info; + hci_set_drvdata(hdev, info); SET_HCIDEV_DEV(hdev, &info->p_dev->dev); hdev->open = bt3c_hci_open; diff --git a/drivers/bluetooth/btmrvl_debugfs.c b/drivers/bluetooth/btmrvl_debugfs.c index 8ecf4c6c2874..60fe333cfd40 100644 --- a/drivers/bluetooth/btmrvl_debugfs.c +++ b/drivers/bluetooth/btmrvl_debugfs.c @@ -384,7 +384,7 @@ static const struct file_operations btmrvl_txdnldready_fops = { void btmrvl_debugfs_init(struct hci_dev *hdev) { - struct btmrvl_private *priv = hdev->driver_data; + struct btmrvl_private *priv = hci_get_drvdata(hdev); struct btmrvl_debugfs_data *dbg; if (!hdev->debugfs) @@ -401,36 +401,34 @@ void btmrvl_debugfs_init(struct hci_dev *hdev) dbg->config_dir = debugfs_create_dir("config", hdev->debugfs); dbg->psmode = debugfs_create_file("psmode", 0644, dbg->config_dir, - hdev->driver_data, &btmrvl_psmode_fops); + priv, &btmrvl_psmode_fops); dbg->pscmd = debugfs_create_file("pscmd", 0644, dbg->config_dir, - hdev->driver_data, &btmrvl_pscmd_fops); + priv, &btmrvl_pscmd_fops); dbg->gpiogap = debugfs_create_file("gpiogap", 0644, dbg->config_dir, - hdev->driver_data, &btmrvl_gpiogap_fops); + priv, &btmrvl_gpiogap_fops); dbg->hsmode = debugfs_create_file("hsmode", 0644, dbg->config_dir, - hdev->driver_data, &btmrvl_hsmode_fops); + priv, &btmrvl_hsmode_fops); dbg->hscmd = debugfs_create_file("hscmd", 0644, dbg->config_dir, - hdev->driver_data, &btmrvl_hscmd_fops); + priv, &btmrvl_hscmd_fops); dbg->hscfgcmd = debugfs_create_file("hscfgcmd", 0644, dbg->config_dir, - hdev->driver_data, &btmrvl_hscfgcmd_fops); + priv, &btmrvl_hscfgcmd_fops); dbg->status_dir = debugfs_create_dir("status", hdev->debugfs); dbg->curpsmode = debugfs_create_file("curpsmode", 0444, - dbg->status_dir, - hdev->driver_data, - &btmrvl_curpsmode_fops); + dbg->status_dir, priv, &btmrvl_curpsmode_fops); dbg->psstate = debugfs_create_file("psstate", 0444, dbg->status_dir, - hdev->driver_data, &btmrvl_psstate_fops); + priv, &btmrvl_psstate_fops); dbg->hsstate = debugfs_create_file("hsstate", 0444, dbg->status_dir, - hdev->driver_data, &btmrvl_hsstate_fops); + priv, &btmrvl_hsstate_fops); dbg->txdnldready = debugfs_create_file("txdnldready", 0444, dbg->status_dir, - hdev->driver_data, + priv, &btmrvl_txdnldready_fops); } void btmrvl_debugfs_remove(struct hci_dev *hdev) { - struct btmrvl_private *priv = hdev->driver_data; + struct btmrvl_private *priv = hci_get_drvdata(hdev); struct btmrvl_debugfs_data *dbg = priv->debugfs_data; if (!dbg) diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index 66b58fd09fbe..d1209adc882d 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -394,12 +394,13 @@ static int btmrvl_send_frame(struct sk_buff *skb) BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len); - if (!hdev || !hdev->driver_data) { + if (!hdev) { BT_ERR("Frame for unknown HCI device"); return -ENODEV; } - priv = (struct btmrvl_private *) hdev->driver_data; + priv = hci_get_drvdata(hdev); + if (!test_bit(HCI_RUNNING, &hdev->flags)) { BT_ERR("Failed testing HCI_RUNING, flags=%lx", hdev->flags); print_hex_dump_bytes("data: ", DUMP_PREFIX_OFFSET, @@ -430,7 +431,7 @@ static int btmrvl_send_frame(struct sk_buff *skb) static int btmrvl_flush(struct hci_dev *hdev) { - struct btmrvl_private *priv = hdev->driver_data; + struct btmrvl_private *priv = hci_get_drvdata(hdev); skb_queue_purge(&priv->adapter->tx_queue); @@ -439,7 +440,7 @@ static int btmrvl_flush(struct hci_dev *hdev) static int btmrvl_close(struct hci_dev *hdev) { - struct btmrvl_private *priv = hdev->driver_data; + struct btmrvl_private *priv = hci_get_drvdata(hdev); if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) return 0; @@ -542,7 +543,7 @@ int btmrvl_register_hdev(struct btmrvl_private *priv) } priv->btmrvl_dev.hcidev = hdev; - hdev->driver_data = priv; + hci_set_drvdata(hdev, priv); hdev->bus = HCI_SDIO; hdev->open = btmrvl_open; diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c index 2d6e4ed1637f..e10ea0347051 100644 --- a/drivers/bluetooth/btsdio.c +++ b/drivers/bluetooth/btsdio.c @@ -189,7 +189,7 @@ static void btsdio_interrupt(struct sdio_func *func) static int btsdio_open(struct hci_dev *hdev) { - struct btsdio_data *data = hdev->driver_data; + struct btsdio_data *data = hci_get_drvdata(hdev); int err; BT_DBG("%s", hdev->name); @@ -225,7 +225,7 @@ release: static int btsdio_close(struct hci_dev *hdev) { - struct btsdio_data *data = hdev->driver_data; + struct btsdio_data *data = hci_get_drvdata(hdev); BT_DBG("%s", hdev->name); @@ -246,7 +246,7 @@ static int btsdio_close(struct hci_dev *hdev) static int btsdio_flush(struct hci_dev *hdev) { - struct btsdio_data *data = hdev->driver_data; + struct btsdio_data *data = hci_get_drvdata(hdev); BT_DBG("%s", hdev->name); @@ -258,7 +258,7 @@ static int btsdio_flush(struct hci_dev *hdev) static int btsdio_send_frame(struct sk_buff *skb) { struct hci_dev *hdev = (struct hci_dev *) skb->dev; - struct btsdio_data *data = hdev->driver_data; + struct btsdio_data *data = hci_get_drvdata(hdev); BT_DBG("%s", hdev->name); @@ -321,7 +321,7 @@ static int btsdio_probe(struct sdio_func *func, } hdev->bus = HCI_SDIO; - hdev->driver_data = data; + hci_set_drvdata(hdev, data); if (id->class == SDIO_CLASS_BT_AMP) hdev->dev_type = HCI_AMP; diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index 80ad2b9b352e..194224d07f7c 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -397,7 +397,7 @@ static void btuart_change_speed(btuart_info_t *info, unsigned int speed) static int btuart_hci_flush(struct hci_dev *hdev) { - btuart_info_t *info = (btuart_info_t *)(hdev->driver_data); + btuart_info_t *info = hci_get_drvdata(hdev); /* Drop TX queue */ skb_queue_purge(&(info->txq)); @@ -435,7 +435,7 @@ static int btuart_hci_send_frame(struct sk_buff *skb) return -ENODEV; } - info = (btuart_info_t *)(hdev->driver_data); + info = hci_get_drvdata(hdev); switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: @@ -493,7 +493,7 @@ static int btuart_open(btuart_info_t *info) info->hdev = hdev; hdev->bus = HCI_PCCARD; - hdev->driver_data = info; + hci_set_drvdata(hdev, info); SET_HCIDEV_DEV(hdev, &info->p_dev->dev); hdev->open = btuart_hci_open; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index afcd2816a24b..f4fb256d6ca6 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -243,7 +243,7 @@ static int inc_tx(struct btusb_data *data) static void btusb_intr_complete(struct urb *urb) { struct hci_dev *hdev = urb->context; - struct btusb_data *data = hdev->driver_data; + struct btusb_data *data = hci_get_drvdata(hdev); int err; BT_DBG("%s urb %p status %d count %d", hdev->name, @@ -282,7 +282,7 @@ static void btusb_intr_complete(struct urb *urb) static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags) { - struct btusb_data *data = hdev->driver_data; + struct btusb_data *data = hci_get_drvdata(hdev); struct urb *urb; unsigned char *buf; unsigned int pipe; @@ -331,7 +331,7 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags) static void btusb_bulk_complete(struct urb *urb) { struct hci_dev *hdev = urb->context; - struct btusb_data *data = hdev->driver_data; + struct btusb_data *data = hci_get_drvdata(hdev); int err; BT_DBG("%s urb %p status %d count %d", hdev->name, @@ -370,7 +370,7 @@ static void btusb_bulk_complete(struct urb *urb) static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags) { - struct btusb_data *data = hdev->driver_data; + struct btusb_data *data = hci_get_drvdata(hdev); struct urb *urb; unsigned char *buf; unsigned int pipe; @@ -417,7 +417,7 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags) static void btusb_isoc_complete(struct urb *urb) { struct hci_dev *hdev = urb->context; - struct btusb_data *data = hdev->driver_data; + struct btusb_data *data = hci_get_drvdata(hdev); int i, err; BT_DBG("%s urb %p status %d count %d", hdev->name, @@ -484,7 +484,7 @@ static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu) static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags) { - struct btusb_data *data = hdev->driver_data; + struct btusb_data *data = hci_get_drvdata(hdev); struct urb *urb; unsigned char *buf; unsigned int pipe; @@ -537,7 +537,7 @@ static void btusb_tx_complete(struct urb *urb) { struct sk_buff *skb = urb->context; struct hci_dev *hdev = (struct hci_dev *) skb->dev; - struct btusb_data *data = hdev->driver_data; + struct btusb_data *data = hci_get_drvdata(hdev); BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status, urb->actual_length); @@ -584,7 +584,7 @@ done: static int btusb_open(struct hci_dev *hdev) { - struct btusb_data *data = hdev->driver_data; + struct btusb_data *data = hci_get_drvdata(hdev); int err; BT_DBG("%s", hdev->name); @@ -634,7 +634,7 @@ static void btusb_stop_traffic(struct btusb_data *data) static int btusb_close(struct hci_dev *hdev) { - struct btusb_data *data = hdev->driver_data; + struct btusb_data *data = hci_get_drvdata(hdev); int err; BT_DBG("%s", hdev->name); @@ -664,7 +664,7 @@ failed: static int btusb_flush(struct hci_dev *hdev) { - struct btusb_data *data = hdev->driver_data; + struct btusb_data *data = hci_get_drvdata(hdev); BT_DBG("%s", hdev->name); @@ -676,7 +676,7 @@ static int btusb_flush(struct hci_dev *hdev) static int btusb_send_frame(struct sk_buff *skb) { struct hci_dev *hdev = (struct hci_dev *) skb->dev; - struct btusb_data *data = hdev->driver_data; + struct btusb_data *data = hci_get_drvdata(hdev); struct usb_ctrlrequest *dr; struct urb *urb; unsigned int pipe; @@ -786,7 +786,7 @@ done: static void btusb_notify(struct hci_dev *hdev, unsigned int evt) { - struct btusb_data *data = hdev->driver_data; + struct btusb_data *data = hci_get_drvdata(hdev); BT_DBG("%s evt %d", hdev->name, evt); @@ -798,7 +798,7 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt) static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting) { - struct btusb_data *data = hdev->driver_data; + struct btusb_data *data = hci_get_drvdata(hdev); struct usb_interface *intf = data->isoc; struct usb_endpoint_descriptor *ep_desc; int i, err; @@ -986,7 +986,7 @@ static int btusb_probe(struct usb_interface *intf, } hdev->bus = HCI_USB; - hdev->driver_data = data; + hci_set_drvdata(hdev, data); data->hdev = hdev; diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c index b81b32e4fa12..88694697f34f 100644 --- a/drivers/bluetooth/btwilink.c +++ b/drivers/bluetooth/btwilink.c @@ -161,7 +161,7 @@ static int ti_st_open(struct hci_dev *hdev) return -EBUSY; /* provide contexts for callbacks from ST */ - hst = hdev->driver_data; + hst = hci_get_drvdata(hdev); for (i = 0; i < MAX_BT_CHNL_IDS; i++) { ti_st_proto[i].priv_data = hst; @@ -236,7 +236,7 @@ done: static int ti_st_close(struct hci_dev *hdev) { int err, i; - struct ti_st *hst = hdev->driver_data; + struct ti_st *hst = hci_get_drvdata(hdev); if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) return 0; @@ -264,7 +264,7 @@ static int ti_st_send_frame(struct sk_buff *skb) if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; - hst = hdev->driver_data; + hst = hci_get_drvdata(hdev); /* Prepend skb with frame type */ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); @@ -312,7 +312,7 @@ static int bt_ti_probe(struct platform_device *pdev) hst->hdev = hdev; hdev->bus = HCI_UART; - hdev->driver_data = hst; + hci_set_drvdata(hdev, hst); hdev->open = ti_st_open; hdev->close = ti_st_close; hdev->flush = NULL; diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 295cf1b4a052..049c0594a76b 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -364,7 +364,7 @@ static int dtl1_hci_open(struct hci_dev *hdev) static int dtl1_hci_flush(struct hci_dev *hdev) { - dtl1_info_t *info = (dtl1_info_t *)(hdev->driver_data); + dtl1_info_t *info = hci_get_drvdata(hdev); /* Drop TX queue */ skb_queue_purge(&(info->txq)); @@ -396,7 +396,7 @@ static int dtl1_hci_send_frame(struct sk_buff *skb) return -ENODEV; } - info = (dtl1_info_t *)(hdev->driver_data); + info = hci_get_drvdata(hdev); switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: @@ -475,7 +475,7 @@ static int dtl1_open(dtl1_info_t *info) info->hdev = hdev; hdev->bus = HCI_PCCARD; - hdev->driver_data = info; + hci_set_drvdata(hdev, info); SET_HCIDEV_DEV(hdev, &info->p_dev->dev); hdev->open = dtl1_hci_open; diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 459ff0ba5a42..01c23dfe2a10 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -174,7 +174,7 @@ static int hci_uart_open(struct hci_dev *hdev) /* Reset device */ static int hci_uart_flush(struct hci_dev *hdev) { - struct hci_uart *hu = (struct hci_uart *) hdev->driver_data; + struct hci_uart *hu = hci_get_drvdata(hdev); struct tty_struct *tty = hu->tty; BT_DBG("hdev %p tty %p", hdev, tty); @@ -220,7 +220,7 @@ static int hci_uart_send_frame(struct sk_buff *skb) if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; - hu = (struct hci_uart *) hdev->driver_data; + hu = hci_get_drvdata(hdev); BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len); @@ -384,7 +384,7 @@ static int hci_uart_register_dev(struct hci_uart *hu) hu->hdev = hdev; hdev->bus = HCI_UART; - hdev->driver_data = hu; + hci_set_drvdata(hdev, hu); hdev->open = hci_uart_open; hdev->close = hci_uart_close; diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 5f305c131a0d..158bfe507da7 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -61,7 +61,7 @@ static int vhci_open_dev(struct hci_dev *hdev) static int vhci_close_dev(struct hci_dev *hdev) { - struct vhci_data *data = hdev->driver_data; + struct vhci_data *data = hci_get_drvdata(hdev); if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) return 0; @@ -73,7 +73,7 @@ static int vhci_close_dev(struct hci_dev *hdev) static int vhci_flush(struct hci_dev *hdev) { - struct vhci_data *data = hdev->driver_data; + struct vhci_data *data = hci_get_drvdata(hdev); skb_queue_purge(&data->readq); @@ -93,7 +93,7 @@ static int vhci_send_frame(struct sk_buff *skb) if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; - data = hdev->driver_data; + data = hci_get_drvdata(hdev); memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); skb_queue_tail(&data->readq, skb); @@ -234,7 +234,7 @@ static int vhci_open(struct inode *inode, struct file *file) data->hdev = hdev; hdev->bus = HCI_VIRTUAL; - hdev->driver_data = data; + hci_set_drvdata(hdev, data); if (amp) hdev->dev_type = HCI_AMP; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 221d772ded55..4559189a22a1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -254,7 +254,6 @@ struct hci_dev { struct sk_buff_head driver_init; - void *driver_data; void *core_data; atomic_t promisc; @@ -625,6 +624,16 @@ static inline struct hci_dev *hci_dev_hold(struct hci_dev *d) #define to_hci_dev(d) container_of(d, struct hci_dev, dev) +static inline void *hci_get_drvdata(struct hci_dev *hdev) +{ + return dev_get_drvdata(&hdev->dev); +} + +static inline void hci_set_drvdata(struct hci_dev *hdev, void *data) +{ + dev_set_drvdata(&hdev->dev, data); +} + struct hci_dev *hci_dev_get(int index); struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst); -- cgit From 3dc07322b1ce3c8477690d54ebbf15a165f43066 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 9 Feb 2012 21:58:33 +0100 Subject: Bluetooth: Introduce to_hci_conn This avoids using the dev_set/get_drvdata() functions to retrieve a pointer to our own structure. We can use simple pointer arithmetic here. The drvdata field is actually not needed by any other code-path but this makes the code more consistent with hci_dev. Signed-off-by: David Herrmann Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_sysfs.c | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4559189a22a1..b20d990436b4 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -623,6 +623,7 @@ static inline struct hci_dev *hci_dev_hold(struct hci_dev *d) #define hci_dev_unlock(d) mutex_unlock(&d->lock) #define to_hci_dev(d) container_of(d, struct hci_dev, dev) +#define to_hci_conn(c) container_of(c, struct hci_conn, dev) static inline void *hci_get_drvdata(struct hci_dev *hdev) { diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 2a0243add1e4..17e6cd48ad4d 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -33,19 +33,19 @@ static inline char *link_typetostr(int type) static ssize_t show_link_type(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_conn *conn = dev_get_drvdata(dev); + struct hci_conn *conn = to_hci_conn(dev); return sprintf(buf, "%s\n", link_typetostr(conn->type)); } static ssize_t show_link_address(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_conn *conn = dev_get_drvdata(dev); + struct hci_conn *conn = to_hci_conn(dev); return sprintf(buf, "%s\n", batostr(&conn->dst)); } static ssize_t show_link_features(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_conn *conn = dev_get_drvdata(dev); + struct hci_conn *conn = to_hci_conn(dev); return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n", conn->features[0], conn->features[1], @@ -79,7 +79,7 @@ static const struct attribute_group *bt_link_groups[] = { static void bt_link_release(struct device *dev) { - void *data = dev_get_drvdata(dev); + void *data = to_hci_conn(dev); kfree(data); } @@ -120,8 +120,6 @@ void hci_conn_add_sysfs(struct hci_conn *conn) dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle); - dev_set_drvdata(&conn->dev, conn); - if (device_add(&conn->dev) < 0) { BT_ERR("Failed to register connection device"); return; -- cgit From 95100358491abaa2e9a5483811370059bbca4645 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 24 Nov 2011 20:03:08 +0100 Subject: printk/tracing: Add console output tracing Add a printk.console trace point to record any printk messages into the trace, regardless of the current console loglevel. This can help correlate (existing) printk debugging with other tracing. Link: http://lkml.kernel.org/r/1322161388.5366.54.camel@jlt3.sipsolutions.net Acked-by: Frederic Weisbecker Cc: Christoph Hellwig Cc: Ingo Molnar Acked-by: Peter Zijlstra Acked-by: Thomas Gleixner Signed-off-by: Johannes Berg Signed-off-by: Steven Rostedt --- include/trace/events/printk.h | 41 +++++++++++++++++++++++++++++++++++++++++ kernel/printk.c | 5 +++++ 2 files changed, 46 insertions(+) create mode 100644 include/trace/events/printk.h (limited to 'include') diff --git a/include/trace/events/printk.h b/include/trace/events/printk.h new file mode 100644 index 000000000000..94ec79cc011a --- /dev/null +++ b/include/trace/events/printk.h @@ -0,0 +1,41 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM printk + +#if !defined(_TRACE_PRINTK_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_PRINTK_H + +#include + +TRACE_EVENT_CONDITION(console, + TP_PROTO(const char *log_buf, unsigned start, unsigned end, + unsigned log_buf_len), + + TP_ARGS(log_buf, start, end, log_buf_len), + + TP_CONDITION(start != end), + + TP_STRUCT__entry( + __dynamic_array(char, msg, end - start + 1) + ), + + TP_fast_assign( + if ((start & (log_buf_len - 1)) > (end & (log_buf_len - 1))) { + memcpy(__get_dynamic_array(msg), + log_buf + (start & (log_buf_len - 1)), + log_buf_len - (start & (log_buf_len - 1))); + memcpy((char *)__get_dynamic_array(msg) + + log_buf_len - (start & (log_buf_len - 1)), + log_buf, end & (log_buf_len - 1)); + } else + memcpy(__get_dynamic_array(msg), + log_buf + (start & (log_buf_len - 1)), + end - start); + ((char *)__get_dynamic_array(msg))[end - start] = 0; + ), + + TP_printk("%s", __get_str(msg)) +); +#endif /* _TRACE_PRINTK_H */ + +/* This part must be outside protection */ +#include diff --git a/kernel/printk.c b/kernel/printk.c index 13c0a1143f49..cb8a6bd697c6 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -44,6 +44,9 @@ #include +#define CREATE_TRACE_POINTS +#include + /* * Architectures can override it: */ @@ -542,6 +545,8 @@ MODULE_PARM_DESC(ignore_loglevel, "ignore loglevel setting, to" static void _call_console_drivers(unsigned start, unsigned end, int msg_log_level) { + trace_console(&LOG_BUF(0), start, end, log_buf_len); + if ((msg_log_level < console_loglevel || ignore_loglevel) && console_drivers && start != end) { if ((start & LOG_BUF_MASK) > (end & LOG_BUF_MASK)) { -- cgit From 4296c70a5ec316903ef037ed15f154dd3d354ad7 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 6 Jan 2012 10:34:31 -0800 Subject: USB/xHCI: Enable USB 3.0 hub remote wakeup. USB 3.0 hubs have a different remote wakeup policy than USB 2.0 hubs. USB 2.0 hubs, once they have remote wakeup enabled, will always send remote wakes when anything changes on a port. However, USB 3.0 hubs have a per-port remote wake up policy that is off by default. The Set Feature remote wake mask can be changed for any port, enabling remote wakeup for a connect, disconnect, or overcurrent event, much like EHCI and xHCI host controller "wake on" port status bits. The bits are cleared to zero on the initial hub power on, or after the hub has been reset. Without this patch, when a USB 3.0 hub gets suspended, it will not send a remote wakeup on device connect or disconnect. This would show up to the user as "dead ports" unless they ran lsusb -v (since newer versions of lsusb use the sysfs files, rather than sending control transfers). Change the hub driver's suspend method to enable remote wake up for disconnect, connect, and overcurrent for all ports on the hub. Modify the xHCI driver's roothub code to handle that request, and set the "wake on" bits in the port status registers accordingly. Signed-off-by: Sarah Sharp --- drivers/usb/core/hub.c | 12 ++++++++++++ drivers/usb/host/xhci-hub.c | 41 +++++++++++++++++++++++++++++++++++++++++ include/linux/usb/ch11.h | 5 +++++ 3 files changed, 58 insertions(+) (limited to 'include') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 70622d633fda..b3137fa65f2a 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2731,6 +2731,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev = hub->hdev; unsigned port1; + int status; /* Warn if children aren't already suspended */ for (port1 = 1; port1 <= hdev->maxchild; port1++) { @@ -2743,6 +2744,17 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) return -EBUSY; } } + if (hub_is_superspeed(hdev) && hdev->do_remote_wakeup) { + /* Enable hub to send remote wakeup for all ports. */ + for (port1 = 1; port1 <= hdev->maxchild; port1++) { + status = set_port_feature(hdev, + port1 | + USB_PORT_FEAT_REMOTE_WAKE_CONNECT | + USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT | + USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT, + USB_PORT_FEAT_REMOTE_WAKE_MASK); + } + } dev_dbg(&intf->dev, "%s\n", __func__); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 557b6f32db86..673ad120c43e 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -422,6 +422,32 @@ void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array, xhci_writel(xhci, temp, port_array[port_id]); } +void xhci_set_remote_wake_mask(struct xhci_hcd *xhci, + __le32 __iomem **port_array, int port_id, u16 wake_mask) +{ + u32 temp; + + temp = xhci_readl(xhci, port_array[port_id]); + temp = xhci_port_state_to_neutral(temp); + + if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT) + temp |= PORT_WKCONN_E; + else + temp &= ~PORT_WKCONN_E; + + if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT) + temp |= PORT_WKDISC_E; + else + temp &= ~PORT_WKDISC_E; + + if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT) + temp |= PORT_WKOC_E; + else + temp &= ~PORT_WKOC_E; + + xhci_writel(xhci, temp, port_array[port_id]); +} + /* Test and clear port RWC bit */ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array, int port_id, u32 port_bit) @@ -448,6 +474,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, int slot_id; struct xhci_bus_state *bus_state; u16 link_state = 0; + u16 wake_mask = 0; max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; @@ -593,6 +620,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case SetPortFeature: if (wValue == USB_PORT_FEAT_LINK_STATE) link_state = (wIndex & 0xff00) >> 3; + if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK) + wake_mask = wIndex & 0xff00; wIndex &= 0xff; if (!wIndex || wIndex > max_ports) goto error; @@ -703,6 +732,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_readl(xhci, port_array[wIndex]); xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp); break; + case USB_PORT_FEAT_REMOTE_WAKE_MASK: + xhci_set_remote_wake_mask(xhci, port_array, + wIndex, wake_mask); + temp = xhci_readl(xhci, port_array[wIndex]); + xhci_dbg(xhci, "set port remote wake mask, " + "actual port %d status = 0x%x\n", + wIndex, temp); + break; case USB_PORT_FEAT_BH_PORT_RESET: temp |= PORT_WR; xhci_writel(xhci, temp, port_array[wIndex]); @@ -883,6 +920,10 @@ int xhci_bus_suspend(struct usb_hcd *hcd) t2 |= PORT_LINK_STROBE | XDEV_U3; set_bit(port_index, &bus_state->bus_suspended); } + /* USB core sets remote wake mask for USB 3.0 hubs, + * including the USB 3.0 roothub, but only if CONFIG_USB_SUSPEND + * is enabled, so also enable remote wake here. + */ if (hcd->self.root_hub->do_remote_wakeup) { if (t1 & PORT_CONNECT) { t2 |= PORT_WKOC_E | PORT_WKDISC_E; diff --git a/include/linux/usb/ch11.h b/include/linux/usb/ch11.h index 0b83acd3360a..f1d26b6067f1 100644 --- a/include/linux/usb/ch11.h +++ b/include/linux/usb/ch11.h @@ -76,6 +76,11 @@ #define USB_PORT_FEAT_C_BH_PORT_RESET 29 #define USB_PORT_FEAT_FORCE_LINKPM_ACCEPT 30 +/* USB 3.0 hub remote wake mask bits, see table 10-14 */ +#define USB_PORT_FEAT_REMOTE_WAKE_CONNECT (1 << 8) +#define USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT (1 << 9) +#define USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT (1 << 10) + /* * Hub Status and Hub Change results * See USB 2.0 spec Table 11-19 and Table 11-20 -- cgit From 4ee823b83bc9851743fab756c76b27d6a1e2472b Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 14 Nov 2011 18:00:01 -0800 Subject: USB/xHCI: Support device-initiated USB 3.0 resume. USB 3.0 hubs don't have a port suspend change bit (that bit is now reserved). Instead, when a host-initiated resume finishes, the hub sets the port link state change bit. When a USB 3.0 device initiates remote wakeup, the parent hubs with their upstream links in U3 will pass the LFPS up the chain. The first hub that has an upstream link in U0 (which may be the roothub) will reflect that LFPS back down the path to the device. However, the parent hubs in the resumed path will not set their link state change bit. Instead, the device that initiated the resume has to send an asynchronous "Function Wake" Device Notification up to the host controller. Therefore, we need a way to notify the USB core of a device resume without going through the normal hub URB completion method. First, make the xHCI roothub act like an external USB 3.0 hub and not pass up the port link state change bit when a device-initiated resume finishes. Introduce a new xHCI bit field, port_remote_wakeup, so that we can tell the difference between a port coming out of the U3Exit state (host-initiated resume) and the RExit state (ending state of device-initiated resume). Since the USB core can't tell whether a port on a hub has resumed by looking at the Hub Status buffer, we need to introduce a bitfield, wakeup_bits, that indicates which ports have resumed. When the xHCI driver notices a port finishing a device-initiated resume, we call into a new USB core function, usb_wakeup_notification(), that will set the right bit in wakeup_bits, and kick khubd for that hub. We also call usb_wakeup_notification() when the Function Wake Device Notification is received by the xHCI driver. This covers the case where the link between the roothub and the first-tier hub is in U0, and the hub reflects the resume signaling back to the device without giving any indication it has done so until the device sends the Function Wake notification. Change the code in khubd that handles the remote wakeup to look at the state the USB core thinks the device is in, and handle the remote wakeup if the port's wakeup bit is set. This patch only takes care of the case where the device is attached directly to the roothub, or the USB 3.0 hub that is attached to the root hub is the device sending the Function Wake Device Notification (e.g. because a new USB device was attached). The other cases will be covered in a second patch. Signed-off-by: Sarah Sharp --- drivers/usb/core/hub.c | 51 +++++++++++++++++++++++++++++++++----------- drivers/usb/host/xhci-ring.c | 40 +++++++++++++++++++++++++++------- drivers/usb/host/xhci.h | 1 + include/linux/usb/hcd.h | 2 ++ 4 files changed, 74 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index ba9509454ed5..1c32bbac9862 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -62,6 +62,8 @@ struct usb_hub { resumed */ unsigned long removed_bits[1]; /* ports with a "removed" device present */ + unsigned long wakeup_bits[1]; /* ports that have signaled + remote wakeup */ #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ #error event_bits[] is too short! #endif @@ -411,6 +413,29 @@ void usb_kick_khubd(struct usb_device *hdev) kick_khubd(hub); } +/* + * Let the USB core know that a USB 3.0 device has sent a Function Wake Device + * Notification, which indicates it had initiated remote wakeup. + * + * USB 3.0 hubs do not report the port link state change from U3 to U0 when the + * device initiates resume, so the USB core will not receive notice of the + * resume through the normal hub interrupt URB. + */ +void usb_wakeup_notification(struct usb_device *hdev, + unsigned int portnum) +{ + struct usb_hub *hub; + + if (!hdev) + return; + + hub = hdev_to_hub(hdev); + if (hub) { + set_bit(portnum, hub->wakeup_bits); + kick_khubd(hub); + } +} +EXPORT_SYMBOL_GPL(usb_wakeup_notification); /* completion function, fires on port status changes and various faults */ static void hub_irq(struct urb *urb) @@ -807,12 +832,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_ENABLE); } - if (portchange & USB_PORT_STAT_C_LINK_STATE) { - need_debounce_delay = true; - clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_PORT_LINK_STATE); - } - if ((portchange & USB_PORT_STAT_C_BH_RESET) && hub_is_superspeed(hub->hdev)) { need_debounce_delay = true; @@ -3498,11 +3517,18 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, int ret; hdev = hub->hdev; - if (!(portchange & USB_PORT_STAT_C_SUSPEND)) - return 0; - clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND); - udev = hdev->children[port-1]; + if (!hub_is_superspeed(hdev)) { + if (!(portchange & USB_PORT_STAT_C_SUSPEND)) + return 0; + clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND); + } else { + if (!udev || udev->state != USB_STATE_SUSPENDED || + !test_and_clear_bit(udev->portnum, + hub->wakeup_bits)) + return 0; + } + if (udev) { /* TRSMRCY = 10 msec */ msleep(10); @@ -3533,7 +3559,7 @@ static void hub_events(void) u16 portstatus; u16 portchange; int i, ret; - int connect_change; + int connect_change, wakeup_change; /* * We restart the list every time to avoid a deadlock with @@ -3612,8 +3638,9 @@ static void hub_events(void) if (test_bit(i, hub->busy_bits)) continue; connect_change = test_bit(i, hub->change_bits); + wakeup_change = test_bit(i, hub->wakeup_bits); if (!test_and_clear_bit(i, hub->event_bits) && - !connect_change) + !connect_change && !wakeup_change) continue; ret = hub_port_status(hub, i, diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index ffe549338cec..3a033240ec64 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1241,18 +1241,20 @@ static void handle_device_notification(struct xhci_hcd *xhci, union xhci_trb *event) { u32 slot_id; + struct usb_device *udev; slot_id = TRB_TO_SLOT_ID(event->generic.field[3]); - if (!xhci->devs[slot_id]) + if (!xhci->devs[slot_id]) { xhci_warn(xhci, "Device Notification event for " "unused slot %u\n", slot_id); - else - xhci_dbg(xhci, "Device Notification event for slot ID %u\n", - slot_id); - /* XXX should we kick khubd for the parent hub? It should have send an - * interrupt transfer when the port started signaling resume, so there's - * probably no need to do so. - */ + return; + } + + xhci_dbg(xhci, "Device Wake Notification event for slot ID %u\n", + slot_id); + udev = xhci->devs[slot_id]->udev; + if (udev && udev->parent) + usb_wakeup_notification(udev->parent, udev->portnum); } static void handle_port_status(struct xhci_hcd *xhci, @@ -1340,6 +1342,11 @@ static void handle_port_status(struct xhci_hcd *xhci, if (DEV_SUPERSPEED(temp)) { xhci_dbg(xhci, "remote wake SS port %d\n", port_id); + /* Set a flag to say the port signaled remote wakeup, + * so we can tell the difference between the end of + * device and host initiated resume. + */ + bus_state->port_remote_wakeup |= 1 << faked_port_index; xhci_test_and_clear_bit(xhci, port_array, faked_port_index, PORT_PLC); xhci_set_link_state(xhci, port_array, faked_port_index, @@ -1362,10 +1369,27 @@ static void handle_port_status(struct xhci_hcd *xhci, if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_U0 && DEV_SUPERSPEED(temp)) { xhci_dbg(xhci, "resume SS port %d finished\n", port_id); + /* We've just brought the device into U0 through either the + * Resume state after a device remote wakeup, or through the + * U3Exit state after a host-initiated resume. If it's a device + * initiated remote wake, don't pass up the link state change, + * so the roothub behavior is consistent with external + * USB 3.0 hub behavior. + */ slot_id = xhci_find_slot_id_by_port(hcd, xhci, faked_port_index + 1); if (slot_id && xhci->devs[slot_id]) xhci_ring_device(xhci, slot_id); + if (bus_state->port_remote_wakeup && (1 << faked_port_index)) { + bus_state->port_remote_wakeup &= + ~(1 << faked_port_index); + xhci_test_and_clear_bit(xhci, port_array, + faked_port_index, PORT_PLC); + usb_wakeup_notification(hcd->self.root_hub, + faked_port_index + 1); + bogus_port_status = true; + goto cleanup; + } } if (hcd->speed != HCD_USB3) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index fb99c8379142..0f4936956103 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1344,6 +1344,7 @@ struct xhci_bus_state { /* ports suspend status arrays - max 31 ports for USB2, 15 for USB3 */ u32 port_c_suspend; u32 suspended_ports; + u32 port_remote_wakeup; unsigned long resume_done[USB_MAXCHILDREN]; }; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index b2f62f3a32af..2e6071efbfb7 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -412,6 +412,8 @@ extern irqreturn_t usb_hcd_irq(int irq, void *__hcd); extern void usb_hc_died(struct usb_hcd *hcd); extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd); +extern void usb_wakeup_notification(struct usb_device *hdev, + unsigned int portnum); /* The D0/D1 toggle bits ... USE WITH CAUTION (they're almost hcd-internal) */ #define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1) -- cgit From 0d4e1b2a7eb7f4db8b3dc1c7ef4b507040f87ded Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Thu, 2 Feb 2012 22:00:48 +0530 Subject: usb: uac2: Add ACHeader and FormatType descriptor Add missing, but needed, ACHeader and FormatType descriptor definitions. Signed-off-by: Yadi Brar Signed-off-by: Jassi Brar Signed-off-by: Felipe Balbi --- include/linux/usb/audio-v2.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'include') diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index 964cb603f7c7..ed13053153f4 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h @@ -43,6 +43,27 @@ static inline bool uac2_control_is_writeable(u32 bmControls, u8 control) return (bmControls >> (control * 2)) & 0x2; } +/* 4.7.2 Class-Specific AC Interface Descriptor */ +struct uac2_ac_header_descriptor { + __u8 bLength; /* 9 */ + __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */ + __u8 bDescriptorSubtype; /* UAC_MS_HEADER */ + __le16 bcdADC; /* 0x0200 */ + __u8 bCategory; + __le16 wTotalLength; /* includes Unit and Terminal desc. */ + __u8 bmControls; +} __packed; + +/* 2.3.1.6 Type I Format Type Descriptor (Frmts20 final.pdf)*/ +struct uac2_format_type_i_descriptor { + __u8 bLength; /* in bytes: 6 */ + __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */ + __u8 bDescriptorSubtype; /* FORMAT_TYPE */ + __u8 bFormatType; /* FORMAT_TYPE_1 */ + __u8 bSubslotSize; /* {1,2,3,4} */ + __u8 bBitResolution; +} __packed; + /* 4.7.2.1 Clock Source Descriptor */ struct uac_clock_source_descriptor { -- cgit From d705ae6b133f9f6a8beee617b1224b6a5c99c5da Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 15 Feb 2012 09:45:49 +0100 Subject: block: replace icq->changed with icq->flags icq->changed was used for ICQ_*_CHANGED bits. Rename it to flags and access it under ioc->lock instead of using atomic bitops. ioc_get_changed() is added so that the changed part can be fetched and cleared as before. icq->flags will be used to carry other flags. Signed-off-by: Tejun Heo Tested-by: Shaohua Li Signed-off-by: Jens Axboe --- block/blk-ioc.c | 30 ++++++++++++++++++++++++++---- block/cfq-iosched.c | 12 ++++++------ include/linux/iocontext.h | 9 ++++++--- 3 files changed, 38 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/block/blk-ioc.c b/block/blk-ioc.c index 8b782a63c297..811879c752e4 100644 --- a/block/blk-ioc.c +++ b/block/blk-ioc.c @@ -363,13 +363,13 @@ struct io_cq *ioc_create_icq(struct request_queue *q, gfp_t gfp_mask) return icq; } -void ioc_set_changed(struct io_context *ioc, int which) +void ioc_set_icq_flags(struct io_context *ioc, unsigned int flags) { struct io_cq *icq; struct hlist_node *n; hlist_for_each_entry(icq, n, &ioc->icq_list, ioc_node) - set_bit(which, &icq->changed); + icq->flags |= flags; } /** @@ -387,7 +387,7 @@ void ioc_ioprio_changed(struct io_context *ioc, int ioprio) spin_lock_irqsave(&ioc->lock, flags); ioc->ioprio = ioprio; - ioc_set_changed(ioc, ICQ_IOPRIO_CHANGED); + ioc_set_icq_flags(ioc, ICQ_IOPRIO_CHANGED); spin_unlock_irqrestore(&ioc->lock, flags); } @@ -404,11 +404,33 @@ void ioc_cgroup_changed(struct io_context *ioc) unsigned long flags; spin_lock_irqsave(&ioc->lock, flags); - ioc_set_changed(ioc, ICQ_CGROUP_CHANGED); + ioc_set_icq_flags(ioc, ICQ_CGROUP_CHANGED); spin_unlock_irqrestore(&ioc->lock, flags); } EXPORT_SYMBOL(ioc_cgroup_changed); +/** + * icq_get_changed - fetch and clear icq changed mask + * @icq: icq of interest + * + * Fetch and clear ICQ_*_CHANGED bits from @icq. Grabs and releases + * @icq->ioc->lock. + */ +unsigned icq_get_changed(struct io_cq *icq) +{ + unsigned int changed = 0; + unsigned long flags; + + if (unlikely(icq->flags & ICQ_CHANGED_MASK)) { + spin_lock_irqsave(&icq->ioc->lock, flags); + changed = icq->flags & ICQ_CHANGED_MASK; + icq->flags &= ~ICQ_CHANGED_MASK; + spin_unlock_irqrestore(&icq->ioc->lock, flags); + } + return changed; +} +EXPORT_SYMBOL(icq_get_changed); + static int __init blk_ioc_init(void) { iocontext_cachep = kmem_cache_create("blkdev_ioc", diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index d0ba50533668..457295253566 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -3470,20 +3470,20 @@ cfq_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask) const int rw = rq_data_dir(rq); const bool is_sync = rq_is_sync(rq); struct cfq_queue *cfqq; + unsigned int changed; might_sleep_if(gfp_mask & __GFP_WAIT); spin_lock_irq(q->queue_lock); /* handle changed notifications */ - if (unlikely(cic->icq.changed)) { - if (test_and_clear_bit(ICQ_IOPRIO_CHANGED, &cic->icq.changed)) - changed_ioprio(cic); + changed = icq_get_changed(&cic->icq); + if (unlikely(changed & ICQ_IOPRIO_CHANGED)) + changed_ioprio(cic); #ifdef CONFIG_CFQ_GROUP_IOSCHED - if (test_and_clear_bit(ICQ_CGROUP_CHANGED, &cic->icq.changed)) - changed_cgroup(cic); + if (unlikely(changed & ICQ_CGROUP_CHANGED)) + changed_cgroup(cic); #endif - } new_queue: cfqq = cic_to_cfqq(cic, is_sync); diff --git a/include/linux/iocontext.h b/include/linux/iocontext.h index 119773eebe31..17839c7b9614 100644 --- a/include/linux/iocontext.h +++ b/include/linux/iocontext.h @@ -6,8 +6,10 @@ #include enum { - ICQ_IOPRIO_CHANGED, - ICQ_CGROUP_CHANGED, + ICQ_IOPRIO_CHANGED = 1 << 0, + ICQ_CGROUP_CHANGED = 1 << 1, + + ICQ_CHANGED_MASK = ICQ_IOPRIO_CHANGED | ICQ_CGROUP_CHANGED, }; /* @@ -88,7 +90,7 @@ struct io_cq { struct rcu_head __rcu_head; }; - unsigned long changed; + unsigned int flags; }; /* @@ -139,6 +141,7 @@ struct io_context *get_task_io_context(struct task_struct *task, gfp_t gfp_flags, int node); void ioc_ioprio_changed(struct io_context *ioc, int ioprio); void ioc_cgroup_changed(struct io_context *ioc); +unsigned int icq_get_changed(struct io_cq *icq); #else struct io_context; static inline void put_io_context(struct io_context *ioc) { } -- cgit From 621032ad6eaabf2fe771c4fa0d8f58e1fcfcdba6 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 15 Feb 2012 09:45:53 +0100 Subject: block: exit_io_context() should call elevator_exit_icq_fn() While updating locking, b2efa05265 "block, cfq: unlink cfq_io_context's immediately" moved elevator_exit_icq_fn() invocation from exit_io_context() to the final ioc put. While this doesn't cause catastrophic failure, it effectively removes task exit notification to elevator and cause noticeable IO performance degradation with CFQ. On task exit, CFQ used to immediately expire the slice if it was being used by the exiting task as no more IO would be issued by the task; however, after b2efa05265, the notification is lost and disk could sit idle needlessly, leading to noticeable IO performance degradation for certain workloads. This patch renames ioc_exit_icq() to ioc_destroy_icq(), separates elevator_exit_icq_fn() invocation into ioc_exit_icq() and invokes it from exit_io_context(). ICQ_EXITED flag is added to avoid invoking the callback more than once for the same icq. Walking icq_list from ioc side and invoking elevator callback requires reverse double locking. This may be better implemented using RCU; unfortunately, using RCU isn't trivial. e.g. RCU protection would need to cover request_queue and queue_lock switch on cleanup makes grabbing queue_lock from RCU unsafe. Reverse double locking should do, at least for now. Signed-off-by: Tejun Heo Reported-and-bisected-by: Shaohua Li LKML-Reference: Tested-by: Shaohua Li Signed-off-by: Jens Axboe --- block/blk-ioc.c | 55 ++++++++++++++++++++++++++++++++++++++++------- include/linux/iocontext.h | 1 + 2 files changed, 48 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/block/blk-ioc.c b/block/blk-ioc.c index f53c80ecaf07..92bf55540d87 100644 --- a/block/blk-ioc.c +++ b/block/blk-ioc.c @@ -36,10 +36,22 @@ static void icq_free_icq_rcu(struct rcu_head *head) kmem_cache_free(icq->__rcu_icq_cache, icq); } -/* - * Exit and free an icq. Called with both ioc and q locked. - */ +/* Exit an icq. Called with both ioc and q locked. */ static void ioc_exit_icq(struct io_cq *icq) +{ + struct elevator_type *et = icq->q->elevator->type; + + if (icq->flags & ICQ_EXITED) + return; + + if (et->ops.elevator_exit_icq_fn) + et->ops.elevator_exit_icq_fn(icq); + + icq->flags |= ICQ_EXITED; +} + +/* Release an icq. Called with both ioc and q locked. */ +static void ioc_destroy_icq(struct io_cq *icq) { struct io_context *ioc = icq->ioc; struct request_queue *q = icq->q; @@ -60,8 +72,7 @@ static void ioc_exit_icq(struct io_cq *icq) if (rcu_dereference_raw(ioc->icq_hint) == icq) rcu_assign_pointer(ioc->icq_hint, NULL); - if (et->ops.elevator_exit_icq_fn) - et->ops.elevator_exit_icq_fn(icq); + ioc_exit_icq(icq); /* * @icq->q might have gone away by the time RCU callback runs @@ -95,7 +106,7 @@ static void ioc_release_fn(struct work_struct *work) struct request_queue *q = icq->q; if (spin_trylock(q->queue_lock)) { - ioc_exit_icq(icq); + ioc_destroy_icq(icq); spin_unlock(q->queue_lock); } else { spin_unlock_irqrestore(&ioc->lock, flags); @@ -142,13 +153,41 @@ EXPORT_SYMBOL(put_io_context); void exit_io_context(struct task_struct *task) { struct io_context *ioc; + struct io_cq *icq; + struct hlist_node *n; + unsigned long flags; task_lock(task); ioc = task->io_context; task->io_context = NULL; task_unlock(task); - atomic_dec(&ioc->nr_tasks); + if (!atomic_dec_and_test(&ioc->nr_tasks)) { + put_io_context(ioc); + return; + } + + /* + * Need ioc lock to walk icq_list and q lock to exit icq. Perform + * reverse double locking. Read comment in ioc_release_fn() for + * explanation on the nested locking annotation. + */ +retry: + spin_lock_irqsave_nested(&ioc->lock, flags, 1); + hlist_for_each_entry(icq, n, &ioc->icq_list, ioc_node) { + if (icq->flags & ICQ_EXITED) + continue; + if (spin_trylock(icq->q->queue_lock)) { + ioc_exit_icq(icq); + spin_unlock(icq->q->queue_lock); + } else { + spin_unlock_irqrestore(&ioc->lock, flags); + cpu_relax(); + goto retry; + } + } + spin_unlock_irqrestore(&ioc->lock, flags); + put_io_context(ioc); } @@ -168,7 +207,7 @@ void ioc_clear_queue(struct request_queue *q) struct io_context *ioc = icq->ioc; spin_lock(&ioc->lock); - ioc_exit_icq(icq); + ioc_destroy_icq(icq); spin_unlock(&ioc->lock); } } diff --git a/include/linux/iocontext.h b/include/linux/iocontext.h index 17839c7b9614..1a3018063034 100644 --- a/include/linux/iocontext.h +++ b/include/linux/iocontext.h @@ -8,6 +8,7 @@ enum { ICQ_IOPRIO_CHANGED = 1 << 0, ICQ_CGROUP_CHANGED = 1 << 1, + ICQ_EXITED = 1 << 2, ICQ_CHANGED_MASK = ICQ_IOPRIO_CHANGED | ICQ_CGROUP_CHANGED, }; -- cgit From e70bb2e89959983aebcfce28f645a1104ffa9ab2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 13 Feb 2012 16:59:33 +0200 Subject: Bluetooth: Implement Read Supported Commands commands for mgmt This patch implements the Read Supported Commands mgmt command which was recently added to the API specification. It returns a list of supported commands and events to user space. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 7 ++++ net/bluetooth/mgmt.c | 99 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 5b5edeed59e2..255a99600f08 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -63,6 +63,13 @@ struct mgmt_rp_read_version { __le16 revision; } __packed; +#define MGMT_OP_READ_COMMANDS 0x0002 +struct mgmt_rp_read_commands { + __le16 num_commands; + __le16 num_events; + __le16 opcodes[0]; +} __packed; + #define MGMT_OP_READ_INDEX_LIST 0x0003 struct mgmt_rp_read_index_list { __le16 num_controllers; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a2c2e12516c6..8efbd8eaa1b3 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -35,6 +35,69 @@ #define MGMT_VERSION 0 #define MGMT_REVISION 1 +static const u16 mgmt_commands[] = { + MGMT_OP_READ_INDEX_LIST, + MGMT_OP_READ_INFO, + MGMT_OP_SET_POWERED, + MGMT_OP_SET_DISCOVERABLE, + MGMT_OP_SET_CONNECTABLE, + MGMT_OP_SET_FAST_CONNECTABLE, + MGMT_OP_SET_PAIRABLE, + MGMT_OP_SET_LINK_SECURITY, + MGMT_OP_SET_SSP, + MGMT_OP_SET_HS, + MGMT_OP_SET_LE, + MGMT_OP_SET_DEV_CLASS, + MGMT_OP_SET_LOCAL_NAME, + MGMT_OP_ADD_UUID, + MGMT_OP_REMOVE_UUID, + MGMT_OP_LOAD_LINK_KEYS, + MGMT_OP_LOAD_LONG_TERM_KEYS, + MGMT_OP_DISCONNECT, + MGMT_OP_GET_CONNECTIONS, + MGMT_OP_PIN_CODE_REPLY, + MGMT_OP_PIN_CODE_NEG_REPLY, + MGMT_OP_SET_IO_CAPABILITY, + MGMT_OP_PAIR_DEVICE, + MGMT_OP_CANCEL_PAIR_DEVICE, + MGMT_OP_UNPAIR_DEVICE, + MGMT_OP_USER_CONFIRM_REPLY, + MGMT_OP_USER_CONFIRM_NEG_REPLY, + MGMT_OP_USER_PASSKEY_REPLY, + MGMT_OP_USER_PASSKEY_NEG_REPLY, + MGMT_OP_READ_LOCAL_OOB_DATA, + MGMT_OP_ADD_REMOTE_OOB_DATA, + MGMT_OP_REMOVE_REMOTE_OOB_DATA, + MGMT_OP_START_DISCOVERY, + MGMT_OP_STOP_DISCOVERY, + MGMT_OP_CONFIRM_NAME, + MGMT_OP_BLOCK_DEVICE, + MGMT_OP_UNBLOCK_DEVICE, +}; + +static const u16 mgmt_events[] = { + MGMT_EV_CONTROLLER_ERROR, + MGMT_EV_INDEX_ADDED, + MGMT_EV_INDEX_REMOVED, + MGMT_EV_NEW_SETTINGS, + MGMT_EV_CLASS_OF_DEV_CHANGED, + MGMT_EV_LOCAL_NAME_CHANGED, + MGMT_EV_NEW_LINK_KEY, + MGMT_EV_NEW_LONG_TERM_KEY, + MGMT_EV_DEVICE_CONNECTED, + MGMT_EV_DEVICE_DISCONNECTED, + MGMT_EV_CONNECT_FAILED, + MGMT_EV_PIN_CODE_REQUEST, + MGMT_EV_USER_CONFIRM_REQUEST, + MGMT_EV_USER_PASSKEY_REQUEST, + MGMT_EV_AUTH_FAILED, + MGMT_EV_DEVICE_FOUND, + MGMT_EV_DISCOVERING, + MGMT_EV_DEVICE_BLOCKED, + MGMT_EV_DEVICE_UNBLOCKED, + MGMT_EV_DEVICE_UNPAIRED, +}; + /* * These LE scan and inquiry parameters were chosen according to LE General * Discovery Procedure specification. @@ -206,6 +269,39 @@ static int read_version(struct sock *sk) sizeof(rp)); } +static int read_commands(struct sock *sk) +{ + struct mgmt_rp_read_commands *rp; + u16 num_commands = ARRAY_SIZE(mgmt_commands); + u16 num_events = ARRAY_SIZE(mgmt_events); + u16 *opcode; + size_t rp_size; + int i, err; + + BT_DBG("sock %p", sk); + + rp_size = sizeof(*rp) + ((num_commands + num_events) * sizeof(u16)); + + rp = kmalloc(rp_size, GFP_KERNEL); + if (!rp) + return -ENOMEM; + + put_unaligned_le16(num_commands, &rp->num_commands); + put_unaligned_le16(num_events, &rp->num_events); + + for (i = 0, opcode = rp->opcodes; i < num_commands; i++, opcode++) + put_unaligned_le16(mgmt_commands[i], opcode); + + for (i = 0; i < num_events; i++, opcode++) + put_unaligned_le16(mgmt_events[i], opcode); + + err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_COMMANDS, rp, + rp_size); + kfree(rp); + + return err; +} + static int read_index_list(struct sock *sk) { struct mgmt_rp_read_index_list *rp; @@ -2323,6 +2419,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_READ_VERSION: err = read_version(sk); break; + case MGMT_OP_READ_COMMANDS: + err = read_commands(sk); + break; case MGMT_OP_READ_INDEX_LIST: err = read_index_list(sk); break; -- cgit From 4c5e40366a8879a3fda6a22a4b65c02ef20a08c4 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 15 Feb 2012 16:37:44 -0500 Subject: decnet: net/dn.h needs net/flow.h Reported-by: Randy Dunlap Signed-off-by: David S. Miller --- include/net/dn.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/net/dn.h b/include/net/dn.h index 298521e0d8a2..814af0b9387d 100644 --- a/include/net/dn.h +++ b/include/net/dn.h @@ -3,6 +3,7 @@ #include #include +#include #include #include -- cgit From 80703d265b7e8a801560d907b1bfe340e574dbca Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 15 Feb 2012 17:48:35 -0500 Subject: ipv4: Eliminate spurious argument to __ipv4_neigh_lookup 'tbl' is always arp_tbl, so specifying it is pointless. Signed-off-by: David S. Miller --- include/net/arp.h | 4 ++-- net/ipv4/route.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/arp.h b/include/net/arp.h index 0013dc87940b..4a1f3fb562eb 100644 --- a/include/net/arp.h +++ b/include/net/arp.h @@ -15,14 +15,14 @@ static inline u32 arp_hashfn(u32 key, const struct net_device *dev, u32 hash_rnd return val * hash_rnd; } -static inline struct neighbour *__ipv4_neigh_lookup(struct neigh_table *tbl, struct net_device *dev, u32 key) +static inline struct neighbour *__ipv4_neigh_lookup(struct net_device *dev, u32 key) { struct neigh_hash_table *nht; struct neighbour *n; u32 hash_val; rcu_read_lock_bh(); - nht = rcu_dereference_bh(tbl->nht); + nht = rcu_dereference_bh(arp_tbl.nht); hash_val = arp_hashfn(key, dev, nht->hash_rnd[0]) >> (32 - nht->hash_shift); for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); n != NULL; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 4eeb8ce856e2..0489cedc1671 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1127,7 +1127,7 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, const vo else if (rt->rt_gateway) pkey = (const __be32 *) &rt->rt_gateway; - n = __ipv4_neigh_lookup(&arp_tbl, dev, *(__force u32 *)pkey); + n = __ipv4_neigh_lookup(dev, *(__force u32 *)pkey); if (n) return n; return neigh_create(&arp_tbl, pkey, dev); -- cgit From 150647fb2c313d7c5184fca3fa0829a4a7d6f7bc Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 15 Feb 2012 17:54:56 +0000 Subject: net: sh_eth: change the condition of initialization The SH7757 has 2 Fast Ethernet and 2 Gigabit Ethernet, and the first Gigabit channel needs the initialization. So, this patch adds the parameter of "needs_init", and if the sh_eth_plat_data is set it to 1, the driver will initialize the channel. Signed-off-by: Yoshihiro Shimoda Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 4 ++-- include/linux/sh_eth.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 8f53b5ac7f05..5a5afbc7273b 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1859,8 +1859,8 @@ static int sh_eth_drv_probe(struct platform_device *pdev) /* read and set MAC address */ read_mac_address(ndev, pd->mac_addr); - /* First device only init */ - if (!devno) { + /* initialize first or needed device */ + if (!devno || pd->needs_init) { if (mdp->cd->tsu) { struct resource *rtsu; rtsu = platform_get_resource(pdev, IORESOURCE_MEM, 1); diff --git a/include/linux/sh_eth.h b/include/linux/sh_eth.h index 2076acf8294d..b17d765ded84 100644 --- a/include/linux/sh_eth.h +++ b/include/linux/sh_eth.h @@ -20,6 +20,7 @@ struct sh_eth_plat_data { unsigned char mac_addr[6]; unsigned no_ether_link:1; unsigned ether_link_active_low:1; + unsigned needs_init:1; }; #endif -- cgit From 33ef95ed30283eb17c686a815caf1d33e966fe4a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Feb 2012 23:56:27 +0200 Subject: Bluetooth: mgmt: Add support for Set Link Security command The Set Link Security mgmt command is used to enable or disable link level security, also known as Security Mode 3. This is rarely enabled in modern systems but the command needs to be available for completeness, qualification purposes and those few systems that actually want to enable it. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 3 ++ net/bluetooth/mgmt.c | 87 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b20d990436b4..66f84adbbbef 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -994,6 +994,7 @@ int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); +int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status); int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, u8 *randomizer, u8 status); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b0784ee5f8b9..239e9fb8f7c5 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -254,6 +254,9 @@ static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_AUTH, &hdev->flags); } + if (test_bit(HCI_MGMT, &hdev->dev_flags)) + mgmt_auth_enable_complete(hdev, status); + hci_req_complete(hdev, HCI_OP_WRITE_AUTH_ENABLE, status); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d5dbe402bc03..0c9fbb45d2e9 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -964,6 +964,65 @@ failed: return err; } +static int set_link_security(struct sock *sk, u16 index, void *data, u16 len) +{ + struct mgmt_mode *cp = data; + struct pending_cmd *cmd; + struct hci_dev *hdev; + uint8_t val; + int err; + + BT_DBG("request for hci%u", index); + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY, + MGMT_STATUS_INVALID_PARAMS); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + if (!test_bit(HCI_UP, &hdev->flags)) { + err = cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY, + MGMT_STATUS_NOT_POWERED); + goto failed; + } + + if (mgmt_pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) { + err = cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY, + MGMT_STATUS_BUSY); + goto failed; + } + + val = !!cp->val; + + if (test_bit(HCI_AUTH, &hdev->flags) == val) { + err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev); + goto failed; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_SET_LINK_SECURITY, hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + + err = hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(val), &val); + if (err < 0) { + mgmt_pending_remove(cmd); + goto failed; + } + +failed: + hci_dev_unlock(hdev); + hci_dev_put(hdev); + + return err; +} + static int add_uuid(struct sock *sk, u16 index, void *data, u16 len) { struct mgmt_cp_add_uuid *cp = data; @@ -2443,6 +2502,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_SET_PAIRABLE: err = set_pairable(sk, index, cp, len); break; + case MGMT_OP_SET_LINK_SECURITY: + err = set_link_security(sk, index, cp, len); + break; case MGMT_OP_ADD_UUID: err = add_uuid(sk, index, cp, len); break; @@ -2965,6 +3027,31 @@ int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL); } +int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status) +{ + struct cmd_lookup match = { NULL, hdev }; + __le32 ev; + int err; + + if (status) { + u8 mgmt_err = mgmt_status(status); + mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, + cmd_status_rsp, &mgmt_err); + return 0; + } + + mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp, + &match); + + ev = cpu_to_le32(get_current_settings(hdev)); + err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk); + + if (match.sk) + sock_put(match.sk); + + return err; +} + int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) { struct pending_cmd *cmd; -- cgit From ed2c4ee360709ca838efa0ea4d6295590aff3d24 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 17 Feb 2012 00:56:28 +0200 Subject: Bluetooth: mgmt: Add support for Set SSP command The Set SSP mgmt command can be used for enabling and disabling Secure Simple Pairing support for controllers that support it. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 6 ++- net/bluetooth/mgmt.c | 85 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 66f84adbbbef..43e0b1eda020 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -995,6 +995,7 @@ int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status); +int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 status); int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, u8 *randomizer, u8 status); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 239e9fb8f7c5..179d127601fc 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -447,7 +447,7 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%x", hdev->name, status); if (status) - return; + goto done; sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SSP_MODE); if (!sent) @@ -457,6 +457,10 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) set_bit(HCI_SSP_ENABLED, &hdev->dev_flags); else clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); + +done: + if (test_bit(HCI_MGMT, &hdev->dev_flags)) + mgmt_ssp_enable_complete(hdev, status); } static u8 hci_get_inquiry_mode(struct hci_dev *hdev) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0c9fbb45d2e9..36c4ff6fdf05 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1023,6 +1023,64 @@ failed: return err; } +static int set_ssp(struct sock *sk, u16 index, void *data, u16 len) +{ + struct mgmt_mode *cp = data; + struct pending_cmd *cmd; + struct hci_dev *hdev; + uint8_t val; + int err; + + BT_DBG("request for hci%u", index); + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_SET_SSP, + MGMT_STATUS_INVALID_PARAMS); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_SET_SSP, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + if (!test_bit(HCI_UP, &hdev->flags)) { + err = cmd_status(sk, index, MGMT_OP_SET_SSP, + MGMT_STATUS_NOT_POWERED); + goto failed; + } + + if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) { + err = cmd_status(sk, index, MGMT_OP_SET_SSP, MGMT_STATUS_BUSY); + goto failed; + } + + val = !!cp->val; + + if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) == val) { + err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev); + goto failed; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_SET_SSP, hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + + err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(val), &val); + if (err < 0) { + mgmt_pending_remove(cmd); + goto failed; + } + +failed: + hci_dev_unlock(hdev); + hci_dev_put(hdev); + + return err; +} + static int add_uuid(struct sock *sk, u16 index, void *data, u16 len) { struct mgmt_cp_add_uuid *cp = data; @@ -2505,6 +2563,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_SET_LINK_SECURITY: err = set_link_security(sk, index, cp, len); break; + case MGMT_OP_SET_SSP: + err = set_ssp(sk, index, cp, len); + break; case MGMT_OP_ADD_UUID: err = add_uuid(sk, index, cp, len); break; @@ -3052,6 +3113,30 @@ int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status) return err; } +int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 status) +{ + struct cmd_lookup match = { NULL, hdev }; + __le32 ev; + int err; + + if (status) { + u8 mgmt_err = mgmt_status(status); + mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, + cmd_status_rsp, &mgmt_err); + return 0; + } + + mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match); + + ev = cpu_to_le32(get_current_settings(hdev)); + err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk); + + if (match.sk) + sock_put(match.sk); + + return err; +} + int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) { struct pending_cmd *cmd; -- cgit From 3ed7003e724a04482e0ef1e794eece8c1c177b37 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 16 Feb 2012 23:32:42 -0800 Subject: Bluetooth: Add logging functions bt_info and bt_err Use specific logging functions instead of a generic bt_printk function can save some text. Remove now unused bt_printk function. Add compatibility BT_INFO and BT_ERR macros. (compiled x86 and defconfig with bluetooth and all bluetooth drivers) $ size net/bluetooth/built-in.o* text data bss dec hex filename 381662 20072 100416 502150 7a986 net/bluetooth/built-in.o.allyesconfig.new 382463 20072 100400 502935 7ac97 net/bluetooth/built-in.o.allyesconfig.old 126635 1388 132 128155 1f49b net/bluetooth/built-in.o.defconfig.new 127175 1388 132 128695 1f6b7 net/bluetooth/built-in.o.defconfig.old $ size drivers/bluetooth/built-in.o* 127575 8976 29476 166027 2888b drivers/bluetooth/built-in.o.allyesconfig.new 129512 8976 29516 168004 29044 drivers/bluetooth/built-in.o.allyesconfig.old 52998 3292 156 56446 dc7e drivers/bluetooth/built-in.o.defconfig.new 54358 3292 156 57806 e1ce drivers/bluetooth/built-in.o.defconfig.old Signed-off-by: Joe Perches Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/bluetooth.h | 14 ++++++++------ net/bluetooth/lib.c | 27 ++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 4a82ca0bb0b2..9f5f2c1c5554 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -109,12 +109,14 @@ struct bt_power { */ #define BT_CHANNEL_POLICY_AMP_PREFERRED 2 -__printf(2, 3) -int bt_printk(const char *level, const char *fmt, ...); - -#define BT_INFO(fmt, arg...) bt_printk(KERN_INFO, pr_fmt(fmt), ##arg) -#define BT_ERR(fmt, arg...) bt_printk(KERN_ERR, pr_fmt(fmt), ##arg) -#define BT_DBG(fmt, arg...) pr_debug(fmt "\n", ##arg) +__printf(1, 2) +int bt_info(const char *fmt, ...); +__printf(1, 2) +int bt_err(const char *fmt, ...); + +#define BT_INFO(fmt, ...) bt_info(fmt "\n", ##__VA_ARGS__) +#define BT_ERR(fmt, ...) bt_err(fmt "\n", ##__VA_ARGS__) +#define BT_DBG(fmt, ...) pr_debug(fmt "\n", ##__VA_ARGS__) /* Connection and socket states */ enum { diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index 86a6bed229df..506628876f36 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -24,6 +24,8 @@ /* Bluetooth kernel library. */ +#define pr_fmt(fmt) "Bluetooth: " fmt + #include #include @@ -151,7 +153,26 @@ int bt_to_errno(__u16 code) } EXPORT_SYMBOL(bt_to_errno); -int bt_printk(const char *level, const char *format, ...) +int bt_info(const char *format, ...) +{ + struct va_format vaf; + va_list args; + int r; + + va_start(args, format); + + vaf.fmt = format; + vaf.va = &args; + + r = pr_info("%pV", &vaf); + + va_end(args); + + return r; +} +EXPORT_SYMBOL(bt_info); + +int bt_err(const char *format, ...) { struct va_format vaf; va_list args; @@ -162,10 +183,10 @@ int bt_printk(const char *level, const char *format, ...) vaf.fmt = format; vaf.va = &args; - r = printk("%sBluetooth: %pV\n", level, &vaf); + r = pr_err("%pV", &vaf); va_end(args); return r; } -EXPORT_SYMBOL(bt_printk); +EXPORT_SYMBOL(bt_err); -- cgit From 20d1803a70ddafc8410b461caaa397e49da246ac Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 17 Feb 2012 11:40:55 +0200 Subject: Bluetooth: Move scope of state_to_string Function state_to_string will be used in other files in debug statements. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/bluetooth.h | 27 +++++++++++++++++++++++++++ net/bluetooth/l2cap_core.c | 26 -------------------------- 2 files changed, 27 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 9f5f2c1c5554..5ca9219fe940 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -131,6 +131,33 @@ enum { BT_CLOSED }; +/* If unused will be removed by compiler */ +static inline const char *state_to_string(int state) +{ + switch (state) { + case BT_CONNECTED: + return "BT_CONNECTED"; + case BT_OPEN: + return "BT_OPEN"; + case BT_BOUND: + return "BT_BOUND"; + case BT_LISTEN: + return "BT_LISTEN"; + case BT_CONNECT: + return "BT_CONNECT"; + case BT_CONNECT2: + return "BT_CONNECT2"; + case BT_CONFIG: + return "BT_CONFIG"; + case BT_DISCONN: + return "BT_DISCONN"; + case BT_CLOSED: + return "BT_CLOSED"; + } + + return "invalid state"; +} + /* BD Address */ typedef struct { __u8 b[6]; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8efac7884ffb..252a96e474ca 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -215,32 +215,6 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn) return 0; } -static char *state_to_string(int state) -{ - switch(state) { - case BT_CONNECTED: - return "BT_CONNECTED"; - case BT_OPEN: - return "BT_OPEN"; - case BT_BOUND: - return "BT_BOUND"; - case BT_LISTEN: - return "BT_LISTEN"; - case BT_CONNECT: - return "BT_CONNECT"; - case BT_CONNECT2: - return "BT_CONNECT2"; - case BT_CONFIG: - return "BT_CONFIG"; - case BT_DISCONN: - return "BT_DISCONN"; - case BT_CLOSED: - return "BT_CLOSED"; - } - - return "invalid state"; -} - static void l2cap_state_change(struct l2cap_chan *chan, int state) { BT_DBG("%p %s -> %s", chan, state_to_string(chan->state), -- cgit From e05dcc3291dcfe9ab1b456f38ccb3041ebbda59c Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 17 Feb 2012 11:40:56 +0200 Subject: Bluetooth: Use symbolic names for state in debug Use state_to_string function in debug statements. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 3 ++- net/bluetooth/hci_conn.c | 2 +- net/bluetooth/l2cap_core.c | 5 +++-- net/bluetooth/l2cap_sock.c | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 42fdbb833254..bbb0e214e51d 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -612,7 +612,8 @@ static inline void l2cap_chan_put(struct l2cap_chan *c) static inline void l2cap_set_timer(struct l2cap_chan *chan, struct delayed_work *work, long timeout) { - BT_DBG("chan %p state %d timeout %ld", chan, chan->state, timeout); + BT_DBG("chan %p state %s timeout %ld", chan, + state_to_string(chan->state), timeout); if (!cancel_delayed_work(work)) l2cap_chan_hold(chan); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index b4ecddee11b5..f38e1b11d835 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -282,7 +282,7 @@ static void hci_conn_timeout(struct work_struct *work) disc_work.work); __u8 reason; - BT_DBG("conn %p state %d", conn, conn->state); + BT_DBG("conn %p state %s", conn, state_to_string(conn->state)); if (atomic_read(&conn->refcnt)) return; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 252a96e474ca..88968e711d3d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -231,7 +231,7 @@ static void l2cap_chan_timeout(struct work_struct *work) struct sock *sk = chan->sk; int reason; - BT_DBG("chan %p state %d", chan, chan->state); + BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); lock_sock(sk); @@ -413,7 +413,8 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) struct l2cap_conn *conn = chan->conn; struct sock *sk = chan->sk; - BT_DBG("chan %p state %d socket %p", chan, chan->state, sk->sk_socket); + BT_DBG("chan %p state %s sk %p", chan, + state_to_string(chan->state), sk); switch (chan->state) { case BT_LISTEN: diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 138fe3446678..b48d6c1b9db6 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -783,7 +783,7 @@ static void l2cap_sock_kill(struct sock *sk) if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket) return; - BT_DBG("sk %p state %d", sk, sk->sk_state); + BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state)); /* Kill poor orphan */ -- cgit From d753fdc40f60da2eef03b4816392081a552fea5a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 17 Feb 2012 14:06:34 +0200 Subject: Bluetooth: mgmt: Add address type to link key messages The latest mgmt API includes an address type wherever there's an address present. This patch updates the link key messages to match it. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 2 +- net/bluetooth/mgmt.c | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 255a99600f08..5aafe929d011 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -153,7 +153,7 @@ struct mgmt_cp_remove_uuid { } __packed; struct mgmt_link_key_info { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; u8 type; u8 val[16]; u8 pin_len; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 36c4ff6fdf05..b0de7194249e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1274,8 +1274,8 @@ static int load_link_keys(struct sock *sk, u16 index, void *data, u16 len) for (i = 0; i < key_count; i++) { struct mgmt_link_key_info *key = &cp->keys[i]; - hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type, - key->pin_len); + hci_add_link_key(hdev, NULL, 0, &key->addr.bdaddr, key->val, + key->type, key->pin_len); } cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, NULL, 0); @@ -2788,7 +2788,8 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, memset(&ev, 0, sizeof(ev)); ev.store_hint = persistent; - bacpy(&ev.key.bdaddr, &key->bdaddr); + bacpy(&ev.key.addr.bdaddr, &key->bdaddr); + ev.key.addr.type = MGMT_ADDR_BREDR; ev.key.type = key->type; memcpy(ev.key.val, key->val, 16); ev.key.pin_len = key->pin_len; -- cgit From d8457698e7f23a05055396a15ec72ba663282867 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 17 Feb 2012 14:24:57 +0200 Subject: Bluetooth: mgmt: Add address type to PIN code messages The latest mgmt API includes address types for all messages containing an address. This patch updates the PIN code messages to match this. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 8 ++++---- net/bluetooth/mgmt.c | 22 +++++++++++++--------- 2 files changed, 17 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 5aafe929d011..eb584cc287d6 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -199,18 +199,18 @@ struct mgmt_rp_get_connections { #define MGMT_OP_PIN_CODE_REPLY 0x0016 struct mgmt_cp_pin_code_reply { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __u8 pin_len; __u8 pin_code[16]; } __packed; struct mgmt_rp_pin_code_reply { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; uint8_t status; } __packed; #define MGMT_OP_PIN_CODE_NEG_REPLY 0x0017 struct mgmt_cp_pin_code_neg_reply { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; } __packed; #define MGMT_OP_SET_IO_CAPABILITY 0x0018 @@ -377,7 +377,7 @@ struct mgmt_ev_connect_failed { #define MGMT_EV_PIN_CODE_REQUEST 0x000E struct mgmt_ev_pin_code_request { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __u8 secure; } __packed; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b0de7194249e..68623401933f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1528,8 +1528,8 @@ static int send_pin_code_neg_reply(struct sock *sk, u16 index, if (!cmd) return -ENOMEM; - err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(cp->bdaddr), - &cp->bdaddr); + err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, + sizeof(cp->addr.bdaddr), &cp->addr.bdaddr); if (err < 0) mgmt_pending_remove(cmd); @@ -1541,7 +1541,6 @@ static int pin_code_reply(struct sock *sk, u16 index, void *data, u16 len) struct hci_dev *hdev; struct hci_conn *conn; struct mgmt_cp_pin_code_reply *cp = data; - struct mgmt_cp_pin_code_neg_reply ncp; struct hci_cp_pin_code_reply reply; struct pending_cmd *cmd; int err; @@ -1565,7 +1564,7 @@ static int pin_code_reply(struct sock *sk, u16 index, void *data, u16 len) goto failed; } - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr); if (!conn) { err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, MGMT_STATUS_NOT_CONNECTED); @@ -1573,7 +1572,9 @@ static int pin_code_reply(struct sock *sk, u16 index, void *data, u16 len) } if (conn->pending_sec_level == BT_SECURITY_HIGH && cp->pin_len != 16) { - bacpy(&ncp.bdaddr, &cp->bdaddr); + struct mgmt_cp_pin_code_neg_reply ncp; + + memcpy(&ncp.addr, &cp->addr, sizeof(ncp.addr)); BT_ERR("PIN code is not 16 bytes long"); @@ -1592,7 +1593,7 @@ static int pin_code_reply(struct sock *sk, u16 index, void *data, u16 len) goto failed; } - bacpy(&reply.bdaddr, &cp->bdaddr); + bacpy(&reply.bdaddr, &cp->addr.bdaddr); reply.pin_len = cp->pin_len; memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code)); @@ -2945,7 +2946,8 @@ int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure) { struct mgmt_ev_pin_code_request ev; - bacpy(&ev.bdaddr, bdaddr); + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = MGMT_ADDR_BREDR; ev.secure = secure; return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev), @@ -2963,7 +2965,8 @@ int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, if (!cmd) return -ENOENT; - bacpy(&rp.bdaddr, bdaddr); + bacpy(&rp.addr.bdaddr, bdaddr); + rp.addr.type = MGMT_ADDR_BREDR; rp.status = mgmt_status(status); err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, &rp, @@ -2985,7 +2988,8 @@ int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, if (!cmd) return -ENOENT; - bacpy(&rp.bdaddr, bdaddr); + bacpy(&rp.addr.bdaddr, bdaddr); + rp.addr.type = MGMT_ADDR_BREDR; rp.status = mgmt_status(status); err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, &rp, -- cgit From a198e7b100b26dd6ac0240487ca37bad0f53e3e6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 17 Feb 2012 14:27:06 +0200 Subject: Bluetooth: mgmt: Add address type to confirm name command The latest mgmt API includes an address type for all messages containing an address. This patch updates the confirm name command to match this. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 4 ++-- net/bluetooth/mgmt.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index eb584cc287d6..14c1816cac67 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -296,11 +296,11 @@ struct mgmt_cp_start_discovery { #define MGMT_OP_CONFIRM_NAME 0x0025 struct mgmt_cp_confirm_name { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __u8 name_known; } __packed; struct mgmt_rp_confirm_name { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __u8 status; } __packed; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 68623401933f..01c8d6239a4b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2299,7 +2299,7 @@ static int confirm_name(struct sock *sk, u16 index, void *data, u16 len) goto failed; } - e = hci_inquiry_cache_lookup_unknown(hdev, &cp->bdaddr); + e = hci_inquiry_cache_lookup_unknown(hdev, &cp->addr.bdaddr); if (!e) { err = cmd_status (sk, index, MGMT_OP_CONFIRM_NAME, MGMT_STATUS_INVALID_PARAMS); -- cgit From ea585ab51d3fe2eb2d738c91f83e7c309e76b4fe Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 17 Feb 2012 14:50:39 +0200 Subject: Bluetooth: Add Intel copyright to mgmt files This patch adds the appropriate Intel copyright to mgmt files. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 1 + net/bluetooth/mgmt.c | 2 ++ 2 files changed, 3 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 14c1816cac67..ee625a6ad791 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -2,6 +2,7 @@ BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2010 Nokia Corporation + Copyright (C) 2011-2012 Intel Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 01c8d6239a4b..f9f3e4c44150 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1,6 +1,8 @@ /* BlueZ - Bluetooth protocol stack for Linux + Copyright (C) 2010 Nokia Corporation + Copyright (C) 2011-2012 Intel Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as -- cgit From f39799f5047c4827b200acbf33cd0ba076afd7ed Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 17 Feb 2012 20:39:35 -0300 Subject: Bluetooth: Prepare start_discovery This patch does some code refactoring in start_discovery function in order to prepare it for interleaved discovery support. MGMT_ADDR_* macros were moved to hci_core.h since they are now used to define discovery type macros. Discovery type macros were defined according to mgmt-api.txt specification: Possible values for the Type parameter are a bit-wise or of the following bits: 1 BR/EDR 2 LE Public 3 LE Random By combining these e.g. the following values are possible: 1 BR/EDR 6 LE (public & random) 7 BR/EDR/LE (interleaved discovery) Signed-off-by: Andre Guedes Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 12 ++++++++++++ include/net/bluetooth/mgmt.h | 5 ----- net/bluetooth/mgmt.c | 15 ++++++++++----- 3 files changed, 22 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 43e0b1eda020..be8da5d54abb 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -956,6 +956,18 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb, struct sock *skip_sk); /* Management interface */ +#define MGMT_ADDR_BREDR 0x00 +#define MGMT_ADDR_LE_PUBLIC 0x01 +#define MGMT_ADDR_LE_RANDOM 0x02 +#define MGMT_ADDR_INVALID 0xff + +#define DISCOV_TYPE_BREDR (BIT(MGMT_ADDR_BREDR)) +#define DISCOV_TYPE_LE (BIT(MGMT_ADDR_LE_PUBLIC) | \ + BIT(MGMT_ADDR_LE_RANDOM)) +#define DISCOV_TYPE_INTERLEAVED (BIT(MGMT_ADDR_BREDR) | \ + BIT(MGMT_ADDR_LE_PUBLIC) | \ + BIT(MGMT_ADDR_LE_RANDOM)) + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); int mgmt_index_added(struct hci_dev *hdev); int mgmt_index_removed(struct hci_dev *hdev); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index ee625a6ad791..ad54b5fd634c 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -48,11 +48,6 @@ struct mgmt_hdr { __le16 len; } __packed; -#define MGMT_ADDR_BREDR 0x00 -#define MGMT_ADDR_LE_PUBLIC 0x01 -#define MGMT_ADDR_LE_RANDOM 0x02 -#define MGMT_ADDR_INVALID 0xff - struct mgmt_addr_info { bdaddr_t bdaddr; __u8 type; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f9f3e4c44150..196215c9d424 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2157,7 +2157,6 @@ static int start_discovery(struct sock *sk, u16 index, void *data, u16 len) { struct mgmt_cp_start_discovery *cp = data; - unsigned long discov_type = cp->type; struct pending_cmd *cmd; struct hci_dev *hdev; int err; @@ -2193,14 +2192,20 @@ static int start_discovery(struct sock *sk, u16 index, goto failed; } - if (test_bit(MGMT_ADDR_BREDR, &discov_type)) + switch (cp->type) { + case DISCOV_TYPE_BREDR: + case DISCOV_TYPE_INTERLEAVED: err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR); - else if (test_bit(MGMT_ADDR_LE_PUBLIC, &discov_type) && - test_bit(MGMT_ADDR_LE_RANDOM, &discov_type)) + break; + + case DISCOV_TYPE_LE: err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY); - else + break; + + default: err = -EINVAL; + } if (err < 0) mgmt_pending_remove(cmd); -- cgit From 4aab14e5504e84c42534378f91e836e6f55d0886 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 17 Feb 2012 20:39:36 -0300 Subject: Bluetooth: Track discovery type This patch adds to struct discovery_state the field 'type' so that we can track the discovery type the device is performing. Signed-off-by: Andre Guedes Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 2 ++ net/bluetooth/mgmt.c | 4 +++- 3 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index be8da5d54abb..d7c79b5335c2 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -57,6 +57,7 @@ struct inquiry_entry { }; struct discovery_state { + int type; enum { DISCOVERY_STOPPED, DISCOVERY_STARTING, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index dc31e7d6028e..29a9b01c3b9b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -380,6 +380,8 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state) switch (state) { case DISCOVERY_STOPPED: + hdev->discovery.type = 0; + if (hdev->discovery.state != DISCOVERY_STARTING) mgmt_discovering(hdev, 0); break; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 196215c9d424..9d98382e48c7 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2192,7 +2192,9 @@ static int start_discovery(struct sock *sk, u16 index, goto failed; } - switch (cp->type) { + hdev->discovery.type = cp->type; + + switch (hdev->discovery.type) { case DISCOV_TYPE_BREDR: case DISCOV_TYPE_INTERLEAVED: err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR); -- cgit From 343f935bfa44189c68527102c409286b0cfc4526 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 17 Feb 2012 20:39:37 -0300 Subject: Bluetooth: Merge INQUIRY and LE_SCAN discovery states This patch merges DISCOVERY_INQUIRY and DISCOVERY_LE_SCAN states into a new state called DISCOVERY_FINDING. From the discovery perspective, we are pretty much worried about to know just if we are finding devices than what exactly phase of "finding devices" (inquiry or LE scan) we are currently running. Besides, to know if the controller is performing inquiry or LE scan we should check HCI_INQUIRY or HCI_LE_SCAN bits in hdev flags. Moreover, merging this two states will simplify the discovery state machine and will keep interleaved discovery implementation simpler. Signed-off-by: Andre Guedes Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 3 +-- net/bluetooth/hci_core.c | 6 ++---- net/bluetooth/hci_event.c | 6 +++--- net/bluetooth/mgmt.c | 2 +- 4 files changed, 7 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d7c79b5335c2..942de7764278 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -61,8 +61,7 @@ struct discovery_state { enum { DISCOVERY_STOPPED, DISCOVERY_STARTING, - DISCOVERY_INQUIRY, - DISCOVERY_LE_SCAN, + DISCOVERY_FINDING, DISCOVERY_RESOLVING, DISCOVERY_STOPPING, } state; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 29a9b01c3b9b..fabca080ae70 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -361,8 +361,7 @@ bool hci_discovery_active(struct hci_dev *hdev) struct discovery_state *discov = &hdev->discovery; switch (discov->state) { - case DISCOVERY_INQUIRY: - case DISCOVERY_LE_SCAN: + case DISCOVERY_FINDING: case DISCOVERY_RESOLVING: return true; @@ -387,8 +386,7 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state) break; case DISCOVERY_STARTING: break; - case DISCOVERY_INQUIRY: - case DISCOVERY_LE_SCAN: + case DISCOVERY_FINDING: mgmt_discovering(hdev, 1); break; case DISCOVERY_RESOLVING: diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 179d127601fc..9aea7b898821 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1080,7 +1080,7 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, hci_dev_lock(hdev); hci_adv_entries_clear(hdev); - hci_discovery_set_state(hdev, DISCOVERY_LE_SCAN); + hci_discovery_set_state(hdev, DISCOVERY_FINDING); hci_dev_unlock(hdev); break; @@ -1159,7 +1159,7 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) set_bit(HCI_INQUIRY, &hdev->flags); hci_dev_lock(hdev); - hci_discovery_set_state(hdev, DISCOVERY_INQUIRY); + hci_discovery_set_state(hdev, DISCOVERY_FINDING); hci_dev_unlock(hdev); } @@ -1645,7 +1645,7 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff hci_dev_lock(hdev); - if (discov->state != DISCOVERY_INQUIRY) + if (discov->state != DISCOVERY_FINDING) goto unlock; if (list_empty(&discov->resolve)) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 9d98382e48c7..a9cd38dc2cab 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2250,7 +2250,7 @@ static int stop_discovery(struct sock *sk, u16 index) goto unlock; } - if (hdev->discovery.state == DISCOVERY_INQUIRY) { + if (hdev->discovery.state == DISCOVERY_FINDING) { err = hci_cancel_inquiry(hdev); if (err < 0) mgmt_pending_remove(cmd); -- cgit From 5e0452c00a2e4b04ec1482248c897dacf106f1df Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 17 Feb 2012 20:39:38 -0300 Subject: Bluetooth: Interleaved discovery support This patch adds interleaved discovery support to MGMT Start Discovery command. In case interleaved discovery is not supported (not a dual mode device), we perform BR/EDR or LE-only discovery according to the device capabilities. Signed-off-by: Andre Guedes Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_event.c | 13 +++++++---- net/bluetooth/mgmt.c | 47 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 942de7764278..2aafeb3a8793 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -716,6 +716,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR) #define lmp_no_flush_capable(dev) ((dev)->features[6] & LMP_NO_FLUSH) #define lmp_le_capable(dev) ((dev)->features[4] & LMP_LE) +#define lmp_bredr_capable(dev) (!((dev)->features[4] & LMP_NO_BREDR)) /* ----- Extended LMP capabilities ----- */ #define lmp_host_le_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE) @@ -1019,6 +1020,7 @@ int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status); int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status); int mgmt_discovering(struct hci_dev *hdev, u8 discovering); +int mgmt_interleaved_discovery(struct hci_dev *hdev); int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 9aea7b898821..04fb1f02dfcc 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1090,11 +1090,16 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, clear_bit(HCI_LE_SCAN, &hdev->dev_flags); - hci_dev_lock(hdev); - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - hci_dev_unlock(hdev); - schedule_delayed_work(&hdev->adv_work, ADV_CLEAR_TIMEOUT); + + if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED) { + mgmt_interleaved_discovery(hdev); + } else { + hci_dev_lock(hdev); + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + hci_dev_unlock(hdev); + } + break; default: diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a9cd38dc2cab..89754bbcd02b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -108,8 +108,10 @@ static const u16 mgmt_events[] = { #define LE_SCAN_WIN 0x12 #define LE_SCAN_INT 0x12 #define LE_SCAN_TIMEOUT_LE_ONLY 10240 /* TGAP(gen_disc_scan_min) */ +#define LE_SCAN_TIMEOUT_BREDR_LE 5120 /* TGAP(100)/2 */ #define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */ +#define INQUIRY_LEN_BREDR_LE 0x04 /* TGAP(100)/2 */ #define SERVICE_CACHE_TIMEOUT (5 * 1000) @@ -2153,6 +2155,46 @@ static int remove_remote_oob_data(struct sock *sk, u16 index, return err; } +static int discovery(struct hci_dev *hdev) +{ + int err; + + if (lmp_host_le_capable(hdev)) { + if (lmp_bredr_capable(hdev)) { + err = hci_le_scan(hdev, LE_SCAN_TYPE, + LE_SCAN_INT, LE_SCAN_WIN, + LE_SCAN_TIMEOUT_BREDR_LE); + } else { + hdev->discovery.type = DISCOV_TYPE_LE; + err = hci_le_scan(hdev, LE_SCAN_TYPE, + LE_SCAN_INT, LE_SCAN_WIN, + LE_SCAN_TIMEOUT_LE_ONLY); + } + } else { + hdev->discovery.type = DISCOV_TYPE_BREDR; + err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR); + } + + return err; +} + +int mgmt_interleaved_discovery(struct hci_dev *hdev) +{ + int err; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR_LE); + if (err < 0) + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + + hci_dev_unlock(hdev); + + return err; +} + static int start_discovery(struct sock *sk, u16 index, void *data, u16 len) { @@ -2196,7 +2238,6 @@ static int start_discovery(struct sock *sk, u16 index, switch (hdev->discovery.type) { case DISCOV_TYPE_BREDR: - case DISCOV_TYPE_INTERLEAVED: err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR); break; @@ -2205,6 +2246,10 @@ static int start_discovery(struct sock *sk, u16 index, LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY); break; + case DISCOV_TYPE_INTERLEAVED: + err = discovery(hdev); + break; + default: err = -EINVAL; } -- cgit From 3c6b764020d19b0993fe67f938b4b08f25c9bdd9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 18 Feb 2012 14:49:58 +0200 Subject: Bluetooth: mgmt: Change ordering of cmd_status paramters In accordance to the latest mgmt API specification the opcode comes first and then the status. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index ad54b5fd634c..36e68b4551af 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -318,8 +318,8 @@ struct mgmt_ev_cmd_complete { #define MGMT_EV_CMD_STATUS 0x0002 struct mgmt_ev_cmd_status { - __u8 status; __le16 opcode; + __u8 status; } __packed; #define MGMT_EV_CONTROLLER_ERROR 0x0003 -- cgit From aee9b218036476b8b659de5bbfada3a4633f635b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 18 Feb 2012 15:07:59 +0200 Subject: Bluetooth: mgmt: Move status parameters into the cmd_complete header Instead of having status paramters part of each individual command response it's simpler to just have the status as part of the command complete header. This patch updates the code to follow this convention and thereby also ensures compliance with the latest mgmt API specification. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 8 +--- net/bluetooth/mgmt.c | 107 ++++++++++++++++++++++--------------------- 2 files changed, 56 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 36e68b4551af..7e3d38bfaec3 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -184,7 +184,6 @@ struct mgmt_cp_disconnect { } __packed; struct mgmt_rp_disconnect { struct mgmt_addr_info addr; - __u8 status; } __packed; #define MGMT_OP_GET_CONNECTIONS 0x0015 @@ -201,7 +200,6 @@ struct mgmt_cp_pin_code_reply { } __packed; struct mgmt_rp_pin_code_reply { struct mgmt_addr_info addr; - uint8_t status; } __packed; #define MGMT_OP_PIN_CODE_NEG_REPLY 0x0017 @@ -221,7 +219,6 @@ struct mgmt_cp_pair_device { } __packed; struct mgmt_rp_pair_device { struct mgmt_addr_info addr; - __u8 status; } __packed; #define MGMT_OP_CANCEL_PAIR_DEVICE 0x001A @@ -233,7 +230,6 @@ struct mgmt_cp_unpair_device { } __packed; struct mgmt_rp_unpair_device { struct mgmt_addr_info addr; - __u8 status; }; #define MGMT_OP_USER_CONFIRM_REPLY 0x001C @@ -242,7 +238,6 @@ struct mgmt_cp_user_confirm_reply { } __packed; struct mgmt_rp_user_confirm_reply { struct mgmt_addr_info addr; - __u8 status; } __packed; #define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x001D @@ -257,7 +252,6 @@ struct mgmt_cp_user_passkey_reply { } __packed; struct mgmt_rp_user_passkey_reply { struct mgmt_addr_info addr; - __u8 status; } __packed; #define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x001F @@ -297,7 +291,6 @@ struct mgmt_cp_confirm_name { } __packed; struct mgmt_rp_confirm_name { struct mgmt_addr_info addr; - __u8 status; } __packed; #define MGMT_OP_BLOCK_DEVICE 0x0026 @@ -313,6 +306,7 @@ struct mgmt_cp_unblock_device { #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; + __u8 status; __u8 data[0]; } __packed; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 89754bbcd02b..61d0250bd77e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -227,8 +227,8 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) return err; } -static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp, - size_t rp_len) +static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status, + void *rp, size_t rp_len) { struct sk_buff *skb; struct mgmt_hdr *hdr; @@ -249,6 +249,7 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp, ev = (void *) skb_put(skb, sizeof(*ev) + rp_len); put_unaligned_le16(cmd, &ev->opcode); + ev->status = status; if (rp) memcpy(ev->data, rp, rp_len); @@ -269,7 +270,7 @@ static int read_version(struct sock *sk) rp.version = MGMT_VERSION; put_unaligned_le16(MGMT_REVISION, &rp.revision); - return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, &rp, + return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, 0, &rp, sizeof(rp)); } @@ -299,7 +300,7 @@ static int read_commands(struct sock *sk) for (i = 0; i < num_events; i++, opcode++) put_unaligned_le16(mgmt_events[i], opcode); - err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_COMMANDS, rp, + err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_COMMANDS, 0, rp, rp_size); kfree(rp); @@ -347,7 +348,7 @@ static int read_index_list(struct sock *sk) read_unlock(&hci_dev_list_lock); - err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, rp, + err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, 0, rp, rp_len); kfree(rp); @@ -637,7 +638,7 @@ static int read_controller_info(struct sock *sk, u16 index) hci_dev_unlock(hdev); hci_dev_put(hdev); - return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp)); + return cmd_complete(sk, index, MGMT_OP_READ_INFO, 0, &rp, sizeof(rp)); } static void mgmt_pending_free(struct pending_cmd *cmd) @@ -717,7 +718,8 @@ static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) { __le32 settings = cpu_to_le32(get_current_settings(hdev)); - return cmd_complete(sk, hdev->id, opcode, &settings, sizeof(settings)); + return cmd_complete(sk, hdev->id, opcode, 0, &settings, + sizeof(settings)); } static int set_powered(struct sock *sk, u16 index, void *data, u16 len) @@ -1124,7 +1126,7 @@ static int add_uuid(struct sock *sk, u16 index, void *data, u16 len) if (err < 0) goto failed; - err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0); + err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, 0, NULL, 0); failed: hci_dev_unlock(hdev); @@ -1185,7 +1187,7 @@ static int remove_uuid(struct sock *sk, u16 index, void *data, u16 len) if (err < 0) goto unlock; - err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0); + err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, 0, NULL, 0); unlock: hci_dev_unlock(hdev); @@ -1226,7 +1228,8 @@ static int set_dev_class(struct sock *sk, u16 index, void *data, u16 len) err = update_class(hdev); if (err == 0) - err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, NULL, 0); + err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, 0, + NULL, 0); hci_dev_unlock(hdev); hci_dev_put(hdev); @@ -1282,7 +1285,7 @@ static int load_link_keys(struct sock *sk, u16 index, void *data, u16 len) key->type, key->pin_len); } - cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, NULL, 0); + cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0); hci_dev_unlock(hdev); hci_dev_put(hdev); @@ -1310,6 +1313,7 @@ static int unpair_device(struct sock *sk, u16 index, void *data, u16 len) struct hci_cp_disconnect dc; struct pending_cmd *cmd; struct hci_conn *conn; + u8 status = 0; int err; if (len != sizeof(*cp)) @@ -1333,13 +1337,13 @@ static int unpair_device(struct sock *sk, u16 index, void *data, u16 len) err = hci_remove_ltk(hdev, &cp->addr.bdaddr); if (err < 0) { - rp.status = MGMT_STATUS_NOT_PAIRED; + status = MGMT_STATUS_NOT_PAIRED; goto unlock; } if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) { - err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp, - sizeof(rp)); + err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, status, + &rp, sizeof(rp)); device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk); goto unlock; } @@ -1352,8 +1356,8 @@ static int unpair_device(struct sock *sk, u16 index, void *data, u16 len) &cp->addr.bdaddr); if (!conn) { - err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp, - sizeof(rp)); + err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, status, + &rp, sizeof(rp)); device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk); goto unlock; } @@ -1373,8 +1377,8 @@ static int unpair_device(struct sock *sk, u16 index, void *data, u16 len) unlock: if (err < 0) - err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp, - sizeof(rp)); + err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, status, + &rp, sizeof(rp)); hci_dev_unlock(hdev); hci_dev_put(hdev); @@ -1512,7 +1516,7 @@ static int get_connections(struct sock *sk, u16 index) /* Recalculate length in case of filtered SCO connections, etc */ rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info)); - err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, rp, rp_len); + err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, 0, rp, rp_len); unlock: kfree(rp); @@ -1672,7 +1676,7 @@ static int set_io_capability(struct sock *sk, u16 index, void *data, u16 len) hci_dev_unlock(hdev); hci_dev_put(hdev); - return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, NULL, 0); + return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, 0, NULL, 0); } static inline struct pending_cmd *find_pairing(struct hci_conn *conn) @@ -1700,9 +1704,9 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status) bacpy(&rp.addr.bdaddr, &conn->dst); rp.addr.type = link_to_mgmt(conn->type, conn->dst_type); - rp.status = status; - cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, &rp, sizeof(rp)); + cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status, + &rp, sizeof(rp)); /* So we don't get further callbacks for this connection */ conn->connect_cfm_cb = NULL; @@ -1735,6 +1739,7 @@ static int pair_device(struct sock *sk, u16 index, void *data, u16 len) struct pending_cmd *cmd; u8 sec_level, auth_type; struct hci_conn *conn; + u8 status = 0; int err; BT_DBG(""); @@ -1768,16 +1773,16 @@ static int pair_device(struct sock *sk, u16 index, void *data, u16 len) rp.addr.type = cp->addr.type; if (IS_ERR(conn)) { - rp.status = -PTR_ERR(conn); - err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE, + status = -PTR_ERR(conn); + err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE, status, &rp, sizeof(rp)); goto unlock; } if (conn->connect_cfm_cb) { hci_conn_put(conn); - rp.status = EBUSY; - err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE, + status = EBUSY; + err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE, status, &rp, sizeof(rp)); goto unlock; } @@ -1850,7 +1855,7 @@ static int cancel_pair_device(struct sock *sk, u16 index, pairing_complete(cmd, MGMT_STATUS_CANCELLED); - err = cmd_complete(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE, addr, + err = cmd_complete(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE, 0, addr, sizeof(*addr)); unlock: hci_dev_unlock(hdev); @@ -2112,8 +2117,8 @@ static int add_remote_oob_data(struct sock *sk, u16 index, void *data, err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, MGMT_STATUS_FAILED); else - err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL, - 0); + err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, 0, + NULL, 0); hci_dev_unlock(hdev); hci_dev_put(hdev); @@ -2147,7 +2152,7 @@ static int remove_remote_oob_data(struct sock *sk, u16 index, MGMT_STATUS_INVALID_PARAMS); else err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, - NULL, 0); + 0, NULL, 0); hci_dev_unlock(hdev); hci_dev_put(hdev); @@ -2307,7 +2312,8 @@ static int stop_discovery(struct sock *sk, u16 index) e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_PENDING); if (!e) { mgmt_pending_remove(cmd); - err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY, NULL, 0); + err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY, 0, + NULL, 0); hci_discovery_set_state(hdev, DISCOVERY_STOPPED); goto unlock; } @@ -2400,8 +2406,8 @@ static int block_device(struct sock *sk, u16 index, void *data, u16 len) err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, MGMT_STATUS_FAILED); else - err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE, - NULL, 0); + err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE, 0, + NULL, 0); hci_dev_unlock(hdev); hci_dev_put(hdev); @@ -2434,7 +2440,7 @@ static int unblock_device(struct sock *sk, u16 index, void *data, u16 len) err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, MGMT_STATUS_INVALID_PARAMS); else - err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE, + err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE, 0, NULL, 0); hci_dev_unlock(hdev); @@ -2490,8 +2496,8 @@ static int set_fast_connectable(struct sock *sk, u16 index, goto done; } - err = cmd_complete(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, - NULL, 0); + err = cmd_complete(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, 0, + NULL, 0); done: hci_dev_unlock(hdev); hci_dev_put(hdev); @@ -2908,9 +2914,9 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data) bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); rp.addr.type = cp->addr.type; - rp.status = 0; - cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, &rp, sizeof(rp)); + cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, 0, &rp, + sizeof(rp)); *sk = cmd->sk; sock_hold(*sk); @@ -2930,7 +2936,7 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data) device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk); - cmd_complete(cmd->sk, cmd->index, cmd->opcode, &rp, sizeof(rp)); + cmd_complete(cmd->sk, cmd->index, cmd->opcode, 0, &rp, sizeof(rp)); mgmt_pending_remove(cmd); } @@ -2972,10 +2978,9 @@ int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, bacpy(&rp.addr.bdaddr, bdaddr); rp.addr.type = link_to_mgmt(link_type, addr_type); - rp.status = mgmt_status(status); err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, - &rp, sizeof(rp)); + mgmt_status(status), &rp, sizeof(rp)); mgmt_pending_remove(cmd); @@ -3021,10 +3026,9 @@ int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, bacpy(&rp.addr.bdaddr, bdaddr); rp.addr.type = MGMT_ADDR_BREDR; - rp.status = mgmt_status(status); - err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, &rp, - sizeof(rp)); + err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, + mgmt_status(status), &rp, sizeof(rp)); mgmt_pending_remove(cmd); @@ -3044,10 +3048,9 @@ int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, bacpy(&rp.addr.bdaddr, bdaddr); rp.addr.type = MGMT_ADDR_BREDR; - rp.status = mgmt_status(status); - err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, &rp, - sizeof(rp)); + err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, + mgmt_status(status), &rp, sizeof(rp)); mgmt_pending_remove(cmd); @@ -3099,8 +3102,8 @@ static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, bacpy(&rp.addr.bdaddr, bdaddr); rp.addr.type = link_to_mgmt(link_type, addr_type); - rp.status = mgmt_status(status); - err = cmd_complete(cmd->sk, hdev->id, opcode, &rp, sizeof(rp)); + err = cmd_complete(cmd->sk, hdev->id, opcode, mgmt_status(status), + &rp, sizeof(rp)); mgmt_pending_remove(cmd); @@ -3217,7 +3220,7 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) update_eir(hdev); - err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, &ev, + err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, &ev, sizeof(ev)); if (err < 0) goto failed; @@ -3256,7 +3259,7 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, - &rp, sizeof(rp)); + 0, &rp, sizeof(rp)); } mgmt_pending_remove(cmd); @@ -3365,7 +3368,7 @@ int mgmt_discovering(struct hci_dev *hdev, u8 discovering) cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); if (cmd != NULL) { - cmd_complete(cmd->sk, hdev->id, cmd->opcode, NULL, 0); + cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, NULL, 0); mgmt_pending_remove(cmd); } -- cgit From 470fe1b540fb50ba8ce01e0ac985602e8fbb108c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 20 Feb 2012 14:50:30 +0100 Subject: Bluetooth: Split sending for HCI raw and control sockets The sending functions for HCI raw and control sockets have nothing in common except that they iterate over the socket list. Split them into two so they can do their job more efficient. In addition the code becomes more readable. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 4 ++-- net/bluetooth/hci_core.c | 4 ++-- net/bluetooth/hci_event.c | 2 +- net/bluetooth/hci_sock.c | 51 ++++++++++++++++++++++++++++++---------- net/bluetooth/mgmt.c | 2 +- 5 files changed, 45 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 2aafeb3a8793..9209e4c8a211 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -953,8 +953,8 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data); /* ----- HCI Sockets ----- */ -void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb, - struct sock *skip_sk); +void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); +void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk); /* Management interface */ #define MGMT_ADDR_BREDR 0x00 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index fabca080ae70..638fa8c393d8 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2131,7 +2131,7 @@ static int hci_send_frame(struct sk_buff *skb) /* Time stamp */ __net_timestamp(skb); - hci_send_to_sock(hdev, skb, NULL); + hci_send_to_sock(hdev, skb); } /* Get rid of skb owner, prior to sending to the driver. */ @@ -2818,7 +2818,7 @@ static void hci_rx_work(struct work_struct *work) while ((skb = skb_dequeue(&hdev->rx_q))) { if (atomic_read(&hdev->promisc)) { /* Send copy to the sockets */ - hci_send_to_sock(hdev, skb, NULL); + hci_send_to_sock(hdev, skb); } if (test_bit(HCI_RAW, &hdev->flags)) { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 04fb1f02dfcc..e69db4a7b3ef 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3571,7 +3571,7 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) bt_cb(skb)->pkt_type = HCI_EVENT_PKT; skb->dev = (void *) hdev; - hci_send_to_sock(hdev, skb, NULL); + hci_send_to_sock(hdev, skb); kfree_skb(skb); } diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 9e854d9fb460..b5b3bc8d2848 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -85,8 +85,7 @@ static struct bt_sock_list hci_sk_list = { }; /* Send frame to RAW socket */ -void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb, - struct sock *skip_sk) +void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) { struct sock *sk; struct hlist_node *node; @@ -94,13 +93,11 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb, BT_DBG("hdev %p len %d", hdev, skb->len); read_lock(&hci_sk_list.lock); + sk_for_each(sk, node, &hci_sk_list.head) { struct hci_filter *flt; struct sk_buff *nskb; - if (sk == skip_sk) - continue; - if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev) continue; @@ -108,12 +105,9 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb, if (skb->sk == sk) continue; - if (bt_cb(skb)->channel != hci_pi(sk)->channel) + if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) continue; - if (bt_cb(skb)->channel == HCI_CHANNEL_CONTROL) - goto clone; - /* Apply filter */ flt = &hci_pi(sk)->filter; @@ -137,18 +131,51 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb, continue; } -clone: nskb = skb_clone(skb, GFP_ATOMIC); if (!nskb) continue; /* Put type byte before the data */ - if (bt_cb(skb)->channel == HCI_CHANNEL_RAW) - memcpy(skb_push(nskb, 1), &bt_cb(nskb)->pkt_type, 1); + memcpy(skb_push(nskb, 1), &bt_cb(nskb)->pkt_type, 1); + + if (sock_queue_rcv_skb(sk, nskb)) + kfree_skb(nskb); + } + + read_unlock(&hci_sk_list.lock); +} + +/* Send frame to control socket */ +void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk) +{ + struct sock *sk; + struct hlist_node *node; + + BT_DBG("len %d", skb->len); + + read_lock(&hci_sk_list.lock); + + sk_for_each(sk, node, &hci_sk_list.head) { + struct sk_buff *nskb; + + /* Skip the original socket */ + if (sk == skip_sk) + continue; + + if (sk->sk_state != BT_BOUND) + continue; + + if (hci_pi(sk)->channel != HCI_CHANNEL_CONTROL) + continue; + + nskb = skb_clone(skb, GFP_ATOMIC); + if (!nskb) + continue; if (sock_queue_rcv_skb(sk, nskb)) kfree_skb(nskb); } + read_unlock(&hci_sk_list.lock); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 18d593f23934..1695d04d927d 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -924,7 +924,7 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, if (data) memcpy(skb_put(skb, data_len), data, data_len); - hci_send_to_sock(NULL, skb, skip_sk); + hci_send_to_control(skb, skip_sk); kfree_skb(skb); return 0; -- cgit From a6fb08dfe8654e399c9bbca34be914e213560b5e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 20 Feb 2012 14:50:31 +0100 Subject: Bluetooth: Remove unneeded bt_cb(skb)->channel variable The bt_cb(skb)->channel was only needed to make hci_send_to_sock() be used for HCI raw and control sockets. Since they have now separate sending functions this is no longer needed. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/bluetooth.h | 1 - net/bluetooth/mgmt.c | 2 -- 2 files changed, 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 5ca9219fe940..262ebd1747d4 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -222,7 +222,6 @@ struct bt_skb_cb { __u16 tx_seq; __u8 retries; __u8 sar; - unsigned short channel; __u8 force_active; }; #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb)) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1695d04d927d..bc71b45ef4e5 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -911,8 +911,6 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, if (!skb) return -ENOMEM; - bt_cb(skb)->channel = HCI_CHANNEL_CONTROL; - hdr = (void *) skb_put(skb, sizeof(*hdr)); hdr->opcode = cpu_to_le16(event); if (hdev) -- cgit From 040030ef7d907107e6489b39da518bdf94136d68 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 20 Feb 2012 14:50:37 +0100 Subject: Bluetooth: Remove HCI notifier handling The HCI notifier handling was never used outside of Bluetooth core layer and thus remove it and replace it with direct function calls. Also move the stack internal event generation into the HCI socket layer. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 7 +-- net/bluetooth/hci_conn.c | 1 - net/bluetooth/hci_core.c | 16 +----- net/bluetooth/hci_event.c | 29 ----------- net/bluetooth/hci_sock.c | 105 +++++++++++++++++++++++---------------- 5 files changed, 64 insertions(+), 94 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9209e4c8a211..41adae509e9c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -941,21 +941,18 @@ static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, int hci_register_cb(struct hci_cb *hcb); int hci_unregister_cb(struct hci_cb *hcb); -int hci_register_notifier(struct notifier_block *nb); -int hci_unregister_notifier(struct notifier_block *nb); - int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param); void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags); void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb); void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); -void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data); - /* ----- HCI Sockets ----- */ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk); +void hci_sock_dev_event(struct hci_dev *hdev, int event); + /* Management interface */ #define MGMT_ADDR_BREDR 0x00 #define MGMT_ADDR_LE_PUBLIC 0x01 diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 8549d04e3313..3c68e606d5e5 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 638fa8c393d8..47217281d9ac 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include @@ -69,24 +68,11 @@ DEFINE_RWLOCK(hci_dev_list_lock); LIST_HEAD(hci_cb_list); DEFINE_RWLOCK(hci_cb_list_lock); -/* HCI notifiers list */ -static ATOMIC_NOTIFIER_HEAD(hci_notifier); - /* ---- HCI notifications ---- */ -int hci_register_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&hci_notifier, nb); -} - -int hci_unregister_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&hci_notifier, nb); -} - static void hci_notify(struct hci_dev *hdev, int event) { - atomic_notifier_call_chain(&hci_notifier, event, hdev); + hci_sock_dev_event(hdev, event); } /* ---- HCI requests ---- */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e69db4a7b3ef..f00faf0ac32f 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include @@ -3547,33 +3546,5 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hdev->stat.evt_rx++; } -/* Generate internal stack event */ -void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) -{ - struct hci_event_hdr *hdr; - struct hci_ev_stack_internal *ev; - struct sk_buff *skb; - - skb = bt_skb_alloc(HCI_EVENT_HDR_SIZE + sizeof(*ev) + dlen, GFP_ATOMIC); - if (!skb) - return; - - hdr = (void *) skb_put(skb, HCI_EVENT_HDR_SIZE); - hdr->evt = HCI_EV_STACK_INTERNAL; - hdr->plen = sizeof(*ev) + dlen; - - ev = (void *) skb_put(skb, sizeof(*ev) + dlen); - ev->type = type; - memcpy(ev->data, data, dlen); - - bt_cb(skb)->incoming = 1; - __net_timestamp(skb); - - bt_cb(skb)->pkt_type = HCI_EVENT_PKT; - skb->dev = (void *) hdev; - hci_send_to_sock(hdev, skb); - kfree_skb(skb); -} - module_param(enable_le, bool, 0644); MODULE_PARM_DESC(enable_le, "Enable LE support"); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index cf940bd7a2b0..14727cb43f63 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -189,6 +189,67 @@ void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk) read_unlock(&hci_sk_list.lock); } +/* Generate internal stack event */ +static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) +{ + struct hci_event_hdr *hdr; + struct hci_ev_stack_internal *ev; + struct sk_buff *skb; + + skb = bt_skb_alloc(HCI_EVENT_HDR_SIZE + sizeof(*ev) + dlen, GFP_ATOMIC); + if (!skb) + return; + + hdr = (void *) skb_put(skb, HCI_EVENT_HDR_SIZE); + hdr->evt = HCI_EV_STACK_INTERNAL; + hdr->plen = sizeof(*ev) + dlen; + + ev = (void *) skb_put(skb, sizeof(*ev) + dlen); + ev->type = type; + memcpy(ev->data, data, dlen); + + bt_cb(skb)->incoming = 1; + __net_timestamp(skb); + + bt_cb(skb)->pkt_type = HCI_EVENT_PKT; + skb->dev = (void *) hdev; + hci_send_to_sock(hdev, skb); + kfree_skb(skb); +} + +void hci_sock_dev_event(struct hci_dev *hdev, int event) +{ + struct hci_ev_si_device ev; + + BT_DBG("hdev %s event %d", hdev->name, event); + + /* Send event to sockets */ + ev.event = event; + ev.dev_id = hdev->id; + hci_si_event(NULL, HCI_EV_SI_DEVICE, sizeof(ev), &ev); + + if (event == HCI_DEV_UNREG) { + struct sock *sk; + struct hlist_node *node; + + /* Detach sockets from device */ + read_lock(&hci_sk_list.lock); + sk_for_each(sk, node, &hci_sk_list.head) { + bh_lock_sock_nested(sk); + if (hci_pi(sk)->hdev == hdev) { + hci_pi(sk)->hdev = NULL; + sk->sk_err = EPIPE; + sk->sk_state = BT_OPEN; + sk->sk_state_change(sk); + + hci_dev_put(hdev); + } + bh_unlock_sock(sk); + } + read_unlock(&hci_sk_list.lock); + } +} + static int hci_sock_release(struct socket *sock) { struct sock *sk = sock->sk; @@ -821,52 +882,12 @@ static int hci_sock_create(struct net *net, struct socket *sock, int protocol, return 0; } -static int hci_sock_dev_event(struct notifier_block *this, unsigned long event, void *ptr) -{ - struct hci_dev *hdev = (struct hci_dev *) ptr; - struct hci_ev_si_device ev; - - BT_DBG("hdev %s event %ld", hdev->name, event); - - /* Send event to sockets */ - ev.event = event; - ev.dev_id = hdev->id; - hci_si_event(NULL, HCI_EV_SI_DEVICE, sizeof(ev), &ev); - - if (event == HCI_DEV_UNREG) { - struct sock *sk; - struct hlist_node *node; - - /* Detach sockets from device */ - read_lock(&hci_sk_list.lock); - sk_for_each(sk, node, &hci_sk_list.head) { - bh_lock_sock_nested(sk); - if (hci_pi(sk)->hdev == hdev) { - hci_pi(sk)->hdev = NULL; - sk->sk_err = EPIPE; - sk->sk_state = BT_OPEN; - sk->sk_state_change(sk); - - hci_dev_put(hdev); - } - bh_unlock_sock(sk); - } - read_unlock(&hci_sk_list.lock); - } - - return NOTIFY_DONE; -} - static const struct net_proto_family hci_sock_family_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .create = hci_sock_create, }; -static struct notifier_block hci_sock_nblock = { - .notifier_call = hci_sock_dev_event -}; - int __init hci_sock_init(void) { int err; @@ -879,8 +900,6 @@ int __init hci_sock_init(void) if (err < 0) goto error; - hci_register_notifier(&hci_sock_nblock); - BT_INFO("HCI socket layer initialized"); return 0; @@ -896,8 +915,6 @@ void hci_sock_cleanup(void) if (bt_sock_unregister(BTPROTO_HCI) < 0) BT_ERR("HCI socket unregistration failed"); - hci_unregister_notifier(&hci_sock_nblock); - proto_unregister(&hci_sk_proto); } -- cgit From cd82e61c110a36e398323e422896fcfe05879fed Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 20 Feb 2012 20:34:38 +0100 Subject: Bluetooth: Add support for HCI monitor channel The HCI monitor channel can be used to monitor all packets and events from the Bluetooth subsystem. The monitor is not bound to any specific HCI device and allows even capturing multiple devices at the same time. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/hci_mon.h | 51 ++++++++++ net/bluetooth/hci_core.c | 13 ++- net/bluetooth/hci_sock.c | 207 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 270 insertions(+), 3 deletions(-) create mode 100644 include/net/bluetooth/hci_mon.h (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 1b634e126878..60a4727be935 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1320,6 +1320,7 @@ struct sockaddr_hci { #define HCI_CHANNEL_RAW 0 #define HCI_CHANNEL_CONTROL 1 +#define HCI_CHANNEL_MONITOR 2 struct hci_filter { unsigned long type_mask; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 41adae509e9c..094b5dbdb130 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -950,6 +950,7 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); /* ----- HCI Sockets ----- */ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk); +void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb); void hci_sock_dev_event(struct hci_dev *hdev, int event); diff --git a/include/net/bluetooth/hci_mon.h b/include/net/bluetooth/hci_mon.h new file mode 100644 index 000000000000..07a25c92502c --- /dev/null +++ b/include/net/bluetooth/hci_mon.h @@ -0,0 +1,51 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + + Copyright (C) 2011-2012 Intel Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +#ifndef __HCI_MON_H +#define __HCI_MON_H + +struct hci_mon_hdr { + __le16 opcode; + __le16 index; + __le16 len; +} __packed; +#define HCI_MON_HDR_SIZE 6 + +#define HCI_MON_NEW_INDEX 0 +#define HCI_MON_DEL_INDEX 1 +#define HCI_MON_COMMAND_PKT 2 +#define HCI_MON_EVENT_PKT 3 +#define HCI_MON_ACL_TX_PKT 4 +#define HCI_MON_ACL_RX_PKT 5 +#define HCI_MON_SCO_TX_PKT 6 +#define HCI_MON_SCO_RX_PKT 7 + +struct hci_mon_new_index { + __u8 type; + __u8 bus; + bdaddr_t bdaddr; + char name[8]; +} __packed; +#define HCI_MON_NEW_INDEX_SIZE 16 + +#endif /* __HCI_MON_H */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 47217281d9ac..87ff7ffdb367 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2113,10 +2113,14 @@ static int hci_send_frame(struct sk_buff *skb) BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len); - if (atomic_read(&hdev->promisc)) { - /* Time stamp */ - __net_timestamp(skb); + /* Time stamp */ + __net_timestamp(skb); + /* Send copy to monitor */ + hci_send_to_monitor(hdev, skb); + + if (atomic_read(&hdev->promisc)) { + /* Send copy to the sockets */ hci_send_to_sock(hdev, skb); } @@ -2802,6 +2806,9 @@ static void hci_rx_work(struct work_struct *work) BT_DBG("%s", hdev->name); while ((skb = skb_dequeue(&hdev->rx_q))) { + /* Send copy to monitor */ + hci_send_to_monitor(hdev, skb); + if (atomic_read(&hdev->promisc)) { /* Send copy to the sockets */ hci_send_to_sock(hdev, skb); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 14727cb43f63..213697d23771 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -48,9 +48,12 @@ #include #include +#include static bool enable_mgmt; +static atomic_t monitor_promisc = ATOMIC_INIT(0); + /* ----- HCI socket interface ----- */ static inline int hci_test_bit(int nr, void *addr) @@ -189,6 +192,174 @@ void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk) read_unlock(&hci_sk_list.lock); } +/* Send frame to monitor socket */ +void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct sock *sk; + struct hlist_node *node; + struct sk_buff *skb_copy = NULL; + __le16 opcode; + + if (!atomic_read(&monitor_promisc)) + return; + + BT_DBG("hdev %p len %d", hdev, skb->len); + + switch (bt_cb(skb)->pkt_type) { + case HCI_COMMAND_PKT: + opcode = __constant_cpu_to_le16(HCI_MON_COMMAND_PKT); + break; + case HCI_EVENT_PKT: + opcode = __constant_cpu_to_le16(HCI_MON_EVENT_PKT); + break; + case HCI_ACLDATA_PKT: + if (bt_cb(skb)->incoming) + opcode = __constant_cpu_to_le16(HCI_MON_ACL_RX_PKT); + else + opcode = __constant_cpu_to_le16(HCI_MON_ACL_TX_PKT); + break; + case HCI_SCODATA_PKT: + if (bt_cb(skb)->incoming) + opcode = __constant_cpu_to_le16(HCI_MON_SCO_RX_PKT); + else + opcode = __constant_cpu_to_le16(HCI_MON_SCO_TX_PKT); + break; + default: + return; + } + + read_lock(&hci_sk_list.lock); + + sk_for_each(sk, node, &hci_sk_list.head) { + struct sk_buff *nskb; + + if (sk->sk_state != BT_BOUND) + continue; + + if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR) + continue; + + if (!skb_copy) { + struct hci_mon_hdr *hdr; + + /* Create a private copy with headroom */ + skb_copy = __pskb_copy(skb, HCI_MON_HDR_SIZE, GFP_ATOMIC); + if (!skb_copy) + continue; + + /* Put header before the data */ + hdr = (void *) skb_push(skb_copy, HCI_MON_HDR_SIZE); + hdr->opcode = opcode; + hdr->index = cpu_to_le16(hdev->id); + hdr->len = cpu_to_le16(skb->len); + } + + nskb = skb_clone(skb_copy, GFP_ATOMIC); + if (!nskb) + continue; + + if (sock_queue_rcv_skb(sk, nskb)) + kfree_skb(nskb); + } + + read_unlock(&hci_sk_list.lock); + + kfree_skb(skb_copy); +} + +static void send_monitor_event(struct sk_buff *skb) +{ + struct sock *sk; + struct hlist_node *node; + + BT_DBG("len %d", skb->len); + + read_lock(&hci_sk_list.lock); + + sk_for_each(sk, node, &hci_sk_list.head) { + struct sk_buff *nskb; + + if (sk->sk_state != BT_BOUND) + continue; + + if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR) + continue; + + nskb = skb_clone(skb, GFP_ATOMIC); + if (!nskb) + continue; + + if (sock_queue_rcv_skb(sk, nskb)) + kfree_skb(nskb); + } + + read_unlock(&hci_sk_list.lock); +} + +static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event) +{ + struct hci_mon_hdr *hdr; + struct hci_mon_new_index *ni; + struct sk_buff *skb; + __le16 opcode; + + switch (event) { + case HCI_DEV_REG: + skb = bt_skb_alloc(HCI_MON_NEW_INDEX_SIZE, GFP_ATOMIC); + if (!skb) + return NULL; + + ni = (void *) skb_put(skb, HCI_MON_NEW_INDEX_SIZE); + ni->type = hdev->dev_type; + ni->bus = hdev->bus; + bacpy(&ni->bdaddr, &hdev->bdaddr); + memcpy(ni->name, hdev->name, 8); + + opcode = __constant_cpu_to_le16(HCI_MON_NEW_INDEX); + break; + + case HCI_DEV_UNREG: + skb = bt_skb_alloc(0, GFP_ATOMIC); + if (!skb) + return NULL; + + opcode = __constant_cpu_to_le16(HCI_MON_DEL_INDEX); + break; + + default: + return NULL; + } + + __net_timestamp(skb); + + hdr = (void *) skb_push(skb, HCI_MON_HDR_SIZE); + hdr->opcode = opcode; + hdr->index = cpu_to_le16(hdev->id); + hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE); + + return skb; +} + +static void send_monitor_replay(struct sock *sk) +{ + struct hci_dev *hdev; + + read_lock(&hci_dev_list_lock); + + list_for_each_entry(hdev, &hci_dev_list, list) { + struct sk_buff *skb; + + skb = create_monitor_event(hdev, HCI_DEV_REG); + if (!skb) + continue; + + if (sock_queue_rcv_skb(sk, skb)) + kfree_skb(skb); + } + + read_unlock(&hci_dev_list_lock); +} + /* Generate internal stack event */ static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) { @@ -223,6 +394,17 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event) BT_DBG("hdev %s event %d", hdev->name, event); + /* Send event to monitor */ + if (atomic_read(&monitor_promisc)) { + struct sk_buff *skb; + + skb = create_monitor_event(hdev, event); + if (skb) { + send_monitor_event(skb); + kfree_skb(skb); + } + } + /* Send event to sockets */ ev.event = event; ev.dev_id = hdev->id; @@ -262,6 +444,9 @@ static int hci_sock_release(struct socket *sock) hdev = hci_pi(sk)->hdev; + if (hci_pi(sk)->channel == HCI_CHANNEL_MONITOR) + atomic_dec(&monitor_promisc); + bt_sock_unlink(&hci_sk_list, sk); if (hdev) { @@ -474,6 +659,22 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le set_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags); break; + case HCI_CHANNEL_MONITOR: + if (haddr.hci_dev != HCI_DEV_NONE) { + err = -EINVAL; + goto done; + } + + if (!capable(CAP_NET_RAW)) { + err = -EPERM; + goto done; + } + + send_monitor_replay(sk); + + atomic_inc(&monitor_promisc); + break; + default: err = -EINVAL; goto done; @@ -578,6 +779,9 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock, case HCI_CHANNEL_RAW: hci_sock_cmsg(sk, msg, skb); break; + case HCI_CHANNEL_MONITOR: + sock_recv_timestamp(msg, sk, skb); + break; } skb_free_datagram(sk, skb); @@ -612,6 +816,9 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, case HCI_CHANNEL_CONTROL: err = mgmt_control(sk, msg, len); goto done; + case HCI_CHANNEL_MONITOR: + err = -EOPNOTSUPP; + goto done; default: err = -EINVAL; goto done; -- cgit From d7b7e79688c07b445bc52adfedf9a176be156f4b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 20 Feb 2012 21:47:49 +0100 Subject: Bluetooth: Set supported settings based on enabled HS and/or LE Since neither High Speed (HS) nor Low Energy (LE) are fully implemented yet, only expose them in supported settings when enabled. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_core.c | 5 ----- net/bluetooth/hci_event.c | 5 ----- net/bluetooth/mgmt.c | 18 ++++++++++++++++-- 4 files changed, 17 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 60a4727be935..ad5e94c757e7 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1422,5 +1422,6 @@ struct hci_inquiry_req { #define IREQ_CACHE_FLUSH 0x0001 extern bool enable_hs; +extern bool enable_le; #endif /* __HCI_H */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 87ff7ffdb367..cc52e037440e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -54,8 +54,6 @@ #define AUTO_OFF_TIMEOUT 2000 -bool enable_hs; - static void hci_rx_work(struct work_struct *work); static void hci_cmd_work(struct work_struct *work); static void hci_tx_work(struct work_struct *work); @@ -2913,6 +2911,3 @@ int hci_cancel_inquiry(struct hci_dev *hdev) return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL); } - -module_param(enable_hs, bool, 0644); -MODULE_PARM_DESC(enable_hs, "Enable High Speed"); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f00faf0ac32f..5d0f92a948c2 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -44,8 +44,6 @@ #include #include -static bool enable_le; - /* Handle HCI Event packets */ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) @@ -3545,6 +3543,3 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) kfree_skb(skb); hdev->stat.evt_rx++; } - -module_param(enable_le, bool, 0644); -MODULE_PARM_DESC(enable_le, "Enable LE support"); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bc71b45ef4e5..f7c2969d8829 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -34,6 +34,9 @@ #include #include +bool enable_hs; +bool enable_le; + #define MGMT_VERSION 1 #define MGMT_REVISION 0 @@ -374,8 +377,13 @@ static u32 get_supported_settings(struct hci_dev *hdev) settings |= MGMT_SETTING_LINK_SECURITY; } - if (hdev->features[4] & LMP_LE) - settings |= MGMT_SETTING_LE; + if (enable_hs) + settings |= MGMT_SETTING_HS; + + if (enable_le) { + if (hdev->features[4] & LMP_LE) + settings |= MGMT_SETTING_LE; + } return settings; } @@ -3421,3 +3429,9 @@ int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev), cmd ? cmd->sk : NULL); } + +module_param(enable_hs, bool, 0644); +MODULE_PARM_DESC(enable_hs, "Enable High Speed support"); + +module_param(enable_le, bool, 0644); +MODULE_PARM_DESC(enable_le, "Enable Low Energy support"); -- cgit From d930650b59be72342bc373ef52006ca99c1dd09e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 20 Feb 2012 23:25:18 +0200 Subject: Bluetooth: mgmt: Add address type parameter to Stop Discovery command This patch adds an address type parameter to the Stop Discovery command which should match the value given to Start Discovery. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 3 +++ net/bluetooth/mgmt.c | 33 ++++++++++++++++++++++----------- 2 files changed, 25 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 7e3d38bfaec3..870a3deab6ea 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -283,6 +283,9 @@ struct mgmt_cp_start_discovery { } __packed; #define MGMT_OP_STOP_DISCOVERY 0x0024 +struct mgmt_cp_stop_discovery { + __u8 type; +} __packed; #define MGMT_OP_CONFIRM_NAME 0x0025 struct mgmt_cp_confirm_name { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f7c2969d8829..3db8525b0293 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2281,8 +2281,9 @@ failed: return err; } -static int stop_discovery(struct sock *sk, u16 index) +static int stop_discovery(struct sock *sk, u16 index, void *data, u16 len) { + struct mgmt_cp_stop_discovery *mgmt_cp = data; struct hci_dev *hdev; struct pending_cmd *cmd; struct hci_cp_remote_name_req_cancel cp; @@ -2291,6 +2292,10 @@ static int stop_discovery(struct sock *sk, u16 index) BT_DBG("hci%u", index); + if (len != sizeof(*mgmt_cp)) + return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, + MGMT_STATUS_INVALID_PARAMS); + hdev = hci_dev_get(index); if (!hdev) return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, @@ -2299,8 +2304,16 @@ static int stop_discovery(struct sock *sk, u16 index) hci_dev_lock(hdev); if (!hci_discovery_active(hdev)) { - err = cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, - MGMT_STATUS_REJECTED); + err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY, + MGMT_STATUS_REJECTED, + &mgmt_cp->type, sizeof(mgmt_cp->type)); + goto unlock; + } + + if (hdev->discovery.type != mgmt_cp->type) { + err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY, + MGMT_STATUS_INVALID_PARAMS, + &mgmt_cp->type, sizeof(mgmt_cp->type)); goto unlock; } @@ -2323,7 +2336,7 @@ static int stop_discovery(struct sock *sk, u16 index) if (!e) { mgmt_pending_remove(cmd); err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY, 0, - NULL, 0); + &mgmt_cp->type, sizeof(mgmt_cp->type)); hci_discovery_set_state(hdev, DISCOVERY_STOPPED); goto unlock; } @@ -2706,7 +2719,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) err = start_discovery(sk, index, cp, len); break; case MGMT_OP_STOP_DISCOVERY: - err = stop_discovery(sk, index); + err = stop_discovery(sk, index, cp, len); break; case MGMT_OP_CONFIRM_NAME: err = confirm_name(sk, index, cp, len); @@ -3369,7 +3382,9 @@ int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status) if (!cmd) return -ENOENT; - err = cmd_status(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status)); + err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status), + &hdev->discovery.type, + sizeof(hdev->discovery.type)); mgmt_pending_remove(cmd); return err; @@ -3389,12 +3404,8 @@ int mgmt_discovering(struct hci_dev *hdev, u8 discovering) if (cmd != NULL) { u8 type = hdev->discovery.type; - if (discovering) - cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, + cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, &type, sizeof(type)); - else - cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, - NULL, 0); mgmt_pending_remove(cmd); } -- cgit From f963e8e9d3652f4a8065d969206707a1c21ff9b0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 20 Feb 2012 23:30:44 +0200 Subject: Bluetooth: mgmt: Add address type parameter to Discovering event This patch adds an address type parameter to the Discovering event. The value matches that given to Start/Stop Discovery. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 4 ++++ net/bluetooth/hci_core.c | 3 +-- net/bluetooth/mgmt.c | 8 ++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 870a3deab6ea..1dbadbe14785 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -402,6 +402,10 @@ struct mgmt_ev_device_found { } __packed; #define MGMT_EV_DISCOVERING 0x0013 +struct mgmt_ev_discovering { + __u8 type; + __u8 discovering; +} __packed; #define MGMT_EV_DEVICE_BLOCKED 0x0014 struct mgmt_ev_device_blocked { diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index cc52e037440e..a7439aeb1f9b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -363,10 +363,9 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state) switch (state) { case DISCOVERY_STOPPED: - hdev->discovery.type = 0; - if (hdev->discovery.state != DISCOVERY_STARTING) mgmt_discovering(hdev, 0); + hdev->discovery.type = 0; break; case DISCOVERY_STARTING: break; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 3db8525b0293..86148b182891 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3392,6 +3392,7 @@ int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status) int mgmt_discovering(struct hci_dev *hdev, u8 discovering) { + struct mgmt_ev_discovering ev; struct pending_cmd *cmd; BT_DBG("%s discovering %u", hdev->name, discovering); @@ -3409,8 +3410,11 @@ int mgmt_discovering(struct hci_dev *hdev, u8 discovering) mgmt_pending_remove(cmd); } - return mgmt_event(MGMT_EV_DISCOVERING, hdev, &discovering, - sizeof(discovering), NULL); + memset(&ev, 0, sizeof(ev)); + ev.type = hdev->discovery.type; + ev.discovering = discovering; + + return mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL); } int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -- cgit From 6d80dfd094a7b286e95cdcac79efeb7bbb4e226f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 20 Feb 2012 23:50:38 +0200 Subject: Bluetooth: mgmt: Add basic support for Set High Speed command This patch adds rudimentary support for the Set High Speed command in the form of a new HCI dev flag (HCI_HS_ENABLED). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + net/bluetooth/mgmt.c | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index ad5e94c757e7..ec370494e568 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -95,6 +95,7 @@ enum { HCI_LE_SCAN, HCI_SSP_ENABLED, + HCI_HS_ENABLED, }; /* HCI ioctl defines */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 86148b182891..edf84c3e6a2b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -418,6 +418,9 @@ static u32 get_current_settings(struct hci_dev *hdev) if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) settings |= MGMT_SETTING_SSP; + if (test_bit(HCI_HS_ENABLED, &hdev->dev_flags)) + settings |= MGMT_SETTING_HS; + return settings; } @@ -1093,6 +1096,41 @@ failed: return err; } +static int set_hs(struct sock *sk, u16 index, void *data, u16 len) +{ + struct mgmt_mode *cp = data; + struct hci_dev *hdev; + int err; + + BT_DBG("request for hci%u", index); + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_SET_HS, + MGMT_STATUS_INVALID_PARAMS); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_SET_HS, + MGMT_STATUS_INVALID_PARAMS); + + if (!enable_hs) { + err = cmd_status(sk, index, MGMT_OP_SET_HS, + MGMT_STATUS_NOT_SUPPORTED); + goto failed; + } + + if (cp->val) + set_bit(HCI_HS_ENABLED, &hdev->dev_flags); + else + clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); + + err = send_settings_rsp(sk, MGMT_OP_SET_HS, hdev); + +failed: + hci_dev_put(hdev); + return err; +} + static int add_uuid(struct sock *sk, u16 index, void *data, u16 len) { struct mgmt_cp_add_uuid *cp = data; @@ -2655,6 +2693,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_SET_SSP: err = set_ssp(sk, index, cp, len); break; + case MGMT_OP_SET_HS: + err = set_hs(sk, index, cp, len); + break; case MGMT_OP_ADD_UUID: err = add_uuid(sk, index, cp, len); break; -- cgit From c059e05353081471908c05029585765614e07cec Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 21 Feb 2012 08:14:26 +0100 Subject: Bluetooth: Fix parameter list for setting local name The parameter list for setting the local name via management interface was missing the short name parameter. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 1dbadbe14785..ac59cdd0fa1b 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -135,6 +135,7 @@ struct mgmt_cp_set_dev_class { #define MGMT_OP_SET_LOCAL_NAME 0x000F struct mgmt_cp_set_local_name { __u8 name[MGMT_MAX_NAME_LENGTH]; + __u8 short_name[MGMT_MAX_SHORT_NAME_LENGTH]; } __packed; #define MGMT_OP_ADD_UUID 0x0010 -- cgit From e248491ac283b516958ca9ab62c8e74b6718bca8 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 15 Feb 2012 15:51:48 +0100 Subject: ftrace: Add enable/disable ftrace_ops control interface Adding a way to temporarily enable/disable ftrace_ops. The change follows the same way as 'global' ftrace_ops are done. Introducing 2 global ftrace_ops - control_ops and ftrace_control_list which take over all ftrace_ops registered with FTRACE_OPS_FL_CONTROL flag. In addition new per cpu flag called 'disabled' is also added to ftrace_ops to provide the control information for each cpu. When ftrace_ops with FTRACE_OPS_FL_CONTROL is registered, it is set as disabled for all cpus. The ftrace_control_list contains all the registered 'control' ftrace_ops. The control_ops provides function which iterates ftrace_control_list and does the check for 'disabled' flag on current cpu. Adding 3 inline functions: ftrace_function_local_disable/ftrace_function_local_enable - enable/disable the ftrace_ops on current cpu ftrace_function_local_disabled - get disabled ftrace_ops::disabled value for current cpu Link: http://lkml.kernel.org/r/1329317514-8131-2-git-send-email-jolsa@redhat.com Acked-by: Frederic Weisbecker Signed-off-by: Jiri Olsa Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 66 +++++++++++++++++++++++++++++ kernel/trace/ftrace.c | 111 +++++++++++++++++++++++++++++++++++++++++++++---- kernel/trace/trace.h | 2 + 3 files changed, 172 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index f33fb3b041c6..64a309d2fbd6 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -31,16 +31,33 @@ ftrace_enable_sysctl(struct ctl_table *table, int write, typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip); +/* + * FTRACE_OPS_FL_* bits denote the state of ftrace_ops struct and are + * set in the flags member. + * + * ENABLED - set/unset when ftrace_ops is registered/unregistered + * GLOBAL - set manualy by ftrace_ops user to denote the ftrace_ops + * is part of the global tracers sharing the same filter + * via set_ftrace_* debugfs files. + * DYNAMIC - set when ftrace_ops is registered to denote dynamically + * allocated ftrace_ops which need special care + * CONTROL - set manualy by ftrace_ops user to denote the ftrace_ops + * could be controled by following calls: + * ftrace_function_local_enable + * ftrace_function_local_disable + */ enum { FTRACE_OPS_FL_ENABLED = 1 << 0, FTRACE_OPS_FL_GLOBAL = 1 << 1, FTRACE_OPS_FL_DYNAMIC = 1 << 2, + FTRACE_OPS_FL_CONTROL = 1 << 3, }; struct ftrace_ops { ftrace_func_t func; struct ftrace_ops *next; unsigned long flags; + int __percpu *disabled; #ifdef CONFIG_DYNAMIC_FTRACE struct ftrace_hash *notrace_hash; struct ftrace_hash *filter_hash; @@ -97,6 +114,55 @@ int register_ftrace_function(struct ftrace_ops *ops); int unregister_ftrace_function(struct ftrace_ops *ops); void clear_ftrace_function(void); +/** + * ftrace_function_local_enable - enable controlled ftrace_ops on current cpu + * + * This function enables tracing on current cpu by decreasing + * the per cpu control variable. + * It must be called with preemption disabled and only on ftrace_ops + * registered with FTRACE_OPS_FL_CONTROL. If called without preemption + * disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled. + */ +static inline void ftrace_function_local_enable(struct ftrace_ops *ops) +{ + if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL))) + return; + + (*this_cpu_ptr(ops->disabled))--; +} + +/** + * ftrace_function_local_disable - enable controlled ftrace_ops on current cpu + * + * This function enables tracing on current cpu by decreasing + * the per cpu control variable. + * It must be called with preemption disabled and only on ftrace_ops + * registered with FTRACE_OPS_FL_CONTROL. If called without preemption + * disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled. + */ +static inline void ftrace_function_local_disable(struct ftrace_ops *ops) +{ + if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL))) + return; + + (*this_cpu_ptr(ops->disabled))++; +} + +/** + * ftrace_function_local_disabled - returns ftrace_ops disabled value + * on current cpu + * + * This function returns value of ftrace_ops::disabled on current cpu. + * It must be called with preemption disabled and only on ftrace_ops + * registered with FTRACE_OPS_FL_CONTROL. If called without preemption + * disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled. + */ +static inline int ftrace_function_local_disabled(struct ftrace_ops *ops) +{ + WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL)); + return *this_cpu_ptr(ops->disabled); +} + extern void ftrace_stub(unsigned long a0, unsigned long a1); #else /* !CONFIG_FUNCTION_TRACER */ diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index d1499e910fe8..f615f974d90e 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -62,6 +62,8 @@ #define FTRACE_HASH_DEFAULT_BITS 10 #define FTRACE_HASH_MAX_BITS 12 +#define FL_GLOBAL_CONTROL_MASK (FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_CONTROL) + /* ftrace_enabled is a method to turn ftrace on or off */ int ftrace_enabled __read_mostly; static int last_ftrace_enabled; @@ -89,12 +91,14 @@ static struct ftrace_ops ftrace_list_end __read_mostly = { }; static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end; +static struct ftrace_ops *ftrace_control_list __read_mostly = &ftrace_list_end; static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end; ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; static ftrace_func_t __ftrace_trace_function_delay __read_mostly = ftrace_stub; ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub; ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub; static struct ftrace_ops global_ops; +static struct ftrace_ops control_ops; static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip); @@ -168,6 +172,32 @@ static void ftrace_test_stop_func(unsigned long ip, unsigned long parent_ip) } #endif +static void control_ops_disable_all(struct ftrace_ops *ops) +{ + int cpu; + + for_each_possible_cpu(cpu) + *per_cpu_ptr(ops->disabled, cpu) = 1; +} + +static int control_ops_alloc(struct ftrace_ops *ops) +{ + int __percpu *disabled; + + disabled = alloc_percpu(int); + if (!disabled) + return -ENOMEM; + + ops->disabled = disabled; + control_ops_disable_all(ops); + return 0; +} + +static void control_ops_free(struct ftrace_ops *ops) +{ + free_percpu(ops->disabled); +} + static void update_global_ops(void) { ftrace_func_t func; @@ -259,6 +289,26 @@ static int remove_ftrace_ops(struct ftrace_ops **list, struct ftrace_ops *ops) return 0; } +static void add_ftrace_list_ops(struct ftrace_ops **list, + struct ftrace_ops *main_ops, + struct ftrace_ops *ops) +{ + int first = *list == &ftrace_list_end; + add_ftrace_ops(list, ops); + if (first) + add_ftrace_ops(&ftrace_ops_list, main_ops); +} + +static int remove_ftrace_list_ops(struct ftrace_ops **list, + struct ftrace_ops *main_ops, + struct ftrace_ops *ops) +{ + int ret = remove_ftrace_ops(list, ops); + if (!ret && *list == &ftrace_list_end) + ret = remove_ftrace_ops(&ftrace_ops_list, main_ops); + return ret; +} + static int __register_ftrace_function(struct ftrace_ops *ops) { if (ftrace_disabled) @@ -270,15 +320,20 @@ static int __register_ftrace_function(struct ftrace_ops *ops) if (WARN_ON(ops->flags & FTRACE_OPS_FL_ENABLED)) return -EBUSY; + /* We don't support both control and global flags set. */ + if ((ops->flags & FL_GLOBAL_CONTROL_MASK) == FL_GLOBAL_CONTROL_MASK) + return -EINVAL; + if (!core_kernel_data((unsigned long)ops)) ops->flags |= FTRACE_OPS_FL_DYNAMIC; if (ops->flags & FTRACE_OPS_FL_GLOBAL) { - int first = ftrace_global_list == &ftrace_list_end; - add_ftrace_ops(&ftrace_global_list, ops); + add_ftrace_list_ops(&ftrace_global_list, &global_ops, ops); ops->flags |= FTRACE_OPS_FL_ENABLED; - if (first) - add_ftrace_ops(&ftrace_ops_list, &global_ops); + } else if (ops->flags & FTRACE_OPS_FL_CONTROL) { + if (control_ops_alloc(ops)) + return -ENOMEM; + add_ftrace_list_ops(&ftrace_control_list, &control_ops, ops); } else add_ftrace_ops(&ftrace_ops_list, ops); @@ -302,11 +357,23 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) return -EINVAL; if (ops->flags & FTRACE_OPS_FL_GLOBAL) { - ret = remove_ftrace_ops(&ftrace_global_list, ops); - if (!ret && ftrace_global_list == &ftrace_list_end) - ret = remove_ftrace_ops(&ftrace_ops_list, &global_ops); + ret = remove_ftrace_list_ops(&ftrace_global_list, + &global_ops, ops); if (!ret) ops->flags &= ~FTRACE_OPS_FL_ENABLED; + } else if (ops->flags & FTRACE_OPS_FL_CONTROL) { + ret = remove_ftrace_list_ops(&ftrace_control_list, + &control_ops, ops); + if (!ret) { + /* + * The ftrace_ops is now removed from the list, + * so there'll be no new users. We must ensure + * all current users are done before we free + * the control data. + */ + synchronize_sched(); + control_ops_free(ops); + } } else ret = remove_ftrace_ops(&ftrace_ops_list, ops); @@ -3873,6 +3940,36 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) #endif /* CONFIG_DYNAMIC_FTRACE */ +static void +ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip) +{ + struct ftrace_ops *op; + + if (unlikely(trace_recursion_test(TRACE_CONTROL_BIT))) + return; + + /* + * Some of the ops may be dynamically allocated, + * they must be freed after a synchronize_sched(). + */ + preempt_disable_notrace(); + trace_recursion_set(TRACE_CONTROL_BIT); + op = rcu_dereference_raw(ftrace_control_list); + while (op != &ftrace_list_end) { + if (!ftrace_function_local_disabled(op) && + ftrace_ops_test(op, ip)) + op->func(ip, parent_ip); + + op = rcu_dereference_raw(op->next); + }; + trace_recursion_clear(TRACE_CONTROL_BIT); + preempt_enable_notrace(); +} + +static struct ftrace_ops control_ops = { + .func = ftrace_ops_control_func, +}; + static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip) { diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index b93ecbadad6d..55c6ea00f28a 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -288,6 +288,8 @@ struct tracer { /* for function tracing recursion */ #define TRACE_INTERNAL_BIT (1<<11) #define TRACE_GLOBAL_BIT (1<<12) +#define TRACE_CONTROL_BIT (1<<13) + /* * Abuse of the trace_recursion. * As we need a way to maintain state if we are tracing the function -- cgit From ceec0b6fc7cd43b38a40c2d40223f9cd0616f0cd Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 15 Feb 2012 15:51:49 +0100 Subject: ftrace, perf: Add open/close tracepoint perf registration actions Adding TRACE_REG_PERF_OPEN and TRACE_REG_PERF_CLOSE to differentiate register/unregister from open/close actions. The register/unregister actions are invoked for the first/last tracepoint user when opening/closing the event. The open/close actions are invoked for each tracepoint user when opening/closing the event. Link: http://lkml.kernel.org/r/1329317514-8131-3-git-send-email-jolsa@redhat.com Acked-by: Frederic Weisbecker Signed-off-by: Jiri Olsa Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 6 ++- kernel/trace/trace_event_perf.c | 116 ++++++++++++++++++++++++++-------------- kernel/trace/trace_events.c | 10 ++-- kernel/trace/trace_kprobe.c | 6 ++- kernel/trace/trace_syscalls.c | 14 +++-- 5 files changed, 101 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index c3da42dd22ba..195e3606ddd7 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -146,6 +146,8 @@ enum trace_reg { TRACE_REG_UNREGISTER, TRACE_REG_PERF_REGISTER, TRACE_REG_PERF_UNREGISTER, + TRACE_REG_PERF_OPEN, + TRACE_REG_PERF_CLOSE, }; struct ftrace_event_call; @@ -157,7 +159,7 @@ struct ftrace_event_class { void *perf_probe; #endif int (*reg)(struct ftrace_event_call *event, - enum trace_reg type); + enum trace_reg type, void *data); int (*define_fields)(struct ftrace_event_call *); struct list_head *(*get_fields)(struct ftrace_event_call *); struct list_head fields; @@ -165,7 +167,7 @@ struct ftrace_event_class { }; extern int ftrace_event_reg(struct ftrace_event_call *event, - enum trace_reg type); + enum trace_reg type, void *data); enum { TRACE_EVENT_FL_ENABLED_BIT, diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index 19a359d5e6d5..0cfcc37f63de 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c @@ -44,23 +44,17 @@ static int perf_trace_event_perm(struct ftrace_event_call *tp_event, return 0; } -static int perf_trace_event_init(struct ftrace_event_call *tp_event, - struct perf_event *p_event) +static int perf_trace_event_reg(struct ftrace_event_call *tp_event, + struct perf_event *p_event) { struct hlist_head __percpu *list; - int ret; + int ret = -ENOMEM; int cpu; - ret = perf_trace_event_perm(tp_event, p_event); - if (ret) - return ret; - p_event->tp_event = tp_event; if (tp_event->perf_refcount++ > 0) return 0; - ret = -ENOMEM; - list = alloc_percpu(struct hlist_head); if (!list) goto fail; @@ -83,7 +77,7 @@ static int perf_trace_event_init(struct ftrace_event_call *tp_event, } } - ret = tp_event->class->reg(tp_event, TRACE_REG_PERF_REGISTER); + ret = tp_event->class->reg(tp_event, TRACE_REG_PERF_REGISTER, NULL); if (ret) goto fail; @@ -108,6 +102,69 @@ fail: return ret; } +static void perf_trace_event_unreg(struct perf_event *p_event) +{ + struct ftrace_event_call *tp_event = p_event->tp_event; + int i; + + if (--tp_event->perf_refcount > 0) + goto out; + + tp_event->class->reg(tp_event, TRACE_REG_PERF_UNREGISTER, NULL); + + /* + * Ensure our callback won't be called anymore. The buffers + * will be freed after that. + */ + tracepoint_synchronize_unregister(); + + free_percpu(tp_event->perf_events); + tp_event->perf_events = NULL; + + if (!--total_ref_count) { + for (i = 0; i < PERF_NR_CONTEXTS; i++) { + free_percpu(perf_trace_buf[i]); + perf_trace_buf[i] = NULL; + } + } +out: + module_put(tp_event->mod); +} + +static int perf_trace_event_open(struct perf_event *p_event) +{ + struct ftrace_event_call *tp_event = p_event->tp_event; + return tp_event->class->reg(tp_event, TRACE_REG_PERF_OPEN, p_event); +} + +static void perf_trace_event_close(struct perf_event *p_event) +{ + struct ftrace_event_call *tp_event = p_event->tp_event; + tp_event->class->reg(tp_event, TRACE_REG_PERF_CLOSE, p_event); +} + +static int perf_trace_event_init(struct ftrace_event_call *tp_event, + struct perf_event *p_event) +{ + int ret; + + ret = perf_trace_event_perm(tp_event, p_event); + if (ret) + return ret; + + ret = perf_trace_event_reg(tp_event, p_event); + if (ret) + return ret; + + ret = perf_trace_event_open(p_event); + if (ret) { + perf_trace_event_unreg(p_event); + return ret; + } + + return 0; +} + int perf_trace_init(struct perf_event *p_event) { struct ftrace_event_call *tp_event; @@ -130,6 +187,14 @@ int perf_trace_init(struct perf_event *p_event) return ret; } +void perf_trace_destroy(struct perf_event *p_event) +{ + mutex_lock(&event_mutex); + perf_trace_event_close(p_event); + perf_trace_event_unreg(p_event); + mutex_unlock(&event_mutex); +} + int perf_trace_add(struct perf_event *p_event, int flags) { struct ftrace_event_call *tp_event = p_event->tp_event; @@ -154,37 +219,6 @@ void perf_trace_del(struct perf_event *p_event, int flags) hlist_del_rcu(&p_event->hlist_entry); } -void perf_trace_destroy(struct perf_event *p_event) -{ - struct ftrace_event_call *tp_event = p_event->tp_event; - int i; - - mutex_lock(&event_mutex); - if (--tp_event->perf_refcount > 0) - goto out; - - tp_event->class->reg(tp_event, TRACE_REG_PERF_UNREGISTER); - - /* - * Ensure our callback won't be called anymore. The buffers - * will be freed after that. - */ - tracepoint_synchronize_unregister(); - - free_percpu(tp_event->perf_events); - tp_event->perf_events = NULL; - - if (!--total_ref_count) { - for (i = 0; i < PERF_NR_CONTEXTS; i++) { - free_percpu(perf_trace_buf[i]); - perf_trace_buf[i] = NULL; - } - } -out: - module_put(tp_event->mod); - mutex_unlock(&event_mutex); -} - __kprobes void *perf_trace_buf_prepare(int size, unsigned short type, struct pt_regs *regs, int *rctxp) { diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index c212a7f934ec..5138fea37908 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -147,7 +147,8 @@ int trace_event_raw_init(struct ftrace_event_call *call) } EXPORT_SYMBOL_GPL(trace_event_raw_init); -int ftrace_event_reg(struct ftrace_event_call *call, enum trace_reg type) +int ftrace_event_reg(struct ftrace_event_call *call, + enum trace_reg type, void *data) { switch (type) { case TRACE_REG_REGISTER: @@ -170,6 +171,9 @@ int ftrace_event_reg(struct ftrace_event_call *call, enum trace_reg type) call->class->perf_probe, call); return 0; + case TRACE_REG_PERF_OPEN: + case TRACE_REG_PERF_CLOSE: + return 0; #endif } return 0; @@ -209,7 +213,7 @@ static int ftrace_event_enable_disable(struct ftrace_event_call *call, tracing_stop_cmdline_record(); call->flags &= ~TRACE_EVENT_FL_RECORDED_CMD; } - call->class->reg(call, TRACE_REG_UNREGISTER); + call->class->reg(call, TRACE_REG_UNREGISTER, NULL); } break; case 1: @@ -218,7 +222,7 @@ static int ftrace_event_enable_disable(struct ftrace_event_call *call, tracing_start_cmdline_record(); call->flags |= TRACE_EVENT_FL_RECORDED_CMD; } - ret = call->class->reg(call, TRACE_REG_REGISTER); + ret = call->class->reg(call, TRACE_REG_REGISTER, NULL); if (ret) { tracing_stop_cmdline_record(); pr_info("event trace: Could not enable event " diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 00d527c945a4..5667f8958cc9 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -1892,7 +1892,8 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri, #endif /* CONFIG_PERF_EVENTS */ static __kprobes -int kprobe_register(struct ftrace_event_call *event, enum trace_reg type) +int kprobe_register(struct ftrace_event_call *event, + enum trace_reg type, void *data) { struct trace_probe *tp = (struct trace_probe *)event->data; @@ -1909,6 +1910,9 @@ int kprobe_register(struct ftrace_event_call *event, enum trace_reg type) case TRACE_REG_PERF_UNREGISTER: disable_trace_probe(tp, TP_FLAG_PROFILE); return 0; + case TRACE_REG_PERF_OPEN: + case TRACE_REG_PERF_CLOSE: + return 0; #endif } return 0; diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 43500153dd1e..e23515f51ed4 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -17,9 +17,9 @@ static DECLARE_BITMAP(enabled_enter_syscalls, NR_syscalls); static DECLARE_BITMAP(enabled_exit_syscalls, NR_syscalls); static int syscall_enter_register(struct ftrace_event_call *event, - enum trace_reg type); + enum trace_reg type, void *data); static int syscall_exit_register(struct ftrace_event_call *event, - enum trace_reg type); + enum trace_reg type, void *data); static int syscall_enter_define_fields(struct ftrace_event_call *call); static int syscall_exit_define_fields(struct ftrace_event_call *call); @@ -649,7 +649,7 @@ void perf_sysexit_disable(struct ftrace_event_call *call) #endif /* CONFIG_PERF_EVENTS */ static int syscall_enter_register(struct ftrace_event_call *event, - enum trace_reg type) + enum trace_reg type, void *data) { switch (type) { case TRACE_REG_REGISTER: @@ -664,13 +664,16 @@ static int syscall_enter_register(struct ftrace_event_call *event, case TRACE_REG_PERF_UNREGISTER: perf_sysenter_disable(event); return 0; + case TRACE_REG_PERF_OPEN: + case TRACE_REG_PERF_CLOSE: + return 0; #endif } return 0; } static int syscall_exit_register(struct ftrace_event_call *event, - enum trace_reg type) + enum trace_reg type, void *data) { switch (type) { case TRACE_REG_REGISTER: @@ -685,6 +688,9 @@ static int syscall_exit_register(struct ftrace_event_call *event, case TRACE_REG_PERF_UNREGISTER: perf_sysexit_disable(event); return 0; + case TRACE_REG_PERF_OPEN: + case TRACE_REG_PERF_CLOSE: + return 0; #endif } return 0; -- cgit From 489c75c3b333dfda4c8d2b7ad1b00e5da024bfa7 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 15 Feb 2012 15:51:50 +0100 Subject: ftrace, perf: Add add/del tracepoint perf registration actions Adding TRACE_REG_PERF_ADD and TRACE_REG_PERF_DEL to handle perf event schedule in/out actions. The add action is invoked for when the perf event is scheduled in, while the del action is invoked when the event is scheduled out. Link: http://lkml.kernel.org/r/1329317514-8131-4-git-send-email-jolsa@redhat.com Acked-by: Frederic Weisbecker Signed-off-by: Jiri Olsa Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 2 ++ kernel/trace/trace_event_perf.c | 4 +++- kernel/trace/trace_events.c | 2 ++ kernel/trace/trace_kprobe.c | 2 ++ kernel/trace/trace_syscalls.c | 4 ++++ 5 files changed, 13 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 195e3606ddd7..2bf677cb2d6f 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -148,6 +148,8 @@ enum trace_reg { TRACE_REG_PERF_UNREGISTER, TRACE_REG_PERF_OPEN, TRACE_REG_PERF_CLOSE, + TRACE_REG_PERF_ADD, + TRACE_REG_PERF_DEL, }; struct ftrace_event_call; diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index 0cfcc37f63de..d72af0b03822 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c @@ -211,12 +211,14 @@ int perf_trace_add(struct perf_event *p_event, int flags) list = this_cpu_ptr(pcpu_list); hlist_add_head_rcu(&p_event->hlist_entry, list); - return 0; + return tp_event->class->reg(tp_event, TRACE_REG_PERF_ADD, p_event); } void perf_trace_del(struct perf_event *p_event, int flags) { + struct ftrace_event_call *tp_event = p_event->tp_event; hlist_del_rcu(&p_event->hlist_entry); + tp_event->class->reg(tp_event, TRACE_REG_PERF_DEL, p_event); } __kprobes void *perf_trace_buf_prepare(int size, unsigned short type, diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 5138fea37908..079a93ae8a9d 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -173,6 +173,8 @@ int ftrace_event_reg(struct ftrace_event_call *call, return 0; case TRACE_REG_PERF_OPEN: case TRACE_REG_PERF_CLOSE: + case TRACE_REG_PERF_ADD: + case TRACE_REG_PERF_DEL: return 0; #endif } diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 5667f8958cc9..580a05ec926b 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -1912,6 +1912,8 @@ int kprobe_register(struct ftrace_event_call *event, return 0; case TRACE_REG_PERF_OPEN: case TRACE_REG_PERF_CLOSE: + case TRACE_REG_PERF_ADD: + case TRACE_REG_PERF_DEL: return 0; #endif } diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index e23515f51ed4..96fc73369099 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -666,6 +666,8 @@ static int syscall_enter_register(struct ftrace_event_call *event, return 0; case TRACE_REG_PERF_OPEN: case TRACE_REG_PERF_CLOSE: + case TRACE_REG_PERF_ADD: + case TRACE_REG_PERF_DEL: return 0; #endif } @@ -690,6 +692,8 @@ static int syscall_exit_register(struct ftrace_event_call *event, return 0; case TRACE_REG_PERF_OPEN: case TRACE_REG_PERF_CLOSE: + case TRACE_REG_PERF_ADD: + case TRACE_REG_PERF_DEL: return 0; #endif } -- cgit From ced39002f5ea736b716ae233fb68b26d59783912 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 15 Feb 2012 15:51:52 +0100 Subject: ftrace, perf: Add support to use function tracepoint in perf Adding perf registration support for the ftrace function event, so it is now possible to register it via perf interface. The perf_event struct statically contains ftrace_ops as a handle for function tracer. The function tracer is registered/unregistered in open/close actions. To be efficient, we enable/disable ftrace_ops each time the traced process is scheduled in/out (via TRACE_REG_PERF_(ADD|DELL) handlers). This way tracing is enabled only when the process is running. Intentionally using this way instead of the event's hw state PERF_HES_STOPPED, which would not disable the ftrace_ops. It is now possible to use function trace within perf commands like: perf record -e ftrace:function ls perf stat -e ftrace:function ls Allowed only for root. Link: http://lkml.kernel.org/r/1329317514-8131-6-git-send-email-jolsa@redhat.com Acked-by: Frederic Weisbecker Signed-off-by: Jiri Olsa Signed-off-by: Steven Rostedt --- include/linux/perf_event.h | 3 ++ kernel/trace/trace.h | 11 ++++++ kernel/trace/trace_entries.h | 6 ++- kernel/trace/trace_event_perf.c | 86 +++++++++++++++++++++++++++++++++++++++++ kernel/trace/trace_export.c | 5 +++ 5 files changed, 109 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 412b790f5da6..92a056f6d18d 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -859,6 +859,9 @@ struct perf_event { #ifdef CONFIG_EVENT_TRACING struct ftrace_event_call *tp_event; struct event_filter *filter; +#ifdef CONFIG_FUNCTION_TRACER + struct ftrace_ops ftrace_ops; +#endif #endif #ifdef CONFIG_CGROUP_PERF diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 638476af8d7b..76a1c5094bbf 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -595,6 +595,8 @@ static inline int ftrace_trace_task(struct task_struct *task) static inline int ftrace_is_dead(void) { return 0; } #endif +int ftrace_event_is_function(struct ftrace_event_call *call); + /* * struct trace_parser - servers for reading the user input separated by spaces * @cont: set if the input is not complete - no final space char was found @@ -832,4 +834,13 @@ extern const char *__stop___trace_bprintk_fmt[]; FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print)) #include "trace_entries.h" +#ifdef CONFIG_PERF_EVENTS +#ifdef CONFIG_FUNCTION_TRACER +int perf_ftrace_event_register(struct ftrace_event_call *call, + enum trace_reg type, void *data); +#else +#define perf_ftrace_event_register NULL +#endif /* CONFIG_FUNCTION_TRACER */ +#endif /* CONFIG_PERF_EVENTS */ + #endif /* _LINUX_KERNEL_TRACE_H */ diff --git a/kernel/trace/trace_entries.h b/kernel/trace/trace_entries.h index 93365907f219..47db7eda0531 100644 --- a/kernel/trace/trace_entries.h +++ b/kernel/trace/trace_entries.h @@ -55,7 +55,7 @@ /* * Function trace entry - function address and parent function address: */ -FTRACE_ENTRY(function, ftrace_entry, +FTRACE_ENTRY_REG(function, ftrace_entry, TRACE_FN, @@ -64,7 +64,9 @@ FTRACE_ENTRY(function, ftrace_entry, __field( unsigned long, parent_ip ) ), - F_printk(" %lx <-- %lx", __entry->ip, __entry->parent_ip) + F_printk(" %lx <-- %lx", __entry->ip, __entry->parent_ip), + + perf_ftrace_event_register ); /* Function call entry */ diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index d72af0b03822..fdeeb5c49627 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c @@ -24,6 +24,11 @@ static int total_ref_count; static int perf_trace_event_perm(struct ftrace_event_call *tp_event, struct perf_event *p_event) { + /* The ftrace function trace is allowed only for root. */ + if (ftrace_event_is_function(tp_event) && + perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN)) + return -EPERM; + /* No tracing, just counting, so no obvious leak */ if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW)) return 0; @@ -250,3 +255,84 @@ __kprobes void *perf_trace_buf_prepare(int size, unsigned short type, return raw_data; } EXPORT_SYMBOL_GPL(perf_trace_buf_prepare); + +#ifdef CONFIG_FUNCTION_TRACER +static void +perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip) +{ + struct ftrace_entry *entry; + struct hlist_head *head; + struct pt_regs regs; + int rctx; + +#define ENTRY_SIZE (ALIGN(sizeof(struct ftrace_entry) + sizeof(u32), \ + sizeof(u64)) - sizeof(u32)) + + BUILD_BUG_ON(ENTRY_SIZE > PERF_MAX_TRACE_SIZE); + + perf_fetch_caller_regs(®s); + + entry = perf_trace_buf_prepare(ENTRY_SIZE, TRACE_FN, NULL, &rctx); + if (!entry) + return; + + entry->ip = ip; + entry->parent_ip = parent_ip; + + head = this_cpu_ptr(event_function.perf_events); + perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, 0, + 1, ®s, head); + +#undef ENTRY_SIZE +} + +static int perf_ftrace_function_register(struct perf_event *event) +{ + struct ftrace_ops *ops = &event->ftrace_ops; + + ops->flags |= FTRACE_OPS_FL_CONTROL; + ops->func = perf_ftrace_function_call; + return register_ftrace_function(ops); +} + +static int perf_ftrace_function_unregister(struct perf_event *event) +{ + struct ftrace_ops *ops = &event->ftrace_ops; + return unregister_ftrace_function(ops); +} + +static void perf_ftrace_function_enable(struct perf_event *event) +{ + ftrace_function_local_enable(&event->ftrace_ops); +} + +static void perf_ftrace_function_disable(struct perf_event *event) +{ + ftrace_function_local_disable(&event->ftrace_ops); +} + +int perf_ftrace_event_register(struct ftrace_event_call *call, + enum trace_reg type, void *data) +{ + switch (type) { + case TRACE_REG_REGISTER: + case TRACE_REG_UNREGISTER: + break; + case TRACE_REG_PERF_REGISTER: + case TRACE_REG_PERF_UNREGISTER: + return 0; + case TRACE_REG_PERF_OPEN: + return perf_ftrace_function_register(data); + case TRACE_REG_PERF_CLOSE: + return perf_ftrace_function_unregister(data); + case TRACE_REG_PERF_ADD: + perf_ftrace_function_enable(data); + return 0; + case TRACE_REG_PERF_DEL: + perf_ftrace_function_disable(data); + return 0; + } + + return -EINVAL; +} +#endif /* CONFIG_FUNCTION_TRACER */ diff --git a/kernel/trace/trace_export.c b/kernel/trace/trace_export.c index f74de861cde9..a3dbee6d04cd 100644 --- a/kernel/trace/trace_export.c +++ b/kernel/trace/trace_export.c @@ -184,4 +184,9 @@ __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call; FTRACE_ENTRY_REG(call, struct_name, etype, \ PARAMS(tstruct), PARAMS(print), NULL) +int ftrace_event_is_function(struct ftrace_event_call *call) +{ + return call == &event_function; +} + #include "trace_entries.h" -- cgit From 02aa3162edaa166a01d193f80ccde890be8b55da Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 15 Feb 2012 15:51:53 +0100 Subject: ftrace: Allow to specify filter field type for ftrace events Adding FILTER_TRACE_FN event field type for function tracepoint event, so it can be properly recognized within filtering code. Currently all fields of ftrace subsystem events share the common field type FILTER_OTHER. Since the function trace fields need special care within the filtering code we need to recognize it properly, hence adding the FILTER_TRACE_FN event type. Adding filter parameter to the FTRACE_ENTRY macro, to specify the filter field type for the event. Link: http://lkml.kernel.org/r/1329317514-8131-7-git-send-email-jolsa@redhat.com Signed-off-by: Jiri Olsa Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 1 + kernel/trace/trace.h | 23 +++++++++-------- kernel/trace/trace_entries.h | 48 +++++++++++++++++++++++++---------- kernel/trace/trace_events_filter.c | 7 +++++- kernel/trace/trace_export.c | 51 +++++++++++++++++++++----------------- 5 files changed, 83 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 2bf677cb2d6f..dd478fc8f9f5 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -245,6 +245,7 @@ enum { FILTER_STATIC_STRING, FILTER_DYN_STRING, FILTER_PTR_STRING, + FILTER_TRACE_FN, }; #define EVENT_STORAGE_SIZE 128 diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 76a1c5094bbf..29f93cd434a5 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -56,21 +56,23 @@ enum trace_type { #define F_STRUCT(args...) args #undef FTRACE_ENTRY -#define FTRACE_ENTRY(name, struct_name, id, tstruct, print) \ - struct struct_name { \ - struct trace_entry ent; \ - tstruct \ +#define FTRACE_ENTRY(name, struct_name, id, tstruct, print, filter) \ + struct struct_name { \ + struct trace_entry ent; \ + tstruct \ } #undef TP_ARGS #define TP_ARGS(args...) args #undef FTRACE_ENTRY_DUP -#define FTRACE_ENTRY_DUP(name, name_struct, id, tstruct, printk) +#define FTRACE_ENTRY_DUP(name, name_struct, id, tstruct, printk, filter) #undef FTRACE_ENTRY_REG -#define FTRACE_ENTRY_REG(name, struct_name, id, tstruct, print, regfn) \ - FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print)) +#define FTRACE_ENTRY_REG(name, struct_name, id, tstruct, print, \ + filter, regfn) \ + FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print), \ + filter) #include "trace_entries.h" @@ -826,12 +828,13 @@ extern const char *__start___trace_bprintk_fmt[]; extern const char *__stop___trace_bprintk_fmt[]; #undef FTRACE_ENTRY -#define FTRACE_ENTRY(call, struct_name, id, tstruct, print) \ +#define FTRACE_ENTRY(call, struct_name, id, tstruct, print, filter) \ extern struct ftrace_event_call \ __attribute__((__aligned__(4))) event_##call; #undef FTRACE_ENTRY_DUP -#define FTRACE_ENTRY_DUP(call, struct_name, id, tstruct, print) \ - FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print)) +#define FTRACE_ENTRY_DUP(call, struct_name, id, tstruct, print, filter) \ + FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print), \ + filter) #include "trace_entries.h" #ifdef CONFIG_PERF_EVENTS diff --git a/kernel/trace/trace_entries.h b/kernel/trace/trace_entries.h index 47db7eda0531..d91eb0541b3a 100644 --- a/kernel/trace/trace_entries.h +++ b/kernel/trace/trace_entries.h @@ -66,6 +66,8 @@ FTRACE_ENTRY_REG(function, ftrace_entry, F_printk(" %lx <-- %lx", __entry->ip, __entry->parent_ip), + FILTER_TRACE_FN, + perf_ftrace_event_register ); @@ -80,7 +82,9 @@ FTRACE_ENTRY(funcgraph_entry, ftrace_graph_ent_entry, __field_desc( int, graph_ent, depth ) ), - F_printk("--> %lx (%d)", __entry->func, __entry->depth) + F_printk("--> %lx (%d)", __entry->func, __entry->depth), + + FILTER_OTHER ); /* Function return entry */ @@ -100,7 +104,9 @@ FTRACE_ENTRY(funcgraph_exit, ftrace_graph_ret_entry, F_printk("<-- %lx (%d) (start: %llx end: %llx) over: %d", __entry->func, __entry->depth, __entry->calltime, __entry->rettime, - __entry->depth) + __entry->depth), + + FILTER_OTHER ); /* @@ -129,8 +135,9 @@ FTRACE_ENTRY(context_switch, ctx_switch_entry, F_printk("%u:%u:%u ==> %u:%u:%u [%03u]", __entry->prev_pid, __entry->prev_prio, __entry->prev_state, __entry->next_pid, __entry->next_prio, __entry->next_state, - __entry->next_cpu - ) + __entry->next_cpu), + + FILTER_OTHER ); /* @@ -148,8 +155,9 @@ FTRACE_ENTRY_DUP(wakeup, ctx_switch_entry, F_printk("%u:%u:%u ==+ %u:%u:%u [%03u]", __entry->prev_pid, __entry->prev_prio, __entry->prev_state, __entry->next_pid, __entry->next_prio, __entry->next_state, - __entry->next_cpu - ) + __entry->next_cpu), + + FILTER_OTHER ); /* @@ -171,7 +179,9 @@ FTRACE_ENTRY(kernel_stack, stack_entry, "\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n", __entry->caller[0], __entry->caller[1], __entry->caller[2], __entry->caller[3], __entry->caller[4], __entry->caller[5], - __entry->caller[6], __entry->caller[7]) + __entry->caller[6], __entry->caller[7]), + + FILTER_OTHER ); FTRACE_ENTRY(user_stack, userstack_entry, @@ -187,7 +197,9 @@ FTRACE_ENTRY(user_stack, userstack_entry, "\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n", __entry->caller[0], __entry->caller[1], __entry->caller[2], __entry->caller[3], __entry->caller[4], __entry->caller[5], - __entry->caller[6], __entry->caller[7]) + __entry->caller[6], __entry->caller[7]), + + FILTER_OTHER ); /* @@ -204,7 +216,9 @@ FTRACE_ENTRY(bprint, bprint_entry, ), F_printk("%08lx fmt:%p", - __entry->ip, __entry->fmt) + __entry->ip, __entry->fmt), + + FILTER_OTHER ); FTRACE_ENTRY(print, print_entry, @@ -217,7 +231,9 @@ FTRACE_ENTRY(print, print_entry, ), F_printk("%08lx %s", - __entry->ip, __entry->buf) + __entry->ip, __entry->buf), + + FILTER_OTHER ); FTRACE_ENTRY(mmiotrace_rw, trace_mmiotrace_rw, @@ -236,7 +252,9 @@ FTRACE_ENTRY(mmiotrace_rw, trace_mmiotrace_rw, F_printk("%lx %lx %lx %d %x %x", (unsigned long)__entry->phys, __entry->value, __entry->pc, - __entry->map_id, __entry->opcode, __entry->width) + __entry->map_id, __entry->opcode, __entry->width), + + FILTER_OTHER ); FTRACE_ENTRY(mmiotrace_map, trace_mmiotrace_map, @@ -254,7 +272,9 @@ FTRACE_ENTRY(mmiotrace_map, trace_mmiotrace_map, F_printk("%lx %lx %lx %d %x", (unsigned long)__entry->phys, __entry->virt, __entry->len, - __entry->map_id, __entry->opcode) + __entry->map_id, __entry->opcode), + + FILTER_OTHER ); @@ -274,6 +294,8 @@ FTRACE_ENTRY(branch, trace_branch, F_printk("%u:%s:%s (%u)", __entry->line, - __entry->func, __entry->file, __entry->correct) + __entry->func, __entry->file, __entry->correct), + + FILTER_OTHER ); diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 76afaee99dbc..3da3d0ec3584 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -899,6 +899,11 @@ int filter_assign_type(const char *type) return FILTER_OTHER; } +static bool is_function_field(struct ftrace_event_field *field) +{ + return field->filter_type == FILTER_TRACE_FN; +} + static bool is_string_field(struct ftrace_event_field *field) { return field->filter_type == FILTER_DYN_STRING || @@ -986,7 +991,7 @@ static int init_pred(struct filter_parse_state *ps, fn = filter_pred_strloc; else fn = filter_pred_pchar; - } else { + } else if (!is_function_field(field)) { if (field->is_signed) ret = strict_strtoll(pred->regex.pattern, 0, &val); else diff --git a/kernel/trace/trace_export.c b/kernel/trace/trace_export.c index a3dbee6d04cd..7b46c9bd22ae 100644 --- a/kernel/trace/trace_export.c +++ b/kernel/trace/trace_export.c @@ -23,8 +23,10 @@ * function and thus become accesible via perf. */ #undef FTRACE_ENTRY_REG -#define FTRACE_ENTRY_REG(name, struct_name, id, tstruct, print, regfn) \ - FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print)) +#define FTRACE_ENTRY_REG(name, struct_name, id, tstruct, print, \ + filter, regfn) \ + FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print), \ + filter) /* not needed for this file */ #undef __field_struct @@ -52,21 +54,22 @@ #define F_printk(fmt, args...) fmt, args #undef FTRACE_ENTRY -#define FTRACE_ENTRY(name, struct_name, id, tstruct, print) \ -struct ____ftrace_##name { \ - tstruct \ -}; \ -static void __always_unused ____ftrace_check_##name(void) \ -{ \ - struct ____ftrace_##name *__entry = NULL; \ - \ - /* force compile-time check on F_printk() */ \ - printk(print); \ +#define FTRACE_ENTRY(name, struct_name, id, tstruct, print, filter) \ +struct ____ftrace_##name { \ + tstruct \ +}; \ +static void __always_unused ____ftrace_check_##name(void) \ +{ \ + struct ____ftrace_##name *__entry = NULL; \ + \ + /* force compile-time check on F_printk() */ \ + printk(print); \ } #undef FTRACE_ENTRY_DUP -#define FTRACE_ENTRY_DUP(name, struct_name, id, tstruct, print) \ - FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print)) +#define FTRACE_ENTRY_DUP(name, struct_name, id, tstruct, print, filter) \ + FTRACE_ENTRY(name, struct_name, id, PARAMS(tstruct), PARAMS(print), \ + filter) #include "trace_entries.h" @@ -75,7 +78,7 @@ static void __always_unused ____ftrace_check_##name(void) \ ret = trace_define_field(event_call, #type, #item, \ offsetof(typeof(field), item), \ sizeof(field.item), \ - is_signed_type(type), FILTER_OTHER); \ + is_signed_type(type), filter_type); \ if (ret) \ return ret; @@ -85,7 +88,7 @@ static void __always_unused ____ftrace_check_##name(void) \ offsetof(typeof(field), \ container.item), \ sizeof(field.container.item), \ - is_signed_type(type), FILTER_OTHER); \ + is_signed_type(type), filter_type); \ if (ret) \ return ret; @@ -99,7 +102,7 @@ static void __always_unused ____ftrace_check_##name(void) \ ret = trace_define_field(event_call, event_storage, #item, \ offsetof(typeof(field), item), \ sizeof(field.item), \ - is_signed_type(type), FILTER_OTHER); \ + is_signed_type(type), filter_type); \ mutex_unlock(&event_storage_mutex); \ if (ret) \ return ret; \ @@ -112,7 +115,7 @@ static void __always_unused ____ftrace_check_##name(void) \ offsetof(typeof(field), \ container.item), \ sizeof(field.container.item), \ - is_signed_type(type), FILTER_OTHER); \ + is_signed_type(type), filter_type); \ if (ret) \ return ret; @@ -120,17 +123,18 @@ static void __always_unused ____ftrace_check_##name(void) \ #define __dynamic_array(type, item) \ ret = trace_define_field(event_call, #type, #item, \ offsetof(typeof(field), item), \ - 0, is_signed_type(type), FILTER_OTHER);\ + 0, is_signed_type(type), filter_type);\ if (ret) \ return ret; #undef FTRACE_ENTRY -#define FTRACE_ENTRY(name, struct_name, id, tstruct, print) \ +#define FTRACE_ENTRY(name, struct_name, id, tstruct, print, filter) \ int \ ftrace_define_fields_##name(struct ftrace_event_call *event_call) \ { \ struct struct_name field; \ int ret; \ + int filter_type = filter; \ \ tstruct; \ \ @@ -161,7 +165,8 @@ ftrace_define_fields_##name(struct ftrace_event_call *event_call) \ #define F_printk(fmt, args...) #fmt ", " __stringify(args) #undef FTRACE_ENTRY_REG -#define FTRACE_ENTRY_REG(call, struct_name, etype, tstruct, print, regfn)\ +#define FTRACE_ENTRY_REG(call, struct_name, etype, tstruct, print, filter,\ + regfn) \ \ struct ftrace_event_class event_class_ftrace_##call = { \ .system = __stringify(TRACE_SYSTEM), \ @@ -180,9 +185,9 @@ struct ftrace_event_call __used \ __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call; #undef FTRACE_ENTRY -#define FTRACE_ENTRY(call, struct_name, etype, tstruct, print) \ +#define FTRACE_ENTRY(call, struct_name, etype, tstruct, print, filter) \ FTRACE_ENTRY_REG(call, struct_name, etype, \ - PARAMS(tstruct), PARAMS(print), NULL) + PARAMS(tstruct), PARAMS(print), filter, NULL) int ftrace_event_is_function(struct ftrace_event_call *call) { -- cgit From 5500fa51199aee770ce53718853732600543619e Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 15 Feb 2012 15:51:54 +0100 Subject: ftrace, perf: Add filter support for function trace event Adding support to filter function trace event via perf interface. It is now possible to use filter interface in the perf tool like: perf record -e ftrace:function --filter="(ip == mm_*)" ls The filter syntax is restricted to the the 'ip' field only, and following operators are accepted '==' '!=' '||', ending up with the filter strings like: ip == f1[, ]f2 ... || ip != f3[, ]f4 ... with comma ',' or space ' ' as a function separator. If the space ' ' is used as a separator, the right side of the assignment needs to be enclosed in double quotes '"', e.g.: perf record -e ftrace:function --filter '(ip == do_execve,sys_*,ext*)' ls perf record -e ftrace:function --filter '(ip == "do_execve,sys_*,ext*")' ls perf record -e ftrace:function --filter '(ip == "do_execve sys_* ext*")' ls The '==' operator adds trace filter with same effect as would be added via set_ftrace_filter file. The '!=' operator adds trace filter with same effect as would be added via set_ftrace_notrace file. The right side of the '!=', '==' operators is list of functions or regexp. to be added to filter separated by space. The '||' operator is used for connecting multiple filter definitions together. It is possible to have more than one '==' and '!=' operators within one filter string. Link: http://lkml.kernel.org/r/1329317514-8131-8-git-send-email-jolsa@redhat.com Signed-off-by: Jiri Olsa Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 7 +- kernel/trace/ftrace.c | 6 ++ kernel/trace/trace.h | 2 - kernel/trace/trace_event_perf.c | 4 +- kernel/trace/trace_events_filter.c | 165 +++++++++++++++++++++++++++++++++++-- 5 files changed, 172 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 64a309d2fbd6..72a6cabb4d5b 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -250,6 +250,7 @@ int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf, int len, int reset); void ftrace_set_global_filter(unsigned char *buf, int len, int reset); void ftrace_set_global_notrace(unsigned char *buf, int len, int reset); +void ftrace_free_filter(struct ftrace_ops *ops); int register_ftrace_command(struct ftrace_func_command *cmd); int unregister_ftrace_command(struct ftrace_func_command *cmd); @@ -380,9 +381,6 @@ extern void ftrace_enable_daemon(void); #else static inline int skip_trace(unsigned long ip) { return 0; } static inline int ftrace_force_update(void) { return 0; } -static inline void ftrace_set_filter(unsigned char *buf, int len, int reset) -{ -} static inline void ftrace_disable_daemon(void) { } static inline void ftrace_enable_daemon(void) { } static inline void ftrace_release_mod(struct module *mod) {} @@ -406,6 +404,9 @@ static inline int ftrace_text_reserved(void *start, void *end) */ #define ftrace_regex_open(ops, flag, inod, file) ({ -ENODEV; }) #define ftrace_set_early_filter(ops, buf, enable) do { } while (0) +#define ftrace_set_filter(ops, buf, len, reset) ({ -ENODEV; }) +#define ftrace_set_notrace(ops, buf, len, reset) ({ -ENODEV; }) +#define ftrace_free_filter(ops) do { } while (0) static inline ssize_t ftrace_filter_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) { return -ENODEV; } diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f615f974d90e..867bd1dd2dd0 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1186,6 +1186,12 @@ static void free_ftrace_hash_rcu(struct ftrace_hash *hash) call_rcu_sched(&hash->rcu, __free_ftrace_hash_rcu); } +void ftrace_free_filter(struct ftrace_ops *ops) +{ + free_ftrace_hash(ops->filter_hash); + free_ftrace_hash(ops->notrace_hash); +} + static struct ftrace_hash *alloc_ftrace_hash(int size_bits) { struct ftrace_hash *hash; diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 29f93cd434a5..54faec790bc1 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -776,9 +776,7 @@ struct filter_pred { u64 val; struct regex regex; unsigned short *ops; -#ifdef CONFIG_FTRACE_STARTUP_TEST struct ftrace_event_field *field; -#endif int offset; int not; int op; diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index fdeeb5c49627..fee3752ae8f6 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c @@ -298,7 +298,9 @@ static int perf_ftrace_function_register(struct perf_event *event) static int perf_ftrace_function_unregister(struct perf_event *event) { struct ftrace_ops *ops = &event->ftrace_ops; - return unregister_ftrace_function(ops); + int ret = unregister_ftrace_function(ops); + ftrace_free_filter(ops); + return ret; } static void perf_ftrace_function_enable(struct perf_event *event) diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 3da3d0ec3584..431dba8b7542 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -81,6 +81,7 @@ enum { FILT_ERR_TOO_MANY_PREDS, FILT_ERR_MISSING_FIELD, FILT_ERR_INVALID_FILTER, + FILT_ERR_IP_FIELD_ONLY, }; static char *err_text[] = { @@ -96,6 +97,7 @@ static char *err_text[] = { "Too many terms in predicate expression", "Missing field name and/or value", "Meaningless filter expression", + "Only 'ip' field is supported for function trace", }; struct opstack_op { @@ -991,7 +993,12 @@ static int init_pred(struct filter_parse_state *ps, fn = filter_pred_strloc; else fn = filter_pred_pchar; - } else if (!is_function_field(field)) { + } else if (is_function_field(field)) { + if (strcmp(field->name, "ip")) { + parse_error(ps, FILT_ERR_IP_FIELD_ONLY, 0); + return -EINVAL; + } + } else { if (field->is_signed) ret = strict_strtoll(pred->regex.pattern, 0, &val); else @@ -1338,10 +1345,7 @@ static struct filter_pred *create_pred(struct filter_parse_state *ps, strcpy(pred.regex.pattern, operand2); pred.regex.len = strlen(pred.regex.pattern); - -#ifdef CONFIG_FTRACE_STARTUP_TEST pred.field = field; -#endif return init_pred(ps, field, &pred) ? NULL : &pred; } @@ -1954,6 +1958,148 @@ void ftrace_profile_free_filter(struct perf_event *event) __free_filter(filter); } +struct function_filter_data { + struct ftrace_ops *ops; + int first_filter; + int first_notrace; +}; + +#ifdef CONFIG_FUNCTION_TRACER +static char ** +ftrace_function_filter_re(char *buf, int len, int *count) +{ + char *str, *sep, **re; + + str = kstrndup(buf, len, GFP_KERNEL); + if (!str) + return NULL; + + /* + * The argv_split function takes white space + * as a separator, so convert ',' into spaces. + */ + while ((sep = strchr(str, ','))) + *sep = ' '; + + re = argv_split(GFP_KERNEL, str, count); + kfree(str); + return re; +} + +static int ftrace_function_set_regexp(struct ftrace_ops *ops, int filter, + int reset, char *re, int len) +{ + int ret; + + if (filter) + ret = ftrace_set_filter(ops, re, len, reset); + else + ret = ftrace_set_notrace(ops, re, len, reset); + + return ret; +} + +static int __ftrace_function_set_filter(int filter, char *buf, int len, + struct function_filter_data *data) +{ + int i, re_cnt, ret; + int *reset; + char **re; + + reset = filter ? &data->first_filter : &data->first_notrace; + + /* + * The 'ip' field could have multiple filters set, separated + * either by space or comma. We first cut the filter and apply + * all pieces separatelly. + */ + re = ftrace_function_filter_re(buf, len, &re_cnt); + if (!re) + return -EINVAL; + + for (i = 0; i < re_cnt; i++) { + ret = ftrace_function_set_regexp(data->ops, filter, *reset, + re[i], strlen(re[i])); + if (ret) + break; + + if (*reset) + *reset = 0; + } + + argv_free(re); + return ret; +} + +static int ftrace_function_check_pred(struct filter_pred *pred, int leaf) +{ + struct ftrace_event_field *field = pred->field; + + if (leaf) { + /* + * Check the leaf predicate for function trace, verify: + * - only '==' and '!=' is used + * - the 'ip' field is used + */ + if ((pred->op != OP_EQ) && (pred->op != OP_NE)) + return -EINVAL; + + if (strcmp(field->name, "ip")) + return -EINVAL; + } else { + /* + * Check the non leaf predicate for function trace, verify: + * - only '||' is used + */ + if (pred->op != OP_OR) + return -EINVAL; + } + + return 0; +} + +static int ftrace_function_set_filter_cb(enum move_type move, + struct filter_pred *pred, + int *err, void *data) +{ + /* Checking the node is valid for function trace. */ + if ((move != MOVE_DOWN) || + (pred->left != FILTER_PRED_INVALID)) { + *err = ftrace_function_check_pred(pred, 0); + } else { + *err = ftrace_function_check_pred(pred, 1); + if (*err) + return WALK_PRED_ABORT; + + *err = __ftrace_function_set_filter(pred->op == OP_EQ, + pred->regex.pattern, + pred->regex.len, + data); + } + + return (*err) ? WALK_PRED_ABORT : WALK_PRED_DEFAULT; +} + +static int ftrace_function_set_filter(struct perf_event *event, + struct event_filter *filter) +{ + struct function_filter_data data = { + .first_filter = 1, + .first_notrace = 1, + .ops = &event->ftrace_ops, + }; + + return walk_pred_tree(filter->preds, filter->root, + ftrace_function_set_filter_cb, &data); +} +#else +static int ftrace_function_set_filter(struct perf_event *event, + struct event_filter *filter) +{ + return -ENODEV; +} +#endif /* CONFIG_FUNCTION_TRACER */ + int ftrace_profile_set_filter(struct perf_event *event, int event_id, char *filter_str) { @@ -1974,9 +2120,16 @@ int ftrace_profile_set_filter(struct perf_event *event, int event_id, goto out_unlock; err = create_filter(call, filter_str, false, &filter); - if (!err) - event->filter = filter; + if (err) + goto free_filter; + + if (ftrace_event_is_function(call)) + err = ftrace_function_set_filter(event, filter); else + event->filter = filter; + +free_filter: + if (err || ftrace_event_is_function(call)) __free_filter(filter); out_unlock: -- cgit From 18fec7d8758dd416904da205375e6fa667defc80 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 4 Jan 2012 11:44:57 -0800 Subject: rcu: Improve synchronize_rcu() diagnostics Although TREE_PREEMPT_RCU indirectly uses might_sleep() to detect illegal use of synchronize_sched() and synchronize_rcu_bh() from within an RCU read-side critical section, this might_sleep() check is bypassed when there is only a single CPU (for example, when running an SMP kernel on a single-CPU system). This patch therefore adds a might_sleep() call to the rcu_blocking_is_gp() check that is unconditionally invoked from both synchronize_sched() and synchronize_rcu_bh(). Signed-off-by: Frederic Weisbecker Signed-off-by: Paul E. McKenney --- include/linux/rcutree.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h index 67458468f1a8..73e7195f9997 100644 --- a/include/linux/rcutree.h +++ b/include/linux/rcutree.h @@ -83,6 +83,7 @@ extern void rcu_sched_force_quiescent_state(void); /* A context switch is a grace period for RCU-sched and RCU-bh. */ static inline int rcu_blocking_is_gp(void) { + might_sleep(); /* Check for RCU read-side critical section. */ return num_online_cpus() == 1; } -- cgit From 486e259340fc4c60474f2c14703e3b3634bb58ca Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 6 Jan 2012 14:11:30 -0800 Subject: rcu: Avoid waking up CPUs having only kfree_rcu() callbacks When CONFIG_RCU_FAST_NO_HZ is enabled, RCU will allow a given CPU to enter dyntick-idle mode even if it still has RCU callbacks queued. RCU avoids system hangs in this case by scheduling a timer for several jiffies in the future. However, if all of the callbacks on that CPU are from kfree_rcu(), there is no reason to wake the CPU up, as it is not a problem to defer freeing of memory. This commit therefore tracks the number of callbacks on a given CPU that are from kfree_rcu(), and avoids scheduling the timer if all of a given CPU's callbacks are from kfree_rcu(). Signed-off-by: Paul E. McKenney Signed-off-by: Paul E. McKenney --- include/linux/rcupdate.h | 2 +- include/linux/rcutiny.h | 6 ++++ include/linux/rcutree.h | 2 ++ include/trace/events/rcu.h | 63 ++++++++++++++++++++++-------------- kernel/rcu.h | 4 ++- kernel/rcutiny.c | 4 +-- kernel/rcutree.c | 29 ++++++++++------- kernel/rcutree.h | 3 +- kernel/rcutree_plugin.h | 79 ++++++++++++++++++++++++++++++++++++++++++++-- kernel/rcutree_trace.c | 8 ++--- 10 files changed, 153 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 81c04f4348ec..a67d5f1072ea 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -841,7 +841,7 @@ void __kfree_rcu(struct rcu_head *head, unsigned long offset) /* See the kfree_rcu() header comment. */ BUILD_BUG_ON(!__is_kfree_rcu_offset(offset)); - call_rcu(head, (rcu_callback)offset); + kfree_call_rcu(head, (rcu_callback)offset); } /** diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h index 00b7a5e493d2..51bf29c81485 100644 --- a/include/linux/rcutiny.h +++ b/include/linux/rcutiny.h @@ -83,6 +83,12 @@ static inline void synchronize_sched_expedited(void) synchronize_sched(); } +static inline void kfree_call_rcu(struct rcu_head *head, + void (*func)(struct rcu_head *rcu)) +{ + call_rcu(head, func); +} + #ifdef CONFIG_TINY_RCU static inline void rcu_preempt_note_context_switch(void) diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h index 73e7195f9997..73892483fd05 100644 --- a/include/linux/rcutree.h +++ b/include/linux/rcutree.h @@ -61,6 +61,8 @@ extern void synchronize_rcu_bh(void); extern void synchronize_sched_expedited(void); extern void synchronize_rcu_expedited(void); +void kfree_call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu)); + static inline void synchronize_rcu_bh_expedited(void) { synchronize_sched_expedited(); diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h index d2d88bed891b..337099783f37 100644 --- a/include/trace/events/rcu.h +++ b/include/trace/events/rcu.h @@ -313,19 +313,22 @@ TRACE_EVENT(rcu_prep_idle, /* * Tracepoint for the registration of a single RCU callback function. * The first argument is the type of RCU, the second argument is - * a pointer to the RCU callback itself, and the third element is the - * new RCU callback queue length for the current CPU. + * a pointer to the RCU callback itself, the third element is the + * number of lazy callbacks queued, and the fourth element is the + * total number of callbacks queued. */ TRACE_EVENT(rcu_callback, - TP_PROTO(char *rcuname, struct rcu_head *rhp, long qlen), + TP_PROTO(char *rcuname, struct rcu_head *rhp, long qlen_lazy, + long qlen), - TP_ARGS(rcuname, rhp, qlen), + TP_ARGS(rcuname, rhp, qlen_lazy, qlen), TP_STRUCT__entry( __field(char *, rcuname) __field(void *, rhp) __field(void *, func) + __field(long, qlen_lazy) __field(long, qlen) ), @@ -333,11 +336,13 @@ TRACE_EVENT(rcu_callback, __entry->rcuname = rcuname; __entry->rhp = rhp; __entry->func = rhp->func; + __entry->qlen_lazy = qlen_lazy; __entry->qlen = qlen; ), - TP_printk("%s rhp=%p func=%pf %ld", - __entry->rcuname, __entry->rhp, __entry->func, __entry->qlen) + TP_printk("%s rhp=%p func=%pf %ld/%ld", + __entry->rcuname, __entry->rhp, __entry->func, + __entry->qlen_lazy, __entry->qlen) ); /* @@ -345,20 +350,21 @@ TRACE_EVENT(rcu_callback, * kfree() form. The first argument is the RCU type, the second argument * is a pointer to the RCU callback, the third argument is the offset * of the callback within the enclosing RCU-protected data structure, - * and the fourth argument is the new RCU callback queue length for the - * current CPU. + * the fourth argument is the number of lazy callbacks queued, and the + * fifth argument is the total number of callbacks queued. */ TRACE_EVENT(rcu_kfree_callback, TP_PROTO(char *rcuname, struct rcu_head *rhp, unsigned long offset, - long qlen), + long qlen_lazy, long qlen), - TP_ARGS(rcuname, rhp, offset, qlen), + TP_ARGS(rcuname, rhp, offset, qlen_lazy, qlen), TP_STRUCT__entry( __field(char *, rcuname) __field(void *, rhp) __field(unsigned long, offset) + __field(long, qlen_lazy) __field(long, qlen) ), @@ -366,41 +372,45 @@ TRACE_EVENT(rcu_kfree_callback, __entry->rcuname = rcuname; __entry->rhp = rhp; __entry->offset = offset; + __entry->qlen_lazy = qlen_lazy; __entry->qlen = qlen; ), - TP_printk("%s rhp=%p func=%ld %ld", + TP_printk("%s rhp=%p func=%ld %ld/%ld", __entry->rcuname, __entry->rhp, __entry->offset, - __entry->qlen) + __entry->qlen_lazy, __entry->qlen) ); /* * Tracepoint for marking the beginning rcu_do_batch, performed to start * RCU callback invocation. The first argument is the RCU flavor, - * the second is the total number of callbacks (including those that - * are not yet ready to be invoked), and the third argument is the - * current RCU-callback batch limit. + * the second is the number of lazy callbacks queued, the third is + * the total number of callbacks queued, and the fourth argument is + * the current RCU-callback batch limit. */ TRACE_EVENT(rcu_batch_start, - TP_PROTO(char *rcuname, long qlen, int blimit), + TP_PROTO(char *rcuname, long qlen_lazy, long qlen, int blimit), - TP_ARGS(rcuname, qlen, blimit), + TP_ARGS(rcuname, qlen_lazy, qlen, blimit), TP_STRUCT__entry( __field(char *, rcuname) + __field(long, qlen_lazy) __field(long, qlen) __field(int, blimit) ), TP_fast_assign( __entry->rcuname = rcuname; + __entry->qlen_lazy = qlen_lazy; __entry->qlen = qlen; __entry->blimit = blimit; ), - TP_printk("%s CBs=%ld bl=%d", - __entry->rcuname, __entry->qlen, __entry->blimit) + TP_printk("%s CBs=%ld/%ld bl=%d", + __entry->rcuname, __entry->qlen_lazy, __entry->qlen, + __entry->blimit) ); /* @@ -531,16 +541,21 @@ TRACE_EVENT(rcu_torture_read, #else /* #ifdef CONFIG_RCU_TRACE */ #define trace_rcu_grace_period(rcuname, gpnum, gpevent) do { } while (0) -#define trace_rcu_grace_period_init(rcuname, gpnum, level, grplo, grphi, qsmask) do { } while (0) +#define trace_rcu_grace_period_init(rcuname, gpnum, level, grplo, grphi, \ + qsmask) do { } while (0) #define trace_rcu_preempt_task(rcuname, pid, gpnum) do { } while (0) #define trace_rcu_unlock_preempted_task(rcuname, gpnum, pid) do { } while (0) -#define trace_rcu_quiescent_state_report(rcuname, gpnum, mask, qsmask, level, grplo, grphi, gp_tasks) do { } while (0) +#define trace_rcu_quiescent_state_report(rcuname, gpnum, mask, qsmask, level, \ + grplo, grphi, gp_tasks) do { } \ + while (0) #define trace_rcu_fqs(rcuname, gpnum, cpu, qsevent) do { } while (0) #define trace_rcu_dyntick(polarity, oldnesting, newnesting) do { } while (0) #define trace_rcu_prep_idle(reason) do { } while (0) -#define trace_rcu_callback(rcuname, rhp, qlen) do { } while (0) -#define trace_rcu_kfree_callback(rcuname, rhp, offset, qlen) do { } while (0) -#define trace_rcu_batch_start(rcuname, qlen, blimit) do { } while (0) +#define trace_rcu_callback(rcuname, rhp, qlen_lazy, qlen) do { } while (0) +#define trace_rcu_kfree_callback(rcuname, rhp, offset, qlen_lazy, qlen) \ + do { } while (0) +#define trace_rcu_batch_start(rcuname, qlen_lazy, qlen, blimit) \ + do { } while (0) #define trace_rcu_invoke_callback(rcuname, rhp) do { } while (0) #define trace_rcu_invoke_kfree_callback(rcuname, rhp, offset) do { } while (0) #define trace_rcu_batch_end(rcuname, callbacks_invoked, cb, nr, iit, risk) \ diff --git a/kernel/rcu.h b/kernel/rcu.h index aa88baab5f78..a074b0b43fc2 100644 --- a/kernel/rcu.h +++ b/kernel/rcu.h @@ -76,16 +76,18 @@ static inline void debug_rcu_head_unqueue(struct rcu_head *head) extern void kfree(const void *); -static inline void __rcu_reclaim(char *rn, struct rcu_head *head) +static inline bool __rcu_reclaim(char *rn, struct rcu_head *head) { unsigned long offset = (unsigned long)head->func; if (__is_kfree_rcu_offset(offset)) { RCU_TRACE(trace_rcu_invoke_kfree_callback(rn, head, offset)); kfree((void *)head - offset); + return 1; } else { RCU_TRACE(trace_rcu_invoke_callback(rn, head)); head->func(head); + return 0; } } diff --git a/kernel/rcutiny.c b/kernel/rcutiny.c index 8e00d461911e..4eb34fcc2a75 100644 --- a/kernel/rcutiny.c +++ b/kernel/rcutiny.c @@ -258,7 +258,7 @@ static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp) /* If no RCU callbacks ready to invoke, just return. */ if (&rcp->rcucblist == rcp->donetail) { - RCU_TRACE(trace_rcu_batch_start(rcp->name, 0, -1)); + RCU_TRACE(trace_rcu_batch_start(rcp->name, 0, 0, -1)); RCU_TRACE(trace_rcu_batch_end(rcp->name, 0, ACCESS_ONCE(rcp->rcucblist), need_resched(), @@ -269,7 +269,7 @@ static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp) /* Move the ready-to-invoke callbacks to a local list. */ local_irq_save(flags); - RCU_TRACE(trace_rcu_batch_start(rcp->name, 0, -1)); + RCU_TRACE(trace_rcu_batch_start(rcp->name, 0, rcp->qlen, -1)); list = rcp->rcucblist; rcp->rcucblist = *rcp->donetail; *rcp->donetail = NULL; diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 570f7530f4b3..acf2d67ad2f4 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -1261,6 +1261,7 @@ static void rcu_send_cbs_to_online(struct rcu_state *rsp) *receive_rdp->nxttail[RCU_NEXT_TAIL] = rdp->nxtlist; receive_rdp->nxttail[RCU_NEXT_TAIL] = rdp->nxttail[RCU_NEXT_TAIL]; + receive_rdp->qlen_lazy += rdp->qlen_lazy; receive_rdp->qlen += rdp->qlen; receive_rdp->n_cbs_adopted += rdp->qlen; rdp->n_cbs_orphaned += rdp->qlen; @@ -1268,6 +1269,7 @@ static void rcu_send_cbs_to_online(struct rcu_state *rsp) rdp->nxtlist = NULL; for (i = 0; i < RCU_NEXT_SIZE; i++) rdp->nxttail[i] = &rdp->nxtlist; + rdp->qlen_lazy = 0; rdp->qlen = 0; } @@ -1368,11 +1370,11 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp) { unsigned long flags; struct rcu_head *next, *list, **tail; - int bl, count; + int bl, count, count_lazy; /* If no callbacks are ready, just return.*/ if (!cpu_has_callbacks_ready_to_invoke(rdp)) { - trace_rcu_batch_start(rsp->name, 0, 0); + trace_rcu_batch_start(rsp->name, rdp->qlen_lazy, rdp->qlen, 0); trace_rcu_batch_end(rsp->name, 0, !!ACCESS_ONCE(rdp->nxtlist), need_resched(), is_idle_task(current), rcu_is_callbacks_kthread()); @@ -1385,7 +1387,7 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp) */ local_irq_save(flags); bl = rdp->blimit; - trace_rcu_batch_start(rsp->name, rdp->qlen, bl); + trace_rcu_batch_start(rsp->name, rdp->qlen_lazy, rdp->qlen, bl); list = rdp->nxtlist; rdp->nxtlist = *rdp->nxttail[RCU_DONE_TAIL]; *rdp->nxttail[RCU_DONE_TAIL] = NULL; @@ -1396,12 +1398,13 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp) local_irq_restore(flags); /* Invoke callbacks. */ - count = 0; + count = count_lazy = 0; while (list) { next = list->next; prefetch(next); debug_rcu_head_unqueue(list); - __rcu_reclaim(rsp->name, list); + if (__rcu_reclaim(rsp->name, list)) + count_lazy++; list = next; /* Stop only if limit reached and CPU has something to do. */ if (++count >= bl && @@ -1416,6 +1419,7 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp) rcu_is_callbacks_kthread()); /* Update count, and requeue any remaining callbacks. */ + rdp->qlen_lazy -= count_lazy; rdp->qlen -= count; rdp->n_cbs_invoked += count; if (list != NULL) { @@ -1702,7 +1706,7 @@ static void invoke_rcu_core(void) static void __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu), - struct rcu_state *rsp) + struct rcu_state *rsp, bool lazy) { unsigned long flags; struct rcu_data *rdp; @@ -1727,12 +1731,14 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu), *rdp->nxttail[RCU_NEXT_TAIL] = head; rdp->nxttail[RCU_NEXT_TAIL] = &head->next; rdp->qlen++; + if (lazy) + rdp->qlen_lazy++; if (__is_kfree_rcu_offset((unsigned long)func)) trace_rcu_kfree_callback(rsp->name, head, (unsigned long)func, - rdp->qlen); + rdp->qlen_lazy, rdp->qlen); else - trace_rcu_callback(rsp->name, head, rdp->qlen); + trace_rcu_callback(rsp->name, head, rdp->qlen_lazy, rdp->qlen); /* If interrupts were disabled, don't dive into RCU core. */ if (irqs_disabled_flags(flags)) { @@ -1779,16 +1785,16 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu), */ void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) { - __call_rcu(head, func, &rcu_sched_state); + __call_rcu(head, func, &rcu_sched_state, 0); } EXPORT_SYMBOL_GPL(call_rcu_sched); /* - * Queue an RCU for invocation after a quicker grace period. + * Queue an RCU callback for invocation after a quicker grace period. */ void call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) { - __call_rcu(head, func, &rcu_bh_state); + __call_rcu(head, func, &rcu_bh_state, 0); } EXPORT_SYMBOL_GPL(call_rcu_bh); @@ -2036,6 +2042,7 @@ rcu_boot_init_percpu_data(int cpu, struct rcu_state *rsp) rdp->nxtlist = NULL; for (i = 0; i < RCU_NEXT_SIZE; i++) rdp->nxttail[i] = &rdp->nxtlist; + rdp->qlen_lazy = 0; rdp->qlen = 0; rdp->dynticks = &per_cpu(rcu_dynticks, cpu); WARN_ON_ONCE(rdp->dynticks->dynticks_nesting != DYNTICK_TASK_NESTING); diff --git a/kernel/rcutree.h b/kernel/rcutree.h index fddff92d6676..af2af3cc5e65 100644 --- a/kernel/rcutree.h +++ b/kernel/rcutree.h @@ -265,7 +265,8 @@ struct rcu_data { */ struct rcu_head *nxtlist; struct rcu_head **nxttail[RCU_NEXT_SIZE]; - long qlen; /* # of queued callbacks */ + long qlen_lazy; /* # of lazy queued callbacks */ + long qlen; /* # of queued callbacks, incl lazy */ long qlen_last_fqs_check; /* qlen at last check for QS forcing */ unsigned long n_cbs_invoked; /* count of RCU cbs invoked. */ diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index 3680b6b35bf3..7adf232bb66b 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -671,10 +671,24 @@ static void rcu_preempt_do_callbacks(void) */ void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) { - __call_rcu(head, func, &rcu_preempt_state); + __call_rcu(head, func, &rcu_preempt_state, 0); } EXPORT_SYMBOL_GPL(call_rcu); +/* + * Queue an RCU callback for lazy invocation after a grace period. + * This will likely be later named something like "call_rcu_lazy()", + * but this change will require some way of tagging the lazy RCU + * callbacks in the list of pending callbacks. Until then, this + * function may only be called from __kfree_rcu(). + */ +void kfree_call_rcu(struct rcu_head *head, + void (*func)(struct rcu_head *rcu)) +{ + __call_rcu(head, func, &rcu_preempt_state, 1); +} +EXPORT_SYMBOL_GPL(kfree_call_rcu); + /** * synchronize_rcu - wait until a grace period has elapsed. * @@ -1064,6 +1078,22 @@ static void rcu_preempt_process_callbacks(void) { } +/* + * Queue an RCU callback for lazy invocation after a grace period. + * This will likely be later named something like "call_rcu_lazy()", + * but this change will require some way of tagging the lazy RCU + * callbacks in the list of pending callbacks. Until then, this + * function may only be called from __kfree_rcu(). + * + * Because there is no preemptible RCU, we use RCU-sched instead. + */ +void kfree_call_rcu(struct rcu_head *head, + void (*func)(struct rcu_head *rcu)) +{ + __call_rcu(head, func, &rcu_sched_state, 1); +} +EXPORT_SYMBOL_GPL(kfree_call_rcu); + /* * Wait for an rcu-preempt grace period, but make it happen quickly. * But because preemptible RCU does not exist, map to rcu-sched. @@ -2051,6 +2081,48 @@ int rcu_needs_cpu(int cpu) return per_cpu(rcu_dyntick_holdoff, cpu) == jiffies; } +/* + * Does the specified flavor of RCU have non-lazy callbacks pending on + * the specified CPU? Both RCU flavor and CPU are specified by the + * rcu_data structure. + */ +static bool __rcu_cpu_has_nonlazy_callbacks(struct rcu_data *rdp) +{ + return rdp->qlen != rdp->qlen_lazy; +} + +#ifdef CONFIG_TREE_PREEMPT_RCU + +/* + * Are there non-lazy RCU-preempt callbacks? (There cannot be if there + * is no RCU-preempt in the kernel.) + */ +static bool rcu_preempt_cpu_has_nonlazy_callbacks(int cpu) +{ + struct rcu_data *rdp = &per_cpu(rcu_preempt_data, cpu); + + return __rcu_cpu_has_nonlazy_callbacks(rdp); +} + +#else /* #ifdef CONFIG_TREE_PREEMPT_RCU */ + +static bool rcu_preempt_cpu_has_nonlazy_callbacks(int cpu) +{ + return 0; +} + +#endif /* else #ifdef CONFIG_TREE_PREEMPT_RCU */ + +/* + * Does any flavor of RCU have non-lazy callbacks on the specified CPU? + */ +static bool rcu_cpu_has_nonlazy_callbacks(int cpu) +{ + return __rcu_cpu_has_nonlazy_callbacks(&per_cpu(rcu_sched_data, cpu)) || + __rcu_cpu_has_nonlazy_callbacks(&per_cpu(rcu_bh_data, cpu)) || + rcu_preempt_cpu_has_nonlazy_callbacks(cpu); +} + /* * Timer handler used to force CPU to start pushing its remaining RCU * callbacks in the case where it entered dyntick-idle mode with callbacks @@ -2149,8 +2221,9 @@ static void rcu_prepare_for_idle(int cpu) trace_rcu_prep_idle("Dyntick with callbacks"); per_cpu(rcu_dyntick_drain, cpu) = 0; per_cpu(rcu_dyntick_holdoff, cpu) = jiffies - 1; - hrtimer_start(&per_cpu(rcu_idle_gp_timer, cpu), - rcu_idle_gp_wait, HRTIMER_MODE_REL); + if (rcu_cpu_has_nonlazy_callbacks(cpu)) + hrtimer_start(&per_cpu(rcu_idle_gp_timer, cpu), + rcu_idle_gp_wait, HRTIMER_MODE_REL); return; /* Nothing more to do immediately. */ } else if (--per_cpu(rcu_dyntick_drain, cpu) <= 0) { /* We have hit the limit, so time to give up. */ diff --git a/kernel/rcutree_trace.c b/kernel/rcutree_trace.c index 654cfe67f0d1..db0987c1e1bd 100644 --- a/kernel/rcutree_trace.c +++ b/kernel/rcutree_trace.c @@ -73,8 +73,8 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) rdp->dynticks->dynticks_nmi_nesting, rdp->dynticks_fqs); seq_printf(m, " of=%lu ri=%lu", rdp->offline_fqs, rdp->resched_ipi); - seq_printf(m, " ql=%ld qs=%c%c%c%c", - rdp->qlen, + seq_printf(m, " ql=%ld/%ld qs=%c%c%c%c", + rdp->qlen_lazy, rdp->qlen, ".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] != rdp->nxttail[RCU_NEXT_TAIL]], ".R"[rdp->nxttail[RCU_WAIT_TAIL] != @@ -145,7 +145,7 @@ static void print_one_rcu_data_csv(struct seq_file *m, struct rcu_data *rdp) rdp->dynticks->dynticks_nmi_nesting, rdp->dynticks_fqs); seq_printf(m, ",%lu,%lu", rdp->offline_fqs, rdp->resched_ipi); - seq_printf(m, ",%ld,\"%c%c%c%c\"", rdp->qlen, + seq_printf(m, ",%ld,%ld,\"%c%c%c%c\"", rdp->qlen_lazy, rdp->qlen, ".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] != rdp->nxttail[RCU_NEXT_TAIL]], ".R"[rdp->nxttail[RCU_WAIT_TAIL] != @@ -168,7 +168,7 @@ static int show_rcudata_csv(struct seq_file *m, void *unused) { seq_puts(m, "\"CPU\",\"Online?\",\"c\",\"g\",\"pq\",\"pgp\",\"pq\","); seq_puts(m, "\"dt\",\"dt nesting\",\"dt NMI nesting\",\"df\","); - seq_puts(m, "\"of\",\"ri\",\"ql\",\"qs\""); + seq_puts(m, "\"of\",\"ri\",\"qll\",\"ql\",\"qs\""); #ifdef CONFIG_RCU_BOOST seq_puts(m, "\"kt\",\"ktl\""); #endif /* #ifdef CONFIG_RCU_BOOST */ -- cgit From 768dfffdffbfcc07d6927bdd642c714c0dd64c99 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 11 Jan 2012 16:33:17 -0800 Subject: rcu: Prevent RCU callbacks from executing before scheduler initialized This is a port of commit #b0d3041 from TREE_RCU to TREE_PREEMPT_RCU. Under some rare but real combinations of configuration parameters, RCU callbacks are posted during early boot that use kernel facilities that are not yet initialized. Therefore, when these callbacks are invoked, hard hangs and crashes ensue. This commit therefore prevents RCU callbacks from being invoked until after the scheduler is fully up and running, as in after multiple tasks have been spawned. It might well turn out that a better approach is to identify the specific RCU callbacks that are causing this problem, but that discussion will wait until such time as someone really needs an RCU callback to be invoked (as opposed to merely registered) during early boot. Signed-off-by: Paul E. McKenney Signed-off-by: Paul E. McKenney --- include/linux/rcutiny.h | 4 ---- kernel/rcutiny_plugin.h | 15 ++++++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h index 51bf29c81485..e93df77176d1 100644 --- a/include/linux/rcutiny.h +++ b/include/linux/rcutiny.h @@ -27,13 +27,9 @@ #include -#ifdef CONFIG_RCU_BOOST static inline void rcu_init(void) { } -#else /* #ifdef CONFIG_RCU_BOOST */ -void rcu_init(void); -#endif /* #else #ifdef CONFIG_RCU_BOOST */ static inline void rcu_barrier_bh(void) { diff --git a/kernel/rcutiny_plugin.h b/kernel/rcutiny_plugin.h index b58a3200f0ff..95df60ebe363 100644 --- a/kernel/rcutiny_plugin.h +++ b/kernel/rcutiny_plugin.h @@ -914,7 +914,8 @@ static void rcu_preempt_process_callbacks(void) static void invoke_rcu_callbacks(void) { have_rcu_kthread_work = 1; - wake_up(&rcu_kthread_wq); + if (rcu_kthread_task != NULL) + wake_up(&rcu_kthread_wq); } #ifdef CONFIG_RCU_TRACE @@ -975,12 +976,16 @@ early_initcall(rcu_spawn_kthreads); #else /* #ifdef CONFIG_RCU_BOOST */ +/* Hold off callback invocation until early_initcall() time. */ +static int rcu_scheduler_fully_active __read_mostly; + /* * Start up softirq processing of callbacks. */ void invoke_rcu_callbacks(void) { - raise_softirq(RCU_SOFTIRQ); + if (rcu_scheduler_fully_active) + raise_softirq(RCU_SOFTIRQ); } #ifdef CONFIG_RCU_TRACE @@ -995,10 +1000,14 @@ static bool rcu_is_callbacks_kthread(void) #endif /* #ifdef CONFIG_RCU_TRACE */ -void rcu_init(void) +static int __init rcu_scheduler_really_started(void) { + rcu_scheduler_fully_active = 1; open_softirq(RCU_SOFTIRQ, rcu_process_callbacks); + raise_softirq(RCU_SOFTIRQ); /* Invoke any callbacks from early boot. */ + return 0; } +early_initcall(rcu_scheduler_really_started); #endif /* #else #ifdef CONFIG_RCU_BOOST */ -- cgit From 1aa03f1188f7b0b85df2de602b33ee7b6fab8e00 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 11 Jan 2012 17:25:17 -0800 Subject: rcu: Simplify unboosting checks This is a port of commit #82e78d80 from TREE_PREEMPT_RCU to TINY_PREEMPT_RCU. This commit uses the fact that current->rcu_boost_mutex is set any time that the RCU_READ_UNLOCK_BOOSTED flag is set in the current->rcu_read_unlock_special bitmask. This allows tests of the bit to be changed to tests of the pointer, which in turn allows the RCU_READ_UNLOCK_BOOSTED flag to be eliminated. Please note that the check of current->rcu_read_unlock_special need not change because any time that RCU_READ_UNLOCK_BOOSTED was set, so was RCU_READ_UNLOCK_BLOCKED. Therefore, __rcu_read_unlock() can continue testing current->rcu_read_unlock_special for non-zero, as before. Signed-off-by: Paul E. McKenney Signed-off-by: Paul E. McKenney --- include/linux/sched.h | 3 +-- kernel/rcutiny_plugin.h | 10 ++++++---- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 7d379a6bfd88..e692abaf915a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1864,8 +1864,7 @@ extern void task_clear_jobctl_pending(struct task_struct *task, #ifdef CONFIG_PREEMPT_RCU #define RCU_READ_UNLOCK_BLOCKED (1 << 0) /* blocked while in RCU read-side. */ -#define RCU_READ_UNLOCK_BOOSTED (1 << 1) /* boosted while in RCU read-side. */ -#define RCU_READ_UNLOCK_NEED_QS (1 << 2) /* RCU core needs CPU response. */ +#define RCU_READ_UNLOCK_NEED_QS (1 << 1) /* RCU core needs CPU response. */ static inline void rcu_copy_process(struct task_struct *p) { diff --git a/kernel/rcutiny_plugin.h b/kernel/rcutiny_plugin.h index 387c2759e1b0..22ecea0dfb62 100644 --- a/kernel/rcutiny_plugin.h +++ b/kernel/rcutiny_plugin.h @@ -318,7 +318,6 @@ static int rcu_boost(void) t = container_of(tb, struct task_struct, rcu_node_entry); rt_mutex_init_proxy_locked(&mtx, t); t->rcu_boost_mutex = &mtx; - t->rcu_read_unlock_special |= RCU_READ_UNLOCK_BOOSTED; raw_local_irq_restore(flags); rt_mutex_lock(&mtx); rt_mutex_unlock(&mtx); /* Keep lockdep happy. */ @@ -550,6 +549,9 @@ static noinline void rcu_read_unlock_special(struct task_struct *t) int empty_exp; unsigned long flags; struct list_head *np; +#ifdef CONFIG_RCU_BOOST + struct rt_mutex *rbmp = NULL; +#endif /* #ifdef CONFIG_RCU_BOOST */ int special; /* @@ -615,10 +617,10 @@ static noinline void rcu_read_unlock_special(struct task_struct *t) } #ifdef CONFIG_RCU_BOOST /* Unboost self if was boosted. */ - if (special & RCU_READ_UNLOCK_BOOSTED) { - t->rcu_read_unlock_special &= ~RCU_READ_UNLOCK_BOOSTED; - rt_mutex_unlock(t->rcu_boost_mutex); + if (t->rcu_boost_mutex != NULL) { + rbmp = t->rcu_boost_mutex; t->rcu_boost_mutex = NULL; + rt_mutex_unlock(rbmp); } #endif /* #ifdef CONFIG_RCU_BOOST */ local_irq_restore(flags); -- cgit From 50406b98b6372e7de21d903d2cf3914e9d64e094 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 12 Jan 2012 13:49:19 -0800 Subject: rcu: Make rcu_sleep_check() also check rcu_lock_map Although it is OK to be preempted in an RCU read-side critical section for TREE_PREEMPT_RCU, it is definitely not OK to be preempted, block, or might_sleep() within an RCU read-side critical section for TREE_RCU. Unfortunately, rcu_might_sleep() currently only checks for RCU-bh and RCU-sched read-side critical sections. This commit therefore makes rcu_might_sleep() check for RCU read-side critical sections, but only in TREE_RCU builds. Signed-off-by: Paul E. McKenney Signed-off-by: Paul E. McKenney --- include/linux/rcupdate.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index a67d5f1072ea..6df0ae197810 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -381,8 +381,22 @@ extern int rcu_my_thread_group_empty(void); } \ } while (0) +#if defined(CONFIG_PROVE_RCU) && !defined(CONFIG_PREEMPT_RCU) +static inline void rcu_preempt_sleep_check(void) +{ + rcu_lockdep_assert(!lock_is_held(&rcu_lock_map), + "Illegal context switch in RCU read-side " + "critical section"); +} +#else /* #ifdef CONFIG_PROVE_RCU */ +static inline void rcu_preempt_sleep_check(void) +{ +} +#endif /* #else #ifdef CONFIG_PROVE_RCU */ + #define rcu_sleep_check() \ do { \ + rcu_preempt_sleep_check(); \ rcu_lockdep_assert(!lock_is_held(&rcu_bh_lock_map), \ "Illegal context switch in RCU-bh" \ " read-side critical section"); \ -- cgit From 5e1ee6e1016763812018bf5c5e966992821dc47e Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 12 Jan 2012 17:21:20 -0800 Subject: rcu: Note that rcu_access_pointer() can be used for teardown There is no convenient expression for rcu_deference_protected() when it is used in tearing down multilinked structures following a grace period. For example, suppose that an element containing an RCU-protected pointer to a second element is removed from an enclosing RCU-protected data structure, then the write-side lock is released, and finally synchronize_rcu() is invoked to wait for a grace period. Then it is necessary to traverse the pointer in order to free up the second element. But we are not in an RCU read-side critical section and we are holding no locks, so the usual rcu_dereference_check() and rcu_dereference_protected() primitives are not appropriate. Neither is rcu_dereference_raw(), as it is intended for use in data structures where the user defines the locking design (for example, list_head). So this responsibility is added to rcu_access_pointer()'s list, and this commit updates rcu_assign_pointer()'s header comment accordingly. Suggested-by: David Howells Signed-off-by: Paul E. McKenney Acked-by: David Howells --- include/linux/rcupdate.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 6df0ae197810..f409529ff35a 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -484,6 +484,13 @@ static inline void rcu_preempt_sleep_check(void) * NULL. Although rcu_access_pointer() may also be used in cases where * update-side locks prevent the value of the pointer from changing, you * should instead use rcu_dereference_protected() for this use case. + * + * It is also permissible to use rcu_access_pointer() when read-side + * access to the pointer was removed at least one grace period ago, as + * is the case in the context of the RCU callback that is freeing up + * the data, or after a synchronize_rcu() returns. This can be useful + * when tearing down multi-linked structures after a grace period + * has elapsed. */ #define rcu_access_pointer(p) __rcu_access_pointer((p), __rcu) -- cgit From c0d6d01bffdce19fa19baad6cb8cc3eed7bfd6f5 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 23 Jan 2012 12:41:26 -0800 Subject: rcu: Check for illegal use of RCU from offlined CPUs Although it is legal to use RCU during early boot, it is anything but legal to use RCU at runtime from an offlined CPU. After all, RCU explicitly ignores offlined CPUs. This commit therefore adds checks for runtime use of RCU from offlined CPUs. These checks are not perfect, in particular, they can be subverted through use of things like rcu_dereference_raw(). Note that it is not possible to put checks in rcu_read_lock() and friends due to the fact that these primitives are used in code that might be used under either RCU or lock-based protection, which means that checking rcu_read_lock() gets you fat piles of false positives. Signed-off-by: Paul E. McKenney Signed-off-by: Paul E. McKenney --- include/linux/rcupdate.h | 19 +++++++++++++++++++ include/linux/srcu.h | 11 +++++++---- kernel/rcupdate.c | 5 +++++ kernel/rcutree.c | 29 +++++++++++++++++++++++++++++ kernel/rcutree_plugin.h | 1 + 5 files changed, 61 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index f409529ff35a..146d37d31778 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -226,6 +226,15 @@ static inline void destroy_rcu_head_on_stack(struct rcu_head *head) } #endif /* #else !CONFIG_DEBUG_OBJECTS_RCU_HEAD */ +#if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PROVE_RCU) +bool rcu_lockdep_current_cpu_online(void); +#else /* #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PROVE_RCU) */ +static inline bool rcu_lockdep_current_cpu_online(void) +{ + return 1; +} +#endif /* #else #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PROVE_RCU) */ + #ifdef CONFIG_DEBUG_LOCK_ALLOC #ifdef CONFIG_PROVE_RCU @@ -270,6 +279,9 @@ extern int debug_lockdep_rcu_enabled(void); * occur in the same context, for example, it is illegal to invoke * rcu_read_unlock() in process context if the matching rcu_read_lock() * was invoked from within an irq handler. + * + * Note that rcu_read_lock() is disallowed if the CPU is either idle or + * offline from an RCU perspective, so check for those as well. */ static inline int rcu_read_lock_held(void) { @@ -277,6 +289,8 @@ static inline int rcu_read_lock_held(void) return 1; if (rcu_is_cpu_idle()) return 0; + if (!rcu_lockdep_current_cpu_online()) + return 0; return lock_is_held(&rcu_lock_map); } @@ -313,6 +327,9 @@ extern int rcu_read_lock_bh_held(void); * notice an extended quiescent state to other CPUs that started a grace * period. Otherwise we would delay any grace period as long as we run in * the idle task. + * + * Similarly, we avoid claiming an SRCU read lock held if the current + * CPU is offline. */ #ifdef CONFIG_PREEMPT_COUNT static inline int rcu_read_lock_sched_held(void) @@ -323,6 +340,8 @@ static inline int rcu_read_lock_sched_held(void) return 1; if (rcu_is_cpu_idle()) return 0; + if (!rcu_lockdep_current_cpu_online()) + return 0; if (debug_locks) lockdep_opinion = lock_is_held(&rcu_sched_lock_map); return lockdep_opinion || preempt_count() != 0 || irqs_disabled(); diff --git a/include/linux/srcu.h b/include/linux/srcu.h index e1b005918bbb..9a323728e60c 100644 --- a/include/linux/srcu.h +++ b/include/linux/srcu.h @@ -99,15 +99,18 @@ long srcu_batches_completed(struct srcu_struct *sp); * power mode. This way we can notice an extended quiescent state to * other CPUs that started a grace period. Otherwise we would delay any * grace period as long as we run in the idle task. + * + * Similarly, we avoid claiming an SRCU read lock held if the current + * CPU is offline. */ static inline int srcu_read_lock_held(struct srcu_struct *sp) { - if (rcu_is_cpu_idle()) - return 0; - if (!debug_lockdep_rcu_enabled()) return 1; - + if (rcu_is_cpu_idle()) + return 0; + if (!rcu_lockdep_current_cpu_online()) + return 0; return lock_is_held(&sp->dep_map); } diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index 2bc4e135ff23..a86f1741cc27 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c @@ -88,6 +88,9 @@ EXPORT_SYMBOL_GPL(debug_lockdep_rcu_enabled); * section. * * Check debug_lockdep_rcu_enabled() to prevent false positives during boot. + * + * Note that rcu_read_lock() is disallowed if the CPU is either idle or + * offline from an RCU perspective, so check for those as well. */ int rcu_read_lock_bh_held(void) { @@ -95,6 +98,8 @@ int rcu_read_lock_bh_held(void) return 1; if (rcu_is_cpu_idle()) return 0; + if (!rcu_lockdep_current_cpu_online()) + return 0; return in_softirq() || irqs_disabled(); } EXPORT_SYMBOL_GPL(rcu_read_lock_bh_held); diff --git a/kernel/rcutree.c b/kernel/rcutree.c index dccd2f78db4e..bcf7db2f2fd2 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -591,6 +591,35 @@ int rcu_is_cpu_idle(void) } EXPORT_SYMBOL(rcu_is_cpu_idle); +#ifdef CONFIG_HOTPLUG_CPU + +/* + * Is the current CPU online? Disable preemption to avoid false positives + * that could otherwise happen due to the current CPU number being sampled, + * this task being preempted, its old CPU being taken offline, resuming + * on some other CPU, then determining that its old CPU is now offline. + * It is OK to use RCU on an offline processor during initial boot, hence + * the check for rcu_scheduler_fully_active. + * + * Disable checking if in an NMI handler because we cannot safely report + * errors from NMI handlers anyway. + */ +bool rcu_lockdep_current_cpu_online(void) +{ + bool ret; + + if (in_nmi()) + return 1; + preempt_disable(); + ret = cpu_online(smp_processor_id()) || + !rcu_scheduler_fully_active; + preempt_enable(); + return ret; +} +EXPORT_SYMBOL_GPL(rcu_lockdep_current_cpu_online); + +#endif /* #ifdef CONFIG_HOTPLUG_CPU */ + #endif /* #ifdef CONFIG_PROVE_RCU */ /** diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index aa93b074bb2f..cecea84f4f3f 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -1946,6 +1946,7 @@ void synchronize_sched_expedited(void) /* Note that atomic_inc_return() implies full memory barrier. */ firstsnap = snap = atomic_inc_return(&sync_sched_expedited_started); get_online_cpus(); + WARN_ON_ONCE(cpu_is_offline(smp_processor_id())); /* * Each pass through the following loop attempts to force a -- cgit From 236fefafe5d3d34b78ed2ccf5510909716112326 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 31 Jan 2012 14:00:41 -0800 Subject: rcu: Call out dangers of expedited RCU primitives The expedited RCU primitives can be quite useful, but they have some high costs as well. This commit updates and creates docbook comments calling out the costs, and updates the RCU documentation as well. Signed-off-by: Paul E. McKenney --- Documentation/RCU/checklist.txt | 14 ++++++++++++++ include/linux/rcutree.h | 16 ++++++++++++++++ kernel/rcutree.c | 22 ++++++++++++++-------- kernel/rcutree_plugin.h | 20 ++++++++++++++++---- kernel/srcu.c | 27 +++++++++++++++++---------- 5 files changed, 77 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/Documentation/RCU/checklist.txt b/Documentation/RCU/checklist.txt index bff2d8be1e18..5c8d74968090 100644 --- a/Documentation/RCU/checklist.txt +++ b/Documentation/RCU/checklist.txt @@ -180,6 +180,20 @@ over a rather long period of time, but improvements are always welcome! operations that would not normally be undertaken while a real-time workload is running. + In particular, if you find yourself invoking one of the expedited + primitives repeatedly in a loop, please do everyone a favor: + Restructure your code so that it batches the updates, allowing + a single non-expedited primitive to cover the entire batch. + This will very likely be faster than the loop containing the + expedited primitive, and will be much much easier on the rest + of the system, especially to real-time workloads running on + the rest of the system. + + In addition, it is illegal to call the expedited forms from + a CPU-hotplug notifier, or while holding a lock that is acquired + by a CPU-hotplug notifier. Failing to observe this restriction + will result in deadlock. + 7. If the updater uses call_rcu() or synchronize_rcu(), then the corresponding readers must use rcu_read_lock() and rcu_read_unlock(). If the updater uses call_rcu_bh() or diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h index 73892483fd05..e8ee5dd0854c 100644 --- a/include/linux/rcutree.h +++ b/include/linux/rcutree.h @@ -63,6 +63,22 @@ extern void synchronize_rcu_expedited(void); void kfree_call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu)); +/** + * synchronize_rcu_bh_expedited - Brute-force RCU-bh grace period + * + * Wait for an RCU-bh grace period to elapse, but use a "big hammer" + * approach to force the grace period to end quickly. This consumes + * significant time on all CPUs and is unfriendly to real-time workloads, + * so is thus not recommended for any sort of common-case code. In fact, + * if you are using synchronize_rcu_bh_expedited() in a loop, please + * restructure your code to batch your updates, and then use a single + * synchronize_rcu_bh() instead. + * + * Note that it is illegal to call this function while holding any lock + * that is acquired by a CPU-hotplug notifier. And yes, it is also illegal + * to call this function from a CPU-hotplug notifier. Failing to observe + * these restriction will result in deadlock. + */ static inline void synchronize_rcu_bh_expedited(void) { synchronize_sched_expedited(); diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 708469a06860..df0e3c1bb68e 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -1961,15 +1961,21 @@ static int synchronize_sched_expedited_cpu_stop(void *data) return 0; } -/* - * Wait for an rcu-sched grace period to elapse, but use "big hammer" - * approach to force grace period to end quickly. This consumes - * significant time on all CPUs, and is thus not recommended for - * any sort of common-case code. +/** + * synchronize_sched_expedited - Brute-force RCU-sched grace period + * + * Wait for an RCU-sched grace period to elapse, but use a "big hammer" + * approach to force the grace period to end quickly. This consumes + * significant time on all CPUs and is unfriendly to real-time workloads, + * so is thus not recommended for any sort of common-case code. In fact, + * if you are using synchronize_sched_expedited() in a loop, please + * restructure your code to batch your updates, and then use a single + * synchronize_sched() instead. * - * Note that it is illegal to call this function while holding any - * lock that is acquired by a CPU-hotplug notifier. Failing to - * observe this restriction will result in deadlock. + * Note that it is illegal to call this function while holding any lock + * that is acquired by a CPU-hotplug notifier. And yes, it is also illegal + * to call this function from a CPU-hotplug notifier. Failing to observe + * these restriction will result in deadlock. * * This implementation can be thought of as an application of ticket * locking to RCU, with sync_sched_expedited_started and diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index 07f880445d8d..f7ceadf4986e 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -835,10 +835,22 @@ sync_rcu_preempt_exp_init(struct rcu_state *rsp, struct rcu_node *rnp) rcu_report_exp_rnp(rsp, rnp, false); /* Don't wake self. */ } -/* - * Wait for an rcu-preempt grace period, but expedite it. The basic idea - * is to invoke synchronize_sched_expedited() to push all the tasks to - * the ->blkd_tasks lists and wait for this list to drain. +/** + * synchronize_rcu_expedited - Brute-force RCU grace period + * + * Wait for an RCU-preempt grace period, but expedite it. The basic + * idea is to invoke synchronize_sched_expedited() to push all the tasks to + * the ->blkd_tasks lists and wait for this list to drain. This consumes + * significant time on all CPUs and is unfriendly to real-time workloads, + * so is thus not recommended for any sort of common-case code. + * In fact, if you are using synchronize_rcu_expedited() in a loop, + * please restructure your code to batch your updates, and then Use a + * single synchronize_rcu() instead. + * + * Note that it is illegal to call this function while holding any lock + * that is acquired by a CPU-hotplug notifier. And yes, it is also illegal + * to call this function from a CPU-hotplug notifier. Failing to observe + * these restriction will result in deadlock. */ void synchronize_rcu_expedited(void) { diff --git a/kernel/srcu.c b/kernel/srcu.c index 3f99fa0e8ed3..ba35f3a4a1f4 100644 --- a/kernel/srcu.c +++ b/kernel/srcu.c @@ -286,19 +286,26 @@ void synchronize_srcu(struct srcu_struct *sp) EXPORT_SYMBOL_GPL(synchronize_srcu); /** - * synchronize_srcu_expedited - like synchronize_srcu, but less patient + * synchronize_srcu_expedited - Brute-force SRCU grace period * @sp: srcu_struct with which to synchronize. * - * Flip the completed counter, and wait for the old count to drain to zero. - * As with classic RCU, the updater must use some separate means of - * synchronizing concurrent updates. Can block; must be called from - * process context. + * Wait for an SRCU grace period to elapse, but use a "big hammer" + * approach to force the grace period to end quickly. This consumes + * significant time on all CPUs and is unfriendly to real-time workloads, + * so is thus not recommended for any sort of common-case code. In fact, + * if you are using synchronize_srcu_expedited() in a loop, please + * restructure your code to batch your updates, and then use a single + * synchronize_srcu() instead. * - * Note that it is illegal to call synchronize_srcu_expedited() - * from the corresponding SRCU read-side critical section; doing so - * will result in deadlock. However, it is perfectly legal to call - * synchronize_srcu_expedited() on one srcu_struct from some other - * srcu_struct's read-side critical section. + * Note that it is illegal to call this function while holding any lock + * that is acquired by a CPU-hotplug notifier. And yes, it is also illegal + * to call this function from a CPU-hotplug notifier. Failing to observe + * these restriction will result in deadlock. It is also illegal to call + * synchronize_srcu_expedited() from the corresponding SRCU read-side + * critical section; doing so will result in deadlock. However, it is + * perfectly legal to call synchronize_srcu_expedited() on one srcu_struct + * from some other srcu_struct's read-side critical section, as long as + * the resulting graph of srcu_structs is acyclic. */ void synchronize_srcu_expedited(struct srcu_struct *sp) { -- cgit From bde23c6892878e48f64de668660778991bc2fb56 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 1 Feb 2012 10:30:46 -0800 Subject: rcu: Convert WARN_ON_ONCE() in rcu_lock_acquire() to lockdep The WARN_ON_ONCE() in rcu_lock_acquire() results in infinite recursion on S390, and also doesn't print very much information. Remove this. Updated patch to add lockdep-RCU assertions to RCU's read-side primitives. Signed-off-by: Heiko Carstens Signed-off-by: Paul E. McKenney --- include/linux/rcupdate.h | 14 ++++++++++++-- include/linux/srcu.h | 4 ++++ 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 146d37d31778..6ee663c8745a 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -248,13 +248,11 @@ static inline int rcu_is_cpu_idle(void) static inline void rcu_lock_acquire(struct lockdep_map *map) { - WARN_ON_ONCE(rcu_is_cpu_idle()); lock_acquire(map, 0, 0, 2, 1, NULL, _THIS_IP_); } static inline void rcu_lock_release(struct lockdep_map *map) { - WARN_ON_ONCE(rcu_is_cpu_idle()); lock_release(map, 1, _THIS_IP_); } @@ -699,6 +697,8 @@ static inline void rcu_read_lock(void) __rcu_read_lock(); __acquire(RCU); rcu_lock_acquire(&rcu_lock_map); + rcu_lockdep_assert(!rcu_is_cpu_idle(), + "rcu_read_lock() used illegally while idle"); } /* @@ -718,6 +718,8 @@ static inline void rcu_read_lock(void) */ static inline void rcu_read_unlock(void) { + rcu_lockdep_assert(!rcu_is_cpu_idle(), + "rcu_read_unlock() used illegally while idle"); rcu_lock_release(&rcu_lock_map); __release(RCU); __rcu_read_unlock(); @@ -745,6 +747,8 @@ static inline void rcu_read_lock_bh(void) local_bh_disable(); __acquire(RCU_BH); rcu_lock_acquire(&rcu_bh_lock_map); + rcu_lockdep_assert(!rcu_is_cpu_idle(), + "rcu_read_lock_bh() used illegally while idle"); } /* @@ -754,6 +758,8 @@ static inline void rcu_read_lock_bh(void) */ static inline void rcu_read_unlock_bh(void) { + rcu_lockdep_assert(!rcu_is_cpu_idle(), + "rcu_read_unlock_bh() used illegally while idle"); rcu_lock_release(&rcu_bh_lock_map); __release(RCU_BH); local_bh_enable(); @@ -777,6 +783,8 @@ static inline void rcu_read_lock_sched(void) preempt_disable(); __acquire(RCU_SCHED); rcu_lock_acquire(&rcu_sched_lock_map); + rcu_lockdep_assert(!rcu_is_cpu_idle(), + "rcu_read_lock_sched() used illegally while idle"); } /* Used by lockdep and tracing: cannot be traced, cannot call lockdep. */ @@ -793,6 +801,8 @@ static inline notrace void rcu_read_lock_sched_notrace(void) */ static inline void rcu_read_unlock_sched(void) { + rcu_lockdep_assert(!rcu_is_cpu_idle(), + "rcu_read_unlock_sched() used illegally while idle"); rcu_lock_release(&rcu_sched_lock_map); __release(RCU_SCHED); preempt_enable(); diff --git a/include/linux/srcu.h b/include/linux/srcu.h index 9a323728e60c..d3d5fa54f25e 100644 --- a/include/linux/srcu.h +++ b/include/linux/srcu.h @@ -172,6 +172,8 @@ static inline int srcu_read_lock(struct srcu_struct *sp) __acquires(sp) int retval = __srcu_read_lock(sp); rcu_lock_acquire(&(sp)->dep_map); + rcu_lockdep_assert(!rcu_is_cpu_idle(), + "srcu_read_lock() used illegally while idle"); return retval; } @@ -185,6 +187,8 @@ static inline int srcu_read_lock(struct srcu_struct *sp) __acquires(sp) static inline void srcu_read_unlock(struct srcu_struct *sp, int idx) __releases(sp) { + rcu_lockdep_assert(!rcu_is_cpu_idle(), + "srcu_read_unlock() used illegally while idle"); rcu_lock_release(&(sp)->dep_map); __srcu_read_unlock(sp, idx); } -- cgit From 8a2ecf474d3ee8dd5d001490349e422cec52f39f Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 2 Feb 2012 15:42:04 -0800 Subject: rcu: Add RCU_NONIDLE() for idle-loop RCU read-side critical sections RCU, RCU-bh, and RCU-sched read-side critical sections are forbidden in the inner idle loop, that is, between the rcu_idle_enter() and the rcu_idle_exit() -- RCU will happily ignore any such read-side critical sections. However, things like powertop need tracepoints in the inner idle loop. This commit therefore provides an RCU_NONIDLE() macro that can be used to wrap code in the idle loop that requires RCU read-side critical sections. Suggested-by: Steven Rostedt Signed-off-by: Paul E. McKenney Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett Acked-by: Deepthi Dharwar --- include/linux/rcupdate.h | 27 +++++++++++++++++++++++++++ kernel/rcutiny.c | 2 ++ kernel/rcutree.c | 2 ++ 3 files changed, 31 insertions(+) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 6ee663c8745a..937217425c47 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -190,6 +190,33 @@ extern void rcu_idle_exit(void); extern void rcu_irq_enter(void); extern void rcu_irq_exit(void); +/** + * RCU_NONIDLE - Indicate idle-loop code that needs RCU readers + * @a: Code that RCU needs to pay attention to. + * + * RCU, RCU-bh, and RCU-sched read-side critical sections are forbidden + * in the inner idle loop, that is, between the rcu_idle_enter() and + * the rcu_idle_exit() -- RCU will happily ignore any such read-side + * critical sections. However, things like powertop need tracepoints + * in the inner idle loop. + * + * This macro provides the way out: RCU_NONIDLE(do_something_with_RCU()) + * will tell RCU that it needs to pay attending, invoke its argument + * (in this example, a call to the do_something_with_RCU() function), + * and then tell RCU to go back to ignoring this CPU. It is permissible + * to nest RCU_NONIDLE() wrappers, but the nesting level is currently + * quite limited. If deeper nesting is required, it will be necessary + * to adjust DYNTICK_TASK_NESTING_VALUE accordingly. + * + * This macro may be used from process-level code only. + */ +#define RCU_NONIDLE(a) \ + do { \ + rcu_idle_exit(); \ + do { a; } while (0); \ + rcu_idle_enter(); \ + } while (0) + /* * Infrastructure to implement the synchronize_() primitives in * TREE_RCU and rcu_barrier_() primitives in TINY_RCU. diff --git a/kernel/rcutiny.c b/kernel/rcutiny.c index c8b0e1582c04..37a5444204d2 100644 --- a/kernel/rcutiny.c +++ b/kernel/rcutiny.c @@ -97,6 +97,7 @@ void rcu_idle_enter(void) rcu_idle_enter_common(oldval); local_irq_restore(flags); } +EXPORT_SYMBOL_GPL(rcu_idle_enter); /* * Exit an interrupt handler towards idle. @@ -153,6 +154,7 @@ void rcu_idle_exit(void) rcu_idle_exit_common(oldval); local_irq_restore(flags); } +EXPORT_SYMBOL_GPL(rcu_idle_exit); /* * Enter an interrupt handler, moving away from idle. diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 92b47760edf3..eacc10b03531 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -402,6 +402,7 @@ void rcu_idle_enter(void) rcu_idle_enter_common(rdtp, oldval); local_irq_restore(flags); } +EXPORT_SYMBOL_GPL(rcu_idle_enter); /** * rcu_irq_exit - inform RCU that current CPU is exiting irq towards idle @@ -493,6 +494,7 @@ void rcu_idle_exit(void) rcu_idle_exit_common(rdtp, oldval); local_irq_restore(flags); } +EXPORT_SYMBOL_GPL(rcu_idle_exit); /** * rcu_irq_enter - inform RCU that current CPU is entering irq away from idle -- cgit From 5e5282bbfde9ca6157dba913d90cbab859a837e2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 21 Feb 2012 16:01:30 +0200 Subject: Bluetooth: mgmt: Allow connectable/discoverable changes in off state This patch makes it possible to toggle the connectable & discoverable settings when powered off. Two new hdev->dev_flags flags are added to track what the scan mode should be when the device is finally powered on. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 2 + net/bluetooth/hci_core.c | 1 + net/bluetooth/mgmt.c | 91 ++++++++++++++++++++++++++++++++++++--------- 3 files changed, 76 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index ec370494e568..169d2f8cc4ee 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -96,6 +96,8 @@ enum { HCI_LE_SCAN, HCI_SSP_ENABLED, HCI_HS_ENABLED, + HCI_CONNECTABLE, + HCI_DISCOVERABLE, }; /* HCI ioctl defines */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index a787c9c9d4cd..9d199494bd65 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -737,6 +737,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) if (hdev->discov_timeout > 0) { cancel_delayed_work(&hdev->discov_off); hdev->discov_timeout = 0; + clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); } if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0f87030f9c30..6311be775ff2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -398,10 +398,10 @@ static u32 get_current_settings(struct hci_dev *hdev) if (!test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) settings |= MGMT_SETTING_POWERED; - if (test_bit(HCI_PSCAN, &hdev->flags)) + if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) settings |= MGMT_SETTING_CONNECTABLE; - if (test_bit(HCI_ISCAN, &hdev->flags)) + if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) settings |= MGMT_SETTING_DISCOVERABLE; if (test_bit(HCI_PAIRABLE, &hdev->dev_flags)) @@ -804,6 +804,7 @@ static int set_discoverable(struct sock *sk, u16 index, void *data, u16 len) struct mgmt_cp_set_discoverable *cp = data; struct hci_dev *hdev; struct pending_cmd *cmd; + u16 timeout; u8 scan; int err; @@ -818,9 +819,11 @@ static int set_discoverable(struct sock *sk, u16 index, void *data, u16 len) return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, MGMT_STATUS_INVALID_PARAMS); + timeout = get_unaligned_le16(&cp->timeout); + hci_dev_lock(hdev); - if (!hdev_is_powered(hdev)) { + if (!hdev_is_powered(hdev) && timeout > 0) { err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, MGMT_STATUS_NOT_POWERED); goto failed; @@ -833,8 +836,22 @@ static int set_discoverable(struct sock *sk, u16 index, void *data, u16 len) goto failed; } - if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) && - test_bit(HCI_PSCAN, &hdev->flags)) { + if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) { + err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, + MGMT_STATUS_REJECTED); + goto failed; + } + + if (!hdev_is_powered(hdev)) { + if (cp->val) + set_bit(HCI_DISCOVERABLE, &hdev->dev_flags); + else + clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); + err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev); + goto failed; + } + + if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) { err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev); goto failed; } @@ -857,7 +874,7 @@ static int set_discoverable(struct sock *sk, u16 index, void *data, u16 len) mgmt_pending_remove(cmd); if (cp->val) - hdev->discov_timeout = get_unaligned_le16(&cp->timeout); + hdev->discov_timeout = timeout; failed: hci_dev_unlock(hdev); @@ -888,8 +905,13 @@ static int set_connectable(struct sock *sk, u16 index, void *data, u16 len) hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, - MGMT_STATUS_NOT_POWERED); + if (cp->val) + set_bit(HCI_CONNECTABLE, &hdev->dev_flags); + else { + clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); + clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); + } + err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev); goto failed; } @@ -900,7 +922,7 @@ static int set_connectable(struct sock *sk, u16 index, void *data, u16 len) goto failed; } - if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { + if (!!cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev); goto failed; } @@ -2881,9 +2903,22 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) __le32 ev; int err; + if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + return 0; + mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); - if (!powered) { + if (powered) { + u8 scan = 0; + + if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) + scan |= SCAN_PAGE; + if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) + scan |= SCAN_INQUIRY; + + if (scan) + hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); + } else { u8 status = ENETDOWN; mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); } @@ -2902,15 +2937,25 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) { struct cmd_lookup match = { NULL, hdev }; - __le32 ev; - int err; + bool changed = false; + int err = 0; mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, &match); - ev = cpu_to_le32(get_current_settings(hdev)); + if (discoverable) { + if (!test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) + changed = true; + } else { + if (test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) + changed = true; + } - err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), + if (changed) { + __le32 ev = cpu_to_le32(get_current_settings(hdev)); + err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk); + } + if (match.sk) sock_put(match.sk); @@ -2919,16 +2964,26 @@ int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) int mgmt_connectable(struct hci_dev *hdev, u8 connectable) { - __le32 ev; struct cmd_lookup match = { NULL, hdev }; - int err; + bool changed = false; + int err = 0; mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp, &match); - ev = cpu_to_le32(get_current_settings(hdev)); + if (connectable) { + if (!test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags)) + changed = true; + } else { + if (test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags)) + changed = true; + } - err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk); + if (changed) { + __le32 ev = cpu_to_le32(get_current_settings(hdev)); + err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), + match.sk); + } if (match.sk) sock_put(match.sk); -- cgit From 3f518bf745cbd6007d8069100fb9cb09e960c872 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Tue, 21 Feb 2012 07:30:58 +0000 Subject: datagram: Add offset argument to __skb_recv_datagram This one is only considered for MSG_PEEK flag and the value pointed by it specifies where to start peeking bytes from. If the offset happens to point into the middle of the returned skb, the offset within this skb is put back to this very argument. Signed-off-by: Pavel Emelyanov Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 2 +- net/core/datagram.c | 21 +++++++++++++-------- net/ipv4/udp.c | 4 ++-- net/ipv6/udp.c | 4 ++-- 4 files changed, 18 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 2b7317ff297f..f3cf43de3c2a 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -2046,7 +2046,7 @@ static inline void skb_frag_add_head(struct sk_buff *skb, struct sk_buff *frag) for (iter = skb_shinfo(skb)->frag_list; iter; iter = iter->next) extern struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags, - int *peeked, int *err); + int *peeked, int *off, int *err); extern struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, int *err); extern unsigned int datagram_poll(struct file *file, struct socket *sock, diff --git a/net/core/datagram.c b/net/core/datagram.c index 6f54d0a17f8e..d3cf12f62c8f 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -132,6 +132,8 @@ out_noerr: * __skb_recv_datagram - Receive a datagram skbuff * @sk: socket * @flags: MSG_ flags + * @off: an offset in bytes to peek skb from. Returns an offset + * within an skb where data actually starts * @peeked: returns non-zero if this packet has been seen before * @err: error code returned * @@ -158,7 +160,7 @@ out_noerr: * the standard around please. */ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags, - int *peeked, int *err) + int *peeked, int *off, int *err) { struct sk_buff *skb; long timeo; @@ -183,19 +185,22 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags, struct sk_buff_head *queue = &sk->sk_receive_queue; spin_lock_irqsave(&queue->lock, cpu_flags); - skb = skb_peek(queue); - if (skb) { + skb_queue_walk(queue, skb) { *peeked = skb->peeked; if (flags & MSG_PEEK) { + if (*off >= skb->len) { + *off -= skb->len; + continue; + } skb->peeked = 1; atomic_inc(&skb->users); } else __skb_unlink(skb, queue); - } - spin_unlock_irqrestore(&queue->lock, cpu_flags); - if (skb) + spin_unlock_irqrestore(&queue->lock, cpu_flags); return skb; + } + spin_unlock_irqrestore(&queue->lock, cpu_flags); /* User doesn't want to wait */ error = -EAGAIN; @@ -215,10 +220,10 @@ EXPORT_SYMBOL(__skb_recv_datagram); struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, int *err) { - int peeked; + int peeked, off = 0; return __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), - &peeked, err); + &peeked, &off, err); } EXPORT_SYMBOL(skb_recv_datagram); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index cd99f1a0f59f..7c41ab84e72e 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1167,7 +1167,7 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; struct sk_buff *skb; unsigned int ulen, copied; - int peeked; + int peeked, off = 0; int err; int is_udplite = IS_UDPLITE(sk); bool slow; @@ -1183,7 +1183,7 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, try_again: skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), - &peeked, &err); + &peeked, &off, &err); if (!skb) goto out; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 8aebf8f90436..37b0699e95e5 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -342,7 +342,7 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, struct inet_sock *inet = inet_sk(sk); struct sk_buff *skb; unsigned int ulen, copied; - int peeked; + int peeked, off = 0; int err; int is_udplite = IS_UDPLITE(sk); int is_udp4; @@ -359,7 +359,7 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, try_again: skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0), - &peeked, &err); + &peeked, &off, &err); if (!skb) goto out; -- cgit From da5ef6e51b327b41180b5d1000c06e8d3595a936 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Tue, 21 Feb 2012 07:31:18 +0000 Subject: skb: Add skb_peek_next helper Signed-off-by: Pavel Emelyanov Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index f3cf43de3c2a..c11a44ea1bf4 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -876,6 +876,24 @@ static inline struct sk_buff *skb_peek(const struct sk_buff_head *list_) return list; } +/** + * skb_peek_next - peek skb following the given one from a queue + * @skb: skb to start from + * @list_: list to peek at + * + * Returns %NULL when the end of the list is met or a pointer to the + * next element. The reference count is not incremented and the + * reference is therefore volatile. Use with caution. + */ +static inline struct sk_buff *skb_peek_next(struct sk_buff *skb, + const struct sk_buff_head *list_) +{ + struct sk_buff *next = skb->next; + if (next == (struct sk_buff *)list_) + next = NULL; + return next; +} + /** * skb_peek_tail - peek at the tail of an &sk_buff_head * @list_: list to peek at -- cgit From ef64a54f6e558155b4f149bb10666b9e914b6c54 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Tue, 21 Feb 2012 07:31:34 +0000 Subject: sock: Introduce the SO_PEEK_OFF sock option This one specifies where to start MSG_PEEK-ing queue data from. When set to negative value means that MSG_PEEK works as ususally -- peeks from the head of the queue always. When some bytes are peeked from queue and the peeking offset is non negative it is moved forward so that the next peek will return next portion of data. When non-peeking recvmsg occurs and the peeking offset is non negative is is moved backward so that the next peek will still peek the proper data (i.e. the one that would have been picked if there were no non peeking recv in between). The offset is set using per-proto opteration to let the protocol handle the locking issues and to check whether the peeking offset feature is supported by the protocol the socket belongs to. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- arch/alpha/include/asm/socket.h | 1 + arch/arm/include/asm/socket.h | 1 + arch/avr32/include/asm/socket.h | 1 + arch/cris/include/asm/socket.h | 1 + arch/frv/include/asm/socket.h | 1 + arch/h8300/include/asm/socket.h | 1 + arch/ia64/include/asm/socket.h | 1 + arch/m32r/include/asm/socket.h | 1 + arch/m68k/include/asm/socket.h | 1 + arch/mips/include/asm/socket.h | 1 + arch/mn10300/include/asm/socket.h | 1 + arch/parisc/include/asm/socket.h | 1 + arch/powerpc/include/asm/socket.h | 1 + arch/s390/include/asm/socket.h | 1 + arch/sparc/include/asm/socket.h | 1 + arch/xtensa/include/asm/socket.h | 1 + include/asm-generic/socket.h | 1 + include/linux/net.h | 1 + include/net/sock.h | 25 +++++++++++++++++++++++++ net/core/sock.c | 13 +++++++++++++ 20 files changed, 56 insertions(+) (limited to 'include') diff --git a/arch/alpha/include/asm/socket.h b/arch/alpha/include/asm/socket.h index 082355f159e6..16449d330dae 100644 --- a/arch/alpha/include/asm/socket.h +++ b/arch/alpha/include/asm/socket.h @@ -71,6 +71,7 @@ #define SO_WIFI_STATUS 41 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 42 /* O_NONBLOCK clashes with the bits used for socket types. Therefore we * have to define SOCK_NONBLOCK to a different value here. diff --git a/arch/arm/include/asm/socket.h b/arch/arm/include/asm/socket.h index dec6f9afb3cf..d958c74e5260 100644 --- a/arch/arm/include/asm/socket.h +++ b/arch/arm/include/asm/socket.h @@ -64,5 +64,6 @@ #define SO_WIFI_STATUS 41 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 42 #endif /* _ASM_SOCKET_H */ diff --git a/arch/avr32/include/asm/socket.h b/arch/avr32/include/asm/socket.h index 247b88c760be..30078f98b3ab 100644 --- a/arch/avr32/include/asm/socket.h +++ b/arch/avr32/include/asm/socket.h @@ -64,5 +64,6 @@ #define SO_WIFI_STATUS 41 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 42 #endif /* __ASM_AVR32_SOCKET_H */ diff --git a/arch/cris/include/asm/socket.h b/arch/cris/include/asm/socket.h index e269264df7c4..048aba64600c 100644 --- a/arch/cris/include/asm/socket.h +++ b/arch/cris/include/asm/socket.h @@ -66,6 +66,7 @@ #define SO_WIFI_STATUS 41 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 42 #endif /* _ASM_SOCKET_H */ diff --git a/arch/frv/include/asm/socket.h b/arch/frv/include/asm/socket.h index ce80fdadcce5..7a361810f3cc 100644 --- a/arch/frv/include/asm/socket.h +++ b/arch/frv/include/asm/socket.h @@ -64,6 +64,7 @@ #define SO_WIFI_STATUS 41 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 42 #endif /* _ASM_SOCKET_H */ diff --git a/arch/h8300/include/asm/socket.h b/arch/h8300/include/asm/socket.h index cf1daab6f27e..e7bbfcee5b99 100644 --- a/arch/h8300/include/asm/socket.h +++ b/arch/h8300/include/asm/socket.h @@ -64,5 +64,6 @@ #define SO_WIFI_STATUS 41 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 42 #endif /* _ASM_SOCKET_H */ diff --git a/arch/ia64/include/asm/socket.h b/arch/ia64/include/asm/socket.h index 4b03664e3fb5..ced62de9d5a9 100644 --- a/arch/ia64/include/asm/socket.h +++ b/arch/ia64/include/asm/socket.h @@ -73,5 +73,6 @@ #define SO_WIFI_STATUS 41 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 42 #endif /* _ASM_IA64_SOCKET_H */ diff --git a/arch/m32r/include/asm/socket.h b/arch/m32r/include/asm/socket.h index e8b8c5bb053c..696cb4c7ca4e 100644 --- a/arch/m32r/include/asm/socket.h +++ b/arch/m32r/include/asm/socket.h @@ -64,5 +64,6 @@ #define SO_WIFI_STATUS 41 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 42 #endif /* _ASM_M32R_SOCKET_H */ diff --git a/arch/m68k/include/asm/socket.h b/arch/m68k/include/asm/socket.h index d4708ce466e0..e8b41a6775f9 100644 --- a/arch/m68k/include/asm/socket.h +++ b/arch/m68k/include/asm/socket.h @@ -64,5 +64,6 @@ #define SO_WIFI_STATUS 41 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 42 #endif /* _ASM_SOCKET_H */ diff --git a/arch/mips/include/asm/socket.h b/arch/mips/include/asm/socket.h index ad5c0a7a02a7..52104872e9e3 100644 --- a/arch/mips/include/asm/socket.h +++ b/arch/mips/include/asm/socket.h @@ -84,6 +84,7 @@ To add: #define SO_REUSEPORT 0x0200 /* Allow local address and port reuse. */ #define SO_WIFI_STATUS 41 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 42 #ifdef __KERNEL__ diff --git a/arch/mn10300/include/asm/socket.h b/arch/mn10300/include/asm/socket.h index 876356d78522..013fcc51698f 100644 --- a/arch/mn10300/include/asm/socket.h +++ b/arch/mn10300/include/asm/socket.h @@ -64,5 +64,6 @@ #define SO_WIFI_STATUS 41 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 42 #endif /* _ASM_SOCKET_H */ diff --git a/arch/parisc/include/asm/socket.h b/arch/parisc/include/asm/socket.h index d28c51b61067..f717c9bec16f 100644 --- a/arch/parisc/include/asm/socket.h +++ b/arch/parisc/include/asm/socket.h @@ -63,6 +63,7 @@ #define SO_WIFI_STATUS 0x4022 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 0x4023 /* O_NONBLOCK clashes with the bits used for socket types. Therefore we * have to define SOCK_NONBLOCK to a different value here. diff --git a/arch/powerpc/include/asm/socket.h b/arch/powerpc/include/asm/socket.h index 2fc2af8fbf59..fe1c0b478fd7 100644 --- a/arch/powerpc/include/asm/socket.h +++ b/arch/powerpc/include/asm/socket.h @@ -71,5 +71,6 @@ #define SO_WIFI_STATUS 41 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 42 #endif /* _ASM_POWERPC_SOCKET_H */ diff --git a/arch/s390/include/asm/socket.h b/arch/s390/include/asm/socket.h index 67b5c1b14b51..581702fa1b0c 100644 --- a/arch/s390/include/asm/socket.h +++ b/arch/s390/include/asm/socket.h @@ -72,5 +72,6 @@ #define SO_WIFI_STATUS 41 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 42 #endif /* _ASM_SOCKET_H */ diff --git a/arch/sparc/include/asm/socket.h b/arch/sparc/include/asm/socket.h index 8af1b64168b3..68e2e2746f6f 100644 --- a/arch/sparc/include/asm/socket.h +++ b/arch/sparc/include/asm/socket.h @@ -60,6 +60,7 @@ #define SO_WIFI_STATUS 0x0025 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 0x0026 /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 diff --git a/arch/xtensa/include/asm/socket.h b/arch/xtensa/include/asm/socket.h index bb06968be227..74818b161362 100644 --- a/arch/xtensa/include/asm/socket.h +++ b/arch/xtensa/include/asm/socket.h @@ -75,5 +75,6 @@ #define SO_WIFI_STATUS 41 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 42 #endif /* _XTENSA_SOCKET_H */ diff --git a/include/asm-generic/socket.h b/include/asm-generic/socket.h index 49c1704173e7..d9aaac0c36d4 100644 --- a/include/asm-generic/socket.h +++ b/include/asm-generic/socket.h @@ -67,4 +67,5 @@ #define SO_WIFI_STATUS 41 #define SCM_WIFI_STATUS SO_WIFI_STATUS +#define SO_PEEK_OFF 42 #endif /* __ASM_GENERIC_SOCKET_H */ diff --git a/include/linux/net.h b/include/linux/net.h index b29923006b11..be60c7f5e145 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -206,6 +206,7 @@ struct proto_ops { int offset, size_t size, int flags); ssize_t (*splice_read)(struct socket *sock, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned int flags); + void (*set_peek_off)(struct sock *sk, int val); }; #define DECLARE_SOCKADDR(type, dst, src) \ diff --git a/include/net/sock.h b/include/net/sock.h index 91c1c8baf020..9c0553b9e451 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -357,6 +357,7 @@ struct sock { struct page *sk_sndmsg_page; struct sk_buff *sk_send_head; __u32 sk_sndmsg_off; + __s32 sk_peek_off; int sk_write_pending; #ifdef CONFIG_SECURITY void *sk_security; @@ -373,6 +374,30 @@ struct sock { void (*sk_destruct)(struct sock *sk); }; +static inline int sk_peek_offset(struct sock *sk, int flags) +{ + if ((flags & MSG_PEEK) && (sk->sk_peek_off >= 0)) + return sk->sk_peek_off; + else + return 0; +} + +static inline void sk_peek_offset_bwd(struct sock *sk, int val) +{ + if (sk->sk_peek_off >= 0) { + if (sk->sk_peek_off >= val) + sk->sk_peek_off -= val; + else + sk->sk_peek_off = 0; + } +} + +static inline void sk_peek_offset_fwd(struct sock *sk, int val) +{ + if (sk->sk_peek_off >= 0) + sk->sk_peek_off += val; +} + /* * Hashed lists helper routines */ diff --git a/net/core/sock.c b/net/core/sock.c index 02f8dfe320b7..19942d4bb6e6 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -793,6 +793,12 @@ set_rcvbuf: sock_valbool_flag(sk, SOCK_WIFI_STATUS, valbool); break; + case SO_PEEK_OFF: + if (sock->ops->set_peek_off) + sock->ops->set_peek_off(sk, val); + else + ret = -EOPNOTSUPP; + break; default: ret = -ENOPROTOOPT; break; @@ -1018,6 +1024,12 @@ int sock_getsockopt(struct socket *sock, int level, int optname, v.val = !!sock_flag(sk, SOCK_WIFI_STATUS); break; + case SO_PEEK_OFF: + if (!sock->ops->set_peek_off) + return -EOPNOTSUPP; + + v.val = sk->sk_peek_off; + break; default: return -ENOPROTOOPT; } @@ -2092,6 +2104,7 @@ void sock_init_data(struct socket *sock, struct sock *sk) sk->sk_sndmsg_page = NULL; sk->sk_sndmsg_off = 0; + sk->sk_peek_off = -1; sk->sk_peer_pid = NULL; sk->sk_peer_cred = NULL; -- cgit From de5bdff7a72acc281219be2b8edeeca1fd81c542 Mon Sep 17 00:00:00 2001 From: Hiroshi Shimamoto Date: Thu, 16 Feb 2012 14:52:21 +0900 Subject: sched: Make initial SCHED_RR timeslace DEF_TIMESLICE Current the initial SCHED_RR timeslice of init_task is HZ, which means 1s, and is not same as the default SCHED_RR timeslice DEF_TIMESLICE. Change that initial timeslice to the DEF_TIMESLICE. Signed-off-by: Hiroshi Shimamoto [ s/DEF_TIMESLICE/RR_TIMESLICE/g ] Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/4F3C9995.3010800@ct.jp.nec.com Signed-off-by: Ingo Molnar --- include/linux/init_task.h | 2 +- include/linux/sched.h | 6 ++++++ kernel/sched/rt.c | 4 ++-- kernel/sched/sched.h | 4 ---- 4 files changed, 9 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 9c66b1ada9d7..f994d51f70f2 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -149,7 +149,7 @@ extern struct cred init_cred; }, \ .rt = { \ .run_list = LIST_HEAD_INIT(tsk.rt.run_list), \ - .time_slice = HZ, \ + .time_slice = RR_TIMESLICE, \ .nr_cpus_allowed = NR_CPUS, \ }, \ .tasks = LIST_HEAD_INIT(tsk.tasks), \ diff --git a/include/linux/sched.h b/include/linux/sched.h index 5dba2ad52431..eb5de466f099 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1234,6 +1234,12 @@ struct sched_rt_entity { #endif }; +/* + * default timeslice is 100 msecs (used only for SCHED_RR tasks). + * Timeslices get refilled after they expire. + */ +#define RR_TIMESLICE (100 * HZ / 1000) + struct rcu_node; enum perf_event_task_context { diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index f42ae7fb5ec5..f70206c2c802 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -1972,7 +1972,7 @@ static void task_tick_rt(struct rq *rq, struct task_struct *p, int queued) if (--p->rt.time_slice) return; - p->rt.time_slice = DEF_TIMESLICE; + p->rt.time_slice = RR_TIMESLICE; /* * Requeue to the end of queue if we are not the only element @@ -2000,7 +2000,7 @@ static unsigned int get_rr_interval_rt(struct rq *rq, struct task_struct *task) * Time slice is 0 for SCHED_FIFO tasks */ if (task->policy == SCHED_RR) - return DEF_TIMESLICE; + return RR_TIMESLICE; else return 0; } diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 8a2c768d2f98..c0660a1a0cd1 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -36,11 +36,7 @@ extern __read_mostly int scheduler_running; /* * These are the 'tuning knobs' of the scheduler: - * - * default timeslice is 100 msecs (used only for SCHED_RR tasks). - * Timeslices get refilled after they expire. */ -#define DEF_TIMESLICE (100 * HZ / 1000) /* * single value that denotes runtime == period, ie unlimited time. -- cgit From 8860020e0be1f03d83dc9e9e93e18a4ddbe01038 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 13 Feb 2012 15:17:18 +0100 Subject: cfg80211: restructure AP/GO mode API The AP/GO mode API isn't very clearly defined, it has "set beacon" and "new beacon" etc. Modify the API to the following: * start AP -- all settings * change beacon -- new beacon data * stop AP -- stop AP mode operation This also reflects in the nl80211 API, rename the commands there correspondingly (but keep the old names for compatibility.) Overall, this makes it much clearer what's going on in the API. Kalle developed the ath6kl changes, I created the rest of the patch. Signed-off-by: Kalle Valo Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 82 ++++++---- include/linux/nl80211.h | 34 ++-- include/net/cfg80211.h | 70 ++++---- net/mac80211/cfg.c | 146 +++++++---------- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/tx.c | 13 +- net/wireless/nl80211.c | 255 ++++++++++++++++------------- 7 files changed, 316 insertions(+), 286 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index d1922d8eb3bb..5370333883e4 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2269,25 +2269,11 @@ static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif, return ret; } -static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info, bool add) +static int ath6kl_set_ies(struct ath6kl_vif *vif, + struct cfg80211_beacon_data *info) { - struct ath6kl *ar = ath6kl_priv(dev); - struct ath6kl_vif *vif = netdev_priv(dev); - struct ieee80211_mgmt *mgmt; - u8 *ies; - int ies_len; - struct wmi_connect_cmd p; + struct ath6kl *ar = vif->ar; int res; - int i, ret; - - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add); - - if (!ath6kl_cfg80211_ready(vif)) - return -EIO; - - if (vif->next_mode != AP_NETWORK) - return -EOPNOTSUPP; if (info->beacon_ies) { res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, @@ -2297,12 +2283,14 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, if (res) return res; } + if (info->proberesp_ies) { res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies, info->proberesp_ies_len); if (res) return res; } + if (info->assocresp_ies) { res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, WMI_FRAME_ASSOC_RESP, @@ -2312,8 +2300,30 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, return res; } - if (!add) - return 0; + return 0; +} + +static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ap_settings *info) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + struct ieee80211_mgmt *mgmt; + u8 *ies; + int ies_len; + struct wmi_connect_cmd p; + int res; + int i, ret; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__); + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (vif->next_mode != AP_NETWORK) + return -EOPNOTSUPP; + + res = ath6kl_set_ies(vif, &info->beacon); ar->ap_mode_bkey.valid = false; @@ -2322,13 +2332,13 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, * info->dtim_period */ - if (info->head == NULL) + if (info->beacon.head == NULL) return -EINVAL; - mgmt = (struct ieee80211_mgmt *) info->head; + mgmt = (struct ieee80211_mgmt *) info->beacon.head; ies = mgmt->u.beacon.variable; - if (ies > info->head + info->head_len) + if (ies > info->beacon.head + info->beacon.head_len) return -EINVAL; - ies_len = info->head + info->head_len - ies; + ies_len = info->beacon.head + info->beacon.head_len - ies; if (info->ssid == NULL) return -EINVAL; @@ -2436,19 +2446,21 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, return 0; } -static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info) +static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_beacon_data *beacon) { - return ath6kl_ap_beacon(wiphy, dev, info, true); -} + struct ath6kl_vif *vif = netdev_priv(dev); -static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info) -{ - return ath6kl_ap_beacon(wiphy, dev, info, false); + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + if (vif->next_mode != AP_NETWORK) + return -EOPNOTSUPP; + + return ath6kl_set_ies(vif, beacon); } -static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev) +static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); @@ -2783,9 +2795,9 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .resume = __ath6kl_cfg80211_resume, #endif .set_channel = ath6kl_set_channel, - .add_beacon = ath6kl_add_beacon, - .set_beacon = ath6kl_set_beacon, - .del_beacon = ath6kl_del_beacon, + .start_ap = ath6kl_start_ap, + .change_beacon = ath6kl_change_beacon, + .stop_ap = ath6kl_stop_ap, .del_station = ath6kl_del_station, .change_station = ath6kl_change_station, .remain_on_channel = ath6kl_remain_on_channel, diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index ad56e21a9f10..be35a68746a7 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -156,21 +156,23 @@ * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX * or %NL80211_ATTR_MAC. * - * @NL80211_CMD_GET_BEACON: retrieve beacon information (returned in a - * %NL80222_CMD_NEW_BEACON message) - * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface - * using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD, - * %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes. - * Following attributes are provided for drivers that generate full Beacon - * and Probe Response frames internally: %NL80211_ATTR_SSID, + * @NL80211_CMD_GET_BEACON: (not used) + * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface + * using the %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL + * attributes. For drivers that generate the beacon and probe responses + * internally, the following attributes must be provided: %NL80211_ATTR_IE, + * %NL80211_ATTR_IE_PROBE_RESP and %NL80211_ATTR_IE_ASSOC_RESP. + * @NL80211_CMD_START_AP: Start AP operation on an AP interface, parameters + * are like for %NL80211_CMD_SET_BEACON, and additionally parameters that + * do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL, + * %NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID, * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE, * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, - * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, - * %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_IE, %NL80211_ATTR_IE_PROBE_RESP, - * %NL80211_ATTR_IE_ASSOC_RESP. - * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface, - * parameters are like for %NL80211_CMD_SET_BEACON. - * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it + * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY and + * %NL80211_ATTR_AUTH_TYPE. + * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP + * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface + * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP * * @NL80211_CMD_GET_STATION: Get station attributes for station identified by * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. @@ -565,8 +567,10 @@ enum nl80211_commands { NL80211_CMD_GET_BEACON, NL80211_CMD_SET_BEACON, - NL80211_CMD_NEW_BEACON, - NL80211_CMD_DEL_BEACON, + NL80211_CMD_START_AP, + NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP, + NL80211_CMD_STOP_AP, + NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP, NL80211_CMD_GET_STATION, NL80211_CMD_SET_STATION, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e0c9ff3a1977..755a7707a7c5 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -366,25 +366,13 @@ struct cfg80211_crypto_settings { }; /** - * struct beacon_parameters - beacon parameters - * - * Used to configure the beacon for an interface. - * + * struct cfg80211_beacon_data - beacon data * @head: head portion of beacon (before TIM IE) * or %NULL if not changed * @tail: tail portion of beacon (after TIM IE) * or %NULL if not changed - * @interval: beacon interval or zero if not changed - * @dtim_period: DTIM period or zero if not changed * @head_len: length of @head * @tail_len: length of @tail - * @ssid: SSID to be used in the BSS (note: may be %NULL if not provided from - * user space) - * @ssid_len: length of @ssid - * @hidden_ssid: whether to hide the SSID in Beacon/Probe Response frames - * @crypto: crypto settings - * @privacy: the BSS uses privacy - * @auth_type: Authentication type (algorithm) * @beacon_ies: extra information element(s) to add into Beacon frames or %NULL * @beacon_ies_len: length of beacon_ies in octets * @proberesp_ies: extra information element(s) to add into Probe Response @@ -396,24 +384,46 @@ struct cfg80211_crypto_settings { * @probe_resp_len: length of probe response template (@probe_resp) * @probe_resp: probe response template (AP mode only) */ -struct beacon_parameters { - u8 *head, *tail; - int interval, dtim_period; - int head_len, tail_len; +struct cfg80211_beacon_data { + const u8 *head, *tail; + const u8 *beacon_ies; + const u8 *proberesp_ies; + const u8 *assocresp_ies; + const u8 *probe_resp; + + size_t head_len, tail_len; + size_t beacon_ies_len; + size_t proberesp_ies_len; + size_t assocresp_ies_len; + size_t probe_resp_len; +}; + +/** + * struct cfg80211_ap_settings - AP configuration + * + * Used to configure an AP interface. + * + * @beacon: beacon data + * @beacon_interval: beacon interval + * @dtim_period: DTIM period + * @ssid: SSID to be used in the BSS (note: may be %NULL if not provided from + * user space) + * @ssid_len: length of @ssid + * @hidden_ssid: whether to hide the SSID in Beacon/Probe Response frames + * @crypto: crypto settings + * @privacy: the BSS uses privacy + * @auth_type: Authentication type (algorithm) + */ +struct cfg80211_ap_settings { + struct cfg80211_beacon_data beacon; + + int beacon_interval, dtim_period; const u8 *ssid; size_t ssid_len; enum nl80211_hidden_ssid hidden_ssid; struct cfg80211_crypto_settings crypto; bool privacy; enum nl80211_auth_type auth_type; - const u8 *beacon_ies; - size_t beacon_ies_len; - const u8 *proberesp_ies; - size_t proberesp_ies_len; - const u8 *assocresp_ies; - size_t assocresp_ies_len; - int probe_resp_len; - u8 *probe_resp; }; /** @@ -1518,11 +1528,11 @@ struct cfg80211_ops { struct net_device *netdev, u8 key_index); - int (*add_beacon)(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info); - int (*set_beacon)(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info); - int (*del_beacon)(struct wiphy *wiphy, struct net_device *dev); + int (*start_ap)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ap_settings *settings); + int (*change_beacon)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_beacon_data *info); + int (*stop_ap)(struct wiphy *wiphy, struct net_device *dev); int (*add_station)(struct wiphy *wiphy, struct net_device *dev, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c3de921c8cfd..f7eb25aabf8f 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -489,27 +489,13 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, return ret; } -static void ieee80211_config_ap_ssid(struct ieee80211_sub_if_data *sdata, - struct beacon_parameters *params) -{ - struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; - - bss_conf->ssid_len = params->ssid_len; - - if (params->ssid_len) - memcpy(bss_conf->ssid, params->ssid, params->ssid_len); - - bss_conf->hidden_ssid = - (params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE); -} - static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, - u8 *resp, size_t resp_len) + const u8 *resp, size_t resp_len) { struct sk_buff *new, *old; if (!resp || !resp_len) - return -EINVAL; + return 1; old = rtnl_dereference(sdata->u.ap.probe_resp); @@ -520,50 +506,28 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, memcpy(skb_put(new, resp_len), resp, resp_len); rcu_assign_pointer(sdata->u.ap.probe_resp, new); - synchronize_rcu(); - - if (old) + if (old) { + /* TODO: use call_rcu() */ + synchronize_rcu(); dev_kfree_skb(old); + } return 0; } -/* - * This handles both adding a beacon and setting new beacon info - */ -static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, - struct beacon_parameters *params) +static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, + struct cfg80211_beacon_data *params) { struct beacon_data *new, *old; int new_head_len, new_tail_len; - int size; - int err = -EINVAL; - u32 changed = 0; + int size, err; + u32 changed = BSS_CHANGED_BEACON; old = rtnl_dereference(sdata->u.ap.beacon); - /* head must not be zero-length */ - if (params->head && !params->head_len) - return -EINVAL; - - /* - * This is a kludge. beacon interval should really be part - * of the beacon information. - */ - if (params->interval && - (sdata->vif.bss_conf.beacon_int != params->interval)) { - sdata->vif.bss_conf.beacon_int = params->interval; - ieee80211_bss_info_change_notify(sdata, - BSS_CHANGED_BEACON_INT); - } - /* Need to have a beacon head if we don't have one yet */ if (!params->head && !old) - return err; - - /* sorry, no way to start beaconing without dtim period */ - if (!params->dtim_period && !old) - return err; + return -EINVAL; /* new or old head? */ if (params->head) @@ -586,12 +550,6 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, /* start filling the new info now */ - /* new or old dtim period? */ - if (params->dtim_period) - new->dtim_period = params->dtim_period; - else - new->dtim_period = old->dtim_period; - /* * pointers go into the block we allocated, * memory is | beacon_data | head | tail | @@ -614,46 +572,37 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, if (old) memcpy(new->tail, old->tail, new_tail_len); - sdata->vif.bss_conf.dtim_period = new->dtim_period; - - rcu_assign_pointer(sdata->u.ap.beacon, new); - - synchronize_rcu(); - - kfree(old); - err = ieee80211_set_probe_resp(sdata, params->probe_resp, params->probe_resp_len); - if (!err) + if (err < 0) + return err; + if (err == 0) changed |= BSS_CHANGED_AP_PROBE_RESP; - ieee80211_config_ap_ssid(sdata, params); - changed |= BSS_CHANGED_BEACON_ENABLED | - BSS_CHANGED_BEACON | - BSS_CHANGED_SSID; + rcu_assign_pointer(sdata->u.ap.beacon, new); + + if (old) + kfree_rcu(old, rcu_head); - ieee80211_bss_info_change_notify(sdata, changed); - return 0; + return changed; } -static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *params) +static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ap_settings *params) { - struct ieee80211_sub_if_data *sdata; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct beacon_data *old; struct ieee80211_sub_if_data *vlan; - int ret; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); + u32 changed = BSS_CHANGED_BEACON_INT | + BSS_CHANGED_BEACON_ENABLED | + BSS_CHANGED_BEACON | + BSS_CHANGED_SSID; + int err; old = rtnl_dereference(sdata->u.ap.beacon); if (old) return -EALREADY; - ret = ieee80211_config_beacon(sdata, params); - if (ret) - return ret; - /* * Apply control port protocol, this allows us to * not encrypt dynamic WEP control frames. @@ -667,14 +616,32 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, params->crypto.control_port_no_encrypt; } + sdata->vif.bss_conf.beacon_int = params->beacon_interval; + sdata->vif.bss_conf.dtim_period = params->dtim_period; + + sdata->vif.bss_conf.ssid_len = params->ssid_len; + if (params->ssid_len) + memcpy(sdata->vif.bss_conf.ssid, params->ssid, + params->ssid_len); + sdata->vif.bss_conf.hidden_ssid = + (params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE); + + err = ieee80211_assign_beacon(sdata, ¶ms->beacon); + if (err < 0) + return err; + changed |= err; + + ieee80211_bss_info_change_notify(sdata, changed); + return 0; } -static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *params) +static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_beacon_data *params) { struct ieee80211_sub_if_data *sdata; struct beacon_data *old; + int err; sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -682,10 +649,14 @@ static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, if (!old) return -ENOENT; - return ieee80211_config_beacon(sdata, params); + err = ieee80211_assign_beacon(sdata, params); + if (err < 0) + return err; + ieee80211_bss_info_change_notify(sdata, err); + return 0; } -static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) +static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) { struct ieee80211_sub_if_data *sdata; struct beacon_data *old; @@ -697,10 +668,11 @@ static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) return -ENOENT; RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); - synchronize_rcu(); - kfree(old); + + kfree_rcu(old, rcu_head); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); + return 0; } @@ -2699,9 +2671,9 @@ struct cfg80211_ops mac80211_config_ops = { .get_key = ieee80211_get_key, .set_default_key = ieee80211_config_default_key, .set_default_mgmt_key = ieee80211_config_default_mgmt_key, - .add_beacon = ieee80211_add_beacon, - .set_beacon = ieee80211_set_beacon, - .del_beacon = ieee80211_del_beacon, + .start_ap = ieee80211_start_ap, + .change_beacon = ieee80211_change_beacon, + .stop_ap = ieee80211_stop_ap, .add_station = ieee80211_add_station, .del_station = ieee80211_del_station, .change_station = ieee80211_change_station, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 74594f012cd3..67aed1eff135 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -228,7 +228,7 @@ struct ieee80211_rx_data { struct beacon_data { u8 *head, *tail; int head_len, tail_len; - int dtim_period; + struct rcu_head rcu_head; }; struct ieee80211_if_ap { diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 1be0ca2b5936..c6eadac9ca4e 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2206,7 +2206,8 @@ void ieee80211_tx_pending(unsigned long data) /* functions for drivers to get certain frames */ -static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss, +static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, + struct ieee80211_if_ap *bss, struct sk_buff *skb, struct beacon_data *beacon) { @@ -2223,7 +2224,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss, IEEE80211_MAX_AID+1); if (bss->dtim_count == 0) - bss->dtim_count = beacon->dtim_period - 1; + bss->dtim_count = sdata->vif.bss_conf.dtim_period - 1; else bss->dtim_count--; @@ -2231,7 +2232,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss, *pos++ = WLAN_EID_TIM; *pos++ = 4; *pos++ = bss->dtim_count; - *pos++ = beacon->dtim_period; + *pos++ = sdata->vif.bss_conf.dtim_period; if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf)) aid0 = 1; @@ -2324,12 +2325,14 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, * of the tim bitmap in mac80211 and the driver. */ if (local->tim_in_locked_section) { - ieee80211_beacon_add_tim(ap, skb, beacon); + ieee80211_beacon_add_tim(sdata, ap, skb, + beacon); } else { unsigned long flags; spin_lock_irqsave(&local->tim_lock, flags); - ieee80211_beacon_add_tim(ap, skb, beacon); + ieee80211_beacon_add_tim(sdata, ap, skb, + beacon); spin_unlock_irqrestore(&local->tim_lock, flags); } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index fe2747653564..1998c3682774 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -871,7 +871,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, CMD(add_virtual_intf, NEW_INTERFACE); CMD(change_virtual_intf, SET_INTERFACE); CMD(add_key, NEW_KEY); - CMD(add_beacon, NEW_BEACON); + CMD(start_ap, START_AP); CMD(add_station, NEW_STATION); CMD(add_mpath, NEW_MPATH); CMD(update_mesh_config, SET_MESH_CONFIG); @@ -2075,15 +2075,10 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) return err; } -static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) +static int nl80211_parse_beacon(struct genl_info *info, + struct cfg80211_beacon_data *bcn) { - int (*call)(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info); - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net_device *dev = info->user_ptr[1]; - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct beacon_parameters params; - int haveinfo = 0, err; + bool haveinfo = false; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) || !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) || @@ -2091,149 +2086,183 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP])) return -EINVAL; - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - memset(¶ms, 0, sizeof(params)); - - switch (info->genlhdr->cmd) { - case NL80211_CMD_NEW_BEACON: - /* these are required for NEW_BEACON */ - if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] || - !info->attrs[NL80211_ATTR_DTIM_PERIOD] || - !info->attrs[NL80211_ATTR_BEACON_HEAD]) - return -EINVAL; - - params.interval = - nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); - params.dtim_period = - nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); - - err = cfg80211_validate_beacon_int(rdev, params.interval); - if (err) - return err; - - /* - * In theory, some of these attributes could be required for - * NEW_BEACON, but since they were not used when the command was - * originally added, keep them optional for old user space - * programs to work with drivers that do not need the additional - * information. - */ - if (info->attrs[NL80211_ATTR_SSID]) { - params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - params.ssid_len = - nla_len(info->attrs[NL80211_ATTR_SSID]); - if (params.ssid_len == 0 || - params.ssid_len > IEEE80211_MAX_SSID_LEN) - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) { - params.hidden_ssid = nla_get_u32( - info->attrs[NL80211_ATTR_HIDDEN_SSID]); - if (params.hidden_ssid != - NL80211_HIDDEN_SSID_NOT_IN_USE && - params.hidden_ssid != - NL80211_HIDDEN_SSID_ZERO_LEN && - params.hidden_ssid != - NL80211_HIDDEN_SSID_ZERO_CONTENTS) - return -EINVAL; - } - - params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; - - if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { - params.auth_type = nla_get_u32( - info->attrs[NL80211_ATTR_AUTH_TYPE]); - if (!nl80211_valid_auth_type(params.auth_type)) - return -EINVAL; - } else - params.auth_type = NL80211_AUTHTYPE_AUTOMATIC; - - err = nl80211_crypto_settings(rdev, info, ¶ms.crypto, - NL80211_MAX_NR_CIPHER_SUITES); - if (err) - return err; - - call = rdev->ops->add_beacon; - break; - case NL80211_CMD_SET_BEACON: - call = rdev->ops->set_beacon; - break; - default: - WARN_ON(1); - return -EOPNOTSUPP; - } - - if (!call) - return -EOPNOTSUPP; + memset(bcn, 0, sizeof(*bcn)); if (info->attrs[NL80211_ATTR_BEACON_HEAD]) { - params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]); - params.head_len = - nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]); - haveinfo = 1; + bcn->head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]); + bcn->head_len = nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]); + if (!bcn->head_len) + return -EINVAL; + haveinfo = true; } if (info->attrs[NL80211_ATTR_BEACON_TAIL]) { - params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]); - params.tail_len = + bcn->tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]); + bcn->tail_len = nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]); - haveinfo = 1; + haveinfo = true; } if (!haveinfo) return -EINVAL; if (info->attrs[NL80211_ATTR_IE]) { - params.beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]); - params.beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); + bcn->beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]); + bcn->beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); } if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) { - params.proberesp_ies = + bcn->proberesp_ies = nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]); - params.proberesp_ies_len = + bcn->proberesp_ies_len = nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]); } if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) { - params.assocresp_ies = + bcn->assocresp_ies = nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]); - params.assocresp_ies_len = + bcn->assocresp_ies_len = nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]); } if (info->attrs[NL80211_ATTR_PROBE_RESP]) { - params.probe_resp = + bcn->probe_resp = nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]); - params.probe_resp_len = + bcn->probe_resp_len = nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]); } - err = call(&rdev->wiphy, dev, ¶ms); - if (!err && params.interval) - wdev->beacon_interval = params.interval; + return 0; +} + +static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_ap_settings params; + int err; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + if (!rdev->ops->start_ap) + return -EOPNOTSUPP; + + if (wdev->beacon_interval) + return -EALREADY; + + memset(¶ms, 0, sizeof(params)); + + /* these are required for START_AP */ + if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] || + !info->attrs[NL80211_ATTR_DTIM_PERIOD] || + !info->attrs[NL80211_ATTR_BEACON_HEAD]) + return -EINVAL; + + err = nl80211_parse_beacon(info, ¶ms.beacon); + if (err) + return err; + + params.beacon_interval = + nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); + params.dtim_period = + nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); + + err = cfg80211_validate_beacon_int(rdev, params.beacon_interval); + if (err) + return err; + + /* + * In theory, some of these attributes should be required here + * but since they were not used when the command was originally + * added, keep them optional for old user space programs to let + * them continue to work with drivers that do not need the + * additional information -- drivers must check! + */ + if (info->attrs[NL80211_ATTR_SSID]) { + params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + params.ssid_len = + nla_len(info->attrs[NL80211_ATTR_SSID]); + if (params.ssid_len == 0 || + params.ssid_len > IEEE80211_MAX_SSID_LEN) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) { + params.hidden_ssid = nla_get_u32( + info->attrs[NL80211_ATTR_HIDDEN_SSID]); + if (params.hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE && + params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_LEN && + params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_CONTENTS) + return -EINVAL; + } + + params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; + + if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { + params.auth_type = nla_get_u32( + info->attrs[NL80211_ATTR_AUTH_TYPE]); + if (!nl80211_valid_auth_type(params.auth_type)) + return -EINVAL; + } else + params.auth_type = NL80211_AUTHTYPE_AUTOMATIC; + + err = nl80211_crypto_settings(rdev, info, ¶ms.crypto, + NL80211_MAX_NR_CIPHER_SUITES); + if (err) + return err; + + err = rdev->ops->start_ap(&rdev->wiphy, dev, ¶ms); + if (!err) + wdev->beacon_interval = params.beacon_interval; return err; } -static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) +static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_beacon_data params; + int err; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + if (!rdev->ops->change_beacon) + return -EOPNOTSUPP; + + if (!wdev->beacon_interval) + return -EINVAL; + + err = nl80211_parse_beacon(info, ¶ms); + if (err) + return err; + + return rdev->ops->change_beacon(&rdev->wiphy, dev, ¶ms); +} + +static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; int err; - if (!rdev->ops->del_beacon) + if (!rdev->ops->stop_ap) return -EOPNOTSUPP; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EOPNOTSUPP; - err = rdev->ops->del_beacon(&rdev->wiphy, dev); + if (!wdev->beacon_interval) + return -ENOENT; + + err = rdev->ops->stop_ap(&rdev->wiphy, dev); if (!err) wdev->beacon_interval = 0; return err; @@ -6357,23 +6386,23 @@ static struct genl_ops nl80211_ops[] = { .cmd = NL80211_CMD_SET_BEACON, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .doit = nl80211_addset_beacon, + .doit = nl80211_set_beacon, .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, { - .cmd = NL80211_CMD_NEW_BEACON, + .cmd = NL80211_CMD_START_AP, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .doit = nl80211_addset_beacon, + .doit = nl80211_start_ap, .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, { - .cmd = NL80211_CMD_DEL_BEACON, + .cmd = NL80211_CMD_STOP_AP, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .doit = nl80211_del_beacon, + .doit = nl80211_stop_ap, .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, -- cgit From 4ff16c25e2cc48cbe6956e356c38a25ac063a64d Mon Sep 17 00:00:00 2001 From: David Smith Date: Tue, 7 Feb 2012 10:11:05 -0600 Subject: tracepoint, vfs, sched: Add exec() tracepoint Added a minimal exec tracepoint. Exec is an important major event in the life of a task, like fork(), clone() or exit(), all of which we already trace. [ We also do scheduling re-balancing during exec() - so it's useful from a scheduler instrumentation POV as well. ] If you want to watch a task start up, when it gets exec'ed is a good place to start. With the addition of this tracepoint, exec's can be monitored and better picture of general system activity can be obtained. This tracepoint will also enable better process life tracking, allowing you to answer questions like "what process keeps starting up binary X?". This tracepoint can also be useful in ftrace filtering and trigger conditions: i.e. starting or stopping filtering when exec is called. Signed-off-by: David Smith Signed-off-by: Peter Zijlstra Cc: Steven Rostedt Cc: Christoph Hellwig Cc: Al Viro Cc: Andrew Morton Cc: Linus Torvalds Link: http://lkml.kernel.org/r/4F314D19.7030504@redhat.com Signed-off-by: Ingo Molnar --- fs/exec.c | 9 ++++++--- include/trace/events/sched.h | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/fs/exec.c b/fs/exec.c index aeb135c7ff5c..d0d208092773 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -63,6 +63,8 @@ #include #include "internal.h" +#include + int core_uses_pid; char core_pattern[CORENAME_MAX_SIZE] = "core"; unsigned int core_pipe_limit; @@ -1401,9 +1403,10 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) */ bprm->recursion_depth = depth; if (retval >= 0) { - if (depth == 0) - ptrace_event(PTRACE_EVENT_EXEC, - old_pid); + if (depth == 0) { + trace_sched_process_exec(current, old_pid, bprm); + ptrace_event(PTRACE_EVENT_EXEC, old_pid); + } put_binfmt(fmt); allow_write_access(bprm->file); if (bprm->file) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 6ba596b07a72..e61ddfe8fe9e 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -6,6 +6,7 @@ #include #include +#include /* * Tracepoint for calling kthread_stop, performed to end a kthread: @@ -275,6 +276,32 @@ TRACE_EVENT(sched_process_fork, __entry->child_comm, __entry->child_pid) ); +/* + * Tracepoint for exec: + */ +TRACE_EVENT(sched_process_exec, + + TP_PROTO(struct task_struct *p, pid_t old_pid, + struct linux_binprm *bprm), + + TP_ARGS(p, old_pid, bprm), + + TP_STRUCT__entry( + __string( filename, bprm->filename ) + __field( pid_t, pid ) + __field( pid_t, old_pid ) + ), + + TP_fast_assign( + __assign_str(filename, bprm->filename); + __entry->pid = p->pid; + __entry->old_pid = p->pid; + ), + + TP_printk("filename=%s pid=%d old_pid=%d", __get_str(filename), + __entry->pid, __entry->old_pid) +); + /* * XXX the below sched_stat tracepoints only apply to SCHED_OTHER/BATCH/IDLE * adding sched_stat support to SCHED_FIFO/RR would be welcome. -- cgit From c03b355ea2938495bbdf25a4645be545be8890f4 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 21 Feb 2012 12:54:56 +0200 Subject: Bluetooth: Add l2cap_chan_lock Channel lock will be used to lock L2CAP channels which are locked currently by socket locks. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Reviewed-by: Ulisses Furquim Acked-by: Gustavo F. Padovan Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 11 +++++++++++ net/bluetooth/l2cap_core.c | 2 ++ 2 files changed, 13 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index bbb0e214e51d..d6d8ec8eb8cf 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -497,6 +497,7 @@ struct l2cap_chan { void *data; struct l2cap_ops *ops; + struct mutex lock; }; struct l2cap_ops { @@ -609,6 +610,16 @@ static inline void l2cap_chan_put(struct l2cap_chan *c) kfree(c); } +static inline void l2cap_chan_lock(struct l2cap_chan *chan) +{ + mutex_lock(&chan->lock); +} + +static inline void l2cap_chan_unlock(struct l2cap_chan *chan) +{ + mutex_unlock(&chan->lock); +} + static inline void l2cap_set_timer(struct l2cap_chan *chan, struct delayed_work *work, long timeout) { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8e8e9e93fb34..e39eba1ac4da 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -247,6 +247,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk) if (!chan) return NULL; + mutex_init(&chan->lock); + chan->sk = sk; write_lock(&chan_list_lock); -- cgit From 47990ea09d393da8fb6cf284f4dba704c3661973 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 22 Feb 2012 11:58:37 +0200 Subject: Bluetooth: mgmt: Make Set Link Security callable while powered off This patch makes it possible to change the Link Security setting while powered off and have it automatically enabled when powering on a device. To track the desired state once powered on a new HCI_LINK_SECURITY flag is added. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_event.c | 6 ++++++ net/bluetooth/mgmt.c | 33 ++++++++++++++++++++++++++++----- 3 files changed, 35 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 169d2f8cc4ee..806eb4120797 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -98,6 +98,7 @@ enum { HCI_HS_ENABLED, HCI_CONNECTABLE, HCI_DISCOVERABLE, + HCI_LINK_SECURITY, }; /* HCI ioctl defines */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 2a5d05c05e35..5fb1ee516d3a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -594,6 +594,12 @@ static void hci_setup(struct hci_dev *hdev) sizeof(cp), &cp); } + if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) { + u8 enable = 1; + hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, + sizeof(enable), &enable); + } + if (hdev->features[4] & LMP_LE) hci_set_le_support(hdev); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e8f890d7256a..69d4e1a699a3 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -410,7 +410,7 @@ static u32 get_current_settings(struct hci_dev *hdev) if (hdev->host_features[0] & LMP_HOST_LE) settings |= MGMT_SETTING_LE; - if (test_bit(HCI_AUTH, &hdev->flags)) + if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) settings |= MGMT_SETTING_LINK_SECURITY; if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) @@ -1067,8 +1067,21 @@ static int set_link_security(struct sock *sk, u16 index, void *data, u16 len) hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY, - MGMT_STATUS_NOT_POWERED); + bool changed = false; + + if (!!cp->val != test_bit(HCI_LINK_SECURITY, + &hdev->dev_flags)) { + change_bit(HCI_LINK_SECURITY, &hdev->dev_flags); + changed = true; + } + + err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev); + if (err < 0) + goto failed; + + if (changed) + err = new_settings(hdev, sk); + goto failed; } @@ -3338,7 +3351,8 @@ int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status) { struct cmd_lookup match = { NULL, hdev }; - int err; + bool changed = false; + int err = 0; if (status) { u8 mgmt_err = mgmt_status(status); @@ -3347,10 +3361,19 @@ int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status) return 0; } + if (test_bit(HCI_AUTH, &hdev->flags)) { + if (!test_and_set_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) + changed = true; + } else { + if (test_and_clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) + changed = true; + } + mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp, &match); - err = new_settings(hdev, match.sk); + if (changed) + err = new_settings(hdev, match.sk); if (match.sk) sock_put(match.sk); -- cgit From c0ecddc2507da980af307aae40d6bcdea4c195dc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 22 Feb 2012 12:38:31 +0200 Subject: Bluetooth: mgmt: Make Set SSP command callable while powered off This patch makes it possible to enable SSP through mgmt even when powered off. The setting will then get automatically actiated when powering on. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_event.c | 20 ++++++++---------- net/bluetooth/mgmt.c | 44 ++++++++++++++++++++++++++++++++-------- 3 files changed, 46 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 094b5dbdb130..6ba3a4b1078e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1006,7 +1006,7 @@ int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status); -int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 status); +int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status); int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, u8 *randomizer, u8 status); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 1b1c3480a24d..240dc1640c04 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -427,21 +427,18 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%x", hdev->name, status); - if (status) - goto done; - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SSP_MODE); if (!sent) return; - if (*((u8 *) sent)) - set_bit(HCI_SSP_ENABLED, &hdev->dev_flags); - else - clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); - -done: if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_ssp_enable_complete(hdev, status); + mgmt_ssp_enable_complete(hdev, *((u8 *) sent), status); + else if (!status) { + if (*((u8 *) sent)) + set_bit(HCI_SSP_ENABLED, &hdev->dev_flags); + else + clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); + } } static u8 hci_get_inquiry_mode(struct hci_dev *hdev) @@ -560,7 +557,8 @@ static void hci_setup(struct hci_dev *hdev) if (hdev->hci_ver > BLUETOOTH_VER_1_1) hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL); - if (hdev->features[6] & LMP_SIMPLE_PAIR) { + if (hdev->features[6] & LMP_SIMPLE_PAIR && + test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { u8 mode = 0x01; hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 69d4e1a699a3..eefd08468002 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1138,9 +1138,23 @@ static int set_ssp(struct sock *sk, u16 index, void *data, u16 len) hci_dev_lock(hdev); + val = !!cp->val; + if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, index, MGMT_OP_SET_SSP, - MGMT_STATUS_NOT_POWERED); + bool changed = false; + + if (val != test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { + change_bit(HCI_SSP_ENABLED, &hdev->dev_flags); + changed = true; + } + + err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev); + if (err < 0) + goto failed; + + if (changed) + err = new_settings(hdev, sk); + goto failed; } @@ -1155,8 +1169,6 @@ static int set_ssp(struct sock *sk, u16 index, void *data, u16 len) goto failed; } - val = !!cp->val; - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) == val) { err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev); goto failed; @@ -3393,21 +3405,37 @@ static int clear_eir(struct hci_dev *hdev) return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); } -int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 status) +int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) { struct cmd_lookup match = { NULL, hdev }; - int err; + bool changed = false; + int err = 0; if (status) { u8 mgmt_err = mgmt_status(status); + + if (enable && test_and_clear_bit(HCI_SSP_ENABLED, + &hdev->dev_flags)) + err = new_settings(hdev, NULL); + mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, cmd_status_rsp, &mgmt_err); - return 0; + + return err; + } + + if (enable) { + if (!test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) + changed = true; + } else { + if (test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) + changed = true; } mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match); - err = new_settings(hdev, match.sk); + if (changed) + err = new_settings(hdev, match.sk); if (match.sk) { sock_put(match.sk); -- cgit From 06199cf86a84206cfdc96b8dc02d5c27efa8c60f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 22 Feb 2012 16:37:11 +0200 Subject: Bluetooth: mgmt: Implement Set LE command This patch implements support for the Set LE mgmt command. Now, in addition to the enable_le module parameter user space needs to send an explicit Enable LE command to enable LE support. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 7 ++- net/bluetooth/mgmt.c | 119 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 126 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 806eb4120797..c97cf0872ac9 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -96,6 +96,7 @@ enum { HCI_LE_SCAN, HCI_SSP_ENABLED, HCI_HS_ENABLED, + HCI_LE_ENABLED, HCI_CONNECTABLE, HCI_DISCOVERABLE, HCI_LINK_SECURITY, diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6ba3a4b1078e..abdaa7900edb 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1010,6 +1010,7 @@ int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status); int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, u8 *randomizer, u8 status); +int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status); int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 *eir, u16 eir_len); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3476d5c7b02d..498b71a0579a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -539,7 +539,7 @@ static void hci_set_le_support(struct hci_dev *hdev) memset(&cp, 0, sizeof(cp)); - if (enable_le) { + if (enable_le && test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { cp.le = 1; cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); } @@ -1130,10 +1130,15 @@ static inline void hci_cc_write_le_host_supported(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_cp_read_local_ext_features cp; + struct hci_cp_write_le_host_supported *sent; __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%x", hdev->name, status); + sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED); + if (sent && test_bit(HCI_MGMT, &hdev->dev_flags)) + mgmt_le_enable_complete(hdev, sent->le, status); + if (status) return; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ac8ba839a78b..8bc6a7a48732 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -407,7 +407,7 @@ static u32 get_current_settings(struct hci_dev *hdev) if (!(hdev->features[4] & LMP_NO_BREDR)) settings |= MGMT_SETTING_BREDR; - if (hdev->host_features[0] & LMP_HOST_LE) + if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) settings |= MGMT_SETTING_LE; if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) @@ -1231,6 +1231,82 @@ failed: return err; } +static int set_le(struct sock *sk, u16 index, void *data, u16 len) +{ + struct mgmt_mode *cp = data; + struct hci_cp_write_le_host_supported hci_cp; + struct pending_cmd *cmd; + struct hci_dev *hdev; + int err; + u8 val; + + BT_DBG("request for hci%u", index); + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_SET_LE, + MGMT_STATUS_INVALID_PARAMS); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_SET_LE, + MGMT_STATUS_INVALID_PARAMS); + + if (!enable_le || !(hdev->features[4] & LMP_LE)) { + err = cmd_status(sk, index, MGMT_OP_SET_LE, + MGMT_STATUS_NOT_SUPPORTED); + goto failed; + } + + val = !!cp->val; + + if (!hdev_is_powered(hdev)) { + bool changed = false; + + if (val != test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { + change_bit(HCI_LE_ENABLED, &hdev->dev_flags); + changed = true; + } + + err = send_settings_rsp(sk, MGMT_OP_SET_LE, hdev); + if (err < 0) + goto failed; + + if (changed) + err = new_settings(hdev, sk); + + goto failed; + } + + if (mgmt_pending_find(MGMT_OP_SET_LE, hdev)) { + err = cmd_status(sk, index, MGMT_OP_SET_LE, MGMT_STATUS_BUSY); + goto failed; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_SET_LE, hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + + memset(&hci_cp, 0, sizeof(hci_cp)); + + if (val) { + hci_cp.le = val; + hci_cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); + } + + err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, + sizeof(hci_cp), &hci_cp); + if (err < 0) { + mgmt_pending_remove(cmd); + goto failed; + } + +failed: + hci_dev_put(hdev); + return err; +} + static int add_uuid(struct sock *sk, u16 index, void *data, u16 len) { struct mgmt_cp_add_uuid *cp = data; @@ -2816,6 +2892,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_SET_HS: err = set_hs(sk, index, cp, len); break; + case MGMT_OP_SET_LE: + err = set_le(sk, index, cp, len); + break; case MGMT_OP_ADD_UUID: err = add_uuid(sk, index, cp, len); break; @@ -3521,6 +3600,44 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, return err; } +int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) +{ + struct cmd_lookup match = { NULL, hdev }; + bool changed = false; + int err = 0; + + if (status) { + u8 mgmt_err = mgmt_status(status); + + if (enable && test_and_clear_bit(HCI_LE_ENABLED, + &hdev->dev_flags)) + err = new_settings(hdev, NULL); + + mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, + cmd_status_rsp, &mgmt_err); + + return err; + } + + if (enable) { + if (!test_and_set_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + changed = true; + } else { + if (test_and_clear_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + changed = true; + } + + mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match); + + if (changed) + err = new_settings(hdev, match.sk); + + if (match.sk) + sock_put(match.sk); + + return err; +} + int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 *eir, u16 eir_len) -- cgit From 7f9a903c57bb42b9f7ad8fb7867859d3252229ab Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 22 Feb 2012 18:38:01 +0100 Subject: Bluetooth: Send management event for class of device changes Currently there are no events to other management sockets if the class of device got changed. So make sure they are sent. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_event.c | 13 +++++++++---- net/bluetooth/mgmt.c | 10 ++++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index abdaa7900edb..24dd770d442b 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1007,6 +1007,8 @@ int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status); int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status); +int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, + u8 status); int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, u8 *randomizer, u8 status); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e44e3fd68628..c79ffb955554 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -350,14 +350,19 @@ static void hci_cc_write_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%x", hdev->name, status); - if (status) - return; - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_CLASS_OF_DEV); if (!sent) return; - memcpy(hdev->dev_class, sent, 3); + hci_dev_lock(hdev); + + if (status == 0) + memcpy(hdev->dev_class, sent, 3); + + if (test_bit(HCI_MGMT, &hdev->dev_flags)) + mgmt_set_class_of_dev_complete(hdev, sent, status); + + hci_dev_unlock(hdev); } static void hci_cc_read_voice_setting(struct hci_dev *hdev, struct sk_buff *skb) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f7e111f30434..16bddd22713f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3546,6 +3546,16 @@ int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) return err; } +int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, + u8 status) +{ + int err; + + err = mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, 3, NULL); + + return err; +} + int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) { struct pending_cmd *cmd; -- cgit From 490c5baba7a5ad80782d5eb778638d1cfc8d70ce Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 22 Feb 2012 19:19:09 +0200 Subject: Bluetooth: Add hdev->short_name for EIR generation It's possible to provide a short name through the mgmt interface and this name can be used for EIR generation when the full name doesn't fit there. This patch adds the preliminary tracking of the provided short name. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 +++ include/net/bluetooth/mgmt.h | 2 +- net/bluetooth/mgmt.c | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 24dd770d442b..3fcc7f0d08c3 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -129,6 +129,8 @@ struct le_scan_params { int timeout; }; +#define HCI_MAX_SHORT_NAME_LENGTH 10 + #define NUM_REASSEMBLY 4 struct hci_dev { struct list_head list; @@ -141,6 +143,7 @@ struct hci_dev { __u8 dev_type; bdaddr_t bdaddr; __u8 dev_name[HCI_MAX_NAME_LENGTH]; + __u8 short_name[HCI_MAX_SHORT_NAME_LENGTH]; __u8 eir[HCI_MAX_EIR_LENGTH]; __u8 dev_class[3]; __u8 major_class; diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index ac59cdd0fa1b..495668c77fb6 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -75,7 +75,7 @@ struct mgmt_rp_read_index_list { /* Reserve one extra byte for names in management messages so that they * are always guaranteed to be nul-terminated */ #define MGMT_MAX_NAME_LENGTH (HCI_MAX_NAME_LENGTH + 1) -#define MGMT_MAX_SHORT_NAME_LENGTH (10 + 1) +#define MGMT_MAX_SHORT_NAME_LENGTH (HCI_MAX_SHORT_NAME_LENGTH + 1) #define MGMT_SETTING_POWERED 0x00000001 #define MGMT_SETTING_CONNECTABLE 0x00000002 diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 16bddd22713f..3f6a2df9d150 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2273,6 +2273,9 @@ static int set_local_name(struct sock *sk, u16 index, void *data, goto failed; } + memcpy(hdev->short_name, mgmt_cp->short_name, + sizeof(hdev->short_name)); + memcpy(hci_cp.name, mgmt_cp->name, sizeof(hci_cp.name)); err = hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(hci_cp), &hci_cp); -- cgit From 9a395a80dc6a2004787539dcc0c7d167ba87e89a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 23 Feb 2012 00:00:32 +0200 Subject: Bluetooth: mgmt: Fix device_found parameters According to the latest mgmt API there's a flags field instead of a separate confirm_name paramter. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 5 ++++- net/bluetooth/mgmt.c | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 495668c77fb6..09646f5ef36a 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -393,11 +393,14 @@ struct mgmt_ev_auth_failed { __u8 status; } __packed; +#define MGMT_DEV_FOUND_CONFIRM_NAME 0x01 +#define MGMT_DEV_FOUND_LEGACY_PAIRING 0x02 + #define MGMT_EV_DEVICE_FOUND 0x0012 struct mgmt_ev_device_found { struct mgmt_addr_info addr; __s8 rssi; - __u8 confirm_name; + __u8 flags[4]; __le16 eir_len; __u8 eir[0]; } __packed; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 36bebfb2d840..b7b10ca297d5 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3745,7 +3745,8 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, bacpy(&ev->addr.bdaddr, bdaddr); ev->addr.type = link_to_mgmt(link_type, addr_type); ev->rssi = rssi; - ev->confirm_name = cfm_name; + if (cfm_name) + ev->flags[0] |= MGMT_DEV_FOUND_CONFIRM_NAME; if (eir_len > 0) memcpy(ev->eir, eir, eir_len); -- cgit From 388fc8faf200f80159353eb86cde4ab75d0a0bbd Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 23 Feb 2012 00:38:59 +0200 Subject: Bluetooth: mgmt: Add legacy pairing info to dev_found events This patch makes sure that legacy pairing vs SSP infomation gets properly propageted to the device_found events in the form of the legacy pairing flag. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 5 +++-- net/bluetooth/hci_core.c | 8 +++++++- net/bluetooth/hci_event.c | 26 ++++++++++++++------------ net/bluetooth/mgmt.c | 4 +++- 4 files changed, 27 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3fcc7f0d08c3..720bdc26b7e9 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -407,7 +407,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev, void hci_inquiry_cache_update_resolve(struct hci_dev *hdev, struct inquiry_entry *ie); bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, - bool name_known); + bool name_known, bool *ssp); /* ----- HCI Connections ----- */ enum { @@ -1018,7 +1018,8 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status); int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *dev_class, s8 rssi, - u8 cfm_name, u8 *eir, u16 eir_len); + u8 cfm_name, u8 ssp, u8 *eir, + u16 eir_len); int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, s8 rssi, u8 *name, u8 name_len); int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 2ab78bfc108e..e6cbb8a1f47d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -466,15 +466,21 @@ void hci_inquiry_cache_update_resolve(struct hci_dev *hdev, } bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, - bool name_known) + bool name_known, bool *ssp) { struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *ie; BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr)); + if (ssp) + *ssp = data->ssp_mode; + ie = hci_inquiry_cache_lookup(hdev, &data->bdaddr); if (ie) { + if (ie->data.ssp_mode && ssp) + *ssp = true; + if (ie->name_state == NAME_NEEDED && data->rssi != ie->data.rssi) { ie->data.rssi = data->rssi; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 9b30587c0de6..276f3ac06089 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1696,7 +1696,7 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff * hci_dev_lock(hdev); for (; num_rsp; num_rsp--, info++) { - bool name_known; + bool name_known, ssp; bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; @@ -1707,9 +1707,9 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff * data.rssi = 0x00; data.ssp_mode = 0x00; - name_known = hci_inquiry_cache_update(hdev, &data, false); + name_known = hci_inquiry_cache_update(hdev, &data, false, &ssp); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, 0, !name_known, + info->dev_class, 0, !name_known, ssp, NULL, 0); } @@ -2783,7 +2783,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct { struct inquiry_data data; int num_rsp = *((__u8 *) skb->data); - bool name_known; + bool name_known, ssp; BT_DBG("%s num_rsp %d", hdev->name, num_rsp); @@ -2807,10 +2807,10 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct data.ssp_mode = 0x00; name_known = hci_inquiry_cache_update(hdev, &data, - false); + false, &ssp); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, - !name_known, NULL, 0); + !name_known, ssp, NULL, 0); } } else { struct inquiry_info_with_rssi *info = (void *) (skb->data + 1); @@ -2825,10 +2825,10 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct data.rssi = info->rssi; data.ssp_mode = 0x00; name_known = hci_inquiry_cache_update(hdev, &data, - false); + false, &ssp); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, - !name_known, NULL, 0); + !name_known, ssp, NULL, 0); } } @@ -2964,7 +2964,7 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct hci_dev_lock(hdev); for (; num_rsp; num_rsp--, info++) { - bool name_known; + bool name_known, ssp; bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; @@ -2982,10 +2982,11 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct else name_known = true; - name_known = hci_inquiry_cache_update(hdev, &data, name_known); + name_known = hci_inquiry_cache_update(hdev, &data, name_known, + &ssp); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, - !name_known, info->data, + !name_known, ssp, info->data, sizeof(info->data)); } @@ -3310,7 +3311,8 @@ static inline void hci_le_adv_report_evt(struct hci_dev *hdev, rssi = ev->data[ev->length]; mgmt_device_found(hdev, &ev->bdaddr, LE_LINK, ev->bdaddr_type, - NULL, rssi, 0, ev->data, ev->length); + NULL, rssi, 0, 1, ev->data, + ev->length); ptr += sizeof(*ev) + ev->length + 1; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b7b10ca297d5..42d665bdc01f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3730,7 +3730,7 @@ int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *dev_class, s8 rssi, - u8 cfm_name, u8 *eir, u16 eir_len) + u8 cfm_name, u8 ssp, u8 *eir, u16 eir_len) { char buf[512]; struct mgmt_ev_device_found *ev = (void *) buf; @@ -3747,6 +3747,8 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, ev->rssi = rssi; if (cfm_name) ev->flags[0] |= MGMT_DEV_FOUND_CONFIRM_NAME; + if (!ssp) + ev->flags[0] |= MGMT_DEV_FOUND_LEGACY_PAIRING; if (eir_len > 0) memcpy(ev->eir, eir, eir_len); -- cgit From 08c79b6133b70a6e3d462d11a89c80259ac66ec7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 23 Feb 2012 22:31:51 +0200 Subject: Bluetooth: mgmt: Add flags parameter to device_connected This patch updates the Device Connected events to match the latest API by adding a flags parameter to them. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 4 ++-- include/net/bluetooth/mgmt.h | 1 + net/bluetooth/hci_event.c | 8 ++++---- net/bluetooth/mgmt.c | 6 ++++-- 4 files changed, 11 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 720bdc26b7e9..facd7ed32b74 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -980,8 +980,8 @@ int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, u8 persistent); int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 *name, u8 name_len, - u8 *dev_class); + u8 addr_type, u32 flags, u8 *name, + u8 name_len, u8 *dev_class); int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type); int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 09646f5ef36a..7aab53e6b813 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -357,6 +357,7 @@ struct mgmt_ev_new_long_term_key { #define MGMT_EV_DEVICE_CONNECTED 0x000B struct mgmt_ev_device_connected { struct mgmt_addr_info addr; + __le32 flags; __le16 eir_len; __u8 eir[0]; } __packed; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3d1eef0df2a3..fb6543b60dec 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1368,7 +1368,7 @@ static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn, struct inquiry_entry *e; if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) - mgmt_device_connected(hdev, bdaddr, ACL_LINK, 0x00, + mgmt_device_connected(hdev, bdaddr, ACL_LINK, 0x00, 0, name, name_len, conn->dev_class); if (discov->state == DISCOVERY_STOPPED) @@ -2104,7 +2104,7 @@ static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); } else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) mgmt_device_connected(hdev, &conn->dst, conn->type, - conn->dst_type, NULL, 0, + conn->dst_type, 0, NULL, 0, conn->dev_class); if (!hci_outgoing_auth_needed(hdev, conn)) { @@ -2872,7 +2872,7 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); } else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) mgmt_device_connected(hdev, &conn->dst, conn->type, - conn->dst_type, NULL, 0, + conn->dst_type, 0, NULL, 0, conn->dev_class); if (!hci_outgoing_auth_needed(hdev, conn)) { @@ -3282,7 +3282,7 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) mgmt_device_connected(hdev, &ev->bdaddr, conn->type, - conn->dst_type, NULL, 0, 0); + conn->dst_type, 0, NULL, 0, 0); conn->sec_level = BT_SECURITY_LOW; conn->handle = __le16_to_cpu(ev->handle); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 93f2c1348add..79fe57573463 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3249,8 +3249,8 @@ int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent) } int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 *name, u8 name_len, - u8 *dev_class) + u8 addr_type, u32 flags, u8 *name, + u8 name_len, u8 *dev_class) { char buf[512]; struct mgmt_ev_device_connected *ev = (void *) buf; @@ -3259,6 +3259,8 @@ int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, bacpy(&ev->addr.bdaddr, bdaddr); ev->addr.type = link_to_mgmt(link_type, addr_type); + put_unaligned_le32(flags, &ev->flags); + if (name_len > 0) eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name, name_len); -- cgit From c95f0ba76f902bc8b540468b695bcfe8948e8e46 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 23 Feb 2012 22:54:38 +0200 Subject: Bluetooth: mgmt: Track pending class changes This patch adds a flag to track pending changes to the class of device. This is needed since we cannot cleanly handle multiple simultaneous commands and need to return a "busy" error status in the mgmt commands that might trigger a class change. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + net/bluetooth/mgmt.c | 29 +++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index c97cf0872ac9..05bd9aca4054 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -100,6 +100,7 @@ enum { HCI_CONNECTABLE, HCI_DISCOVERABLE, HCI_LINK_SECURITY, + HCI_PENDING_CLASS, }; /* HCI ioctl defines */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 79fe57573463..9f912dc71bae 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -570,6 +570,7 @@ static u8 get_service_classes(struct hci_dev *hdev) static int update_class(struct hci_dev *hdev) { u8 cod[3]; + int err; BT_DBG("%s", hdev->name); @@ -586,7 +587,11 @@ static int update_class(struct hci_dev *hdev) if (memcmp(cod, hdev->dev_class, 3) == 0) return 0; - return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); + err = hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); + if (err == 0) + set_bit(HCI_PENDING_CLASS, &hdev->dev_flags); + + return err; } static void service_cache_off(struct work_struct *work) @@ -1344,6 +1349,12 @@ static int add_uuid(struct sock *sk, u16 index, void *data, u16 len) hci_dev_lock(hdev); + if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { + err = cmd_status(sk, index, MGMT_OP_ADD_UUID, + MGMT_STATUS_BUSY); + goto failed; + } + uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC); if (!uuid) { err = -ENOMEM; @@ -1393,6 +1404,12 @@ static int remove_uuid(struct sock *sk, u16 index, void *data, u16 len) hci_dev_lock(hdev); + if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { + err = cmd_status(sk, index, MGMT_OP_REMOVE_UUID, + MGMT_STATUS_BUSY); + goto unlock; + } + if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) { err = hci_uuids_clear(hdev); @@ -1460,6 +1477,12 @@ static int set_dev_class(struct sock *sk, u16 index, void *data, u16 len) hci_dev_lock(hdev); + if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { + err = cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, + MGMT_STATUS_BUSY); + goto unlock; + } + hdev->major_class = cp->major; hdev->minor_class = cp->minor; @@ -3259,7 +3282,7 @@ int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, bacpy(&ev->addr.bdaddr, bdaddr); ev->addr.type = link_to_mgmt(link_type, addr_type); - put_unaligned_le32(flags, &ev->flags); + ev->flags = __cpu_to_le32(flags); if (name_len > 0) eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, @@ -3614,6 +3637,8 @@ int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, { int err; + clear_bit(HCI_PENDING_CLASS, &hdev->dev_flags); + err = mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, 3, NULL); return err; -- cgit From c5905afb0ee6550b42c49213da1c22d67316c194 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 24 Feb 2012 08:31:31 +0100 Subject: static keys: Introduce 'struct static_key', static_key_true()/false() and static_key_slow_[inc|dec]() So here's a boot tested patch on top of Jason's series that does all the cleanups I talked about and turns jump labels into a more intuitive to use facility. It should also address the various misconceptions and confusions that surround jump labels. Typical usage scenarios: #include struct static_key key = STATIC_KEY_INIT_TRUE; if (static_key_false(&key)) do unlikely code else do likely code Or: if (static_key_true(&key)) do likely code else do unlikely code The static key is modified via: static_key_slow_inc(&key); ... static_key_slow_dec(&key); The 'slow' prefix makes it abundantly clear that this is an expensive operation. I've updated all in-kernel code to use this everywhere. Note that I (intentionally) have not pushed through the rename blindly through to the lowest levels: the actual jump-label patching arch facility should be named like that, so we want to decouple jump labels from the static-key facility a bit. On non-jump-label enabled architectures static keys default to likely()/unlikely() branches. Signed-off-by: Ingo Molnar Acked-by: Jason Baron Acked-by: Steven Rostedt Cc: a.p.zijlstra@chello.nl Cc: mathieu.desnoyers@efficios.com Cc: davem@davemloft.net Cc: ddaney.cavm@gmail.com Cc: Linus Torvalds Link: http://lkml.kernel.org/r/20120222085809.GA26397@elte.hu Signed-off-by: Ingo Molnar --- arch/Kconfig | 29 ++++--- arch/ia64/include/asm/paravirt.h | 6 +- arch/ia64/kernel/paravirt.c | 4 +- arch/mips/include/asm/jump_label.h | 2 +- arch/powerpc/include/asm/jump_label.h | 2 +- arch/s390/include/asm/jump_label.h | 2 +- arch/sparc/include/asm/jump_label.h | 2 +- arch/x86/include/asm/jump_label.h | 6 +- arch/x86/include/asm/paravirt.h | 6 +- arch/x86/kernel/kvm.c | 4 +- arch/x86/kernel/paravirt.c | 4 +- arch/x86/kvm/mmu_audit.c | 8 +- include/linux/jump_label.h | 139 ++++++++++++++++++++++++---------- include/linux/netdevice.h | 4 +- include/linux/netfilter.h | 6 +- include/linux/perf_event.h | 12 +-- include/linux/static_key.h | 1 + include/linux/tracepoint.h | 8 +- include/net/sock.h | 6 +- kernel/events/core.c | 16 ++-- kernel/jump_label.c | 128 ++++++++++++++++++------------- kernel/sched/core.c | 18 ++--- kernel/sched/fair.c | 8 +- kernel/sched/sched.h | 14 ++-- kernel/tracepoint.c | 20 ++--- net/core/dev.c | 24 +++--- net/core/net-sysfs.c | 4 +- net/core/sock.c | 4 +- net/core/sysctl_net_core.c | 4 +- net/ipv4/tcp_memcontrol.c | 6 +- net/netfilter/core.c | 6 +- 31 files changed, 298 insertions(+), 205 deletions(-) create mode 100644 include/linux/static_key.h (limited to 'include') diff --git a/arch/Kconfig b/arch/Kconfig index 4f55c736be11..5b448a74d0f7 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -47,18 +47,29 @@ config KPROBES If in doubt, say "N". config JUMP_LABEL - bool "Optimize trace point call sites" + bool "Optimize very unlikely/likely branches" depends on HAVE_ARCH_JUMP_LABEL help + This option enables a transparent branch optimization that + makes certain almost-always-true or almost-always-false branch + conditions even cheaper to execute within the kernel. + + Certain performance-sensitive kernel code, such as trace points, + scheduler functionality, networking code and KVM have such + branches and include support for this optimization technique. + If it is detected that the compiler has support for "asm goto", - the kernel will compile trace point locations with just a - nop instruction. When trace points are enabled, the nop will - be converted to a jump to the trace function. This technique - lowers overhead and stress on the branch prediction of the - processor. - - On i386, options added to the compiler flags may increase - the size of the kernel slightly. + the kernel will compile such branches with just a nop + instruction. When the condition flag is toggled to true, the + nop will be converted to a jump instruction to execute the + conditional block of instructions. + + This technique lowers overhead and stress on the branch prediction + of the processor and generally makes the kernel faster. The update + of the condition is slower, but those are always very rare. + + ( On 32-bit x86, the necessary options added to the compiler + flags may increase the size of the kernel slightly. ) config OPTPROBES def_bool y diff --git a/arch/ia64/include/asm/paravirt.h b/arch/ia64/include/asm/paravirt.h index 32551d304cd7..b149b88ea795 100644 --- a/arch/ia64/include/asm/paravirt.h +++ b/arch/ia64/include/asm/paravirt.h @@ -281,9 +281,9 @@ paravirt_init_missing_ticks_accounting(int cpu) pv_time_ops.init_missing_ticks_accounting(cpu); } -struct jump_label_key; -extern struct jump_label_key paravirt_steal_enabled; -extern struct jump_label_key paravirt_steal_rq_enabled; +struct static_key; +extern struct static_key paravirt_steal_enabled; +extern struct static_key paravirt_steal_rq_enabled; static inline int paravirt_do_steal_accounting(unsigned long *new_itm) diff --git a/arch/ia64/kernel/paravirt.c b/arch/ia64/kernel/paravirt.c index 100868216c55..1b22f6de2932 100644 --- a/arch/ia64/kernel/paravirt.c +++ b/arch/ia64/kernel/paravirt.c @@ -634,8 +634,8 @@ struct pv_irq_ops pv_irq_ops = { * pv_time_ops * time operations */ -struct jump_label_key paravirt_steal_enabled; -struct jump_label_key paravirt_steal_rq_enabled; +struct static_key paravirt_steal_enabled; +struct static_key paravirt_steal_rq_enabled; static int ia64_native_do_steal_accounting(unsigned long *new_itm) diff --git a/arch/mips/include/asm/jump_label.h b/arch/mips/include/asm/jump_label.h index 1881b316ca45..4d6d77ed9b9d 100644 --- a/arch/mips/include/asm/jump_label.h +++ b/arch/mips/include/asm/jump_label.h @@ -20,7 +20,7 @@ #define WORD_INSN ".word" #endif -static __always_inline bool arch_static_branch(struct jump_label_key *key) +static __always_inline bool arch_static_branch(struct static_key *key) { asm goto("1:\tnop\n\t" "nop\n\t" diff --git a/arch/powerpc/include/asm/jump_label.h b/arch/powerpc/include/asm/jump_label.h index 938986e412f1..ae098c438f00 100644 --- a/arch/powerpc/include/asm/jump_label.h +++ b/arch/powerpc/include/asm/jump_label.h @@ -17,7 +17,7 @@ #define JUMP_ENTRY_TYPE stringify_in_c(FTR_ENTRY_LONG) #define JUMP_LABEL_NOP_SIZE 4 -static __always_inline bool arch_static_branch(struct jump_label_key *key) +static __always_inline bool arch_static_branch(struct static_key *key) { asm goto("1:\n\t" "nop\n\t" diff --git a/arch/s390/include/asm/jump_label.h b/arch/s390/include/asm/jump_label.h index 95a6cf2b5b67..6c32190dc73e 100644 --- a/arch/s390/include/asm/jump_label.h +++ b/arch/s390/include/asm/jump_label.h @@ -13,7 +13,7 @@ #define ASM_ALIGN ".balign 4" #endif -static __always_inline bool arch_static_branch(struct jump_label_key *key) +static __always_inline bool arch_static_branch(struct static_key *key) { asm goto("0: brcl 0,0\n" ".pushsection __jump_table, \"aw\"\n" diff --git a/arch/sparc/include/asm/jump_label.h b/arch/sparc/include/asm/jump_label.h index fc73a82366f8..5080d16a832f 100644 --- a/arch/sparc/include/asm/jump_label.h +++ b/arch/sparc/include/asm/jump_label.h @@ -7,7 +7,7 @@ #define JUMP_LABEL_NOP_SIZE 4 -static __always_inline bool arch_static_branch(struct jump_label_key *key) +static __always_inline bool arch_static_branch(struct static_key *key) { asm goto("1:\n\t" "nop\n\t" diff --git a/arch/x86/include/asm/jump_label.h b/arch/x86/include/asm/jump_label.h index a32b18ce6ead..3a16c1483b45 100644 --- a/arch/x86/include/asm/jump_label.h +++ b/arch/x86/include/asm/jump_label.h @@ -9,12 +9,12 @@ #define JUMP_LABEL_NOP_SIZE 5 -#define JUMP_LABEL_INITIAL_NOP ".byte 0xe9 \n\t .long 0\n\t" +#define STATIC_KEY_INITIAL_NOP ".byte 0xe9 \n\t .long 0\n\t" -static __always_inline bool arch_static_branch(struct jump_label_key *key) +static __always_inline bool arch_static_branch(struct static_key *key) { asm goto("1:" - JUMP_LABEL_INITIAL_NOP + STATIC_KEY_INITIAL_NOP ".pushsection __jump_table, \"aw\" \n\t" _ASM_ALIGN "\n\t" _ASM_PTR "1b, %l[l_yes], %c0 \n\t" diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h index a7d2db9a74fb..c0180fd372d2 100644 --- a/arch/x86/include/asm/paravirt.h +++ b/arch/x86/include/asm/paravirt.h @@ -230,9 +230,9 @@ static inline unsigned long long paravirt_sched_clock(void) return PVOP_CALL0(unsigned long long, pv_time_ops.sched_clock); } -struct jump_label_key; -extern struct jump_label_key paravirt_steal_enabled; -extern struct jump_label_key paravirt_steal_rq_enabled; +struct static_key; +extern struct static_key paravirt_steal_enabled; +extern struct static_key paravirt_steal_rq_enabled; static inline u64 paravirt_steal_clock(int cpu) { diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c index f0c6fd6f176b..694d801bf606 100644 --- a/arch/x86/kernel/kvm.c +++ b/arch/x86/kernel/kvm.c @@ -438,9 +438,9 @@ void __init kvm_guest_init(void) static __init int activate_jump_labels(void) { if (has_steal_clock) { - jump_label_inc(¶virt_steal_enabled); + static_key_slow_inc(¶virt_steal_enabled); if (steal_acc) - jump_label_inc(¶virt_steal_rq_enabled); + static_key_slow_inc(¶virt_steal_rq_enabled); } return 0; diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c index d90272e6bc40..ada2f99388dd 100644 --- a/arch/x86/kernel/paravirt.c +++ b/arch/x86/kernel/paravirt.c @@ -202,8 +202,8 @@ static void native_flush_tlb_single(unsigned long addr) __native_flush_tlb_single(addr); } -struct jump_label_key paravirt_steal_enabled; -struct jump_label_key paravirt_steal_rq_enabled; +struct static_key paravirt_steal_enabled; +struct static_key paravirt_steal_rq_enabled; static u64 native_steal_clock(int cpu) { diff --git a/arch/x86/kvm/mmu_audit.c b/arch/x86/kvm/mmu_audit.c index fe15dcc07a6b..ea7b4fd34676 100644 --- a/arch/x86/kvm/mmu_audit.c +++ b/arch/x86/kvm/mmu_audit.c @@ -234,7 +234,7 @@ static void audit_vcpu_spte(struct kvm_vcpu *vcpu) } static bool mmu_audit; -static struct jump_label_key mmu_audit_key; +static struct static_key mmu_audit_key; static void __kvm_mmu_audit(struct kvm_vcpu *vcpu, int point) { @@ -250,7 +250,7 @@ static void __kvm_mmu_audit(struct kvm_vcpu *vcpu, int point) static inline void kvm_mmu_audit(struct kvm_vcpu *vcpu, int point) { - if (static_branch((&mmu_audit_key))) + if (static_key_false((&mmu_audit_key))) __kvm_mmu_audit(vcpu, point); } @@ -259,7 +259,7 @@ static void mmu_audit_enable(void) if (mmu_audit) return; - jump_label_inc(&mmu_audit_key); + static_key_slow_inc(&mmu_audit_key); mmu_audit = true; } @@ -268,7 +268,7 @@ static void mmu_audit_disable(void) if (!mmu_audit) return; - jump_label_dec(&mmu_audit_key); + static_key_slow_dec(&mmu_audit_key); mmu_audit = false; } diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index f7c69580fea7..2172da2d9bb4 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h @@ -9,15 +9,15 @@ * * Jump labels provide an interface to generate dynamic branches using * self-modifying code. Assuming toolchain and architecture support the result - * of a "if (static_branch(&key))" statement is a unconditional branch (which + * of a "if (static_key_false(&key))" statement is a unconditional branch (which * defaults to false - and the true block is placed out of line). * - * However at runtime we can change the 'static' branch target using - * jump_label_{inc,dec}(). These function as a 'reference' count on the key + * However at runtime we can change the branch target using + * static_key_slow_{inc,dec}(). These function as a 'reference' count on the key * object and for as long as there are references all branches referring to * that particular key will point to the (out of line) true block. * - * Since this relies on modifying code the jump_label_{inc,dec}() functions + * Since this relies on modifying code the static_key_slow_{inc,dec}() functions * must be considered absolute slow paths (machine wide synchronization etc.). * OTOH, since the affected branches are unconditional their runtime overhead * will be absolutely minimal, esp. in the default (off) case where the total @@ -26,12 +26,26 @@ * * When the control is directly exposed to userspace it is prudent to delay the * decrement to avoid high frequency code modifications which can (and do) - * cause significant performance degradation. Struct jump_label_key_deferred and - * jump_label_dec_deferred() provide for this. + * cause significant performance degradation. Struct static_key_deferred and + * static_key_slow_dec_deferred() provide for this. * * Lacking toolchain and or architecture support, it falls back to a simple * conditional branch. - */ + * + * struct static_key my_key = STATIC_KEY_INIT_TRUE; + * + * if (static_key_true(&my_key)) { + * } + * + * will result in the true case being in-line and starts the key with a single + * reference. Mixing static_key_true() and static_key_false() on the same key is not + * allowed. + * + * Not initializing the key (static data is initialized to 0s anyway) is the + * same as using STATIC_KEY_INIT_FALSE and static_key_false() is + * equivalent with static_branch(). + * +*/ #include #include @@ -39,16 +53,17 @@ #if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_JUMP_LABEL) -struct jump_label_key { +struct static_key { atomic_t enabled; +/* Set lsb bit to 1 if branch is default true, 0 ot */ struct jump_entry *entries; #ifdef CONFIG_MODULES - struct jump_label_mod *next; + struct static_key_mod *next; #endif }; -struct jump_label_key_deferred { - struct jump_label_key key; +struct static_key_deferred { + struct static_key key; unsigned long timeout; struct delayed_work work; }; @@ -66,13 +81,34 @@ struct module; #ifdef HAVE_JUMP_LABEL -#ifdef CONFIG_MODULES -#define JUMP_LABEL_INIT {ATOMIC_INIT(0), NULL, NULL} -#else -#define JUMP_LABEL_INIT {ATOMIC_INIT(0), NULL} -#endif +#define JUMP_LABEL_TRUE_BRANCH 1UL + +static +inline struct jump_entry *jump_label_get_entries(struct static_key *key) +{ + return (struct jump_entry *)((unsigned long)key->entries + & ~JUMP_LABEL_TRUE_BRANCH); +} + +static inline bool jump_label_get_branch_default(struct static_key *key) +{ + if ((unsigned long)key->entries & JUMP_LABEL_TRUE_BRANCH) + return true; + return false; +} + +static __always_inline bool static_key_false(struct static_key *key) +{ + return arch_static_branch(key); +} -static __always_inline bool static_branch(struct jump_label_key *key) +static __always_inline bool static_key_true(struct static_key *key) +{ + return !static_key_false(key); +} + +/* Deprecated. Please use 'static_key_false() instead. */ +static __always_inline bool static_branch(struct static_key *key) { return arch_static_branch(key); } @@ -88,21 +124,24 @@ extern void arch_jump_label_transform(struct jump_entry *entry, extern void arch_jump_label_transform_static(struct jump_entry *entry, enum jump_label_type type); extern int jump_label_text_reserved(void *start, void *end); -extern void jump_label_inc(struct jump_label_key *key); -extern void jump_label_dec(struct jump_label_key *key); -extern void jump_label_dec_deferred(struct jump_label_key_deferred *key); -extern bool jump_label_enabled(struct jump_label_key *key); +extern void static_key_slow_inc(struct static_key *key); +extern void static_key_slow_dec(struct static_key *key); +extern void static_key_slow_dec_deferred(struct static_key_deferred *key); +extern bool static_key_enabled(struct static_key *key); extern void jump_label_apply_nops(struct module *mod); -extern void jump_label_rate_limit(struct jump_label_key_deferred *key, - unsigned long rl); +extern void +jump_label_rate_limit(struct static_key_deferred *key, unsigned long rl); + +#define STATIC_KEY_INIT_TRUE ((struct static_key) \ + { .enabled = ATOMIC_INIT(1), .entries = (void *)1 }) +#define STATIC_KEY_INIT_FALSE ((struct static_key) \ + { .enabled = ATOMIC_INIT(0), .entries = (void *)0 }) #else /* !HAVE_JUMP_LABEL */ #include -#define JUMP_LABEL_INIT {ATOMIC_INIT(0)} - -struct jump_label_key { +struct static_key { atomic_t enabled; }; @@ -110,30 +149,45 @@ static __always_inline void jump_label_init(void) { } -struct jump_label_key_deferred { - struct jump_label_key key; +struct static_key_deferred { + struct static_key key; }; -static __always_inline bool static_branch(struct jump_label_key *key) +static __always_inline bool static_key_false(struct static_key *key) +{ + if (unlikely(atomic_read(&key->enabled)) > 0) + return true; + return false; +} + +static __always_inline bool static_key_true(struct static_key *key) { - if (unlikely(atomic_read(&key->enabled))) + if (likely(atomic_read(&key->enabled)) > 0) return true; return false; } -static inline void jump_label_inc(struct jump_label_key *key) +/* Deprecated. Please use 'static_key_false() instead. */ +static __always_inline bool static_branch(struct static_key *key) +{ + if (unlikely(atomic_read(&key->enabled)) > 0) + return true; + return false; +} + +static inline void static_key_slow_inc(struct static_key *key) { atomic_inc(&key->enabled); } -static inline void jump_label_dec(struct jump_label_key *key) +static inline void static_key_slow_dec(struct static_key *key) { atomic_dec(&key->enabled); } -static inline void jump_label_dec_deferred(struct jump_label_key_deferred *key) +static inline void static_key_slow_dec_deferred(struct static_key_deferred *key) { - jump_label_dec(&key->key); + static_key_slow_dec(&key->key); } static inline int jump_label_text_reserved(void *start, void *end) @@ -144,9 +198,9 @@ static inline int jump_label_text_reserved(void *start, void *end) static inline void jump_label_lock(void) {} static inline void jump_label_unlock(void) {} -static inline bool jump_label_enabled(struct jump_label_key *key) +static inline bool static_key_enabled(struct static_key *key) { - return !!atomic_read(&key->enabled); + return (atomic_read(&key->enabled) > 0); } static inline int jump_label_apply_nops(struct module *mod) @@ -154,13 +208,20 @@ static inline int jump_label_apply_nops(struct module *mod) return 0; } -static inline void jump_label_rate_limit(struct jump_label_key_deferred *key, +static inline void +jump_label_rate_limit(struct static_key_deferred *key, unsigned long rl) { } + +#define STATIC_KEY_INIT_TRUE ((struct static_key) \ + { .enabled = ATOMIC_INIT(1) }) +#define STATIC_KEY_INIT_FALSE ((struct static_key) \ + { .enabled = ATOMIC_INIT(0) }) + #endif /* HAVE_JUMP_LABEL */ -#define jump_label_key_enabled ((struct jump_label_key){ .enabled = ATOMIC_INIT(1), }) -#define jump_label_key_disabled ((struct jump_label_key){ .enabled = ATOMIC_INIT(0), }) +#define STATIC_KEY_INIT STATIC_KEY_INIT_FALSE +#define jump_label_enabled static_key_enabled #endif /* _LINUX_JUMP_LABEL_H */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0eac07c95255..7dfaae7846ab 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -214,8 +214,8 @@ enum { #include #ifdef CONFIG_RPS -#include -extern struct jump_label_key rps_needed; +#include +extern struct static_key rps_needed; #endif struct neighbour; diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index b809265607d0..29734be334c1 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -163,13 +163,13 @@ extern struct ctl_path nf_net_ipv4_netfilter_sysctl_path[]; extern struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; #if defined(CONFIG_JUMP_LABEL) -#include -extern struct jump_label_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; +#include +extern struct static_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; static inline bool nf_hooks_active(u_int8_t pf, unsigned int hook) { if (__builtin_constant_p(pf) && __builtin_constant_p(hook)) - return static_branch(&nf_hooks_needed[pf][hook]); + return static_key_false(&nf_hooks_needed[pf][hook]); return !list_empty(&nf_hooks[pf][hook]); } diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 412b790f5da6..0d21e6f1cf53 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -514,7 +514,7 @@ struct perf_guest_info_callbacks { #include #include #include -#include +#include #include #include @@ -1038,7 +1038,7 @@ static inline int is_software_event(struct perf_event *event) return event->pmu->task_ctx_nr == perf_sw_context; } -extern struct jump_label_key perf_swevent_enabled[PERF_COUNT_SW_MAX]; +extern struct static_key perf_swevent_enabled[PERF_COUNT_SW_MAX]; extern void __perf_sw_event(u32, u64, struct pt_regs *, u64); @@ -1066,7 +1066,7 @@ perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr) { struct pt_regs hot_regs; - if (static_branch(&perf_swevent_enabled[event_id])) { + if (static_key_false(&perf_swevent_enabled[event_id])) { if (!regs) { perf_fetch_caller_regs(&hot_regs); regs = &hot_regs; @@ -1075,12 +1075,12 @@ perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr) } } -extern struct jump_label_key_deferred perf_sched_events; +extern struct static_key_deferred perf_sched_events; static inline void perf_event_task_sched_in(struct task_struct *prev, struct task_struct *task) { - if (static_branch(&perf_sched_events.key)) + if (static_key_false(&perf_sched_events.key)) __perf_event_task_sched_in(prev, task); } @@ -1089,7 +1089,7 @@ static inline void perf_event_task_sched_out(struct task_struct *prev, { perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, NULL, 0); - if (static_branch(&perf_sched_events.key)) + if (static_key_false(&perf_sched_events.key)) __perf_event_task_sched_out(prev, next); } diff --git a/include/linux/static_key.h b/include/linux/static_key.h new file mode 100644 index 000000000000..27bd3f8a0857 --- /dev/null +++ b/include/linux/static_key.h @@ -0,0 +1 @@ +#include diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index fc36da97ff7e..bd96ecd0e05c 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -17,7 +17,7 @@ #include #include #include -#include +#include struct module; struct tracepoint; @@ -29,7 +29,7 @@ struct tracepoint_func { struct tracepoint { const char *name; /* Tracepoint name */ - struct jump_label_key key; + struct static_key key; void (*regfunc)(void); void (*unregfunc)(void); struct tracepoint_func __rcu *funcs; @@ -145,7 +145,7 @@ static inline void tracepoint_synchronize_unregister(void) extern struct tracepoint __tracepoint_##name; \ static inline void trace_##name(proto) \ { \ - if (static_branch(&__tracepoint_##name.key)) \ + if (static_key_false(&__tracepoint_##name.key)) \ __DO_TRACE(&__tracepoint_##name, \ TP_PROTO(data_proto), \ TP_ARGS(data_args), \ @@ -188,7 +188,7 @@ static inline void tracepoint_synchronize_unregister(void) __attribute__((section("__tracepoints_strings"))) = #name; \ struct tracepoint __tracepoint_##name \ __attribute__((section("__tracepoints"))) = \ - { __tpstrtab_##name, JUMP_LABEL_INIT, reg, unreg, NULL };\ + { __tpstrtab_##name, STATIC_KEY_INIT_FALSE, reg, unreg, NULL };\ static struct tracepoint * const __tracepoint_ptr_##name __used \ __attribute__((section("__tracepoints_ptrs"))) = \ &__tracepoint_##name; diff --git a/include/net/sock.h b/include/net/sock.h index 91c1c8baf020..dcde2d9268cd 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -55,7 +55,7 @@ #include #include #include -#include +#include #include #include @@ -924,13 +924,13 @@ inline void sk_refcnt_debug_release(const struct sock *sk) #endif /* SOCK_REFCNT_DEBUG */ #if defined(CONFIG_CGROUP_MEM_RES_CTLR_KMEM) && defined(CONFIG_NET) -extern struct jump_label_key memcg_socket_limit_enabled; +extern struct static_key memcg_socket_limit_enabled; static inline struct cg_proto *parent_cg_proto(struct proto *proto, struct cg_proto *cg_proto) { return proto->proto_cgroup(parent_mem_cgroup(cg_proto->memcg)); } -#define mem_cgroup_sockets_enabled static_branch(&memcg_socket_limit_enabled) +#define mem_cgroup_sockets_enabled static_key_false(&memcg_socket_limit_enabled) #else #define mem_cgroup_sockets_enabled 0 static inline struct cg_proto *parent_cg_proto(struct proto *proto, diff --git a/kernel/events/core.c b/kernel/events/core.c index 7c3b9de55f6b..5e0f8bb89b2b 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -128,7 +128,7 @@ enum event_type_t { * perf_sched_events : >0 events exist * perf_cgroup_events: >0 per-cpu cgroup events exist on this cpu */ -struct jump_label_key_deferred perf_sched_events __read_mostly; +struct static_key_deferred perf_sched_events __read_mostly; static DEFINE_PER_CPU(atomic_t, perf_cgroup_events); static atomic_t nr_mmap_events __read_mostly; @@ -2769,7 +2769,7 @@ static void free_event(struct perf_event *event) if (!event->parent) { if (event->attach_state & PERF_ATTACH_TASK) - jump_label_dec_deferred(&perf_sched_events); + static_key_slow_dec_deferred(&perf_sched_events); if (event->attr.mmap || event->attr.mmap_data) atomic_dec(&nr_mmap_events); if (event->attr.comm) @@ -2780,7 +2780,7 @@ static void free_event(struct perf_event *event) put_callchain_buffers(); if (is_cgroup_event(event)) { atomic_dec(&per_cpu(perf_cgroup_events, event->cpu)); - jump_label_dec_deferred(&perf_sched_events); + static_key_slow_dec_deferred(&perf_sched_events); } } @@ -4982,7 +4982,7 @@ fail: return err; } -struct jump_label_key perf_swevent_enabled[PERF_COUNT_SW_MAX]; +struct static_key perf_swevent_enabled[PERF_COUNT_SW_MAX]; static void sw_perf_event_destroy(struct perf_event *event) { @@ -4990,7 +4990,7 @@ static void sw_perf_event_destroy(struct perf_event *event) WARN_ON(event->parent); - jump_label_dec(&perf_swevent_enabled[event_id]); + static_key_slow_dec(&perf_swevent_enabled[event_id]); swevent_hlist_put(event); } @@ -5020,7 +5020,7 @@ static int perf_swevent_init(struct perf_event *event) if (err) return err; - jump_label_inc(&perf_swevent_enabled[event_id]); + static_key_slow_inc(&perf_swevent_enabled[event_id]); event->destroy = sw_perf_event_destroy; } @@ -5843,7 +5843,7 @@ done: if (!event->parent) { if (event->attach_state & PERF_ATTACH_TASK) - jump_label_inc(&perf_sched_events.key); + static_key_slow_inc(&perf_sched_events.key); if (event->attr.mmap || event->attr.mmap_data) atomic_inc(&nr_mmap_events); if (event->attr.comm) @@ -6081,7 +6081,7 @@ SYSCALL_DEFINE5(perf_event_open, * - that may need work on context switch */ atomic_inc(&per_cpu(perf_cgroup_events, event->cpu)); - jump_label_inc(&perf_sched_events.key); + static_key_slow_inc(&perf_sched_events.key); } /* diff --git a/kernel/jump_label.c b/kernel/jump_label.c index 543782e7cdd2..bf9dcadbb53a 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #ifdef HAVE_JUMP_LABEL @@ -29,10 +29,11 @@ void jump_label_unlock(void) mutex_unlock(&jump_label_mutex); } -bool jump_label_enabled(struct jump_label_key *key) +bool static_key_enabled(struct static_key *key) { - return !!atomic_read(&key->enabled); + return (atomic_read(&key->enabled) > 0); } +EXPORT_SYMBOL_GPL(static_key_enabled); static int jump_label_cmp(const void *a, const void *b) { @@ -58,22 +59,26 @@ jump_label_sort_entries(struct jump_entry *start, struct jump_entry *stop) sort(start, size, sizeof(struct jump_entry), jump_label_cmp, NULL); } -static void jump_label_update(struct jump_label_key *key, int enable); +static void jump_label_update(struct static_key *key, int enable); -void jump_label_inc(struct jump_label_key *key) +void static_key_slow_inc(struct static_key *key) { if (atomic_inc_not_zero(&key->enabled)) return; jump_label_lock(); - if (atomic_read(&key->enabled) == 0) - jump_label_update(key, JUMP_LABEL_ENABLE); + if (atomic_read(&key->enabled) == 0) { + if (!jump_label_get_branch_default(key)) + jump_label_update(key, JUMP_LABEL_ENABLE); + else + jump_label_update(key, JUMP_LABEL_DISABLE); + } atomic_inc(&key->enabled); jump_label_unlock(); } -EXPORT_SYMBOL_GPL(jump_label_inc); +EXPORT_SYMBOL_GPL(static_key_slow_inc); -static void __jump_label_dec(struct jump_label_key *key, +static void __static_key_slow_dec(struct static_key *key, unsigned long rate_limit, struct delayed_work *work) { if (!atomic_dec_and_mutex_lock(&key->enabled, &jump_label_mutex)) { @@ -85,32 +90,35 @@ static void __jump_label_dec(struct jump_label_key *key, if (rate_limit) { atomic_inc(&key->enabled); schedule_delayed_work(work, rate_limit); - } else - jump_label_update(key, JUMP_LABEL_DISABLE); - + } else { + if (!jump_label_get_branch_default(key)) + jump_label_update(key, JUMP_LABEL_DISABLE); + else + jump_label_update(key, JUMP_LABEL_ENABLE); + } jump_label_unlock(); } -EXPORT_SYMBOL_GPL(jump_label_dec); static void jump_label_update_timeout(struct work_struct *work) { - struct jump_label_key_deferred *key = - container_of(work, struct jump_label_key_deferred, work.work); - __jump_label_dec(&key->key, 0, NULL); + struct static_key_deferred *key = + container_of(work, struct static_key_deferred, work.work); + __static_key_slow_dec(&key->key, 0, NULL); } -void jump_label_dec(struct jump_label_key *key) +void static_key_slow_dec(struct static_key *key) { - __jump_label_dec(key, 0, NULL); + __static_key_slow_dec(key, 0, NULL); } +EXPORT_SYMBOL_GPL(static_key_slow_dec); -void jump_label_dec_deferred(struct jump_label_key_deferred *key) +void static_key_slow_dec_deferred(struct static_key_deferred *key) { - __jump_label_dec(&key->key, key->timeout, &key->work); + __static_key_slow_dec(&key->key, key->timeout, &key->work); } +EXPORT_SYMBOL_GPL(static_key_slow_dec_deferred); - -void jump_label_rate_limit(struct jump_label_key_deferred *key, +void jump_label_rate_limit(struct static_key_deferred *key, unsigned long rl) { key->timeout = rl; @@ -153,7 +161,7 @@ void __weak __init_or_module arch_jump_label_transform_static(struct jump_entry arch_jump_label_transform(entry, type); } -static void __jump_label_update(struct jump_label_key *key, +static void __jump_label_update(struct static_key *key, struct jump_entry *entry, struct jump_entry *stop, int enable) { @@ -170,27 +178,40 @@ static void __jump_label_update(struct jump_label_key *key, } } +static enum jump_label_type jump_label_type(struct static_key *key) +{ + bool true_branch = jump_label_get_branch_default(key); + bool state = static_key_enabled(key); + + if ((!true_branch && state) || (true_branch && !state)) + return JUMP_LABEL_ENABLE; + + return JUMP_LABEL_DISABLE; +} + void __init jump_label_init(void) { struct jump_entry *iter_start = __start___jump_table; struct jump_entry *iter_stop = __stop___jump_table; - struct jump_label_key *key = NULL; + struct static_key *key = NULL; struct jump_entry *iter; jump_label_lock(); jump_label_sort_entries(iter_start, iter_stop); for (iter = iter_start; iter < iter_stop; iter++) { - struct jump_label_key *iterk; + struct static_key *iterk; - iterk = (struct jump_label_key *)(unsigned long)iter->key; - arch_jump_label_transform_static(iter, jump_label_enabled(iterk) ? - JUMP_LABEL_ENABLE : JUMP_LABEL_DISABLE); + iterk = (struct static_key *)(unsigned long)iter->key; + arch_jump_label_transform_static(iter, jump_label_type(iterk)); if (iterk == key) continue; key = iterk; - key->entries = iter; + /* + * Set key->entries to iter, but preserve JUMP_LABEL_TRUE_BRANCH. + */ + *((unsigned long *)&key->entries) += (unsigned long)iter; #ifdef CONFIG_MODULES key->next = NULL; #endif @@ -200,8 +221,8 @@ void __init jump_label_init(void) #ifdef CONFIG_MODULES -struct jump_label_mod { - struct jump_label_mod *next; +struct static_key_mod { + struct static_key_mod *next; struct jump_entry *entries; struct module *mod; }; @@ -221,9 +242,9 @@ static int __jump_label_mod_text_reserved(void *start, void *end) start, end); } -static void __jump_label_mod_update(struct jump_label_key *key, int enable) +static void __jump_label_mod_update(struct static_key *key, int enable) { - struct jump_label_mod *mod = key->next; + struct static_key_mod *mod = key->next; while (mod) { struct module *m = mod->mod; @@ -254,11 +275,7 @@ void jump_label_apply_nops(struct module *mod) return; for (iter = iter_start; iter < iter_stop; iter++) { - struct jump_label_key *iterk; - - iterk = (struct jump_label_key *)(unsigned long)iter->key; - arch_jump_label_transform_static(iter, jump_label_enabled(iterk) ? - JUMP_LABEL_ENABLE : JUMP_LABEL_DISABLE); + arch_jump_label_transform_static(iter, JUMP_LABEL_DISABLE); } } @@ -267,8 +284,8 @@ static int jump_label_add_module(struct module *mod) struct jump_entry *iter_start = mod->jump_entries; struct jump_entry *iter_stop = iter_start + mod->num_jump_entries; struct jump_entry *iter; - struct jump_label_key *key = NULL; - struct jump_label_mod *jlm; + struct static_key *key = NULL; + struct static_key_mod *jlm; /* if the module doesn't have jump label entries, just return */ if (iter_start == iter_stop) @@ -277,28 +294,30 @@ static int jump_label_add_module(struct module *mod) jump_label_sort_entries(iter_start, iter_stop); for (iter = iter_start; iter < iter_stop; iter++) { - if (iter->key == (jump_label_t)(unsigned long)key) - continue; + struct static_key *iterk; - key = (struct jump_label_key *)(unsigned long)iter->key; + iterk = (struct static_key *)(unsigned long)iter->key; + if (iterk == key) + continue; + key = iterk; if (__module_address(iter->key) == mod) { - atomic_set(&key->enabled, 0); - key->entries = iter; + /* + * Set key->entries to iter, but preserve JUMP_LABEL_TRUE_BRANCH. + */ + *((unsigned long *)&key->entries) += (unsigned long)iter; key->next = NULL; continue; } - - jlm = kzalloc(sizeof(struct jump_label_mod), GFP_KERNEL); + jlm = kzalloc(sizeof(struct static_key_mod), GFP_KERNEL); if (!jlm) return -ENOMEM; - jlm->mod = mod; jlm->entries = iter; jlm->next = key->next; key->next = jlm; - if (jump_label_enabled(key)) + if (jump_label_type(key) == JUMP_LABEL_ENABLE) __jump_label_update(key, iter, iter_stop, JUMP_LABEL_ENABLE); } @@ -310,14 +329,14 @@ static void jump_label_del_module(struct module *mod) struct jump_entry *iter_start = mod->jump_entries; struct jump_entry *iter_stop = iter_start + mod->num_jump_entries; struct jump_entry *iter; - struct jump_label_key *key = NULL; - struct jump_label_mod *jlm, **prev; + struct static_key *key = NULL; + struct static_key_mod *jlm, **prev; for (iter = iter_start; iter < iter_stop; iter++) { if (iter->key == (jump_label_t)(unsigned long)key) continue; - key = (struct jump_label_key *)(unsigned long)iter->key; + key = (struct static_key *)(unsigned long)iter->key; if (__module_address(iter->key) == mod) continue; @@ -419,9 +438,10 @@ int jump_label_text_reserved(void *start, void *end) return ret; } -static void jump_label_update(struct jump_label_key *key, int enable) +static void jump_label_update(struct static_key *key, int enable) { - struct jump_entry *entry = key->entries, *stop = __stop___jump_table; + struct jump_entry *stop = __stop___jump_table; + struct jump_entry *entry = jump_label_get_entries(key); #ifdef CONFIG_MODULES struct module *mod = __module_address((unsigned long)key); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 5255c9d2e053..112c6824476b 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -162,13 +162,13 @@ static int sched_feat_show(struct seq_file *m, void *v) #ifdef HAVE_JUMP_LABEL -#define jump_label_key__true jump_label_key_enabled -#define jump_label_key__false jump_label_key_disabled +#define jump_label_key__true STATIC_KEY_INIT_TRUE +#define jump_label_key__false STATIC_KEY_INIT_FALSE #define SCHED_FEAT(name, enabled) \ jump_label_key__##enabled , -struct jump_label_key sched_feat_keys[__SCHED_FEAT_NR] = { +struct static_key sched_feat_keys[__SCHED_FEAT_NR] = { #include "features.h" }; @@ -176,14 +176,14 @@ struct jump_label_key sched_feat_keys[__SCHED_FEAT_NR] = { static void sched_feat_disable(int i) { - if (jump_label_enabled(&sched_feat_keys[i])) - jump_label_dec(&sched_feat_keys[i]); + if (static_key_enabled(&sched_feat_keys[i])) + static_key_slow_dec(&sched_feat_keys[i]); } static void sched_feat_enable(int i) { - if (!jump_label_enabled(&sched_feat_keys[i])) - jump_label_inc(&sched_feat_keys[i]); + if (!static_key_enabled(&sched_feat_keys[i])) + static_key_slow_inc(&sched_feat_keys[i]); } #else static void sched_feat_disable(int i) { }; @@ -894,7 +894,7 @@ static void update_rq_clock_task(struct rq *rq, s64 delta) delta -= irq_delta; #endif #ifdef CONFIG_PARAVIRT_TIME_ACCOUNTING - if (static_branch((¶virt_steal_rq_enabled))) { + if (static_key_false((¶virt_steal_rq_enabled))) { u64 st; steal = paravirt_steal_clock(cpu_of(rq)); @@ -2756,7 +2756,7 @@ void account_idle_time(cputime_t cputime) static __always_inline bool steal_account_process_tick(void) { #ifdef CONFIG_PARAVIRT - if (static_branch(¶virt_steal_enabled)) { + if (static_key_false(¶virt_steal_enabled)) { u64 steal, st = 0; steal = paravirt_steal_clock(smp_processor_id()); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 7c6414fc669d..423547ada38a 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1399,20 +1399,20 @@ entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued) #ifdef CONFIG_CFS_BANDWIDTH #ifdef HAVE_JUMP_LABEL -static struct jump_label_key __cfs_bandwidth_used; +static struct static_key __cfs_bandwidth_used; static inline bool cfs_bandwidth_used(void) { - return static_branch(&__cfs_bandwidth_used); + return static_key_false(&__cfs_bandwidth_used); } void account_cfs_bandwidth_used(int enabled, int was_enabled) { /* only need to count groups transitioning between enabled/!enabled */ if (enabled && !was_enabled) - jump_label_inc(&__cfs_bandwidth_used); + static_key_slow_inc(&__cfs_bandwidth_used); else if (!enabled && was_enabled) - jump_label_dec(&__cfs_bandwidth_used); + static_key_slow_dec(&__cfs_bandwidth_used); } #else /* HAVE_JUMP_LABEL */ static bool cfs_bandwidth_used(void) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 98c0c2623db8..b4cd6d8ea150 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -611,7 +611,7 @@ static inline void __set_task_cpu(struct task_struct *p, unsigned int cpu) * Tunables that become constants when CONFIG_SCHED_DEBUG is off: */ #ifdef CONFIG_SCHED_DEBUG -# include +# include # define const_debug __read_mostly #else # define const_debug const @@ -630,18 +630,18 @@ enum { #undef SCHED_FEAT #if defined(CONFIG_SCHED_DEBUG) && defined(HAVE_JUMP_LABEL) -static __always_inline bool static_branch__true(struct jump_label_key *key) +static __always_inline bool static_branch__true(struct static_key *key) { - return likely(static_branch(key)); /* Not out of line branch. */ + return static_key_true(key); /* Not out of line branch. */ } -static __always_inline bool static_branch__false(struct jump_label_key *key) +static __always_inline bool static_branch__false(struct static_key *key) { - return unlikely(static_branch(key)); /* Out of line branch. */ + return static_key_false(key); /* Out of line branch. */ } #define SCHED_FEAT(name, enabled) \ -static __always_inline bool static_branch_##name(struct jump_label_key *key) \ +static __always_inline bool static_branch_##name(struct static_key *key) \ { \ return static_branch__##enabled(key); \ } @@ -650,7 +650,7 @@ static __always_inline bool static_branch_##name(struct jump_label_key *key) \ #undef SCHED_FEAT -extern struct jump_label_key sched_feat_keys[__SCHED_FEAT_NR]; +extern struct static_key sched_feat_keys[__SCHED_FEAT_NR]; #define sched_feat(x) (static_branch_##x(&sched_feat_keys[__SCHED_FEAT_##x])) #else /* !(SCHED_DEBUG && HAVE_JUMP_LABEL) */ #define sched_feat(x) (sysctl_sched_features & (1UL << __SCHED_FEAT_##x)) diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index f1539decd99d..d96ba22dabfa 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include extern struct tracepoint * const __start___tracepoints_ptrs[]; extern struct tracepoint * const __stop___tracepoints_ptrs[]; @@ -256,9 +256,9 @@ static void set_tracepoint(struct tracepoint_entry **entry, { WARN_ON(strcmp((*entry)->name, elem->name) != 0); - if (elem->regfunc && !jump_label_enabled(&elem->key) && active) + if (elem->regfunc && !static_key_enabled(&elem->key) && active) elem->regfunc(); - else if (elem->unregfunc && jump_label_enabled(&elem->key) && !active) + else if (elem->unregfunc && static_key_enabled(&elem->key) && !active) elem->unregfunc(); /* @@ -269,10 +269,10 @@ static void set_tracepoint(struct tracepoint_entry **entry, * is used. */ rcu_assign_pointer(elem->funcs, (*entry)->funcs); - if (active && !jump_label_enabled(&elem->key)) - jump_label_inc(&elem->key); - else if (!active && jump_label_enabled(&elem->key)) - jump_label_dec(&elem->key); + if (active && !static_key_enabled(&elem->key)) + static_key_slow_inc(&elem->key); + else if (!active && static_key_enabled(&elem->key)) + static_key_slow_dec(&elem->key); } /* @@ -283,11 +283,11 @@ static void set_tracepoint(struct tracepoint_entry **entry, */ static void disable_tracepoint(struct tracepoint *elem) { - if (elem->unregfunc && jump_label_enabled(&elem->key)) + if (elem->unregfunc && static_key_enabled(&elem->key)) elem->unregfunc(); - if (jump_label_enabled(&elem->key)) - jump_label_dec(&elem->key); + if (static_key_enabled(&elem->key)) + static_key_slow_dec(&elem->key); rcu_assign_pointer(elem->funcs, NULL); } diff --git a/net/core/dev.c b/net/core/dev.c index 115dee1d985d..da7ce7f0e566 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -134,7 +134,7 @@ #include #include #include -#include +#include #include #include "net-sysfs.h" @@ -1441,11 +1441,11 @@ int call_netdevice_notifiers(unsigned long val, struct net_device *dev) } EXPORT_SYMBOL(call_netdevice_notifiers); -static struct jump_label_key netstamp_needed __read_mostly; +static struct static_key netstamp_needed __read_mostly; #ifdef HAVE_JUMP_LABEL -/* We are not allowed to call jump_label_dec() from irq context +/* We are not allowed to call static_key_slow_dec() from irq context * If net_disable_timestamp() is called from irq context, defer the - * jump_label_dec() calls. + * static_key_slow_dec() calls. */ static atomic_t netstamp_needed_deferred; #endif @@ -1457,12 +1457,12 @@ void net_enable_timestamp(void) if (deferred) { while (--deferred) - jump_label_dec(&netstamp_needed); + static_key_slow_dec(&netstamp_needed); return; } #endif WARN_ON(in_interrupt()); - jump_label_inc(&netstamp_needed); + static_key_slow_inc(&netstamp_needed); } EXPORT_SYMBOL(net_enable_timestamp); @@ -1474,19 +1474,19 @@ void net_disable_timestamp(void) return; } #endif - jump_label_dec(&netstamp_needed); + static_key_slow_dec(&netstamp_needed); } EXPORT_SYMBOL(net_disable_timestamp); static inline void net_timestamp_set(struct sk_buff *skb) { skb->tstamp.tv64 = 0; - if (static_branch(&netstamp_needed)) + if (static_key_false(&netstamp_needed)) __net_timestamp(skb); } #define net_timestamp_check(COND, SKB) \ - if (static_branch(&netstamp_needed)) { \ + if (static_key_false(&netstamp_needed)) { \ if ((COND) && !(SKB)->tstamp.tv64) \ __net_timestamp(SKB); \ } \ @@ -2660,7 +2660,7 @@ EXPORT_SYMBOL(__skb_get_rxhash); struct rps_sock_flow_table __rcu *rps_sock_flow_table __read_mostly; EXPORT_SYMBOL(rps_sock_flow_table); -struct jump_label_key rps_needed __read_mostly; +struct static_key rps_needed __read_mostly; static struct rps_dev_flow * set_rps_cpu(struct net_device *dev, struct sk_buff *skb, @@ -2945,7 +2945,7 @@ int netif_rx(struct sk_buff *skb) trace_netif_rx(skb); #ifdef CONFIG_RPS - if (static_branch(&rps_needed)) { + if (static_key_false(&rps_needed)) { struct rps_dev_flow voidflow, *rflow = &voidflow; int cpu; @@ -3309,7 +3309,7 @@ int netif_receive_skb(struct sk_buff *skb) return NET_RX_SUCCESS; #ifdef CONFIG_RPS - if (static_branch(&rps_needed)) { + if (static_key_false(&rps_needed)) { struct rps_dev_flow voidflow, *rflow = &voidflow; int cpu, ret; diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index a1727cda03d7..495586232aa1 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -608,10 +608,10 @@ static ssize_t store_rps_map(struct netdev_rx_queue *queue, spin_unlock(&rps_map_lock); if (map) - jump_label_inc(&rps_needed); + static_key_slow_inc(&rps_needed); if (old_map) { kfree_rcu(old_map, rcu); - jump_label_dec(&rps_needed); + static_key_slow_dec(&rps_needed); } free_cpumask_var(mask); return len; diff --git a/net/core/sock.c b/net/core/sock.c index 3e81fd2e3c75..3a4e5817a2a7 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -111,7 +111,7 @@ #include #include #include -#include +#include #include #include @@ -184,7 +184,7 @@ void mem_cgroup_sockets_destroy(struct cgroup *cgrp, struct cgroup_subsys *ss) static struct lock_class_key af_family_keys[AF_MAX]; static struct lock_class_key af_family_slock_keys[AF_MAX]; -struct jump_label_key memcg_socket_limit_enabled; +struct static_key memcg_socket_limit_enabled; EXPORT_SYMBOL(memcg_socket_limit_enabled); /* diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index d05559d4d9cd..0c2850874254 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -69,9 +69,9 @@ static int rps_sock_flow_sysctl(ctl_table *table, int write, if (sock_table != orig_sock_table) { rcu_assign_pointer(rps_sock_flow_table, sock_table); if (sock_table) - jump_label_inc(&rps_needed); + static_key_slow_inc(&rps_needed); if (orig_sock_table) { - jump_label_dec(&rps_needed); + static_key_slow_dec(&rps_needed); synchronize_rcu(); vfree(orig_sock_table); } diff --git a/net/ipv4/tcp_memcontrol.c b/net/ipv4/tcp_memcontrol.c index 49978788a9dc..602fb305365f 100644 --- a/net/ipv4/tcp_memcontrol.c +++ b/net/ipv4/tcp_memcontrol.c @@ -111,7 +111,7 @@ void tcp_destroy_cgroup(struct cgroup *cgrp, struct cgroup_subsys *ss) val = res_counter_read_u64(&tcp->tcp_memory_allocated, RES_LIMIT); if (val != RESOURCE_MAX) - jump_label_dec(&memcg_socket_limit_enabled); + static_key_slow_dec(&memcg_socket_limit_enabled); } EXPORT_SYMBOL(tcp_destroy_cgroup); @@ -143,9 +143,9 @@ static int tcp_update_limit(struct mem_cgroup *memcg, u64 val) net->ipv4.sysctl_tcp_mem[i]); if (val == RESOURCE_MAX && old_lim != RESOURCE_MAX) - jump_label_dec(&memcg_socket_limit_enabled); + static_key_slow_dec(&memcg_socket_limit_enabled); else if (old_lim == RESOURCE_MAX && val != RESOURCE_MAX) - jump_label_inc(&memcg_socket_limit_enabled); + static_key_slow_inc(&memcg_socket_limit_enabled); return 0; } diff --git a/net/netfilter/core.c b/net/netfilter/core.c index b4e8ff05b301..e1b7e051332e 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -56,7 +56,7 @@ struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS] __read_mostly; EXPORT_SYMBOL(nf_hooks); #if defined(CONFIG_JUMP_LABEL) -struct jump_label_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; +struct static_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; EXPORT_SYMBOL(nf_hooks_needed); #endif @@ -77,7 +77,7 @@ int nf_register_hook(struct nf_hook_ops *reg) list_add_rcu(®->list, elem->list.prev); mutex_unlock(&nf_hook_mutex); #if defined(CONFIG_JUMP_LABEL) - jump_label_inc(&nf_hooks_needed[reg->pf][reg->hooknum]); + static_key_slow_inc(&nf_hooks_needed[reg->pf][reg->hooknum]); #endif return 0; } @@ -89,7 +89,7 @@ void nf_unregister_hook(struct nf_hook_ops *reg) list_del_rcu(®->list); mutex_unlock(&nf_hook_mutex); #if defined(CONFIG_JUMP_LABEL) - jump_label_dec(&nf_hooks_needed[reg->pf][reg->hooknum]); + static_key_slow_dec(&nf_hooks_needed[reg->pf][reg->hooknum]); #endif synchronize_net(); } -- cgit From 36eabda3d094dae30a74350c6289c163349b744d Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Sat, 11 Feb 2012 15:39:14 +0000 Subject: net: Support RXFCS feature flag. When set on hardware that supports the feature, this causes the Ethernet FCS to be appended to the end of the skb. Useful for sniffing packets. Signed-off-by: Ben Greear Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- Documentation/networking/netdev-features.txt | 6 ++++++ include/linux/netdev_features.h | 2 ++ net/core/ethtool.c | 1 + 3 files changed, 9 insertions(+) (limited to 'include') diff --git a/Documentation/networking/netdev-features.txt b/Documentation/networking/netdev-features.txt index 4b1c0dcef84c..7d2781230d30 100644 --- a/Documentation/networking/netdev-features.txt +++ b/Documentation/networking/netdev-features.txt @@ -152,3 +152,9 @@ NETIF_F_VLAN_CHALLENGED should be set for devices which can't cope with VLAN headers. Some drivers set this because the cards can't handle the bigger MTU. [FIXME: Those cases could be fixed in VLAN code by allowing only reduced-MTU VLANs. This may be not useful, though.] + +* rx-fcs + +This requests that the NIC append the Ethernet Frame Checksum (FCS) +to the end of the skb data. This allows sniffers and other tools to +read the CRC recorded by the NIC on receipt of the packet. diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index 77f5202977ce..d1331865f830 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h @@ -54,6 +54,7 @@ enum { NETIF_F_RXCSUM_BIT, /* Receive checksumming offload */ NETIF_F_NOCACHE_COPY_BIT, /* Use no-cache copyfromuser */ NETIF_F_LOOPBACK_BIT, /* Enable loopback */ + NETIF_F_RXFCS_BIT, /* Append FCS to skb pkt data */ /* * Add your fresh new feature above and remember to update @@ -98,6 +99,7 @@ enum { #define NETIF_F_TSO __NETIF_F(TSO) #define NETIF_F_UFO __NETIF_F(UFO) #define NETIF_F_VLAN_CHALLENGED __NETIF_F(VLAN_CHALLENGED) +#define NETIF_F_RXFCS __NETIF_F(RXFCS) /* Features valid for ethtool to change */ /* = all defined minus driver/device-class-related */ diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 3f79db1b612a..080161924a0d 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -73,6 +73,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_RXCSUM_BIT] = "rx-checksum", [NETIF_F_NOCACHE_COPY_BIT] = "tx-nocache-copy", [NETIF_F_LOOPBACK_BIT] = "loopback", + [NETIF_F_RXFCS_BIT] = "rx-fcs", }; static int ethtool_get_features(struct net_device *dev, void __user *useraddr) -- cgit From 3bdc0eba0b8b47797f4a76e377dd8360f317450f Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Sat, 11 Feb 2012 15:39:30 +0000 Subject: net: Add framework to allow sending packets with customized CRC. This is useful for testing RX handling of frames with bad CRCs. Requires driver support to actually put the packet on the wire properly. Signed-off-by: Ben Greear Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- arch/alpha/include/asm/socket.h | 3 +++ arch/arm/include/asm/socket.h | 3 +++ arch/avr32/include/asm/socket.h | 3 +++ arch/cris/include/asm/socket.h | 3 +++ arch/frv/include/asm/socket.h | 3 +++ arch/h8300/include/asm/socket.h | 3 +++ arch/ia64/include/asm/socket.h | 3 +++ arch/m32r/include/asm/socket.h | 3 +++ arch/m68k/include/asm/socket.h | 3 +++ arch/mips/include/asm/socket.h | 3 +++ arch/mn10300/include/asm/socket.h | 3 +++ arch/parisc/include/asm/socket.h | 4 ++++ arch/powerpc/include/asm/socket.h | 3 +++ arch/s390/include/asm/socket.h | 3 +++ arch/sparc/include/asm/socket.h | 4 ++++ arch/xtensa/include/asm/socket.h | 3 +++ include/asm-generic/socket.h | 4 ++++ include/linux/if.h | 2 ++ include/linux/netdevice.h | 8 +++++++- include/linux/skbuff.h | 4 +++- include/net/sock.h | 4 ++++ net/core/skbuff.c | 1 + net/core/sock.c | 5 +++++ net/packet/af_packet.c | 32 ++++++++++++++++++++++++++++---- 24 files changed, 104 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/alpha/include/asm/socket.h b/arch/alpha/include/asm/socket.h index 16449d330dae..dcb221a4b5be 100644 --- a/arch/alpha/include/asm/socket.h +++ b/arch/alpha/include/asm/socket.h @@ -73,6 +73,9 @@ #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 42 +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 43 + /* O_NONBLOCK clashes with the bits used for socket types. Therefore we * have to define SOCK_NONBLOCK to a different value here. */ diff --git a/arch/arm/include/asm/socket.h b/arch/arm/include/asm/socket.h index d958c74e5260..6433cadb6ed4 100644 --- a/arch/arm/include/asm/socket.h +++ b/arch/arm/include/asm/socket.h @@ -66,4 +66,7 @@ #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 42 +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 43 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/avr32/include/asm/socket.h b/arch/avr32/include/asm/socket.h index 30078f98b3ab..a473f8c6a9aa 100644 --- a/arch/avr32/include/asm/socket.h +++ b/arch/avr32/include/asm/socket.h @@ -66,4 +66,7 @@ #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 42 +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 43 + #endif /* __ASM_AVR32_SOCKET_H */ diff --git a/arch/cris/include/asm/socket.h b/arch/cris/include/asm/socket.h index 048aba64600c..ae52825021af 100644 --- a/arch/cris/include/asm/socket.h +++ b/arch/cris/include/asm/socket.h @@ -68,6 +68,9 @@ #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 42 +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 43 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/frv/include/asm/socket.h b/arch/frv/include/asm/socket.h index 7a361810f3cc..a5b1d7dbb205 100644 --- a/arch/frv/include/asm/socket.h +++ b/arch/frv/include/asm/socket.h @@ -66,5 +66,8 @@ #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 42 +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 43 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/h8300/include/asm/socket.h b/arch/h8300/include/asm/socket.h index e7bbfcee5b99..ec4554e7b04b 100644 --- a/arch/h8300/include/asm/socket.h +++ b/arch/h8300/include/asm/socket.h @@ -66,4 +66,7 @@ #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 42 +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 43 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/ia64/include/asm/socket.h b/arch/ia64/include/asm/socket.h index ced62de9d5a9..41fc28a4a18a 100644 --- a/arch/ia64/include/asm/socket.h +++ b/arch/ia64/include/asm/socket.h @@ -75,4 +75,7 @@ #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 42 +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 43 + #endif /* _ASM_IA64_SOCKET_H */ diff --git a/arch/m32r/include/asm/socket.h b/arch/m32r/include/asm/socket.h index 696cb4c7ca4e..a15f40b52783 100644 --- a/arch/m32r/include/asm/socket.h +++ b/arch/m32r/include/asm/socket.h @@ -66,4 +66,7 @@ #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 42 +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 43 + #endif /* _ASM_M32R_SOCKET_H */ diff --git a/arch/m68k/include/asm/socket.h b/arch/m68k/include/asm/socket.h index e8b41a6775f9..d1be684edf97 100644 --- a/arch/m68k/include/asm/socket.h +++ b/arch/m68k/include/asm/socket.h @@ -66,4 +66,7 @@ #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 42 +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 43 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/mips/include/asm/socket.h b/arch/mips/include/asm/socket.h index 52104872e9e3..a2ed6fdad4e0 100644 --- a/arch/mips/include/asm/socket.h +++ b/arch/mips/include/asm/socket.h @@ -86,6 +86,9 @@ To add: #define SO_REUSEPORT 0x0200 /* Allow local address and port reuse. */ #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 42 +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 43 + #ifdef __KERNEL__ /** sock_type - Socket types diff --git a/arch/mn10300/include/asm/socket.h b/arch/mn10300/include/asm/socket.h index 013fcc51698f..820463a484b8 100644 --- a/arch/mn10300/include/asm/socket.h +++ b/arch/mn10300/include/asm/socket.h @@ -66,4 +66,7 @@ #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 42 +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 43 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/parisc/include/asm/socket.h b/arch/parisc/include/asm/socket.h index f717c9bec16f..1b52c2c31a7a 100644 --- a/arch/parisc/include/asm/socket.h +++ b/arch/parisc/include/asm/socket.h @@ -65,6 +65,10 @@ #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 0x4023 +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 0x4024 + + /* O_NONBLOCK clashes with the bits used for socket types. Therefore we * have to define SOCK_NONBLOCK to a different value here. */ diff --git a/arch/powerpc/include/asm/socket.h b/arch/powerpc/include/asm/socket.h index fe1c0b478fd7..3d5179bb122f 100644 --- a/arch/powerpc/include/asm/socket.h +++ b/arch/powerpc/include/asm/socket.h @@ -73,4 +73,7 @@ #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 42 +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 43 + #endif /* _ASM_POWERPC_SOCKET_H */ diff --git a/arch/s390/include/asm/socket.h b/arch/s390/include/asm/socket.h index 581702fa1b0c..c91b720965c0 100644 --- a/arch/s390/include/asm/socket.h +++ b/arch/s390/include/asm/socket.h @@ -74,4 +74,7 @@ #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 42 +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 43 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/sparc/include/asm/socket.h b/arch/sparc/include/asm/socket.h index 68e2e2746f6f..bea1568ae4af 100644 --- a/arch/sparc/include/asm/socket.h +++ b/arch/sparc/include/asm/socket.h @@ -62,6 +62,10 @@ #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 0x0026 +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 0x0027 + + /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 #define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002 diff --git a/arch/xtensa/include/asm/socket.h b/arch/xtensa/include/asm/socket.h index 74818b161362..e36c68184920 100644 --- a/arch/xtensa/include/asm/socket.h +++ b/arch/xtensa/include/asm/socket.h @@ -77,4 +77,7 @@ #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 42 +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 43 + #endif /* _XTENSA_SOCKET_H */ diff --git a/include/asm-generic/socket.h b/include/asm-generic/socket.h index d9aaac0c36d4..b1bea03274d5 100644 --- a/include/asm-generic/socket.h +++ b/include/asm-generic/socket.h @@ -68,4 +68,8 @@ #define SO_WIFI_STATUS 41 #define SCM_WIFI_STATUS SO_WIFI_STATUS #define SO_PEEK_OFF 42 + +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define SO_NOFCS 43 + #endif /* __ASM_GENERIC_SOCKET_H */ diff --git a/include/linux/if.h b/include/linux/if.h index 06b6ef60c821..f995c663c493 100644 --- a/include/linux/if.h +++ b/include/linux/if.h @@ -80,6 +80,8 @@ * skbs on transmit */ #define IFF_UNICAST_FLT 0x20000 /* Supports unicast filtering */ #define IFF_TEAM_PORT 0x40000 /* device used as team port */ +#define IFF_SUPP_NOFCS 0x80000 /* device supports sending custom FCS */ + #define IF_GET_IFACE 0x0001 /* for querying only */ #define IF_GET_PROTO 0x0002 diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0eac07c95255..f1b7d037c2c5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1082,7 +1082,8 @@ struct net_device { const struct header_ops *header_ops; unsigned int flags; /* interface flags (a la BSD) */ - unsigned int priv_flags; /* Like 'flags' but invisible to userspace. */ + unsigned int priv_flags; /* Like 'flags' but invisible to userspace. + * See if.h for definitions. */ unsigned short gflags; unsigned short padded; /* How much padding added by alloc_netdev() */ @@ -2650,6 +2651,11 @@ static inline int netif_is_bond_slave(struct net_device *dev) return dev->flags & IFF_SLAVE && dev->priv_flags & IFF_BONDING; } +static inline bool netif_supports_nofcs(struct net_device *dev) +{ + return dev->priv_flags & IFF_SUPP_NOFCS; +} + extern struct pernet_operations __net_initdata loopback_net_ops; /* Logging, debugging and troubleshooting/diagnostic helpers. */ diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index c11a44ea1bf4..06a4c0fd7bef 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -361,6 +361,7 @@ typedef unsigned char *sk_buff_data_t; * ports. * @wifi_acked_valid: wifi_acked was set * @wifi_acked: whether frame was acked on wifi or not + * @no_fcs: Request NIC to treat last 4 bytes as Ethernet FCS * @dma_cookie: a cookie to one of several possible DMA operations * done by skb DMA functions * @secmark: security marking @@ -459,7 +460,8 @@ struct sk_buff { __u8 l4_rxhash:1; __u8 wifi_acked_valid:1; __u8 wifi_acked:1; - /* 10/12 bit hole (depending on ndisc_nodetype presence) */ + __u8 no_fcs:1; + /* 9/11 bit hole (depending on ndisc_nodetype presence) */ kmemcheck_bitfield_end(flags2); #ifdef CONFIG_NET_DMA diff --git a/include/net/sock.h b/include/net/sock.h index 9c0553b9e451..ba761e7de252 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -615,6 +615,10 @@ enum sock_flags { SOCK_RXQ_OVFL, SOCK_ZEROCOPY, /* buffers from userspace */ SOCK_WIFI_STATUS, /* push wifi status to userspace */ + SOCK_NOFCS, /* Tell NIC not to do the Ethernet FCS. + * Will use last 4 bytes of packet sent from + * user-space instead. + */ }; static inline void sock_copy_flags(struct sock *nsk, struct sock *osk) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index f3a530780753..6eb656acdfe5 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -592,6 +592,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) new->rxhash = old->rxhash; new->ooo_okay = old->ooo_okay; new->l4_rxhash = old->l4_rxhash; + new->no_fcs = old->no_fcs; #ifdef CONFIG_XFRM new->sp = secpath_get(old->sp); #endif diff --git a/net/core/sock.c b/net/core/sock.c index 19942d4bb6e6..55011cb691ad 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -799,6 +799,11 @@ set_rcvbuf: else ret = -EOPNOTSUPP; break; + + case SO_NOFCS: + sock_valbool_flag(sk, SOCK_NOFCS, valbool); + break; + default: ret = -ENOPROTOOPT; break; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 2dbb32b988c4..ae2d484416dd 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1459,6 +1459,7 @@ static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock, struct net_device *dev; __be16 proto = 0; int err; + int extra_len = 0; /* * Get and verify the address. @@ -1493,8 +1494,16 @@ retry: * raw protocol and you must do your own fragmentation at this level. */ + if (unlikely(sock_flag(sk, SOCK_NOFCS))) { + if (!netif_supports_nofcs(dev)) { + err = -EPROTONOSUPPORT; + goto out_unlock; + } + extra_len = 4; /* We're doing our own CRC */ + } + err = -EMSGSIZE; - if (len > dev->mtu + dev->hard_header_len + VLAN_HLEN) + if (len > dev->mtu + dev->hard_header_len + VLAN_HLEN + extra_len) goto out_unlock; if (!skb) { @@ -1526,7 +1535,7 @@ retry: goto retry; } - if (len > (dev->mtu + dev->hard_header_len)) { + if (len > (dev->mtu + dev->hard_header_len + extra_len)) { /* Earlier code assumed this would be a VLAN pkt, * double-check this now that we have the actual * packet in hand. @@ -1548,6 +1557,9 @@ retry: if (err < 0) goto out_unlock; + if (unlikely(extra_len == 4)) + skb->no_fcs = 1; + dev_queue_xmit(skb); rcu_read_unlock(); return len; @@ -2209,6 +2221,7 @@ static int packet_snd(struct socket *sock, struct packet_sock *po = pkt_sk(sk); unsigned short gso_type = 0; int hlen, tlen; + int extra_len = 0; /* * Get and verify the address. @@ -2288,8 +2301,16 @@ static int packet_snd(struct socket *sock, } } + if (unlikely(sock_flag(sk, SOCK_NOFCS))) { + if (!netif_supports_nofcs(dev)) { + err = -EPROTONOSUPPORT; + goto out_unlock; + } + extra_len = 4; /* We're doing our own CRC */ + } + err = -EMSGSIZE; - if (!gso_type && (len > dev->mtu + reserve + VLAN_HLEN)) + if (!gso_type && (len > dev->mtu + reserve + VLAN_HLEN + extra_len)) goto out_unlock; err = -ENOBUFS; @@ -2315,7 +2336,7 @@ static int packet_snd(struct socket *sock, if (err < 0) goto out_free; - if (!gso_type && (len > dev->mtu + reserve)) { + if (!gso_type && (len > dev->mtu + reserve + extra_len)) { /* Earlier code assumed this would be a VLAN pkt, * double-check this now that we have the actual * packet in hand. @@ -2353,6 +2374,9 @@ static int packet_snd(struct socket *sock, len += vnet_hdr_len; } + if (unlikely(extra_len == 4)) + skb->no_fcs = 1; + /* * Now send it */ -- cgit From 5e0c03c8cd40e5c3b7ba624b8ba9a343de79ade1 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Sat, 11 Feb 2012 15:39:45 +0000 Subject: net: Support RX-ALL feature flag. This flag requests that network devices pass all received frames up the stack, even ones with errors such as invalid FCS (frame check sum). This will allow sniffers to see bad packets and perhaps give the user some idea how to fix the problem. Signed-off-by: Ben Greear Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- Documentation/networking/netdev-features.txt | 7 +++++++ include/linux/netdev_features.h | 2 ++ net/core/ethtool.c | 1 + 3 files changed, 10 insertions(+) (limited to 'include') diff --git a/Documentation/networking/netdev-features.txt b/Documentation/networking/netdev-features.txt index 7d2781230d30..4164f5c02e4b 100644 --- a/Documentation/networking/netdev-features.txt +++ b/Documentation/networking/netdev-features.txt @@ -158,3 +158,10 @@ VLANs. This may be not useful, though.] This requests that the NIC append the Ethernet Frame Checksum (FCS) to the end of the skb data. This allows sniffers and other tools to read the CRC recorded by the NIC on receipt of the packet. + +* rx-all + +This requests that the NIC receive all possible frames, including errored +frames (such as bad FCS, etc). This can be helpful when sniffing a link with +bad packets on it. Some NICs may receive more packets if also put into normal +PROMISC mdoe. diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index d1331865f830..5ac32123035a 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h @@ -55,6 +55,7 @@ enum { NETIF_F_NOCACHE_COPY_BIT, /* Use no-cache copyfromuser */ NETIF_F_LOOPBACK_BIT, /* Enable loopback */ NETIF_F_RXFCS_BIT, /* Append FCS to skb pkt data */ + NETIF_F_RXALL_BIT, /* Receive errored frames too */ /* * Add your fresh new feature above and remember to update @@ -100,6 +101,7 @@ enum { #define NETIF_F_UFO __NETIF_F(UFO) #define NETIF_F_VLAN_CHALLENGED __NETIF_F(VLAN_CHALLENGED) #define NETIF_F_RXFCS __NETIF_F(RXFCS) +#define NETIF_F_RXALL __NETIF_F(RXALL) /* Features valid for ethtool to change */ /* = all defined minus driver/device-class-related */ diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 080161924a0d..6d6d7d25caaa 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -74,6 +74,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_NOCACHE_COPY_BIT] = "tx-nocache-copy", [NETIF_F_LOOPBACK_BIT] = "loopback", [NETIF_F_RXFCS_BIT] = "rx-fcs", + [NETIF_F_RXALL_BIT] = "rx-all", }; static int ethtool_get_features(struct net_device *dev, void __user *useraddr) -- cgit From 765e0ba62613fb90f09c1b5926750df0aa56f349 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 23 Feb 2012 14:55:59 -0500 Subject: usb-serial: new API for driver registration This patch (as1522) adds two new routines to the usb-serial core, for registering and unregistering serial drivers. Instead of registering the usb_driver and usb_serial_drivers separately, with error checking for each one, the drivers can all be registered and unregistered by a single function call. This reduces duplicated code. More importantly, the new core routines change the order in which the drivers are registered. Currently the usb-serial drivers are all registered first and the usb_driver is done last, which leaves a window for problems. A udev script may quickly add a new dynamic-ID for a usb-serial driver, causing the corresponding usb_driver to be probed. If the usb_driver hasn't been registered yet then an oops will occur. The new routine prevents such problems by registering the usb_driver first. To insure that it gets probed properly for already-attached serial devices, we call driver_attach() after all the usb-serial drivers have been registered. Along with adding the new routines, the patch modifies the "generic" serial driver to use them. Further patches will similarly modify all the other in-tree USB serial drivers. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 17 ++++------ drivers/usb/serial/usb-serial.c | 75 +++++++++++++++++++++++++++++++++++++++++ include/linux/usb/serial.h | 9 +++++ 3 files changed, 90 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 2a2fa2d0489d..664deb63807c 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -54,7 +54,6 @@ static struct usb_driver generic_driver = { .probe = generic_probe, .disconnect = usb_serial_disconnect, .id_table = generic_serial_ids, - .no_dynamic_id = 1, }; /* All of the device info needed for the Generic Serial Converter */ @@ -64,7 +63,6 @@ struct usb_serial_driver usb_serial_generic_device = { .name = "generic", }, .id_table = generic_device_ids, - .usb_driver = &generic_driver, .num_ports = 1, .disconnect = usb_serial_generic_disconnect, .release = usb_serial_generic_release, @@ -73,6 +71,10 @@ struct usb_serial_driver usb_serial_generic_device = { .resume = usb_serial_generic_resume, }; +static struct usb_serial_driver * const serial_drivers[] = { + &usb_serial_generic_device, NULL +}; + static int generic_probe(struct usb_interface *interface, const struct usb_device_id *id) { @@ -97,13 +99,7 @@ int usb_serial_generic_register(int _debug) USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT; /* register our generic driver with ourselves */ - retval = usb_serial_register(&usb_serial_generic_device); - if (retval) - goto exit; - retval = usb_register(&generic_driver); - if (retval) - usb_serial_deregister(&usb_serial_generic_device); -exit: + retval = usb_serial_register_drivers(&generic_driver, serial_drivers); #endif return retval; } @@ -112,8 +108,7 @@ void usb_serial_generic_deregister(void) { #ifdef CONFIG_USB_SERIAL_GENERIC /* remove our generic driver */ - usb_deregister(&generic_driver); - usb_serial_deregister(&usb_serial_generic_device); + usb_serial_deregister_drivers(&generic_driver, serial_drivers); #endif } diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 611b206591cb..45b3658c601f 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1338,6 +1338,11 @@ static void fixup_generic(struct usb_serial_driver *device) set_to_generic_if_null(device, prepare_write_buffer); } +/* + * The next two routines are mainly for internal use. + * They are exported only for out-of-tree modules. + * New drivers should call usb_serial_{de}register_drivers() instead. + */ int usb_serial_register(struct usb_serial_driver *driver) { int retval; @@ -1386,6 +1391,76 @@ void usb_serial_deregister(struct usb_serial_driver *device) } EXPORT_SYMBOL_GPL(usb_serial_deregister); +/** + * usb_serial_register_drivers - register drivers for a usb-serial module + * @udriver: usb_driver used for matching devices/interfaces + * @serial_drivers: NULL-terminated array of pointers to drivers to be registered + * + * Registers @udriver and all the drivers in the @serial_drivers array. + * Automatically fills in the .no_dynamic_id field in @udriver and + * the .usb_driver field in each serial driver. + */ +int usb_serial_register_drivers(struct usb_driver *udriver, + struct usb_serial_driver * const serial_drivers[]) +{ + int rc; + const struct usb_device_id *saved_id_table; + struct usb_serial_driver * const *sd; + + /* + * udriver must be registered before any of the serial drivers, + * because the store_new_id() routine for the serial drivers (in + * bus.c) probes udriver. + * + * Performance hack: We don't want udriver to be probed until + * the serial drivers are registered, because the probe would + * simply fail for lack of a matching serial driver. + * Therefore save off udriver's id_table until we are all set. + */ + saved_id_table = udriver->id_table; + udriver->id_table = NULL; + + udriver->no_dynamic_id = 1; + rc = usb_register(udriver); + if (rc) + return rc; + + for (sd = serial_drivers; *sd; ++sd) { + (*sd)->usb_driver = udriver; + rc = usb_serial_register(*sd); + if (rc) + goto failed; + } + + /* Now restore udriver's id_table and look for matches */ + udriver->id_table = saved_id_table; + rc = driver_attach(&udriver->drvwrap.driver); + return 0; + + failed: + while (sd-- > serial_drivers) + usb_serial_deregister(*sd); + usb_deregister(udriver); + return rc; +} +EXPORT_SYMBOL_GPL(usb_serial_register_drivers); + +/** + * usb_serial_deregister_drivers - deregister drivers for a usb-serial module + * @udriver: usb_driver to unregister + * @serial_drivers: NULL-terminated array of pointers to drivers to be deregistered + * + * Deregisters @udriver and all the drivers in the @serial_drivers array. + */ +void usb_serial_deregister_drivers(struct usb_driver *udriver, + struct usb_serial_driver * const serial_drivers[]) +{ + for (; *serial_drivers; ++serial_drivers) + usb_serial_deregister(*serial_drivers); + usb_deregister(udriver); +} +EXPORT_SYMBOL_GPL(usb_serial_deregister_drivers); + /* Module information */ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 10cb74d2ad1d..8c8dbf9c5b89 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -300,8 +300,17 @@ struct usb_serial_driver { #define to_usb_serial_driver(d) \ container_of(d, struct usb_serial_driver, driver) +/* + * These two routines are kept only for backward compatibility. + * Don't use them; call usb_serial_{de}register_drivers() instead. + */ extern int usb_serial_register(struct usb_serial_driver *driver); extern void usb_serial_deregister(struct usb_serial_driver *driver); + +extern int usb_serial_register_drivers(struct usb_driver *udriver, + struct usb_serial_driver * const serial_drivers[]); +extern void usb_serial_deregister_drivers(struct usb_driver *udriver, + struct usb_serial_driver * const serial_drivers[]); extern void usb_serial_port_softint(struct usb_serial_port *port); extern int usb_serial_probe(struct usb_interface *iface, -- cgit From f799e7678390029e322ae2dc3cda389b11f38124 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 24 Feb 2012 12:50:30 -0800 Subject: USB: serial: remove usb_serial_register and usb_serial_deregister No one uses them anymore, they should be using the safer usb_serial_register_drivers() and usb_serial_deregister_drivers() functions instead. Thanks to Alan Stern for writing these functions and porting all in-kernel users to them. Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 12 ++---------- include/linux/usb/serial.h | 7 ------- 2 files changed, 2 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 45b3658c601f..63ba47dbcc71 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1338,12 +1338,7 @@ static void fixup_generic(struct usb_serial_driver *device) set_to_generic_if_null(device, prepare_write_buffer); } -/* - * The next two routines are mainly for internal use. - * They are exported only for out-of-tree modules. - * New drivers should call usb_serial_{de}register_drivers() instead. - */ -int usb_serial_register(struct usb_serial_driver *driver) +static int usb_serial_register(struct usb_serial_driver *driver) { int retval; @@ -1377,10 +1372,8 @@ int usb_serial_register(struct usb_serial_driver *driver) mutex_unlock(&table_lock); return retval; } -EXPORT_SYMBOL_GPL(usb_serial_register); - -void usb_serial_deregister(struct usb_serial_driver *device) +static void usb_serial_deregister(struct usb_serial_driver *device) { printk(KERN_INFO "USB Serial deregistering driver %s\n", device->description); @@ -1389,7 +1382,6 @@ void usb_serial_deregister(struct usb_serial_driver *device) usb_serial_bus_deregister(device); mutex_unlock(&table_lock); } -EXPORT_SYMBOL_GPL(usb_serial_deregister); /** * usb_serial_register_drivers - register drivers for a usb-serial module diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 8c8dbf9c5b89..34c06a723dcc 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -300,13 +300,6 @@ struct usb_serial_driver { #define to_usb_serial_driver(d) \ container_of(d, struct usb_serial_driver, driver) -/* - * These two routines are kept only for backward compatibility. - * Don't use them; call usb_serial_{de}register_drivers() instead. - */ -extern int usb_serial_register(struct usb_serial_driver *driver); -extern void usb_serial_deregister(struct usb_serial_driver *driver); - extern int usb_serial_register_drivers(struct usb_driver *udriver, struct usb_serial_driver * const serial_drivers[]); extern void usb_serial_deregister_drivers(struct usb_driver *udriver, -- cgit From 247ff8e610cb63c015de19191db9666754c2ed79 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 24 Feb 2012 12:47:11 +0000 Subject: vt: lock the accent table First step to debletcherising the vt console layer - pick a victim and fix the locking This is a nice simple object with its own rules so lets pick it out for treatment. The user of the table already has a lock so we will also use the same lock for updates. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/tty/vt/vt_ioctl.c | 81 +---------------------- include/linux/vt_kern.h | 3 + 3 files changed, 168 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index a605549ee28f..bdf838dd3e44 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -1452,3 +1452,165 @@ int __init kbd_init(void) return 0; } + +/* Ioctl support code */ + +/** + * vt_do_diacrit - diacritical table updates + * @cmd: ioctl request + * @up: pointer to user data for ioctl + * @perm: permissions check computed by caller + * + * Update the diacritical tables atomically and safely. Lock them + * against simultaneous keypresses + */ +int vt_do_diacrit(unsigned int cmd, void __user *up, int perm) +{ + struct kbdiacrs __user *a = up; + unsigned long flags; + int asize; + int ret = 0; + + switch (cmd) { + case KDGKBDIACR: + { + struct kbdiacr *diacr; + int i; + + diacr = kmalloc(MAX_DIACR * sizeof(struct kbdiacr), + GFP_KERNEL); + if (diacr == NULL) + return -ENOMEM; + + /* Lock the diacriticals table, make a copy and then + copy it after we unlock */ + spin_lock_irqsave(&kbd_event_lock, flags); + + asize = accent_table_size; + for (i = 0; i < asize; i++) { + diacr[i].diacr = conv_uni_to_8bit( + accent_table[i].diacr); + diacr[i].base = conv_uni_to_8bit( + accent_table[i].base); + diacr[i].result = conv_uni_to_8bit( + accent_table[i].result); + } + spin_unlock_irqrestore(&kbd_event_lock, flags); + + if (put_user(asize, &a->kb_cnt)) + ret = -EFAULT; + else if (copy_to_user(a->kbdiacr, diacr, + asize * sizeof(struct kbdiacr))) + ret = -EFAULT; + kfree(diacr); + return ret; + } + case KDGKBDIACRUC: + { + struct kbdiacrsuc __user *a = up; + void *buf; + + buf = kmalloc(MAX_DIACR * sizeof(struct kbdiacruc), + GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + /* Lock the diacriticals table, make a copy and then + copy it after we unlock */ + spin_lock_irqsave(&kbd_event_lock, flags); + + asize = accent_table_size; + memcpy(buf, accent_table, asize * sizeof(struct kbdiacruc)); + + spin_unlock_irqrestore(&kbd_event_lock, flags); + + if (put_user(asize, &a->kb_cnt)) + ret = -EFAULT; + else if (copy_to_user(a->kbdiacruc, buf, + asize*sizeof(struct kbdiacruc))) + ret = -EFAULT; + kfree(buf); + return ret; + } + + case KDSKBDIACR: + { + struct kbdiacrs __user *a = up; + struct kbdiacr *diacr = NULL; + unsigned int ct; + int i; + + if (!perm) + return -EPERM; + if (get_user(ct, &a->kb_cnt)) + return -EFAULT; + if (ct >= MAX_DIACR) + return -EINVAL; + + if (ct) { + diacr = kmalloc(sizeof(struct kbdiacr) * ct, + GFP_KERNEL); + if (diacr == NULL) + return -ENOMEM; + + if (copy_from_user(diacr, a->kbdiacr, + sizeof(struct kbdiacr) * ct)) { + kfree(diacr); + return -EFAULT; + } + } + + spin_lock_irqsave(&kbd_event_lock, flags); + accent_table_size = ct; + for (i = 0; i < ct; i++) { + accent_table[i].diacr = + conv_8bit_to_uni(diacr[i].diacr); + accent_table[i].base = + conv_8bit_to_uni(diacr[i].base); + accent_table[i].result = + conv_8bit_to_uni(diacr[i].result); + } + spin_unlock_irqrestore(&kbd_event_lock, flags); + kfree(diacr); + return 0; + } + + case KDSKBDIACRUC: + { + struct kbdiacrsuc __user *a = up; + unsigned int ct; + void *buf = NULL; + + if (!perm) + return -EPERM; + + if (get_user(ct, &a->kb_cnt)) + return -EFAULT; + + if (ct >= MAX_DIACR) + return -EINVAL; + + if (ct) { + buf = kmalloc(ct * sizeof(struct kbdiacruc), + GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + if (copy_from_user(buf, a->kbdiacruc, + ct * sizeof(struct kbdiacruc))) { + kfree(buf); + return -EFAULT; + } + } + spin_lock_irqsave(&kbd_event_lock, flags); + if (ct) + memcpy(accent_table, buf, + ct * sizeof(struct kbdiacruc)); + accent_table_size = ct; + spin_unlock_irqrestore(&kbd_event_lock, flags); + kfree(buf); + return 0; + } + } + return ret; +} diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 65447c5f91d7..80af0f9bef5b 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -753,89 +753,14 @@ int vt_ioctl(struct tty_struct *tty, ret = do_kdgkb_ioctl(cmd, up, perm); break; + /* Diacritical processing. Handled in keyboard.c as it has + to operate on the keyboard locks and structures */ case KDGKBDIACR: - { - struct kbdiacrs __user *a = up; - struct kbdiacr diacr; - int i; - - if (put_user(accent_table_size, &a->kb_cnt)) { - ret = -EFAULT; - break; - } - for (i = 0; i < accent_table_size; i++) { - diacr.diacr = conv_uni_to_8bit(accent_table[i].diacr); - diacr.base = conv_uni_to_8bit(accent_table[i].base); - diacr.result = conv_uni_to_8bit(accent_table[i].result); - if (copy_to_user(a->kbdiacr + i, &diacr, sizeof(struct kbdiacr))) { - ret = -EFAULT; - break; - } - } - break; - } case KDGKBDIACRUC: - { - struct kbdiacrsuc __user *a = up; - - if (put_user(accent_table_size, &a->kb_cnt)) - ret = -EFAULT; - else if (copy_to_user(a->kbdiacruc, accent_table, - accent_table_size*sizeof(struct kbdiacruc))) - ret = -EFAULT; - break; - } - case KDSKBDIACR: - { - struct kbdiacrs __user *a = up; - struct kbdiacr diacr; - unsigned int ct; - int i; - - if (!perm) - goto eperm; - if (get_user(ct,&a->kb_cnt)) { - ret = -EFAULT; - break; - } - if (ct >= MAX_DIACR) { - ret = -EINVAL; - break; - } - accent_table_size = ct; - for (i = 0; i < ct; i++) { - if (copy_from_user(&diacr, a->kbdiacr + i, sizeof(struct kbdiacr))) { - ret = -EFAULT; - break; - } - accent_table[i].diacr = conv_8bit_to_uni(diacr.diacr); - accent_table[i].base = conv_8bit_to_uni(diacr.base); - accent_table[i].result = conv_8bit_to_uni(diacr.result); - } - break; - } - case KDSKBDIACRUC: - { - struct kbdiacrsuc __user *a = up; - unsigned int ct; - - if (!perm) - goto eperm; - if (get_user(ct,&a->kb_cnt)) { - ret = -EFAULT; - break; - } - if (ct >= MAX_DIACR) { - ret = -EINVAL; - break; - } - accent_table_size = ct; - if (copy_from_user(accent_table, a->kbdiacruc, ct*sizeof(struct kbdiacruc))) - ret = -EFAULT; + ret = vt_do_diacrit(cmd, up, perm); break; - } /* the ioctls below read/set the flags usually shown in the leds */ /* don't use them - they will go away without warning */ diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index c2164fad0083..5d8726ad5729 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -167,4 +167,7 @@ extern int unregister_vt_notifier(struct notifier_block *nb); extern void hide_boot_cursor(bool hide); +/* keyboard provided interfaces */ +extern int vt_do_diacrit(unsigned int cmd, void __user *up, int eperm); + #endif /* _VT_KERN_H */ -- cgit From 946a720c814ff63a9e5c7755395bff080b931eae Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Sat, 25 Feb 2012 02:09:46 +0000 Subject: xfrm: remove unneeded method typedef declaration in net/xfrm.h. The patch removes unneeded method typedef declaration (icv_update_fn_t ) and the two struct declarations which appear in its prototype (struct hash_desc and struct scatterlist) in net/xfrm.h. Signed-off-by: Rami Rosen Signed-off-by: David S. Miller --- include/net/xfrm.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 89174e29dca9..96239e78e621 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1566,11 +1566,6 @@ extern struct xfrm_algo_desc *xfrm_calg_get_byname(const char *name, int probe); extern struct xfrm_algo_desc *xfrm_aead_get_byname(const char *name, int icv_len, int probe); -struct hash_desc; -struct scatterlist; -typedef int (icv_update_fn_t)(struct hash_desc *, struct scatterlist *, - unsigned int); - static inline int xfrm_addr_cmp(const xfrm_address_t *a, const xfrm_address_t *b, int family) -- cgit From 80d326fab534a5380e8f6e509a0b9076655a9670 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 24 Feb 2012 14:30:15 +0000 Subject: netlink: add netlink_dump_control structure for netlink_dump_start() Davem considers that the argument list of this interface is getting out of control. This patch tries to address this issue following his proposal: struct netlink_dump_control c = { .dump = dump, .done = done, ... }; netlink_dump_start(..., &c); Suggested by David S. Miller. Signed-off-by: Pablo Neira Ayuso Signed-off-by: David S. Miller --- crypto/crypto_user.c | 10 +++++++--- drivers/infiniband/core/netlink.c | 10 +++++++--- include/linux/netlink.h | 10 +++++++--- net/core/rtnetlink.c | 9 +++++++-- net/ipv4/inet_diag.c | 18 ++++++++++++------ net/netfilter/ipset/ip_set_core.c | 10 +++++++--- net/netfilter/nf_conntrack_netlink.c | 18 ++++++++++++------ net/netfilter/nfnetlink_acct.c | 6 ++++-- net/netlink/af_netlink.c | 11 ++++------- net/netlink/genetlink.c | 9 +++++++-- net/unix/diag.c | 10 ++++++---- net/xfrm/xfrm_user.c | 9 +++++++-- 12 files changed, 87 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c index 16f8693cc147..b6ac1387770c 100644 --- a/crypto/crypto_user.c +++ b/crypto/crypto_user.c @@ -389,9 +389,13 @@ static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) (nlh->nlmsg_flags & NLM_F_DUMP))) { if (link->dump == NULL) return -EINVAL; - - return netlink_dump_start(crypto_nlsk, skb, nlh, - link->dump, link->done, 0); + { + struct netlink_dump_control c = { + .dump = link->dump, + .done = link->done, + }; + return netlink_dump_start(crypto_nlsk, skb, nlh, &c); + } } err = nlmsg_parse(nlh, crypto_msg_min[type], attrs, CRYPTOCFGA_MAX, diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c index d1c8196d15d7..396e29370304 100644 --- a/drivers/infiniband/core/netlink.c +++ b/drivers/infiniband/core/netlink.c @@ -147,9 +147,13 @@ static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (op < 0 || op >= client->nops || !client->cb_table[RDMA_NL_GET_OP(op)].dump) return -EINVAL; - return netlink_dump_start(nls, skb, nlh, - client->cb_table[op].dump, - NULL, 0); + + { + struct netlink_dump_control c = { + .dump = client->cb_table[op].dump, + }; + return netlink_dump_start(nls, skb, nlh, &c); + } } } diff --git a/include/linux/netlink.h b/include/linux/netlink.h index a390e9d54827..1f8c1a95f57c 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -248,11 +248,15 @@ __nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags) #define NLMSG_PUT(skb, pid, seq, type, len) \ NLMSG_NEW(skb, pid, seq, type, len, 0) +struct netlink_dump_control { + int (*dump)(struct sk_buff *skb, struct netlink_callback *); + int (*done)(struct netlink_callback*); + u16 min_dump_alloc; +}; + extern int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, const struct nlmsghdr *nlh, - int (*dump)(struct sk_buff *skb, struct netlink_callback*), - int (*done)(struct netlink_callback*), - u16 min_dump_alloc); + struct netlink_dump_control *control); #define NL_NONROOT_RECV 0x1 diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 65aebd450027..7aef62e53113 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1981,8 +1981,13 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) __rtnl_unlock(); rtnl = net->rtnl; - err = netlink_dump_start(rtnl, skb, nlh, dumpit, - NULL, min_dump_alloc); + { + struct netlink_dump_control c = { + .dump = dumpit, + .min_dump_alloc = min_dump_alloc, + }; + err = netlink_dump_start(rtnl, skb, nlh, &c); + } rtnl_lock(); return err; } diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index fcf281819cd4..8d25a1c557eb 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -960,9 +960,12 @@ static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh) inet_diag_bc_audit(nla_data(attr), nla_len(attr))) return -EINVAL; } - - return netlink_dump_start(sock_diag_nlsk, skb, nlh, - inet_diag_dump_compat, NULL, 0); + { + struct netlink_dump_control c = { + .dump = inet_diag_dump_compat, + }; + return netlink_dump_start(sock_diag_nlsk, skb, nlh, &c); + } } return inet_diag_get_exact_compat(skb, nlh); @@ -985,9 +988,12 @@ static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) inet_diag_bc_audit(nla_data(attr), nla_len(attr))) return -EINVAL; } - - return netlink_dump_start(sock_diag_nlsk, skb, h, - inet_diag_dump, NULL, 0); + { + struct netlink_dump_control c = { + .dump = inet_diag_dump, + }; + return netlink_dump_start(sock_diag_nlsk, skb, h, &c); + } } return inet_diag_get_exact(skb, h, (struct inet_diag_req_v2 *)NLMSG_DATA(h)); diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 32dbf0fa89db..e7f90e7082b4 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1162,9 +1162,13 @@ ip_set_dump(struct sock *ctnl, struct sk_buff *skb, if (unlikely(protocol_failed(attr))) return -IPSET_ERR_PROTOCOL; - return netlink_dump_start(ctnl, skb, nlh, - ip_set_dump_start, - ip_set_dump_done, 0); + { + struct netlink_dump_control c = { + .dump = ip_set_dump_start, + .done = ip_set_dump_done, + }; + return netlink_dump_start(ctnl, skb, nlh, &c); + } } /* Add, del and test */ diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 9307b033c0c9..61f7feb7932b 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -977,9 +977,13 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, u16 zone; int err; - if (nlh->nlmsg_flags & NLM_F_DUMP) - return netlink_dump_start(ctnl, skb, nlh, ctnetlink_dump_table, - ctnetlink_done, 0); + if (nlh->nlmsg_flags & NLM_F_DUMP) { + struct netlink_dump_control c = { + .dump = ctnetlink_dump_table, + .done = ctnetlink_done, + }; + return netlink_dump_start(ctnl, skb, nlh, &c); + } err = ctnetlink_parse_zone(cda[CTA_ZONE], &zone); if (err < 0) @@ -1850,9 +1854,11 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, int err; if (nlh->nlmsg_flags & NLM_F_DUMP) { - return netlink_dump_start(ctnl, skb, nlh, - ctnetlink_exp_dump_table, - ctnetlink_exp_done, 0); + struct netlink_dump_control c = { + .dump = ctnetlink_exp_dump_table, + .done = ctnetlink_exp_done, + }; + return netlink_dump_start(ctnl, skb, nlh, &c); } err = ctnetlink_parse_zone(cda[CTA_EXPECT_ZONE], &zone); diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c index 11ba013e47f6..3eb348bfc4fb 100644 --- a/net/netfilter/nfnetlink_acct.c +++ b/net/netfilter/nfnetlink_acct.c @@ -171,8 +171,10 @@ nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb, char *acct_name; if (nlh->nlmsg_flags & NLM_F_DUMP) { - return netlink_dump_start(nfnl, skb, nlh, nfnl_acct_dump, - NULL, 0); + struct netlink_dump_control c = { + .dump = nfnl_acct_dump, + }; + return netlink_dump_start(nfnl, skb, nlh, &c); } if (!tb[NFACCT_NAME]) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 4d751e3d4b4b..ab74845876d2 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1736,10 +1736,7 @@ errout_skb: int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, const struct nlmsghdr *nlh, - int (*dump)(struct sk_buff *skb, - struct netlink_callback *), - int (*done)(struct netlink_callback *), - u16 min_dump_alloc) + struct netlink_dump_control *control) { struct netlink_callback *cb; struct sock *sk; @@ -1750,10 +1747,10 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, if (cb == NULL) return -ENOBUFS; - cb->dump = dump; - cb->done = done; + cb->dump = control->dump; + cb->done = control->done; cb->nlh = nlh; - cb->min_dump_alloc = min_dump_alloc; + cb->min_dump_alloc = control->min_dump_alloc; atomic_inc(&skb->users); cb->skb = skb; diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index a1154717219e..9f40441d7a7d 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -563,8 +563,13 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) return -EOPNOTSUPP; genl_unlock(); - err = netlink_dump_start(net->genl_sock, skb, nlh, - ops->dumpit, ops->done, 0); + { + struct netlink_dump_control c = { + .dump = ops->dumpit, + .done = ops->done, + }; + err = netlink_dump_start(net->genl_sock, skb, nlh, &c); + } genl_lock(); return err; } diff --git a/net/unix/diag.c b/net/unix/diag.c index 6b7697fd911b..4195555aea65 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -301,10 +301,12 @@ static int unix_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) if (nlmsg_len(h) < hdrlen) return -EINVAL; - if (h->nlmsg_flags & NLM_F_DUMP) - return netlink_dump_start(sock_diag_nlsk, skb, h, - unix_diag_dump, NULL, 0); - else + if (h->nlmsg_flags & NLM_F_DUMP) { + struct netlink_dump_control c = { + .dump = unix_diag_dump, + }; + return netlink_dump_start(sock_diag_nlsk, skb, h, &c); + } else return unix_diag_get_exact(skb, h, (struct unix_diag_req *)NLMSG_DATA(h)); } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 66b84fbf2746..7128dde0fe1a 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2299,8 +2299,13 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (link->dump == NULL) return -EINVAL; - return netlink_dump_start(net->xfrm.nlsk, skb, nlh, - link->dump, link->done, 0); + { + struct netlink_dump_control c = { + .dump = link->dump, + .done = link->done, + }; + return netlink_dump_start(net->xfrm.nlsk, skb, nlh, &c); + } } err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX, -- cgit From 7175c883071515f0d4c1cece2646203b8a5a7415 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 24 Feb 2012 14:30:16 +0000 Subject: netlink: allow to pass data pointer to netlink_dump_start() callback This patch allows you to pass a data pointer that can be accessed from the dump callback. Netfilter is going to use this patch to provide filtered dumps to user-space. This is specifically interesting in ctnetlink that may handle lots of conntrack entries. We can save precious cycles by skipping the conversion to TLV format of conntrack entries that are not interesting for user-space. More specifically, ctnetlink will include one operation to allow to filter the dumping of conntrack entries by ctmark values. Signed-off-by: Pablo Neira Ayuso Signed-off-by: David S. Miller --- include/linux/netlink.h | 2 ++ net/netlink/af_netlink.c | 1 + 2 files changed, 3 insertions(+) (limited to 'include') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 1f8c1a95f57c..a2092f582a78 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -225,6 +225,7 @@ struct netlink_callback { int (*dump)(struct sk_buff * skb, struct netlink_callback *cb); int (*done)(struct netlink_callback *cb); + void *data; u16 family; u16 min_dump_alloc; unsigned int prev_seq, seq; @@ -251,6 +252,7 @@ __nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags) struct netlink_dump_control { int (*dump)(struct sk_buff *skb, struct netlink_callback *); int (*done)(struct netlink_callback*); + void *data; u16 min_dump_alloc; }; diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index ab74845876d2..32bb75324e76 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1750,6 +1750,7 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, cb->dump = control->dump; cb->done = control->done; cb->nlh = nlh; + cb->data = control->data; cb->min_dump_alloc = control->min_dump_alloc; atomic_inc(&skb->users); cb->skb = skb; -- cgit From 0f298a285f2e365cb34f69d1f79bb9fc996f683d Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 24 Feb 2012 14:41:50 +0000 Subject: netfilter: ctnetlink: support kernel-space dump filtering by ctmark This patch adds CTA_MARK_MASK which, together with CTA_MARK, allows you to selectively send conntrack entries to user-space by returning those that match mark & mask. With this, we can save cycles in the building and the parsing of the entries that may be later on filtered out in user-space by using the ctmark & mask. Signed-off-by: Pablo Neira Ayuso Signed-off-by: David S. Miller --- include/linux/netfilter/nfnetlink_conntrack.h | 1 + net/netfilter/nf_conntrack_netlink.c | 35 ++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index debf1aefd753..d498a4426ebf 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -43,6 +43,7 @@ enum ctattr_type { CTA_ZONE, CTA_SECCTX, CTA_TIMESTAMP, + CTA_MARK_MASK, __CTA_MAX }; #define CTA_MAX (__CTA_MAX - 1) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 61f7feb7932b..28d0312d890a 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -691,9 +691,18 @@ static int ctnetlink_done(struct netlink_callback *cb) { if (cb->args[1]) nf_ct_put((struct nf_conn *)cb->args[1]); + if (cb->data) + kfree(cb->data); return 0; } +struct ctnetlink_dump_filter { + struct { + u_int32_t val; + u_int32_t mask; + } mark; +}; + static int ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) { @@ -703,7 +712,9 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) struct hlist_nulls_node *n; struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); u_int8_t l3proto = nfmsg->nfgen_family; - +#ifdef CONFIG_NF_CONNTRACK_MARK + const struct ctnetlink_dump_filter *filter = cb->data; +#endif spin_lock_bh(&nf_conntrack_lock); last = (struct nf_conn *)cb->args[1]; for (; cb->args[0] < net->ct.htable_size; cb->args[0]++) { @@ -723,6 +734,12 @@ restart: continue; cb->args[1] = 0; } +#ifdef CONFIG_NF_CONNTRACK_MARK + if (filter && !((ct->mark & filter->mark.mask) == + filter->mark.val)) { + continue; + } +#endif if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, NFNL_MSG_TYPE( @@ -894,6 +911,7 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = { [CTA_NAT_DST] = { .type = NLA_NESTED }, [CTA_TUPLE_MASTER] = { .type = NLA_NESTED }, [CTA_ZONE] = { .type = NLA_U16 }, + [CTA_MARK_MASK] = { .type = NLA_U32 }, }; static int @@ -982,6 +1000,21 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, .dump = ctnetlink_dump_table, .done = ctnetlink_done, }; +#ifdef CONFIG_NF_CONNTRACK_MARK + if (cda[CTA_MARK] && cda[CTA_MARK_MASK]) { + struct ctnetlink_dump_filter *filter; + + filter = kzalloc(sizeof(struct ctnetlink_dump_filter), + GFP_ATOMIC); + if (filter == NULL) + return -ENOMEM; + + filter->mark.val = ntohl(nla_get_be32(cda[CTA_MARK])); + filter->mark.mask = + ntohl(nla_get_be32(cda[CTA_MARK_MASK])); + c.data = filter; + } +#endif return netlink_dump_start(ctnl, skb, nlh, &c); } -- cgit From 622121719934f60378279eb440d3cec2fc3176d2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 25 Feb 2012 00:51:05 +0000 Subject: mlx4_en: dont change mac_header on xmit A driver xmit function is not allowed to change skb without special care. mlx4_en_xmit() should not call skb_reset_mac_header() and instead should use skb->data to access ethernet header. This removes a dumb test : if (ethh && ethh->h_dest) Also remove this slow mlx4_en_mac_to_u64() call, we can use get_unaligned() to get faster code. Signed-off-by: Eric Dumazet Cc: Yevgeny Petrilin Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_tx.c | 15 +++------------ include/linux/mlx4/qp.h | 5 ++++- 2 files changed, 7 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index ff3250586584..2fd51405a509 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -601,8 +601,6 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) struct skb_frag_struct *frag; struct mlx4_en_tx_info *tx_info; struct ethhdr *ethh; - u64 mac; - u32 mac_l, mac_h; int tx_ind = 0; int nr_txbb; int desc_size; @@ -687,16 +685,9 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) } /* Copy dst mac address to wqe */ - skb_reset_mac_header(skb); - ethh = eth_hdr(skb); - if (ethh && ethh->h_dest) { - mac = mlx4_en_mac_to_u64(ethh->h_dest); - mac_h = (u32) ((mac & 0xffff00000000ULL) >> 16); - mac_l = (u32) (mac & 0xffffffff); - tx_desc->ctrl.srcrb_flags |= cpu_to_be32(mac_h); - tx_desc->ctrl.imm = cpu_to_be32(mac_l); - } - + ethh = (struct ethhdr *)skb->data; + tx_desc->ctrl.srcrb_flags16[0] = get_unaligned((u16 *)ethh->h_dest); + tx_desc->ctrl.imm = get_unaligned((u32 *)(ethh->h_dest + 2)); /* Handle LSO (TSO) packets */ if (lso_header_size) { /* Mark opcode as LSO */ diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h index bee8fa231276..091f9e7dc8b9 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -212,7 +212,10 @@ struct mlx4_wqe_ctrl_seg { * [1] SE (solicited event) * [0] FL (force loopback) */ - __be32 srcrb_flags; + union { + __be32 srcrb_flags; + __be16 srcrb_flags16[2]; + }; /* * imm is immediate data for send/RDMA write w/ immediate; * also invalidation key for send with invalidate; input -- cgit From 816a11d5ced501d368fabe09172f3d62744e8b53 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 26 Feb 2012 13:04:52 +0200 Subject: Bluetooth: Use kernel int types instead of ones from stdint.h u8/__u8/u32/etc should be used in the kernel instead of stdint.h types. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 4 ++-- net/bluetooth/bnep/sock.c | 6 +++--- net/bluetooth/cmtp/sock.c | 6 +++--- net/bluetooth/hidp/sock.c | 6 +++--- net/bluetooth/mgmt.c | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 05bd9aca4054..0c54fcfe7e0f 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -691,8 +691,8 @@ struct hci_cp_host_buffer_size { #define HCI_OP_WRITE_EIR 0x0c52 struct hci_cp_write_eir { - uint8_t fec; - uint8_t data[HCI_MAX_EIR_LENGTH]; + __u8 fec; + __u8 data[HCI_MAX_EIR_LENGTH]; } __packed; #define HCI_OP_READ_SSP_MODE 0x0c55 diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c index 17800b1d28ea..9f9c8dcd8af0 100644 --- a/net/bluetooth/bnep/sock.c +++ b/net/bluetooth/bnep/sock.c @@ -143,10 +143,10 @@ static int bnep_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne { if (cmd == BNEPGETCONNLIST) { struct bnep_connlist_req cl; - uint32_t uci; + u32 uci; int err; - if (get_user(cl.cnum, (uint32_t __user *) arg) || + if (get_user(cl.cnum, (u32 __user *) arg) || get_user(uci, (u32 __user *) (arg + 4))) return -EFAULT; @@ -157,7 +157,7 @@ static int bnep_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne err = bnep_get_connlist(&cl); - if (!err && put_user(cl.cnum, (uint32_t __user *) arg)) + if (!err && put_user(cl.cnum, (u32 __user *) arg)) err = -EFAULT; return err; diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c index 3f2dd5c25ae5..1230faaac29b 100644 --- a/net/bluetooth/cmtp/sock.c +++ b/net/bluetooth/cmtp/sock.c @@ -137,10 +137,10 @@ static int cmtp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne { if (cmd == CMTPGETCONNLIST) { struct cmtp_connlist_req cl; - uint32_t uci; + u32 uci; int err; - if (get_user(cl.cnum, (uint32_t __user *) arg) || + if (get_user(cl.cnum, (u32 __user *) arg) || get_user(uci, (u32 __user *) (arg + 4))) return -EFAULT; @@ -151,7 +151,7 @@ static int cmtp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne err = cmtp_get_connlist(&cl); - if (!err && put_user(cl.cnum, (uint32_t __user *) arg)) + if (!err && put_user(cl.cnum, (u32 __user *) arg)) err = -EFAULT; return err; diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c index 178ac7f127ad..73a32d705c1f 100644 --- a/net/bluetooth/hidp/sock.c +++ b/net/bluetooth/hidp/sock.c @@ -160,10 +160,10 @@ static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne { if (cmd == HIDPGETCONNLIST) { struct hidp_connlist_req cl; - uint32_t uci; + u32 uci; int err; - if (get_user(cl.cnum, (uint32_t __user *) arg) || + if (get_user(cl.cnum, (u32 __user *) arg) || get_user(uci, (u32 __user *) (arg + 4))) return -EFAULT; @@ -174,7 +174,7 @@ static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne err = hidp_get_connlist(&cl); - if (!err && put_user(cl.cnum, (uint32_t __user *) arg)) + if (!err && put_user(cl.cnum, (u32 __user *) arg)) err = -EFAULT; return err; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 07e31f73f703..27830f401698 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1075,7 +1075,7 @@ static int set_link_security(struct sock *sk, u16 index, void *data, u16 len) struct mgmt_mode *cp = data; struct pending_cmd *cmd; struct hci_dev *hdev; - uint8_t val; + u8 val; int err; BT_DBG("request for hci%u", index); @@ -1147,7 +1147,7 @@ static int set_ssp(struct sock *sk, u16 index, void *data, u16 len) struct mgmt_mode *cp = data; struct pending_cmd *cmd; struct hci_dev *hdev; - uint8_t val; + u8 val; int err; BT_DBG("request for hci%u", index); -- cgit From 136ced891ad2762dfe5ba476103e82ea2b9aff41 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 13 Feb 2012 13:24:19 +0200 Subject: usb: otg: Remove OTG specific members from usb_phy All the drivers are now converted to use struct usb_otg, so removing the OTG specific members from struct usb_phy. Signed-off-by: Heikki Krogerus Reviewed-by: Marek Vasut Signed-off-by: Felipe Balbi --- include/linux/usb/otg.h | 50 +++++-------------------------------------------- 1 file changed, 5 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 5c1cfbc73555..de89342098e4 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -89,15 +89,11 @@ struct usb_phy { const char *label; unsigned int flags; - u8 default_a; enum usb_otg_state state; enum usb_phy_events last_event; struct usb_otg *otg; - struct usb_bus *host; - struct usb_gadget *gadget; - struct usb_phy_io_ops *io_ops; void __iomem *io_priv; @@ -112,32 +108,14 @@ struct usb_phy { int (*init)(struct usb_phy *x); void (*shutdown)(struct usb_phy *x); - /* bind/unbind the host controller */ - int (*set_host)(struct usb_phy *x, - struct usb_bus *host); - - /* bind/unbind the peripheral controller */ - int (*set_peripheral)(struct usb_phy *x, - struct usb_gadget *gadget); - /* effective for B devices, ignored for A-peripheral */ int (*set_power)(struct usb_phy *x, unsigned mA); - /* effective for A-peripheral, ignored for B devices */ - int (*set_vbus)(struct usb_phy *x, - bool enabled); - /* for non-OTG B devices: set transceiver into suspend mode */ int (*set_suspend)(struct usb_phy *x, int suspend); - /* for B devices only: start session with A-Host */ - int (*start_srp)(struct usb_phy *x); - - /* start or continue HNP role switch */ - int (*start_hnp)(struct usb_phy *x); - }; @@ -219,7 +197,7 @@ otg_start_hnp(struct usb_phy *x) if (x->otg && x->otg->start_hnp) return x->otg->start_hnp(x->otg); - return x->start_hnp(x); + return -ENOTSUPP; } /* Context: can sleep */ @@ -229,7 +207,7 @@ otg_set_vbus(struct usb_phy *x, bool enabled) if (x->otg && x->otg->set_vbus) return x->otg->set_vbus(x->otg, enabled); - return x->set_vbus(x, enabled); + return -ENOTSUPP; } /* for HCDs */ @@ -239,7 +217,7 @@ otg_set_host(struct usb_phy *x, struct usb_bus *host) if (x->otg && x->otg->set_host) return x->otg->set_host(x->otg, host); - return x->set_host(x, host); + return -ENOTSUPP; } /* for usb peripheral controller drivers */ @@ -251,7 +229,7 @@ otg_set_peripheral(struct usb_phy *x, struct usb_gadget *periph) if (x->otg && x->otg->set_peripheral) return x->otg->set_peripheral(x->otg, periph); - return x->set_peripheral(x, periph); + return -ENOTSUPP; } static inline int @@ -278,7 +256,7 @@ otg_start_srp(struct usb_phy *x) if (x->otg && x->otg->start_srp) return x->otg->start_srp(x->otg); - return x->start_srp(x); + return -ENOTSUPP; } /* notifiers */ @@ -297,22 +275,4 @@ usb_unregister_notifier(struct usb_phy *x, struct notifier_block *nb) /* for OTG controller drivers (and maybe other stuff) */ extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num); -/* Temporary aliases for transceiver functions */ -#define otg_set_transceiver(x) usb_set_transceiver(x) -#define otg_get_transceiver() usb_get_transceiver() -#define otg_put_transceiver(x) usb_put_transceiver(x) - -#define otg_io_read(x, a) usb_phy_io_read(x, a) -#define otg_io_write(x, a, b) usb_phy_io_write(x, a, b) - -#define otg_init(x) usb_phy_init(x) -#define otg_shutdown(x) usb_phy_shutdown(x) -#define otg_set_power(x, a) usb_phy_set_power(x, a) -#define otg_set_suspend(x, a) usb_phy_set_suspend(x, a) - -#define otg_register_notifier(x, a) usb_register_notifier(x, a) -#define otg_unregister_notifier(x, a) usb_unregiser_notifier(x, a) - -#define otg_io_access_ops usb_phy_io_ops - #endif /* __LINUX_USB_OTG_H */ -- cgit From 6e13c6505cdff9766d5268ffb8c972c1a2f996e6 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 13 Feb 2012 13:24:20 +0200 Subject: usb: otg: Convert all users to pass struct usb_otg for OTG functions This changes the otg functions so that they receive struct otg instead of struct usb_phy as parameter and converts all users of these functions to pass the otg member of their usb_phy. Includes fixes to IMX code from Sascha Hauer. [ balbi@ti.com : fixed a compile warning on ehci-mv.c ] Signed-off-by: Heikki Krogerus Acked-by: Sascha Hauer Acked-by: Igor Grinberg Acked-by: Pavankumar Kondeti Acked-by: Li Yang Acked-by: Alan Stern Reviewed-by: Marek Vasut Signed-off-by: Felipe Balbi --- arch/arm/mach-pxa/pxa3xx-ulpi.c | 8 ++++---- drivers/usb/gadget/ci13xxx_udc.c | 7 ++++--- drivers/usb/gadget/fsl_udc_core.c | 5 +++-- drivers/usb/gadget/langwell_udc.c | 2 +- drivers/usb/gadget/mv_udc_core.c | 3 ++- drivers/usb/gadget/omap_udc.c | 7 ++++--- drivers/usb/gadget/pxa25x_udc.c | 5 +++-- drivers/usb/gadget/pxa27x_udc.c | 5 +++-- drivers/usb/gadget/s3c-hsudc.c | 5 +++-- drivers/usb/host/ehci-fsl.c | 4 ++-- drivers/usb/host/ehci-hub.c | 2 +- drivers/usb/host/ehci-msm.c | 4 ++-- drivers/usb/host/ehci-mv.c | 4 ++-- drivers/usb/host/ehci-mxc.c | 2 +- drivers/usb/host/ehci-tegra.c | 6 +++--- drivers/usb/host/ohci-omap.c | 6 +++--- drivers/usb/musb/musb_core.c | 2 +- drivers/usb/musb/musb_gadget.c | 8 ++++---- drivers/usb/musb/omap2430.c | 8 ++++---- include/linux/usb/otg.h | 30 +++++++++++++++--------------- 20 files changed, 65 insertions(+), 58 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-pxa/pxa3xx-ulpi.c b/arch/arm/mach-pxa/pxa3xx-ulpi.c index a13f33565623..5ead6d480c6d 100644 --- a/arch/arm/mach-pxa/pxa3xx-ulpi.c +++ b/arch/arm/mach-pxa/pxa3xx-ulpi.c @@ -145,13 +145,13 @@ static int pxa310_start_otg_host_transcvr(struct usb_bus *host) return err; } - err = otg_set_vbus(u2d->otg, 1); + err = otg_set_vbus(u2d->otg->otg, 1); if (err) { pr_err("OTG transceiver VBUS set failed"); return err; } - err = otg_set_host(u2d->otg, host); + err = otg_set_host(u2d->otg->otg, host); if (err) pr_err("OTG transceiver Host mode set failed"); @@ -189,8 +189,8 @@ static void pxa310_stop_otg_hc(void) { pxa310_otg_transceiver_rtsm(); - otg_set_host(u2d->otg, NULL); - otg_set_vbus(u2d->otg, 0); + otg_set_host(u2d->otg->otg, NULL); + otg_set_vbus(u2d->otg->otg, 0); usb_phy_shutdown(u2d->otg); } diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 68ad160f2589..b27cb0b0077b 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -2928,7 +2928,8 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, goto unreg_device; if (udc->transceiver) { - retval = otg_set_peripheral(udc->transceiver, &udc->gadget); + retval = otg_set_peripheral(udc->transceiver->otg, + &udc->gadget); if (retval) goto remove_dbg; } @@ -2945,7 +2946,7 @@ static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, remove_trans: if (udc->transceiver) { - otg_set_peripheral(udc->transceiver, &udc->gadget); + otg_set_peripheral(udc->transceiver->otg, &udc->gadget); usb_put_transceiver(udc->transceiver); } @@ -2981,7 +2982,7 @@ static void udc_remove(void) usb_del_gadget_udc(&udc->gadget); if (udc->transceiver) { - otg_set_peripheral(udc->transceiver, &udc->gadget); + otg_set_peripheral(udc->transceiver->otg, &udc->gadget); usb_put_transceiver(udc->transceiver); } #ifdef CONFIG_USB_GADGET_DEBUG_FILES diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 21fdfdb18ba8..1e8c0c425fa1 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -1966,7 +1966,8 @@ static int fsl_start(struct usb_gadget_driver *driver, /* connect to bus through transceiver */ if (udc_controller->transceiver) { - retval = otg_set_peripheral(udc_controller->transceiver, + retval = otg_set_peripheral( + udc_controller->transceiver->otg, &udc_controller->gadget); if (retval < 0) { ERR("can't bind to transceiver\n"); @@ -2006,7 +2007,7 @@ static int fsl_stop(struct usb_gadget_driver *driver) return -EINVAL; if (udc_controller->transceiver) - otg_set_peripheral(udc_controller->transceiver, NULL); + otg_set_peripheral(udc_controller->transceiver->otg, NULL); /* stop DR, disable intr */ dr_controller_stop(udc_controller); diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index b19a9e46e69d..42a88b680f25 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -1906,7 +1906,7 @@ static int langwell_stop(struct usb_gadget *g, /* unbind OTG transceiver */ if (dev->transceiver) - (void)otg_set_peripheral(dev->transceiver, 0); + (void)otg_set_peripheral(dev->transceiver->otg, 0); /* disable interrupt and set controller to stop state */ langwell_udc_stop(dev); diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 50baf3ea336b..7369fd92c03f 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -1384,7 +1384,8 @@ static int mv_udc_start(struct usb_gadget_driver *driver, } if (udc->transceiver) { - retval = otg_set_peripheral(udc->transceiver, &udc->gadget); + retval = otg_set_peripheral(udc->transceiver->otg, + &udc->gadget); if (retval) { dev_err(&udc->dev->dev, "unable to register peripheral to otg\n"); diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index e82c6995ce26..ace8a652b32b 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -1213,7 +1213,7 @@ static int omap_wakeup(struct usb_gadget *gadget) /* NOTE: non-OTG systems may use SRP TOO... */ } else if (!(udc->devstat & UDC_ATT)) { if (udc->transceiver) - retval = otg_start_srp(udc->transceiver); + retval = otg_start_srp(udc->transceiver->otg); } spin_unlock_irqrestore(&udc->lock, flags); @@ -2156,7 +2156,8 @@ static int omap_udc_start(struct usb_gadget_driver *driver, /* connect to bus through transceiver */ if (udc->transceiver) { - status = otg_set_peripheral(udc->transceiver, &udc->gadget); + status = otg_set_peripheral(udc->transceiver->otg, + &udc->gadget); if (status < 0) { ERR("can't bind to transceiver\n"); if (driver->unbind) { @@ -2202,7 +2203,7 @@ static int omap_udc_stop(struct usb_gadget_driver *driver) omap_vbus_session(&udc->gadget, 0); if (udc->transceiver) - (void) otg_set_peripheral(udc->transceiver, NULL); + (void) otg_set_peripheral(udc->transceiver->otg, NULL); else pullup_disable(udc); diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index b86518e49415..df681b5cd695 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -1301,7 +1301,8 @@ fail: /* connect to bus through transceiver */ if (dev->transceiver) { - retval = otg_set_peripheral(dev->transceiver, &dev->gadget); + retval = otg_set_peripheral(dev->transceiver->otg, + &dev->gadget); if (retval) { DMSG("can't bind to transceiver\n"); if (driver->unbind) @@ -1360,7 +1361,7 @@ static int pxa25x_stop(struct usb_gadget_driver *driver) local_irq_enable(); if (dev->transceiver) - (void) otg_set_peripheral(dev->transceiver, NULL); + (void) otg_set_peripheral(dev->transceiver->otg, NULL); driver->unbind(&dev->gadget); dev->gadget.dev.driver = NULL; diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 1906ed0dcdc2..98acb3ab9e17 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -1835,7 +1835,8 @@ static int pxa27x_udc_start(struct usb_gadget_driver *driver, driver->driver.name); if (udc->transceiver) { - retval = otg_set_peripheral(udc->transceiver, &udc->gadget); + retval = otg_set_peripheral(udc->transceiver->otg, + &udc->gadget); if (retval) { dev_err(udc->dev, "can't bind to transceiver\n"); goto transceiver_fail; @@ -1908,7 +1909,7 @@ static int pxa27x_udc_stop(struct usb_gadget_driver *driver) driver->driver.name); if (udc->transceiver) - return otg_set_peripheral(udc->transceiver, NULL); + return otg_set_peripheral(udc->transceiver->otg, NULL); return 0; } diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index e124bfa399f5..c2f3aa650584 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1166,7 +1166,8 @@ static int s3c_hsudc_start(struct usb_gadget *gadget, /* connect to bus through transceiver */ if (hsudc->transceiver) { - ret = otg_set_peripheral(hsudc->transceiver, &hsudc->gadget); + ret = otg_set_peripheral(hsudc->transceiver->otg, + &hsudc->gadget); if (ret) { dev_err(hsudc->dev, "%s: can't bind to transceiver\n", hsudc->gadget.name); @@ -1214,7 +1215,7 @@ static int s3c_hsudc_stop(struct usb_gadget *gadget, spin_unlock_irqrestore(&hsudc->lock, flags); if (hsudc->transceiver) - (void) otg_set_peripheral(hsudc->transceiver, NULL); + (void) otg_set_peripheral(hsudc->transceiver->otg, NULL); disable_irq(hsudc->irq); diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 481effdc7f54..4786ba5f2e6c 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -147,7 +147,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, hcd, ehci, ehci->transceiver); if (ehci->transceiver) { - retval = otg_set_host(ehci->transceiver, + retval = otg_set_host(ehci->transceiver->otg, &ehci_to_hcd(ehci)->self); if (retval) { if (ehci->transceiver) @@ -194,7 +194,7 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, struct ehci_hcd *ehci = hcd_to_ehci(hcd); if (ehci->transceiver) { - otg_set_host(ehci->transceiver, NULL); + otg_set_host(ehci->transceiver->otg, NULL); put_device(ehci->transceiver->dev); } diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 77bbb2357e47..75a2b30d8aa6 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -727,7 +727,7 @@ static int ehci_hub_control ( #ifdef CONFIG_USB_OTG if ((hcd->self.otg_port == (wIndex + 1)) && hcd->self.b_hnp_enable) { - otg_start_hnp(ehci->transceiver); + otg_start_hnp(ehci->transceiver->otg); break; } #endif diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index d8db5ecf5e37..9803a55fd5f4 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -152,7 +152,7 @@ static int ehci_msm_probe(struct platform_device *pdev) goto unmap; } - ret = otg_set_host(phy, &hcd->self); + ret = otg_set_host(phy->otg, &hcd->self); if (ret < 0) { dev_err(&pdev->dev, "unable to register with transceiver\n"); goto put_transceiver; @@ -186,7 +186,7 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); - otg_set_host(phy, NULL); + otg_set_host(phy->otg, NULL); usb_put_transceiver(phy); usb_put_hcd(hcd); diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 62a63b598149..a936bbcff8f4 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -261,7 +261,7 @@ static int mv_ehci_probe(struct platform_device *pdev) goto err_disable_clk; } - retval = otg_set_host(ehci_mv->otg, &hcd->self); + retval = otg_set_host(ehci_mv->otg->otg, &hcd->self); if (retval < 0) { dev_err(&pdev->dev, "unable to register with transceiver\n"); @@ -332,7 +332,7 @@ static int mv_ehci_remove(struct platform_device *pdev) usb_remove_hcd(hcd); if (ehci_mv->otg) { - otg_set_host(ehci_mv->otg, NULL); + otg_set_host(ehci_mv->otg->otg, NULL); usb_put_transceiver(ehci_mv->otg); } diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index b762fe0955b9..a797d51ecbe8 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -226,7 +226,7 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) ret = -ENODEV; goto err_add; } - ret = otg_set_vbus(pdata->otg, 1); + ret = otg_set_vbus(pdata->otg->otg, 1); if (ret) { dev_err(dev, "unable to enable vbus on transceiver\n"); goto err_add; diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 701a0bf58623..3de48a2d7955 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -735,7 +735,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) if (pdata->operating_mode == TEGRA_USB_OTG) { tegra->transceiver = usb_get_transceiver(); if (tegra->transceiver) - otg_set_host(tegra->transceiver, &hcd->self); + otg_set_host(tegra->transceiver->otg, &hcd->self); } #endif @@ -750,7 +750,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) fail: #ifdef CONFIG_USB_OTG_UTILS if (tegra->transceiver) { - otg_set_host(tegra->transceiver, NULL); + otg_set_host(tegra->transceiver->otg, NULL); usb_put_transceiver(tegra->transceiver); } #endif @@ -808,7 +808,7 @@ static int tegra_ehci_remove(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS if (tegra->transceiver) { - otg_set_host(tegra->transceiver, NULL); + otg_set_host(tegra->transceiver->otg, NULL); usb_put_transceiver(tegra->transceiver); } #endif diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 744e25da800f..96451e41ee8a 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -171,7 +171,7 @@ static void start_hnp(struct ohci_hcd *ohci) unsigned long flags; u32 l; - otg_start_hnp(ohci->transceiver); + otg_start_hnp(ohci->transceiver->otg); local_irq_save(flags); ohci->transceiver->state = OTG_STATE_A_SUSPEND; @@ -212,7 +212,7 @@ static int ohci_omap_init(struct usb_hcd *hcd) if (need_transceiver) { ohci->transceiver = usb_get_transceiver(); if (ohci->transceiver) { - int status = otg_set_host(ohci->transceiver, + int status = otg_set_host(ohci->transceiver->otg, &ohci_to_hcd(ohci)->self); dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n", ohci->transceiver->label, status); @@ -404,7 +404,7 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) usb_remove_hcd(hcd); if (ohci->transceiver) { - (void) otg_set_host(ohci->transceiver, 0); + (void) otg_set_host(ohci->transceiver->otg, 0); put_device(ohci->transceiver->dev); } if (machine_is_omap_osk()) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 7e9be74dba4d..88bf453e33da 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1961,7 +1961,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) if (is_host_enabled(musb)) { struct usb_hcd *hcd = musb_to_hcd(musb); - otg_set_host(musb->xceiv, &hcd->self); + otg_set_host(musb->xceiv->otg, &hcd->self); if (is_otg_enabled(musb)) hcd->self.otg_port = 1; diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 3a1793663d91..caeaf9f96f73 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1624,7 +1624,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) } spin_unlock_irqrestore(&musb->lock, flags); - otg_start_srp(musb->xceiv); + otg_start_srp(musb->xceiv->otg); spin_lock_irqsave(&musb->lock, flags); /* Block idling for at least 1s */ @@ -1915,7 +1915,7 @@ static int musb_gadget_start(struct usb_gadget *g, spin_lock_irqsave(&musb->lock, flags); musb->is_active = 1; - otg_set_peripheral(musb->xceiv, &musb->g); + otg_set_peripheral(otg, &musb->g); musb->xceiv->state = OTG_STATE_B_IDLE; /* @@ -1947,7 +1947,7 @@ static int musb_gadget_start(struct usb_gadget *g, if ((musb->xceiv->last_event == USB_EVENT_ID) && otg->set_vbus) - otg_set_vbus(musb->xceiv, 1); + otg_set_vbus(otg, 1); hcd->self.uses_pio_for_control = 1; } @@ -2029,7 +2029,7 @@ static int musb_gadget_stop(struct usb_gadget *g, musb->xceiv->state = OTG_STATE_UNDEFINED; stop_activity(musb, driver); - otg_set_peripheral(musb->xceiv, NULL); + otg_set_peripheral(musb->xceiv->otg, NULL); dev_dbg(musb->controller, "unregistering driver %s\n", driver->function); diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 96a3d3763cc4..d8df60a9fc2a 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -164,8 +164,8 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on) } } - if (ret && musb->xceiv->set_vbus) - otg_set_vbus(musb->xceiv, 1); + if (ret && otg->set_vbus) + otg_set_vbus(otg, 1); } else { musb->is_active = 1; otg->default_a = 1; @@ -270,8 +270,8 @@ static void musb_otg_notifier_work(struct work_struct *data_notifier_work) } if (data->interface_type == MUSB_INTERFACE_UTMI) { - if (musb->xceiv->set_vbus) - otg_set_vbus(musb->xceiv, 0); + if (musb->xceiv->otg->set_vbus) + otg_set_vbus(musb->xceiv->otg, 0); } usb_phy_shutdown(musb->xceiv); break; diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index de89342098e4..f67810f8f21b 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -192,30 +192,30 @@ static inline const char *otg_state_string(enum usb_otg_state state) /* Context: can sleep */ static inline int -otg_start_hnp(struct usb_phy *x) +otg_start_hnp(struct usb_otg *otg) { - if (x->otg && x->otg->start_hnp) - return x->otg->start_hnp(x->otg); + if (otg && otg->start_hnp) + return otg->start_hnp(otg); return -ENOTSUPP; } /* Context: can sleep */ static inline int -otg_set_vbus(struct usb_phy *x, bool enabled) +otg_set_vbus(struct usb_otg *otg, bool enabled) { - if (x->otg && x->otg->set_vbus) - return x->otg->set_vbus(x->otg, enabled); + if (otg && otg->set_vbus) + return otg->set_vbus(otg, enabled); return -ENOTSUPP; } /* for HCDs */ static inline int -otg_set_host(struct usb_phy *x, struct usb_bus *host) +otg_set_host(struct usb_otg *otg, struct usb_bus *host) { - if (x->otg && x->otg->set_host) - return x->otg->set_host(x->otg, host); + if (otg && otg->set_host) + return otg->set_host(otg, host); return -ENOTSUPP; } @@ -224,10 +224,10 @@ otg_set_host(struct usb_phy *x, struct usb_bus *host) /* Context: can sleep */ static inline int -otg_set_peripheral(struct usb_phy *x, struct usb_gadget *periph) +otg_set_peripheral(struct usb_otg *otg, struct usb_gadget *periph) { - if (x->otg && x->otg->set_peripheral) - return x->otg->set_peripheral(x->otg, periph); + if (otg && otg->set_peripheral) + return otg->set_peripheral(otg, periph); return -ENOTSUPP; } @@ -251,10 +251,10 @@ usb_phy_set_suspend(struct usb_phy *x, int suspend) } static inline int -otg_start_srp(struct usb_phy *x) +otg_start_srp(struct usb_otg *otg) { - if (x->otg && x->otg->start_srp) - return x->otg->start_srp(x->otg); + if (otg && otg->start_srp) + return otg->start_srp(otg); return -ENOTSUPP; } -- cgit From cc2c04ec1ea8bd5137c99dc88bc04b4a07a11443 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 28 Feb 2012 02:03:24 +0200 Subject: Bluetooth: Add missing host features definitions This patch adds missing SSP and "Simultaneous LE & BR/EDR" feature bit definitions to hci.h. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 0c54fcfe7e0f..3acbebfb22b9 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -237,7 +237,9 @@ enum { #define LMP_EXTFEATURES 0x80 /* Extended LMP features */ -#define LMP_HOST_LE 0x02 +#define LMP_HOST_SSP 0x01 +#define LMP_HOST_LE 0x02 +#define LMP_HOST_LE_BREDR 0x04 /* Connection modes */ #define HCI_CM_ACTIVE 0x0000 -- cgit From a698908d3b3be915ac20cd37faeff1216f6b4fe8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 15 Dec 2011 13:31:48 +0200 Subject: usb: gadget: add generic map/unmap request utilities such utilities are currently duplicated on all UDC drivers basically with the same structure. Let's group all implementations into one generic implementation and get rid of that duplication. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc-core.c | 52 +++++++++++++++++++++++++++++++++++++++++++ include/linux/usb/gadget.h | 10 +++++++++ 2 files changed, 62 insertions(+) (limited to 'include') diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 0b0d12ccc487..56da49f31d6c 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -49,6 +50,57 @@ static DEFINE_MUTEX(udc_lock); /* ------------------------------------------------------------------------- */ +int usb_gadget_map_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in) +{ + if (req->length == 0) + return 0; + + if (req->num_sgs) { + int mapped; + + mapped = dma_map_sg(&gadget->dev, req->sg, req->num_sgs, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (mapped == 0) { + dev_err(&gadget->dev, "failed to map SGs\n"); + return -EFAULT; + } + + req->num_mapped_sgs = mapped; + } else { + req->dma = dma_map_single(&gadget->dev, req->buf, req->length, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (dma_mapping_error(&gadget->dev, req->dma)) { + dev_err(&gadget->dev, "failed to map buffer\n"); + return -EFAULT; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(usb_gadget_map_request); + +void usb_gadget_unmap_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in) +{ + if (req->length == 0) + return; + + if (req->num_mapped_sgs) { + dma_unmap_sg(&gadget->dev, req->sg, req->num_mapped_sgs, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + req->num_mapped_sgs = 0; + } else { + dma_unmap_single(&gadget->dev, req->dma, req->length, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + } +} +EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); + +/* ------------------------------------------------------------------------- */ + /** * usb_gadget_start - tells usb device controller to start up * @gadget: The gadget we want to get started diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index da653b5c7134..9517466ababb 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -950,6 +950,16 @@ static inline void usb_free_descriptors(struct usb_descriptor_header **v) /*-------------------------------------------------------------------------*/ +/* utility to simplify map/unmap of usb_requests to/from DMA */ + +extern int usb_gadget_map_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in); + +extern void usb_gadget_unmap_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in); + +/*-------------------------------------------------------------------------*/ + /* utility wrapping a simple endpoint selection policy */ extern struct usb_ep *usb_ep_autoconfig(struct usb_gadget *, -- cgit From fbe74e361c6586cdd996bc2805033999dd469e99 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 15 Feb 2012 11:58:54 +0100 Subject: security: struct security_operations kerneldoc fix unix_may_send hook has the prototype: int (*unix_may_send) (struct socket *sock, struct socket *other) so the documentation is wrongly referring to the second argument as @sock. Signed-off-by: Javier Martinez Canillas Signed-off-by: Jiri Kosina --- include/linux/security.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/security.h b/include/linux/security.h index 83c18e8c846d..c8949385e56e 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -812,7 +812,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * Check permissions before connecting or sending datagrams from @sock to * @other. * @sock contains the socket structure. - * @sock contains the peer socket structure. + * @other contains the peer socket structure. * Return 0 if permission is granted. * * The @unix_stream_connect and @unix_may_send hooks were necessary because -- cgit From e6be0c9e7e9f68b99e5a5ba609c455bae4b141d5 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Thu, 23 Feb 2012 13:42:30 +0100 Subject: compiler.h: Fix typo Signed-off-by: Alexander Stein Signed-off-by: Jiri Kosina --- include/linux/compiler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/compiler.h b/include/linux/compiler.h index 4a243546d142..923d093c9cea 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -236,7 +236,7 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect); /* * Rather then using noinline to prevent stack consumption, use - * noinline_for_stack instead. For documentaiton reasons. + * noinline_for_stack instead. For documentation reasons. */ #define noinline_for_stack noinline -- cgit From 8eedce996556d7d06522cd3a0e6069141c8dffe0 Mon Sep 17 00:00:00 2001 From: Jason Baron Date: Tue, 28 Feb 2012 13:49:01 -0500 Subject: static keys: Inline the static_key_enabled() function In the jump label enabled case, calling static_key_enabled() results in a function call. The function returns the results of a compare, so it really doesn't need the overhead of a full function call. Let's make it 'static inline' for both the jump label enabled and disabled cases. Signed-off-by: Jason Baron Cc: a.p.zijlstra@chello.nl Cc: rostedt@goodmis.org Cc: mathieu.desnoyers@polymtl.ca Cc: Andrew Morton Cc: Linus Torvalds Link: http://lkml.kernel.org/r/201202281849.q1SIn1p2023270@int-mx10.intmail.prod.int.phx2.redhat.com Signed-off-by: Ingo Molnar --- include/linux/jump_label.h | 11 +++++------ kernel/jump_label.c | 6 ------ 2 files changed, 5 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index 2172da2d9bb4..c513a40510f5 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h @@ -127,7 +127,6 @@ extern int jump_label_text_reserved(void *start, void *end); extern void static_key_slow_inc(struct static_key *key); extern void static_key_slow_dec(struct static_key *key); extern void static_key_slow_dec_deferred(struct static_key_deferred *key); -extern bool static_key_enabled(struct static_key *key); extern void jump_label_apply_nops(struct module *mod); extern void jump_label_rate_limit(struct static_key_deferred *key, unsigned long rl); @@ -198,11 +197,6 @@ static inline int jump_label_text_reserved(void *start, void *end) static inline void jump_label_lock(void) {} static inline void jump_label_unlock(void) {} -static inline bool static_key_enabled(struct static_key *key) -{ - return (atomic_read(&key->enabled) > 0); -} - static inline int jump_label_apply_nops(struct module *mod) { return 0; @@ -224,4 +218,9 @@ jump_label_rate_limit(struct static_key_deferred *key, #define STATIC_KEY_INIT STATIC_KEY_INIT_FALSE #define jump_label_enabled static_key_enabled +static inline bool static_key_enabled(struct static_key *key) +{ + return (atomic_read(&key->enabled) > 0); +} + #endif /* _LINUX_JUMP_LABEL_H */ diff --git a/kernel/jump_label.c b/kernel/jump_label.c index bf9dcadbb53a..43049192b5ec 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c @@ -29,12 +29,6 @@ void jump_label_unlock(void) mutex_unlock(&jump_label_mutex); } -bool static_key_enabled(struct static_key *key) -{ - return (atomic_read(&key->enabled) > 0); -} -EXPORT_SYMBOL_GPL(static_key_enabled); - static int jump_label_cmp(const void *a, const void *b) { const struct jump_entry *jea = a; -- cgit From 7ac4704c099125214a4f0a4bd54ce94d15be2ffb Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Sat, 25 Feb 2012 18:28:09 +0100 Subject: usb/storage: a couple defines from drivers/usb/storage/transport.h to include/linux/usb/storage.h This moves the BOT data structures for CBW and CSW from drivers internal header file to global include able file in include/. The storage gadget is using the same name for CSW but a different for CBW so I fix it up properly. The same goes for the ub driver and keucr driver in staging. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/block/ub.c | 37 ----------------------------------- drivers/staging/keucr/transport.h | 37 ----------------------------------- drivers/usb/gadget/f_mass_storage.c | 2 +- drivers/usb/gadget/file_storage.c | 2 +- drivers/usb/gadget/storage_common.c | 19 ------------------ drivers/usb/storage/transport.h | 39 ------------------------------------- include/linux/usb/storage.h | 39 +++++++++++++++++++++++++++++++++++++ 7 files changed, 41 insertions(+), 134 deletions(-) (limited to 'include') diff --git a/drivers/block/ub.c b/drivers/block/ub.c index 7333b9e44411..298ac75ac8e5 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -117,43 +117,6 @@ #define UB_SENSE_SIZE 18 -/* - */ - -/* command block wrapper */ -struct bulk_cb_wrap { - __le32 Signature; /* contains 'USBC' */ - u32 Tag; /* unique per command id */ - __le32 DataTransferLength; /* size of data */ - u8 Flags; /* direction in bit 0 */ - u8 Lun; /* LUN */ - u8 Length; /* of of the CDB */ - u8 CDB[UB_MAX_CDB_SIZE]; /* max command */ -}; - -#define US_BULK_CB_WRAP_LEN 31 -#define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */ -#define US_BULK_FLAG_IN 1 -#define US_BULK_FLAG_OUT 0 - -/* command status wrapper */ -struct bulk_cs_wrap { - __le32 Signature; /* should = 'USBS' */ - u32 Tag; /* same as original command */ - __le32 Residue; /* amount not transferred */ - u8 Status; /* see below */ -}; - -#define US_BULK_CS_WRAP_LEN 13 -#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */ -#define US_BULK_STAT_OK 0 -#define US_BULK_STAT_FAIL 1 -#define US_BULK_STAT_PHASE 2 - -/* bulk-only class specific requests */ -#define US_BULK_RESET_REQUEST 0xff -#define US_BULK_GET_MAX_LUN 0xfe - /* */ struct ub_dev; diff --git a/drivers/staging/keucr/transport.h b/drivers/staging/keucr/transport.h index 4ae57d0145b2..2a11a98375d7 100644 --- a/drivers/staging/keucr/transport.h +++ b/drivers/staging/keucr/transport.h @@ -3,43 +3,6 @@ #include -/* Bulk only data structures */ - -/* command block wrapper */ -struct bulk_cb_wrap { - __le32 Signature; /* contains 'USBC' */ - __u32 Tag; /* unique per command id */ - __le32 DataTransferLength; /* size of data */ - __u8 Flags; /* direction in bit 0 */ - __u8 Lun; /* LUN normally 0 */ - __u8 Length; /* of of the CDB */ - __u8 CDB[16]; /* max command */ -}; - -#define US_BULK_CB_WRAP_LEN 31 -#define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */ -#define US_BULK_FLAG_IN 1 -#define US_BULK_FLAG_OUT 0 - -/* command status wrapper */ -struct bulk_cs_wrap { - __le32 Signature; /* should = 'USBS' */ - __u32 Tag; /* same as original command */ - __le32 Residue; /* amount not transferred */ - __u8 Status; /* see below */ - __u8 Filler[18]; -}; - -#define US_BULK_CS_WRAP_LEN 13 -#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */ -#define US_BULK_STAT_OK 0 -#define US_BULK_STAT_FAIL 1 -#define US_BULK_STAT_PHASE 2 - -/* bulk-only class specific requests */ -#define US_BULK_RESET_REQUEST 0xff -#define US_BULK_GET_MAX_LUN 0xfe - /* usb_stor_bulk_transfer_xxx() return codes, in order of severity */ #define USB_STOR_XFER_GOOD 0 /* good transfer */ #define USB_STOR_XFER_SHORT 1 /* transferred less than expected */ diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index ee8ceec01560..9e71fe7fc502 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -2221,7 +2221,7 @@ unknown_cmnd: static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) { struct usb_request *req = bh->outreq; - struct fsg_bulk_cb_wrap *cbw = req->buf; + struct bulk_cb_wrap *cbw = req->buf; struct fsg_common *common = fsg->common; /* Was this a real packet? Should it be ignored? */ diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 47766f0e7caa..15e6e8e55ef6 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -2609,7 +2609,7 @@ static int do_scsi_command(struct fsg_dev *fsg) static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) { struct usb_request *req = bh->outreq; - struct fsg_bulk_cb_wrap *cbw = req->buf; + struct bulk_cb_wrap *cbw = req->buf; /* Was this a real packet? Should it be ignored? */ if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 85ea14e2545e..4095696b1582 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -153,29 +153,10 @@ /* Bulk-only data structures */ -/* Command Block Wrapper */ -struct fsg_bulk_cb_wrap { - __le32 Signature; /* Contains 'USBC' */ - u32 Tag; /* Unique per command id */ - __le32 DataTransferLength; /* Size of the data */ - u8 Flags; /* Direction in bit 7 */ - u8 Lun; /* LUN (normally 0) */ - u8 Length; /* Of the CDB, <= MAX_COMMAND_SIZE */ - u8 CDB[16]; /* Command Data Block */ -}; - #define USB_BULK_CB_WRAP_LEN 31 #define USB_BULK_CB_SIG 0x43425355 /* Spells out USBC */ #define USB_BULK_IN_FLAG 0x80 -/* Command Status Wrapper */ -struct bulk_cs_wrap { - __le32 Signature; /* Should = 'USBS' */ - u32 Tag; /* Same as original command */ - __le32 Residue; /* Amount not transferred */ - u8 Status; /* See below */ -}; - #define USB_BULK_CS_WRAP_LEN 13 #define USB_BULK_CS_SIG 0x53425355 /* Spells out 'USBS' */ #define USB_STATUS_PASS 0 diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index 242ff5e791a5..9369d752d419 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -41,45 +41,6 @@ #include -/* - * Bulk only data structures - */ - -/* command block wrapper */ -struct bulk_cb_wrap { - __le32 Signature; /* contains 'USBC' */ - __u32 Tag; /* unique per command id */ - __le32 DataTransferLength; /* size of data */ - __u8 Flags; /* direction in bit 0 */ - __u8 Lun; /* LUN normally 0 */ - __u8 Length; /* of of the CDB */ - __u8 CDB[16]; /* max command */ -}; - -#define US_BULK_CB_WRAP_LEN 31 -#define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */ -#define US_BULK_FLAG_IN 1 -#define US_BULK_FLAG_OUT 0 - -/* command status wrapper */ -struct bulk_cs_wrap { - __le32 Signature; /* should = 'USBS' */ - __u32 Tag; /* same as original command */ - __le32 Residue; /* amount not transferred */ - __u8 Status; /* see below */ - __u8 Filler[18]; -}; - -#define US_BULK_CS_WRAP_LEN 13 -#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */ -#define US_BULK_STAT_OK 0 -#define US_BULK_STAT_FAIL 1 -#define US_BULK_STAT_PHASE 2 - -/* bulk-only class specific requests */ -#define US_BULK_RESET_REQUEST 0xff -#define US_BULK_GET_MAX_LUN 0xfe - /* * usb_stor_bulk_transfer_xxx() return codes, in order of severity */ diff --git a/include/linux/usb/storage.h b/include/linux/usb/storage.h index d7fc910f1dc4..87a94bf34d47 100644 --- a/include/linux/usb/storage.h +++ b/include/linux/usb/storage.h @@ -45,4 +45,43 @@ #define USB_PR_DEVICE 0xff /* Use device's value */ + /* + * Bulk only data structures + */ + +/* command block wrapper */ +struct bulk_cb_wrap { + __le32 Signature; /* contains 'USBC' */ + __u32 Tag; /* unique per command id */ + __le32 DataTransferLength; /* size of data */ + __u8 Flags; /* direction in bit 0 */ + __u8 Lun; /* LUN normally 0 */ + __u8 Length; /* of of the CDB */ + __u8 CDB[16]; /* max command */ +}; + +#define US_BULK_CB_WRAP_LEN 31 +#define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */ +#define US_BULK_FLAG_IN 1 +#define US_BULK_FLAG_OUT 0 + +/* command status wrapper */ +struct bulk_cs_wrap { + __le32 Signature; /* should = 'USBS' */ + __u32 Tag; /* same as original command */ + __le32 Residue; /* amount not transferred */ + __u8 Status; /* see below */ + __u8 Filler[18]; +}; + +#define US_BULK_CS_WRAP_LEN 13 +#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */ +#define US_BULK_STAT_OK 0 +#define US_BULK_STAT_FAIL 1 +#define US_BULK_STAT_PHASE 2 + +/* bulk-only class specific requests */ +#define US_BULK_RESET_REQUEST 0xff +#define US_BULK_GET_MAX_LUN 0xfe + #endif -- cgit From b8db6d6402ddca1c78a27407fbd10a6ccb23ab14 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Sat, 25 Feb 2012 18:28:10 +0100 Subject: usb/storage: redefine US_BULK_FLAG_IN and use it US_BULK_FLAG_IN is defined as 1 and not used. The USB storage spec says that bit 7 of flags within CBW defines the data direction. 1 is DATA-IN (read from device) and 0 is the DATA-OUT. Bit 6 is obselete and bits 0-5 are reserved. This patch redefines the unsued define US_BULK_FLAG_IN from 1 to 1 << 7 aka 0x80 and replaces the obvious users. In a following patch the storage gadget will use it as well. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/ene_ub6250.c | 26 +++++++++++++------------- drivers/usb/storage/realtek_cr.c | 4 ++-- drivers/usb/storage/transport.c | 3 ++- include/linux/usb/storage.h | 2 +- 4 files changed, 18 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index 30532d93eecc..e7e678109500 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -674,7 +674,7 @@ static int sd_scsi_read(struct us_data *us, struct scsi_cmnd *srb) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = blenByte; - bcb->Flags = 0x80; + bcb->Flags = US_BULK_FLAG_IN; bcb->CDB[0] = 0xF1; bcb->CDB[5] = (unsigned char)(bnByte); bcb->CDB[4] = (unsigned char)(bnByte>>8); @@ -858,7 +858,7 @@ static int ms_read_readpage(struct us_data *us, u32 PhyBlockAddr, memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = 0x200; - bcb->Flags = 0x80; + bcb->Flags = US_BULK_FLAG_IN; bcb->CDB[0] = 0xF1; bcb->CDB[1] = 0x02; /* in init.c ENE_MSInit() is 0x01 */ @@ -877,7 +877,7 @@ static int ms_read_readpage(struct us_data *us, u32 PhyBlockAddr, memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = 0x4; - bcb->Flags = 0x80; + bcb->Flags = US_BULK_FLAG_IN; bcb->CDB[0] = 0xF1; bcb->CDB[1] = 0x03; @@ -1170,7 +1170,7 @@ static int ms_read_eraseblock(struct us_data *us, u32 PhyBlockAddr) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = 0x200; - bcb->Flags = 0x80; + bcb->Flags = US_BULK_FLAG_IN; bcb->CDB[0] = 0xF2; bcb->CDB[1] = 0x06; bcb->CDB[4] = (unsigned char)(bn); @@ -1249,7 +1249,7 @@ static int ms_lib_overwrite_extra(struct us_data *us, u32 PhyBlockAddr, memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = 0x4; - bcb->Flags = 0x80; + bcb->Flags = US_BULK_FLAG_IN; bcb->CDB[0] = 0xF2; bcb->CDB[1] = 0x05; bcb->CDB[5] = (unsigned char)(PageNum); @@ -1331,7 +1331,7 @@ static int ms_lib_read_extra(struct us_data *us, u32 PhyBlock, memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = 0x4; - bcb->Flags = 0x80; + bcb->Flags = US_BULK_FLAG_IN; bcb->CDB[0] = 0xF1; bcb->CDB[1] = 0x03; bcb->CDB[5] = (unsigned char)(PageNum); @@ -1533,7 +1533,7 @@ static int ms_lib_read_extrablock(struct us_data *us, u32 PhyBlock, memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = 0x4 * blen; - bcb->Flags = 0x80; + bcb->Flags = US_BULK_FLAG_IN; bcb->CDB[0] = 0xF1; bcb->CDB[1] = 0x03; bcb->CDB[5] = (unsigned char)(PageNum); @@ -1650,7 +1650,7 @@ static int ms_scsi_read(struct us_data *us, struct scsi_cmnd *srb) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = blenByte; - bcb->Flags = 0x80; + bcb->Flags = US_BULK_FLAG_IN; bcb->CDB[0] = 0xF1; bcb->CDB[1] = 0x02; bcb->CDB[5] = (unsigned char)(bn); @@ -1694,7 +1694,7 @@ static int ms_scsi_read(struct us_data *us, struct scsi_cmnd *srb) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = 0x200 * len; - bcb->Flags = 0x80; + bcb->Flags = US_BULK_FLAG_IN; bcb->CDB[0] = 0xF1; bcb->CDB[1] = 0x02; bcb->CDB[5] = (unsigned char)(blkno); @@ -1827,7 +1827,7 @@ static int ene_get_card_type(struct us_data *us, u16 index, void *buf) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = 0x01; - bcb->Flags = 0x80; + bcb->Flags = US_BULK_FLAG_IN; bcb->CDB[0] = 0xED; bcb->CDB[2] = (unsigned char)(index>>8); bcb->CDB[3] = (unsigned char)index; @@ -2083,7 +2083,7 @@ static int ene_ms_init(struct us_data *us) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = 0x200; - bcb->Flags = 0x80; + bcb->Flags = US_BULK_FLAG_IN; bcb->CDB[0] = 0xF1; bcb->CDB[1] = 0x01; @@ -2134,7 +2134,7 @@ static int ene_sd_init(struct us_data *us) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); - bcb->Flags = 0x80; + bcb->Flags = US_BULK_FLAG_IN; bcb->CDB[0] = 0xF2; result = ene_send_scsi_cmd(us, FDIR_READ, NULL, 0); @@ -2153,7 +2153,7 @@ static int ene_sd_init(struct us_data *us) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = 0x200; - bcb->Flags = 0x80; + bcb->Flags = US_BULK_FLAG_IN; bcb->CDB[0] = 0xF1; result = ene_send_scsi_cmd(us, FDIR_READ, &buf, 0); diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 84a4bc0cbee4..63cf2822e299 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -219,7 +219,7 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun, /* set up the command wrapper */ bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = cpu_to_le32(buf_len); - bcb->Flags = (dir == DMA_FROM_DEVICE) ? 1 << 7 : 0; + bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : 0; bcb->Tag = ++us->tag; bcb->Lun = lun; bcb->Length = cmd_len; @@ -305,7 +305,7 @@ static int rts51x_bulk_transport_special(struct us_data *us, u8 lun, /* set up the command wrapper */ bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = cpu_to_le32(buf_len); - bcb->Flags = (dir == DMA_FROM_DEVICE) ? 1 << 7 : 0; + bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : 0; bcb->Tag = ++us->tag; bcb->Lun = lun; bcb->Length = cmd_len; diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 0e5c91c6187f..c70109e5d60b 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1071,7 +1071,8 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) /* set up the command wrapper */ bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = cpu_to_le32(transfer_length); - bcb->Flags = srb->sc_data_direction == DMA_FROM_DEVICE ? 1 << 7 : 0; + bcb->Flags = srb->sc_data_direction == DMA_FROM_DEVICE ? + US_BULK_FLAG_IN : 0; bcb->Tag = ++us->tag; bcb->Lun = srb->device->lun; if (us->fflags & US_FL_SCM_MULT_TARG) diff --git a/include/linux/usb/storage.h b/include/linux/usb/storage.h index 87a94bf34d47..4de58b15fad6 100644 --- a/include/linux/usb/storage.h +++ b/include/linux/usb/storage.h @@ -62,7 +62,7 @@ struct bulk_cb_wrap { #define US_BULK_CB_WRAP_LEN 31 #define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */ -#define US_BULK_FLAG_IN 1 +#define US_BULK_FLAG_IN (1 << 7) #define US_BULK_FLAG_OUT 0 /* command status wrapper */ -- cgit From 03892d5fefbe7d4df68466bd4cfda86fac84ebd8 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Sat, 25 Feb 2012 18:28:12 +0100 Subject: usb/storage: remove Filler member from struct bulk_cs_wrap As Alan Stern pointed out this member has nothing to do with the Command Status Wrapper (CSW) as specified by the Universal Serial Bus Mass Storage Class Bulk-Only Transport rev 1.0. It defines the structure without the additional 18 filler bytes and defines the total size of the struct to exactly 13 bytes. Larger responses should be dropped. All in-tree users use a defines instead of sizeof() of this struct as far I can tell. Cc: Alan Stern Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/storage.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/usb/storage.h b/include/linux/usb/storage.h index 4de58b15fad6..cb33fff2ba0b 100644 --- a/include/linux/usb/storage.h +++ b/include/linux/usb/storage.h @@ -71,7 +71,6 @@ struct bulk_cs_wrap { __u32 Tag; /* same as original command */ __le32 Residue; /* amount not transferred */ __u8 Status; /* see below */ - __u8 Filler[18]; }; #define US_BULK_CS_WRAP_LEN 13 -- cgit From d1cddb4a8e9b09c33158acae05c48069d74fa4d0 Mon Sep 17 00:00:00 2001 From: Greg KH Date: Fri, 24 Feb 2012 15:38:14 -0800 Subject: USB: create module_usb_serial_driver macro Now that Alan Stern has cleaned up the usb serial driver registration, we have the ability to create a module_usb_serial_driver macro to make things a bit simpler, like the other *_driver macros created. But, as we need two functions here, we can't reuse the existing module_driver() macro, so we need to roll our own. Here's a patch implementing module_usb_serial_driver() and it converts the pl2303 driver to use it, showing a nice cleanup. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 18 +----------------- include/linux/usb/serial.h | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index cc65d810c8f5..ff4a174fa5de 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -855,23 +855,7 @@ static struct usb_serial_driver * const serial_drivers[] = { &pl2303_device, NULL }; -static int __init pl2303_init(void) -{ - int retval; - - retval = usb_serial_register_drivers(&pl2303_driver, serial_drivers); - if (retval == 0) - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n"); - return retval; -} - -static void __exit pl2303_exit(void) -{ - usb_serial_deregister_drivers(&pl2303_driver, serial_drivers); -} - -module_init(pl2303_init); -module_exit(pl2303_exit); +module_usb_serial_driver(pl2303_driver, serial_drivers); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 34c06a723dcc..7b1db841e2a8 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -406,5 +406,33 @@ do { \ } \ } while (0) +/* + * module_usb_serial_driver() - Helper macro for registering a USB Serial driver + * @__usb_driver: usb_driver struct to register + * @__serial_drivers: list of usb_serial drivers to register + * + * Helper macro for USB serial drivers which do not do anything special + * in module init/exit. This eliminates a lot of boilerplate. Each + * module may only use this macro once, and calling it replaces + * module_init() and module_exit() + * + * Note, we can't use the generic module_driver() call here, due to the + * two parameters in the usb_serial_* functions, so we roll our own here + * :( + */ +#define module_usb_serial_driver(__usb_driver, __serial_drivers) \ +static int __init usb_serial_driver_init(void) \ +{ \ + return usb_serial_register_drivers(&(__usb_driver), \ + (__serial_drivers)); \ +} \ +module_init(usb_serial_driver_init); \ +static void __exit usb_serial_driver_exit(void) \ +{ \ + return usb_serial_deregister_drivers(&(__usb_driver), \ + (__serial_drivers)); \ +} \ +module_exit(usb_serial_driver_exit); + #endif /* __LINUX_USB_SERIAL_H */ -- cgit From 978c93b90fc4768e295b20492b5db76d5e026e5e Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 29 Feb 2012 10:41:41 +0200 Subject: Bluetooth: Save remote L2CAP fixed channel mask Fixed channel mask needs to be stored to decide whether to use A2MP for example. So far save only one relevant byte which keeps all information we need. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index d6d8ec8eb8cf..3732a4849f2e 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -522,6 +522,7 @@ struct l2cap_conn { unsigned int mtu; __u32 feat_mask; + __u8 fixed_chan_mask; __u8 info_state; __u8 info_ident; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index df3be692f2cf..bc8d558b01f6 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3209,7 +3209,8 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm return 0; } - if (type == L2CAP_IT_FEAT_MASK) { + switch (type) { + case L2CAP_IT_FEAT_MASK: conn->feat_mask = get_unaligned_le32(rsp->data); if (conn->feat_mask & L2CAP_FEAT_FIXED_CHAN) { @@ -3226,11 +3227,15 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm l2cap_conn_start(conn); } - } else if (type == L2CAP_IT_FIXED_CHAN) { + break; + + case L2CAP_IT_FIXED_CHAN: + conn->fixed_chan_mask = rsp->data[0]; conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_ident = 0; l2cap_conn_start(conn); + break; } return 0; -- cgit From 63c9c5e77c36f8793dddf0e905a4bc43a0972735 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 24 Feb 2012 13:50:51 +0100 Subject: cfg80211: remove cookies from callbacks In "cfg80211: no cookies in cfg80211_send_XXX()" Holger Schurig removed the cookies in the calls from mac80211 to cfg80211, but the ones in the other direction were left in. Remove them now. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 6 ++---- net/mac80211/cfg.c | 12 ++++-------- net/mac80211/ieee80211_i.h | 6 ++---- net/mac80211/mlme.c | 28 ++++++++++++++-------------- net/wireless/mlme.c | 6 +++--- 5 files changed, 25 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 755a7707a7c5..0178c7489373 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1587,11 +1587,9 @@ struct cfg80211_ops { int (*assoc)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_assoc_request *req); int (*deauth)(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_deauth_request *req, - void *cookie); + struct cfg80211_deauth_request *req); int (*disassoc)(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_disassoc_request *req, - void *cookie); + struct cfg80211_disassoc_request *req); int (*connect)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index f7eb25aabf8f..6a77d4c910f9 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1595,19 +1595,15 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_deauth_request *req, - void *cookie) + struct cfg80211_deauth_request *req) { - return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), - req, cookie); + return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), req); } static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_disassoc_request *req, - void *cookie) + struct cfg80211_disassoc_request *req) { - return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), - req, cookie); + return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), req); } static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 4d1682950a60..cee0c7493fd0 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1150,11 +1150,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct cfg80211_assoc_request *req); int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, - struct cfg80211_deauth_request *req, - void *cookie); + struct cfg80211_deauth_request *req); int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, - struct cfg80211_disassoc_request *req, - void *cookie); + struct cfg80211_disassoc_request *req); void ieee80211_send_pspoll(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata); void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 0c220e1b6c9c..edba1d8158fc 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -612,8 +612,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) } static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, - const u8 *bssid, u16 stype, u16 reason, - void *cookie, bool send_frame) + const u8 *bssid, u16 stype, + u16 reason, bool cfg80211_locked, + bool send_frame) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -637,12 +638,12 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, mgmt->u.deauth.reason_code = cpu_to_le16(reason); if (stype == IEEE80211_STYPE_DEAUTH) - if (cookie) + if (cfg80211_locked) __cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); else cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); else - if (cookie) + if (cfg80211_locked) __cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); else cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); @@ -1696,7 +1697,7 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) ieee80211_send_deauth_disassoc(sdata, bssid, IEEE80211_STYPE_DEAUTH, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, - NULL, true); + false, true); mutex_lock(&local->mtx); ieee80211_recalc_idle(local); @@ -2706,8 +2707,8 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, * but that's not a problem. */ ieee80211_send_deauth_disassoc(sdata, bssid, - IEEE80211_STYPE_DEAUTH, reason, - NULL, true); + IEEE80211_STYPE_DEAUTH, + reason, false, true); mutex_lock(&local->mtx); ieee80211_recalc_idle(local); @@ -3439,8 +3440,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, } int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, - struct cfg80211_deauth_request *req, - void *cookie) + struct cfg80211_deauth_request *req) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; bool assoc_bss = false; @@ -3461,8 +3461,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n", sdata->name, req->bssid, req->reason_code); - ieee80211_send_deauth_disassoc(sdata, req->bssid, IEEE80211_STYPE_DEAUTH, - req->reason_code, cookie, true); + ieee80211_send_deauth_disassoc(sdata, req->bssid, + IEEE80211_STYPE_DEAUTH, + req->reason_code, true, true); if (assoc_bss) sta_info_flush(sdata->local, sdata); @@ -3474,8 +3475,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, } int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, - struct cfg80211_disassoc_request *req, - void *cookie) + struct cfg80211_disassoc_request *req) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 bssid[ETH_ALEN]; @@ -3503,7 +3503,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_send_deauth_disassoc(sdata, req->bss->bssid, IEEE80211_STYPE_DISASSOC, req->reason_code, - cookie, !req->local_state_change); + true, !req->local_state_change); sta_info_flush(sdata->local, sdata); mutex_lock(&sdata->local->mtx); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index d553d365e751..fb1e72179117 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -455,7 +455,7 @@ int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, return 0; } - return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); + return rdev->ops->deauth(&rdev->wiphy, dev, &req); } int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, @@ -500,7 +500,7 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, else return -ENOTCONN; - return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev); + return rdev->ops->disassoc(&rdev->wiphy, dev, &req); } int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, @@ -541,7 +541,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN); req.bssid = bssid; - rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); + rdev->ops->deauth(&rdev->wiphy, dev, &req); if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); -- cgit From 02f2f1a951f87644166926862ec32fb13511e2f3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 27 Feb 2012 12:18:30 +0100 Subject: mac80211: handle non-bufferable MMPDUs correctly This renames the IEEE80211_TX_CTL_POLL_RESPONSE TX flag to IEEE80211_TX_CTL_NO_PS_BUFFER and also uses it for non-bufferable MMPDUs (all MMPDUs but deauth, disassoc and action frames.) Previously, mac80211 would let the MMPDU through but not set the flag so drivers supporting some hardware aids for avoiding the PS races would then reject the frame. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/4965-mac.c | 2 +- drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 6 +++++- drivers/net/wireless/p54/txrx.c | 2 +- include/net/mac80211.h | 17 ++++++++++------- net/mac80211/sta_info.c | 4 ++-- net/mac80211/tx.c | 17 +++++++++++------ 6 files changed, 30 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 2d01db0f08e0..2329033e23d7 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -1693,7 +1693,7 @@ il4965_tx_skb(struct il_priv *il, struct sk_buff *skb) sta_priv = (void *)sta->drv_priv; if (sta_priv && sta_priv->asleep && - (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE)) { + (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { /* * This sends an asynchronous command to the device, * but we can rely on it being processed before the diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index da18a8fc9af7..5f78567f4180 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -322,7 +322,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) sta_priv = (void *)info->control.sta->drv_priv; if (sta_priv && sta_priv->asleep && - (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE)) { + (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { /* * This sends an asynchronous command to the device, * but we can rely on it being processed before the @@ -331,6 +331,10 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) * counter. * For now set the counter to just 1 since we do not * support uAPSD yet. + * + * FIXME: If we get two non-bufferable frames one + * after the other, we might only send out one of + * them because this is racy. */ iwl_sta_modify_sleep_tx_count(priv, sta_id, 1); } diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 42b97bc38477..a08a6f0e4dd1 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -690,7 +690,7 @@ static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb, if (!(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)) *flags |= P54_HDR_FLAG_DATA_OUT_SEQNR; - if (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE) + if (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER) *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL; if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index cbff4f94a200..7477f020ee7a 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -341,9 +341,9 @@ struct ieee80211_bss_conf { * used to indicate that a frame was already retried due to PS * @IEEE80211_TX_INTFL_DONT_ENCRYPT: completely internal to mac80211, * used to indicate frame should not be encrypted - * @IEEE80211_TX_CTL_POLL_RESPONSE: This frame is a response to a poll - * frame (PS-Poll or uAPSD) and should be sent although the station - * is in powersave mode. + * @IEEE80211_TX_CTL_NO_PS_BUFFER: This frame is a response to a poll + * frame (PS-Poll or uAPSD) or a non-bufferable MMPDU and must + * be sent although the station is in powersave mode. * @IEEE80211_TX_CTL_MORE_FRAMES: More frames will be passed to the * transmit function after the current frame, this can be used * by drivers to kick the DMA queue only if unset or when the @@ -399,7 +399,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14), IEEE80211_TX_INTFL_RETRIED = BIT(15), IEEE80211_TX_INTFL_DONT_ENCRYPT = BIT(16), - IEEE80211_TX_CTL_POLL_RESPONSE = BIT(17), + IEEE80211_TX_CTL_NO_PS_BUFFER = BIT(17), IEEE80211_TX_CTL_MORE_FRAMES = BIT(18), IEEE80211_TX_INTFL_RETRANSMISSION = BIT(19), /* hole at 20, use later */ @@ -425,7 +425,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_SEND_AFTER_DTIM | IEEE80211_TX_CTL_AMPDU | \ IEEE80211_TX_STAT_TX_FILTERED | IEEE80211_TX_STAT_ACK | \ IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_STAT_AMPDU_NO_BACK | \ - IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_POLL_RESPONSE | \ + IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_NO_PS_BUFFER | \ IEEE80211_TX_CTL_MORE_FRAMES | IEEE80211_TX_CTL_LDPC | \ IEEE80211_TX_CTL_STBC | IEEE80211_TX_STATUS_EOSP) @@ -1634,7 +1634,7 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); * the station sends a PS-Poll or a uAPSD trigger frame, mac80211 * will inform the driver of this with the @allow_buffered_frames * callback; this callback is optional. mac80211 will then transmit - * the frames as usual and set the %IEEE80211_TX_CTL_POLL_RESPONSE + * the frames as usual and set the %IEEE80211_TX_CTL_NO_PS_BUFFER * on each frame. The last frame in the service period (or the only * response to a PS-Poll) also has %IEEE80211_TX_STATUS_EOSP set to * indicate that it ends the service period; as this frame must have @@ -1642,6 +1642,9 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); * When TX status is reported for this frame, the service period is * marked has having ended and a new one can be started by the peer. * + * Additionally, non-bufferable MMPDUs can also be transmitted by + * mac80211 with the %IEEE80211_TX_CTL_NO_PS_BUFFER set in them. + * * Another race condition can happen on some devices like iwlwifi * when there are frames queued for the station and it wakes up * or polls; the frames that are already queued could end up being @@ -2140,7 +2143,7 @@ enum ieee80211_frame_release_type { * @allow_buffered_frames: Prepare device to allow the given number of frames * to go out to the given station. The frames will be sent by mac80211 * via the usual TX path after this call. The TX information for frames - * released will also have the %IEEE80211_TX_CTL_POLL_RESPONSE flag set + * released will also have the %IEEE80211_TX_CTL_NO_PS_BUFFER flag set * and the last one will also have %IEEE80211_TX_STATUS_EOSP set. In case * frames from multiple TIDs are released and the driver might reorder * them between the TIDs, it must set the %IEEE80211_TX_STATUS_EOSP flag diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 98613c8f08cf..cd0f265f42e5 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1050,7 +1050,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, * exchange. Also set EOSP to indicate this packet * ends the poll/service period. */ - info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE | + info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER | IEEE80211_TX_STATUS_EOSP | IEEE80211_TX_CTL_REQ_TX_STATUS; @@ -1177,7 +1177,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, * STA may still remain is PS mode after this frame * exchange. */ - info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE; + info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER; /* * Use MoreData flag to indicate whether there are diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7c021f255716..570737df2d22 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -448,18 +448,23 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; struct ieee80211_local *local = tx->local; - if (unlikely(!sta || - ieee80211_is_probe_resp(hdr->frame_control) || - ieee80211_is_auth(hdr->frame_control) || - ieee80211_is_assoc_resp(hdr->frame_control) || - ieee80211_is_reassoc_resp(hdr->frame_control))) + if (unlikely(!sta)) return TX_CONTINUE; if (unlikely((test_sta_flag(sta, WLAN_STA_PS_STA) || test_sta_flag(sta, WLAN_STA_PS_DRIVER)) && - !(info->flags & IEEE80211_TX_CTL_POLL_RESPONSE))) { + !(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER))) { int ac = skb_get_queue_mapping(tx->skb); + /* only deauth, disassoc and action are bufferable MMPDUs */ + if (ieee80211_is_mgmt(hdr->frame_control) && + !ieee80211_is_deauth(hdr->frame_control) && + !ieee80211_is_disassoc(hdr->frame_control) && + !ieee80211_is_action(hdr->frame_control)) { + info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER; + return TX_CONTINUE; + } + #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "STA %pM aid %d: PS buffer for AC %d\n", sta->sta.addr, sta->sta.aid, ac); -- cgit From c5491ea779793f977d282754db478157cc409d82 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 21 Mar 2011 12:09:35 +0100 Subject: sched/rt: Add schedule_preempt_disabled() Add helper to get rid of the ever repeating: preempt_enable_no_resched(); schedule(); preempt_disable(); patterns. Signed-off-by: Thomas Gleixner Acked-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-wxx7btox7coby6ifv5vzhzgp@git.kernel.org Signed-off-by: Ingo Molnar --- include/linux/sched.h | 1 + kernel/sched/core.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 1a6398424fab..03dd224d0667 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -361,6 +361,7 @@ extern signed long schedule_timeout_interruptible(signed long timeout); extern signed long schedule_timeout_killable(signed long timeout); extern signed long schedule_timeout_uninterruptible(signed long timeout); asmlinkage void schedule(void); +extern void schedule_preempt_disabled(void); extern int mutex_spin_on_owner(struct mutex *lock, struct task_struct *owner); struct nsproxy; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 91fe6a0e9098..73022395c00e 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -3246,6 +3246,18 @@ asmlinkage void __sched schedule(void) } EXPORT_SYMBOL(schedule); +/** + * schedule_preempt_disabled - called with preemption disabled + * + * Returns with preemption disabled. Note: preempt_count must be 1 + */ +void __sched schedule_preempt_disabled(void) +{ + preempt_enable_no_resched(); + schedule(); + preempt_disable(); +} + #ifdef CONFIG_MUTEX_SPIN_ON_OWNER static inline bool owner_running(struct mutex *lock, struct task_struct *owner) -- cgit From ba74c1448f127649046615ec017bded7b2a76f29 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 21 Mar 2011 13:32:17 +0100 Subject: sched/rt: Document scheduler related skip-resched-check sites Create a distinction between scheduler related preempt_enable_no_resched() calls and the nearly one hundred other places in the kernel that do not want to reschedule, for one reason or another. This distinction matters for -rt, where the scheduler and the non-scheduler preempt models (and checks) are different. For upstream it's purely documentational. Signed-off-by: Thomas Gleixner Link: http://lkml.kernel.org/n/tip-gs88fvx2mdv5psnzxnv575ke@git.kernel.org Signed-off-by: Ingo Molnar --- arch/powerpc/kernel/idle.c | 2 +- arch/sparc/kernel/process_64.c | 2 +- include/linux/preempt.h | 5 ++++- kernel/sched/core.c | 6 +++--- kernel/softirq.c | 4 ++-- 5 files changed, 11 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/arch/powerpc/kernel/idle.c b/arch/powerpc/kernel/idle.c index 65035141552b..c97fc60c790c 100644 --- a/arch/powerpc/kernel/idle.c +++ b/arch/powerpc/kernel/idle.c @@ -102,7 +102,7 @@ void cpu_idle(void) rcu_idle_exit(); tick_nohz_idle_exit(); if (cpu_should_die()) { - preempt_enable_no_resched(); + sched_preempt_enable_no_resched(); cpu_die(); } schedule_preempt_disabled(); diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c index ab9a29268213..06b5b5fc20c7 100644 --- a/arch/sparc/kernel/process_64.c +++ b/arch/sparc/kernel/process_64.c @@ -106,7 +106,7 @@ void cpu_idle(void) #ifdef CONFIG_HOTPLUG_CPU if (cpu_is_offline(cpu)) { - preempt_enable_no_resched(); + sched_preempt_enable_no_resched(); cpu_play_dead(); } #endif diff --git a/include/linux/preempt.h b/include/linux/preempt.h index 58969b2a8a82..5a710b9c578e 100644 --- a/include/linux/preempt.h +++ b/include/linux/preempt.h @@ -48,12 +48,14 @@ do { \ barrier(); \ } while (0) -#define preempt_enable_no_resched() \ +#define sched_preempt_enable_no_resched() \ do { \ barrier(); \ dec_preempt_count(); \ } while (0) +#define preempt_enable_no_resched() sched_preempt_enable_no_resched() + #define preempt_enable() \ do { \ preempt_enable_no_resched(); \ @@ -92,6 +94,7 @@ do { \ #else /* !CONFIG_PREEMPT_COUNT */ #define preempt_disable() do { } while (0) +#define sched_preempt_enable_no_resched() do { } while (0) #define preempt_enable_no_resched() do { } while (0) #define preempt_enable() do { } while (0) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 73022395c00e..643cc37fcb23 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -3220,7 +3220,7 @@ need_resched: post_schedule(rq); - preempt_enable_no_resched(); + sched_preempt_enable_no_resched(); if (need_resched()) goto need_resched; } @@ -3253,7 +3253,7 @@ EXPORT_SYMBOL(schedule); */ void __sched schedule_preempt_disabled(void) { - preempt_enable_no_resched(); + sched_preempt_enable_no_resched(); schedule(); preempt_disable(); } @@ -4486,7 +4486,7 @@ SYSCALL_DEFINE0(sched_yield) __release(rq->lock); spin_release(&rq->lock.dep_map, 1, _THIS_IP_); do_raw_spin_unlock(&rq->lock); - preempt_enable_no_resched(); + sched_preempt_enable_no_resched(); schedule(); diff --git a/kernel/softirq.c b/kernel/softirq.c index 79b524767a24..f268369ebe1f 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -353,7 +353,7 @@ void irq_exit(void) tick_nohz_irq_exit(); #endif rcu_irq_exit(); - preempt_enable_no_resched(); + sched_preempt_enable_no_resched(); } /* @@ -759,7 +759,7 @@ static int run_ksoftirqd(void * __bind_cpu) if (local_softirq_pending()) __do_softirq(); local_irq_enable(); - preempt_enable_no_resched(); + sched_preempt_enable_no_resched(); cond_resched(); preempt_disable(); rcu_note_context_switch((long)__bind_cpu); -- cgit From 63b2001169e75cd71e917ec953fdab572e3f944a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 1 Dec 2011 00:04:00 +0100 Subject: sched/wait: Add __wake_up_all_locked() API For code which protects the waitqueue itself with another lock it makes no sense to acquire the waitqueue lock for wakeup all. Provide __wake_up_all_locked(). This is an optimization on the vanilla kernel (to be used by the PCI code) and an important semantic distinction on -rt. Signed-off-by: Thomas Gleixner Acked-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-ux6m4b8jonb9inx8xafh77ds@git.kernel.org Signed-off-by: Ingo Molnar --- include/linux/wait.h | 5 +++-- kernel/sched/core.c | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/wait.h b/include/linux/wait.h index a9ce45e8501c..7d9a9e990ce6 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -157,7 +157,7 @@ void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr, void *key); void __wake_up_locked_key(wait_queue_head_t *q, unsigned int mode, void *key); void __wake_up_sync_key(wait_queue_head_t *q, unsigned int mode, int nr, void *key); -void __wake_up_locked(wait_queue_head_t *q, unsigned int mode); +void __wake_up_locked(wait_queue_head_t *q, unsigned int mode, int nr); void __wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr); void __wake_up_bit(wait_queue_head_t *, void *, int); int __wait_on_bit(wait_queue_head_t *, struct wait_bit_queue *, int (*)(void *), unsigned); @@ -170,7 +170,8 @@ wait_queue_head_t *bit_waitqueue(void *, int); #define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL) #define wake_up_nr(x, nr) __wake_up(x, TASK_NORMAL, nr, NULL) #define wake_up_all(x) __wake_up(x, TASK_NORMAL, 0, NULL) -#define wake_up_locked(x) __wake_up_locked((x), TASK_NORMAL) +#define wake_up_locked(x) __wake_up_locked((x), TASK_NORMAL, 1) +#define wake_up_all_locked(x) __wake_up_locked((x), TASK_NORMAL, 0) #define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL) #define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 643cc37fcb23..820f7453fda9 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -3418,9 +3418,9 @@ EXPORT_SYMBOL(__wake_up); /* * Same as __wake_up but called with the spinlock in wait_queue_head_t held. */ -void __wake_up_locked(wait_queue_head_t *q, unsigned int mode) +void __wake_up_locked(wait_queue_head_t *q, unsigned int mode, int nr) { - __wake_up_common(q, mode, 1, 0, NULL); + __wake_up_common(q, mode, nr, 0, NULL); } EXPORT_SYMBOL_GPL(__wake_up_locked); -- cgit From 3c7d51843b03a6839e9ec7cda724e54d2319a63a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 17 Jul 2011 20:46:52 +0200 Subject: sched/rt: Do not submit new work when PI-blocked When we are PI-blocked then we want to get things done ASAP. Signed-off-by: Thomas Gleixner Acked-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-vw8et3445km5b8mpihf4trae@git.kernel.org Signed-off-by: Ingo Molnar --- include/linux/sched.h | 8 ++++++++ kernel/sched/core.c | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 03dd224d0667..c628a9151437 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2082,12 +2082,20 @@ extern unsigned int sysctl_sched_cfs_bandwidth_slice; extern int rt_mutex_getprio(struct task_struct *p); extern void rt_mutex_setprio(struct task_struct *p, int prio); extern void rt_mutex_adjust_pi(struct task_struct *p); +static inline bool tsk_is_pi_blocked(struct task_struct *tsk) +{ + return tsk->pi_blocked_on != NULL; +} #else static inline int rt_mutex_getprio(struct task_struct *p) { return p->normal_prio; } # define rt_mutex_adjust_pi(p) do { } while (0) +static inline bool tsk_is_pi_blocked(struct task_struct *tsk) +{ + return false; +} #endif extern bool yield_to(struct task_struct *p, bool preempt); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index c2bbdecaa27d..25e06a5e048b 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -3227,7 +3227,7 @@ need_resched: static inline void sched_submit_work(struct task_struct *tsk) { - if (!tsk->state) + if (!tsk->state || tsk_is_pi_blocked(tsk)) return; /* * If we are going to sleep and we have plugged IO queued, -- cgit From cd70469d084fde198dc07c1a31b8463562228a5a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 29 Feb 2012 16:46:23 +0200 Subject: usb: core: hcd: make hcd->irq unsigned There's really no point in having hcd->irq as a signed integer when we consider the fact that IRQ 0 means NO_IRQ. In order to avoid confusion, make hcd->irq unsigned and fix users who were passing -1 as the IRQ number to usb_add_hcd. Tested-by: Kuninori Morimoto Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 6 +++--- drivers/usb/host/ohci-hcd.c | 2 +- drivers/usb/host/xhci-ring.c | 2 +- drivers/usb/host/xhci.c | 8 ++++---- drivers/usb/musb/musb_core.c | 2 +- drivers/usb/musb/musb_gadget.c | 2 +- include/linux/usb/hcd.h | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index e1282328fc27..9d7fc9a39933 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2352,7 +2352,7 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd, "io mem" : "io base", (unsigned long long)hcd->rsrc_start); } else { - hcd->irq = -1; + hcd->irq = 0; if (hcd->rsrc_start) dev_info(hcd->self.controller, "%s 0x%08llx\n", (hcd->driver->flags & HCD_MEMORY) ? @@ -2508,7 +2508,7 @@ err_register_root_hub: clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); del_timer_sync(&hcd->rh_timer); err_hcd_driver_start: - if (usb_hcd_is_primary_hcd(hcd) && hcd->irq >= 0) + if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0) free_irq(irqnum, hcd); err_request_irq: err_hcd_driver_setup: @@ -2573,7 +2573,7 @@ void usb_remove_hcd(struct usb_hcd *hcd) del_timer_sync(&hcd->rh_timer); if (usb_hcd_is_primary_hcd(hcd)) { - if (hcd->irq >= 0) + if (hcd->irq > 0) free_irq(hcd->irq, hcd); } diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 34b9edd86651..831fa40c609a 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -899,7 +899,7 @@ static void ohci_stop (struct usb_hcd *hcd) ohci_usb_reset (ohci); ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); free_irq(hcd->irq, hcd); - hcd->irq = -1; + hcd->irq = 0; if (quirk_zfmicro(ohci)) del_timer(&ohci->unlink_watchdog); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 3a033240ec64..9e71f7c46a85 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2396,7 +2396,7 @@ hw_died: /* FIXME when MSI-X is supported and there are multiple vectors */ /* Clear the MSI-X event interrupt status */ - if (hcd->irq != -1) { + if (hcd->irq) { u32 irq_pending; /* Acknowledge the PCI interrupt */ irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index c939f5fdef9e..a629ad860329 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -224,13 +224,13 @@ static void xhci_free_irq(struct xhci_hcd *xhci) int ret; /* return if using legacy interrupt */ - if (xhci_to_hcd(xhci)->irq >= 0) + if (xhci_to_hcd(xhci)->irq > 0) return; ret = xhci_free_msi(xhci); if (!ret) return; - if (pdev->irq >= 0) + if (pdev->irq > 0) free_irq(pdev->irq, xhci_to_hcd(xhci)); return; @@ -341,7 +341,7 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) /* unregister the legacy interrupt */ if (hcd->irq) free_irq(hcd->irq, hcd); - hcd->irq = -1; + hcd->irq = 0; ret = xhci_setup_msix(xhci); if (ret) @@ -349,7 +349,7 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) ret = xhci_setup_msi(xhci); if (!ret) - /* hcd->irq is -1, we have MSI */ + /* hcd->irq is 0, we have MSI */ return 0; if (!pdev->irq) { diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b527e9e6dbac..0f8b82918a40 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1987,7 +1987,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb->xceiv->otg->default_a = 1; musb->xceiv->state = OTG_STATE_A_IDLE; - status = usb_add_hcd(musb_to_hcd(musb), -1, 0); + status = usb_add_hcd(musb_to_hcd(musb), 0, 0); hcd->self.uses_pio_for_control = 1; dev_dbg(musb->controller, "%s mode, status %d, devctl %02x %c\n", diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index a495a3079c07..f42c29b11f71 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1957,7 +1957,7 @@ static int musb_gadget_start(struct usb_gadget *g, * handles power budgeting ... this way also * ensures HdrcStart is indirectly called. */ - retval = usb_add_hcd(musb_to_hcd(musb), -1, 0); + retval = usb_add_hcd(musb_to_hcd(musb), 0, 0); if (retval < 0) { dev_dbg(musb->controller, "add_hcd failed, %d\n", retval); goto err2; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 2e6071efbfb7..5de415707c23 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -127,7 +127,7 @@ struct usb_hcd { unsigned authorized_default:1; unsigned has_tt:1; /* Integrated TT in root hub */ - int irq; /* irq allocated */ + unsigned int irq; /* irq allocated */ void __iomem *regs; /* device memory/io */ u64 rsrc_start; /* memory/io resource start */ u64 rsrc_len; /* memory/io resource length */ -- cgit From 9c717758c9289331e22c88ef69d8c248def9cd29 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 29 Feb 2012 14:23:27 +0000 Subject: mdio: Export mdio.h to userland The ID packing definitions are needed by userland and the register definitions may also be useful there. Do not export mdio_phy_id_{is_c45,prtad,devad}() as the use of bool is problematic and it's not that useful to export only a subset of these. Do not export MDIO_SUPPORTS_{C22,C45} directly; these flags are only exposed to userland through struct ethtool_cmd so they should be defined alongside that with appropriate names. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/Kbuild | 1 + include/linux/mdio.h | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index c94e71781b79..4b0b7ed4a999 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -238,6 +238,7 @@ header-y += magic.h header-y += major.h header-y += map_to_7segment.h header-y += matroxfb.h +header-y += mdio.h header-y += media.h header-y += mempolicy.h header-y += meye.h diff --git a/include/linux/mdio.h b/include/linux/mdio.h index b1494aced217..ec90da7232cc 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -273,6 +273,8 @@ static inline __u16 mdio_phy_id_c45(int prtad, int devad) return MDIO_PHY_ID_C45 | (prtad << 5) | devad; } +#ifdef __KERNEL__ + static inline bool mdio_phy_id_is_c45(int phy_id) { return (phy_id & MDIO_PHY_ID_C45) && !(phy_id & ~MDIO_PHY_ID_C45_MASK); @@ -288,11 +290,6 @@ static inline __u16 mdio_phy_id_devad(int phy_id) return phy_id & MDIO_PHY_ID_DEVAD; } -#define MDIO_SUPPORTS_C22 1 -#define MDIO_SUPPORTS_C45 2 - -#ifdef __KERNEL__ - /** * struct mdio_if_info - Ethernet controller MDIO interface * @prtad: PRTAD of the PHY (%MDIO_PRTAD_NONE if not present/unknown) @@ -321,6 +318,8 @@ struct mdio_if_info { #define MDIO_PRTAD_NONE (-1) #define MDIO_DEVAD_NONE (-1) +#define MDIO_SUPPORTS_C22 1 +#define MDIO_SUPPORTS_C45 2 #define MDIO_EMULATE_C22 4 struct ethtool_cmd; -- cgit From 9c4df53bc3f9c72d7f1f1226927f456d46412381 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 29 Feb 2012 14:26:22 +0000 Subject: ethtool, mdio, mii: Specify MDIO information fields in struct ethtool_cmd Add comments for ethtool_cmd::phy_address and ethtool_cmd::mdio_support, and definitions of the flags currently used in mdio_support. In the mdio library, assert that its own flags continue to match those in the ethtool interface. In the mii library, use the ethtool flag definition and stop including . Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/mdio.c | 3 +++ drivers/net/mii.c | 4 ++-- include/linux/ethtool.h | 23 +++++++++++++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/net/mdio.c b/drivers/net/mdio.c index 16fbb11d92ac..8403316eb02b 100644 --- a/drivers/net/mdio.c +++ b/drivers/net/mdio.c @@ -190,6 +190,9 @@ void mdio45_ethtool_gset_npage(const struct mdio_if_info *mdio, int reg; u32 speed; + BUILD_BUG_ON(MDIO_SUPPORTS_C22 != ETH_MDIO_SUPPORTS_C22); + BUILD_BUG_ON(MDIO_SUPPORTS_C45 != ETH_MDIO_SUPPORTS_C45); + ecmd->transceiver = XCVR_INTERNAL; ecmd->phy_address = mdio->prtad; ecmd->mdio_support = diff --git a/drivers/net/mii.c b/drivers/net/mii.c index c70c2332d15e..4a99c3919037 100644 --- a/drivers/net/mii.c +++ b/drivers/net/mii.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include static u32 mii_get_an(struct mii_if_info *mii, u16 addr) { @@ -74,7 +74,7 @@ int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd) /* this isn't fully supported at higher layers */ ecmd->phy_address = mii->phy_id; - ecmd->mdio_support = MDIO_SUPPORTS_C22; + ecmd->mdio_support = ETH_MDIO_SUPPORTS_C22; ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII; diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index da5b2de99ae4..e1d9e0ede309 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -30,10 +30,15 @@ struct ethtool_cmd { * access it */ __u8 duplex; /* Duplex, half or full */ __u8 port; /* Which connector port */ - __u8 phy_address; + __u8 phy_address; /* MDIO PHY address (PRTAD for clause 45). + * May be read-only or read-write + * depending on the driver. + */ __u8 transceiver; /* Which transceiver to use */ __u8 autoneg; /* Enable or disable autonegotiation */ - __u8 mdio_support; + __u8 mdio_support; /* MDIO protocols supported. Read-only. + * Not set by all drivers. + */ __u32 maxtxpkt; /* Tx pkts before generating tx int */ __u32 maxrxpkt; /* Rx pkts before generating rx int */ __u16 speed_hi; /* The forced speed (upper @@ -59,6 +64,20 @@ static inline __u32 ethtool_cmd_speed(const struct ethtool_cmd *ep) return (ep->speed_hi << 16) | ep->speed; } +/* Device supports clause 22 register access to PHY or peripherals + * using the interface defined in . This should not be + * set if there are known to be no such peripherals present or if + * the driver only emulates clause 22 registers for compatibility. + */ +#define ETH_MDIO_SUPPORTS_C22 1 + +/* Device supports clause 45 register access to PHY or peripherals + * using the interface defined in and . + * This should not be set if there are known to be no such peripherals + * present. + */ +#define ETH_MDIO_SUPPORTS_C45 2 + #define ETHTOOL_FWVERS_LEN 32 #define ETHTOOL_BUSINFO_LEN 32 /* these strings are set to whatever the driver author decides... */ -- cgit From 9d1acbfb774fa5e043a44adedfcc36c9837a5e61 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 1 Mar 2012 22:23:42 +0200 Subject: Bluetooth: mgmt: Add defines for command sizes These defines are shorter than "sizeof(struct mgmt_cp_foo_bar...)" and will be helpful when extending the command lookup table to contain the expected command size information. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 7aab53e6b813..d33457d657c3 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -52,14 +52,17 @@ struct mgmt_addr_info { bdaddr_t bdaddr; __u8 type; } __packed; +#define MGMT_ADDR_INFO_SIZE 7 #define MGMT_OP_READ_VERSION 0x0001 +#define MGMT_READ_VERSION_SIZE 0 struct mgmt_rp_read_version { __u8 version; __le16 revision; } __packed; #define MGMT_OP_READ_COMMANDS 0x0002 +#define MGMT_READ_COMMANDS_SIZE 0 struct mgmt_rp_read_commands { __le16 num_commands; __le16 num_events; @@ -67,6 +70,7 @@ struct mgmt_rp_read_commands { } __packed; #define MGMT_OP_READ_INDEX_LIST 0x0003 +#define MGMT_READ_INDEX_LIST_SIZE 0 struct mgmt_rp_read_index_list { __le16 num_controllers; __le16 index[0]; @@ -89,6 +93,7 @@ struct mgmt_rp_read_index_list { #define MGMT_SETTING_LE 0x00000200 #define MGMT_OP_READ_INFO 0x0004 +#define MGMT_READ_INFO_SIZE 0 struct mgmt_rp_read_info { bdaddr_t bdaddr; __u8 version; @@ -104,6 +109,8 @@ struct mgmt_mode { __u8 val; } __packed; +#define MGMT_SETTING_SIZE 1 + #define MGMT_OP_SET_POWERED 0x0005 #define MGMT_OP_SET_DISCOVERABLE 0x0006 @@ -111,6 +118,7 @@ struct mgmt_cp_set_discoverable { __u8 val; __u16 timeout; } __packed; +#define MGMT_SET_DISCOVERABLE_SIZE 3 #define MGMT_OP_SET_CONNECTABLE 0x0007 @@ -131,23 +139,27 @@ struct mgmt_cp_set_dev_class { __u8 major; __u8 minor; } __packed; +#define MGMT_SET_DEV_CLASS_SIZE 2 #define MGMT_OP_SET_LOCAL_NAME 0x000F struct mgmt_cp_set_local_name { __u8 name[MGMT_MAX_NAME_LENGTH]; __u8 short_name[MGMT_MAX_SHORT_NAME_LENGTH]; } __packed; +#define MGMT_SET_LOCAL_NAME_SIZE 260 #define MGMT_OP_ADD_UUID 0x0010 struct mgmt_cp_add_uuid { __u8 uuid[16]; __u8 svc_hint; } __packed; +#define MGMT_ADD_UUID_SIZE 17 #define MGMT_OP_REMOVE_UUID 0x0011 struct mgmt_cp_remove_uuid { __u8 uuid[16]; } __packed; +#define MGMT_REMOVE_UUID_SIZE 16 struct mgmt_link_key_info { struct mgmt_addr_info addr; @@ -162,6 +174,7 @@ struct mgmt_cp_load_link_keys { __le16 key_count; struct mgmt_link_key_info keys[0]; } __packed; +#define MGMT_LOAD_LINK_KEYS_SIZE 3 struct mgmt_ltk_info { struct mgmt_addr_info addr; @@ -178,16 +191,19 @@ struct mgmt_cp_load_long_term_keys { __le16 key_count; struct mgmt_ltk_info keys[0]; } __packed; +#define MGMT_LOAD_LONG_TERM_KEYS_SIZE 2 #define MGMT_OP_DISCONNECT 0x0014 struct mgmt_cp_disconnect { struct mgmt_addr_info addr; } __packed; +#define MGMT_DISCONNECT_SIZE MGMT_ADDR_INFO_SIZE struct mgmt_rp_disconnect { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_GET_CONNECTIONS 0x0015 +#define MGMT_GET_CONNECTIONS_SIZE 0 struct mgmt_rp_get_connections { __le16 conn_count; struct mgmt_addr_info addr[0]; @@ -199,6 +215,7 @@ struct mgmt_cp_pin_code_reply { __u8 pin_len; __u8 pin_code[16]; } __packed; +#define MGMT_PIN_CODE_REPLY_SIZE (MGMT_ADDR_INFO_SIZE + 17) struct mgmt_rp_pin_code_reply { struct mgmt_addr_info addr; } __packed; @@ -207,28 +224,33 @@ struct mgmt_rp_pin_code_reply { struct mgmt_cp_pin_code_neg_reply { struct mgmt_addr_info addr; } __packed; +#define MGMT_PIN_CODE_NEG_REPLY_SIZE MGMT_ADDR_INFO_SIZE #define MGMT_OP_SET_IO_CAPABILITY 0x0018 struct mgmt_cp_set_io_capability { __u8 io_capability; } __packed; +#define MGMT_SET_IO_CAPABILITY_SIZE 1 #define MGMT_OP_PAIR_DEVICE 0x0019 struct mgmt_cp_pair_device { struct mgmt_addr_info addr; __u8 io_cap; } __packed; +#define MGMT_PAIR_DEVICE_SIZE (MGMT_ADDR_INFO_SIZE + 1) struct mgmt_rp_pair_device { struct mgmt_addr_info addr; } __packed; #define MGMT_OP_CANCEL_PAIR_DEVICE 0x001A +#define MGMT_CANCEL_PAIR_DEVICE_SIZE MGMT_ADDR_INFO_SIZE #define MGMT_OP_UNPAIR_DEVICE 0x001B struct mgmt_cp_unpair_device { struct mgmt_addr_info addr; __u8 disconnect; } __packed; +#define MGMT_UNPAIR_DEVICE_SIZE (MGMT_ADDR_INFO_SIZE + 1) struct mgmt_rp_unpair_device { struct mgmt_addr_info addr; }; @@ -237,6 +259,7 @@ struct mgmt_rp_unpair_device { struct mgmt_cp_user_confirm_reply { struct mgmt_addr_info addr; } __packed; +#define MGMT_USER_CONFIRM_REPLY_SIZE MGMT_ADDR_INFO_SIZE struct mgmt_rp_user_confirm_reply { struct mgmt_addr_info addr; } __packed; @@ -245,12 +268,14 @@ struct mgmt_rp_user_confirm_reply { struct mgmt_cp_user_confirm_neg_reply { struct mgmt_addr_info addr; } __packed; +#define MGMT_USER_CONFIRM_NEG_REPLY_SIZE MGMT_ADDR_INFO_SIZE #define MGMT_OP_USER_PASSKEY_REPLY 0x001E struct mgmt_cp_user_passkey_reply { struct mgmt_addr_info addr; __le32 passkey; } __packed; +#define MGMT_USER_PASSKEY_REPLY_SIZE (MGMT_ADDR_INFO_SIZE + 4) struct mgmt_rp_user_passkey_reply { struct mgmt_addr_info addr; } __packed; @@ -259,8 +284,10 @@ struct mgmt_rp_user_passkey_reply { struct mgmt_cp_user_passkey_neg_reply { struct mgmt_addr_info addr; } __packed; +#define MGMT_USER_PASSKEY_NEG_REPLY_SIZE MGMT_ADDR_INFO_SIZE #define MGMT_OP_READ_LOCAL_OOB_DATA 0x0020 +#define MGMT_READ_LOCAL_OOB_DATA_SIZE 0 struct mgmt_rp_read_local_oob_data { __u8 hash[16]; __u8 randomizer[16]; @@ -272,27 +299,32 @@ struct mgmt_cp_add_remote_oob_data { __u8 hash[16]; __u8 randomizer[16]; } __packed; +#define MGMT_ADD_REMOTE_OOB_DATA_SIZE (MGMT_ADDR_INFO_SIZE + 32) #define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x0022 struct mgmt_cp_remove_remote_oob_data { struct mgmt_addr_info addr; } __packed; +#define MGMT_REMOVE_REMOTE_OOB_DATA_SIZE MGMT_ADDR_INFO_SIZE #define MGMT_OP_START_DISCOVERY 0x0023 struct mgmt_cp_start_discovery { __u8 type; } __packed; +#define MGMT_START_DISCOVERY_SIZE 1 #define MGMT_OP_STOP_DISCOVERY 0x0024 struct mgmt_cp_stop_discovery { __u8 type; } __packed; +#define MGMT_STOP_DISCOVERY_SIZE 1 #define MGMT_OP_CONFIRM_NAME 0x0025 struct mgmt_cp_confirm_name { struct mgmt_addr_info addr; __u8 name_known; } __packed; +#define MGMT_CONFIRM_NAME_SIZE (MGMT_ADDR_INFO_SIZE + 1) struct mgmt_rp_confirm_name { struct mgmt_addr_info addr; } __packed; @@ -301,11 +333,13 @@ struct mgmt_rp_confirm_name { struct mgmt_cp_block_device { struct mgmt_addr_info addr; } __packed; +#define MGMT_BLOCK_DEVICE_SIZE MGMT_ADDR_INFO_SIZE #define MGMT_OP_UNBLOCK_DEVICE 0x0027 struct mgmt_cp_unblock_device { struct mgmt_addr_info addr; } __packed; +#define MGMT_UNBLOCK_DEVICE_SIZE MGMT_ADDR_INFO_SIZE #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { -- cgit From ba13ccd9b911e043c0f11e60cbb72bd4de194205 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 1 Mar 2012 14:25:33 -0800 Subject: Bluetooth: Update L2CAP timeout constants to use msecs_to_jiffies The L2CAP timeout constants are always used in form of jiffies. So just include the conversion from msecs in the define itself. This has the advantage of making the code where the timeout is used more readable. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 10 +++++----- net/bluetooth/l2cap_core.c | 18 ++++++------------ net/bluetooth/l2cap_sock.c | 2 +- 3 files changed, 12 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 3732a4849f2e..e4669d0230c5 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -45,11 +45,11 @@ #define L2CAP_DEFAULT_SDU_ITIME 0xFFFFFFFF #define L2CAP_DEFAULT_ACC_LAT 0xFFFFFFFF -#define L2CAP_DISC_TIMEOUT (100) -#define L2CAP_DISC_REJ_TIMEOUT (5000) /* 5 seconds */ -#define L2CAP_ENC_TIMEOUT (5000) /* 5 seconds */ -#define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */ -#define L2CAP_INFO_TIMEOUT (4000) /* 4 seconds */ +#define L2CAP_DISC_TIMEOUT msecs_to_jiffies(100) +#define L2CAP_DISC_REJ_TIMEOUT msecs_to_jiffies(5000) +#define L2CAP_ENC_TIMEOUT msecs_to_jiffies(5000) +#define L2CAP_CONN_TIMEOUT msecs_to_jiffies(40000) +#define L2CAP_INFO_TIMEOUT msecs_to_jiffies(4000) /* L2CAP socket address */ struct sockaddr_l2 { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bc8d558b01f6..0b1aabff8649 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -700,8 +700,7 @@ static void l2cap_do_start(struct l2cap_chan *chan) conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT; conn->info_ident = l2cap_get_ident(conn); - schedule_delayed_work(&conn->info_timer, - msecs_to_jiffies(L2CAP_INFO_TIMEOUT)); + schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT); l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ, sizeof(req), &req); @@ -2741,8 +2740,7 @@ sendresp: conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT; conn->info_ident = l2cap_get_ident(conn); - schedule_delayed_work(&conn->info_timer, - msecs_to_jiffies(L2CAP_INFO_TIMEOUT)); + schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT); l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ, sizeof(info), &info); @@ -3028,8 +3026,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr default: l2cap_chan_set_err(chan, ECONNRESET); - __set_chan_timer(chan, - msecs_to_jiffies(L2CAP_DISC_REJ_TIMEOUT)); + __set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT); l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; } @@ -4539,8 +4536,7 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) if (encrypt == 0x00) { if (chan->sec_level == BT_SECURITY_MEDIUM) { __clear_chan_timer(chan); - __set_chan_timer(chan, - msecs_to_jiffies(L2CAP_ENC_TIMEOUT)); + __set_chan_timer(chan, L2CAP_ENC_TIMEOUT); } else if (chan->sec_level == BT_SECURITY_HIGH) l2cap_chan_close(chan, ECONNREFUSED); } else { @@ -4598,8 +4594,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) l2cap_send_conn_req(chan); } else { __clear_chan_timer(chan); - __set_chan_timer(chan, - msecs_to_jiffies(L2CAP_DISC_TIMEOUT)); + __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); } } else if (chan->state == BT_CONNECT2) { struct sock *sk = chan->sk; @@ -4622,8 +4617,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) } } else { __l2cap_state_change(chan, BT_DISCONN); - __set_chan_timer(chan, - msecs_to_jiffies(L2CAP_DISC_TIMEOUT)); + __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); res = L2CAP_CR_SEC_BLOCK; stat = L2CAP_CS_NO_INFO; } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 52c94c765779..3da56c5c1fc9 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1039,7 +1039,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int p INIT_LIST_HEAD(&bt_sk(sk)->accept_q); sk->sk_destruct = l2cap_sock_destruct; - sk->sk_sndtimeo = msecs_to_jiffies(L2CAP_CONN_TIMEOUT); + sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT; sock_reset_flag(sk, SOCK_ZAPPED); -- cgit From 5f15903279143eb640f9ba1c0e72b52fe9e9e2a6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 2 Mar 2012 03:13:19 +0200 Subject: Bluetooth: mgmt: Add new error code for invalid index The index is part of the command header and not its parameters so it makes sense to distinguish this from the invalid parameters error. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 1 + net/bluetooth/mgmt.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index d33457d657c3..0ca3519e08bd 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -41,6 +41,7 @@ #define MGMT_STATUS_DISCONNECTED 0x0e #define MGMT_STATUS_NOT_POWERED 0x0f #define MGMT_STATUS_CANCELLED 0x10 +#define MGMT_STATUS_INVALID_INDEX 0x11 struct mgmt_hdr { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bd01e4a4784e..fa9a58964278 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2682,7 +2682,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) hdev = hci_dev_get(index); if (!hdev) { err = cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_PARAMS); + MGMT_STATUS_INVALID_INDEX); goto done; } } @@ -2698,7 +2698,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) if ((hdev && opcode < MGMT_OP_READ_INFO) || (!hdev && opcode >= MGMT_OP_READ_INFO)) { err = cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_PARAMS); + MGMT_STATUS_INVALID_INDEX); goto done; } @@ -2745,7 +2745,7 @@ int mgmt_index_added(struct hci_dev *hdev) int mgmt_index_removed(struct hci_dev *hdev) { - u8 status = MGMT_STATUS_INVALID_PARAMS; + u8 status = MGMT_STATUS_INVALID_INDEX; mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); -- cgit From fe316bf2d5847bc5dd975668671a7b1067603bc7 Mon Sep 17 00:00:00 2001 From: Jun'ichi Nomura Date: Fri, 2 Mar 2012 10:38:33 +0100 Subject: block: Fix NULL pointer dereference in sd_revalidate_disk Since 2.6.39 (1196f8b), when a driver returns -ENOMEDIUM for open(), __blkdev_get() calls rescan_partitions() to remove in-kernel partition structures and raise KOBJ_CHANGE uevent. However it ends up calling driver's revalidate_disk without open and could cause oops. In the case of SCSI: process A process B ---------------------------------------------- sys_open __blkdev_get sd_open returns -ENOMEDIUM scsi_remove_device rescan_partitions sd_revalidate_disk Oopses are reported here: http://marc.info/?l=linux-scsi&m=132388619710052 This patch separates the partition invalidation from rescan_partitions() and use it for -ENOMEDIUM case. Reported-by: Huajun Li Signed-off-by: Jun'ichi Nomura Acked-by: Tejun Heo Cc: stable@kernel.org Signed-off-by: Jens Axboe --- block/partition-generic.c | 48 +++++++++++++++++++++++++++++++++++++++-------- fs/block_dev.c | 16 ++++++++++++---- include/linux/genhd.h | 1 + 3 files changed, 53 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/block/partition-generic.c b/block/partition-generic.c index d06ec1c829c2..6df5d6928a44 100644 --- a/block/partition-generic.c +++ b/block/partition-generic.c @@ -389,17 +389,11 @@ static bool disk_unlock_native_capacity(struct gendisk *disk) } } -int rescan_partitions(struct gendisk *disk, struct block_device *bdev) +static int drop_partitions(struct gendisk *disk, struct block_device *bdev) { - struct parsed_partitions *state = NULL; struct disk_part_iter piter; struct hd_struct *part; - int p, highest, res; -rescan: - if (state && !IS_ERR(state)) { - kfree(state); - state = NULL; - } + int res; if (bdev->bd_part_count) return -EBUSY; @@ -412,6 +406,24 @@ rescan: delete_partition(disk, part->partno); disk_part_iter_exit(&piter); + return 0; +} + +int rescan_partitions(struct gendisk *disk, struct block_device *bdev) +{ + struct parsed_partitions *state = NULL; + struct hd_struct *part; + int p, highest, res; +rescan: + if (state && !IS_ERR(state)) { + kfree(state); + state = NULL; + } + + res = drop_partitions(disk, bdev); + if (res) + return res; + if (disk->fops->revalidate_disk) disk->fops->revalidate_disk(disk); check_disk_size_change(disk, bdev); @@ -515,6 +527,26 @@ rescan: return 0; } +int invalidate_partitions(struct gendisk *disk, struct block_device *bdev) +{ + int res; + + if (!bdev->bd_invalidated) + return 0; + + res = drop_partitions(disk, bdev); + if (res) + return res; + + set_capacity(disk, 0); + check_disk_size_change(disk, bdev); + bdev->bd_invalidated = 0; + /* tell userspace that the media / partition table may have changed */ + kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE); + + return 0; +} + unsigned char *read_dev_sector(struct block_device *bdev, sector_t n, Sector *p) { struct address_space *mapping = bdev->bd_inode->i_mapping; diff --git a/fs/block_dev.c b/fs/block_dev.c index 0e575d1304b4..5e9f198f7712 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1183,8 +1183,12 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) * The latter is necessary to prevent ghost * partitions on a removed medium. */ - if (bdev->bd_invalidated && (!ret || ret == -ENOMEDIUM)) - rescan_partitions(disk, bdev); + if (bdev->bd_invalidated) { + if (!ret) + rescan_partitions(disk, bdev); + else if (ret == -ENOMEDIUM) + invalidate_partitions(disk, bdev); + } if (ret) goto out_clear; } else { @@ -1214,8 +1218,12 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) if (bdev->bd_disk->fops->open) ret = bdev->bd_disk->fops->open(bdev, mode); /* the same as first opener case, read comment there */ - if (bdev->bd_invalidated && (!ret || ret == -ENOMEDIUM)) - rescan_partitions(bdev->bd_disk, bdev); + if (bdev->bd_invalidated) { + if (!ret) + rescan_partitions(bdev->bd_disk, bdev); + else if (ret == -ENOMEDIUM) + invalidate_partitions(bdev->bd_disk, bdev); + } if (ret) goto out_unlock_bdev; } diff --git a/include/linux/genhd.h b/include/linux/genhd.h index fe23ee768589..e61d3192448e 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -596,6 +596,7 @@ extern char *disk_name (struct gendisk *hd, int partno, char *buf); extern int disk_expand_part_tbl(struct gendisk *disk, int target); extern int rescan_partitions(struct gendisk *disk, struct block_device *bdev); +extern int invalidate_partitions(struct gendisk *disk, struct block_device *bdev); extern struct hd_struct * __must_check add_partition(struct gendisk *disk, int partno, sector_t start, sector_t len, int flags, -- cgit From 62d3c5439c534b0e6c653fc63e6d8c67be3a57b1 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 2 Mar 2012 10:51:00 +0100 Subject: Block: use a freezable workqueue for disk-event polling This patch (as1519) fixes a bug in the block layer's disk-events polling. The polling is done by a work routine queued on the system_nrt_wq workqueue. Since that workqueue isn't freezable, the polling continues even in the middle of a system sleep transition. Obviously, polling a suspended drive for media changes and such isn't a good thing to do; in the case of USB mass-storage devices it can lead to real problems requiring device resets and even re-enumeration. The patch fixes things by creating a new system-wide, non-reentrant, freezable workqueue and using it for disk-events polling. Signed-off-by: Alan Stern CC: Acked-by: Tejun Heo Acked-by: Rafael J. Wysocki Signed-off-by: Jens Axboe --- block/genhd.c | 10 +++++----- include/linux/workqueue.h | 4 ++++ kernel/workqueue.c | 7 ++++++- 3 files changed, 15 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/block/genhd.c b/block/genhd.c index b26c4085590d..df9816ede75b 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1478,9 +1478,9 @@ static void __disk_unblock_events(struct gendisk *disk, bool check_now) intv = disk_events_poll_jiffies(disk); set_timer_slack(&ev->dwork.timer, intv / 4); if (check_now) - queue_delayed_work(system_nrt_wq, &ev->dwork, 0); + queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, 0); else if (intv) - queue_delayed_work(system_nrt_wq, &ev->dwork, intv); + queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, intv); out_unlock: spin_unlock_irqrestore(&ev->lock, flags); } @@ -1524,7 +1524,7 @@ void disk_flush_events(struct gendisk *disk, unsigned int mask) ev->clearing |= mask; if (!ev->block) { cancel_delayed_work(&ev->dwork); - queue_delayed_work(system_nrt_wq, &ev->dwork, 0); + queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, 0); } spin_unlock_irq(&ev->lock); } @@ -1561,7 +1561,7 @@ unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask) /* uncondtionally schedule event check and wait for it to finish */ disk_block_events(disk); - queue_delayed_work(system_nrt_wq, &ev->dwork, 0); + queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, 0); flush_delayed_work(&ev->dwork); __disk_unblock_events(disk, false); @@ -1598,7 +1598,7 @@ static void disk_events_workfn(struct work_struct *work) intv = disk_events_poll_jiffies(disk); if (!ev->block && intv) - queue_delayed_work(system_nrt_wq, &ev->dwork, intv); + queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, intv); spin_unlock_irq(&ev->lock); diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index eb8b9f15f2e0..af155450cabb 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -289,12 +289,16 @@ enum { * * system_freezable_wq is equivalent to system_wq except that it's * freezable. + * + * system_nrt_freezable_wq is equivalent to system_nrt_wq except that + * it's freezable. */ extern struct workqueue_struct *system_wq; extern struct workqueue_struct *system_long_wq; extern struct workqueue_struct *system_nrt_wq; extern struct workqueue_struct *system_unbound_wq; extern struct workqueue_struct *system_freezable_wq; +extern struct workqueue_struct *system_nrt_freezable_wq; extern struct workqueue_struct * __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active, diff --git a/kernel/workqueue.c b/kernel/workqueue.c index bec7b5b53e03..f2c5638bb5ab 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -253,11 +253,13 @@ struct workqueue_struct *system_long_wq __read_mostly; struct workqueue_struct *system_nrt_wq __read_mostly; struct workqueue_struct *system_unbound_wq __read_mostly; struct workqueue_struct *system_freezable_wq __read_mostly; +struct workqueue_struct *system_nrt_freezable_wq __read_mostly; EXPORT_SYMBOL_GPL(system_wq); EXPORT_SYMBOL_GPL(system_long_wq); EXPORT_SYMBOL_GPL(system_nrt_wq); EXPORT_SYMBOL_GPL(system_unbound_wq); EXPORT_SYMBOL_GPL(system_freezable_wq); +EXPORT_SYMBOL_GPL(system_nrt_freezable_wq); #define CREATE_TRACE_POINTS #include @@ -3833,8 +3835,11 @@ static int __init init_workqueues(void) WQ_UNBOUND_MAX_ACTIVE); system_freezable_wq = alloc_workqueue("events_freezable", WQ_FREEZABLE, 0); + system_nrt_freezable_wq = alloc_workqueue("events_nrt_freezable", + WQ_NON_REENTRANT | WQ_FREEZABLE, 0); BUG_ON(!system_wq || !system_long_wq || !system_nrt_wq || - !system_unbound_wq || !system_freezable_wq); + !system_unbound_wq || !system_freezable_wq || + !system_nrt_freezable_wq); return 0; } early_initcall(init_workqueues); -- cgit From d28a9689c93195d39f91f35a9519876688605b65 Mon Sep 17 00:00:00 2001 From: Anton Tikhomirov Date: Wed, 15 Feb 2012 17:04:56 +0900 Subject: usb: dwc3: Add Exynos Specific Glue layer Adds Exynos Specific Glue layer to support USB peripherals on Samsung Exynos5 chips. [ balbi@ti.com : prevent compilation of Exynos glue layer on platforms which don't provide clk API implementation ] Signed-off-by: Anton Tikhomirov Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/Makefile | 13 +++ drivers/usb/dwc3/dwc3-exynos.c | 151 ++++++++++++++++++++++++++++++ include/linux/platform_data/dwc3-exynos.h | 24 +++++ 3 files changed, 188 insertions(+) create mode 100644 drivers/usb/dwc3/dwc3-exynos.c create mode 100644 include/linux/platform_data/dwc3-exynos.h (limited to 'include') diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 900ae74357f1..d441fe4c180b 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -28,6 +28,19 @@ endif obj-$(CONFIG_USB_DWC3) += dwc3-omap.o +## +# REVISIT Samsung Exynos platform needs the clk API which isn't +# defined on all architectures. If we allow dwc3-exynos.c compile +# always we will fail the linking phase on those architectures +# which don't provide clk api implementation and that's unnaceptable. +# +# When Samsung's platform start supporting pm_runtime, this check +# for HAVE_CLK should be removed. +## +ifneq ($(CONFIG_HAVE_CLK),) + obj-$(CONFIG_USB_DWC3) += dwc3-exynos.o +endif + ifneq ($(CONFIG_PCI),) obj-$(CONFIG_USB_DWC3) += dwc3-pci.o endif diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c new file mode 100644 index 000000000000..d19030198086 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -0,0 +1,151 @@ +/** + * dwc3-exynos.c - Samsung EXYNOS DWC3 Specific Glue layer + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Anton Tikhomirov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" + +struct dwc3_exynos { + struct platform_device *dwc3; + struct device *dev; + + struct clk *clk; +}; + +static int __devinit dwc3_exynos_probe(struct platform_device *pdev) +{ + struct dwc3_exynos_data *pdata = pdev->dev.platform_data; + struct platform_device *dwc3; + struct dwc3_exynos *exynos; + struct clk *clk; + + int devid; + int ret = -ENOMEM; + + exynos = kzalloc(sizeof(*exynos), GFP_KERNEL); + if (!exynos) { + dev_err(&pdev->dev, "not enough memory\n"); + goto err0; + } + + platform_set_drvdata(pdev, exynos); + + devid = dwc3_get_device_id(); + if (devid < 0) + goto err1; + + dwc3 = platform_device_alloc("dwc3", devid); + if (!dwc3) { + dev_err(&pdev->dev, "couldn't allocate dwc3 device\n"); + goto err2; + } + + clk = clk_get(&pdev->dev, "usbdrd30"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "couldn't get clock\n"); + ret = -EINVAL; + goto err3; + } + + dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask); + + dwc3->dev.parent = &pdev->dev; + dwc3->dev.dma_mask = pdev->dev.dma_mask; + dwc3->dev.dma_parms = pdev->dev.dma_parms; + exynos->dwc3 = dwc3; + exynos->dev = &pdev->dev; + exynos->clk = clk; + + clk_enable(exynos->clk); + + /* PHY initialization */ + if (!pdata) { + dev_dbg(&pdev->dev, "missing platform data\n"); + } else { + if (pdata->phy_init) + pdata->phy_init(pdev, pdata->phy_type); + } + + ret = platform_device_add_resources(dwc3, pdev->resource, + pdev->num_resources); + if (ret) { + dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n"); + goto err4; + } + + ret = platform_device_add(dwc3); + if (ret) { + dev_err(&pdev->dev, "failed to register dwc3 device\n"); + goto err4; + } + + return 0; + +err4: + if (pdata && pdata->phy_exit) + pdata->phy_exit(pdev, pdata->phy_type); + + clk_disable(clk); + clk_put(clk); +err3: + platform_device_put(dwc3); +err2: + dwc3_put_device_id(devid); +err1: + kfree(exynos); +err0: + return ret; +} + +static int __devexit dwc3_exynos_remove(struct platform_device *pdev) +{ + struct dwc3_exynos *exynos = platform_get_drvdata(pdev); + struct dwc3_exynos_data *pdata = pdev->dev.platform_data; + + platform_device_unregister(exynos->dwc3); + + dwc3_put_device_id(exynos->dwc3->id); + + if (pdata && pdata->phy_exit) + pdata->phy_exit(pdev, pdata->phy_type); + + clk_disable(exynos->clk); + clk_put(exynos->clk); + + kfree(exynos); + + return 0; +} + +static struct platform_driver dwc3_exynos_driver = { + .probe = dwc3_exynos_probe, + .remove = __devexit_p(dwc3_exynos_remove), + .driver = { + .name = "exynos-dwc3", + }, +}; + +module_platform_driver(dwc3_exynos_driver); + +MODULE_ALIAS("platform:exynos-dwc3"); +MODULE_AUTHOR("Anton Tikhomirov "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DesignWare USB3 EXYNOS Glue Layer"); diff --git a/include/linux/platform_data/dwc3-exynos.h b/include/linux/platform_data/dwc3-exynos.h new file mode 100644 index 000000000000..5eb7da9b3772 --- /dev/null +++ b/include/linux/platform_data/dwc3-exynos.h @@ -0,0 +1,24 @@ +/** + * dwc3-exynos.h - Samsung EXYNOS DWC3 Specific Glue layer, header. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Anton Tikhomirov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _DWC3_EXYNOS_H_ +#define _DWC3_EXYNOS_H_ + +struct dwc3_exynos_data { + int phy_type; + int (*phy_init)(struct platform_device *pdev, int type); + int (*phy_exit)(struct platform_device *pdev, int type); +}; + +#endif /* _DWC3_EXYNOS_H_ */ -- cgit From 2e5b5b3a1b7768c89fbfeca18e75f8ee377e924c Mon Sep 17 00:00:00 2001 From: Hiroshi Shimamoto Date: Thu, 23 Feb 2012 17:41:27 +0900 Subject: sched: Clean up parameter passing of proc_sched_autogroup_set_nice() Pass nice as a value to proc_sched_autogroup_set_nice(). No side effect is expected, and the variable err will be overwritten with the return value. Signed-off-by: Hiroshi Shimamoto Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/4F45FBB7.5090607@ct.jp.nec.com Signed-off-by: Ingo Molnar --- fs/proc/base.c | 3 +-- include/linux/sched.h | 2 +- kernel/sched/auto_group.c | 12 ++++++------ 3 files changed, 8 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/fs/proc/base.c b/fs/proc/base.c index d4548dd49b02..965d4bde3a3b 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1310,8 +1310,7 @@ sched_autogroup_write(struct file *file, const char __user *buf, if (!p) return -ESRCH; - err = nice; - err = proc_sched_autogroup_set_nice(p, &err); + err = proc_sched_autogroup_set_nice(p, nice); if (err) count = err; diff --git a/include/linux/sched.h b/include/linux/sched.h index c628a9151437..c298fb9cf5ad 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2065,7 +2065,7 @@ extern void sched_autogroup_fork(struct signal_struct *sig); extern void sched_autogroup_exit(struct signal_struct *sig); #ifdef CONFIG_PROC_FS extern void proc_sched_autogroup_show_task(struct task_struct *p, struct seq_file *m); -extern int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice); +extern int proc_sched_autogroup_set_nice(struct task_struct *p, int nice); #endif #else static inline void sched_autogroup_create_attach(struct task_struct *p) { } diff --git a/kernel/sched/auto_group.c b/kernel/sched/auto_group.c index e8a1f83ee0e7..0984a21076a3 100644 --- a/kernel/sched/auto_group.c +++ b/kernel/sched/auto_group.c @@ -195,20 +195,20 @@ __setup("noautogroup", setup_autogroup); #ifdef CONFIG_PROC_FS -int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice) +int proc_sched_autogroup_set_nice(struct task_struct *p, int nice) { static unsigned long next = INITIAL_JIFFIES; struct autogroup *ag; int err; - if (*nice < -20 || *nice > 19) + if (nice < -20 || nice > 19) return -EINVAL; - err = security_task_setnice(current, *nice); + err = security_task_setnice(current, nice); if (err) return err; - if (*nice < 0 && !can_nice(current, *nice)) + if (nice < 0 && !can_nice(current, nice)) return -EPERM; /* this is a heavy operation taking global locks.. */ @@ -219,9 +219,9 @@ int proc_sched_autogroup_set_nice(struct task_struct *p, int *nice) ag = autogroup_task_get(p); down_write(&ag->lock); - err = sched_group_set_shares(ag->tg, prio_to_weight[*nice + 20]); + err = sched_group_set_shares(ag->tg, prio_to_weight[nice + 20]); if (!err) - ag->nice = *nice; + ag->nice = nice; up_write(&ag->lock); autogroup_kref_put(ag); -- cgit From 4f87da80a5210e66fb47b0e839f4d05016986f78 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 2 Mar 2012 19:55:56 +0200 Subject: Bluetooth: Remove HCI_PI_MGMT_INIT flag for sockets This flag is of no use right now and is in fact harmful in that it prevents the HCI_MGMT flag to be set for any controllers that may need it after the first one that bluetoothd takes into use (the flag is cleared for the first controller so any subsequent ones through the same bluetoothd mgmt socket never get the HCI_MGMT flag set). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 4 ---- net/bluetooth/hci_sock.c | 1 - net/bluetooth/mgmt.c | 18 ++++++++---------- 3 files changed, 8 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index facd7ed32b74..25cb0a15b579 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1034,16 +1034,12 @@ int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) -/* HCI socket flags */ -#define HCI_PI_MGMT_INIT 0 - struct hci_pinfo { struct bt_sock bt; struct hci_dev *hdev; struct hci_filter filter; __u32 cmsg_mask; unsigned short channel; - unsigned long flags; }; /* HCI security filter */ diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 8a814bca00d7..63afd234283e 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -659,7 +659,6 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le goto done; } - set_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags); break; case HCI_CHANNEL_MONITOR: diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index fa9a58964278..4b1efedc18c5 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -615,19 +615,17 @@ static void service_cache_off(struct work_struct *work) static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev) { - if (!test_and_clear_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags)) + if (test_and_set_bit(HCI_MGMT, &hdev->dev_flags)) return; - if (!test_and_set_bit(HCI_MGMT, &hdev->dev_flags)) { - INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off); + INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off); - /* Non-mgmt controlled devices get this bit set - * implicitly so that pairing works for them, however - * for mgmt we require user-space to explicitly enable - * it - */ - clear_bit(HCI_PAIRABLE, &hdev->dev_flags); - } + /* Non-mgmt controlled devices get this bit set + * implicitly so that pairing works for them, however + * for mgmt we require user-space to explicitly enable + * it + */ + clear_bit(HCI_PAIRABLE, &hdev->dev_flags); } static int read_controller_info(struct sock *sk, struct hci_dev *hdev, -- cgit From 2b88f2de30510c0f4e623d3cd5fcd85cdb70b51f Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Fri, 10 Feb 2012 03:19:41 +0000 Subject: net: dcb: getnumtcs()/setnumtcs() should return an int {g|s}etnumtcs() today returns a u8 that is only used by the DCB code to verify no error occurred. Today the driver implementations return negative error codes which end up being non-zero so the logic works out but triggers some sparse warnings. To fix the sparse warnings convert the return value to an int. CC: Eilon Greenstein Reported-by: Dan Carpenter Signed-off-by: John Fastabend Tested-by: Ross Brattain Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c | 4 ++-- drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c | 4 ++-- include/net/dcbnl.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c index 9a9bd3ab4793..9a4ed05bb30a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c @@ -2142,7 +2142,7 @@ static u8 bnx2x_dcbnl_get_cap(struct net_device *netdev, int capid, u8 *cap) return rval; } -static u8 bnx2x_dcbnl_get_numtcs(struct net_device *netdev, int tcid, u8 *num) +static int bnx2x_dcbnl_get_numtcs(struct net_device *netdev, int tcid, u8 *num) { struct bnx2x *bp = netdev_priv(netdev); u8 rval = 0; @@ -2169,7 +2169,7 @@ static u8 bnx2x_dcbnl_get_numtcs(struct net_device *netdev, int tcid, u8 *num) return rval; } -static u8 bnx2x_dcbnl_set_numtcs(struct net_device *netdev, int tcid, u8 num) +static int bnx2x_dcbnl_set_numtcs(struct net_device *netdev, int tcid, u8 num) { struct bnx2x *bp = netdev_priv(netdev); DP(NETIF_MSG_LINK, "num tcs = %d; Not supported\n", num); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c index 95e5d11bb555..dde65f951400 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c @@ -490,7 +490,7 @@ static u8 ixgbe_dcbnl_getcap(struct net_device *netdev, int capid, u8 *cap) return 0; } -static u8 ixgbe_dcbnl_getnumtcs(struct net_device *netdev, int tcid, u8 *num) +static int ixgbe_dcbnl_getnumtcs(struct net_device *netdev, int tcid, u8 *num) { struct ixgbe_adapter *adapter = netdev_priv(netdev); u8 rval = 0; @@ -514,7 +514,7 @@ static u8 ixgbe_dcbnl_getnumtcs(struct net_device *netdev, int tcid, u8 *num) return rval; } -static u8 ixgbe_dcbnl_setnumtcs(struct net_device *netdev, int tcid, u8 num) +static int ixgbe_dcbnl_setnumtcs(struct net_device *netdev, int tcid, u8 num) { return -EINVAL; } diff --git a/include/net/dcbnl.h b/include/net/dcbnl.h index 2cd66d0be348..f55c980d8e23 100644 --- a/include/net/dcbnl.h +++ b/include/net/dcbnl.h @@ -72,8 +72,8 @@ struct dcbnl_rtnl_ops { void (*getpfccfg)(struct net_device *, int, u8 *); u8 (*setall)(struct net_device *); u8 (*getcap)(struct net_device *, int, u8 *); - u8 (*getnumtcs)(struct net_device *, int, u8 *); - u8 (*setnumtcs)(struct net_device *, int, u8); + int (*getnumtcs)(struct net_device *, int, u8 *); + int (*setnumtcs)(struct net_device *, int, u8); u8 (*getpfcstate)(struct net_device *); void (*setpfcstate)(struct net_device *, u8); void (*getbcncfg)(struct net_device *, int, u32 *); -- cgit From f541fb7e20c848f947ca65fbf169efe69400c942 Mon Sep 17 00:00:00 2001 From: Samuel Jero Date: Sun, 26 Feb 2012 18:22:02 -0700 Subject: dccp: fix bug in sequence number validation during connection setup This fixes a bug in the sequence number validation during the initial handshake. The code did not treat the initial sequence numbers ISS and ISR as read-only and did not keep state for GSR and GSS as required by the specification. This causes problems with retransmissions during the initial handshake, causing the budding connection to be reset. This patch now treats ISS/ISR as read-only and tracks GSS/GSR as required. Signed-off-by: Samuel Jero Signed-off-by: Gerrit Renker --- include/linux/dccp.h | 8 ++++++-- net/dccp/ipv4.c | 8 +++++--- net/dccp/ipv6.c | 8 +++++--- net/dccp/minisocks.c | 18 +++++++++++------- net/dccp/output.c | 10 +++++----- 5 files changed, 32 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 710c04302a15..eaf95a023af4 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -376,8 +376,10 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) /** * struct dccp_request_sock - represent DCCP-specific connection request * @dreq_inet_rsk: structure inherited from - * @dreq_iss: initial sequence number sent on the Response (RFC 4340, 7.1) - * @dreq_isr: initial sequence number received on the Request + * @dreq_iss: initial sequence number, sent on the first Response (RFC 4340, 7.1) + * @dreq_gss: greatest sequence number sent (for retransmitted Responses) + * @dreq_isr: initial sequence number received in the first Request + * @dreq_gsr: greatest sequence number received (for retransmitted Request(s)) * @dreq_service: service code present on the Request (there is just one) * @dreq_featneg: feature negotiation options for this connection * The following two fields are analogous to the ones in dccp_sock: @@ -387,7 +389,9 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) struct dccp_request_sock { struct inet_request_sock dreq_inet_rsk; __u64 dreq_iss; + __u64 dreq_gss; __u64 dreq_isr; + __u64 dreq_gsr; __be32 dreq_service; struct list_head dreq_featneg; __u32 dreq_timestamp_echo; diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 1c67fe8ff90d..caf6e1734b62 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -300,7 +300,8 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info) */ WARN_ON(req->sk); - if (seq != dccp_rsk(req)->dreq_iss) { + if (!between48(seq, dccp_rsk(req)->dreq_iss, + dccp_rsk(req)->dreq_gss)) { NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); goto out; } @@ -639,11 +640,12 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) * * Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookie * - * In fact we defer setting S.GSR, S.SWL, S.SWH to - * dccp_create_openreq_child. + * Setting S.SWL/S.SWH to is deferred to dccp_create_openreq_child(). */ dreq->dreq_isr = dcb->dccpd_seq; + dreq->dreq_gsr = dreq->dreq_isr; dreq->dreq_iss = dccp_v4_init_sequence(skb); + dreq->dreq_gss = dreq->dreq_iss; dreq->dreq_service = service; if (dccp_v4_send_response(sk, req, NULL)) diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index ce903f747e64..4dc588f520e0 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -193,7 +193,8 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, */ WARN_ON(req->sk != NULL); - if (seq != dccp_rsk(req)->dreq_iss) { + if (!between48(seq, dccp_rsk(req)->dreq_iss, + dccp_rsk(req)->dreq_gss)) { NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); goto out; } @@ -440,11 +441,12 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) * * Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookie * - * In fact we defer setting S.GSR, S.SWL, S.SWH to - * dccp_create_openreq_child. + * Setting S.SWL/S.SWH to is deferred to dccp_create_openreq_child(). */ dreq->dreq_isr = dcb->dccpd_seq; + dreq->dreq_gsr = dreq->dreq_isr; dreq->dreq_iss = dccp_v6_init_sequence(skb); + dreq->dreq_gss = dreq->dreq_iss; dreq->dreq_service = service; if (dccp_v6_send_response(sk, req, NULL)) diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index 5a7f90bbffac..ea850ce35d4a 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -127,9 +127,11 @@ struct sock *dccp_create_openreq_child(struct sock *sk, * activation below, as these windows all depend on the local * and remote Sequence Window feature values (7.5.2). */ - newdp->dccps_gss = newdp->dccps_iss = dreq->dreq_iss; + newdp->dccps_iss = dreq->dreq_iss; + newdp->dccps_gss = dreq->dreq_gss; newdp->dccps_gar = newdp->dccps_iss; - newdp->dccps_gsr = newdp->dccps_isr = dreq->dreq_isr; + newdp->dccps_isr = dreq->dreq_isr; + newdp->dccps_gsr = dreq->dreq_gsr; /* * Activate features: initialise CCIDs, sequence windows etc. @@ -164,9 +166,9 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, /* Check for retransmitted REQUEST */ if (dccp_hdr(skb)->dccph_type == DCCP_PKT_REQUEST) { - if (after48(DCCP_SKB_CB(skb)->dccpd_seq, dreq->dreq_isr)) { + if (after48(DCCP_SKB_CB(skb)->dccpd_seq, dreq->dreq_gsr)) { dccp_pr_debug("Retransmitted REQUEST\n"); - dreq->dreq_isr = DCCP_SKB_CB(skb)->dccpd_seq; + dreq->dreq_gsr = DCCP_SKB_CB(skb)->dccpd_seq; /* * Send another RESPONSE packet * To protect against Request floods, increment retrans @@ -186,12 +188,14 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, goto drop; /* Invalid ACK */ - if (DCCP_SKB_CB(skb)->dccpd_ack_seq != dreq->dreq_iss) { + if (!between48(DCCP_SKB_CB(skb)->dccpd_ack_seq, + dreq->dreq_iss, dreq->dreq_gss)) { dccp_pr_debug("Invalid ACK number: ack_seq=%llu, " - "dreq_iss=%llu\n", + "dreq_iss=%llu, dreq_gss=%llu\n", (unsigned long long) DCCP_SKB_CB(skb)->dccpd_ack_seq, - (unsigned long long) dreq->dreq_iss); + (unsigned long long) dreq->dreq_iss, + (unsigned long long) dreq->dreq_gss); goto drop; } diff --git a/net/dccp/output.c b/net/dccp/output.c index dede3edb8849..787367308797 100644 --- a/net/dccp/output.c +++ b/net/dccp/output.c @@ -408,10 +408,10 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst, skb_dst_set(skb, dst_clone(dst)); dreq = dccp_rsk(req); - if (inet_rsk(req)->acked) /* increase ISS upon retransmission */ - dccp_inc_seqno(&dreq->dreq_iss); + if (inet_rsk(req)->acked) /* increase GSS upon retransmission */ + dccp_inc_seqno(&dreq->dreq_gss); DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_RESPONSE; - DCCP_SKB_CB(skb)->dccpd_seq = dreq->dreq_iss; + DCCP_SKB_CB(skb)->dccpd_seq = dreq->dreq_gss; /* Resolve feature dependencies resulting from choice of CCID */ if (dccp_feat_server_ccid_dependencies(dreq)) @@ -429,8 +429,8 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst, DCCP_SKB_CB(skb)->dccpd_opt_len) / 4; dh->dccph_type = DCCP_PKT_RESPONSE; dh->dccph_x = 1; - dccp_hdr_set_seq(dh, dreq->dreq_iss); - dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), dreq->dreq_isr); + dccp_hdr_set_seq(dh, dreq->dreq_gss); + dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), dreq->dreq_gsr); dccp_hdr_response(skb)->dccph_resp_service = dreq->dreq_service; dccp_csum_outgoing(skb); -- cgit From 2556cd8603cd1f39205aaf61f054a76133359ae6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 3 Mar 2012 15:04:45 -0500 Subject: mdio.h: Include linux/types.h Fixes: /home/davem/src/GIT/net-next/usr/include/linux/mdio.h:271: found __[us]{8,16,32,64} type without #include Signed-off-by: David S. Miller --- include/linux/mdio.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/mdio.h b/include/linux/mdio.h index ec90da7232cc..dfb947959ec9 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -10,6 +10,7 @@ #ifndef __LINUX_MDIO_H__ #define __LINUX_MDIO_H__ +#include #include /* MDIO Manageable Devices (MMDs). */ -- cgit From 784db3f06228da81627d23052a6d2ecc38db001a Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sun, 4 Mar 2012 12:54:54 +0000 Subject: ppp: Change copyright notices from ANU to me This changes the copyright notices on the PPP code that I developed in the late 1990s from being copyright The Australian National University to copyright Paul Mackerras. I can do this as I have an acknowledgement in writing from the Head of the Computer Science Department at ANU (where I worked then) that ANU does not claim any intellectual property in this code. While I'm at it, change the copyright notice from BSD-style to GNU GPL like the rest of the kernel. Signed-off-by: Paul Mackerras Signed-off-by: David S. Miller --- drivers/net/ppp/ppp_deflate.c | 30 ++++-------------------------- include/linux/ppp-comp.h | 38 ++++---------------------------------- include/linux/ppp_defs.h | 38 ++++---------------------------------- 3 files changed, 12 insertions(+), 94 deletions(-) (limited to 'include') diff --git a/drivers/net/ppp/ppp_deflate.c b/drivers/net/ppp/ppp_deflate.c index 1dbdf82a6dfd..602c625d95d5 100644 --- a/drivers/net/ppp/ppp_deflate.c +++ b/drivers/net/ppp/ppp_deflate.c @@ -1,34 +1,12 @@ /* - * ==FILEVERSION 980319== - * * ppp_deflate.c - interface the zlib procedures for Deflate compression * and decompression (as used by gzip) to the PPP code. - * This version is for use with Linux kernel 1.3.X. - * - * Copyright (c) 1994 The Australian National University. - * All rights reserved. - * - * Permission to use, copy, modify, and distribute this software and its - * documentation is hereby granted, provided that the above copyright - * notice appears in all copies. This software is provided without any - * warranty, express or implied. The Australian National University - * makes no representations about the suitability of this software for - * any purpose. - * - * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY - * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF - * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY - * OF SUCH DAMAGE. * - * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO - * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, - * OR MODIFICATIONS. + * Copyright 1994-1998 Paul Mackerras. * - * From: deflate.c,v 1.1 1996/01/18 03:17:48 paulus Exp + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. */ #include diff --git a/include/linux/ppp-comp.h b/include/linux/ppp-comp.h index b8d4ddd22736..e53ff65935dd 100644 --- a/include/linux/ppp-comp.h +++ b/include/linux/ppp-comp.h @@ -1,42 +1,12 @@ /* * ppp-comp.h - Definitions for doing PPP packet compression. * - * Copyright (c) 1994 The Australian National University. - * All rights reserved. + * Copyright 1994-1998 Paul Mackerras. * - * Permission to use, copy, modify, and distribute this software and its - * documentation is hereby granted, provided that the above copyright - * notice appears in all copies. This software is provided without any - * warranty, express or implied. The Australian National University - * makes no representations about the suitability of this software for - * any purpose. - * - * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY - * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF - * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY - * OF SUCH DAMAGE. - * - * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO - * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, - * OR MODIFICATIONS. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. */ - -/* - * ==FILEVERSION 980319== - * - * NOTE TO MAINTAINERS: - * If you modify this file at all, please set the above date. - * ppp-comp.h is shipped with a PPP distribution as well as with the kernel; - * if everyone increases the FILEVERSION number above, then scripts - * can do the right thing when deciding whether to install a new ppp-comp.h - * file. Don't change the format of that line otherwise, so the - * installation script can recognize it. - */ - #ifndef _NET_PPP_COMP_H #define _NET_PPP_COMP_H diff --git a/include/linux/ppp_defs.h b/include/linux/ppp_defs.h index 0f93ed6b4a88..ba416f67eb62 100644 --- a/include/linux/ppp_defs.h +++ b/include/linux/ppp_defs.h @@ -1,44 +1,14 @@ /* * ppp_defs.h - PPP definitions. * - * Copyright (c) 1994 The Australian National University. - * All rights reserved. + * Copyright 1994-2000 Paul Mackerras. * - * Permission to use, copy, modify, and distribute this software and its - * documentation is hereby granted, provided that the above copyright - * notice appears in all copies. This software is provided without any - * warranty, express or implied. The Australian National University - * makes no representations about the suitability of this software for - * any purpose. - * - * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY - * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF - * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY - * OF SUCH DAMAGE. - * - * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO - * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, - * OR MODIFICATIONS. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. */ - #include -/* - * ==FILEVERSION 20000114== - * - * NOTE TO MAINTAINERS: - * If you modify this file at all, please set the above date. - * ppp_defs.h is shipped with a PPP distribution as well as with the kernel; - * if everyone increases the FILEVERSION number above, then scripts - * can do the right thing when deciding whether to install a new ppp_defs.h - * file. Don't change the format of that line otherwise, so the - * installation script can recognize it. - */ - #ifndef _PPP_DEFS_H_ #define _PPP_DEFS_H_ -- cgit From bf7daebb9fba540cb8864f435f153678b3e5c171 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sun, 4 Mar 2012 12:56:04 +0000 Subject: ppp: Move ioctl definitions from if_ppp.h to new ppp-ioctl.h This moves the definitions of the ioctls, constants and structures relating to the ppp_generic interface to userspace out from if_ppp.h to a new file, ppp-ioctl.h. The new file has my copyright since I designed and implemented the ppp_generic interface in the late 1990s. None of the contents of this file comes from the original if_ppp.h published by Carnegie Mellon University. Of the remainder of if_ppp.h, only the PPP_MTU definition was being used, and this replaces the uses of it with PPP_MRU (which is identical). Therefore, this replaces the entire file with the single line #include which clearly doesn't contain any CMU code. Thus I have removed the CMU copyright notice with its problematic advertising clause, and in fact since it's only one trivial line I have not added any other copyright notice. Signed-off-by: Paul Mackerras Signed-off-by: David S. Miller --- Documentation/ioctl/ioctl-number.txt | 2 +- Documentation/networking/ppp_generic.txt | 6 +- drivers/net/ppp/ppp_generic.c | 4 +- drivers/net/ppp/pptp.c | 4 +- include/linux/Kbuild | 1 + include/linux/if_ppp.h | 174 +------------------------------ include/linux/ppp-ioctl.h | 119 +++++++++++++++++++++ 7 files changed, 129 insertions(+), 181 deletions(-) create mode 100644 include/linux/ppp-ioctl.h (limited to 'include') diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 4840334ea97b..85af5482f0ab 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -255,7 +255,7 @@ Code Seq#(hex) Include File Comments linux/ixjuser.h 'r' 00-1F linux/msdos_fs.h and fs/fat/dir.c 's' all linux/cdk.h -'t' 00-7F linux/if_ppp.h +'t' 00-7F linux/ppp-ioctl.h 't' 80-8F linux/isdn_ppp.h 't' 90 linux/toshiba.h 'u' 00-1F linux/smb_fs.h gone diff --git a/Documentation/networking/ppp_generic.txt b/Documentation/networking/ppp_generic.txt index 15b5172fbb98..091d20273dcb 100644 --- a/Documentation/networking/ppp_generic.txt +++ b/Documentation/networking/ppp_generic.txt @@ -342,7 +342,7 @@ an interface unit are: numbers on received multilink fragments SC_MP_XSHORTSEQ transmit short multilink sequence nos. - The values of these flags are defined in . Note + The values of these flags are defined in . Note that the values of the SC_MULTILINK, SC_MP_SHORTSEQ and SC_MP_XSHORTSEQ bits are ignored if the CONFIG_PPP_MULTILINK option is not selected. @@ -358,7 +358,7 @@ an interface unit are: * PPPIOCSCOMPRESS sets the parameters for packet compression or decompression. The argument should point to a ppp_option_data - structure (defined in ), which contains a + structure (defined in ), which contains a pointer/length pair which should describe a block of memory containing a CCP option specifying a compression method and its parameters. The ppp_option_data struct also contains a `transmit' @@ -395,7 +395,7 @@ an interface unit are: * PPPIOCSNPMODE sets the network-protocol mode for a given network protocol. The argument should point to an npioctl struct (defined - in ). The `protocol' field gives the PPP protocol + in ). The `protocol' field gives the PPP protocol number for the protocol to be affected, and the `mode' field specifies what to do with packets for that protocol: diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 6d4d2ebb0a8a..159da2905fe9 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include @@ -1031,7 +1031,7 @@ static void ppp_setup(struct net_device *dev) { dev->netdev_ops = &ppp_netdev_ops; dev->hard_header_len = PPP_HDRLEN; - dev->mtu = PPP_MTU; + dev->mtu = PPP_MRU; dev->addr_len = 0; dev->tx_queue_len = 3; dev->type = ARPHRD_PPP; diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index 234cd9d87ed9..885dbdd9c39e 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -481,7 +481,7 @@ static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr, po->chan.mtu = dst_mtu(&rt->dst); if (!po->chan.mtu) - po->chan.mtu = PPP_MTU; + po->chan.mtu = PPP_MRU; ip_rt_put(rt); po->chan.mtu -= PPTP_HEADER_OVERHEAD; diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 4b0b7ed4a999..a25555381097 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -305,6 +305,7 @@ header-y += poll.h header-y += posix_types.h header-y += ppdev.h header-y += ppp-comp.h +header-y += ppp-ioctl.h header-y += ppp_defs.h header-y += pps.h header-y += prctl.h diff --git a/include/linux/if_ppp.h b/include/linux/if_ppp.h index c9ad38322576..9048fabb7a4e 100644 --- a/include/linux/if_ppp.h +++ b/include/linux/if_ppp.h @@ -1,173 +1 @@ -/* - * if_ppp.h - Point-to-Point Protocol definitions. - * - * Copyright (c) 1989 Carnegie Mellon University. - * All rights reserved. - * - * Redistribution and use in source and binary forms are permitted - * provided that the above copyright notice and this paragraph are - * duplicated in all such forms and that any documentation, - * advertising materials, and other materials related to such - * distribution and use acknowledge that the software was developed - * by Carnegie Mellon University. The name of the - * University may not be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - */ - -/* - * ==FILEVERSION 20050812== - * - * NOTE TO MAINTAINERS: - * If you modify this file at all, please set the above date. - * if_ppp.h is shipped with a PPP distribution as well as with the kernel; - * if everyone increases the FILEVERSION number above, then scripts - * can do the right thing when deciding whether to install a new if_ppp.h - * file. Don't change the format of that line otherwise, so the - * installation script can recognize it. - */ - -#ifndef _IF_PPP_H_ -#define _IF_PPP_H_ - -#include -#include - -/* - * Packet sizes - */ - -#define PPP_MTU 1500 /* Default MTU (size of Info field) */ -#define PPP_MAXMRU 65000 /* Largest MRU we allow */ -#define PROTO_IPX 0x002b /* protocol numbers */ -#define PROTO_DNA_RT 0x0027 /* DNA Routing */ - - -/* - * Bit definitions for flags. - */ - -#define SC_COMP_PROT 0x00000001 /* protocol compression (output) */ -#define SC_COMP_AC 0x00000002 /* header compression (output) */ -#define SC_COMP_TCP 0x00000004 /* TCP (VJ) compression (output) */ -#define SC_NO_TCP_CCID 0x00000008 /* disable VJ connection-id comp. */ -#define SC_REJ_COMP_AC 0x00000010 /* reject adrs/ctrl comp. on input */ -#define SC_REJ_COMP_TCP 0x00000020 /* reject TCP (VJ) comp. on input */ -#define SC_CCP_OPEN 0x00000040 /* Look at CCP packets */ -#define SC_CCP_UP 0x00000080 /* May send/recv compressed packets */ -#define SC_ENABLE_IP 0x00000100 /* IP packets may be exchanged */ -#define SC_LOOP_TRAFFIC 0x00000200 /* send traffic to pppd */ -#define SC_MULTILINK 0x00000400 /* do multilink encapsulation */ -#define SC_MP_SHORTSEQ 0x00000800 /* use short MP sequence numbers */ -#define SC_COMP_RUN 0x00001000 /* compressor has been inited */ -#define SC_DECOMP_RUN 0x00002000 /* decompressor has been inited */ -#define SC_MP_XSHORTSEQ 0x00004000 /* transmit short MP seq numbers */ -#define SC_DEBUG 0x00010000 /* enable debug messages */ -#define SC_LOG_INPKT 0x00020000 /* log contents of good pkts recvd */ -#define SC_LOG_OUTPKT 0x00040000 /* log contents of pkts sent */ -#define SC_LOG_RAWIN 0x00080000 /* log all chars received */ -#define SC_LOG_FLUSH 0x00100000 /* log all chars flushed */ -#define SC_SYNC 0x00200000 /* synchronous serial mode */ -#define SC_MUST_COMP 0x00400000 /* no uncompressed packets may be sent or received */ -#define SC_MASK 0x0f600fff /* bits that user can change */ - -/* state bits */ -#define SC_XMIT_BUSY 0x10000000 /* (used by isdn_ppp?) */ -#define SC_RCV_ODDP 0x08000000 /* have rcvd char with odd parity */ -#define SC_RCV_EVNP 0x04000000 /* have rcvd char with even parity */ -#define SC_RCV_B7_1 0x02000000 /* have rcvd char with bit 7 = 1 */ -#define SC_RCV_B7_0 0x01000000 /* have rcvd char with bit 7 = 0 */ -#define SC_DC_FERROR 0x00800000 /* fatal decomp error detected */ -#define SC_DC_ERROR 0x00400000 /* non-fatal decomp error detected */ - -/* - * Ioctl definitions. - */ - -struct npioctl { - int protocol; /* PPP protocol, e.g. PPP_IP */ - enum NPmode mode; -}; - -/* Structure describing a CCP configuration option, for PPPIOCSCOMPRESS */ -struct ppp_option_data { - __u8 __user *ptr; - __u32 length; - int transmit; -}; - -struct ifpppstatsreq { - struct ifreq b; - struct ppp_stats stats; /* statistic information */ -}; - -struct ifpppcstatsreq { - struct ifreq b; - struct ppp_comp_stats stats; -}; - -/* For PPPIOCGL2TPSTATS */ -struct pppol2tp_ioc_stats { - __u16 tunnel_id; /* redundant */ - __u16 session_id; /* if zero, get tunnel stats */ - __u32 using_ipsec:1; /* valid only for session_id == 0 */ - __aligned_u64 tx_packets; - __aligned_u64 tx_bytes; - __aligned_u64 tx_errors; - __aligned_u64 rx_packets; - __aligned_u64 rx_bytes; - __aligned_u64 rx_seq_discards; - __aligned_u64 rx_oos_packets; - __aligned_u64 rx_errors; -}; - -#define ifr__name b.ifr_ifrn.ifrn_name -#define stats_ptr b.ifr_ifru.ifru_data - -/* - * Ioctl definitions. - */ - -#define PPPIOCGFLAGS _IOR('t', 90, int) /* get configuration flags */ -#define PPPIOCSFLAGS _IOW('t', 89, int) /* set configuration flags */ -#define PPPIOCGASYNCMAP _IOR('t', 88, int) /* get async map */ -#define PPPIOCSASYNCMAP _IOW('t', 87, int) /* set async map */ -#define PPPIOCGUNIT _IOR('t', 86, int) /* get ppp unit number */ -#define PPPIOCGRASYNCMAP _IOR('t', 85, int) /* get receive async map */ -#define PPPIOCSRASYNCMAP _IOW('t', 84, int) /* set receive async map */ -#define PPPIOCGMRU _IOR('t', 83, int) /* get max receive unit */ -#define PPPIOCSMRU _IOW('t', 82, int) /* set max receive unit */ -#define PPPIOCSMAXCID _IOW('t', 81, int) /* set VJ max slot ID */ -#define PPPIOCGXASYNCMAP _IOR('t', 80, ext_accm) /* get extended ACCM */ -#define PPPIOCSXASYNCMAP _IOW('t', 79, ext_accm) /* set extended ACCM */ -#define PPPIOCXFERUNIT _IO('t', 78) /* transfer PPP unit */ -#define PPPIOCSCOMPRESS _IOW('t', 77, struct ppp_option_data) -#define PPPIOCGNPMODE _IOWR('t', 76, struct npioctl) /* get NP mode */ -#define PPPIOCSNPMODE _IOW('t', 75, struct npioctl) /* set NP mode */ -#define PPPIOCSPASS _IOW('t', 71, struct sock_fprog) /* set pass filter */ -#define PPPIOCSACTIVE _IOW('t', 70, struct sock_fprog) /* set active filt */ -#define PPPIOCGDEBUG _IOR('t', 65, int) /* Read debug level */ -#define PPPIOCSDEBUG _IOW('t', 64, int) /* Set debug level */ -#define PPPIOCGIDLE _IOR('t', 63, struct ppp_idle) /* get idle time */ -#define PPPIOCNEWUNIT _IOWR('t', 62, int) /* create new ppp unit */ -#define PPPIOCATTACH _IOW('t', 61, int) /* attach to ppp unit */ -#define PPPIOCDETACH _IOW('t', 60, int) /* detach from ppp unit/chan */ -#define PPPIOCSMRRU _IOW('t', 59, int) /* set multilink MRU */ -#define PPPIOCCONNECT _IOW('t', 58, int) /* connect channel to unit */ -#define PPPIOCDISCONN _IO('t', 57) /* disconnect channel */ -#define PPPIOCATTCHAN _IOW('t', 56, int) /* attach to ppp channel */ -#define PPPIOCGCHAN _IOR('t', 55, int) /* get ppp channel number */ -#define PPPIOCGL2TPSTATS _IOR('t', 54, struct pppol2tp_ioc_stats) - -#define SIOCGPPPSTATS (SIOCDEVPRIVATE + 0) -#define SIOCGPPPVER (SIOCDEVPRIVATE + 1) /* NEVER change this!! */ -#define SIOCGPPPCSTATS (SIOCDEVPRIVATE + 2) - -#if !defined(ifr_mtu) -#define ifr_mtu ifr_ifru.ifru_metric -#endif - -#endif /* _IF_PPP_H_ */ +#include diff --git a/include/linux/ppp-ioctl.h b/include/linux/ppp-ioctl.h new file mode 100644 index 000000000000..2d9a8859550a --- /dev/null +++ b/include/linux/ppp-ioctl.h @@ -0,0 +1,119 @@ +/* + * ppp-ioctl.h - PPP ioctl definitions. + * + * Copyright 1999-2002 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ +#ifndef _PPP_IOCTL_H +#define _PPP_IOCTL_H + +#include +#include + +/* + * Bit definitions for flags argument to PPPIOCGFLAGS/PPPIOCSFLAGS. + */ +#define SC_COMP_PROT 0x00000001 /* protocol compression (output) */ +#define SC_COMP_AC 0x00000002 /* header compression (output) */ +#define SC_COMP_TCP 0x00000004 /* TCP (VJ) compression (output) */ +#define SC_NO_TCP_CCID 0x00000008 /* disable VJ connection-id comp. */ +#define SC_REJ_COMP_AC 0x00000010 /* reject adrs/ctrl comp. on input */ +#define SC_REJ_COMP_TCP 0x00000020 /* reject TCP (VJ) comp. on input */ +#define SC_CCP_OPEN 0x00000040 /* Look at CCP packets */ +#define SC_CCP_UP 0x00000080 /* May send/recv compressed packets */ +#define SC_ENABLE_IP 0x00000100 /* IP packets may be exchanged */ +#define SC_LOOP_TRAFFIC 0x00000200 /* send traffic to pppd */ +#define SC_MULTILINK 0x00000400 /* do multilink encapsulation */ +#define SC_MP_SHORTSEQ 0x00000800 /* use short MP sequence numbers */ +#define SC_COMP_RUN 0x00001000 /* compressor has been inited */ +#define SC_DECOMP_RUN 0x00002000 /* decompressor has been inited */ +#define SC_MP_XSHORTSEQ 0x00004000 /* transmit short MP seq numbers */ +#define SC_DEBUG 0x00010000 /* enable debug messages */ +#define SC_LOG_INPKT 0x00020000 /* log contents of good pkts recvd */ +#define SC_LOG_OUTPKT 0x00040000 /* log contents of pkts sent */ +#define SC_LOG_RAWIN 0x00080000 /* log all chars received */ +#define SC_LOG_FLUSH 0x00100000 /* log all chars flushed */ +#define SC_SYNC 0x00200000 /* synchronous serial mode */ +#define SC_MUST_COMP 0x00400000 /* no uncompressed packets may be sent or received */ +#define SC_MASK 0x0f600fff /* bits that user can change */ + +/* state bits */ +#define SC_XMIT_BUSY 0x10000000 /* (used by isdn_ppp?) */ +#define SC_RCV_ODDP 0x08000000 /* have rcvd char with odd parity */ +#define SC_RCV_EVNP 0x04000000 /* have rcvd char with even parity */ +#define SC_RCV_B7_1 0x02000000 /* have rcvd char with bit 7 = 1 */ +#define SC_RCV_B7_0 0x01000000 /* have rcvd char with bit 7 = 0 */ +#define SC_DC_FERROR 0x00800000 /* fatal decomp error detected */ +#define SC_DC_ERROR 0x00400000 /* non-fatal decomp error detected */ + +/* Used with PPPIOCGNPMODE/PPPIOCSNPMODE */ +struct npioctl { + int protocol; /* PPP protocol, e.g. PPP_IP */ + enum NPmode mode; +}; + +/* Structure describing a CCP configuration option, for PPPIOCSCOMPRESS */ +struct ppp_option_data { + __u8 __user *ptr; + __u32 length; + int transmit; +}; + +/* For PPPIOCGL2TPSTATS */ +struct pppol2tp_ioc_stats { + __u16 tunnel_id; /* redundant */ + __u16 session_id; /* if zero, get tunnel stats */ + __u32 using_ipsec:1; /* valid only for session_id == 0 */ + __aligned_u64 tx_packets; + __aligned_u64 tx_bytes; + __aligned_u64 tx_errors; + __aligned_u64 rx_packets; + __aligned_u64 rx_bytes; + __aligned_u64 rx_seq_discards; + __aligned_u64 rx_oos_packets; + __aligned_u64 rx_errors; +}; + +/* + * Ioctl definitions. + */ + +#define PPPIOCGFLAGS _IOR('t', 90, int) /* get configuration flags */ +#define PPPIOCSFLAGS _IOW('t', 89, int) /* set configuration flags */ +#define PPPIOCGASYNCMAP _IOR('t', 88, int) /* get async map */ +#define PPPIOCSASYNCMAP _IOW('t', 87, int) /* set async map */ +#define PPPIOCGUNIT _IOR('t', 86, int) /* get ppp unit number */ +#define PPPIOCGRASYNCMAP _IOR('t', 85, int) /* get receive async map */ +#define PPPIOCSRASYNCMAP _IOW('t', 84, int) /* set receive async map */ +#define PPPIOCGMRU _IOR('t', 83, int) /* get max receive unit */ +#define PPPIOCSMRU _IOW('t', 82, int) /* set max receive unit */ +#define PPPIOCSMAXCID _IOW('t', 81, int) /* set VJ max slot ID */ +#define PPPIOCGXASYNCMAP _IOR('t', 80, ext_accm) /* get extended ACCM */ +#define PPPIOCSXASYNCMAP _IOW('t', 79, ext_accm) /* set extended ACCM */ +#define PPPIOCXFERUNIT _IO('t', 78) /* transfer PPP unit */ +#define PPPIOCSCOMPRESS _IOW('t', 77, struct ppp_option_data) +#define PPPIOCGNPMODE _IOWR('t', 76, struct npioctl) /* get NP mode */ +#define PPPIOCSNPMODE _IOW('t', 75, struct npioctl) /* set NP mode */ +#define PPPIOCSPASS _IOW('t', 71, struct sock_fprog) /* set pass filter */ +#define PPPIOCSACTIVE _IOW('t', 70, struct sock_fprog) /* set active filt */ +#define PPPIOCGDEBUG _IOR('t', 65, int) /* Read debug level */ +#define PPPIOCSDEBUG _IOW('t', 64, int) /* Set debug level */ +#define PPPIOCGIDLE _IOR('t', 63, struct ppp_idle) /* get idle time */ +#define PPPIOCNEWUNIT _IOWR('t', 62, int) /* create new ppp unit */ +#define PPPIOCATTACH _IOW('t', 61, int) /* attach to ppp unit */ +#define PPPIOCDETACH _IOW('t', 60, int) /* detach from ppp unit/chan */ +#define PPPIOCSMRRU _IOW('t', 59, int) /* set multilink MRU */ +#define PPPIOCCONNECT _IOW('t', 58, int) /* connect channel to unit */ +#define PPPIOCDISCONN _IO('t', 57) /* disconnect channel */ +#define PPPIOCATTCHAN _IOW('t', 56, int) /* attach to ppp channel */ +#define PPPIOCGCHAN _IOR('t', 55, int) /* get ppp channel number */ +#define PPPIOCGL2TPSTATS _IOR('t', 54, struct pppol2tp_ioc_stats) + +#define SIOCGPPPSTATS (SIOCDEVPRIVATE + 0) +#define SIOCGPPPVER (SIOCDEVPRIVATE + 1) /* NEVER change this!! */ +#define SIOCGPPPCSTATS (SIOCDEVPRIVATE + 2) + +#endif /* _PPP_IOCTL_H */ -- cgit From 4b32da2bcf1de2b7a196a0e48389d231b4472c36 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sun, 4 Mar 2012 12:56:55 +0000 Subject: ppp: Replace uses of with Since all that include/linux/if_ppp.h does is #include , this replaces the occurrences of #include with #include . It also corrects an error in Documentation/networking/l2tp.txt, where it referenced include/linux/if_ppp.h as the source of some definitions that are actually now defined in include/linux/if_pppol2tp.h. Signed-off-by: Paul Mackerras Signed-off-by: David S. Miller --- Documentation/networking/l2tp.txt | 2 +- drivers/isdn/capi/capi.c | 2 +- drivers/net/ppp/ppp_async.c | 2 +- drivers/net/ppp/ppp_synctty.c | 2 +- drivers/net/ppp/pppoe.c | 2 +- drivers/net/ppp/pppox.c | 2 +- drivers/tty/ipwireless/network.c | 2 +- drivers/tty/ipwireless/tty.c | 2 +- fs/compat_ioctl.c | 2 +- include/linux/isdn.h | 2 +- net/atm/pppoatm.c | 2 +- net/irda/irnet/irnet.h | 2 +- net/l2tp/l2tp_ppp.c | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/Documentation/networking/l2tp.txt b/Documentation/networking/l2tp.txt index e7bf3979facb..e63fc1f7bf87 100644 --- a/Documentation/networking/l2tp.txt +++ b/Documentation/networking/l2tp.txt @@ -111,7 +111,7 @@ When creating PPPoL2TP sockets, the application provides information to the driver about the socket in a socket connect() call. Source and destination tunnel and session ids are provided, as well as the file descriptor of a UDP socket. See struct pppol2tp_addr in -include/linux/if_ppp.h. Note that zero tunnel / session ids are +include/linux/if_pppol2tp.h. Note that zero tunnel / session ids are treated specially. When creating the per-tunnel PPPoL2TP management socket in Step 2 above, zero source and destination session ids are specified, which tells the driver to prepare the supplied UDP file diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index d33a70c49180..0cf05464bfb7 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/net/ppp/ppp_async.c b/drivers/net/ppp/ppp_async.c index c6ba64380829..af95a98fd86f 100644 --- a/drivers/net/ppp/ppp_async.c +++ b/drivers/net/ppp/ppp_async.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/net/ppp/ppp_synctty.c b/drivers/net/ppp/ppp_synctty.c index 736a39ee05bb..55e466c511d5 100644 --- a/drivers/net/ppp/ppp_synctty.c +++ b/drivers/net/ppp/ppp_synctty.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index bc9a4bb31980..2fa1a9b6f498 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -72,7 +72,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/net/ppp/pppox.c b/drivers/net/ppp/pppox.c index 8c0d170dabcd..2940e9fe351b 100644 --- a/drivers/net/ppp/pppox.c +++ b/drivers/net/ppp/pppox.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/tty/ipwireless/network.c b/drivers/tty/ipwireless/network.c index f7daeea598e4..57c8b481113f 100644 --- a/drivers/tty/ipwireless/network.c +++ b/drivers/tty/ipwireless/network.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include "network.h" diff --git a/drivers/tty/ipwireless/tty.c b/drivers/tty/ipwireless/tty.c index ef92869502a7..2ffa0b77770a 100644 --- a/drivers/tty/ipwireless/tty.c +++ b/drivers/tty/ipwireless/tty.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index a26bea10e81b..10d8cd90ca6f 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/linux/isdn.h b/include/linux/isdn.h index 4ccf95d681b4..292f27a793d4 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -187,7 +187,7 @@ typedef struct { #endif #include -#include +#include #include #endif diff --git a/net/atm/pppoatm.c b/net/atm/pppoatm.c index df35d9a3b5fe..614d3fc47ede 100644 --- a/net/atm/pppoatm.c +++ b/net/atm/pppoatm.c @@ -44,7 +44,7 @@ #include #include #include -#include +#include #include #include diff --git a/net/irda/irnet/irnet.h b/net/irda/irnet/irnet.h index 979ecb2435a7..564eb0b8afa3 100644 --- a/net/irda/irnet/irnet.h +++ b/net/irda/irnet/irnet.h @@ -254,7 +254,7 @@ #include #include -#include +#include #include #include diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 8a90d756c904..96bc7a67585a 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -82,7 +82,7 @@ #include #include #include -#include +#include #include #include #include -- cgit From 0b471506712dd734964b3270d2aa88160712c262 Mon Sep 17 00:00:00 2001 From: Matt Carlson Date: Mon, 27 Feb 2012 09:44:48 +0000 Subject: tg3: Recode PCI MRRS adjustment as a PCI quirk This patch recodes the MRRS cap for 5719 A0 devices as a PCI quirk. Signed-off-by: Matt Carlson Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 6 ------ drivers/pci/quirks.c | 18 ++++++++++++++++++ include/linux/pci_ids.h | 1 + 3 files changed, 19 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index a8490a473a1c..bc236b6a7a91 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -14242,12 +14242,6 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) tg3_flag_set(tp, PCI_EXPRESS); - if (tp->pci_chip_rev_id == CHIPREV_ID_5719_A0) { - int readrq = pcie_get_readrq(tp->pdev); - if (readrq > 2048) - pcie_set_readrq(tp->pdev, 2048); - } - pci_read_config_word(tp->pdev, pci_pcie_cap(tp->pdev) + PCI_EXP_LNKCTL, &lnkctl); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 64765474676f..f722c5f6951a 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2161,6 +2161,24 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709S, quirk_brcm_570x_limit_vpd); +static void __devinit quirk_brcm_5719_limit_mrrs(struct pci_dev *dev) +{ + u32 rev; + + pci_read_config_dword(dev, 0xf4, &rev); + + /* Only CAP the MRRS if the device is a 5719 A0 */ + if (rev == 0x05719000) { + int readrq = pcie_get_readrq(dev); + if (readrq > 2048) + pcie_set_readrq(dev, 2048); + } +} + +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_TIGON3_5719, + quirk_brcm_5719_limit_mrrs); + /* Originally in EDAC sources for i82875P: * Intel tells BIOS developers to hide device 6 which * configures the overflow device access containing diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 31d77af2ef42..3329965ed63f 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2105,6 +2105,7 @@ #define PCI_DEVICE_ID_NX2_57711E 0x1650 #define PCI_DEVICE_ID_TIGON3_5705 0x1653 #define PCI_DEVICE_ID_TIGON3_5705_2 0x1654 +#define PCI_DEVICE_ID_TIGON3_5719 0x1657 #define PCI_DEVICE_ID_TIGON3_5721 0x1659 #define PCI_DEVICE_ID_TIGON3_5722 0x165a #define PCI_DEVICE_ID_TIGON3_5723 0x165b -- cgit From bce38cd53e5ddba9cb6d708c4ef3d04a4016ec7e Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 9 Feb 2012 23:20:51 +0100 Subject: perf: Add generic taken branch sampling support This patch adds the ability to sample taken branches to the perf_event interface. The ability to capture taken branches is very useful for all sorts of analysis. For instance, basic block profiling, call counts, statistical call graph. This new capability requires hardware assist and as such may not be available on all HW platforms. On Intel x86 it is implemented on top of the Last Branch Record (LBR) facility. To enable taken branches sampling, the PERF_SAMPLE_BRANCH_STACK bit must be set in attr->sample_type. Sampled taken branches may be filtered by type and/or priv levels. The patch adds a new field, called branch_sample_type, to the perf_event_attr structure. It contains a bitmask of filters to apply to the sampled taken branches. Filters may be implemented in HW. If the HW filter does not exist or is not good enough, some arch may also implement a SW filter. The following generic filters are currently defined: - PERF_SAMPLE_USER only branches whose targets are at the user level - PERF_SAMPLE_KERNEL only branches whose targets are at the kernel level - PERF_SAMPLE_HV only branches whose targets are at the hypervisor level - PERF_SAMPLE_ANY any type of branches (subject to priv levels filters) - PERF_SAMPLE_ANY_CALL any call branches (may incl. syscall on some arch) - PERF_SAMPLE_ANY_RET any return branches (may incl. syscall returns on some arch) - PERF_SAMPLE_IND_CALL indirect call branches Obviously filter may be combined. The priv level bits are optional. If not provided, the priv level of the associated event are used. It is possible to collect branches at a priv level different from the associated event. Use of kernel, hv priv levels is subject to permissions and availability (hv). The number of taken branch records present in each sample may vary based on HW, the type of sampled branches, the executed code. Therefore each sample contains the number of taken branches it contains. Signed-off-by: Stephane Eranian Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1328826068-11713-2-git-send-email-eranian@google.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel_lbr.c | 21 +++++---- include/linux/perf_event.h | 71 ++++++++++++++++++++++++++++-- kernel/events/core.c | 68 ++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index 47a7e63bfe54..309d0cc69163 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -142,9 +142,11 @@ static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc) rdmsrl(x86_pmu.lbr_from + lbr_idx, msr_lastbranch.lbr); - cpuc->lbr_entries[i].from = msr_lastbranch.from; - cpuc->lbr_entries[i].to = msr_lastbranch.to; - cpuc->lbr_entries[i].flags = 0; + cpuc->lbr_entries[i].from = msr_lastbranch.from; + cpuc->lbr_entries[i].to = msr_lastbranch.to; + cpuc->lbr_entries[i].mispred = 0; + cpuc->lbr_entries[i].predicted = 0; + cpuc->lbr_entries[i].reserved = 0; } cpuc->lbr_stack.nr = i; } @@ -165,19 +167,22 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) for (i = 0; i < x86_pmu.lbr_nr; i++) { unsigned long lbr_idx = (tos - i) & mask; - u64 from, to, flags = 0; + u64 from, to, mis = 0, pred = 0; rdmsrl(x86_pmu.lbr_from + lbr_idx, from); rdmsrl(x86_pmu.lbr_to + lbr_idx, to); if (lbr_format == LBR_FORMAT_EIP_FLAGS) { - flags = !!(from & LBR_FROM_FLAG_MISPRED); + mis = !!(from & LBR_FROM_FLAG_MISPRED); + pred = !mis; from = (u64)((((s64)from) << 1) >> 1); } - cpuc->lbr_entries[i].from = from; - cpuc->lbr_entries[i].to = to; - cpuc->lbr_entries[i].flags = flags; + cpuc->lbr_entries[i].from = from; + cpuc->lbr_entries[i].to = to; + cpuc->lbr_entries[i].mispred = mis; + cpuc->lbr_entries[i].predicted = pred; + cpuc->lbr_entries[i].reserved = 0; } cpuc->lbr_stack.nr = i; } diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 64426b71381f..5fc494f4a094 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -129,10 +129,39 @@ enum perf_event_sample_format { PERF_SAMPLE_PERIOD = 1U << 8, PERF_SAMPLE_STREAM_ID = 1U << 9, PERF_SAMPLE_RAW = 1U << 10, + PERF_SAMPLE_BRANCH_STACK = 1U << 11, - PERF_SAMPLE_MAX = 1U << 11, /* non-ABI */ + PERF_SAMPLE_MAX = 1U << 12, /* non-ABI */ }; +/* + * values to program into branch_sample_type when PERF_SAMPLE_BRANCH is set + * + * If the user does not pass priv level information via branch_sample_type, + * the kernel uses the event's priv level. Branch and event priv levels do + * not have to match. Branch priv level is checked for permissions. + * + * The branch types can be combined, however BRANCH_ANY covers all types + * of branches and therefore it supersedes all the other types. + */ +enum perf_branch_sample_type { + PERF_SAMPLE_BRANCH_USER = 1U << 0, /* user branches */ + PERF_SAMPLE_BRANCH_KERNEL = 1U << 1, /* kernel branches */ + PERF_SAMPLE_BRANCH_HV = 1U << 2, /* hypervisor branches */ + + PERF_SAMPLE_BRANCH_ANY = 1U << 3, /* any branch types */ + PERF_SAMPLE_BRANCH_ANY_CALL = 1U << 4, /* any call branch */ + PERF_SAMPLE_BRANCH_ANY_RETURN = 1U << 5, /* any return branch */ + PERF_SAMPLE_BRANCH_IND_CALL = 1U << 6, /* indirect calls */ + + PERF_SAMPLE_BRANCH_MAX = 1U << 7, /* non-ABI */ +}; + +#define PERF_SAMPLE_BRANCH_PLM_ALL \ + (PERF_SAMPLE_BRANCH_USER|\ + PERF_SAMPLE_BRANCH_KERNEL|\ + PERF_SAMPLE_BRANCH_HV) + /* * The format of the data returned by read() on a perf event fd, * as specified by attr.read_format: @@ -240,6 +269,7 @@ struct perf_event_attr { __u64 bp_len; __u64 config2; /* extension of config1 */ }; + __u64 branch_sample_type; /* enum branch_sample_type */ }; /* @@ -458,6 +488,8 @@ enum perf_event_type { * * { u32 size; * char data[size];}&& PERF_SAMPLE_RAW + * + * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK * }; */ PERF_RECORD_SAMPLE = 9, @@ -530,12 +562,34 @@ struct perf_raw_record { void *data; }; +/* + * single taken branch record layout: + * + * from: source instruction (may not always be a branch insn) + * to: branch target + * mispred: branch target was mispredicted + * predicted: branch target was predicted + * + * support for mispred, predicted is optional. In case it + * is not supported mispred = predicted = 0. + */ struct perf_branch_entry { - __u64 from; - __u64 to; - __u64 flags; + __u64 from; + __u64 to; + __u64 mispred:1, /* target mispredicted */ + predicted:1,/* target predicted */ + reserved:62; }; +/* + * branch stack layout: + * nr: number of taken branches stored in entries[] + * + * Note that nr can vary from sample to sample + * branches (to, from) are stored from most recent + * to least recent, i.e., entries[0] contains the most + * recent branch. + */ struct perf_branch_stack { __u64 nr; struct perf_branch_entry entries[0]; @@ -566,7 +620,9 @@ struct hw_perf_event { unsigned long event_base; int idx; int last_cpu; + struct hw_perf_event_extra extra_reg; + struct hw_perf_event_extra branch_reg; }; struct { /* software */ struct hrtimer hrtimer; @@ -1007,12 +1063,14 @@ struct perf_sample_data { u64 period; struct perf_callchain_entry *callchain; struct perf_raw_record *raw; + struct perf_branch_stack *br_stack; }; static inline void perf_sample_data_init(struct perf_sample_data *data, u64 addr) { data->addr = addr; data->raw = NULL; + data->br_stack = NULL; } extern void perf_output_sample(struct perf_output_handle *handle, @@ -1151,6 +1209,11 @@ extern void perf_bp_event(struct perf_event *event, void *data); # define perf_instruction_pointer(regs) instruction_pointer(regs) #endif +static inline bool has_branch_stack(struct perf_event *event) +{ + return event->attr.sample_type & PERF_SAMPLE_BRANCH_STACK; +} + extern int perf_output_begin(struct perf_output_handle *handle, struct perf_event *event, unsigned int size); extern void perf_output_end(struct perf_output_handle *handle); diff --git a/kernel/events/core.c b/kernel/events/core.c index e8b32ac75ce3..5820efdf47cd 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -118,6 +118,13 @@ static int cpu_function_call(int cpu, int (*func) (void *info), void *info) PERF_FLAG_FD_OUTPUT |\ PERF_FLAG_PID_CGROUP) +/* + * branch priv levels that need permission checks + */ +#define PERF_SAMPLE_BRANCH_PERM_PLM \ + (PERF_SAMPLE_BRANCH_KERNEL |\ + PERF_SAMPLE_BRANCH_HV) + enum event_type_t { EVENT_FLEXIBLE = 0x1, EVENT_PINNED = 0x2, @@ -3907,6 +3914,24 @@ void perf_output_sample(struct perf_output_handle *handle, } } } + + if (sample_type & PERF_SAMPLE_BRANCH_STACK) { + if (data->br_stack) { + size_t size; + + size = data->br_stack->nr + * sizeof(struct perf_branch_entry); + + perf_output_put(handle, data->br_stack->nr); + perf_output_copy(handle, data->br_stack->entries, size); + } else { + /* + * we always store at least the value of nr + */ + u64 nr = 0; + perf_output_put(handle, nr); + } + } } void perf_prepare_sample(struct perf_event_header *header, @@ -3949,6 +3974,15 @@ void perf_prepare_sample(struct perf_event_header *header, WARN_ON_ONCE(size & (sizeof(u64)-1)); header->size += size; } + + if (sample_type & PERF_SAMPLE_BRANCH_STACK) { + int size = sizeof(u64); /* nr */ + if (data->br_stack) { + size += data->br_stack->nr + * sizeof(struct perf_branch_entry); + } + header->size += size; + } } static void perf_event_output(struct perf_event *event, @@ -5935,6 +5969,40 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, if (attr->read_format & ~(PERF_FORMAT_MAX-1)) return -EINVAL; + if (attr->sample_type & PERF_SAMPLE_BRANCH_STACK) { + u64 mask = attr->branch_sample_type; + + /* only using defined bits */ + if (mask & ~(PERF_SAMPLE_BRANCH_MAX-1)) + return -EINVAL; + + /* at least one branch bit must be set */ + if (!(mask & ~PERF_SAMPLE_BRANCH_PLM_ALL)) + return -EINVAL; + + /* kernel level capture: check permissions */ + if ((mask & PERF_SAMPLE_BRANCH_PERM_PLM) + && perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN)) + return -EACCES; + + /* propagate priv level, when not set for branch */ + if (!(mask & PERF_SAMPLE_BRANCH_PLM_ALL)) { + + /* exclude_kernel checked on syscall entry */ + if (!attr->exclude_kernel) + mask |= PERF_SAMPLE_BRANCH_KERNEL; + + if (!attr->exclude_user) + mask |= PERF_SAMPLE_BRANCH_USER; + + if (!attr->exclude_hv) + mask |= PERF_SAMPLE_BRANCH_HV; + /* + * adjust user setting (for HW filter setup) + */ + attr->branch_sample_type = mask; + } + } out: return ret; -- cgit From d010b3326cf06b3406cdd88af16dcf4e4b6fec2e Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 9 Feb 2012 23:21:00 +0100 Subject: perf: Add callback to flush branch_stack on context switch With branch stack sampling, it is possible to filter by priv levels. In system-wide mode, that means it is possible to capture only user level branches. The builtin SW LBR filter needs to disassemble code based on LBR captured addresses. For that, it needs to know the task the addresses are associated with. Because of context switches, the content of the branch stack buffer may contain addresses from different tasks. We need a callback on context switch to either flush the branch stack or save it. This patch adds a new callback in struct pmu which is called during context switches. The callback is called only when necessary. That is when a system-wide context has, at least, one event which uses PERF_SAMPLE_BRANCH_STACK. The callback is never called for per-thread context. In this version, the Intel x86 code simply flushes (resets) the LBR on context switches (fills it with zeroes). Those zeroed branches are then filtered out by the SW filter. Signed-off-by: Stephane Eranian Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1328826068-11713-11-git-send-email-eranian@google.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event.c | 21 ++++++--- arch/x86/kernel/cpu/perf_event.h | 1 + arch/x86/kernel/cpu/perf_event_intel.c | 13 ++++++ include/linux/perf_event.h | 9 +++- kernel/events/core.c | 85 ++++++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index cea567483274..0a18d16cb58d 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1671,25 +1671,32 @@ static const struct attribute_group *x86_pmu_attr_groups[] = { NULL, }; +static void x86_pmu_flush_branch_stack(void) +{ + if (x86_pmu.flush_branch_stack) + x86_pmu.flush_branch_stack(); +} + static struct pmu pmu = { - .pmu_enable = x86_pmu_enable, - .pmu_disable = x86_pmu_disable, + .pmu_enable = x86_pmu_enable, + .pmu_disable = x86_pmu_disable, .attr_groups = x86_pmu_attr_groups, .event_init = x86_pmu_event_init, - .add = x86_pmu_add, - .del = x86_pmu_del, - .start = x86_pmu_start, - .stop = x86_pmu_stop, - .read = x86_pmu_read, + .add = x86_pmu_add, + .del = x86_pmu_del, + .start = x86_pmu_start, + .stop = x86_pmu_stop, + .read = x86_pmu_read, .start_txn = x86_pmu_start_txn, .cancel_txn = x86_pmu_cancel_txn, .commit_txn = x86_pmu_commit_txn, .event_idx = x86_pmu_event_idx, + .flush_branch_stack = x86_pmu_flush_branch_stack, }; void perf_update_user_clock(struct perf_event_mmap_page *userpg, u64 now) diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index f104c054dc5c..74387c12dc72 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h @@ -324,6 +324,7 @@ struct x86_pmu { void (*cpu_starting)(int cpu); void (*cpu_dying)(int cpu); void (*cpu_dead)(int cpu); + void (*flush_branch_stack)(void); /* * Intel Arch Perfmon v2+ diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 7cc1e2dcc4dd..6627089232a7 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -1539,6 +1539,18 @@ static void intel_pmu_cpu_dying(int cpu) fini_debug_store_on_cpu(cpu); } +static void intel_pmu_flush_branch_stack(void) +{ + /* + * Intel LBR does not tag entries with the + * PID of the current task, then we need to + * flush it on ctxsw + * For now, we simply reset it + */ + if (x86_pmu.lbr_nr) + intel_pmu_lbr_reset(); +} + static __initconst const struct x86_pmu intel_pmu = { .name = "Intel", .handle_irq = intel_pmu_handle_irq, @@ -1566,6 +1578,7 @@ static __initconst const struct x86_pmu intel_pmu = { .cpu_starting = intel_pmu_cpu_starting, .cpu_dying = intel_pmu_cpu_dying, .guest_get_msrs = intel_guest_get_msrs, + .flush_branch_stack = intel_pmu_flush_branch_stack, }; static __init void intel_clovertown_quirk(void) diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 5fc494f4a094..fbbf5e598368 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -746,6 +746,11 @@ struct pmu { * if no implementation is provided it will default to: event->hw.idx + 1. */ int (*event_idx) (struct perf_event *event); /*optional */ + + /* + * flush branch stack on context-switches (needed in cpu-wide mode) + */ + void (*flush_branch_stack) (void); }; /** @@ -979,7 +984,8 @@ struct perf_event_context { u64 parent_gen; u64 generation; int pin_count; - int nr_cgroups; /* cgroup events present */ + int nr_cgroups; /* cgroup evts */ + int nr_branch_stack; /* branch_stack evt */ struct rcu_head rcu_head; }; @@ -1044,6 +1050,7 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, extern u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running); + struct perf_sample_data { u64 type; diff --git a/kernel/events/core.c b/kernel/events/core.c index 242bb51c67f2..c61234b1a988 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -137,6 +137,7 @@ enum event_type_t { */ struct static_key_deferred perf_sched_events __read_mostly; static DEFINE_PER_CPU(atomic_t, perf_cgroup_events); +static DEFINE_PER_CPU(atomic_t, perf_branch_stack_events); static atomic_t nr_mmap_events __read_mostly; static atomic_t nr_comm_events __read_mostly; @@ -888,6 +889,9 @@ list_add_event(struct perf_event *event, struct perf_event_context *ctx) if (is_cgroup_event(event)) ctx->nr_cgroups++; + if (has_branch_stack(event)) + ctx->nr_branch_stack++; + list_add_rcu(&event->event_entry, &ctx->event_list); if (!ctx->nr_events) perf_pmu_rotate_start(ctx->pmu); @@ -1027,6 +1031,9 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx) cpuctx->cgrp = NULL; } + if (has_branch_stack(event)) + ctx->nr_branch_stack--; + ctx->nr_events--; if (event->attr.inherit_stat) ctx->nr_stat--; @@ -2201,6 +2208,66 @@ static void perf_event_context_sched_in(struct perf_event_context *ctx, perf_pmu_rotate_start(ctx->pmu); } +/* + * When sampling the branck stack in system-wide, it may be necessary + * to flush the stack on context switch. This happens when the branch + * stack does not tag its entries with the pid of the current task. + * Otherwise it becomes impossible to associate a branch entry with a + * task. This ambiguity is more likely to appear when the branch stack + * supports priv level filtering and the user sets it to monitor only + * at the user level (which could be a useful measurement in system-wide + * mode). In that case, the risk is high of having a branch stack with + * branch from multiple tasks. Flushing may mean dropping the existing + * entries or stashing them somewhere in the PMU specific code layer. + * + * This function provides the context switch callback to the lower code + * layer. It is invoked ONLY when there is at least one system-wide context + * with at least one active event using taken branch sampling. + */ +static void perf_branch_stack_sched_in(struct task_struct *prev, + struct task_struct *task) +{ + struct perf_cpu_context *cpuctx; + struct pmu *pmu; + unsigned long flags; + + /* no need to flush branch stack if not changing task */ + if (prev == task) + return; + + local_irq_save(flags); + + rcu_read_lock(); + + list_for_each_entry_rcu(pmu, &pmus, entry) { + cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); + + /* + * check if the context has at least one + * event using PERF_SAMPLE_BRANCH_STACK + */ + if (cpuctx->ctx.nr_branch_stack > 0 + && pmu->flush_branch_stack) { + + pmu = cpuctx->ctx.pmu; + + perf_ctx_lock(cpuctx, cpuctx->task_ctx); + + perf_pmu_disable(pmu); + + pmu->flush_branch_stack(); + + perf_pmu_enable(pmu); + + perf_ctx_unlock(cpuctx, cpuctx->task_ctx); + } + } + + rcu_read_unlock(); + + local_irq_restore(flags); +} + /* * Called from scheduler to add the events of the current task * with interrupts disabled. @@ -2232,6 +2299,10 @@ void __perf_event_task_sched_in(struct task_struct *prev, */ if (atomic_read(&__get_cpu_var(perf_cgroup_events))) perf_cgroup_sched_in(prev, task); + + /* check for system-wide branch_stack events */ + if (atomic_read(&__get_cpu_var(perf_branch_stack_events))) + perf_branch_stack_sched_in(prev, task); } static u64 perf_calculate_period(struct perf_event *event, u64 nsec, u64 count) @@ -2798,6 +2869,14 @@ static void free_event(struct perf_event *event) atomic_dec(&per_cpu(perf_cgroup_events, event->cpu)); static_key_slow_dec_deferred(&perf_sched_events); } + + if (has_branch_stack(event)) { + static_key_slow_dec_deferred(&perf_sched_events); + /* is system-wide event */ + if (!(event->attach_state & PERF_ATTACH_TASK)) + atomic_dec(&per_cpu(perf_branch_stack_events, + event->cpu)); + } } if (event->rb) { @@ -5924,6 +6003,12 @@ done: return ERR_PTR(err); } } + if (has_branch_stack(event)) { + static_key_slow_inc(&perf_sched_events.key); + if (!(event->attach_state & PERF_ATTACH_TASK)) + atomic_inc(&per_cpu(perf_branch_stack_events, + event->cpu)); + } } return event; -- cgit From edd8d90b624e97105d26615d1655da22a3605fff Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 28 Feb 2012 00:56:04 +0100 Subject: ssb: sprom fix some sizes / signedness Some parts of the sprom struct are bigger than needed. The leddc and maxpwr values are just 8 bit long and not 16. rxpo2g and rxpo5g are signed I got these information for the open source part of the Broadcom SDK covering sprom version 1 to 9. rxpo2g contained a negative number on my bcm5354 based device, this cased an error and Broadcom SDK says this is signed. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- include/linux/ssb/ssb.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index bbc2612cb64a..f1696213e3b5 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -33,8 +33,8 @@ struct ssb_sprom { u8 et1mdcport; /* MDIO for enet1 */ u16 board_rev; /* Board revision number from SPROM. */ u8 country_code; /* Country Code */ - u16 leddc_on_time; /* LED Powersave Duty Cycle On Count */ - u16 leddc_off_time; /* LED Powersave Duty Cycle Off Count */ + u8 leddc_on_time; /* LED Powersave Duty Cycle On Count */ + u8 leddc_off_time; /* LED Powersave Duty Cycle Off Count */ u8 ant_available_a; /* 2GHz antenna available bits (up to 4) */ u8 ant_available_bg; /* 5GHz antenna available bits (up to 4) */ u16 pa0b0; @@ -53,10 +53,10 @@ struct ssb_sprom { u8 gpio1; /* GPIO pin 1 */ u8 gpio2; /* GPIO pin 2 */ u8 gpio3; /* GPIO pin 3 */ - u16 maxpwr_bg; /* 2.4GHz Amplifier Max Power (in dBm Q5.2) */ - u16 maxpwr_al; /* 5.2GHz Amplifier Max Power (in dBm Q5.2) */ - u16 maxpwr_a; /* 5.3GHz Amplifier Max Power (in dBm Q5.2) */ - u16 maxpwr_ah; /* 5.8GHz Amplifier Max Power (in dBm Q5.2) */ + u8 maxpwr_bg; /* 2.4GHz Amplifier Max Power (in dBm Q5.2) */ + u8 maxpwr_al; /* 5.2GHz Amplifier Max Power (in dBm Q5.2) */ + u8 maxpwr_a; /* 5.3GHz Amplifier Max Power (in dBm Q5.2) */ + u8 maxpwr_ah; /* 5.8GHz Amplifier Max Power (in dBm Q5.2) */ u8 itssi_a; /* Idle TSSI Target for A-PHY */ u8 itssi_bg; /* Idle TSSI Target for B/G-PHY */ u8 tri2g; /* 2.4GHz TX isolation */ @@ -67,8 +67,8 @@ struct ssb_sprom { u8 txpid5gl[4]; /* 4.9 - 5.1GHz TX power index */ u8 txpid5g[4]; /* 5.1 - 5.5GHz TX power index */ u8 txpid5gh[4]; /* 5.5 - ...GHz TX power index */ - u8 rxpo2g; /* 2GHz RX power offset */ - u8 rxpo5g; /* 5GHz RX power offset */ + s8 rxpo2g; /* 2GHz RX power offset */ + s8 rxpo5g; /* 5GHz RX power offset */ u8 rssisav2g; /* 2GHz RSSI params */ u8 rssismc2g; u8 rssismf2g; -- cgit From f8f8a660ba501ad14617ccd0d91a1ed8ce54d6d0 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 28 Feb 2012 00:56:05 +0100 Subject: ssb: remove 5GHz antenna gain from sprom There is no 2.4 GHz or 5GHz antenna gain stored in sprom. The sprom just stores the gain values for antenna 1 and 2 or 1 to 4 for more recent sprom versions. On old devices antenna 2 was used for 5 GHz wifi. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/net/wireless/b43legacy/phy.c | 2 +- drivers/ssb/pci.c | 41 ++++++++++++------------------------ drivers/ssb/pcmcia.c | 12 ++++------- drivers/ssb/sdio.c | 12 ++++------- include/linux/ssb/ssb.h | 7 +----- 5 files changed, 24 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/b43legacy/phy.c b/drivers/net/wireless/b43legacy/phy.c index 96faaef3661b..950334197f40 100644 --- a/drivers/net/wireless/b43legacy/phy.c +++ b/drivers/net/wireless/b43legacy/phy.c @@ -1860,7 +1860,7 @@ void b43legacy_phy_xmitpower(struct b43legacy_wldev *dev) * which accounts for the factor of 4 */ #define REG_MAX_PWR 20 max_pwr = min(REG_MAX_PWR * 4 - - dev->dev->bus->sprom.antenna_gain.ghz24.a0 + - dev->dev->bus->sprom.antenna_gain.a0 - 0x6, max_pwr); /* find the desired power in Q5.2 - power_level is in dBm diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index befa89eac6f3..ed4124469a3a 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -331,7 +331,6 @@ static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in) { int i; u16 v; - s8 gain; u16 loc[3]; if (out->revision == 3) /* rev 3 moved MAC */ @@ -390,20 +389,12 @@ static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in) SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0); /* Extract the antenna gain values. */ - gain = r123_extract_antgain(out->revision, in, - SSB_SPROM1_AGAIN_BG, - SSB_SPROM1_AGAIN_BG_SHIFT); - out->antenna_gain.ghz24.a0 = gain; - out->antenna_gain.ghz24.a1 = gain; - out->antenna_gain.ghz24.a2 = gain; - out->antenna_gain.ghz24.a3 = gain; - gain = r123_extract_antgain(out->revision, in, - SSB_SPROM1_AGAIN_A, - SSB_SPROM1_AGAIN_A_SHIFT); - out->antenna_gain.ghz5.a0 = gain; - out->antenna_gain.ghz5.a1 = gain; - out->antenna_gain.ghz5.a2 = gain; - out->antenna_gain.ghz5.a3 = gain; + out->antenna_gain.a0 = r123_extract_antgain(out->revision, in, + SSB_SPROM1_AGAIN_BG, + SSB_SPROM1_AGAIN_BG_SHIFT); + out->antenna_gain.a1 = r123_extract_antgain(out->revision, in, + SSB_SPROM1_AGAIN_A, + SSB_SPROM1_AGAIN_A_SHIFT); } /* Revs 4 5 and 8 have partially shared layout */ @@ -504,16 +495,14 @@ static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in) } /* Extract the antenna gain values. */ - SPEX(antenna_gain.ghz24.a0, SSB_SPROM4_AGAIN01, + SPEX(antenna_gain.a0, SSB_SPROM4_AGAIN01, SSB_SPROM4_AGAIN0, SSB_SPROM4_AGAIN0_SHIFT); - SPEX(antenna_gain.ghz24.a1, SSB_SPROM4_AGAIN01, + SPEX(antenna_gain.a1, SSB_SPROM4_AGAIN01, SSB_SPROM4_AGAIN1, SSB_SPROM4_AGAIN1_SHIFT); - SPEX(antenna_gain.ghz24.a2, SSB_SPROM4_AGAIN23, + SPEX(antenna_gain.a2, SSB_SPROM4_AGAIN23, SSB_SPROM4_AGAIN2, SSB_SPROM4_AGAIN2_SHIFT); - SPEX(antenna_gain.ghz24.a3, SSB_SPROM4_AGAIN23, + SPEX(antenna_gain.a3, SSB_SPROM4_AGAIN23, SSB_SPROM4_AGAIN3, SSB_SPROM4_AGAIN3_SHIFT); - memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24, - sizeof(out->antenna_gain.ghz5)); sprom_extract_r458(out, in); @@ -602,16 +591,14 @@ static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in) SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, 0xFFFFFFFF, 0); /* Extract the antenna gain values. */ - SPEX(antenna_gain.ghz24.a0, SSB_SPROM8_AGAIN01, + SPEX(antenna_gain.a0, SSB_SPROM8_AGAIN01, SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT); - SPEX(antenna_gain.ghz24.a1, SSB_SPROM8_AGAIN01, + SPEX(antenna_gain.a1, SSB_SPROM8_AGAIN01, SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT); - SPEX(antenna_gain.ghz24.a2, SSB_SPROM8_AGAIN23, + SPEX(antenna_gain.a2, SSB_SPROM8_AGAIN23, SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT); - SPEX(antenna_gain.ghz24.a3, SSB_SPROM8_AGAIN23, + SPEX(antenna_gain.a3, SSB_SPROM8_AGAIN23, SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT); - memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24, - sizeof(out->antenna_gain.ghz5)); /* Extract cores power info info */ for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) { diff --git a/drivers/ssb/pcmcia.c b/drivers/ssb/pcmcia.c index c821c6b2a6a0..fbafed5b729b 100644 --- a/drivers/ssb/pcmcia.c +++ b/drivers/ssb/pcmcia.c @@ -676,14 +676,10 @@ static int ssb_pcmcia_do_get_invariants(struct pcmcia_device *p_dev, case SSB_PCMCIA_CIS_ANTGAIN: GOTO_ERROR_ON(tuple->TupleDataLen != 2, "antg tpl size"); - sprom->antenna_gain.ghz24.a0 = tuple->TupleData[1]; - sprom->antenna_gain.ghz24.a1 = tuple->TupleData[1]; - sprom->antenna_gain.ghz24.a2 = tuple->TupleData[1]; - sprom->antenna_gain.ghz24.a3 = tuple->TupleData[1]; - sprom->antenna_gain.ghz5.a0 = tuple->TupleData[1]; - sprom->antenna_gain.ghz5.a1 = tuple->TupleData[1]; - sprom->antenna_gain.ghz5.a2 = tuple->TupleData[1]; - sprom->antenna_gain.ghz5.a3 = tuple->TupleData[1]; + sprom->antenna_gain.a0 = tuple->TupleData[1]; + sprom->antenna_gain.a1 = tuple->TupleData[1]; + sprom->antenna_gain.a2 = tuple->TupleData[1]; + sprom->antenna_gain.a3 = tuple->TupleData[1]; break; case SSB_PCMCIA_CIS_BFLAGS: GOTO_ERROR_ON((tuple->TupleDataLen != 3) && diff --git a/drivers/ssb/sdio.c b/drivers/ssb/sdio.c index 63fd709038ca..b2d36f7736c5 100644 --- a/drivers/ssb/sdio.c +++ b/drivers/ssb/sdio.c @@ -551,14 +551,10 @@ int ssb_sdio_get_invariants(struct ssb_bus *bus, case SSB_SDIO_CIS_ANTGAIN: GOTO_ERROR_ON(tuple->size != 2, "antg tpl size"); - sprom->antenna_gain.ghz24.a0 = tuple->data[1]; - sprom->antenna_gain.ghz24.a1 = tuple->data[1]; - sprom->antenna_gain.ghz24.a2 = tuple->data[1]; - sprom->antenna_gain.ghz24.a3 = tuple->data[1]; - sprom->antenna_gain.ghz5.a0 = tuple->data[1]; - sprom->antenna_gain.ghz5.a1 = tuple->data[1]; - sprom->antenna_gain.ghz5.a2 = tuple->data[1]; - sprom->antenna_gain.ghz5.a3 = tuple->data[1]; + sprom->antenna_gain.a0 = tuple->data[1]; + sprom->antenna_gain.a1 = tuple->data[1]; + sprom->antenna_gain.a2 = tuple->data[1]; + sprom->antenna_gain.a3 = tuple->data[1]; break; case SSB_SDIO_CIS_BFLAGS: GOTO_ERROR_ON((tuple->size != 3) && diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index f1696213e3b5..1de56757e949 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -94,12 +94,7 @@ struct ssb_sprom { * on each band. Values in dBm/4 (Q5.2). Negative gain means the * loss in the connectors is bigger than the gain. */ struct { - struct { - s8 a0, a1, a2, a3; - } ghz24; /* 2.4GHz band */ - struct { - s8 a0, a1, a2, a3; - } ghz5; /* 5GHz band */ + s8 a0, a1, a2, a3; } antenna_gain; struct { -- cgit From 3b64e6f9050ff87a746c8e07dc04d97c7f26c5e2 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 28 Feb 2012 00:56:06 +0100 Subject: ssb: fix per path sprom vars On sprom version 4 and 5 there are 4 values for pa_2g, pa_5gl, pa_5g and pa_5gh, for sprom version 8 and 9 there are only 3. Make the per path sprom store also work for older sprom versions. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- include/linux/ssb/ssb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index 1de56757e949..49284192521b 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -19,7 +19,7 @@ struct ssb_driver; struct ssb_sprom_core_pwr_info { u8 itssi_2g, itssi_5g; u8 maxpwr_2g, maxpwr_5gl, maxpwr_5g, maxpwr_5gh; - u16 pa_2g[3], pa_5gl[3], pa_5g[3], pa_5gh[3]; + u16 pa_2g[4], pa_5gl[4], pa_5g[4], pa_5gh[4]; }; struct ssb_sprom { -- cgit From 03a5642b6a578d8de355b77efff49bbc45b8be6d Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 28 Feb 2012 00:56:07 +0100 Subject: ssb: add alpha2 This member contains the country code encoded with two chars Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- include/linux/ssb/ssb.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index 49284192521b..d658de45ffb9 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -33,6 +33,7 @@ struct ssb_sprom { u8 et1mdcport; /* MDIO for enet1 */ u16 board_rev; /* Board revision number from SPROM. */ u8 country_code; /* Country Code */ + char alpha2[2]; /* Country Code as two chars like EU or US */ u8 leddc_on_time; /* LED Powersave Duty Cycle On Count */ u8 leddc_off_time; /* LED Powersave Duty Cycle Off Count */ u8 ant_available_a; /* 2GHz antenna available bits (up to 4) */ -- cgit From 52aa63f5322aab23e07a52ff8ddd246d34799eb5 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 28 Feb 2012 00:56:08 +0100 Subject: ssb: add some missing sprom attributes This patch extends the sprom struct to contain all sprom attributes found in sprom version 1 to 9. This was done accordingly to the open source part of the Broadcom SDK. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- include/linux/ssb/ssb.h | 76 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index d658de45ffb9..d27683180025 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -32,6 +32,8 @@ struct ssb_sprom { u8 et0mdcport; /* MDIO for enet0 */ u8 et1mdcport; /* MDIO for enet1 */ u16 board_rev; /* Board revision number from SPROM. */ + u16 board_num; /* Board number from SPROM. */ + u16 board_type; /* Board type from SPROM. */ u8 country_code; /* Country Code */ char alpha2[2]; /* Country Code as two chars like EU or US */ u8 leddc_on_time; /* LED Powersave Duty Cycle On Count */ @@ -107,7 +109,79 @@ struct ssb_sprom { } ghz5; } fem; - /* TODO - add any parameters needed from rev 2, 3, 4, 5 or 8 SPROMs */ + u16 mcs2gpo[8]; + u16 mcs5gpo[8]; + u16 mcs5glpo[8]; + u16 mcs5ghpo[8]; + u8 opo; + + u8 rxgainerr2ga[3]; + u8 rxgainerr5gla[3]; + u8 rxgainerr5gma[3]; + u8 rxgainerr5gha[3]; + u8 rxgainerr5gua[3]; + + u8 noiselvl2ga[3]; + u8 noiselvl5gla[3]; + u8 noiselvl5gma[3]; + u8 noiselvl5gha[3]; + u8 noiselvl5gua[3]; + + u8 regrev; + u8 txchain; + u8 rxchain; + u8 antswitch; + u16 cddpo; + u16 stbcpo; + u16 bw40po; + u16 bwduppo; + + u8 tempthresh; + u8 tempoffset; + u16 rawtempsense; + u8 measpower; + u8 tempsense_slope; + u8 tempcorrx; + u8 tempsense_option; + u8 freqoffset_corr; + u8 iqcal_swp_dis; + u8 hw_iqcal_en; + u8 elna2g; + u8 elna5g; + u8 phycal_tempdelta; + u8 temps_period; + u8 temps_hysteresis; + u8 measpower1; + u8 measpower2; + u8 pcieingress_war; + + /* power per rate from sromrev 9 */ + u16 cckbw202gpo; + u16 cckbw20ul2gpo; + u32 legofdmbw202gpo; + u32 legofdmbw20ul2gpo; + u32 legofdmbw205glpo; + u32 legofdmbw20ul5glpo; + u32 legofdmbw205gmpo; + u32 legofdmbw20ul5gmpo; + u32 legofdmbw205ghpo; + u32 legofdmbw20ul5ghpo; + u32 mcsbw202gpo; + u32 mcsbw20ul2gpo; + u32 mcsbw402gpo; + u32 mcsbw205glpo; + u32 mcsbw20ul5glpo; + u32 mcsbw405glpo; + u32 mcsbw205gmpo; + u32 mcsbw20ul5gmpo; + u32 mcsbw405gmpo; + u32 mcsbw205ghpo; + u32 mcsbw20ul5ghpo; + u32 mcsbw405ghpo; + u16 mcs32po; + u16 legofdm40duppo; + u8 sar2g; + u8 sar5g; }; /* Information about the PCB the circuitry is soldered on. */ -- cgit From 1c9351cf2180924c91bb85e5ba607f24a3d875b1 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 28 Feb 2012 00:56:09 +0100 Subject: bcma: export bcma_find_core This function is needed by the bcm47xx arch code to get the number of the ieee80211 core. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/main.c | 3 ++- include/linux/bcma/bcma.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index b8379b90d045..7e138ec21357 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -61,7 +61,7 @@ static struct bus_type bcma_bus_type = { .dev_attrs = bcma_device_attrs, }; -static struct bcma_device *bcma_find_core(struct bcma_bus *bus, u16 coreid) +struct bcma_device *bcma_find_core(struct bcma_bus *bus, u16 coreid) { struct bcma_device *core; @@ -71,6 +71,7 @@ static struct bcma_device *bcma_find_core(struct bcma_bus *bus, u16 coreid) } return NULL; } +EXPORT_SYMBOL_GPL(bcma_find_core); static void bcma_release_core_dev(struct device *dev) { diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index b9f65fbee42f..46bbd088c4ad 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -284,6 +284,7 @@ static inline void bcma_maskset16(struct bcma_device *cc, bcma_write16(cc, offset, (bcma_read16(cc, offset) & mask) | set); } +extern struct bcma_device *bcma_find_core(struct bcma_bus *bus, u16 coreid); extern bool bcma_core_is_enabled(struct bcma_device *core); extern void bcma_core_disable(struct bcma_device *core, u32 flags); extern int bcma_core_enable(struct bcma_device *core, u32 flags); -- cgit From a027237a56f7d519eee5749cfb720e568d0bb0b6 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 28 Feb 2012 00:56:10 +0100 Subject: bcma: add support for sprom not found on the device On SoCs the sprom is stored in the nvram in a special partition on the flash chip. The nvram contains the sprom for the main bus, but sometimes also for a pci devices using bcma. This patch makes it possible for the arch code to register a function to fetch the needed sprom from the nvram and provide it to the bcma code. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/sprom.c | 77 ++++++++++++++++++++++++++++++++++++++++++----- include/linux/bcma/bcma.h | 6 ++++ 2 files changed, 75 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index ca7752510d5b..916ae255ff64 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -2,6 +2,8 @@ * Broadcom specific AMBA * SPROM reading * + * Copyright 2011, 2012, Hauke Mehrtens + * * Licensed under the GNU/GPL. See COPYING for details. */ @@ -14,6 +16,45 @@ #include #include +static int(*get_fallback_sprom)(struct bcma_bus *dev, struct ssb_sprom *out); + +/** + * bcma_arch_register_fallback_sprom - Registers a method providing a + * fallback SPROM if no SPROM is found. + * + * @sprom_callback: The callback function. + * + * With this function the architecture implementation may register a + * callback handler which fills the SPROM data structure. The fallback is + * used for PCI based BCMA devices, where no valid SPROM can be found + * in the shadow registers and to provide the SPROM for SoCs where BCMA is + * to controll the system bus. + * + * This function is useful for weird architectures that have a half-assed + * BCMA device hardwired to their PCI bus. + * + * This function is available for architecture code, only. So it is not + * exported. + */ +int bcma_arch_register_fallback_sprom(int (*sprom_callback)(struct bcma_bus *bus, + struct ssb_sprom *out)) +{ + if (get_fallback_sprom) + return -EEXIST; + get_fallback_sprom = sprom_callback; + + return 0; +} + +static int bcma_fill_sprom_with_fallback(struct bcma_bus *bus, + struct ssb_sprom *out) +{ + if (!get_fallback_sprom) + return -ENOENT; + + return get_fallback_sprom(bus, out); +} + /************************************************** * R/W ops. **************************************************/ @@ -246,23 +287,43 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom) SSB_SROM8_FEM_ANTSWLUT_SHIFT); } +static bool bcma_is_sprom_available(struct bcma_bus *bus) +{ + u32 sromctrl; + + if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM)) + return false; + + if (bus->drv_cc.core->id.rev >= 32) { + sromctrl = bcma_read32(bus->drv_cc.core, BCMA_CC_SROM_CONTROL); + return sromctrl & BCMA_CC_SROM_CONTROL_PRESENT; + } + return true; +} + int bcma_sprom_get(struct bcma_bus *bus) { u16 offset; u16 *sprom; - u32 sromctrl; int err = 0; if (!bus->drv_cc.core) return -EOPNOTSUPP; - if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM)) - return -ENOENT; - - if (bus->drv_cc.core->id.rev >= 32) { - sromctrl = bcma_read32(bus->drv_cc.core, BCMA_CC_SROM_CONTROL); - if (!(sromctrl & BCMA_CC_SROM_CONTROL_PRESENT)) - return -ENOENT; + if (!bcma_is_sprom_available(bus)) { + /* + * Maybe there is no SPROM on the device? + * Now we ask the arch code if there is some sprom + * available for this device in some other storage. + */ + err = bcma_fill_sprom_with_fallback(bus, &bus->sprom); + if (err) { + pr_warn("Using fallback SPROM failed (err %d)\n", err); + } else { + pr_debug("Using SPROM revision %d provided by" + " platform.\n", bus->sprom.revision); + return 0; + } } sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 46bbd088c4ad..5af9a075498f 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -176,6 +176,12 @@ int __bcma_driver_register(struct bcma_driver *drv, struct module *owner); extern void bcma_driver_unregister(struct bcma_driver *drv); +/* Set a fallback SPROM. + * See kdoc at the function definition for complete documentation. */ +extern int bcma_arch_register_fallback_sprom( + int (*sprom_callback)(struct bcma_bus *bus, + struct ssb_sprom *out)); + struct bcma_bus { /* The MMIO area. */ void __iomem *mmio; -- cgit From 5533513784a88049e19dd2ab380a452b61e5171e Mon Sep 17 00:00:00 2001 From: Ashok Nagarajan Date: Tue, 28 Feb 2012 17:04:08 -0800 Subject: {nl,cfg,mac}80211: Implement RSSI threshold for mesh peering Mesh peer links are established only if average rssi of the peer candidate satisfies the threshold. This is not in 802.11s specification but was requested by David Fulgham, an open80211s user. This is a way to avoid marginal peer links with stations that are barely within range. This patch adds a new mesh configuration parameter, mesh_rssi_threshold. This feature is supported only for hardwares that report signal in dBm. Signed-off-by: Ashok Nagarajan Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- include/linux/nl80211.h | 5 +++++ include/net/cfg80211.h | 1 + net/mac80211/cfg.c | 8 ++++++++ net/mac80211/debugfs_netdev.c | 2 ++ net/mac80211/mesh_plink.c | 16 +++++++++++++++- net/wireless/mesh.c | 3 +++ net/wireless/nl80211.c | 5 +++++ 7 files changed, 39 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index be35a68746a7..38fda5ee57f5 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -2112,6 +2112,10 @@ enum nl80211_mntr_flags { * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding * or forwarding entity (default is TRUE - forwarding entity) * + * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the + * threshold for average signal strength of candidate station to establish + * a peer link. + * * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use @@ -2137,6 +2141,7 @@ enum nl80211_meshconf_params { NL80211_MESHCONF_GATE_ANNOUNCEMENTS, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, NL80211_MESHCONF_FORWARDING, + NL80211_MESHCONF_RSSI_THRESHOLD, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0178c7489373..b4e015c90885 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -809,6 +809,7 @@ struct mesh_config { * Still keeping the same nomenclature to be in sync with the spec. */ bool dot11MeshGateAnnouncementProtocol; bool dot11MeshForwarding; + s32 rssi_threshold; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 6a77d4c910f9..ab31cc56a2fb 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1314,6 +1314,14 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, } if (_chg_mesh_attr(NL80211_MESHCONF_FORWARDING, mask)) conf->dot11MeshForwarding = nconf->dot11MeshForwarding; + if (_chg_mesh_attr(NL80211_MESHCONF_RSSI_THRESHOLD, mask)) { + /* our RSSI threshold implementation is supported only for + * devices that report signal in dBm. + */ + if (!(sdata->local->hw.flags & IEEE80211_HW_SIGNAL_DBM)) + return -ENOTSUPP; + conf->rssi_threshold = nconf->rssi_threshold; + } return 0; } diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 510ed1dab3c7..9f484d8905fd 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -443,6 +443,7 @@ IEEE80211_IF_FILE(dot11MeshGateAnnouncementProtocol, IEEE80211_IF_FILE(dot11MeshHWMPRannInterval, u.mesh.mshcfg.dot11MeshHWMPRannInterval, DEC); IEEE80211_IF_FILE(dot11MeshForwarding, u.mesh.mshcfg.dot11MeshForwarding, DEC); +IEEE80211_IF_FILE(rssi_threshold, u.mesh.mshcfg.rssi_threshold, DEC); #endif @@ -581,6 +582,7 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata) MESHPARAMS_ADD(dot11MeshHWMPRootMode); MESHPARAMS_ADD(dot11MeshHWMPRannInterval); MESHPARAMS_ADD(dot11MeshGateAnnouncementProtocol); + MESHPARAMS_ADD(rssi_threshold); #undef MESHPARAMS_ADD } #endif diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 8806e5ef8ffe..80ce52772538 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -31,6 +31,11 @@ #define dot11MeshHoldingTimeout(s) (s->u.mesh.mshcfg.dot11MeshHoldingTimeout) #define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks) +#define sta_meets_rssi_threshold(sta, sdata) \ + (sdata->u.mesh.mshcfg.rssi_threshold == 0 ||\ + (s8) -ewma_read(&sta->avg_signal) > \ + sdata->u.mesh.mshcfg.rssi_threshold) + enum plink_event { PLINK_UNDEFINED, OPN_ACPT, @@ -301,7 +306,8 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates, if (mesh_peer_accepts_plinks(elems) && sta->plink_state == NL80211_PLINK_LISTEN && sdata->u.mesh.accepting_plinks && - sdata->u.mesh.mshcfg.auto_open_plinks) + sdata->u.mesh.mshcfg.auto_open_plinks && + sta_meets_rssi_threshold(sta, sdata)) mesh_plink_open(sta); rcu_read_unlock(); @@ -531,6 +537,14 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m return; } + if (ftype == WLAN_SP_MESH_PEERING_OPEN && + !sta_meets_rssi_threshold(sta, sdata)) { + mpl_dbg("Mesh plink: %pM does not meet rssi threshold\n", + sta->sta.addr); + rcu_read_unlock(); + return; + } + if (sta && !test_sta_flag(sta, WLAN_STA_AUTH)) { mpl_dbg("Mesh plink: Action frame from non-authed peer\n"); rcu_read_unlock(); diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 9d3e3b6bfcf4..ba21ab22187b 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -23,6 +23,8 @@ #define MESH_PERR_MIN_INT 100 #define MESH_DIAM_TRAVERSAL_TIME 50 +#define MESH_RSSI_THRESHOLD 0 + /* * A path will be refreshed if it is used PATH_REFRESH_TIME milliseconds * before timing out. This way it will remain ACTIVE and no data frames @@ -56,6 +58,7 @@ const struct mesh_config default_mesh_config = { .dot11MeshHWMPRannInterval = MESH_RANN_INTERVAL, .dot11MeshGateAnnouncementProtocol = false, .dot11MeshForwarding = true, + .rssi_threshold = MESH_RSSI_THRESHOLD, }; const struct mesh_setup default_mesh_setup = { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1998c3682774..25a470abd21d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3290,6 +3290,8 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, cur_params.dot11MeshGateAnnouncementProtocol); NLA_PUT_U8(msg, NL80211_MESHCONF_FORWARDING, cur_params.dot11MeshForwarding); + NLA_PUT_U32(msg, NL80211_MESHCONF_RSSI_THRESHOLD, + cur_params.rssi_threshold); nla_nest_end(msg, pinfoattr); genlmsg_end(msg, hdr); return genlmsg_reply(msg, info); @@ -3322,6 +3324,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 }, [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 }, [NL80211_MESHCONF_FORWARDING] = { .type = NLA_U8 }, + [NL80211_MESHCONF_RSSI_THRESHOLD] = { .type = NLA_U32}, }; static const struct nla_policy @@ -3413,6 +3416,8 @@ do {\ nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, mask, NL80211_MESHCONF_FORWARDING, nla_get_u8); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, + mask, NL80211_MESHCONF_RSSI_THRESHOLD, nla_get_u32); if (mask_out) *mask_out = mask; -- cgit From c04a4ff71b6a59cb5c8deec961b9196226e89573 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 1 Mar 2012 15:28:19 +0100 Subject: cfg80211: fix kernel-doc I forgot to update the kernel-doc in my patch to redesign AP mode APIs, fix that now. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index b4e015c90885..d640a39d9513 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1357,12 +1357,10 @@ struct cfg80211_gtk_rekey_data { * * @set_rekey_data: give the data necessary for GTK rekeying to the driver * - * @add_beacon: Add a beacon with given parameters, @head, @interval - * and @dtim_period will be valid, @tail is optional. - * @set_beacon: Change the beacon parameters for an access point mode - * interface. This should reject the call when no beacon has been - * configured. - * @del_beacon: Remove beacon configuration and stop sending the beacon. + * @start_ap: Start acting in AP mode defined by the parameters. + * @change_beacon: Change the beacon parameters for an access point mode + * interface. This should reject the call when AP mode wasn't started. + * @stop_ap: Stop being an AP, including stopping beaconing. * * @add_station: Add a new station. * @del_station: Remove a station; @mac may be NULL to remove all stations. -- cgit From fe8431f89e25de722610ee5beb2892bd019d1fed Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 1 Mar 2012 18:00:07 +0100 Subject: mac80211: add an rx flag for ignoring a packet's signal strength For A-MPDU rx it makes sense to only process the signal strength once per aggregate instead of once per subframe. Additonally, some hardware (e.g. Atheros) only provides valid signal strength information for the last subframe. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- include/net/mac80211.h | 3 +++ net/mac80211/rx.c | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 7477f020ee7a..c06974accfa6 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -659,6 +659,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index * @RX_FLAG_40MHZ: HT40 (40 MHz) was used * @RX_FLAG_SHORT_GI: Short guard interval was used + * @RX_FLAG_NO_SIGNAL_VAL: The signal strength value is not present. + * Valid only for data frames (mainly A-MPDU) */ enum mac80211_rx_flags { RX_FLAG_MMIC_ERROR = 1<<0, @@ -672,6 +674,7 @@ enum mac80211_rx_flags { RX_FLAG_HT = 1<<9, RX_FLAG_40MHZ = 1<<10, RX_FLAG_SHORT_GI = 1<<11, + RX_FLAG_NO_SIGNAL_VAL = 1<<12, }; /** diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 3cf011fc97f4..f3b515d16f24 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -177,7 +177,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, pos += 2; /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */ - if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) { + if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM && + !(status->flag & RX_FLAG_NO_SIGNAL_VAL)) { *pos = status->signal; rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL); @@ -1309,8 +1310,10 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) sta->rx_fragments++; sta->rx_bytes += rx->skb->len; - sta->last_signal = status->signal; - ewma_add(&sta->avg_signal, -status->signal); + if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) { + sta->last_signal = status->signal; + ewma_add(&sta->avg_signal, -status->signal); + } /* * Change STA power saving mode only at the end of a frame -- cgit From 77a1abf54f4b003ad6e59c535045b2ad89fedfeb Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 5 Mar 2012 04:50:09 +0000 Subject: net: export netdev_stats_to_stats64 Some drivers use internal netdev stats member to store part of their stats, yet advertize ndo_get_stats64() to implement some 64bit fields. Allow them to use netdev_stats_to_stats64() helper to make the copy of netdev stats before they compute their 64bit counters. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 ++ net/core/dev.c | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f1b7d037c2c5..4d279c5287f8 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2557,6 +2557,8 @@ extern void dev_load(struct net *net, const char *name); extern void dev_mcast_init(void); extern struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage); +extern void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, + const struct net_device_stats *netdev_stats); extern int netdev_max_backlog; extern int netdev_tstamp_prequeue; diff --git a/net/core/dev.c b/net/core/dev.c index 763a0eda7158..5ef3b65c3687 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5834,12 +5834,12 @@ void netdev_run_todo(void) /* Convert net_device_stats to rtnl_link_stats64. They have the same * fields in the same order, with only the type differing. */ -static void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, - const struct net_device_stats *netdev_stats) +void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, + const struct net_device_stats *netdev_stats) { #if BITS_PER_LONG == 64 - BUILD_BUG_ON(sizeof(*stats64) != sizeof(*netdev_stats)); - memcpy(stats64, netdev_stats, sizeof(*stats64)); + BUILD_BUG_ON(sizeof(*stats64) != sizeof(*netdev_stats)); + memcpy(stats64, netdev_stats, sizeof(*stats64)); #else size_t i, n = sizeof(*stats64) / sizeof(u64); const unsigned long *src = (const unsigned long *)netdev_stats; @@ -5851,6 +5851,7 @@ static void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, dst[i] = src[i]; #endif } +EXPORT_SYMBOL(netdev_stats_to_stats64); /** * dev_get_stats - get network device statistics -- cgit From 1b658f118b11de3c4052ed8cbdd5803cd1fa5670 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 2 Mar 2012 15:50:02 +0530 Subject: cfg80211: Add an attribute to set inactivity timeout in AP mode This patch adds an attribute, NL80211_ATTR_INACTIVITY_TIMEOUT, to set the inactivity timeout which can be used to remove the station in AP mode. This can be passed in NL80211_CMD_START_AP and used by the drivers which have AP MLME in firmware but don't support get_station() properly. To disable inactivity timer in userspace, wpa_s for example, there is a new flag, NL80211_FEATURE_INACTIVITY_TIMER, in nl80211_feature_flags through which drivers can register their capability to use the inactivity timeout to free the stations. Signed-off-by: Vasanthakumar Thiagarajan Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 15 +++++++++++++-- include/net/cfg80211.h | 2 ++ net/wireless/nl80211.c | 8 ++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 38fda5ee57f5..9f46c62b1eee 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -168,8 +168,8 @@ * %NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID, * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE, * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, - * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY and - * %NL80211_ATTR_AUTH_TYPE. + * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, + * %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT. * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP @@ -1197,6 +1197,12 @@ enum nl80211_commands { * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of * up to 16 TIDs. * + * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be + * used by the drivers which has MLME in firmware and does not have support + * to report per station tx/rx activity to free up the staion entry from + * the list. This needs to be used when the driver advertises the + * capability to timeout the stations. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1442,6 +1448,8 @@ enum nl80211_attrs { NL80211_ATTR_NOACK_MAP, + NL80211_ATTR_INACTIVITY_TIMEOUT, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2809,10 +2817,13 @@ enum nl80211_ap_sme_features { * TX status to the socket error queue when requested with the * socket option. * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates. + * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up + * the connected inactive stations in AP mode. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, NL80211_FEATURE_HT_IBSS = 1 << 1, + NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, }; /** diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index d640a39d9513..804805827736 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -413,6 +413,7 @@ struct cfg80211_beacon_data { * @crypto: crypto settings * @privacy: the BSS uses privacy * @auth_type: Authentication type (algorithm) + * @inactivity_timeout: time in seconds to determine station's inactivity. */ struct cfg80211_ap_settings { struct cfg80211_beacon_data beacon; @@ -424,6 +425,7 @@ struct cfg80211_ap_settings { struct cfg80211_crypto_settings crypto; bool privacy; enum nl80211_auth_type auth_type; + int inactivity_timeout; }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 25a470abd21d..4b6afc338aac 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -204,6 +204,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { .len = NL80211_HT_CAPABILITY_LEN }, [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 }, + [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 }, }; /* policy for the key attributes */ @@ -2214,6 +2215,13 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) if (err) return err; + if (info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]) { + if (!(rdev->wiphy.features & NL80211_FEATURE_INACTIVITY_TIMER)) + return -EOPNOTSUPP; + params.inactivity_timeout = nla_get_u16( + info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]); + } + err = rdev->ops->start_ap(&rdev->wiphy, dev, ¶ms); if (!err) wdev->beacon_interval = params.beacon_interval; -- cgit From 804483e90794256f9ed53e795ffbf1e94de237c8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 5 Mar 2012 22:18:41 +0100 Subject: cfg80211/mac80211: report signal strength for mgmt frames Add the signal strength (in dBm only for now) to frames that are received via nl80211's various frame APIs. Signed-off-by: Johannes Berg Acked-by: Kalle Valo Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath6kl/wmi.c | 6 ++++-- include/linux/nl80211.h | 6 ++++++ include/net/cfg80211.h | 8 +++++--- net/mac80211/rx.c | 13 +++++++++++-- net/wireless/mlme.c | 7 ++++--- net/wireless/nl80211.c | 9 +++++++-- net/wireless/nl80211.h | 3 ++- 7 files changed, 39 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 18fa9aa8af92..97abf4699b41 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -556,7 +556,8 @@ static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len, dlen, freq, vif->probe_req_report); if (vif->probe_req_report || vif->nw_type == AP_NETWORK) - cfg80211_rx_mgmt(vif->ndev, freq, ev->data, dlen, GFP_ATOMIC); + cfg80211_rx_mgmt(vif->ndev, freq, 0, + ev->data, dlen, GFP_ATOMIC); return 0; } @@ -595,7 +596,8 @@ static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len, return -EINVAL; } ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq); - cfg80211_rx_mgmt(vif->ndev, freq, ev->data, dlen, GFP_ATOMIC); + cfg80211_rx_mgmt(vif->ndev, freq, 0, + ev->data, dlen, GFP_ATOMIC); return 0; } diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 9f46c62b1eee..c37d67add090 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1203,6 +1203,10 @@ enum nl80211_commands { * the list. This needs to be used when the driver advertises the * capability to timeout the stations. * + * @NL80211_ATTR_RX_SIGNAL_DBM: signal strength in dBm (as a 32-bit int); + * this attribute is (depending on the driver capabilities) added to + * received frames indicated with %NL80211_CMD_FRAME. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1450,6 +1454,8 @@ enum nl80211_attrs { NL80211_ATTR_INACTIVITY_TIMEOUT, + NL80211_ATTR_RX_SIGNAL_DBM, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 804805827736..4682c35a92c4 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3189,6 +3189,7 @@ void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp); * cfg80211_rx_mgmt - notification of received, unprocessed management frame * @dev: network device * @freq: Frequency on which the frame was received in MHz + * @sig_dbm: signal strength in mBm, or 0 if unknown * @buf: Management frame (header + body) * @len: length of the frame data * @gfp: context flags @@ -3201,8 +3202,8 @@ void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp); * This function is called whenever an Action frame is received for a station * mode interface, but is not processed in kernel. */ -bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf, - size_t len, gfp_t gfp); +bool cfg80211_rx_mgmt(struct net_device *dev, int freq, int sig_dbm, + const u8 *buf, size_t len, gfp_t gfp); /** * cfg80211_mgmt_tx_status - notification of TX status for management frame @@ -3315,6 +3316,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, * @frame: the frame * @len: length of the frame * @freq: frequency the frame was received on + * @sig_dbm: signal strength in mBm, or 0 if unknown * @gfp: allocation flags * * Use this function to report to userspace when a beacon was @@ -3323,7 +3325,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, */ void cfg80211_report_obss_beacon(struct wiphy *wiphy, const u8 *frame, size_t len, - int freq, gfp_t gfp); + int freq, int sig_dbm, gfp_t gfp); /* * cfg80211_can_beacon_sec_chan - test if ht40 on extension channel can be used diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 10bbbd33b314..5f6e32ca0858 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2188,9 +2188,14 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx) if (rx->sdata->vif.type == NL80211_IFTYPE_AP && ieee80211_is_beacon(mgmt->frame_control) && !(rx->flags & IEEE80211_RX_BEACON_REPORTED)) { + int sig = 0; + + if (rx->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) + sig = status->signal; + cfg80211_report_obss_beacon(rx->local->hw.wiphy, rx->skb->data, rx->skb->len, - status->freq, GFP_ATOMIC); + status->freq, sig, GFP_ATOMIC); rx->flags |= IEEE80211_RX_BEACON_REPORTED; } @@ -2414,6 +2419,7 @@ static ieee80211_rx_result debug_noinline ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx) { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); + int sig = 0; /* skip known-bad action frames and return them in the next handler */ if (status->rx_flags & IEEE80211_RX_MALFORMED_ACTION_FRM) @@ -2426,7 +2432,10 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx) * it transmitted were processed or returned. */ - if (cfg80211_rx_mgmt(rx->sdata->dev, status->freq, + if (rx->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) + sig = status->signal; + + if (cfg80211_rx_mgmt(rx->sdata->dev, status->freq, sig, rx->skb->data, rx->skb->len, GFP_ATOMIC)) { if (rx->sta) diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index fb1e72179117..f5a7ac3a0939 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -814,8 +814,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, cookie); } -bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf, - size_t len, gfp_t gfp) +bool cfg80211_rx_mgmt(struct net_device *dev, int freq, int sig_mbm, + const u8 *buf, size_t len, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -854,7 +854,8 @@ bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf, /* found match! */ /* Indicate the received Action frame to user space */ - if (nl80211_send_mgmt(rdev, dev, reg->nlpid, freq, + if (nl80211_send_mgmt(rdev, dev, reg->nlpid, + freq, sig_mbm, buf, len, gfp)) continue; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4b6afc338aac..39dbdf2adb12 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7686,7 +7686,8 @@ bool nl80211_unexpected_4addr_frame(struct net_device *dev, int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, struct net_device *netdev, u32 nlpid, - int freq, const u8 *buf, size_t len, gfp_t gfp) + int freq, int sig_dbm, + const u8 *buf, size_t len, gfp_t gfp) { struct sk_buff *msg; void *hdr; @@ -7704,6 +7705,8 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + if (sig_dbm) + NLA_PUT_U32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm); NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); genlmsg_end(msg, hdr); @@ -7965,7 +7968,7 @@ EXPORT_SYMBOL(cfg80211_probe_status); void cfg80211_report_obss_beacon(struct wiphy *wiphy, const u8 *frame, size_t len, - int freq, gfp_t gfp) + int freq, int sig_dbm, gfp_t gfp) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct sk_buff *msg; @@ -7988,6 +7991,8 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy, NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); if (freq) NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + if (sig_dbm) + NLA_PUT_U32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm); NLA_PUT(msg, NL80211_ATTR_FRAME, len, frame); genlmsg_end(msg, hdr); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 12bf4d185abe..4ffe50df9f31 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -92,7 +92,8 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, gfp_t gfp); int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u32 nlpid, int freq, + struct net_device *netdev, u32 nlpid, + int freq, int sig_dbm, const u8 *buf, size_t len, gfp_t gfp); void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev, struct net_device *netdev, u64 cookie, -- cgit From 8097e1494459a4f9cdbaba7440334d9bd11a39f0 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Mon, 5 Mar 2012 15:31:47 -0800 Subject: cfg80211: expose cfg80211_calculate_bitrate() Signed-off-by: Thomas Pedersen Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 8 ++++++++ net/wireless/util.c | 1 + 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 4682c35a92c4..57c9fddc2acf 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3337,6 +3337,14 @@ int cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type); +/* + * cfg80211_calculate_bitrate - calculate actual bitrate (in 100Kbps units) + * @rate: given rate_info to calculate bitrate from + * + * return 0 if MCS index >= 32 + */ +u16 cfg80211_calculate_bitrate(struct rate_info *rate); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/net/wireless/util.c b/net/wireless/util.c index 9aa9db6c8141..1b7a08df933c 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -904,6 +904,7 @@ u16 cfg80211_calculate_bitrate(struct rate_info *rate) /* do NOT round down here */ return (bitrate + 50000) / 100000; } +EXPORT_SYMBOL(cfg80211_calculate_bitrate); int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, u32 beacon_int) -- cgit From 10d8493cd9efd38b1947b7a74276dbdc8311aa1a Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 6 Mar 2012 15:50:48 +0100 Subject: bcma: add support for on-chip OTP memory used for SPROM storage Wireless Broadcom chips can have either their SPROM data stored on either external SPROM or on-chip OTP memory. Both are accessed through the same register space. This patch adds support for the on-chip OTP memory. Tested with: BCM43224 OTP and SPROM BCM4331 SPROM BCM4313 OTP This patch is in response to linux-wireless thread [1]. [1] http://article.gmane.org/gmane.linux.kernel.wireless.general/85426 Tested-by: Saul St. John Tested-by: Rafal Milecki Tested-by: Hauke Mehrtens Cc: Larry Finger Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/bcma/sprom.c | 126 +++++++++++++++++++++++----- include/linux/bcma/bcma_driver_chipcommon.h | 10 ++- 2 files changed, 115 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index fba8066857d2..cdcf75c0954f 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -300,37 +300,128 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom) SSB_SROM8_FEM_ANTSWLUT_SHIFT); } -static bool bcma_is_sprom_available(struct bcma_bus *bus) +/* + * Indicates the presence of external SPROM. + */ +static bool bcma_sprom_ext_available(struct bcma_bus *bus) { - u32 sromctrl; + u32 chip_status; + u32 srom_control; + u32 present_mask; - if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM)) - return false; + if (bus->drv_cc.core->id.rev >= 31) { + if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM)) + return false; - if (bus->drv_cc.core->id.rev >= 32) { - sromctrl = bcma_read32(bus->drv_cc.core, BCMA_CC_SROM_CONTROL); - return sromctrl & BCMA_CC_SROM_CONTROL_PRESENT; + srom_control = bcma_read32(bus->drv_cc.core, + BCMA_CC_SROM_CONTROL); + return srom_control & BCMA_CC_SROM_CONTROL_PRESENT; } - return true; + + /* older chipcommon revisions use chip status register */ + chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT); + switch (bus->chipinfo.id) { + case 0x4313: + present_mask = BCMA_CC_CHIPST_4313_SPROM_PRESENT; + break; + + case 0x4331: + present_mask = BCMA_CC_CHIPST_4331_SPROM_PRESENT; + break; + + default: + return true; + } + + return chip_status & present_mask; +} + +/* + * Indicates that on-chip OTP memory is present and enabled. + */ +static bool bcma_sprom_onchip_available(struct bcma_bus *bus) +{ + u32 chip_status; + u32 otpsize = 0; + bool present; + + chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT); + switch (bus->chipinfo.id) { + case 0x4313: + present = chip_status & BCMA_CC_CHIPST_4313_OTP_PRESENT; + break; + + case 0x4331: + present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT; + break; + + case 43224: + case 43225: + /* for these chips OTP is always available */ + present = true; + break; + + default: + present = false; + break; + } + + if (present) { + otpsize = bus->drv_cc.capabilities & BCMA_CC_CAP_OTPS; + otpsize >>= BCMA_CC_CAP_OTPS_SHIFT; + } + + return otpsize != 0; +} + +/* + * Verify OTP is filled and determine the byte + * offset where SPROM data is located. + * + * On error, returns 0; byte offset otherwise. + */ +static int bcma_sprom_onchip_offset(struct bcma_bus *bus) +{ + struct bcma_device *cc = bus->drv_cc.core; + u32 offset; + + /* verify OTP status */ + if ((bcma_read32(cc, BCMA_CC_OTPS) & BCMA_CC_OTPS_GU_PROG_HW) == 0) + return 0; + + /* obtain bit offset from otplayout register */ + offset = (bcma_read32(cc, BCMA_CC_OTPL) & BCMA_CC_OTPL_GURGN_OFFSET); + return BCMA_CC_SPROM + (offset >> 3); } int bcma_sprom_get(struct bcma_bus *bus) { - u16 offset; + u16 offset = BCMA_CC_SPROM; u16 *sprom; int err = 0; if (!bus->drv_cc.core) return -EOPNOTSUPP; - if (!bcma_is_sprom_available(bus)) { + if (!bcma_sprom_ext_available(bus)) { /* - * Maybe there is no SPROM on the device? - * Now we ask the arch code if there is some sprom - * available for this device in some other storage. + * External SPROM takes precedence so check + * on-chip OTP only when no external SPROM + * is present. */ - err = bcma_fill_sprom_with_fallback(bus, &bus->sprom); - return err; + if (bcma_sprom_onchip_available(bus)) { + /* determine offset */ + offset = bcma_sprom_onchip_offset(bus); + } + if (!offset) { + /* + * Maybe there is no SPROM on the device? + * Now we ask the arch code if there is some sprom + * available for this device in some other storage. + */ + err = bcma_fill_sprom_with_fallback(bus, &bus->sprom); + return err; + } } sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), @@ -341,11 +432,6 @@ int bcma_sprom_get(struct bcma_bus *bus) if (bus->chipinfo.id == 0x4331) bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false); - /* Most cards have SPROM moved by additional offset 0x30 (48 dwords). - * According to brcm80211 this applies to cards with PCIe rev >= 6 - * TODO: understand this condition and use it */ - offset = (bus->chipinfo.id == 0x4331) ? BCMA_CC_SPROM : - BCMA_CC_SPROM_PCIE6; pr_debug("SPROM offset 0x%x\n", offset); bcma_sprom_read(bus, offset, sprom); diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index e72938b10714..8bbfe31fbac8 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -56,6 +56,9 @@ #define BCMA_CC_OTPS_HW_PROTECT 0x00000001 #define BCMA_CC_OTPS_SW_PROTECT 0x00000002 #define BCMA_CC_OTPS_CID_PROTECT 0x00000004 +#define BCMA_CC_OTPS_GU_PROG_IND 0x00000F00 /* General Use programmed indication */ +#define BCMA_CC_OTPS_GU_PROG_IND_SHIFT 8 +#define BCMA_CC_OTPS_GU_PROG_HW 0x00000100 /* HW region programmed */ #define BCMA_CC_OTPC 0x0014 /* OTP control */ #define BCMA_CC_OTPC_RECWAIT 0xFF000000 #define BCMA_CC_OTPC_PROGWAIT 0x00FFFF00 @@ -72,6 +75,8 @@ #define BCMA_CC_OTPP_READ 0x40000000 #define BCMA_CC_OTPP_START 0x80000000 #define BCMA_CC_OTPP_BUSY 0x80000000 +#define BCMA_CC_OTPL 0x001C /* OTP layout */ +#define BCMA_CC_OTPL_GURGN_OFFSET 0x00000FFF /* offset of general use region */ #define BCMA_CC_IRQSTAT 0x0020 #define BCMA_CC_IRQMASK 0x0024 #define BCMA_CC_IRQ_GPIO 0x00000001 /* gpio intr */ @@ -79,6 +84,10 @@ #define BCMA_CC_IRQ_WDRESET 0x80000000 /* watchdog reset occurred */ #define BCMA_CC_CHIPCTL 0x0028 /* Rev >= 11 only */ #define BCMA_CC_CHIPSTAT 0x002C /* Rev >= 11 only */ +#define BCMA_CC_CHIPST_4313_SPROM_PRESENT 1 +#define BCMA_CC_CHIPST_4313_OTP_PRESENT 2 +#define BCMA_CC_CHIPST_4331_SPROM_PRESENT 2 +#define BCMA_CC_CHIPST_4331_OTP_PRESENT 4 #define BCMA_CC_JCMD 0x0030 /* Rev >= 10 only */ #define BCMA_CC_JCMD_START 0x80000000 #define BCMA_CC_JCMD_BUSY 0x80000000 @@ -256,7 +265,6 @@ #define BCMA_CC_PLLCTL_ADDR 0x0660 #define BCMA_CC_PLLCTL_DATA 0x0664 #define BCMA_CC_SPROM 0x0800 /* SPROM beginning */ -#define BCMA_CC_SPROM_PCIE6 0x0830 /* SPROM beginning on PCIe rev >= 6 */ /* Divider allocation in 4716/47162/5356 */ #define BCMA_CC_PMU5_MAINPLL_CPU 1 -- cgit From c970a1ac4e75a5d31c7b6e8e9f0bb192b0a511e7 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Mon, 5 Mar 2012 01:03:34 +0100 Subject: NFC: Add device powered netlink attribute For user space to know if a device is up or down. Signed-off-by: Samuel Ortiz Signed-off-by: John W. Linville --- include/linux/nfc.h | 1 + net/nfc/netlink.c | 3 +++ 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/nfc.h b/include/linux/nfc.h index b4999abcb2a2..39c1fcf089c0 100644 --- a/include/linux/nfc.h +++ b/include/linux/nfc.h @@ -107,6 +107,7 @@ enum nfc_attrs { NFC_ATTR_TARGET_SENSF_RES, NFC_ATTR_COMM_MODE, NFC_ATTR_RF_MODE, + NFC_ATTR_DEVICE_POWERED, /* private: internal use only */ __NFC_ATTR_AFTER_LAST }; diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 07f0348aabf5..a1388e4efd6f 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -48,6 +48,7 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = { [NFC_ATTR_PROTOCOLS] = { .type = NLA_U32 }, [NFC_ATTR_COMM_MODE] = { .type = NLA_U8 }, [NFC_ATTR_RF_MODE] = { .type = NLA_U8 }, + [NFC_ATTR_DEVICE_POWERED] = { .type = NLA_U8 }, }; static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target, @@ -200,6 +201,7 @@ int nfc_genl_device_added(struct nfc_dev *dev) NLA_PUT_STRING(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)); NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx); NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols); + NLA_PUT_U8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up); genlmsg_end(msg, hdr); @@ -261,6 +263,7 @@ static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev, NLA_PUT_STRING(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)); NLA_PUT_U32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx); NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols); + NLA_PUT_U8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up); return genlmsg_end(msg, hdr); -- cgit From 47807d3dbb62e93850cbcb797db1a9ee1806f986 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Mon, 5 Mar 2012 01:03:50 +0100 Subject: NFC: Remove the rf mode parameter from the DEP link up routine When calling nfc_dep_link_up, we implicitely are in initiator mode. Which means we also can provide the general bytes as a function argument, as all drivers will eventually request them. Signed-off-by: Samuel Ortiz Signed-off-by: John W. Linville --- drivers/nfc/pn533.c | 20 +++++--------------- include/net/nfc/nfc.h | 6 ++---- net/nfc/core.c | 22 +++++++++++----------- net/nfc/llcp/llcp.c | 2 +- net/nfc/netlink.c | 11 +++-------- net/nfc/nfc.h | 5 ++--- 6 files changed, 24 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index ef33f64143b5..cb6204f78300 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -1340,21 +1340,15 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg, } static int pn533_dep_link_up(struct nfc_dev *nfc_dev, int target_idx, - u8 comm_mode, u8 rf_mode) + u8 comm_mode, u8* gb, size_t gb_len) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); struct pn533_cmd_jump_dep *cmd; - u8 cmd_len, local_gt_len, *local_gt; + u8 cmd_len; int rc; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - if (rf_mode == NFC_RF_TARGET) { - nfc_dev_err(&dev->interface->dev, "Target mode not supported"); - return -EOPNOTSUPP; - } - - if (dev->poll_mod_count) { nfc_dev_err(&dev->interface->dev, "Cannot bring the DEP link up while polling"); @@ -1367,11 +1361,7 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, int target_idx, return -EBUSY; } - local_gt = nfc_get_local_general_bytes(dev->nfc_dev, &local_gt_len); - if (local_gt_len > NFC_MAX_GT_LEN) - return -EINVAL; - - cmd_len = sizeof(struct pn533_cmd_jump_dep) + local_gt_len; + cmd_len = sizeof(struct pn533_cmd_jump_dep) + gb_len; cmd = kzalloc(cmd_len, GFP_KERNEL); if (cmd == NULL) return -ENOMEM; @@ -1380,9 +1370,9 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, int target_idx, cmd->active = !comm_mode; cmd->baud = 0; - if (local_gt != NULL) { + if (gb != NULL && gb_len > 0) { cmd->next = 4; /* We have some Gi */ - memcpy(cmd->gt, local_gt, local_gt_len); + memcpy(cmd->gt, gb, gb_len); } else { cmd->next = 0; } diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index d253278e5a96..d608e4eac66f 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -53,8 +53,8 @@ struct nfc_ops { int (*dev_down)(struct nfc_dev *dev); int (*start_poll)(struct nfc_dev *dev, u32 protocols); void (*stop_poll)(struct nfc_dev *dev); - int (*dep_link_up)(struct nfc_dev *dev, int target_idx, - u8 comm_mode, u8 rf_mode); + int (*dep_link_up)(struct nfc_dev *dev, int target_idx, u8 comm_mode, + u8 *gb, size_t gb_len); int (*dep_link_down)(struct nfc_dev *dev); int (*activate_target)(struct nfc_dev *dev, u32 target_idx, u32 protocol); @@ -179,8 +179,6 @@ struct sk_buff *nfc_alloc_recv_skb(unsigned int size, gfp_t gfp); int nfc_set_remote_general_bytes(struct nfc_dev *dev, u8 *gt, u8 gt_len); -u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, u8 *gt_len); - int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets, int ntargets); diff --git a/net/nfc/core.c b/net/nfc/core.c index 6089aca67b14..f4f526f73217 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -181,13 +181,13 @@ error: return rc; } -int nfc_dep_link_up(struct nfc_dev *dev, int target_index, - u8 comm_mode, u8 rf_mode) +int nfc_dep_link_up(struct nfc_dev *dev, int target_index, u8 comm_mode) { int rc = 0; + u8 *gb; + size_t gb_len; - pr_debug("dev_name=%s comm:%d rf:%d\n", - dev_name(&dev->dev), comm_mode, rf_mode); + pr_debug("dev_name=%s comm %d\n", dev_name(&dev->dev), comm_mode); if (!dev->ops->dep_link_up) return -EOPNOTSUPP; @@ -204,7 +204,13 @@ int nfc_dep_link_up(struct nfc_dev *dev, int target_index, goto error; } - rc = dev->ops->dep_link_up(dev, target_index, comm_mode, rf_mode); + gb = nfc_llcp_general_bytes(dev, &gb_len); + if (gb_len > NFC_MAX_GT_LEN) { + rc = -EINVAL; + goto error; + } + + rc = dev->ops->dep_link_up(dev, target_index, comm_mode, gb, gb_len); error: device_unlock(&dev->dev); @@ -367,12 +373,6 @@ int nfc_set_remote_general_bytes(struct nfc_dev *dev, u8 *gb, u8 gb_len) } EXPORT_SYMBOL(nfc_set_remote_general_bytes); -u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, u8 *gt_len) -{ - return nfc_llcp_general_bytes(dev, gt_len); -} -EXPORT_SYMBOL(nfc_get_local_general_bytes); - /** * nfc_alloc_send_skb - allocate a skb for data exchange responses * diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index a0cb133c610a..3ce646e35f6b 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -281,7 +281,7 @@ void nfc_llcp_put_ssap(struct nfc_llcp_local *local, u8 ssap) mutex_unlock(&local->sdp_lock); } -u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, u8 *general_bytes_len) +u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len) { struct nfc_llcp_local *local; diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index a1388e4efd6f..fa620d03799e 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -542,13 +542,12 @@ static int nfc_genl_dep_link_up(struct sk_buff *skb, struct genl_info *info) struct nfc_dev *dev; int rc, tgt_idx; u32 idx; - u8 comm, rf; + u8 comm; pr_debug("DEP link up\n"); if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || - !info->attrs[NFC_ATTR_COMM_MODE] || - !info->attrs[NFC_ATTR_RF_MODE]) + !info->attrs[NFC_ATTR_COMM_MODE]) return -EINVAL; idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); @@ -558,19 +557,15 @@ static int nfc_genl_dep_link_up(struct sk_buff *skb, struct genl_info *info) tgt_idx = nla_get_u32(info->attrs[NFC_ATTR_TARGET_INDEX]); comm = nla_get_u8(info->attrs[NFC_ATTR_COMM_MODE]); - rf = nla_get_u8(info->attrs[NFC_ATTR_RF_MODE]); if (comm != NFC_COMM_ACTIVE && comm != NFC_COMM_PASSIVE) return -EINVAL; - if (rf != NFC_RF_INITIATOR && comm != NFC_RF_TARGET) - return -EINVAL; - dev = nfc_get_device(idx); if (!dev) return -ENODEV; - rc = nfc_dep_link_up(dev, tgt_idx, comm, rf); + rc = nfc_dep_link_up(dev, tgt_idx, comm); nfc_put_device(dev); diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index 6d28d75995b0..2fd83b16df68 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -54,7 +54,7 @@ void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx, int nfc_llcp_register_device(struct nfc_dev *dev); void nfc_llcp_unregister_device(struct nfc_dev *dev); int nfc_llcp_set_remote_gb(struct nfc_dev *dev, u8 *gb, u8 gb_len); -u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, u8 *general_bytes_len); +u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len); int __init nfc_llcp_init(void); void nfc_llcp_exit(void); @@ -160,8 +160,7 @@ int nfc_start_poll(struct nfc_dev *dev, u32 protocols); int nfc_stop_poll(struct nfc_dev *dev); -int nfc_dep_link_up(struct nfc_dev *dev, int target_idx, - u8 comm_mode, u8 rf_mode); +int nfc_dep_link_up(struct nfc_dev *dev, int target_idx, u8 comm_mode); int nfc_dep_link_down(struct nfc_dev *dev); -- cgit From 0a40acb24602783fcf6881f915659148aa9807d7 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Mon, 5 Mar 2012 01:03:53 +0100 Subject: NFC: Core code identation fixes Signed-off-by: Samuel Ortiz Signed-off-by: John W. Linville --- include/net/nfc/nfc.h | 24 +++++++++++----------- net/nfc/af_nfc.c | 2 +- net/nfc/core.c | 28 +++++++++++-------------- net/nfc/netlink.c | 57 ++++++++++++++++++++++++--------------------------- net/nfc/nfc.h | 13 ++++++------ net/nfc/rawsock.c | 16 +++++++-------- 6 files changed, 66 insertions(+), 74 deletions(-) (limited to 'include') diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index d608e4eac66f..bac070bf3514 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -57,11 +57,11 @@ struct nfc_ops { u8 *gb, size_t gb_len); int (*dep_link_down)(struct nfc_dev *dev); int (*activate_target)(struct nfc_dev *dev, u32 target_idx, - u32 protocol); + u32 protocol); void (*deactivate_target)(struct nfc_dev *dev, u32 target_idx); int (*data_exchange)(struct nfc_dev *dev, u32 target_idx, - struct sk_buff *skb, data_exchange_cb_t cb, - void *cb_context); + struct sk_buff *skb, data_exchange_cb_t cb, + void *cb_context); }; #define NFC_TARGET_IDX_ANY -1 @@ -110,9 +110,9 @@ struct nfc_dev { extern struct class nfc_class; struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, - u32 supported_protocols, - int tx_headroom, - int tx_tailroom); + u32 supported_protocols, + int tx_headroom, + int tx_tailroom); /** * nfc_free_device - free nfc device @@ -135,7 +135,7 @@ void nfc_unregister_device(struct nfc_dev *dev); * @dev: The parent device */ static inline void nfc_set_parent_dev(struct nfc_dev *nfc_dev, - struct device *dev) + struct device *dev) { nfc_dev->dev.parent = dev; } @@ -172,15 +172,15 @@ static inline const char *nfc_device_name(struct nfc_dev *dev) } struct sk_buff *nfc_alloc_send_skb(struct nfc_dev *dev, struct sock *sk, - unsigned int flags, unsigned int size, - unsigned int *err); + unsigned int flags, unsigned int size, + unsigned int *err); struct sk_buff *nfc_alloc_recv_skb(unsigned int size, gfp_t gfp); int nfc_set_remote_general_bytes(struct nfc_dev *dev, - u8 *gt, u8 gt_len); + u8 *gt, u8 gt_len); -int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets, - int ntargets); +int nfc_targets_found(struct nfc_dev *dev, + struct nfc_target *targets, int ntargets); int nfc_dep_link_is_up(struct nfc_dev *dev, u32 target_idx, u8 comm_mode, u8 rf_mode); diff --git a/net/nfc/af_nfc.c b/net/nfc/af_nfc.c index da67756425ce..9d68441e2a5a 100644 --- a/net/nfc/af_nfc.c +++ b/net/nfc/af_nfc.c @@ -30,7 +30,7 @@ static DEFINE_RWLOCK(proto_tab_lock); static const struct nfc_protocol *proto_tab[NFC_SOCKPROTO_MAX]; static int nfc_sock_create(struct net *net, struct socket *sock, int proto, - int kern) + int kern) { int rc = -EPROTONOSUPPORT; diff --git a/net/nfc/core.c b/net/nfc/core.c index f4f526f73217..295d129864d2 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -256,7 +256,7 @@ error: } int nfc_dep_link_is_up(struct nfc_dev *dev, u32 target_idx, - u8 comm_mode, u8 rf_mode) + u8 comm_mode, u8 rf_mode) { dev->dep_link_up = true; dev->dep_rf_mode = rf_mode; @@ -336,10 +336,8 @@ error: * * The user must wait for the callback before calling this function again. */ -int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, - struct sk_buff *skb, - data_exchange_cb_t cb, - void *cb_context) +int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb, + data_exchange_cb_t cb, void *cb_context) { int rc; @@ -363,8 +361,7 @@ error: int nfc_set_remote_general_bytes(struct nfc_dev *dev, u8 *gb, u8 gb_len) { - pr_debug("dev_name=%s gb_len=%d\n", - dev_name(&dev->dev), gb_len); + pr_debug("dev_name=%s gb_len=%d\n", dev_name(&dev->dev), gb_len); if (gb_len > NFC_MAX_GT_LEN) return -EINVAL; @@ -380,8 +377,8 @@ EXPORT_SYMBOL(nfc_set_remote_general_bytes); * @gfp: gfp flags */ struct sk_buff *nfc_alloc_send_skb(struct nfc_dev *dev, struct sock *sk, - unsigned int flags, unsigned int size, - unsigned int *err) + unsigned int flags, unsigned int size, + unsigned int *err) { struct sk_buff *skb; unsigned int total_size; @@ -428,8 +425,8 @@ EXPORT_SYMBOL(nfc_alloc_recv_skb); * are found. After calling this function, the device driver must stop * polling for targets. */ -int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets, - int n_targets) +int nfc_targets_found(struct nfc_dev *dev, + struct nfc_target *targets, int n_targets) { pr_debug("dev_name=%s n_targets=%d\n", dev_name(&dev->dev), n_targets); @@ -441,7 +438,7 @@ int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets, kfree(dev->targets); dev->targets = kmemdup(targets, n_targets * sizeof(struct nfc_target), - GFP_ATOMIC); + GFP_ATOMIC); if (!dev->targets) { dev->n_targets = 0; @@ -501,15 +498,14 @@ struct nfc_dev *nfc_get_device(unsigned idx) * @supported_protocols: NFC protocols supported by the device */ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, - u32 supported_protocols, - int tx_headroom, - int tx_tailroom) + u32 supported_protocols, + int tx_headroom, int tx_tailroom) { static atomic_t dev_no = ATOMIC_INIT(0); struct nfc_dev *dev; if (!ops->start_poll || !ops->stop_poll || !ops->activate_target || - !ops->deactivate_target || !ops->data_exchange) + !ops->deactivate_target || !ops->data_exchange) return NULL; if (!supported_protocols) diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index fa620d03799e..6404052d6c07 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -52,31 +52,30 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = { }; static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target, - struct netlink_callback *cb, int flags) + struct netlink_callback *cb, int flags) { void *hdr; hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, - &nfc_genl_family, flags, NFC_CMD_GET_TARGET); + &nfc_genl_family, flags, NFC_CMD_GET_TARGET); if (!hdr) return -EMSGSIZE; genl_dump_check_consistent(cb, hdr, &nfc_genl_family); NLA_PUT_U32(msg, NFC_ATTR_TARGET_INDEX, target->idx); - NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS, - target->supported_protocols); + NLA_PUT_U32(msg, NFC_ATTR_PROTOCOLS, target->supported_protocols); NLA_PUT_U16(msg, NFC_ATTR_TARGET_SENS_RES, target->sens_res); NLA_PUT_U8(msg, NFC_ATTR_TARGET_SEL_RES, target->sel_res); if (target->nfcid1_len > 0) NLA_PUT(msg, NFC_ATTR_TARGET_NFCID1, target->nfcid1_len, - target->nfcid1); + target->nfcid1); if (target->sensb_res_len > 0) NLA_PUT(msg, NFC_ATTR_TARGET_SENSB_RES, target->sensb_res_len, - target->sensb_res); + target->sensb_res); if (target->sensf_res_len > 0) NLA_PUT(msg, NFC_ATTR_TARGET_SENSF_RES, target->sensf_res_len, - target->sensf_res); + target->sensf_res); return genlmsg_end(msg, hdr); @@ -92,9 +91,9 @@ static struct nfc_dev *__get_device_from_cb(struct netlink_callback *cb) u32 idx; rc = nlmsg_parse(cb->nlh, GENL_HDRLEN + nfc_genl_family.hdrsize, - nfc_genl_family.attrbuf, - nfc_genl_family.maxattr, - nfc_genl_policy); + nfc_genl_family.attrbuf, + nfc_genl_family.maxattr, + nfc_genl_policy); if (rc < 0) return ERR_PTR(rc); @@ -111,7 +110,7 @@ static struct nfc_dev *__get_device_from_cb(struct netlink_callback *cb) } static int nfc_genl_dump_targets(struct sk_buff *skb, - struct netlink_callback *cb) + struct netlink_callback *cb) { int i = cb->args[0]; struct nfc_dev *dev = (struct nfc_dev *) cb->args[1]; @@ -131,7 +130,7 @@ static int nfc_genl_dump_targets(struct sk_buff *skb, while (i < dev->n_targets) { rc = nfc_genl_send_target(skb, &dev->targets[i], cb, - NLM_F_MULTI); + NLM_F_MULTI); if (rc < 0) break; @@ -167,7 +166,7 @@ int nfc_genl_targets_found(struct nfc_dev *dev) return -ENOMEM; hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, - NFC_EVENT_TARGETS_FOUND); + NFC_EVENT_TARGETS_FOUND); if (!hdr) goto free_msg; @@ -194,7 +193,7 @@ int nfc_genl_device_added(struct nfc_dev *dev) return -ENOMEM; hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, - NFC_EVENT_DEVICE_ADDED); + NFC_EVENT_DEVICE_ADDED); if (!hdr) goto free_msg; @@ -226,7 +225,7 @@ int nfc_genl_device_removed(struct nfc_dev *dev) return -ENOMEM; hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, - NFC_EVENT_DEVICE_REMOVED); + NFC_EVENT_DEVICE_REMOVED); if (!hdr) goto free_msg; @@ -246,14 +245,14 @@ free_msg: } static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev, - u32 pid, u32 seq, - struct netlink_callback *cb, - int flags) + u32 pid, u32 seq, + struct netlink_callback *cb, + int flags) { void *hdr; hdr = genlmsg_put(msg, pid, seq, &nfc_genl_family, flags, - NFC_CMD_GET_DEVICE); + NFC_CMD_GET_DEVICE); if (!hdr) return -EMSGSIZE; @@ -273,7 +272,7 @@ nla_put_failure: } static int nfc_genl_dump_devices(struct sk_buff *skb, - struct netlink_callback *cb) + struct netlink_callback *cb) { struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0]; struct nfc_dev *dev = (struct nfc_dev *) cb->args[1]; @@ -300,8 +299,7 @@ static int nfc_genl_dump_devices(struct sk_buff *skb, int rc; rc = nfc_genl_send_device(skb, dev, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, - cb, NLM_F_MULTI); + cb->nlh->nlmsg_seq, cb, NLM_F_MULTI); if (rc < 0) break; @@ -326,7 +324,7 @@ static int nfc_genl_dump_devices_done(struct netlink_callback *cb) } int nfc_genl_dep_link_up_event(struct nfc_dev *dev, u32 target_idx, - u8 comm_mode, u8 rf_mode) + u8 comm_mode, u8 rf_mode) { struct sk_buff *msg; void *hdr; @@ -337,8 +335,7 @@ int nfc_genl_dep_link_up_event(struct nfc_dev *dev, u32 target_idx, if (!msg) return -ENOMEM; - hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, - NFC_CMD_DEP_LINK_UP); + hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, NFC_CMD_DEP_LINK_UP); if (!hdr) goto free_msg; @@ -375,7 +372,7 @@ int nfc_genl_dep_link_down_event(struct nfc_dev *dev) return -ENOMEM; hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, - NFC_CMD_DEP_LINK_DOWN); + NFC_CMD_DEP_LINK_DOWN); if (!hdr) goto free_msg; @@ -417,7 +414,7 @@ static int nfc_genl_get_device(struct sk_buff *skb, struct genl_info *info) } rc = nfc_genl_send_device(msg, dev, info->snd_pid, info->snd_seq, - NULL, 0); + NULL, 0); if (rc < 0) goto out_free; @@ -484,7 +481,7 @@ static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info) pr_debug("Poll start\n"); if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || - !info->attrs[NFC_ATTR_PROTOCOLS]) + !info->attrs[NFC_ATTR_PROTOCOLS]) return -EINVAL; idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); @@ -640,7 +637,7 @@ static struct genl_ops nfc_genl_ops[] = { }; static int nfc_genl_rcv_nl_event(struct notifier_block *this, - unsigned long event, void *ptr) + unsigned long event, void *ptr) { struct netlink_notify *n = ptr; struct class_dev_iter iter; @@ -693,7 +690,7 @@ int __init nfc_genl_init(void) int rc; rc = genl_register_family_with_ops(&nfc_genl_family, nfc_genl_ops, - ARRAY_SIZE(nfc_genl_ops)); + ARRAY_SIZE(nfc_genl_ops)); if (rc) return rc; diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index 2fd83b16df68..ec8794c1099c 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -32,7 +32,7 @@ struct nfc_protocol { struct proto *proto; struct module *owner; int (*create)(struct net *net, struct socket *sock, - const struct nfc_protocol *nfc_proto); + const struct nfc_protocol *nfc_proto); }; struct nfc_rawsock { @@ -65,7 +65,7 @@ static inline void nfc_llcp_mac_is_down(struct nfc_dev *dev) } static inline void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx, - u8 comm_mode, u8 rf_mode) + u8 comm_mode, u8 rf_mode) { } @@ -78,7 +78,8 @@ static inline void nfc_llcp_unregister_device(struct nfc_dev *dev) { } -static inline int nfc_llcp_set_remote_gb(struct nfc_dev *dev, u8 *gb, u8 gb_len) +static inline int nfc_llcp_set_remote_gb(struct nfc_dev *dev, + u8 *gb, u8 gb_len) { return 0; } @@ -168,9 +169,7 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol); int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx); -int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, - struct sk_buff *skb, - data_exchange_cb_t cb, - void *cb_context); +int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb, + data_exchange_cb_t cb, void *cb_context); #endif /* __LOCAL_NFC_H */ diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c index 5325439b0c60..5a839ceb2e82 100644 --- a/net/nfc/rawsock.c +++ b/net/nfc/rawsock.c @@ -63,7 +63,7 @@ static int rawsock_release(struct socket *sock) } static int rawsock_connect(struct socket *sock, struct sockaddr *_addr, - int len, int flags) + int len, int flags) { struct sock *sk = sock->sk; struct sockaddr_nfc *addr = (struct sockaddr_nfc *)_addr; @@ -73,7 +73,7 @@ static int rawsock_connect(struct socket *sock, struct sockaddr *_addr, pr_debug("sock=%p sk=%p flags=%d\n", sock, sk, flags); if (!addr || len < sizeof(struct sockaddr_nfc) || - addr->sa_family != AF_NFC) + addr->sa_family != AF_NFC) return -EINVAL; pr_debug("addr dev_idx=%u target_idx=%u protocol=%u\n", @@ -120,7 +120,7 @@ static int rawsock_add_header(struct sk_buff *skb) } static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb, - int err) + int err) { struct sock *sk = (struct sock *) context; @@ -173,7 +173,7 @@ static void rawsock_tx_work(struct work_struct *work) sock_hold(sk); rc = nfc_data_exchange(dev, target_idx, skb, - rawsock_data_exchange_complete, sk); + rawsock_data_exchange_complete, sk); if (rc) { rawsock_report_error(sk, rc); sock_put(sk); @@ -181,7 +181,7 @@ static void rawsock_tx_work(struct work_struct *work) } static int rawsock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) + struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct nfc_dev *dev = nfc_rawsock(sk)->dev; @@ -218,7 +218,7 @@ static int rawsock_sendmsg(struct kiocb *iocb, struct socket *sock, } static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) + struct msghdr *msg, size_t len, int flags) { int noblock = flags & MSG_DONTWAIT; struct sock *sk = sock->sk; @@ -274,7 +274,7 @@ static void rawsock_destruct(struct sock *sk) if (sk->sk_state == TCP_ESTABLISHED) { nfc_deactivate_target(nfc_rawsock(sk)->dev, - nfc_rawsock(sk)->target_idx); + nfc_rawsock(sk)->target_idx); nfc_put_device(nfc_rawsock(sk)->dev); } @@ -287,7 +287,7 @@ static void rawsock_destruct(struct sock *sk) } static int rawsock_create(struct net *net, struct socket *sock, - const struct nfc_protocol *nfc_proto) + const struct nfc_protocol *nfc_proto) { struct sock *sk; -- cgit From eb9bc6e9a0ac668d2283b8fea1534f8ba31d1692 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Mon, 5 Mar 2012 01:03:54 +0100 Subject: NFC: NCI code identation fixes Signed-off-by: Samuel Ortiz Signed-off-by: John W. Linville --- include/net/nfc/nci_core.h | 10 ++-- net/nfc/nci/core.c | 113 ++++++++++++++++++++++----------------------- net/nfc/nci/data.c | 28 +++++------ net/nfc/nci/ntf.c | 75 ++++++++++++++---------------- net/nfc/nci/rsp.c | 17 ++++--- 5 files changed, 116 insertions(+), 127 deletions(-) (limited to 'include') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 86fee8b5c65c..feba74027ff8 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -141,17 +141,17 @@ struct nci_dev { /* ----- NCI Devices ----- */ struct nci_dev *nci_allocate_device(struct nci_ops *ops, - __u32 supported_protocols, - int tx_headroom, - int tx_tailroom); + __u32 supported_protocols, + int tx_headroom, + int tx_tailroom); void nci_free_device(struct nci_dev *ndev); int nci_register_device(struct nci_dev *ndev); void nci_unregister_device(struct nci_dev *ndev); int nci_recv_frame(struct sk_buff *skb); static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev, - unsigned int len, - gfp_t how) + unsigned int len, + gfp_t how) { struct sk_buff *skb; diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index a47e90c7d9d1..9ec065bb9ee1 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -66,9 +66,8 @@ static void nci_req_cancel(struct nci_dev *ndev, int err) /* Execute request and wait for completion. */ static int __nci_request(struct nci_dev *ndev, - void (*req)(struct nci_dev *ndev, unsigned long opt), - unsigned long opt, - __u32 timeout) + void (*req)(struct nci_dev *ndev, unsigned long opt), + unsigned long opt, __u32 timeout) { int rc = 0; long completion_rc; @@ -77,9 +76,9 @@ static int __nci_request(struct nci_dev *ndev, init_completion(&ndev->req_completion); req(ndev, opt); - completion_rc = wait_for_completion_interruptible_timeout( - &ndev->req_completion, - timeout); + completion_rc = + wait_for_completion_interruptible_timeout(&ndev->req_completion, + timeout); pr_debug("wait_for_completion return %ld\n", completion_rc); @@ -110,8 +109,9 @@ static int __nci_request(struct nci_dev *ndev, } static inline int nci_request(struct nci_dev *ndev, - void (*req)(struct nci_dev *ndev, unsigned long opt), - unsigned long opt, __u32 timeout) + void (*req)(struct nci_dev *ndev, + unsigned long opt), + unsigned long opt, __u32 timeout) { int rc; @@ -152,14 +152,14 @@ static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt) /* by default mapping is set to NCI_RF_INTERFACE_FRAME */ for (i = 0; i < ndev->num_supported_rf_interfaces; i++) { if (ndev->supported_rf_interfaces[i] == - NCI_RF_INTERFACE_ISO_DEP) { + NCI_RF_INTERFACE_ISO_DEP) { cfg[*num].rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; cfg[*num].mode = NCI_DISC_MAP_MODE_POLL | NCI_DISC_MAP_MODE_LISTEN; cfg[*num].rf_interface = NCI_RF_INTERFACE_ISO_DEP; (*num)++; } else if (ndev->supported_rf_interfaces[i] == - NCI_RF_INTERFACE_NFC_DEP) { + NCI_RF_INTERFACE_NFC_DEP) { cfg[*num].rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; cfg[*num].mode = NCI_DISC_MAP_MODE_POLL | NCI_DISC_MAP_MODE_LISTEN; @@ -172,8 +172,7 @@ static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt) } nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_MAP_CMD, - (1 + ((*num)*sizeof(struct disc_map_config))), - &cmd); + (1 + ((*num) * sizeof(struct disc_map_config))), &cmd); } static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) @@ -184,36 +183,36 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) cmd.num_disc_configs = 0; if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && - (protocols & NFC_PROTO_JEWEL_MASK - || protocols & NFC_PROTO_MIFARE_MASK - || protocols & NFC_PROTO_ISO14443_MASK - || protocols & NFC_PROTO_NFC_DEP_MASK)) { + (protocols & NFC_PROTO_JEWEL_MASK + || protocols & NFC_PROTO_MIFARE_MASK + || protocols & NFC_PROTO_ISO14443_MASK + || protocols & NFC_PROTO_NFC_DEP_MASK)) { cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = - NCI_NFC_A_PASSIVE_POLL_MODE; + NCI_NFC_A_PASSIVE_POLL_MODE; cmd.disc_configs[cmd.num_disc_configs].frequency = 1; cmd.num_disc_configs++; } if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && - (protocols & NFC_PROTO_ISO14443_MASK)) { + (protocols & NFC_PROTO_ISO14443_MASK)) { cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = - NCI_NFC_B_PASSIVE_POLL_MODE; + NCI_NFC_B_PASSIVE_POLL_MODE; cmd.disc_configs[cmd.num_disc_configs].frequency = 1; cmd.num_disc_configs++; } if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && - (protocols & NFC_PROTO_FELICA_MASK - || protocols & NFC_PROTO_NFC_DEP_MASK)) { + (protocols & NFC_PROTO_FELICA_MASK + || protocols & NFC_PROTO_NFC_DEP_MASK)) { cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = - NCI_NFC_F_PASSIVE_POLL_MODE; + NCI_NFC_F_PASSIVE_POLL_MODE; cmd.disc_configs[cmd.num_disc_configs].frequency = 1; cmd.num_disc_configs++; } nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD, - (1 + (cmd.num_disc_configs*sizeof(struct disc_config))), - &cmd); + (1 + (cmd.num_disc_configs * sizeof(struct disc_config))), + &cmd); } struct nci_rf_discover_select_param { @@ -224,7 +223,7 @@ struct nci_rf_discover_select_param { static void nci_rf_discover_select_req(struct nci_dev *ndev, unsigned long opt) { struct nci_rf_discover_select_param *param = - (struct nci_rf_discover_select_param *)opt; + (struct nci_rf_discover_select_param *)opt; struct nci_rf_discover_select_cmd cmd; cmd.rf_discovery_id = param->rf_discovery_id; @@ -245,8 +244,7 @@ static void nci_rf_discover_select_req(struct nci_dev *ndev, unsigned long opt) } nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_SELECT_CMD, - sizeof(struct nci_rf_discover_select_cmd), - &cmd); + sizeof(struct nci_rf_discover_select_cmd), &cmd); } static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt) @@ -256,8 +254,7 @@ static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt) cmd.type = NCI_DEACTIVATE_TYPE_IDLE_MODE; nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD, - sizeof(struct nci_rf_deactivate_cmd), - &cmd); + sizeof(struct nci_rf_deactivate_cmd), &cmd); } static int nci_open_device(struct nci_dev *ndev) @@ -281,16 +278,16 @@ static int nci_open_device(struct nci_dev *ndev) set_bit(NCI_INIT, &ndev->flags); rc = __nci_request(ndev, nci_reset_req, 0, - msecs_to_jiffies(NCI_RESET_TIMEOUT)); + msecs_to_jiffies(NCI_RESET_TIMEOUT)); if (!rc) { rc = __nci_request(ndev, nci_init_req, 0, - msecs_to_jiffies(NCI_INIT_TIMEOUT)); + msecs_to_jiffies(NCI_INIT_TIMEOUT)); } if (!rc) { rc = __nci_request(ndev, nci_init_complete_req, 0, - msecs_to_jiffies(NCI_INIT_TIMEOUT)); + msecs_to_jiffies(NCI_INIT_TIMEOUT)); } clear_bit(NCI_INIT, &ndev->flags); @@ -340,7 +337,7 @@ static int nci_close_device(struct nci_dev *ndev) set_bit(NCI_INIT, &ndev->flags); __nci_request(ndev, nci_reset_req, 0, - msecs_to_jiffies(NCI_RESET_TIMEOUT)); + msecs_to_jiffies(NCI_RESET_TIMEOUT)); clear_bit(NCI_INIT, &ndev->flags); /* Flush cmd wq */ @@ -396,7 +393,7 @@ static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols) int rc; if ((atomic_read(&ndev->state) == NCI_DISCOVERY) || - (atomic_read(&ndev->state) == NCI_W4_ALL_DISCOVERIES)) { + (atomic_read(&ndev->state) == NCI_W4_ALL_DISCOVERIES)) { pr_err("unable to start poll, since poll is already active\n"); return -EBUSY; } @@ -407,17 +404,17 @@ static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols) } if ((atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) || - (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) { + (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) { pr_debug("target active or w4 select, implicitly deactivate\n"); rc = nci_request(ndev, nci_rf_deactivate_req, 0, - msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); + msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); if (rc) return -EBUSY; } rc = nci_request(ndev, nci_rf_discover_req, protocols, - msecs_to_jiffies(NCI_RF_DISC_TIMEOUT)); + msecs_to_jiffies(NCI_RF_DISC_TIMEOUT)); if (!rc) ndev->poll_prots = protocols; @@ -430,17 +427,17 @@ static void nci_stop_poll(struct nfc_dev *nfc_dev) struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); if ((atomic_read(&ndev->state) != NCI_DISCOVERY) && - (atomic_read(&ndev->state) != NCI_W4_ALL_DISCOVERIES)) { + (atomic_read(&ndev->state) != NCI_W4_ALL_DISCOVERIES)) { pr_err("unable to stop poll, since poll is not active\n"); return; } nci_request(ndev, nci_rf_deactivate_req, 0, - msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); + msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); } static int nci_activate_target(struct nfc_dev *nfc_dev, __u32 target_idx, - __u32 protocol) + __u32 protocol) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); struct nci_rf_discover_select_param param; @@ -451,7 +448,7 @@ static int nci_activate_target(struct nfc_dev *nfc_dev, __u32 target_idx, pr_debug("target_idx %d, protocol 0x%x\n", target_idx, protocol); if ((atomic_read(&ndev->state) != NCI_W4_HOST_SELECT) && - (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) { + (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) { pr_err("there is no available target to activate\n"); return -EINVAL; } @@ -494,8 +491,8 @@ static int nci_activate_target(struct nfc_dev *nfc_dev, __u32 target_idx, param.rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; rc = nci_request(ndev, nci_rf_discover_select_req, - (unsigned long)¶m, - msecs_to_jiffies(NCI_RF_DISC_SELECT_TIMEOUT)); + (unsigned long)¶m, + msecs_to_jiffies(NCI_RF_DISC_SELECT_TIMEOUT)); } if (!rc) @@ -519,14 +516,13 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev, __u32 target_idx) if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) { nci_request(ndev, nci_rf_deactivate_req, 0, - msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); + msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); } } static int nci_data_exchange(struct nfc_dev *nfc_dev, __u32 target_idx, - struct sk_buff *skb, - data_exchange_cb_t cb, - void *cb_context) + struct sk_buff *skb, + data_exchange_cb_t cb, void *cb_context) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); int rc; @@ -571,9 +567,8 @@ static struct nfc_ops nci_nfc_ops = { * @supported_protocols: NFC protocols supported by the device */ struct nci_dev *nci_allocate_device(struct nci_ops *ops, - __u32 supported_protocols, - int tx_headroom, - int tx_tailroom) + __u32 supported_protocols, + int tx_headroom, int tx_tailroom) { struct nci_dev *ndev; @@ -594,9 +589,9 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops, ndev->tx_tailroom = tx_tailroom; ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops, - supported_protocols, - tx_headroom + NCI_DATA_HDR_SIZE, - tx_tailroom); + supported_protocols, + tx_headroom + NCI_DATA_HDR_SIZE, + tx_tailroom); if (!ndev->nfc_dev) goto free_exit; @@ -668,9 +663,9 @@ int nci_register_device(struct nci_dev *ndev) skb_queue_head_init(&ndev->tx_q); setup_timer(&ndev->cmd_timer, nci_cmd_timer, - (unsigned long) ndev); + (unsigned long) ndev); setup_timer(&ndev->data_timer, nci_data_timer, - (unsigned long) ndev); + (unsigned long) ndev); mutex_init(&ndev->req_lock); @@ -719,7 +714,7 @@ int nci_recv_frame(struct sk_buff *skb) pr_debug("len %d\n", skb->len); if (!ndev || (!test_bit(NCI_UP, &ndev->flags) - && !test_bit(NCI_INIT, &ndev->flags))) { + && !test_bit(NCI_INIT, &ndev->flags))) { kfree_skb(skb); return -ENXIO; } @@ -799,7 +794,7 @@ static void nci_tx_work(struct work_struct *work) /* Check if data flow control is used */ if (atomic_read(&ndev->credits_cnt) != - NCI_DATA_FLOW_CONTROL_NOT_USED) + NCI_DATA_FLOW_CONTROL_NOT_USED) atomic_dec(&ndev->credits_cnt); pr_debug("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d\n", @@ -810,7 +805,7 @@ static void nci_tx_work(struct work_struct *work) nci_send_frame(skb); mod_timer(&ndev->data_timer, - jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT)); + jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT)); } } @@ -879,6 +874,6 @@ static void nci_cmd_work(struct work_struct *work) nci_send_frame(skb); mod_timer(&ndev->cmd_timer, - jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT)); + jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT)); } } diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c index 7880ae924d5e..a0bc326308a5 100644 --- a/net/nfc/nci/data.c +++ b/net/nfc/nci/data.c @@ -35,8 +35,7 @@ #include /* Complete data exchange transaction and forward skb to nfc core */ -void nci_data_exchange_complete(struct nci_dev *ndev, - struct sk_buff *skb, +void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb, int err) { data_exchange_cb_t cb = ndev->data_exchange_cb; @@ -67,9 +66,9 @@ void nci_data_exchange_complete(struct nci_dev *ndev, /* ----------------- NCI TX Data ----------------- */ static inline void nci_push_data_hdr(struct nci_dev *ndev, - __u8 conn_id, - struct sk_buff *skb, - __u8 pbf) + __u8 conn_id, + struct sk_buff *skb, + __u8 pbf) { struct nci_data_hdr *hdr; int plen = skb->len; @@ -86,8 +85,8 @@ static inline void nci_push_data_hdr(struct nci_dev *ndev, } static int nci_queue_tx_data_frags(struct nci_dev *ndev, - __u8 conn_id, - struct sk_buff *skb) { + __u8 conn_id, + struct sk_buff *skb) { int total_len = skb->len; unsigned char *data = skb->data; unsigned long flags; @@ -105,8 +104,8 @@ static int nci_queue_tx_data_frags(struct nci_dev *ndev, min_t(int, total_len, ndev->max_data_pkt_payload_size); skb_frag = nci_skb_alloc(ndev, - (NCI_DATA_HDR_SIZE + frag_len), - GFP_KERNEL); + (NCI_DATA_HDR_SIZE + frag_len), + GFP_KERNEL); if (skb_frag == NULL) { rc = -ENOMEM; goto free_exit; @@ -118,7 +117,8 @@ static int nci_queue_tx_data_frags(struct nci_dev *ndev, /* second, set the header */ nci_push_data_hdr(ndev, conn_id, skb_frag, - ((total_len == frag_len) ? (NCI_PBF_LAST) : (NCI_PBF_CONT))); + ((total_len == frag_len) ? + (NCI_PBF_LAST) : (NCI_PBF_CONT))); __skb_queue_tail(&frags_q, skb_frag); @@ -186,8 +186,8 @@ exit: /* ----------------- NCI RX Data ----------------- */ static void nci_add_rx_data_frag(struct nci_dev *ndev, - struct sk_buff *skb, - __u8 pbf) + struct sk_buff *skb, + __u8 pbf) { int reassembly_len; int err = 0; @@ -211,8 +211,8 @@ static void nci_add_rx_data_frag(struct nci_dev *ndev, /* second, combine the two fragments */ memcpy(skb_push(skb, reassembly_len), - ndev->rx_data_reassembly->data, - reassembly_len); + ndev->rx_data_reassembly->data, + reassembly_len); /* third, free old reassembly */ kfree_skb(ndev->rx_data_reassembly); diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 03e7b4626a3e..2e3dee42196d 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -40,7 +40,7 @@ /* Handle NCI Notification packets */ static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev, - struct sk_buff *skb) + struct sk_buff *skb) { struct nci_core_conn_credit_ntf *ntf = (void *) skb->data; int i; @@ -62,7 +62,7 @@ static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev, if (ntf->conn_entries[i].conn_id == NCI_STATIC_RF_CONN_ID) { /* found static rf connection */ atomic_add(ntf->conn_entries[i].credits, - &ndev->credits_cnt); + &ndev->credits_cnt); } } @@ -72,7 +72,7 @@ static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev, } static void nci_core_generic_error_ntf_packet(struct nci_dev *ndev, - struct sk_buff *skb) + struct sk_buff *skb) { __u8 status = skb->data[0]; @@ -80,7 +80,7 @@ static void nci_core_generic_error_ntf_packet(struct nci_dev *ndev, if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) { /* Activation failed, so complete the request - (the state remains the same) */ + (the state remains the same) */ nci_req_complete(ndev, status); } } @@ -101,7 +101,7 @@ static void nci_core_conn_intf_error_ntf_packet(struct nci_dev *ndev, static __u8 *nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev, struct rf_tech_specific_params_nfca_poll *nfca_poll, - __u8 *data) + __u8 *data) { nfca_poll->sens_res = __le16_to_cpu(*((__u16 *)data)); data += 2; @@ -128,7 +128,7 @@ static __u8 *nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev, static __u8 *nci_extract_rf_params_nfcb_passive_poll(struct nci_dev *ndev, struct rf_tech_specific_params_nfcb_poll *nfcb_poll, - __u8 *data) + __u8 *data) { nfcb_poll->sensb_res_len = *data++; @@ -142,13 +142,13 @@ static __u8 *nci_extract_rf_params_nfcb_passive_poll(struct nci_dev *ndev, static __u8 *nci_extract_rf_params_nfcf_passive_poll(struct nci_dev *ndev, struct rf_tech_specific_params_nfcf_poll *nfcf_poll, - __u8 *data) + __u8 *data) { nfcf_poll->bit_rate = *data++; nfcf_poll->sensf_res_len = *data++; pr_debug("bit_rate %d, sensf_res_len %d\n", - nfcf_poll->bit_rate, nfcf_poll->sensf_res_len); + nfcf_poll->bit_rate, nfcf_poll->sensf_res_len); memcpy(nfcf_poll->sensf_res, data, nfcf_poll->sensf_res_len); data += nfcf_poll->sensf_res_len; @@ -189,7 +189,7 @@ static int nci_add_new_protocol(struct nci_dev *ndev, target->nfcid1_len = nfca_poll->nfcid1_len; if (target->nfcid1_len > 0) { memcpy(target->nfcid1, nfca_poll->nfcid1, - target->nfcid1_len); + target->nfcid1_len); } } else if (rf_tech_and_mode == NCI_NFC_B_PASSIVE_POLL_MODE) { nfcb_poll = (struct rf_tech_specific_params_nfcb_poll *)params; @@ -197,7 +197,7 @@ static int nci_add_new_protocol(struct nci_dev *ndev, target->sensb_res_len = nfcb_poll->sensb_res_len; if (target->sensb_res_len > 0) { memcpy(target->sensb_res, nfcb_poll->sensb_res, - target->sensb_res_len); + target->sensb_res_len); } } else if (rf_tech_and_mode == NCI_NFC_F_PASSIVE_POLL_MODE) { nfcf_poll = (struct rf_tech_specific_params_nfcf_poll *)params; @@ -205,7 +205,7 @@ static int nci_add_new_protocol(struct nci_dev *ndev, target->sensf_res_len = nfcf_poll->sensf_res_len; if (target->sensf_res_len > 0) { memcpy(target->sensf_res, nfcf_poll->sensf_res, - target->sensf_res_len); + target->sensf_res_len); } } else { pr_err("unsupported rf_tech_and_mode 0x%x\n", rf_tech_and_mode); @@ -220,7 +220,7 @@ static int nci_add_new_protocol(struct nci_dev *ndev, } static void nci_add_new_target(struct nci_dev *ndev, - struct nci_rf_discover_ntf *ntf) + struct nci_rf_discover_ntf *ntf) { struct nfc_target *target; int i, rc; @@ -230,8 +230,8 @@ static void nci_add_new_target(struct nci_dev *ndev, if (target->idx == ntf->rf_discovery_id) { /* This target already exists, add the new protocol */ nci_add_new_protocol(ndev, target, ntf->rf_protocol, - ntf->rf_tech_and_mode, - &ntf->rf_tech_specific_params); + ntf->rf_tech_and_mode, + &ntf->rf_tech_specific_params); return; } } @@ -245,27 +245,27 @@ static void nci_add_new_target(struct nci_dev *ndev, target = &ndev->targets[ndev->n_targets]; rc = nci_add_new_protocol(ndev, target, ntf->rf_protocol, - ntf->rf_tech_and_mode, - &ntf->rf_tech_specific_params); + ntf->rf_tech_and_mode, + &ntf->rf_tech_specific_params); if (!rc) { target->idx = ntf->rf_discovery_id; ndev->n_targets++; pr_debug("target_idx %d, n_targets %d\n", target->idx, - ndev->n_targets); + ndev->n_targets); } } void nci_clear_target_list(struct nci_dev *ndev) { memset(ndev->targets, 0, - (sizeof(struct nfc_target)*NCI_MAX_DISCOVERED_TARGETS)); + (sizeof(struct nfc_target)*NCI_MAX_DISCOVERED_TARGETS)); ndev->n_targets = 0; } static void nci_rf_discover_ntf_packet(struct nci_dev *ndev, - struct sk_buff *skb) + struct sk_buff *skb) { struct nci_rf_discover_ntf ntf; __u8 *data = skb->data; @@ -280,7 +280,7 @@ static void nci_rf_discover_ntf_packet(struct nci_dev *ndev, pr_debug("rf_protocol 0x%x\n", ntf.rf_protocol); pr_debug("rf_tech_and_mode 0x%x\n", ntf.rf_tech_and_mode); pr_debug("rf_tech_specific_params_len %d\n", - ntf.rf_tech_specific_params_len); + ntf.rf_tech_specific_params_len); if (ntf.rf_tech_specific_params_len > 0) { switch (ntf.rf_tech_and_mode) { @@ -318,7 +318,7 @@ static void nci_rf_discover_ntf_packet(struct nci_dev *ndev, } else { atomic_set(&ndev->state, NCI_W4_HOST_SELECT); nfc_targets_found(ndev->nfc_dev, ndev->targets, - ndev->n_targets); + ndev->n_targets); } } @@ -335,20 +335,17 @@ static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev, pr_debug("rats_res_len %d\n", nfca_poll->rats_res_len); if (nfca_poll->rats_res_len > 0) { memcpy(nfca_poll->rats_res, - data, - nfca_poll->rats_res_len); + data, nfca_poll->rats_res_len); } break; case NCI_NFC_B_PASSIVE_POLL_MODE: nfcb_poll = &ntf->activation_params.nfcb_poll_iso_dep; nfcb_poll->attrib_res_len = *data++; - pr_debug("attrib_res_len %d\n", - nfcb_poll->attrib_res_len); + pr_debug("attrib_res_len %d\n", nfcb_poll->attrib_res_len); if (nfcb_poll->attrib_res_len > 0) { memcpy(nfcb_poll->attrib_res, - data, - nfcb_poll->attrib_res_len); + data, nfcb_poll->attrib_res_len); } break; @@ -362,7 +359,7 @@ static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev, } static void nci_target_auto_activated(struct nci_dev *ndev, - struct nci_rf_intf_activated_ntf *ntf) + struct nci_rf_intf_activated_ntf *ntf) { struct nfc_target *target; int rc; @@ -370,8 +367,8 @@ static void nci_target_auto_activated(struct nci_dev *ndev, target = &ndev->targets[ndev->n_targets]; rc = nci_add_new_protocol(ndev, target, ntf->rf_protocol, - ntf->activation_rf_tech_and_mode, - &ntf->rf_tech_specific_params); + ntf->activation_rf_tech_and_mode, + &ntf->rf_tech_specific_params); if (rc) return; @@ -384,7 +381,7 @@ static void nci_target_auto_activated(struct nci_dev *ndev, } static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, - struct sk_buff *skb) + struct sk_buff *skb) { struct nci_rf_intf_activated_ntf ntf; __u8 *data = skb->data; @@ -405,7 +402,8 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, ntf.activation_rf_tech_and_mode); pr_debug("max_data_pkt_payload_size 0x%x\n", ntf.max_data_pkt_payload_size); - pr_debug("initial_num_credits 0x%x\n", ntf.initial_num_credits); + pr_debug("initial_num_credits 0x%x\n", + ntf.initial_num_credits); pr_debug("rf_tech_specific_params_len %d\n", ntf.rf_tech_specific_params_len); @@ -441,18 +439,15 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, pr_debug("data_exch_rf_tech_and_mode 0x%x\n", ntf.data_exch_rf_tech_and_mode); - pr_debug("data_exch_tx_bit_rate 0x%x\n", - ntf.data_exch_tx_bit_rate); - pr_debug("data_exch_rx_bit_rate 0x%x\n", - ntf.data_exch_rx_bit_rate); - pr_debug("activation_params_len %d\n", - ntf.activation_params_len); + pr_debug("data_exch_tx_bit_rate 0x%x\n", ntf.data_exch_tx_bit_rate); + pr_debug("data_exch_rx_bit_rate 0x%x\n", ntf.data_exch_rx_bit_rate); + pr_debug("activation_params_len %d\n", ntf.activation_params_len); if (ntf.activation_params_len > 0) { switch (ntf.rf_interface) { case NCI_RF_INTERFACE_ISO_DEP: err = nci_extract_activation_params_iso_dep(ndev, - &ntf, data); + &ntf, data); break; case NCI_RF_INTERFACE_FRAME: @@ -489,7 +484,7 @@ exit: } static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, - struct sk_buff *skb) + struct sk_buff *skb) { struct nci_rf_deactivate_ntf *ntf = (void *) skb->data; diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index aa63b1e99188..3003c3390e49 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -67,19 +67,18 @@ static void nci_core_init_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) ndev->num_supported_rf_interfaces = rsp_1->num_supported_rf_interfaces; if (ndev->num_supported_rf_interfaces > - NCI_MAX_SUPPORTED_RF_INTERFACES) { + NCI_MAX_SUPPORTED_RF_INTERFACES) { ndev->num_supported_rf_interfaces = NCI_MAX_SUPPORTED_RF_INTERFACES; } memcpy(ndev->supported_rf_interfaces, - rsp_1->supported_rf_interfaces, - ndev->num_supported_rf_interfaces); + rsp_1->supported_rf_interfaces, + ndev->num_supported_rf_interfaces); rsp_2 = (void *) (skb->data + 6 + rsp_1->num_supported_rf_interfaces); - ndev->max_logical_connections = - rsp_2->max_logical_connections; + ndev->max_logical_connections = rsp_2->max_logical_connections; ndev->max_routing_table_size = __le16_to_cpu(rsp_2->max_routing_table_size); ndev->max_ctrl_pkt_payload_len = @@ -121,7 +120,7 @@ exit: } static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev, - struct sk_buff *skb) + struct sk_buff *skb) { __u8 status = skb->data[0]; @@ -143,7 +142,7 @@ static void nci_rf_disc_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) } static void nci_rf_disc_select_rsp_packet(struct nci_dev *ndev, - struct sk_buff *skb) + struct sk_buff *skb) { __u8 status = skb->data[0]; @@ -155,7 +154,7 @@ static void nci_rf_disc_select_rsp_packet(struct nci_dev *ndev, } static void nci_rf_deactivate_rsp_packet(struct nci_dev *ndev, - struct sk_buff *skb) + struct sk_buff *skb) { __u8 status = skb->data[0]; @@ -163,7 +162,7 @@ static void nci_rf_deactivate_rsp_packet(struct nci_dev *ndev, /* If target was active, complete the request only in deactivate_ntf */ if ((status != NCI_STATUS_OK) || - (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) { + (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) { nci_clear_target_list(ndev); atomic_set(&ndev->state, NCI_IDLE); nci_req_complete(ndev, status); -- cgit From 9a9a232a9295deb3b6b5f4ce4290a7d05ff061fa Mon Sep 17 00:00:00 2001 From: Yevgeny Petrilin Date: Tue, 6 Mar 2012 04:04:47 +0000 Subject: net/mlx4: fixing sparse warnings for not declared, functions The SET_PORT functions are implemented in port.c, which is part of mlx4_core, these functions are exported. The functions are in use by the mlx4_en module (were originally part of mlx4_en). Their declaration remained in mlx4_en module, moving the declaration to the right location. Signed-off-by: Yevgeny Petrilin Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 4 ---- include/linux/mlx4/device.h | 5 ++++- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 8f4e655bd57b..9e2b911a1230 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -553,10 +553,6 @@ void mlx4_en_rx_irq(struct mlx4_cq *mcq); int mlx4_SET_MCAST_FLTR(struct mlx4_dev *dev, u8 port, u64 mac, u64 clear, u8 mode); int mlx4_SET_VLAN_FLTR(struct mlx4_dev *dev, struct mlx4_en_priv *priv); -int mlx4_SET_PORT_general(struct mlx4_dev *dev, u8 port, int mtu, - u8 pptx, u8 pfctx, u8 pprx, u8 pfcrx); -int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, - u8 promisc); int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset); int mlx4_en_QUERY_PORT(struct mlx4_en_dev *mdev, u8 port); diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index aea61905499b..44d8144e9ae8 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -622,7 +622,10 @@ int mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac); int mlx4_get_eth_qp(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn); void mlx4_put_eth_qp(struct mlx4_dev *dev, u8 port, u64 mac, int qpn); void mlx4_set_stats_bitmap(struct mlx4_dev *dev, u64 *stats_bitmap); - +int mlx4_SET_PORT_general(struct mlx4_dev *dev, u8 port, int mtu, + u8 pptx, u8 pfctx, u8 pprx, u8 pfcrx); +int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, + u8 promisc); int mlx4_find_cached_vlan(struct mlx4_dev *dev, u8 port, u16 vid, int *idx); int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index); void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, int index); -- cgit From 95f050bf7f64be5168ae2e2c715bb0b0ded141d1 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 6 Mar 2012 16:12:15 -0500 Subject: net: Use bool for return value of dev_valid_name(). Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 +- net/core/dev.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 4d279c5287f8..a89933bc4f2f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2122,7 +2122,7 @@ extern int netdev_rx_handler_register(struct net_device *dev, void *rx_handler_data); extern void netdev_rx_handler_unregister(struct net_device *dev); -extern int dev_valid_name(const char *name); +extern bool dev_valid_name(const char *name); extern int dev_ioctl(struct net *net, unsigned int cmd, void __user *); extern int dev_ethtool(struct net *net, struct ifreq *); extern unsigned dev_get_flags(const struct net_device *); diff --git a/net/core/dev.c b/net/core/dev.c index 5ef3b65c3687..0090809af7bd 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -848,21 +848,21 @@ EXPORT_SYMBOL(dev_get_by_flags_rcu); * to allow sysfs to work. We also disallow any kind of * whitespace. */ -int dev_valid_name(const char *name) +bool dev_valid_name(const char *name) { if (*name == '\0') - return 0; + return false; if (strlen(name) >= IFNAMSIZ) - return 0; + return false; if (!strcmp(name, ".") || !strcmp(name, "..")) - return 0; + return false; while (*name) { if (*name == '/' || isspace(*name)) - return 0; + return false; name++; } - return 1; + return true; } EXPORT_SYMBOL(dev_valid_name); -- cgit From c4762507342dabbe6896ef288df0851ac7dd63d6 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 6 Mar 2012 23:39:50 -0300 Subject: Bluetooth: Fix coding style in mgmt.h Align struct definition in a proper way. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/mgmt.h | 157 +++++++++++++++++++++---------------------- 1 file changed, 78 insertions(+), 79 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 0ca3519e08bd..61953dc848a6 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -44,37 +44,37 @@ #define MGMT_STATUS_INVALID_INDEX 0x11 struct mgmt_hdr { - __le16 opcode; - __le16 index; - __le16 len; + __le16 opcode; + __le16 index; + __le16 len; } __packed; struct mgmt_addr_info { - bdaddr_t bdaddr; - __u8 type; + bdaddr_t bdaddr; + __u8 type; } __packed; #define MGMT_ADDR_INFO_SIZE 7 #define MGMT_OP_READ_VERSION 0x0001 #define MGMT_READ_VERSION_SIZE 0 struct mgmt_rp_read_version { - __u8 version; - __le16 revision; + __u8 version; + __le16 revision; } __packed; #define MGMT_OP_READ_COMMANDS 0x0002 #define MGMT_READ_COMMANDS_SIZE 0 struct mgmt_rp_read_commands { - __le16 num_commands; - __le16 num_events; - __le16 opcodes[0]; + __le16 num_commands; + __le16 num_events; + __le16 opcodes[0]; } __packed; #define MGMT_OP_READ_INDEX_LIST 0x0003 #define MGMT_READ_INDEX_LIST_SIZE 0 struct mgmt_rp_read_index_list { - __le16 num_controllers; - __le16 index[0]; + __le16 num_controllers; + __le16 index[0]; } __packed; /* Reserve one extra byte for names in management messages so that they @@ -96,14 +96,14 @@ struct mgmt_rp_read_index_list { #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 struct mgmt_rp_read_info { - bdaddr_t bdaddr; - __u8 version; - __le16 manufacturer; - __le32 supported_settings; - __le32 current_settings; - __u8 dev_class[3]; - __u8 name[MGMT_MAX_NAME_LENGTH]; - __u8 short_name[MGMT_MAX_SHORT_NAME_LENGTH]; + bdaddr_t bdaddr; + __u8 version; + __le16 manufacturer; + __le32 supported_settings; + __le32 current_settings; + __u8 dev_class[3]; + __u8 name[MGMT_MAX_NAME_LENGTH]; + __u8 short_name[MGMT_MAX_SHORT_NAME_LENGTH]; } __packed; struct mgmt_mode { @@ -116,8 +116,8 @@ struct mgmt_mode { #define MGMT_OP_SET_DISCOVERABLE 0x0006 struct mgmt_cp_set_discoverable { - __u8 val; - __u16 timeout; + __u8 val; + __u16 timeout; } __packed; #define MGMT_SET_DISCOVERABLE_SIZE 3 @@ -134,63 +134,62 @@ struct mgmt_cp_set_discoverable { #define MGMT_OP_SET_HS 0x000C #define MGMT_OP_SET_LE 0x000D - #define MGMT_OP_SET_DEV_CLASS 0x000E struct mgmt_cp_set_dev_class { - __u8 major; - __u8 minor; + __u8 major; + __u8 minor; } __packed; #define MGMT_SET_DEV_CLASS_SIZE 2 #define MGMT_OP_SET_LOCAL_NAME 0x000F struct mgmt_cp_set_local_name { - __u8 name[MGMT_MAX_NAME_LENGTH]; - __u8 short_name[MGMT_MAX_SHORT_NAME_LENGTH]; + __u8 name[MGMT_MAX_NAME_LENGTH]; + __u8 short_name[MGMT_MAX_SHORT_NAME_LENGTH]; } __packed; #define MGMT_SET_LOCAL_NAME_SIZE 260 #define MGMT_OP_ADD_UUID 0x0010 struct mgmt_cp_add_uuid { - __u8 uuid[16]; - __u8 svc_hint; + __u8 uuid[16]; + __u8 svc_hint; } __packed; #define MGMT_ADD_UUID_SIZE 17 #define MGMT_OP_REMOVE_UUID 0x0011 struct mgmt_cp_remove_uuid { - __u8 uuid[16]; + __u8 uuid[16]; } __packed; #define MGMT_REMOVE_UUID_SIZE 16 struct mgmt_link_key_info { struct mgmt_addr_info addr; - u8 type; - u8 val[16]; - u8 pin_len; + u8 type; + u8 val[16]; + u8 pin_len; } __packed; #define MGMT_OP_LOAD_LINK_KEYS 0x0012 struct mgmt_cp_load_link_keys { - __u8 debug_keys; - __le16 key_count; - struct mgmt_link_key_info keys[0]; + __u8 debug_keys; + __le16 key_count; + struct mgmt_link_key_info keys[0]; } __packed; #define MGMT_LOAD_LINK_KEYS_SIZE 3 struct mgmt_ltk_info { struct mgmt_addr_info addr; - __u8 authenticated; - __u8 master; - __u8 enc_size; - __le16 ediv; - __u8 rand[8]; - __u8 val[16]; + __u8 authenticated; + __u8 master; + __u8 enc_size; + __le16 ediv; + __u8 rand[8]; + __u8 val[16]; } __packed; #define MGMT_OP_LOAD_LONG_TERM_KEYS 0x0013 struct mgmt_cp_load_long_term_keys { - __le16 key_count; - struct mgmt_ltk_info keys[0]; + __le16 key_count; + struct mgmt_ltk_info keys[0]; } __packed; #define MGMT_LOAD_LONG_TERM_KEYS_SIZE 2 @@ -213,8 +212,8 @@ struct mgmt_rp_get_connections { #define MGMT_OP_PIN_CODE_REPLY 0x0016 struct mgmt_cp_pin_code_reply { struct mgmt_addr_info addr; - __u8 pin_len; - __u8 pin_code[16]; + __u8 pin_len; + __u8 pin_code[16]; } __packed; #define MGMT_PIN_CODE_REPLY_SIZE (MGMT_ADDR_INFO_SIZE + 17) struct mgmt_rp_pin_code_reply { @@ -229,14 +228,14 @@ struct mgmt_cp_pin_code_neg_reply { #define MGMT_OP_SET_IO_CAPABILITY 0x0018 struct mgmt_cp_set_io_capability { - __u8 io_capability; + __u8 io_capability; } __packed; #define MGMT_SET_IO_CAPABILITY_SIZE 1 #define MGMT_OP_PAIR_DEVICE 0x0019 struct mgmt_cp_pair_device { struct mgmt_addr_info addr; - __u8 io_cap; + __u8 io_cap; } __packed; #define MGMT_PAIR_DEVICE_SIZE (MGMT_ADDR_INFO_SIZE + 1) struct mgmt_rp_pair_device { @@ -274,7 +273,7 @@ struct mgmt_cp_user_confirm_neg_reply { #define MGMT_OP_USER_PASSKEY_REPLY 0x001E struct mgmt_cp_user_passkey_reply { struct mgmt_addr_info addr; - __le32 passkey; + __le32 passkey; } __packed; #define MGMT_USER_PASSKEY_REPLY_SIZE (MGMT_ADDR_INFO_SIZE + 4) struct mgmt_rp_user_passkey_reply { @@ -290,15 +289,15 @@ struct mgmt_cp_user_passkey_neg_reply { #define MGMT_OP_READ_LOCAL_OOB_DATA 0x0020 #define MGMT_READ_LOCAL_OOB_DATA_SIZE 0 struct mgmt_rp_read_local_oob_data { - __u8 hash[16]; - __u8 randomizer[16]; + __u8 hash[16]; + __u8 randomizer[16]; } __packed; #define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0021 struct mgmt_cp_add_remote_oob_data { struct mgmt_addr_info addr; - __u8 hash[16]; - __u8 randomizer[16]; + __u8 hash[16]; + __u8 randomizer[16]; } __packed; #define MGMT_ADD_REMOTE_OOB_DATA_SIZE (MGMT_ADDR_INFO_SIZE + 32) @@ -323,7 +322,7 @@ struct mgmt_cp_stop_discovery { #define MGMT_OP_CONFIRM_NAME 0x0025 struct mgmt_cp_confirm_name { struct mgmt_addr_info addr; - __u8 name_known; + __u8 name_known; } __packed; #define MGMT_CONFIRM_NAME_SIZE (MGMT_ADDR_INFO_SIZE + 1) struct mgmt_rp_confirm_name { @@ -344,20 +343,20 @@ struct mgmt_cp_unblock_device { #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { - __le16 opcode; - __u8 status; - __u8 data[0]; + __le16 opcode; + __u8 status; + __u8 data[0]; } __packed; #define MGMT_EV_CMD_STATUS 0x0002 struct mgmt_ev_cmd_status { - __le16 opcode; - __u8 status; + __le16 opcode; + __u8 status; } __packed; #define MGMT_EV_CONTROLLER_ERROR 0x0003 struct mgmt_ev_controller_error { - __u8 error_code; + __u8 error_code; } __packed; #define MGMT_EV_INDEX_ADDED 0x0004 @@ -368,33 +367,33 @@ struct mgmt_ev_controller_error { #define MGMT_EV_CLASS_OF_DEV_CHANGED 0x0007 struct mgmt_ev_class_of_dev_changed { - __u8 dev_class[3]; + __u8 dev_class[3]; }; #define MGMT_EV_LOCAL_NAME_CHANGED 0x0008 struct mgmt_ev_local_name_changed { - __u8 name[MGMT_MAX_NAME_LENGTH]; - __u8 short_name[MGMT_MAX_SHORT_NAME_LENGTH]; + __u8 name[MGMT_MAX_NAME_LENGTH]; + __u8 short_name[MGMT_MAX_SHORT_NAME_LENGTH]; } __packed; #define MGMT_EV_NEW_LINK_KEY 0x0009 struct mgmt_ev_new_link_key { - __u8 store_hint; + __u8 store_hint; struct mgmt_link_key_info key; } __packed; #define MGMT_EV_NEW_LONG_TERM_KEY 0x000A struct mgmt_ev_new_long_term_key { - __u8 store_hint; + __u8 store_hint; struct mgmt_ltk_info key; } __packed; #define MGMT_EV_DEVICE_CONNECTED 0x000B struct mgmt_ev_device_connected { struct mgmt_addr_info addr; - __le32 flags; - __le16 eir_len; - __u8 eir[0]; + __le32 flags; + __le16 eir_len; + __u8 eir[0]; } __packed; #define MGMT_EV_DEVICE_DISCONNECTED 0x000C @@ -402,20 +401,20 @@ struct mgmt_ev_device_connected { #define MGMT_EV_CONNECT_FAILED 0x000D struct mgmt_ev_connect_failed { struct mgmt_addr_info addr; - __u8 status; + __u8 status; } __packed; #define MGMT_EV_PIN_CODE_REQUEST 0x000E struct mgmt_ev_pin_code_request { struct mgmt_addr_info addr; - __u8 secure; + __u8 secure; } __packed; #define MGMT_EV_USER_CONFIRM_REQUEST 0x000F struct mgmt_ev_user_confirm_request { struct mgmt_addr_info addr; - __u8 confirm_hint; - __le32 value; + __u8 confirm_hint; + __le32 value; } __packed; #define MGMT_EV_USER_PASSKEY_REQUEST 0x0010 @@ -426,7 +425,7 @@ struct mgmt_ev_user_passkey_request { #define MGMT_EV_AUTH_FAILED 0x0011 struct mgmt_ev_auth_failed { struct mgmt_addr_info addr; - __u8 status; + __u8 status; } __packed; #define MGMT_DEV_FOUND_CONFIRM_NAME 0x01 @@ -435,16 +434,16 @@ struct mgmt_ev_auth_failed { #define MGMT_EV_DEVICE_FOUND 0x0012 struct mgmt_ev_device_found { struct mgmt_addr_info addr; - __s8 rssi; - __u8 flags[4]; - __le16 eir_len; - __u8 eir[0]; + __s8 rssi; + __u8 flags[4]; + __le16 eir_len; + __u8 eir[0]; } __packed; #define MGMT_EV_DISCOVERING 0x0013 struct mgmt_ev_discovering { - __u8 type; - __u8 discovering; + __u8 type; + __u8 discovering; } __packed; #define MGMT_EV_DEVICE_BLOCKED 0x0014 -- cgit From 044e1247344d7ff0dbdb1e7edd80d859a8c19aa6 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 6 Mar 2012 23:45:42 -0300 Subject: Bluetooth: Use correct type for userspace exported structs It should be __u8 instead of u8. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/mgmt.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 61953dc848a6..ffc1377e092e 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -163,9 +163,9 @@ struct mgmt_cp_remove_uuid { struct mgmt_link_key_info { struct mgmt_addr_info addr; - u8 type; - u8 val[16]; - u8 pin_len; + __u8 type; + __u8 val[16]; + __u8 pin_len; } __packed; #define MGMT_OP_LOAD_LINK_KEYS 0x0012 -- cgit From f64b993f44c3a5fe709b276ac5652d006afe9d33 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 6 Mar 2012 23:48:33 -0300 Subject: Bluetooth: Fix coding style in all .h files Proper align the struct definitions. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 8 +++---- include/net/bluetooth/hci_core.h | 10 ++++---- include/net/bluetooth/hci_mon.h | 8 +++---- include/net/bluetooth/l2cap.h | 52 ++++++++++++++++++++-------------------- 4 files changed, 39 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 3acbebfb22b9..344b0f972828 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -693,8 +693,8 @@ struct hci_cp_host_buffer_size { #define HCI_OP_WRITE_EIR 0x0c52 struct hci_cp_write_eir { - __u8 fec; - __u8 data[HCI_MAX_EIR_LENGTH]; + __u8 fec; + __u8 data[HCI_MAX_EIR_LENGTH]; } __packed; #define HCI_OP_READ_SSP_MODE 0x0c55 @@ -725,8 +725,8 @@ struct hci_rp_read_flow_control_mode { #define HCI_OP_WRITE_LE_HOST_SUPPORTED 0x0c6d struct hci_cp_write_le_host_supported { - __u8 le; - __u8 simul; + __u8 le; + __u8 simul; } __packed; #define HCI_OP_READ_LOCAL_VERSION 0x1001 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 25cb0a15b579..cbbf68a8510d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -57,7 +57,7 @@ struct inquiry_entry { }; struct discovery_state { - int type; + int type; enum { DISCOVERY_STOPPED, DISCOVERY_STARTING, @@ -65,10 +65,10 @@ struct discovery_state { DISCOVERY_RESOLVING, DISCOVERY_STOPPING, } state; - struct list_head all; /* All devices found during inquiry */ - struct list_head unknown; /* Name state not known */ - struct list_head resolve; /* Name needs to be resolved */ - __u32 timestamp; + struct list_head all; /* All devices found during inquiry */ + struct list_head unknown; /* Name state not known */ + struct list_head resolve; /* Name needs to be resolved */ + __u32 timestamp; }; struct hci_conn_hash { diff --git a/include/net/bluetooth/hci_mon.h b/include/net/bluetooth/hci_mon.h index 07a25c92502c..77d1e5764185 100644 --- a/include/net/bluetooth/hci_mon.h +++ b/include/net/bluetooth/hci_mon.h @@ -41,10 +41,10 @@ struct hci_mon_hdr { #define HCI_MON_SCO_RX_PKT 7 struct hci_mon_new_index { - __u8 type; - __u8 bus; - bdaddr_t bdaddr; - char name[8]; + __u8 type; + __u8 bus; + bdaddr_t bdaddr; + char name[8]; } __packed; #define HCI_MON_NEW_INDEX_SIZE 16 diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index e4669d0230c5..9b242c6bf55b 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -492,16 +492,16 @@ struct l2cap_chan { struct sk_buff_head srej_q; struct list_head srej_l; - struct list_head list; - struct list_head global_l; + struct list_head list; + struct list_head global_l; - void *data; - struct l2cap_ops *ops; + void *data; + struct l2cap_ops *ops; struct mutex lock; }; struct l2cap_ops { - char *name; + char *name; struct l2cap_chan *(*new_connection) (void *data); int (*recv) (void *data, struct sk_buff *skb); @@ -513,35 +513,35 @@ struct l2cap_ops { }; struct l2cap_conn { - struct hci_conn *hcon; - struct hci_chan *hchan; + struct hci_conn *hcon; + struct hci_chan *hchan; - bdaddr_t *dst; - bdaddr_t *src; + bdaddr_t *dst; + bdaddr_t *src; - unsigned int mtu; + unsigned int mtu; - __u32 feat_mask; - __u8 fixed_chan_mask; + __u32 feat_mask; + __u8 fixed_chan_mask; - __u8 info_state; - __u8 info_ident; + __u8 info_state; + __u8 info_ident; - struct delayed_work info_timer; + struct delayed_work info_timer; - spinlock_t lock; + spinlock_t lock; - struct sk_buff *rx_skb; - __u32 rx_len; - __u8 tx_ident; + struct sk_buff *rx_skb; + __u32 rx_len; + __u8 tx_ident; - __u8 disc_reason; + __u8 disc_reason; - struct delayed_work security_timer; - struct smp_chan *smp_chan; + struct delayed_work security_timer; + struct smp_chan *smp_chan; - struct list_head chan_l; - struct mutex chan_lock; + struct list_head chan_l; + struct mutex chan_lock; }; #define L2CAP_INFO_CL_MTU_REQ_SENT 0x01 @@ -556,9 +556,9 @@ struct l2cap_conn { #define l2cap_pi(sk) ((struct l2cap_pinfo *) sk) struct l2cap_pinfo { - struct bt_sock bt; + struct bt_sock bt; struct l2cap_chan *chan; - struct sk_buff *rx_busy_skb; + struct sk_buff *rx_busy_skb; }; enum { -- cgit From 95468fd41a8930743180ea3560f4c601d4174275 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 5 Mar 2012 15:06:02 -0800 Subject: writeback: fix typo in the writeback_control comment Signed-off-by: Andrew Morton Signed-off-by: Fengguang Wu Signed-off-by: Jiri Kosina --- include/linux/writeback.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 995b8bf630ac..a2b84f598e2b 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -64,7 +64,7 @@ struct writeback_control { long pages_skipped; /* Pages which were not written */ /* - * For a_ops->writepages(): is start or end are non-zero then this is + * For a_ops->writepages(): if start or end are non-zero then this is * a hint that the filesystem need only write out the pages inside that * byterange. The byte at `end' is included in the writeout request. */ -- cgit From c15f1c83251049182b1771da004d14f29683ab97 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 14 Feb 2012 00:24:10 +0100 Subject: netfilter: ipset: use NFPROTO_ constants ipset is actually using NFPROTO values rather than AF (xt_set passes that along). Signed-off-by: Jan Engelhardt Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/ipset/ip_set.h | 5 ++++- net/netfilter/ipset/ip_set_bitmap_ip.c | 4 ++-- net/netfilter/ipset/ip_set_bitmap_ipmac.c | 4 ++-- net/netfilter/ipset/ip_set_bitmap_port.c | 4 ++-- net/netfilter/ipset/ip_set_core.c | 16 ++++++++-------- net/netfilter/ipset/ip_set_getport.c | 4 ++-- net/netfilter/ipset/ip_set_hash_ip.c | 18 +++++++++--------- net/netfilter/ipset/ip_set_hash_ipport.c | 10 +++++----- net/netfilter/ipset/ip_set_hash_ipportip.c | 10 +++++----- net/netfilter/ipset/ip_set_hash_ipportnet.c | 12 ++++++------ net/netfilter/ipset/ip_set_hash_net.c | 12 ++++++------ net/netfilter/ipset/ip_set_hash_netiface.c | 12 ++++++------ net/netfilter/ipset/ip_set_hash_netport.c | 12 ++++++------ net/netfilter/ipset/ip_set_list_set.c | 2 +- 14 files changed, 64 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 3540c6e262f7..e7b06f52be84 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -288,7 +288,10 @@ struct ip_set_type { u8 features; /* Set type dimension */ u8 dimension; - /* Supported family: may be AF_UNSPEC for both AF_INET/AF_INET6 */ + /* + * Supported family: may be NFPROTO_UNSPEC for both + * NFPROTO_IPV4/NFPROTO_IPV6. + */ u8 family; /* Type revisions */ u8 revision_min, revision_max; diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c index e3e73997c3be..a72a4dff0031 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ip.c +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c @@ -442,7 +442,7 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map, map->timeout = IPSET_NO_TIMEOUT; set->data = map; - set->family = AF_INET; + set->family = NFPROTO_IPV4; return true; } @@ -550,7 +550,7 @@ static struct ip_set_type bitmap_ip_type __read_mostly = { .protocol = IPSET_PROTOCOL, .features = IPSET_TYPE_IP, .dimension = IPSET_DIM_ONE, - .family = AF_INET, + .family = NFPROTO_IPV4, .revision_min = 0, .revision_max = 0, .create = bitmap_ip_create, diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c index 56096f544978..81324c12c5be 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c @@ -543,7 +543,7 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map, map->timeout = IPSET_NO_TIMEOUT; set->data = map; - set->family = AF_INET; + set->family = NFPROTO_IPV4; return true; } @@ -623,7 +623,7 @@ static struct ip_set_type bitmap_ipmac_type = { .protocol = IPSET_PROTOCOL, .features = IPSET_TYPE_IP | IPSET_TYPE_MAC, .dimension = IPSET_DIM_TWO, - .family = AF_INET, + .family = NFPROTO_IPV4, .revision_min = 0, .revision_max = 0, .create = bitmap_ipmac_create, diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c index 29ba93bb94be..382ec28ba72e 100644 --- a/net/netfilter/ipset/ip_set_bitmap_port.c +++ b/net/netfilter/ipset/ip_set_bitmap_port.c @@ -422,7 +422,7 @@ init_map_port(struct ip_set *set, struct bitmap_port *map, map->timeout = IPSET_NO_TIMEOUT; set->data = map; - set->family = AF_UNSPEC; + set->family = NFPROTO_UNSPEC; return true; } @@ -483,7 +483,7 @@ static struct ip_set_type bitmap_port_type = { .protocol = IPSET_PROTOCOL, .features = IPSET_TYPE_PORT, .dimension = IPSET_DIM_ONE, - .family = AF_UNSPEC, + .family = NFPROTO_UNSPEC, .revision_min = 0, .revision_max = 0, .create = bitmap_port_create, diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index e7f90e7082b4..e6c1c9605a58 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -69,7 +69,7 @@ find_set_type(const char *name, u8 family, u8 revision) list_for_each_entry_rcu(type, &ip_set_type_list, list) if (STREQ(type->name, name) && - (type->family == family || type->family == AF_UNSPEC) && + (type->family == family || type->family == NFPROTO_UNSPEC) && revision >= type->revision_min && revision <= type->revision_max) return type; @@ -149,7 +149,7 @@ __find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max, rcu_read_lock(); list_for_each_entry_rcu(type, &ip_set_type_list, list) if (STREQ(type->name, name) && - (type->family == family || type->family == AF_UNSPEC)) { + (type->family == family || type->family == NFPROTO_UNSPEC)) { found = true; if (type->revision_min < *min) *min = type->revision_min; @@ -164,8 +164,8 @@ __find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max, __find_set_type_minmax(name, family, min, max, true); } -#define family_name(f) ((f) == AF_INET ? "inet" : \ - (f) == AF_INET6 ? "inet6" : "any") +#define family_name(f) ((f) == NFPROTO_IPV4 ? "inet" : \ + (f) == NFPROTO_IPV6 ? "inet6" : "any") /* Register a set type structure. The type is identified by * the unique triple of name, family and revision. @@ -354,7 +354,7 @@ ip_set_test(ip_set_id_t index, const struct sk_buff *skb, pr_debug("set %s, index %u\n", set->name, index); if (opt->dim < set->type->dimension || - !(opt->family == set->family || set->family == AF_UNSPEC)) + !(opt->family == set->family || set->family == NFPROTO_UNSPEC)) return 0; read_lock_bh(&set->lock); @@ -387,7 +387,7 @@ ip_set_add(ip_set_id_t index, const struct sk_buff *skb, pr_debug("set %s, index %u\n", set->name, index); if (opt->dim < set->type->dimension || - !(opt->family == set->family || set->family == AF_UNSPEC)) + !(opt->family == set->family || set->family == NFPROTO_UNSPEC)) return 0; write_lock_bh(&set->lock); @@ -410,7 +410,7 @@ ip_set_del(ip_set_id_t index, const struct sk_buff *skb, pr_debug("set %s, index %u\n", set->name, index); if (opt->dim < set->type->dimension || - !(opt->family == set->family || set->family == AF_UNSPEC)) + !(opt->family == set->family || set->family == NFPROTO_UNSPEC)) return 0; write_lock_bh(&set->lock); @@ -575,7 +575,7 @@ start_msg(struct sk_buff *skb, u32 pid, u32 seq, unsigned int flags, return NULL; nfmsg = nlmsg_data(nlh); - nfmsg->nfgen_family = AF_INET; + nfmsg->nfgen_family = NFPROTO_IPV4; nfmsg->version = NFNETLINK_V0; nfmsg->res_id = 0; diff --git a/net/netfilter/ipset/ip_set_getport.c b/net/netfilter/ipset/ip_set_getport.c index 1f03556666f4..6fdf88ae2353 100644 --- a/net/netfilter/ipset/ip_set_getport.c +++ b/net/netfilter/ipset/ip_set_getport.c @@ -136,10 +136,10 @@ ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port) u8 proto; switch (pf) { - case AF_INET: + case NFPROTO_IPV4: ret = ip_set_get_ip4_port(skb, src, port, &proto); break; - case AF_INET6: + case NFPROTO_IPV6: ret = ip_set_get_ip6_port(skb, src, port, &proto); break; default: diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c index 4015fcaf87bc..5139dea6019e 100644 --- a/net/netfilter/ipset/ip_set_hash_ip.c +++ b/net/netfilter/ipset/ip_set_hash_ip.c @@ -366,11 +366,11 @@ hash_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags) u8 netmask, hbits; struct ip_set_hash *h; - if (!(set->family == AF_INET || set->family == AF_INET6)) + if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6)) return -IPSET_ERR_INVALID_FAMILY; - netmask = set->family == AF_INET ? 32 : 128; + netmask = set->family == NFPROTO_IPV4 ? 32 : 128; pr_debug("Create set %s with family %s\n", - set->name, set->family == AF_INET ? "inet" : "inet6"); + set->name, set->family == NFPROTO_IPV4 ? "inet" : "inet6"); if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) || @@ -389,8 +389,8 @@ hash_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags) if (tb[IPSET_ATTR_NETMASK]) { netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]); - if ((set->family == AF_INET && netmask > 32) || - (set->family == AF_INET6 && netmask > 128) || + if ((set->family == NFPROTO_IPV4 && netmask > 32) || + (set->family == NFPROTO_IPV6 && netmask > 128) || netmask == 0) return -IPSET_ERR_INVALID_NETMASK; } @@ -419,15 +419,15 @@ hash_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags) if (tb[IPSET_ATTR_TIMEOUT]) { h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); - set->variant = set->family == AF_INET + set->variant = set->family == NFPROTO_IPV4 ? &hash_ip4_tvariant : &hash_ip6_tvariant; - if (set->family == AF_INET) + if (set->family == NFPROTO_IPV4) hash_ip4_gc_init(set); else hash_ip6_gc_init(set); } else { - set->variant = set->family == AF_INET + set->variant = set->family == NFPROTO_IPV4 ? &hash_ip4_variant : &hash_ip6_variant; } @@ -443,7 +443,7 @@ static struct ip_set_type hash_ip_type __read_mostly = { .protocol = IPSET_PROTOCOL, .features = IPSET_TYPE_IP, .dimension = IPSET_DIM_ONE, - .family = AF_UNSPEC, + .family = NFPROTO_UNSPEC, .revision_min = 0, .revision_max = 0, .create = hash_ip_create, diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c index 37d667e3f6f8..9c27e249c171 100644 --- a/net/netfilter/ipset/ip_set_hash_ipport.c +++ b/net/netfilter/ipset/ip_set_hash_ipport.c @@ -450,7 +450,7 @@ hash_ipport_create(struct ip_set *set, struct nlattr *tb[], u32 flags) u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; u8 hbits; - if (!(set->family == AF_INET || set->family == AF_INET6)) + if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6)) return -IPSET_ERR_INVALID_FAMILY; if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || @@ -490,15 +490,15 @@ hash_ipport_create(struct ip_set *set, struct nlattr *tb[], u32 flags) if (tb[IPSET_ATTR_TIMEOUT]) { h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); - set->variant = set->family == AF_INET + set->variant = set->family == NFPROTO_IPV4 ? &hash_ipport4_tvariant : &hash_ipport6_tvariant; - if (set->family == AF_INET) + if (set->family == NFPROTO_IPV4) hash_ipport4_gc_init(set); else hash_ipport6_gc_init(set); } else { - set->variant = set->family == AF_INET + set->variant = set->family == NFPROTO_IPV4 ? &hash_ipport4_variant : &hash_ipport6_variant; } @@ -514,7 +514,7 @@ static struct ip_set_type hash_ipport_type __read_mostly = { .protocol = IPSET_PROTOCOL, .features = IPSET_TYPE_IP | IPSET_TYPE_PORT, .dimension = IPSET_DIM_TWO, - .family = AF_UNSPEC, + .family = NFPROTO_UNSPEC, .revision_min = 0, .revision_max = 1, /* SCTP and UDPLITE support added */ .create = hash_ipport_create, diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c index e69e2718fbe1..9134057c0728 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportip.c +++ b/net/netfilter/ipset/ip_set_hash_ipportip.c @@ -468,7 +468,7 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *tb[], u32 flags) u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; u8 hbits; - if (!(set->family == AF_INET || set->family == AF_INET6)) + if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6)) return -IPSET_ERR_INVALID_FAMILY; if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || @@ -508,15 +508,15 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *tb[], u32 flags) if (tb[IPSET_ATTR_TIMEOUT]) { h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); - set->variant = set->family == AF_INET + set->variant = set->family == NFPROTO_IPV4 ? &hash_ipportip4_tvariant : &hash_ipportip6_tvariant; - if (set->family == AF_INET) + if (set->family == NFPROTO_IPV4) hash_ipportip4_gc_init(set); else hash_ipportip6_gc_init(set); } else { - set->variant = set->family == AF_INET + set->variant = set->family == NFPROTO_IPV4 ? &hash_ipportip4_variant : &hash_ipportip6_variant; } @@ -532,7 +532,7 @@ static struct ip_set_type hash_ipportip_type __read_mostly = { .protocol = IPSET_PROTOCOL, .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, .dimension = IPSET_DIM_THREE, - .family = AF_UNSPEC, + .family = NFPROTO_UNSPEC, .revision_min = 0, .revision_max = 1, /* SCTP and UDPLITE support added */ .create = hash_ipportip_create, diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 64199b4e93c9..0edb35b81e36 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -554,7 +554,7 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *tb[], u32 flags) u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; u8 hbits; - if (!(set->family == AF_INET || set->family == AF_INET6)) + if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6)) return -IPSET_ERR_INVALID_FAMILY; if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || @@ -573,7 +573,7 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *tb[], u32 flags) h = kzalloc(sizeof(*h) + sizeof(struct ip_set_hash_nets) - * (set->family == AF_INET ? 32 : 128), GFP_KERNEL); + * (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL); if (!h) return -ENOMEM; @@ -596,16 +596,16 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *tb[], u32 flags) if (tb[IPSET_ATTR_TIMEOUT]) { h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); - set->variant = set->family == AF_INET + set->variant = set->family == NFPROTO_IPV4 ? &hash_ipportnet4_tvariant : &hash_ipportnet6_tvariant; - if (set->family == AF_INET) + if (set->family == NFPROTO_IPV4) hash_ipportnet4_gc_init(set); else hash_ipportnet6_gc_init(set); } else { - set->variant = set->family == AF_INET + set->variant = set->family == NFPROTO_IPV4 ? &hash_ipportnet4_variant : &hash_ipportnet6_variant; } @@ -621,7 +621,7 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = { .protocol = IPSET_PROTOCOL, .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, .dimension = IPSET_DIM_THREE, - .family = AF_UNSPEC, + .family = NFPROTO_UNSPEC, .revision_min = 0, /* 1 SCTP and UDPLITE support added */ .revision_max = 2, /* Range as input support for IPv4 added */ diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index 28988196775e..5a4457adaf85 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -406,7 +406,7 @@ hash_net_create(struct ip_set *set, struct nlattr *tb[], u32 flags) struct ip_set_hash *h; u8 hbits; - if (!(set->family == AF_INET || set->family == AF_INET6)) + if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6)) return -IPSET_ERR_INVALID_FAMILY; if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || @@ -425,7 +425,7 @@ hash_net_create(struct ip_set *set, struct nlattr *tb[], u32 flags) h = kzalloc(sizeof(*h) + sizeof(struct ip_set_hash_nets) - * (set->family == AF_INET ? 32 : 128), GFP_KERNEL); + * (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL); if (!h) return -ENOMEM; @@ -448,15 +448,15 @@ hash_net_create(struct ip_set *set, struct nlattr *tb[], u32 flags) if (tb[IPSET_ATTR_TIMEOUT]) { h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); - set->variant = set->family == AF_INET + set->variant = set->family == NFPROTO_IPV4 ? &hash_net4_tvariant : &hash_net6_tvariant; - if (set->family == AF_INET) + if (set->family == NFPROTO_IPV4) hash_net4_gc_init(set); else hash_net6_gc_init(set); } else { - set->variant = set->family == AF_INET + set->variant = set->family == NFPROTO_IPV4 ? &hash_net4_variant : &hash_net6_variant; } @@ -472,7 +472,7 @@ static struct ip_set_type hash_net_type __read_mostly = { .protocol = IPSET_PROTOCOL, .features = IPSET_TYPE_IP, .dimension = IPSET_DIM_ONE, - .family = AF_UNSPEC, + .family = NFPROTO_UNSPEC, .revision_min = 0, .revision_max = 1, /* Range as input support for IPv4 added */ .create = hash_net_create, diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c index e13095deb50d..a9fb4afafa8b 100644 --- a/net/netfilter/ipset/ip_set_hash_netiface.c +++ b/net/netfilter/ipset/ip_set_hash_netiface.c @@ -678,7 +678,7 @@ hash_netiface_create(struct ip_set *set, struct nlattr *tb[], u32 flags) u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; u8 hbits; - if (!(set->family == AF_INET || set->family == AF_INET6)) + if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6)) return -IPSET_ERR_INVALID_FAMILY; if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || @@ -697,7 +697,7 @@ hash_netiface_create(struct ip_set *set, struct nlattr *tb[], u32 flags) h = kzalloc(sizeof(*h) + sizeof(struct ip_set_hash_nets) - * (set->family == AF_INET ? 32 : 128), GFP_KERNEL); + * (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL); if (!h) return -ENOMEM; @@ -722,15 +722,15 @@ hash_netiface_create(struct ip_set *set, struct nlattr *tb[], u32 flags) if (tb[IPSET_ATTR_TIMEOUT]) { h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); - set->variant = set->family == AF_INET + set->variant = set->family == NFPROTO_IPV4 ? &hash_netiface4_tvariant : &hash_netiface6_tvariant; - if (set->family == AF_INET) + if (set->family == NFPROTO_IPV4) hash_netiface4_gc_init(set); else hash_netiface6_gc_init(set); } else { - set->variant = set->family == AF_INET + set->variant = set->family == NFPROTO_IPV4 ? &hash_netiface4_variant : &hash_netiface6_variant; } @@ -746,7 +746,7 @@ static struct ip_set_type hash_netiface_type __read_mostly = { .protocol = IPSET_PROTOCOL, .features = IPSET_TYPE_IP | IPSET_TYPE_IFACE, .dimension = IPSET_DIM_TWO, - .family = AF_UNSPEC, + .family = NFPROTO_UNSPEC, .revision_min = 0, .create = hash_netiface_create, .create_policy = { diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index 8f9de7207ec9..1fcc1020eaae 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -507,7 +507,7 @@ hash_netport_create(struct ip_set *set, struct nlattr *tb[], u32 flags) u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; u8 hbits; - if (!(set->family == AF_INET || set->family == AF_INET6)) + if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6)) return -IPSET_ERR_INVALID_FAMILY; if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || @@ -526,7 +526,7 @@ hash_netport_create(struct ip_set *set, struct nlattr *tb[], u32 flags) h = kzalloc(sizeof(*h) + sizeof(struct ip_set_hash_nets) - * (set->family == AF_INET ? 32 : 128), GFP_KERNEL); + * (set->family == NFPROTO_IPV4 ? 32 : 128), GFP_KERNEL); if (!h) return -ENOMEM; @@ -549,15 +549,15 @@ hash_netport_create(struct ip_set *set, struct nlattr *tb[], u32 flags) if (tb[IPSET_ATTR_TIMEOUT]) { h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); - set->variant = set->family == AF_INET + set->variant = set->family == NFPROTO_IPV4 ? &hash_netport4_tvariant : &hash_netport6_tvariant; - if (set->family == AF_INET) + if (set->family == NFPROTO_IPV4) hash_netport4_gc_init(set); else hash_netport6_gc_init(set); } else { - set->variant = set->family == AF_INET + set->variant = set->family == NFPROTO_IPV4 ? &hash_netport4_variant : &hash_netport6_variant; } @@ -573,7 +573,7 @@ static struct ip_set_type hash_netport_type __read_mostly = { .protocol = IPSET_PROTOCOL, .features = IPSET_TYPE_IP | IPSET_TYPE_PORT, .dimension = IPSET_DIM_TWO, - .family = AF_UNSPEC, + .family = NFPROTO_UNSPEC, .revision_min = 0, /* 1 SCTP and UDPLITE support added */ .revision_max = 2, /* Range as input support for IPv4 added */ diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 4d10819d462e..7e095f9005f0 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -575,7 +575,7 @@ static struct ip_set_type list_set_type __read_mostly = { .protocol = IPSET_PROTOCOL, .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST, .dimension = IPSET_DIM_ONE, - .family = AF_UNSPEC, + .family = NFPROTO_UNSPEC, .revision_min = 0, .revision_max = 0, .create = list_set_create, -- cgit From ae8ded1cb88b9c24f3c9552ca9eefd894b069716 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 14 Jan 2012 16:26:20 +0100 Subject: netfilter: ipset: expose userspace-relevant parts in ip_set.h iptables's libxt_SET.c depends on these. Signed-off-by: Jan Engelhardt Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/ipset/ip_set.h | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index e7b06f52be84..e921766d3aff 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -11,6 +11,8 @@ * published by the Free Software Foundation. */ +#include + /* The protocol version */ #define IPSET_PROTOCOL 6 @@ -168,19 +170,10 @@ enum ipset_adt { IPSET_CADT_MAX, }; -#ifdef __KERNEL__ -#include -#include -#include -#include -#include -#include -#include - /* Sets are identified by an index in kernel space. Tweak with ip_set_id_t * and IPSET_INVALID_ID if you want to increase the max number of sets. */ -typedef u16 ip_set_id_t; +typedef __u16 ip_set_id_t; #define IPSET_INVALID_ID 65535 @@ -203,6 +196,15 @@ enum ip_set_kopt { IPSET_DIM_THREE_SRC = (1 << IPSET_DIM_THREE), }; +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#include + /* Set features */ enum ip_set_feature { IPSET_TYPE_IP_FLAG = 0, @@ -453,6 +455,8 @@ bitmap_bytes(u32 a, u32 b) return 4 * ((((b - a + 8) / 8) + 3) / 4); } +#endif /* __KERNEL__ */ + /* Interface to iptables/ip6tables */ #define SO_IP_SET 83 @@ -478,6 +482,4 @@ struct ip_set_req_version { unsigned version; }; -#endif /* __KERNEL__ */ - #endif /*_IP_SET_H */ -- cgit From 0927a1ac63388271d58e9f7352d71434e1271374 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 10 Jan 2012 17:04:32 +0100 Subject: netfilter: ipset: Log warning when a hash type of set gets full If the set is full, the SET target cannot add more elements. Log warning so that the admin got notified about it. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/ipset/ip_set_ahash.h | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set_ahash.h b/include/linux/netfilter/ipset/ip_set_ahash.h index b89fb79cb44f..bd1fc8d16851 100644 --- a/include/linux/netfilter/ipset/ip_set_ahash.h +++ b/include/linux/netfilter/ipset/ip_set_ahash.h @@ -353,9 +353,12 @@ retry: htable_bits++; pr_debug("attempt to resize set %s from %u to %u, t %p\n", set->name, orig->htable_bits, htable_bits, orig); - if (!htable_bits) + if (!htable_bits) { /* In case we have plenty of memory :-) */ + pr_warning("Cannot increase the hashsize of set %s further\n", + set->name); return -IPSET_ERR_HASH_FULL; + } t = ip_set_alloc(sizeof(*t) + jhash_size(htable_bits) * sizeof(struct hbucket)); if (!t) @@ -407,8 +410,12 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags) int i, ret = 0; u32 key, multi = 0; - if (h->elements >= h->maxelem) + if (h->elements >= h->maxelem) { + if (net_ratelimit()) + pr_warning("Set %s is full, maxelem %u reached\n", + set->name, h->maxelem); return -IPSET_ERR_HASH_FULL; + } rcu_read_lock_bh(); t = rcu_dereference_bh(h->table); @@ -790,9 +797,12 @@ type_pf_tresize(struct ip_set *set, bool retried) retry: ret = 0; htable_bits++; - if (!htable_bits) + if (!htable_bits) { /* In case we have plenty of memory :-) */ + pr_warning("Cannot increase the hashsize of set %s further\n", + set->name); return -IPSET_ERR_HASH_FULL; + } t = ip_set_alloc(sizeof(*t) + jhash_size(htable_bits) * sizeof(struct hbucket)); if (!t) @@ -843,8 +853,12 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) if (h->elements >= h->maxelem) /* FIXME: when set is full, we slow down here */ type_pf_expire(h); - if (h->elements >= h->maxelem) + if (h->elements >= h->maxelem) { + if (net_ratelimit()) + pr_warning("Set %s is full, maxelem %u reached\n", + set->name, h->maxelem); return -IPSET_ERR_HASH_FULL; + } rcu_read_lock_bh(); t = rcu_dereference_bh(h->table); -- cgit From 2a7cef2a4ba64b9bf0ff9aeaa364554716c06669 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Sat, 14 Jan 2012 17:16:36 +0100 Subject: netfilter: ipset: Exceptions support added to hash:*net* types The "nomatch" keyword and option is added to the hash:*net* types, by which one can add exception entries to sets. Example: ipset create test hash:net ipset add test 192.168.0/24 ipset add test 192.168.0/30 nomatch In this case the IP addresses from 192.168.0/24 except 192.168.0/30 match the elements of the set. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/ipset/ip_set.h | 4 + include/linux/netfilter/ipset/ip_set_ahash.h | 89 ++++++++++++----- net/netfilter/ipset/ip_set_hash_ipportnet.c | 135 +++++++++++++++++++------- net/netfilter/ipset/ip_set_hash_net.c | 77 +++++++++++++-- net/netfilter/ipset/ip_set_hash_netiface.c | 72 +++++++++++--- net/netfilter/ipset/ip_set_hash_netport.c | 138 ++++++++++++++++++++------- 6 files changed, 399 insertions(+), 116 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index e921766d3aff..2f8e18a23227 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -150,6 +150,7 @@ enum ipset_cmd_flags { IPSET_FLAG_LIST_SETNAME = (1 << IPSET_FLAG_BIT_LIST_SETNAME), IPSET_FLAG_BIT_LIST_HEADER = 2, IPSET_FLAG_LIST_HEADER = (1 << IPSET_FLAG_BIT_LIST_HEADER), + IPSET_FLAG_CMD_MAX = 15, /* Lower half */ }; /* Flags at CADT attribute level */ @@ -158,6 +159,9 @@ enum ipset_cadt_flags { IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE), IPSET_FLAG_BIT_PHYSDEV = 1, IPSET_FLAG_PHYSDEV = (1 << IPSET_FLAG_BIT_PHYSDEV), + IPSET_FLAG_BIT_NOMATCH = 2, + IPSET_FLAG_NOMATCH = (1 << IPSET_FLAG_BIT_NOMATCH), + IPSET_FLAG_CADT_MAX = 15, /* Upper half */ }; /* Commands with settype-specific attributes */ diff --git a/include/linux/netfilter/ipset/ip_set_ahash.h b/include/linux/netfilter/ipset/ip_set_ahash.h index bd1fc8d16851..0e5c3cf7618a 100644 --- a/include/linux/netfilter/ipset/ip_set_ahash.h +++ b/include/linux/netfilter/ipset/ip_set_ahash.h @@ -113,6 +113,12 @@ htable_bits(u32 hashsize) } #ifdef IP_SET_HASH_WITH_NETS +#ifdef IP_SET_HASH_WITH_NETS_PACKED +/* When cidr is packed with nomatch, cidr - 1 is stored in the entry */ +#define CIDR(cidr) (cidr + 1) +#else +#define CIDR(cidr) (cidr) +#endif #define SET_HOST_MASK(family) (family == AF_INET ? 32 : 128) @@ -262,6 +268,12 @@ ip_set_hash_destroy(struct ip_set *set) #define type_pf_data_list TOKEN(TYPE, PF, _data_list) #define type_pf_data_tlist TOKEN(TYPE, PF, _data_tlist) #define type_pf_data_next TOKEN(TYPE, PF, _data_next) +#define type_pf_data_flags TOKEN(TYPE, PF, _data_flags) +#ifdef IP_SET_HASH_WITH_NETS +#define type_pf_data_match TOKEN(TYPE, PF, _data_match) +#else +#define type_pf_data_match(d) 1 +#endif #define type_pf_elem TOKEN(TYPE, PF, _elem) #define type_pf_telem TOKEN(TYPE, PF, _telem) @@ -308,8 +320,10 @@ ip_set_hash_destroy(struct ip_set *set) * we spare the maintenance of the internal counters. */ static int type_pf_elem_add(struct hbucket *n, const struct type_pf_elem *value, - u8 ahash_max) + u8 ahash_max, u32 cadt_flags) { + struct type_pf_elem *data; + if (n->pos >= n->size) { void *tmp; @@ -330,7 +344,13 @@ type_pf_elem_add(struct hbucket *n, const struct type_pf_elem *value, n->value = tmp; n->size += AHASH_INIT_SIZE; } - type_pf_data_copy(ahash_data(n, n->pos++), value); + data = ahash_data(n, n->pos++); + type_pf_data_copy(data, value); +#ifdef IP_SET_HASH_WITH_NETS + /* Resizing won't overwrite stored flags */ + if (cadt_flags) + type_pf_data_flags(data, cadt_flags); +#endif return 0; } @@ -371,7 +391,7 @@ retry: for (j = 0; j < n->pos; j++) { data = ahash_data(n, j); m = hbucket(t, HKEY(data, h->initval, htable_bits)); - ret = type_pf_elem_add(m, data, AHASH_MAX(h)); + ret = type_pf_elem_add(m, data, AHASH_MAX(h), 0); if (ret < 0) { read_unlock_bh(&set->lock); ahash_destroy(t); @@ -409,6 +429,7 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags) struct hbucket *n; int i, ret = 0; u32 key, multi = 0; + u32 cadt_flags = flags >> 16; if (h->elements >= h->maxelem) { if (net_ratelimit()) @@ -423,11 +444,17 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags) n = hbucket(t, key); for (i = 0; i < n->pos; i++) if (type_pf_data_equal(ahash_data(n, i), d, &multi)) { +#ifdef IP_SET_HASH_WITH_NETS + if (flags & IPSET_FLAG_EXIST) + /* Support overwriting just the flags */ + type_pf_data_flags(ahash_data(n, i), + cadt_flags); +#endif ret = -IPSET_ERR_EXIST; goto out; } TUNE_AHASH_MAX(h, multi); - ret = type_pf_elem_add(n, value, AHASH_MAX(h)); + ret = type_pf_elem_add(n, value, AHASH_MAX(h), cadt_flags); if (ret != 0) { if (ret == -EAGAIN) type_pf_data_next(h, d); @@ -435,7 +462,7 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags) } #ifdef IP_SET_HASH_WITH_NETS - add_cidr(h, d->cidr, HOST_MASK); + add_cidr(h, CIDR(d->cidr), HOST_MASK); #endif h->elements++; out: @@ -470,7 +497,7 @@ type_pf_del(struct ip_set *set, void *value, u32 timeout, u32 flags) n->pos--; h->elements--; #ifdef IP_SET_HASH_WITH_NETS - del_cidr(h, d->cidr, HOST_MASK); + del_cidr(h, CIDR(d->cidr), HOST_MASK); #endif if (n->pos + AHASH_INIT_SIZE < n->size) { void *tmp = kzalloc((n->size - AHASH_INIT_SIZE) @@ -513,7 +540,7 @@ type_pf_test_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout) for (i = 0; i < n->pos; i++) { data = ahash_data(n, i); if (type_pf_data_equal(data, d, &multi)) - return 1; + return type_pf_data_match(data); } } return 0; @@ -535,7 +562,7 @@ type_pf_test(struct ip_set *set, void *value, u32 timeout, u32 flags) #ifdef IP_SET_HASH_WITH_NETS /* If we test an IP address and not a network address, * try all possible network sizes */ - if (d->cidr == SET_HOST_MASK(set->family)) + if (CIDR(d->cidr) == SET_HOST_MASK(set->family)) return type_pf_test_cidrs(set, d, timeout); #endif @@ -544,7 +571,7 @@ type_pf_test(struct ip_set *set, void *value, u32 timeout, u32 flags) for (i = 0; i < n->pos; i++) { data = ahash_data(n, i); if (type_pf_data_equal(data, d, &multi)) - return 1; + return type_pf_data_match(data); } return 0; } @@ -700,7 +727,7 @@ type_pf_data_timeout_set(struct type_pf_elem *data, u32 timeout) static int type_pf_elem_tadd(struct hbucket *n, const struct type_pf_elem *value, - u8 ahash_max, u32 timeout) + u8 ahash_max, u32 cadt_flags, u32 timeout) { struct type_pf_elem *data; @@ -727,6 +754,11 @@ type_pf_elem_tadd(struct hbucket *n, const struct type_pf_elem *value, data = ahash_tdata(n, n->pos++); type_pf_data_copy(data, value); type_pf_data_timeout_set(data, timeout); +#ifdef IP_SET_HASH_WITH_NETS + /* Resizing won't overwrite stored flags */ + if (cadt_flags) + type_pf_data_flags(data, cadt_flags); +#endif return 0; } @@ -747,7 +779,7 @@ type_pf_expire(struct ip_set_hash *h) if (type_pf_data_expired(data)) { pr_debug("expired %u/%u\n", i, j); #ifdef IP_SET_HASH_WITH_NETS - del_cidr(h, data->cidr, HOST_MASK); + del_cidr(h, CIDR(data->cidr), HOST_MASK); #endif if (j != n->pos - 1) /* Not last one */ @@ -815,7 +847,7 @@ retry: for (j = 0; j < n->pos; j++) { data = ahash_tdata(n, j); m = hbucket(t, HKEY(data, h->initval, htable_bits)); - ret = type_pf_elem_tadd(m, data, AHASH_MAX(h), + ret = type_pf_elem_tadd(m, data, AHASH_MAX(h), 0, type_pf_data_timeout(data)); if (ret < 0) { read_unlock_bh(&set->lock); @@ -849,6 +881,7 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) int ret = 0, i, j = AHASH_MAX(h) + 1; bool flag_exist = flags & IPSET_FLAG_EXIST; u32 key, multi = 0; + u32 cadt_flags = flags >> 16; if (h->elements >= h->maxelem) /* FIXME: when set is full, we slow down here */ @@ -868,6 +901,7 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) data = ahash_tdata(n, i); if (type_pf_data_equal(data, d, &multi)) { if (type_pf_data_expired(data) || flag_exist) + /* Just timeout value may be updated */ j = i; else { ret = -IPSET_ERR_EXIST; @@ -880,15 +914,18 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) if (j != AHASH_MAX(h) + 1) { data = ahash_tdata(n, j); #ifdef IP_SET_HASH_WITH_NETS - del_cidr(h, data->cidr, HOST_MASK); - add_cidr(h, d->cidr, HOST_MASK); + del_cidr(h, CIDR(data->cidr), HOST_MASK); + add_cidr(h, CIDR(d->cidr), HOST_MASK); #endif type_pf_data_copy(data, d); type_pf_data_timeout_set(data, timeout); +#ifdef IP_SET_HASH_WITH_NETS + type_pf_data_flags(data, cadt_flags); +#endif goto out; } TUNE_AHASH_MAX(h, multi); - ret = type_pf_elem_tadd(n, d, AHASH_MAX(h), timeout); + ret = type_pf_elem_tadd(n, d, AHASH_MAX(h), cadt_flags, timeout); if (ret != 0) { if (ret == -EAGAIN) type_pf_data_next(h, d); @@ -896,7 +933,7 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) } #ifdef IP_SET_HASH_WITH_NETS - add_cidr(h, d->cidr, HOST_MASK); + add_cidr(h, CIDR(d->cidr), HOST_MASK); #endif h->elements++; out: @@ -930,7 +967,7 @@ type_pf_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags) n->pos--; h->elements--; #ifdef IP_SET_HASH_WITH_NETS - del_cidr(h, d->cidr, HOST_MASK); + del_cidr(h, CIDR(d->cidr), HOST_MASK); #endif if (n->pos + AHASH_INIT_SIZE < n->size) { void *tmp = kzalloc((n->size - AHASH_INIT_SIZE) @@ -968,8 +1005,9 @@ type_pf_ttest_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout) n = hbucket(t, key); for (i = 0; i < n->pos; i++) { data = ahash_tdata(n, i); - if (type_pf_data_equal(data, d, &multi)) - return !type_pf_data_expired(data); + if (type_pf_data_equal(data, d, &multi) && + !type_pf_data_expired(data)) + return type_pf_data_match(data); } } return 0; @@ -987,15 +1025,16 @@ type_pf_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags) u32 key, multi = 0; #ifdef IP_SET_HASH_WITH_NETS - if (d->cidr == SET_HOST_MASK(set->family)) + if (CIDR(d->cidr) == SET_HOST_MASK(set->family)) return type_pf_ttest_cidrs(set, d, timeout); #endif key = HKEY(d, h->initval, t->htable_bits); n = hbucket(t, key); for (i = 0; i < n->pos; i++) { data = ahash_tdata(n, i); - if (type_pf_data_equal(data, d, &multi)) - return !type_pf_data_expired(data); + if (type_pf_data_equal(data, d, &multi) && + !type_pf_data_expired(data)) + return type_pf_data_match(data); } return 0; } @@ -1108,14 +1147,17 @@ type_pf_gc_init(struct ip_set *set) #undef type_pf_data_isnull #undef type_pf_data_copy #undef type_pf_data_zero_out +#undef type_pf_data_netmask #undef type_pf_data_list #undef type_pf_data_tlist +#undef type_pf_data_next +#undef type_pf_data_flags +#undef type_pf_data_match #undef type_pf_elem #undef type_pf_telem #undef type_pf_data_timeout #undef type_pf_data_expired -#undef type_pf_data_netmask #undef type_pf_data_timeout_set #undef type_pf_elem_add @@ -1125,6 +1167,7 @@ type_pf_gc_init(struct ip_set *set) #undef type_pf_test #undef type_pf_elem_tadd +#undef type_pf_del_telem #undef type_pf_expire #undef type_pf_tadd #undef type_pf_tdel diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 0edb35b81e36..5d05e6969862 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -41,12 +41,19 @@ hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b); /* The type variant functions: IPv4 */ +/* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0 + * However this way we have to store internally cidr - 1, + * dancing back and forth. + */ +#define IP_SET_HASH_WITH_NETS_PACKED + /* Member elements without timeout */ struct hash_ipportnet4_elem { __be32 ip; __be32 ip2; __be16 port; - u8 cidr; + u8 cidr:7; + u8 nomatch:1; u8 proto; }; @@ -55,7 +62,8 @@ struct hash_ipportnet4_telem { __be32 ip; __be32 ip2; __be16 port; - u8 cidr; + u8 cidr:7; + u8 nomatch:1; u8 proto; unsigned long timeout; }; @@ -85,11 +93,23 @@ hash_ipportnet4_data_copy(struct hash_ipportnet4_elem *dst, memcpy(dst, src, sizeof(*dst)); } +static inline void +hash_ipportnet4_data_flags(struct hash_ipportnet4_elem *dst, u32 flags) +{ + dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH); +} + +static inline bool +hash_ipportnet4_data_match(const struct hash_ipportnet4_elem *elem) +{ + return !elem->nomatch; +} + static inline void hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr) { elem->ip2 &= ip_set_netmask(cidr); - elem->cidr = cidr; + elem->cidr = cidr - 1; } static inline void @@ -102,11 +122,15 @@ static bool hash_ipportnet4_data_list(struct sk_buff *skb, const struct hash_ipportnet4_elem *data) { + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr + 1); NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + if (flags) + NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); return 0; nla_put_failure: @@ -119,14 +143,17 @@ hash_ipportnet4_data_tlist(struct sk_buff *skb, { const struct hash_ipportnet4_telem *tdata = (const struct hash_ipportnet4_telem *)data; + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, tdata->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr + 1); NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(tdata->timeout))); + if (flags) + NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); return 0; @@ -158,13 +185,11 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet4_elem data = { - .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK + .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1 }; - if (data.cidr == 0) - return -EINVAL; if (adt == IPSET_TEST) - data.cidr = HOST_MASK; + data.cidr = HOST_MASK - 1; if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &data.port, &data.proto)) @@ -172,7 +197,7 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip); ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2); - data.ip2 &= ip_set_netmask(data.cidr); + data.ip2 &= ip_set_netmask(data.cidr + 1); return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); } @@ -183,17 +208,19 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportnet4_elem data = { .cidr = HOST_MASK }; + struct hash_ipportnet4_elem data = { .cidr = HOST_MASK - 1 }; u32 ip, ip_to = 0, p = 0, port, port_to; u32 ip2_from = 0, ip2_to, ip2_last, ip2; u32 timeout = h->timeout; bool with_ports = false; + u8 cidr; int ret; if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) return -IPSET_ERR_PROTOCOL; if (tb[IPSET_ATTR_LINENO]) @@ -208,9 +235,10 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; if (tb[IPSET_ATTR_CIDR2]) { - data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); - if (!data.cidr) + cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); + if (!cidr || cidr > HOST_MASK) return -IPSET_ERR_INVALID_CIDR; + data.cidr = cidr - 1; } if (tb[IPSET_ATTR_PORT]) @@ -236,12 +264,18 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } + if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) { + u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_NOMATCH) + flags |= (cadt_flags << 16); + } + with_ports = with_ports && tb[IPSET_ATTR_PORT_TO]; if (adt == IPSET_TEST || !(tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_IP_TO] || with_ports || tb[IPSET_ATTR_IP2_TO])) { data.ip = htonl(ip); - data.ip2 = htonl(ip2_from & ip_set_hostmask(data.cidr)); + data.ip2 = htonl(ip2_from & ip_set_hostmask(data.cidr + 1)); ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -275,7 +309,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], if (ip2_from + UINT_MAX == ip2_to) return -IPSET_ERR_HASH_RANGE; } else { - ip_set_mask_from_to(ip2_from, ip2_to, data.cidr); + ip_set_mask_from_to(ip2_from, ip2_to, data.cidr + 1); } if (retried) @@ -290,7 +324,8 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], while (!after(ip2, ip2_to)) { data.ip2 = htonl(ip2); ip2_last = ip_set_range_to_cidr(ip2, ip2_to, - &data.cidr); + &cidr); + data.cidr = cidr - 1; ret = adtfn(set, &data, timeout, flags); if (ret && !ip_set_eexist(ret, flags)) @@ -321,7 +356,8 @@ struct hash_ipportnet6_elem { union nf_inet_addr ip; union nf_inet_addr ip2; __be16 port; - u8 cidr; + u8 cidr:7; + u8 nomatch:1; u8 proto; }; @@ -329,7 +365,8 @@ struct hash_ipportnet6_telem { union nf_inet_addr ip; union nf_inet_addr ip2; __be16 port; - u8 cidr; + u8 cidr:7; + u8 nomatch:1; u8 proto; unsigned long timeout; }; @@ -359,6 +396,18 @@ hash_ipportnet6_data_copy(struct hash_ipportnet6_elem *dst, memcpy(dst, src, sizeof(*dst)); } +static inline void +hash_ipportnet6_data_flags(struct hash_ipportnet6_elem *dst, u32 flags) +{ + dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH); +} + +static inline bool +hash_ipportnet6_data_match(const struct hash_ipportnet6_elem *elem) +{ + return !elem->nomatch; +} + static inline void hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem) { @@ -378,18 +427,22 @@ static inline void hash_ipportnet6_data_netmask(struct hash_ipportnet6_elem *elem, u8 cidr) { ip6_netmask(&elem->ip2, cidr); - elem->cidr = cidr; + elem->cidr = cidr - 1; } static bool hash_ipportnet6_data_list(struct sk_buff *skb, const struct hash_ipportnet6_elem *data) { + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr + 1); NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + if (flags) + NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); return 0; nla_put_failure: @@ -402,14 +455,17 @@ hash_ipportnet6_data_tlist(struct sk_buff *skb, { const struct hash_ipportnet6_telem *e = (const struct hash_ipportnet6_telem *)data; + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr + 1); NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(e->timeout))); + if (flags) + NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); return 0; nla_put_failure: @@ -438,13 +494,11 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet6_elem data = { - .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK + .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1 }; - if (data.cidr == 0) - return -EINVAL; if (adt == IPSET_TEST) - data.cidr = HOST_MASK; + data.cidr = HOST_MASK - 1; if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &data.port, &data.proto)) @@ -452,7 +506,7 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6); ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2.in6); - ip6_netmask(&data.ip2, data.cidr); + ip6_netmask(&data.ip2, data.cidr + 1); return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); } @@ -463,16 +517,18 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_ipportnet6_elem data = { .cidr = HOST_MASK }; + struct hash_ipportnet6_elem data = { .cidr = HOST_MASK - 1 }; u32 port, port_to; u32 timeout = h->timeout; bool with_ports = false; + u8 cidr; int ret; if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR])) return -IPSET_ERR_PROTOCOL; @@ -490,13 +546,14 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], if (ret) return ret; - if (tb[IPSET_ATTR_CIDR2]) - data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); - - if (!data.cidr) - return -IPSET_ERR_INVALID_CIDR; + if (tb[IPSET_ATTR_CIDR2]) { + cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); + if (!cidr || cidr > HOST_MASK) + return -IPSET_ERR_INVALID_CIDR; + data.cidr = cidr - 1; + } - ip6_netmask(&data.ip2, data.cidr); + ip6_netmask(&data.ip2, data.cidr + 1); if (tb[IPSET_ATTR_PORT]) data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); @@ -521,6 +578,12 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } + if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) { + u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_NOMATCH) + flags |= (cadt_flags << 16); + } + if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; @@ -624,7 +687,8 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = { .family = NFPROTO_UNSPEC, .revision_min = 0, /* 1 SCTP and UDPLITE support added */ - .revision_max = 2, /* Range as input support for IPv4 added */ + /* 2 Range as input support for IPv4 added */ + .revision_max = 3, /* nomatch flag support added */ .create = hash_ipportnet_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -643,6 +707,7 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = { [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, }, diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index 5a4457adaf85..7c3d945517cf 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -43,7 +43,7 @@ hash_net_same_set(const struct ip_set *a, const struct ip_set *b); struct hash_net4_elem { __be32 ip; u16 padding0; - u8 padding1; + u8 nomatch; u8 cidr; }; @@ -51,7 +51,7 @@ struct hash_net4_elem { struct hash_net4_telem { __be32 ip; u16 padding0; - u8 padding1; + u8 nomatch; u8 cidr; unsigned long timeout; }; @@ -61,7 +61,8 @@ hash_net4_data_equal(const struct hash_net4_elem *ip1, const struct hash_net4_elem *ip2, u32 *multi) { - return ip1->ip == ip2->ip && ip1->cidr == ip2->cidr; + return ip1->ip == ip2->ip && + ip1->cidr == ip2->cidr; } static inline bool @@ -76,6 +77,19 @@ hash_net4_data_copy(struct hash_net4_elem *dst, { dst->ip = src->ip; dst->cidr = src->cidr; + dst->nomatch = src->nomatch; +} + +static inline void +hash_net4_data_flags(struct hash_net4_elem *dst, u32 flags) +{ + dst->nomatch = flags & IPSET_FLAG_NOMATCH; +} + +static inline bool +hash_net4_data_match(const struct hash_net4_elem *elem) +{ + return !elem->nomatch; } static inline void @@ -95,8 +109,12 @@ hash_net4_data_zero_out(struct hash_net4_elem *elem) static bool hash_net4_data_list(struct sk_buff *skb, const struct hash_net4_elem *data) { + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); + if (flags) + NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); return 0; nla_put_failure: @@ -108,11 +126,14 @@ hash_net4_data_tlist(struct sk_buff *skb, const struct hash_net4_elem *data) { const struct hash_net4_telem *tdata = (const struct hash_net4_telem *)data; + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); NLA_PUT_U8(skb, IPSET_ATTR_CIDR, tdata->cidr); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(tdata->timeout))); + if (flags) + NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); return 0; @@ -167,7 +188,8 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], int ret; if (unlikely(!tb[IPSET_ATTR_IP] || - !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) return -IPSET_ERR_PROTOCOL; if (tb[IPSET_ATTR_LINENO]) @@ -179,7 +201,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], if (tb[IPSET_ATTR_CIDR]) { data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); - if (!data.cidr) + if (!data.cidr || data.cidr > HOST_MASK) return -IPSET_ERR_INVALID_CIDR; } @@ -189,6 +211,12 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } + if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) { + u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_NOMATCH) + flags |= (cadt_flags << 16); + } + if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) { data.ip = htonl(ip & ip_set_hostmask(data.cidr)); ret = adtfn(set, &data, timeout, flags); @@ -236,14 +264,14 @@ hash_net_same_set(const struct ip_set *a, const struct ip_set *b) struct hash_net6_elem { union nf_inet_addr ip; u16 padding0; - u8 padding1; + u8 nomatch; u8 cidr; }; struct hash_net6_telem { union nf_inet_addr ip; u16 padding0; - u8 padding1; + u8 nomatch; u8 cidr; unsigned long timeout; }; @@ -269,6 +297,19 @@ hash_net6_data_copy(struct hash_net6_elem *dst, { dst->ip.in6 = src->ip.in6; dst->cidr = src->cidr; + dst->nomatch = src->nomatch; +} + +static inline void +hash_net6_data_flags(struct hash_net6_elem *dst, u32 flags) +{ + dst->nomatch = flags & IPSET_FLAG_NOMATCH; +} + +static inline bool +hash_net6_data_match(const struct hash_net6_elem *elem) +{ + return !elem->nomatch; } static inline void @@ -296,8 +337,12 @@ hash_net6_data_netmask(struct hash_net6_elem *elem, u8 cidr) static bool hash_net6_data_list(struct sk_buff *skb, const struct hash_net6_elem *data) { + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); + if (flags) + NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); return 0; nla_put_failure: @@ -309,11 +354,14 @@ hash_net6_data_tlist(struct sk_buff *skb, const struct hash_net6_elem *data) { const struct hash_net6_telem *e = (const struct hash_net6_telem *)data; + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); NLA_PUT_U8(skb, IPSET_ATTR_CIDR, e->cidr); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(e->timeout))); + if (flags) + NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); return 0; nla_put_failure: @@ -366,7 +414,8 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[], int ret; if (unlikely(!tb[IPSET_ATTR_IP] || - !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) return -IPSET_ERR_PROTOCOL; if (unlikely(tb[IPSET_ATTR_IP_TO])) return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; @@ -381,7 +430,7 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[], if (tb[IPSET_ATTR_CIDR]) data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); - if (!data.cidr) + if (!data.cidr || data.cidr > HOST_MASK) return -IPSET_ERR_INVALID_CIDR; ip6_netmask(&data.ip, data.cidr); @@ -392,6 +441,12 @@ hash_net6_uadt(struct ip_set *set, struct nlattr *tb[], timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } + if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) { + u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_NOMATCH) + flags |= (cadt_flags << 16); + } + ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; @@ -474,7 +529,8 @@ static struct ip_set_type hash_net_type __read_mostly = { .dimension = IPSET_DIM_ONE, .family = NFPROTO_UNSPEC, .revision_min = 0, - .revision_max = 1, /* Range as input support for IPv4 added */ + /* = 1 Range as input support for IPv4 added */ + .revision_max = 2, /* nomatch flag support added */ .create = hash_net_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -488,6 +544,7 @@ static struct ip_set_type hash_net_type __read_mostly = { [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c index a9fb4afafa8b..f24037ff4322 100644 --- a/net/netfilter/ipset/ip_set_hash_netiface.c +++ b/net/netfilter/ipset/ip_set_hash_netiface.c @@ -163,7 +163,8 @@ struct hash_netiface4_elem_hashed { __be32 ip; u8 physdev; u8 cidr; - u16 padding; + u8 nomatch; + u8 padding; }; #define HKEY_DATALEN sizeof(struct hash_netiface4_elem_hashed) @@ -173,7 +174,8 @@ struct hash_netiface4_elem { __be32 ip; u8 physdev; u8 cidr; - u16 padding; + u8 nomatch; + u8 padding; const char *iface; }; @@ -182,7 +184,8 @@ struct hash_netiface4_telem { __be32 ip; u8 physdev; u8 cidr; - u16 padding; + u8 nomatch; + u8 padding; const char *iface; unsigned long timeout; }; @@ -207,11 +210,25 @@ hash_netiface4_data_isnull(const struct hash_netiface4_elem *elem) static inline void hash_netiface4_data_copy(struct hash_netiface4_elem *dst, - const struct hash_netiface4_elem *src) { + const struct hash_netiface4_elem *src) +{ dst->ip = src->ip; dst->cidr = src->cidr; dst->physdev = src->physdev; dst->iface = src->iface; + dst->nomatch = src->nomatch; +} + +static inline void +hash_netiface4_data_flags(struct hash_netiface4_elem *dst, u32 flags) +{ + dst->nomatch = flags & IPSET_FLAG_NOMATCH; +} + +static inline bool +hash_netiface4_data_match(const struct hash_netiface4_elem *elem) +{ + return !elem->nomatch; } static inline void @@ -233,11 +250,13 @@ hash_netiface4_data_list(struct sk_buff *skb, { u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0; + if (data->nomatch) + flags |= IPSET_FLAG_NOMATCH; NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface); if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, flags); + NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); return 0; nla_put_failure: @@ -252,11 +271,13 @@ hash_netiface4_data_tlist(struct sk_buff *skb, (const struct hash_netiface4_telem *)data; u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0; + if (data->nomatch) + flags |= IPSET_FLAG_NOMATCH; NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface); if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, flags); + NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(tdata->timeout))); @@ -361,7 +382,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[], if (tb[IPSET_ATTR_CIDR]) { data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); - if (!data.cidr) + if (!data.cidr || data.cidr > HOST_MASK) return -IPSET_ERR_INVALID_CIDR; } @@ -387,6 +408,8 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[], u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); if (cadt_flags & IPSET_FLAG_PHYSDEV) data.physdev = 1; + if (adt == IPSET_ADD && (cadt_flags & IPSET_FLAG_NOMATCH)) + flags |= (cadt_flags << 16); } if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) { @@ -440,7 +463,8 @@ struct hash_netiface6_elem_hashed { union nf_inet_addr ip; u8 physdev; u8 cidr; - u16 padding; + u8 nomatch; + u8 padding; }; #define HKEY_DATALEN sizeof(struct hash_netiface6_elem_hashed) @@ -449,7 +473,8 @@ struct hash_netiface6_elem { union nf_inet_addr ip; u8 physdev; u8 cidr; - u16 padding; + u8 nomatch; + u8 padding; const char *iface; }; @@ -457,7 +482,8 @@ struct hash_netiface6_telem { union nf_inet_addr ip; u8 physdev; u8 cidr; - u16 padding; + u8 nomatch; + u8 padding; const char *iface; unsigned long timeout; }; @@ -487,9 +513,22 @@ hash_netiface6_data_copy(struct hash_netiface6_elem *dst, memcpy(dst, src, sizeof(*dst)); } +static inline void +hash_netiface6_data_flags(struct hash_netiface6_elem *dst, u32 flags) +{ + dst->nomatch = flags & IPSET_FLAG_NOMATCH; +} + +static inline bool +hash_netiface6_data_match(const struct hash_netiface6_elem *elem) +{ + return !elem->nomatch; +} + static inline void hash_netiface6_data_zero_out(struct hash_netiface6_elem *elem) { + elem->cidr = 0; } static inline void @@ -514,11 +553,13 @@ hash_netiface6_data_list(struct sk_buff *skb, { u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0; + if (data->nomatch) + flags |= IPSET_FLAG_NOMATCH; NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface); if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, flags); + NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); return 0; nla_put_failure: @@ -533,11 +574,13 @@ hash_netiface6_data_tlist(struct sk_buff *skb, (const struct hash_netiface6_telem *)data; u32 flags = data->physdev ? IPSET_FLAG_PHYSDEV : 0; + if (data->nomatch) + flags |= IPSET_FLAG_NOMATCH; NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface); if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, flags); + NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(e->timeout))); return 0; @@ -636,7 +679,7 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[], if (tb[IPSET_ATTR_CIDR]) data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); - if (!data.cidr) + if (!data.cidr || data.cidr > HOST_MASK) return -IPSET_ERR_INVALID_CIDR; ip6_netmask(&data.ip, data.cidr); @@ -662,6 +705,8 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[], u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); if (cadt_flags & IPSET_FLAG_PHYSDEV) data.physdev = 1; + if (adt == IPSET_ADD && (cadt_flags & IPSET_FLAG_NOMATCH)) + flags |= (cadt_flags << 16); } ret = adtfn(set, &data, timeout, flags); @@ -748,6 +793,7 @@ static struct ip_set_type hash_netiface_type __read_mostly = { .dimension = IPSET_DIM_TWO, .family = NFPROTO_UNSPEC, .revision_min = 0, + .revision_max = 1, /* nomatch flag support added */ .create = hash_netiface_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index 1fcc1020eaae..ce2e77100b64 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -40,12 +40,19 @@ hash_netport_same_set(const struct ip_set *a, const struct ip_set *b); /* The type variant functions: IPv4 */ +/* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0 + * However this way we have to store internally cidr - 1, + * dancing back and forth. + */ +#define IP_SET_HASH_WITH_NETS_PACKED + /* Member elements without timeout */ struct hash_netport4_elem { __be32 ip; __be16 port; u8 proto; - u8 cidr; + u8 cidr:7; + u8 nomatch:1; }; /* Member elements with timeout support */ @@ -53,7 +60,8 @@ struct hash_netport4_telem { __be32 ip; __be16 port; u8 proto; - u8 cidr; + u8 cidr:7; + u8 nomatch:1; unsigned long timeout; }; @@ -82,13 +90,26 @@ hash_netport4_data_copy(struct hash_netport4_elem *dst, dst->port = src->port; dst->proto = src->proto; dst->cidr = src->cidr; + dst->nomatch = src->nomatch; +} + +static inline void +hash_netport4_data_flags(struct hash_netport4_elem *dst, u32 flags) +{ + dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH); +} + +static inline bool +hash_netport4_data_match(const struct hash_netport4_elem *elem) +{ + return !elem->nomatch; } static inline void hash_netport4_data_netmask(struct hash_netport4_elem *elem, u8 cidr) { elem->ip &= ip_set_netmask(cidr); - elem->cidr = cidr; + elem->cidr = cidr - 1; } static inline void @@ -101,10 +122,14 @@ static bool hash_netport4_data_list(struct sk_buff *skb, const struct hash_netport4_elem *data) { + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; + NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1); NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + if (flags) + NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); return 0; nla_put_failure: @@ -117,13 +142,16 @@ hash_netport4_data_tlist(struct sk_buff *skb, { const struct hash_netport4_telem *tdata = (const struct hash_netport4_telem *)data; + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1); NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(tdata->timeout))); + if (flags) + NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); return 0; @@ -154,20 +182,18 @@ hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport4_elem data = { - .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK + .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1 }; - if (data.cidr == 0) - return -EINVAL; if (adt == IPSET_TEST) - data.cidr = HOST_MASK; + data.cidr = HOST_MASK - 1; if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &data.port, &data.proto)) return -EINVAL; ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip); - data.ip &= ip_set_netmask(data.cidr); + data.ip &= ip_set_netmask(data.cidr + 1); return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); } @@ -178,16 +204,18 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_netport4_elem data = { .cidr = HOST_MASK }; + struct hash_netport4_elem data = { .cidr = HOST_MASK - 1 }; u32 port, port_to, p = 0, ip = 0, ip_to, last; u32 timeout = h->timeout; bool with_ports = false; + u8 cidr; int ret; if (unlikely(!tb[IPSET_ATTR_IP] || !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) return -IPSET_ERR_PROTOCOL; if (tb[IPSET_ATTR_LINENO]) @@ -198,9 +226,10 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; if (tb[IPSET_ATTR_CIDR]) { - data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); - if (!data.cidr) + cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); + if (!cidr || cidr > HOST_MASK) return -IPSET_ERR_INVALID_CIDR; + data.cidr = cidr - 1; } if (tb[IPSET_ATTR_PORT]) @@ -227,8 +256,15 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], } with_ports = with_ports && tb[IPSET_ATTR_PORT_TO]; + + if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) { + u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_NOMATCH) + flags |= (cadt_flags << 16); + } + if (adt == IPSET_TEST || !(with_ports || tb[IPSET_ATTR_IP_TO])) { - data.ip = htonl(ip & ip_set_hostmask(data.cidr)); + data.ip = htonl(ip & ip_set_hostmask(data.cidr + 1)); ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -248,14 +284,15 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], if (ip + UINT_MAX == ip_to) return -IPSET_ERR_HASH_RANGE; } else { - ip_set_mask_from_to(ip, ip_to, data.cidr); + ip_set_mask_from_to(ip, ip_to, data.cidr + 1); } if (retried) ip = h->next.ip; while (!after(ip, ip_to)) { data.ip = htonl(ip); - last = ip_set_range_to_cidr(ip, ip_to, &data.cidr); + last = ip_set_range_to_cidr(ip, ip_to, &cidr); + data.cidr = cidr - 1; p = retried && ip == h->next.ip ? h->next.port : port; for (; p <= port_to; p++) { data.port = htons(p); @@ -288,14 +325,16 @@ struct hash_netport6_elem { union nf_inet_addr ip; __be16 port; u8 proto; - u8 cidr; + u8 cidr:7; + u8 nomatch:1; }; struct hash_netport6_telem { union nf_inet_addr ip; __be16 port; u8 proto; - u8 cidr; + u8 cidr:7; + u8 nomatch:1; unsigned long timeout; }; @@ -323,6 +362,18 @@ hash_netport6_data_copy(struct hash_netport6_elem *dst, memcpy(dst, src, sizeof(*dst)); } +static inline void +hash_netport6_data_flags(struct hash_netport6_elem *dst, u32 flags) +{ + dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH); +} + +static inline bool +hash_netport6_data_match(const struct hash_netport6_elem *elem) +{ + return !elem->nomatch; +} + static inline void hash_netport6_data_zero_out(struct hash_netport6_elem *elem) { @@ -342,17 +393,21 @@ static inline void hash_netport6_data_netmask(struct hash_netport6_elem *elem, u8 cidr) { ip6_netmask(&elem->ip, cidr); - elem->cidr = cidr; + elem->cidr = cidr - 1; } static bool hash_netport6_data_list(struct sk_buff *skb, const struct hash_netport6_elem *data) { + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; + NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1); NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + if (flags) + NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); return 0; nla_put_failure: @@ -365,13 +420,16 @@ hash_netport6_data_tlist(struct sk_buff *skb, { const struct hash_netport6_telem *e = (const struct hash_netport6_telem *)data; + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); + NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1); NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(ip_set_timeout_get(e->timeout))); + if (flags) + NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); return 0; nla_put_failure: @@ -400,20 +458,18 @@ hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport6_elem data = { - .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK + .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1, }; - if (data.cidr == 0) - return -EINVAL; if (adt == IPSET_TEST) - data.cidr = HOST_MASK; + data.cidr = HOST_MASK - 1; if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &data.port, &data.proto)) return -EINVAL; ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6); - ip6_netmask(&data.ip, data.cidr); + ip6_netmask(&data.ip, data.cidr + 1); return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); } @@ -424,16 +480,18 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[], { const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_netport6_elem data = { .cidr = HOST_MASK }; + struct hash_netport6_elem data = { .cidr = HOST_MASK - 1 }; u32 port, port_to; u32 timeout = h->timeout; bool with_ports = false; + u8 cidr; int ret; if (unlikely(!tb[IPSET_ATTR_IP] || !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) return -IPSET_ERR_PROTOCOL; if (unlikely(tb[IPSET_ATTR_IP_TO])) return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; @@ -445,11 +503,13 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[], if (ret) return ret; - if (tb[IPSET_ATTR_CIDR]) - data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); - if (!data.cidr) - return -IPSET_ERR_INVALID_CIDR; - ip6_netmask(&data.ip, data.cidr); + if (tb[IPSET_ATTR_CIDR]) { + cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); + if (!cidr || cidr > HOST_MASK) + return -IPSET_ERR_INVALID_CIDR; + data.cidr = cidr - 1; + } + ip6_netmask(&data.ip, data.cidr + 1); if (tb[IPSET_ATTR_PORT]) data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); @@ -474,6 +534,12 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[], timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); } + if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) { + u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_NOMATCH) + flags |= (cadt_flags << 16); + } + if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { ret = adtfn(set, &data, timeout, flags); return ip_set_eexist(ret, flags) ? 0 : ret; @@ -576,7 +642,8 @@ static struct ip_set_type hash_netport_type __read_mostly = { .family = NFPROTO_UNSPEC, .revision_min = 0, /* 1 SCTP and UDPLITE support added */ - .revision_max = 2, /* Range as input support for IPv4 added */ + /* 2, Range as input support for IPv4 added */ + .revision_max = 3, /* nomatch flag support added */ .create = hash_netport_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -595,6 +662,7 @@ static struct ip_set_type hash_netport_type __read_mostly = { [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, }, .me = THIS_MODULE, }; -- cgit From 7f81c951d961bef0bf9aa55449654302b9da8185 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Fri, 13 Jan 2012 22:55:54 +0100 Subject: netfilter: ipset: hash:net,iface timeout bug fixed Timed out entries were still matched till the garbage collector purged them out. The fix is verified in the testsuite. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/ipset/ip_set_ahash.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/linux/netfilter/ipset/ip_set_ahash.h b/include/linux/netfilter/ipset/ip_set_ahash.h index 0e5c3cf7618a..05a5d72680be 100644 --- a/include/linux/netfilter/ipset/ip_set_ahash.h +++ b/include/linux/netfilter/ipset/ip_set_ahash.h @@ -1005,9 +1005,17 @@ type_pf_ttest_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout) n = hbucket(t, key); for (i = 0; i < n->pos; i++) { data = ahash_tdata(n, i); +#ifdef IP_SET_HASH_WITH_MULTI + if (type_pf_data_equal(data, d, &multi)) { + if (!type_pf_data_expired(data)) + return type_pf_data_match(data); + multi = 0; + } +#else if (type_pf_data_equal(data, d, &multi) && !type_pf_data_expired(data)) return type_pf_data_match(data); +#endif } } return 0; -- cgit From b8c5e52c13edc99ce192d78c8a7fe2fd626ac643 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 5 Feb 2012 03:21:12 +0100 Subject: netfilter: ctnetlink: allow to set expectation class This patch allows you to set the expectation class. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nfnetlink_conntrack.h | 1 + net/netfilter/nf_conntrack_netlink.c | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index d498a4426ebf..557a0cfb1433 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -173,6 +173,7 @@ enum ctattr_expect { CTA_EXPECT_HELP_NAME, CTA_EXPECT_ZONE, CTA_EXPECT_FLAGS, + CTA_EXPECT_CLASS, __CTA_EXPECT_MAX }; #define CTA_EXPECT_MAX (__CTA_EXPECT_MAX - 1) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 1b0aea620d62..b6ea39770c80 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1691,6 +1691,7 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb, NLA_PUT_BE32(skb, CTA_EXPECT_TIMEOUT, htonl(timeout)); NLA_PUT_BE32(skb, CTA_EXPECT_ID, htonl((unsigned long)exp)); NLA_PUT_BE32(skb, CTA_EXPECT_FLAGS, htonl(exp->flags)); + NLA_PUT_BE32(skb, CTA_EXPECT_CLASS, htonl(exp->class)); help = nfct_help(master); if (help) { struct nf_conntrack_helper *helper; @@ -1856,6 +1857,7 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = { [CTA_EXPECT_HELP_NAME] = { .type = NLA_NUL_STRING }, [CTA_EXPECT_ZONE] = { .type = NLA_U16 }, [CTA_EXPECT_FLAGS] = { .type = NLA_U32 }, + [CTA_EXPECT_CLASS] = { .type = NLA_U32 }, }; static int @@ -2043,6 +2045,7 @@ ctnetlink_create_expect(struct net *net, u16 zone, struct nf_conn *ct; struct nf_conn_help *help; struct nf_conntrack_helper *helper = NULL; + u_int32_t class = 0; int err = 0; /* caller guarantees that those three CTA_EXPECT_* exist */ @@ -2088,6 +2091,13 @@ ctnetlink_create_expect(struct net *net, u16 zone, } } + if (cda[CTA_EXPECT_CLASS] && helper) { + class = ntohl(nla_get_be32(cda[CTA_EXPECT_CLASS])); + if (class > helper->expect_class_max) { + err = -EINVAL; + goto out; + } + } exp = nf_ct_expect_alloc(ct); if (!exp) { err = -ENOMEM; @@ -2115,7 +2125,7 @@ ctnetlink_create_expect(struct net *net, u16 zone, exp->flags = 0; } - exp->class = 0; + exp->class = class; exp->expectfn = NULL; exp->master = ct; exp->helper = helper; -- cgit From 076a0ca02644657b13e4af363f487ced2942e9cb Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 5 Feb 2012 03:41:52 +0100 Subject: netfilter: ctnetlink: add NAT support for expectations This patch adds the missing bits to create expectations that are created in NAT setups. --- include/linux/netfilter/nfnetlink_conntrack.h | 9 ++++ net/netfilter/nf_conntrack_netlink.c | 68 ++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index 557a0cfb1433..a2f1f483ecc9 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -174,10 +174,19 @@ enum ctattr_expect { CTA_EXPECT_ZONE, CTA_EXPECT_FLAGS, CTA_EXPECT_CLASS, + CTA_EXPECT_NAT, __CTA_EXPECT_MAX }; #define CTA_EXPECT_MAX (__CTA_EXPECT_MAX - 1) +enum ctattr_expect_nat { + CTA_EXPECT_NAT_UNSPEC, + CTA_EXPECT_NAT_DIR, + CTA_EXPECT_NAT_TUPLE, + __CTA_EXPECT_NAT_MAX +}; +#define CTA_EXPECT_NAT_MAX (__CTA_EXPECT_NAT_MAX - 1) + enum ctattr_help { CTA_HELP_UNSPEC, CTA_HELP_NAME, diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index b6ea39770c80..845c8ca28563 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1675,7 +1675,10 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb, struct nf_conn *master = exp->master; long timeout = ((long)exp->timeout.expires - (long)jiffies) / HZ; struct nf_conn_help *help; - +#ifdef CONFIG_NF_NAT_NEEDED + struct nlattr *nest_parms; + struct nf_conntrack_tuple nat_tuple = {}; +#endif if (timeout < 0) timeout = 0; @@ -1688,6 +1691,25 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb, CTA_EXPECT_MASTER) < 0) goto nla_put_failure; +#ifdef CONFIG_NF_NAT_NEEDED + if (exp->saved_ip || exp->saved_proto.all) { + nest_parms = nla_nest_start(skb, CTA_EXPECT_NAT | NLA_F_NESTED); + if (!nest_parms) + goto nla_put_failure; + + NLA_PUT_BE32(skb, CTA_EXPECT_NAT_DIR, htonl(exp->dir)); + + nat_tuple.src.l3num = nf_ct_l3num(master); + nat_tuple.src.u3.ip = exp->saved_ip; + nat_tuple.dst.protonum = nf_ct_protonum(master); + nat_tuple.src.u = exp->saved_proto; + + if (ctnetlink_exp_dump_tuple(skb, &nat_tuple, + CTA_EXPECT_NAT_TUPLE) < 0) + goto nla_put_failure; + nla_nest_end(skb, nest_parms); + } +#endif NLA_PUT_BE32(skb, CTA_EXPECT_TIMEOUT, htonl(timeout)); NLA_PUT_BE32(skb, CTA_EXPECT_ID, htonl((unsigned long)exp)); NLA_PUT_BE32(skb, CTA_EXPECT_FLAGS, htonl(exp->flags)); @@ -1858,6 +1880,7 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = { [CTA_EXPECT_ZONE] = { .type = NLA_U16 }, [CTA_EXPECT_FLAGS] = { .type = NLA_U32 }, [CTA_EXPECT_CLASS] = { .type = NLA_U32 }, + [CTA_EXPECT_NAT] = { .type = NLA_NESTED }, }; static int @@ -2033,6 +2056,41 @@ ctnetlink_change_expect(struct nf_conntrack_expect *x, return -EOPNOTSUPP; } +static const struct nla_policy exp_nat_nla_policy[CTA_EXPECT_NAT_MAX+1] = { + [CTA_EXPECT_NAT_DIR] = { .type = NLA_U32 }, + [CTA_EXPECT_NAT_TUPLE] = { .type = NLA_NESTED }, +}; + +static int +ctnetlink_parse_expect_nat(const struct nlattr *attr, + struct nf_conntrack_expect *exp, + u_int8_t u3) +{ +#ifdef CONFIG_NF_NAT_NEEDED + struct nlattr *tb[CTA_EXPECT_NAT_MAX+1]; + struct nf_conntrack_tuple nat_tuple = {}; + int err; + + nla_parse_nested(tb, CTA_EXPECT_NAT_MAX, attr, exp_nat_nla_policy); + + if (!tb[CTA_EXPECT_NAT_DIR] || !tb[CTA_EXPECT_NAT_TUPLE]) + return -EINVAL; + + err = ctnetlink_parse_tuple((const struct nlattr * const *)tb, + &nat_tuple, CTA_EXPECT_NAT_TUPLE, u3); + if (err < 0) + return err; + + exp->saved_ip = nat_tuple.src.u3.ip; + exp->saved_proto = nat_tuple.src.u; + exp->dir = ntohl(nla_get_be32(tb[CTA_EXPECT_NAT_DIR])); + + return 0; +#else + return -EOPNOTSUPP; +#endif +} + static int ctnetlink_create_expect(struct net *net, u16 zone, const struct nlattr * const cda[], @@ -2133,9 +2191,15 @@ ctnetlink_create_expect(struct net *net, u16 zone, memcpy(&exp->mask.src.u3, &mask.src.u3, sizeof(exp->mask.src.u3)); exp->mask.src.u.all = mask.src.u.all; + if (cda[CTA_EXPECT_NAT]) { + err = ctnetlink_parse_expect_nat(cda[CTA_EXPECT_NAT], + exp, u3); + if (err < 0) + goto err_out; + } err = nf_ct_expect_related_report(exp, pid, report); +err_out: nf_ct_expect_put(exp); - out: nf_ct_put(nf_ct_tuplehash_to_ctrack(h)); return err; -- cgit From 544d5c7d9f4d1ec4f170bc5bcc522012cb7704bc Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 5 Feb 2012 03:44:51 +0100 Subject: netfilter: ctnetlink: allow to set expectfn for expectations This patch allows you to set expectfn which is specifically used by the NAT side of most of the existing conntrack helpers. I have added a symbol map that uses a string as key to look up for the function that is attached to the expectation object. This is the best solution I came out with to solve this issue. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nfnetlink_conntrack.h | 1 + include/net/netfilter/nf_conntrack_helper.h | 13 +++++++ net/ipv4/netfilter/nf_nat_core.c | 8 ++++ net/ipv4/netfilter/nf_nat_h323.c | 14 +++++++ net/ipv4/netfilter/nf_nat_sip.c | 7 ++++ net/netfilter/nf_conntrack_helper.c | 54 +++++++++++++++++++++++++++ net/netfilter/nf_conntrack_netlink.c | 19 +++++++++- 7 files changed, 115 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index a2f1f483ecc9..e58e4b93c108 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -175,6 +175,7 @@ enum ctattr_expect { CTA_EXPECT_FLAGS, CTA_EXPECT_CLASS, CTA_EXPECT_NAT, + CTA_EXPECT_FN, __CTA_EXPECT_MAX }; #define CTA_EXPECT_MAX (__CTA_EXPECT_MAX - 1) diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index f1c1311adc2c..5767dc242dee 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -69,4 +69,17 @@ extern int nf_conntrack_broadcast_help(struct sk_buff *skb, enum ip_conntrack_info ctinfo, unsigned int timeout); +struct nf_ct_helper_expectfn { + struct list_head head; + const char *name; + void (*expectfn)(struct nf_conn *ct, struct nf_conntrack_expect *exp); +}; + +void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n); +void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n); +struct nf_ct_helper_expectfn * +nf_ct_helper_expectfn_find_by_name(const char *name); +struct nf_ct_helper_expectfn * +nf_ct_helper_expectfn_find_by_symbol(const void *symbol); + #endif /*_NF_CONNTRACK_HELPER_H*/ diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c index a708933dc230..abb52adf5acd 100644 --- a/net/ipv4/netfilter/nf_nat_core.c +++ b/net/ipv4/netfilter/nf_nat_core.c @@ -686,6 +686,11 @@ static struct pernet_operations nf_nat_net_ops = { .exit = nf_nat_net_exit, }; +static struct nf_ct_helper_expectfn follow_master_nat = { + .name = "nat-follow-master", + .expectfn = nf_nat_follow_master, +}; + static int __init nf_nat_init(void) { size_t i; @@ -717,6 +722,8 @@ static int __init nf_nat_init(void) l3proto = nf_ct_l3proto_find_get((u_int16_t)AF_INET); + nf_ct_helper_expectfn_register(&follow_master_nat); + BUG_ON(nf_nat_seq_adjust_hook != NULL); RCU_INIT_POINTER(nf_nat_seq_adjust_hook, nf_nat_seq_adjust); BUG_ON(nfnetlink_parse_nat_setup_hook != NULL); @@ -736,6 +743,7 @@ static void __exit nf_nat_cleanup(void) unregister_pernet_subsys(&nf_nat_net_ops); nf_ct_l3proto_put(l3proto); nf_ct_extend_unregister(&nat_extend); + nf_ct_helper_expectfn_unregister(&follow_master_nat); RCU_INIT_POINTER(nf_nat_seq_adjust_hook, NULL); RCU_INIT_POINTER(nfnetlink_parse_nat_setup_hook, NULL); RCU_INIT_POINTER(nf_ct_nat_offset, NULL); diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c index dc1dd912baf4..82536701e3a3 100644 --- a/net/ipv4/netfilter/nf_nat_h323.c +++ b/net/ipv4/netfilter/nf_nat_h323.c @@ -568,6 +568,16 @@ static int nat_callforwarding(struct sk_buff *skb, struct nf_conn *ct, return 0; } +static struct nf_ct_helper_expectfn q931_nat = { + .name = "Q.931", + .expectfn = ip_nat_q931_expect, +}; + +static struct nf_ct_helper_expectfn callforwarding_nat = { + .name = "callforwarding", + .expectfn = ip_nat_callforwarding_expect, +}; + /****************************************************************************/ static int __init init(void) { @@ -590,6 +600,8 @@ static int __init init(void) RCU_INIT_POINTER(nat_h245_hook, nat_h245); RCU_INIT_POINTER(nat_callforwarding_hook, nat_callforwarding); RCU_INIT_POINTER(nat_q931_hook, nat_q931); + nf_ct_helper_expectfn_register(&q931_nat); + nf_ct_helper_expectfn_register(&callforwarding_nat); return 0; } @@ -605,6 +617,8 @@ static void __exit fini(void) RCU_INIT_POINTER(nat_h245_hook, NULL); RCU_INIT_POINTER(nat_callforwarding_hook, NULL); RCU_INIT_POINTER(nat_q931_hook, NULL); + nf_ct_helper_expectfn_unregister(&q931_nat); + nf_ct_helper_expectfn_unregister(&callforwarding_nat); synchronize_rcu(); } diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c index d0319f96269f..57932c43960e 100644 --- a/net/ipv4/netfilter/nf_nat_sip.c +++ b/net/ipv4/netfilter/nf_nat_sip.c @@ -526,6 +526,11 @@ err1: return NF_DROP; } +static struct nf_ct_helper_expectfn sip_nat = { + .name = "sip", + .expectfn = ip_nat_sip_expected, +}; + static void __exit nf_nat_sip_fini(void) { RCU_INIT_POINTER(nf_nat_sip_hook, NULL); @@ -535,6 +540,7 @@ static void __exit nf_nat_sip_fini(void) RCU_INIT_POINTER(nf_nat_sdp_port_hook, NULL); RCU_INIT_POINTER(nf_nat_sdp_session_hook, NULL); RCU_INIT_POINTER(nf_nat_sdp_media_hook, NULL); + nf_ct_helper_expectfn_unregister(&sip_nat); synchronize_rcu(); } @@ -554,6 +560,7 @@ static int __init nf_nat_sip_init(void) RCU_INIT_POINTER(nf_nat_sdp_port_hook, ip_nat_sdp_port); RCU_INIT_POINTER(nf_nat_sdp_session_hook, ip_nat_sdp_session); RCU_INIT_POINTER(nf_nat_sdp_media_hook, ip_nat_sdp_media); + nf_ct_helper_expectfn_register(&sip_nat); return 0; } diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index bbe23baa19b6..436b7cb79ba4 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -181,6 +181,60 @@ void nf_ct_helper_destroy(struct nf_conn *ct) } } +static LIST_HEAD(nf_ct_helper_expectfn_list); + +void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n) +{ + spin_lock_bh(&nf_conntrack_lock); + list_add_rcu(&n->head, &nf_ct_helper_expectfn_list); + spin_unlock_bh(&nf_conntrack_lock); +} +EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_register); + +void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n) +{ + spin_lock_bh(&nf_conntrack_lock); + list_del_rcu(&n->head); + spin_unlock_bh(&nf_conntrack_lock); +} +EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister); + +struct nf_ct_helper_expectfn * +nf_ct_helper_expectfn_find_by_name(const char *name) +{ + struct nf_ct_helper_expectfn *cur; + bool found = false; + + rcu_read_lock(); + list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) { + if (!strcmp(cur->name, name)) { + found = true; + break; + } + } + rcu_read_unlock(); + return found ? cur : NULL; +} +EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_name); + +struct nf_ct_helper_expectfn * +nf_ct_helper_expectfn_find_by_symbol(const void *symbol) +{ + struct nf_ct_helper_expectfn *cur; + bool found = false; + + rcu_read_lock(); + list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) { + if (cur->expectfn == symbol) { + found = true; + break; + } + } + rcu_read_unlock(); + return found ? cur : NULL; +} +EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_symbol); + int nf_conntrack_helper_register(struct nf_conntrack_helper *me) { unsigned int h = helper_hash(&me->tuple); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 845c8ca28563..b8827e8a11fe 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1679,6 +1679,8 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb, struct nlattr *nest_parms; struct nf_conntrack_tuple nat_tuple = {}; #endif + struct nf_ct_helper_expectfn *expfn; + if (timeout < 0) timeout = 0; @@ -1722,6 +1724,9 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb, if (helper) NLA_PUT_STRING(skb, CTA_EXPECT_HELP_NAME, helper->name); } + expfn = nf_ct_helper_expectfn_find_by_symbol(exp->expectfn); + if (expfn != NULL) + NLA_PUT_STRING(skb, CTA_EXPECT_FN, expfn->name); return 0; @@ -1881,6 +1886,7 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = { [CTA_EXPECT_FLAGS] = { .type = NLA_U32 }, [CTA_EXPECT_CLASS] = { .type = NLA_U32 }, [CTA_EXPECT_NAT] = { .type = NLA_NESTED }, + [CTA_EXPECT_FN] = { .type = NLA_NUL_STRING }, }; static int @@ -2182,9 +2188,20 @@ ctnetlink_create_expect(struct net *net, u16 zone, } else exp->flags = 0; } + if (cda[CTA_EXPECT_FN]) { + const char *name = nla_data(cda[CTA_EXPECT_FN]); + struct nf_ct_helper_expectfn *expfn; + + expfn = nf_ct_helper_expectfn_find_by_name(name); + if (expfn == NULL) { + err = -EINVAL; + goto err_out; + } + exp->expectfn = expfn->expectfn; + } else + exp->expectfn = NULL; exp->class = class; - exp->expectfn = NULL; exp->master = ct; exp->helper = helper; memcpy(&exp->tuple, &tuple, sizeof(struct nf_conntrack_tuple)); -- cgit From 6939c33a757bd006c5e0b8b5fd429fc587a4d0f4 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Fri, 10 Feb 2012 23:10:52 +0100 Subject: netfilter: merge ipt_LOG and ip6_LOG into xt_LOG ipt_LOG and ip6_LOG have a lot of common code, merge them to reduce duplicate code. Signed-off-by: Richard Weinberger Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/Kbuild | 1 + include/linux/netfilter/xt_LOG.h | 19 + include/linux/netfilter_ipv4/ipt_LOG.h | 2 + include/linux/netfilter_ipv6/ip6t_LOG.h | 2 + net/ipv4/netfilter/Kconfig | 9 - net/ipv4/netfilter/Makefile | 1 - net/ipv4/netfilter/ipt_LOG.c | 516 ------------------ net/ipv6/netfilter/Kconfig | 9 - net/ipv6/netfilter/Makefile | 1 - net/ipv6/netfilter/ip6t_LOG.c | 527 ------------------ net/netfilter/Kconfig | 9 + net/netfilter/Makefile | 1 + net/netfilter/xt_LOG.c | 921 ++++++++++++++++++++++++++++++++ 13 files changed, 955 insertions(+), 1063 deletions(-) create mode 100644 include/linux/netfilter/xt_LOG.h delete mode 100644 net/ipv4/netfilter/ipt_LOG.c delete mode 100644 net/ipv6/netfilter/ip6t_LOG.c create mode 100644 net/netfilter/xt_LOG.c (limited to 'include') diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index e144f54185c0..aaa72aaa21de 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild @@ -22,6 +22,7 @@ header-y += xt_CT.h header-y += xt_DSCP.h header-y += xt_IDLETIMER.h header-y += xt_LED.h +header-y += xt_LOG.h header-y += xt_MARK.h header-y += xt_nfacct.h header-y += xt_NFLOG.h diff --git a/include/linux/netfilter/xt_LOG.h b/include/linux/netfilter/xt_LOG.h new file mode 100644 index 000000000000..cac079095305 --- /dev/null +++ b/include/linux/netfilter/xt_LOG.h @@ -0,0 +1,19 @@ +#ifndef _XT_LOG_H +#define _XT_LOG_H + +/* make sure not to change this without changing nf_log.h:NF_LOG_* (!) */ +#define XT_LOG_TCPSEQ 0x01 /* Log TCP sequence numbers */ +#define XT_LOG_TCPOPT 0x02 /* Log TCP options */ +#define XT_LOG_IPOPT 0x04 /* Log IP options */ +#define XT_LOG_UID 0x08 /* Log UID owning local socket */ +#define XT_LOG_NFLOG 0x10 /* Unsupported, don't reuse */ +#define XT_LOG_MACDECODE 0x20 /* Decode MAC header */ +#define XT_LOG_MASK 0x2f + +struct xt_log_info { + unsigned char level; + unsigned char logflags; + char prefix[30]; +}; + +#endif /* _XT_LOG_H */ diff --git a/include/linux/netfilter_ipv4/ipt_LOG.h b/include/linux/netfilter_ipv4/ipt_LOG.h index dcdbadf9fd4a..5d8152077d71 100644 --- a/include/linux/netfilter_ipv4/ipt_LOG.h +++ b/include/linux/netfilter_ipv4/ipt_LOG.h @@ -1,6 +1,8 @@ #ifndef _IPT_LOG_H #define _IPT_LOG_H +#warning "Please update iptables, this file will be removed soon!" + /* make sure not to change this without changing netfilter.h:NF_LOG_* (!) */ #define IPT_LOG_TCPSEQ 0x01 /* Log TCP sequence numbers */ #define IPT_LOG_TCPOPT 0x02 /* Log TCP options */ diff --git a/include/linux/netfilter_ipv6/ip6t_LOG.h b/include/linux/netfilter_ipv6/ip6t_LOG.h index 9dd5579e02ec..3dd0bc4e0735 100644 --- a/include/linux/netfilter_ipv6/ip6t_LOG.h +++ b/include/linux/netfilter_ipv6/ip6t_LOG.h @@ -1,6 +1,8 @@ #ifndef _IP6T_LOG_H #define _IP6T_LOG_H +#warning "Please update iptables, this file will be removed soon!" + /* make sure not to change this without changing netfilter.h:NF_LOG_* (!) */ #define IP6T_LOG_TCPSEQ 0x01 /* Log TCP sequence numbers */ #define IP6T_LOG_TCPOPT 0x02 /* Log TCP options */ diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 74dfc9e5211f..fcc543cd987a 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -123,15 +123,6 @@ config IP_NF_TARGET_REJECT To compile it as a module, choose M here. If unsure, say N. -config IP_NF_TARGET_LOG - tristate "LOG target support" - default m if NETFILTER_ADVANCED=n - help - This option adds a `LOG' target, which allows you to create rules in - any iptables table which records the packet header to the syslog. - - To compile it as a module, choose M here. If unsure, say N. - config IP_NF_TARGET_ULOG tristate "ULOG target support" default m if NETFILTER_ADVANCED=n diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 213a462b739b..240b68469a7a 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -54,7 +54,6 @@ obj-$(CONFIG_IP_NF_MATCH_RPFILTER) += ipt_rpfilter.o # targets obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o -obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c deleted file mode 100644 index d76d6c9ed946..000000000000 --- a/net/ipv4/netfilter/ipt_LOG.c +++ /dev/null @@ -1,516 +0,0 @@ -/* - * This is a module which is used for logging packets. - */ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Netfilter Core Team "); -MODULE_DESCRIPTION("Xtables: IPv4 packet logging to syslog"); - -/* One level of recursion won't kill us */ -static void dump_packet(struct sbuff *m, - const struct nf_loginfo *info, - const struct sk_buff *skb, - unsigned int iphoff) -{ - struct iphdr _iph; - const struct iphdr *ih; - unsigned int logflags; - - if (info->type == NF_LOG_TYPE_LOG) - logflags = info->u.log.logflags; - else - logflags = NF_LOG_MASK; - - ih = skb_header_pointer(skb, iphoff, sizeof(_iph), &_iph); - if (ih == NULL) { - sb_add(m, "TRUNCATED"); - return; - } - - /* Important fields: - * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */ - /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */ - sb_add(m, "SRC=%pI4 DST=%pI4 ", - &ih->saddr, &ih->daddr); - - /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */ - sb_add(m, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ", - ntohs(ih->tot_len), ih->tos & IPTOS_TOS_MASK, - ih->tos & IPTOS_PREC_MASK, ih->ttl, ntohs(ih->id)); - - /* Max length: 6 "CE DF MF " */ - if (ntohs(ih->frag_off) & IP_CE) - sb_add(m, "CE "); - if (ntohs(ih->frag_off) & IP_DF) - sb_add(m, "DF "); - if (ntohs(ih->frag_off) & IP_MF) - sb_add(m, "MF "); - - /* Max length: 11 "FRAG:65535 " */ - if (ntohs(ih->frag_off) & IP_OFFSET) - sb_add(m, "FRAG:%u ", ntohs(ih->frag_off) & IP_OFFSET); - - if ((logflags & IPT_LOG_IPOPT) && - ih->ihl * 4 > sizeof(struct iphdr)) { - const unsigned char *op; - unsigned char _opt[4 * 15 - sizeof(struct iphdr)]; - unsigned int i, optsize; - - optsize = ih->ihl * 4 - sizeof(struct iphdr); - op = skb_header_pointer(skb, iphoff+sizeof(_iph), - optsize, _opt); - if (op == NULL) { - sb_add(m, "TRUNCATED"); - return; - } - - /* Max length: 127 "OPT (" 15*4*2chars ") " */ - sb_add(m, "OPT ("); - for (i = 0; i < optsize; i++) - sb_add(m, "%02X", op[i]); - sb_add(m, ") "); - } - - switch (ih->protocol) { - case IPPROTO_TCP: { - struct tcphdr _tcph; - const struct tcphdr *th; - - /* Max length: 10 "PROTO=TCP " */ - sb_add(m, "PROTO=TCP "); - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - th = skb_header_pointer(skb, iphoff + ih->ihl * 4, - sizeof(_tcph), &_tcph); - if (th == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - /* Max length: 20 "SPT=65535 DPT=65535 " */ - sb_add(m, "SPT=%u DPT=%u ", - ntohs(th->source), ntohs(th->dest)); - /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ - if (logflags & IPT_LOG_TCPSEQ) - sb_add(m, "SEQ=%u ACK=%u ", - ntohl(th->seq), ntohl(th->ack_seq)); - /* Max length: 13 "WINDOW=65535 " */ - sb_add(m, "WINDOW=%u ", ntohs(th->window)); - /* Max length: 9 "RES=0x3F " */ - sb_add(m, "RES=0x%02x ", (u8)(ntohl(tcp_flag_word(th) & TCP_RESERVED_BITS) >> 22)); - /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */ - if (th->cwr) - sb_add(m, "CWR "); - if (th->ece) - sb_add(m, "ECE "); - if (th->urg) - sb_add(m, "URG "); - if (th->ack) - sb_add(m, "ACK "); - if (th->psh) - sb_add(m, "PSH "); - if (th->rst) - sb_add(m, "RST "); - if (th->syn) - sb_add(m, "SYN "); - if (th->fin) - sb_add(m, "FIN "); - /* Max length: 11 "URGP=65535 " */ - sb_add(m, "URGP=%u ", ntohs(th->urg_ptr)); - - if ((logflags & IPT_LOG_TCPOPT) && - th->doff * 4 > sizeof(struct tcphdr)) { - unsigned char _opt[4 * 15 - sizeof(struct tcphdr)]; - const unsigned char *op; - unsigned int i, optsize; - - optsize = th->doff * 4 - sizeof(struct tcphdr); - op = skb_header_pointer(skb, - iphoff+ih->ihl*4+sizeof(_tcph), - optsize, _opt); - if (op == NULL) { - sb_add(m, "TRUNCATED"); - return; - } - - /* Max length: 127 "OPT (" 15*4*2chars ") " */ - sb_add(m, "OPT ("); - for (i = 0; i < optsize; i++) - sb_add(m, "%02X", op[i]); - sb_add(m, ") "); - } - break; - } - case IPPROTO_UDP: - case IPPROTO_UDPLITE: { - struct udphdr _udph; - const struct udphdr *uh; - - if (ih->protocol == IPPROTO_UDP) - /* Max length: 10 "PROTO=UDP " */ - sb_add(m, "PROTO=UDP " ); - else /* Max length: 14 "PROTO=UDPLITE " */ - sb_add(m, "PROTO=UDPLITE "); - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - uh = skb_header_pointer(skb, iphoff+ih->ihl*4, - sizeof(_udph), &_udph); - if (uh == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - /* Max length: 20 "SPT=65535 DPT=65535 " */ - sb_add(m, "SPT=%u DPT=%u LEN=%u ", - ntohs(uh->source), ntohs(uh->dest), - ntohs(uh->len)); - break; - } - case IPPROTO_ICMP: { - struct icmphdr _icmph; - const struct icmphdr *ich; - static const size_t required_len[NR_ICMP_TYPES+1] - = { [ICMP_ECHOREPLY] = 4, - [ICMP_DEST_UNREACH] - = 8 + sizeof(struct iphdr), - [ICMP_SOURCE_QUENCH] - = 8 + sizeof(struct iphdr), - [ICMP_REDIRECT] - = 8 + sizeof(struct iphdr), - [ICMP_ECHO] = 4, - [ICMP_TIME_EXCEEDED] - = 8 + sizeof(struct iphdr), - [ICMP_PARAMETERPROB] - = 8 + sizeof(struct iphdr), - [ICMP_TIMESTAMP] = 20, - [ICMP_TIMESTAMPREPLY] = 20, - [ICMP_ADDRESS] = 12, - [ICMP_ADDRESSREPLY] = 12 }; - - /* Max length: 11 "PROTO=ICMP " */ - sb_add(m, "PROTO=ICMP "); - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - ich = skb_header_pointer(skb, iphoff + ih->ihl * 4, - sizeof(_icmph), &_icmph); - if (ich == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - /* Max length: 18 "TYPE=255 CODE=255 " */ - sb_add(m, "TYPE=%u CODE=%u ", ich->type, ich->code); - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - if (ich->type <= NR_ICMP_TYPES && - required_len[ich->type] && - skb->len-iphoff-ih->ihl*4 < required_len[ich->type]) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - switch (ich->type) { - case ICMP_ECHOREPLY: - case ICMP_ECHO: - /* Max length: 19 "ID=65535 SEQ=65535 " */ - sb_add(m, "ID=%u SEQ=%u ", - ntohs(ich->un.echo.id), - ntohs(ich->un.echo.sequence)); - break; - - case ICMP_PARAMETERPROB: - /* Max length: 14 "PARAMETER=255 " */ - sb_add(m, "PARAMETER=%u ", - ntohl(ich->un.gateway) >> 24); - break; - case ICMP_REDIRECT: - /* Max length: 24 "GATEWAY=255.255.255.255 " */ - sb_add(m, "GATEWAY=%pI4 ", &ich->un.gateway); - /* Fall through */ - case ICMP_DEST_UNREACH: - case ICMP_SOURCE_QUENCH: - case ICMP_TIME_EXCEEDED: - /* Max length: 3+maxlen */ - if (!iphoff) { /* Only recurse once. */ - sb_add(m, "["); - dump_packet(m, info, skb, - iphoff + ih->ihl*4+sizeof(_icmph)); - sb_add(m, "] "); - } - - /* Max length: 10 "MTU=65535 " */ - if (ich->type == ICMP_DEST_UNREACH && - ich->code == ICMP_FRAG_NEEDED) - sb_add(m, "MTU=%u ", ntohs(ich->un.frag.mtu)); - } - break; - } - /* Max Length */ - case IPPROTO_AH: { - struct ip_auth_hdr _ahdr; - const struct ip_auth_hdr *ah; - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - - /* Max length: 9 "PROTO=AH " */ - sb_add(m, "PROTO=AH "); - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - ah = skb_header_pointer(skb, iphoff+ih->ihl*4, - sizeof(_ahdr), &_ahdr); - if (ah == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - /* Length: 15 "SPI=0xF1234567 " */ - sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); - break; - } - case IPPROTO_ESP: { - struct ip_esp_hdr _esph; - const struct ip_esp_hdr *eh; - - /* Max length: 10 "PROTO=ESP " */ - sb_add(m, "PROTO=ESP "); - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - eh = skb_header_pointer(skb, iphoff+ih->ihl*4, - sizeof(_esph), &_esph); - if (eh == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - /* Length: 15 "SPI=0xF1234567 " */ - sb_add(m, "SPI=0x%x ", ntohl(eh->spi)); - break; - } - /* Max length: 10 "PROTO 255 " */ - default: - sb_add(m, "PROTO=%u ", ih->protocol); - } - - /* Max length: 15 "UID=4294967295 " */ - if ((logflags & IPT_LOG_UID) && !iphoff && skb->sk) { - read_lock_bh(&skb->sk->sk_callback_lock); - if (skb->sk->sk_socket && skb->sk->sk_socket->file) - sb_add(m, "UID=%u GID=%u ", - skb->sk->sk_socket->file->f_cred->fsuid, - skb->sk->sk_socket->file->f_cred->fsgid); - read_unlock_bh(&skb->sk->sk_callback_lock); - } - - /* Max length: 16 "MARK=0xFFFFFFFF " */ - if (!iphoff && skb->mark) - sb_add(m, "MARK=0x%x ", skb->mark); - - /* Proto Max log string length */ - /* IP: 40+46+6+11+127 = 230 */ - /* TCP: 10+max(25,20+30+13+9+32+11+127) = 252 */ - /* UDP: 10+max(25,20) = 35 */ - /* UDPLITE: 14+max(25,20) = 39 */ - /* ICMP: 11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */ - /* ESP: 10+max(25)+15 = 50 */ - /* AH: 9+max(25)+15 = 49 */ - /* unknown: 10 */ - - /* (ICMP allows recursion one level deep) */ - /* maxlen = IP + ICMP + IP + max(TCP,UDP,ICMP,unknown) */ - /* maxlen = 230+ 91 + 230 + 252 = 803 */ -} - -static void dump_mac_header(struct sbuff *m, - const struct nf_loginfo *info, - const struct sk_buff *skb) -{ - struct net_device *dev = skb->dev; - unsigned int logflags = 0; - - if (info->type == NF_LOG_TYPE_LOG) - logflags = info->u.log.logflags; - - if (!(logflags & IPT_LOG_MACDECODE)) - goto fallback; - - switch (dev->type) { - case ARPHRD_ETHER: - sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", - eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, - ntohs(eth_hdr(skb)->h_proto)); - return; - default: - break; - } - -fallback: - sb_add(m, "MAC="); - if (dev->hard_header_len && - skb->mac_header != skb->network_header) { - const unsigned char *p = skb_mac_header(skb); - unsigned int i; - - sb_add(m, "%02x", *p++); - for (i = 1; i < dev->hard_header_len; i++, p++) - sb_add(m, ":%02x", *p); - } - sb_add(m, " "); -} - -static struct nf_loginfo default_loginfo = { - .type = NF_LOG_TYPE_LOG, - .u = { - .log = { - .level = 5, - .logflags = NF_LOG_MASK, - }, - }, -}; - -static void -ipt_log_packet(u_int8_t pf, - unsigned int hooknum, - const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct nf_loginfo *loginfo, - const char *prefix) -{ - struct sbuff *m = sb_open(); - - if (!loginfo) - loginfo = &default_loginfo; - - sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level, - prefix, - in ? in->name : "", - out ? out->name : ""); -#ifdef CONFIG_BRIDGE_NETFILTER - if (skb->nf_bridge) { - const struct net_device *physindev; - const struct net_device *physoutdev; - - physindev = skb->nf_bridge->physindev; - if (physindev && in != physindev) - sb_add(m, "PHYSIN=%s ", physindev->name); - physoutdev = skb->nf_bridge->physoutdev; - if (physoutdev && out != physoutdev) - sb_add(m, "PHYSOUT=%s ", physoutdev->name); - } -#endif - - if (in != NULL) - dump_mac_header(m, loginfo, skb); - - dump_packet(m, loginfo, skb, 0); - - sb_close(m); -} - -static unsigned int -log_tg(struct sk_buff *skb, const struct xt_action_param *par) -{ - const struct ipt_log_info *loginfo = par->targinfo; - struct nf_loginfo li; - - li.type = NF_LOG_TYPE_LOG; - li.u.log.level = loginfo->level; - li.u.log.logflags = loginfo->logflags; - - ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, par->out, &li, - loginfo->prefix); - return XT_CONTINUE; -} - -static int log_tg_check(const struct xt_tgchk_param *par) -{ - const struct ipt_log_info *loginfo = par->targinfo; - - if (loginfo->level >= 8) { - pr_debug("level %u >= 8\n", loginfo->level); - return -EINVAL; - } - if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') { - pr_debug("prefix is not null-terminated\n"); - return -EINVAL; - } - return 0; -} - -static struct xt_target log_tg_reg __read_mostly = { - .name = "LOG", - .family = NFPROTO_IPV4, - .target = log_tg, - .targetsize = sizeof(struct ipt_log_info), - .checkentry = log_tg_check, - .me = THIS_MODULE, -}; - -static struct nf_logger ipt_log_logger __read_mostly = { - .name = "ipt_LOG", - .logfn = &ipt_log_packet, - .me = THIS_MODULE, -}; - -static int __init log_tg_init(void) -{ - int ret; - - ret = xt_register_target(&log_tg_reg); - if (ret < 0) - return ret; - nf_log_register(NFPROTO_IPV4, &ipt_log_logger); - return 0; -} - -static void __exit log_tg_exit(void) -{ - nf_log_unregister(&ipt_log_logger); - xt_unregister_target(&log_tg_reg); -} - -module_init(log_tg_init); -module_exit(log_tg_exit); diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index 9a68fb5b9e77..d33cddd16fbb 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -154,15 +154,6 @@ config IP6_NF_TARGET_HL (e.g. when running oldconfig). It selects CONFIG_NETFILTER_XT_TARGET_HL. -config IP6_NF_TARGET_LOG - tristate "LOG target support" - default m if NETFILTER_ADVANCED=n - help - This option adds a `LOG' target, which allows you to create rules in - any iptables table which records the packet header to the syslog. - - To compile it as a module, choose M here. If unsure, say N. - config IP6_NF_FILTER tristate "Packet filtering" default m if NETFILTER_ADVANCED=n diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index 2eaed96db02c..d4dfd0a21097 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile @@ -31,5 +31,4 @@ obj-$(CONFIG_IP6_NF_MATCH_RPFILTER) += ip6t_rpfilter.o obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o # targets -obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o diff --git a/net/ipv6/netfilter/ip6t_LOG.c b/net/ipv6/netfilter/ip6t_LOG.c deleted file mode 100644 index e6af8d72f26b..000000000000 --- a/net/ipv6/netfilter/ip6t_LOG.c +++ /dev/null @@ -1,527 +0,0 @@ -/* - * This is a module which is used for logging packets. - */ - -/* (C) 2001 Jan Rekorajski - * (C) 2002-2004 Netfilter Core Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Jan Rekorajski "); -MODULE_DESCRIPTION("Xtables: IPv6 packet logging to syslog"); -MODULE_LICENSE("GPL"); - -struct in_device; -#include -#include - -/* One level of recursion won't kill us */ -static void dump_packet(struct sbuff *m, - const struct nf_loginfo *info, - const struct sk_buff *skb, unsigned int ip6hoff, - int recurse) -{ - u_int8_t currenthdr; - int fragment; - struct ipv6hdr _ip6h; - const struct ipv6hdr *ih; - unsigned int ptr; - unsigned int hdrlen = 0; - unsigned int logflags; - - if (info->type == NF_LOG_TYPE_LOG) - logflags = info->u.log.logflags; - else - logflags = NF_LOG_MASK; - - ih = skb_header_pointer(skb, ip6hoff, sizeof(_ip6h), &_ip6h); - if (ih == NULL) { - sb_add(m, "TRUNCATED"); - return; - } - - /* Max length: 88 "SRC=0000.0000.0000.0000.0000.0000.0000.0000 DST=0000.0000.0000.0000.0000.0000.0000.0000 " */ - sb_add(m, "SRC=%pI6 DST=%pI6 ", &ih->saddr, &ih->daddr); - - /* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */ - sb_add(m, "LEN=%Zu TC=%u HOPLIMIT=%u FLOWLBL=%u ", - ntohs(ih->payload_len) + sizeof(struct ipv6hdr), - (ntohl(*(__be32 *)ih) & 0x0ff00000) >> 20, - ih->hop_limit, - (ntohl(*(__be32 *)ih) & 0x000fffff)); - - fragment = 0; - ptr = ip6hoff + sizeof(struct ipv6hdr); - currenthdr = ih->nexthdr; - while (currenthdr != NEXTHDR_NONE && ip6t_ext_hdr(currenthdr)) { - struct ipv6_opt_hdr _hdr; - const struct ipv6_opt_hdr *hp; - - hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); - if (hp == NULL) { - sb_add(m, "TRUNCATED"); - return; - } - - /* Max length: 48 "OPT (...) " */ - if (logflags & IP6T_LOG_IPOPT) - sb_add(m, "OPT ( "); - - switch (currenthdr) { - case IPPROTO_FRAGMENT: { - struct frag_hdr _fhdr; - const struct frag_hdr *fh; - - sb_add(m, "FRAG:"); - fh = skb_header_pointer(skb, ptr, sizeof(_fhdr), - &_fhdr); - if (fh == NULL) { - sb_add(m, "TRUNCATED "); - return; - } - - /* Max length: 6 "65535 " */ - sb_add(m, "%u ", ntohs(fh->frag_off) & 0xFFF8); - - /* Max length: 11 "INCOMPLETE " */ - if (fh->frag_off & htons(0x0001)) - sb_add(m, "INCOMPLETE "); - - sb_add(m, "ID:%08x ", ntohl(fh->identification)); - - if (ntohs(fh->frag_off) & 0xFFF8) - fragment = 1; - - hdrlen = 8; - - break; - } - case IPPROTO_DSTOPTS: - case IPPROTO_ROUTING: - case IPPROTO_HOPOPTS: - if (fragment) { - if (logflags & IP6T_LOG_IPOPT) - sb_add(m, ")"); - return; - } - hdrlen = ipv6_optlen(hp); - break; - /* Max Length */ - case IPPROTO_AH: - if (logflags & IP6T_LOG_IPOPT) { - struct ip_auth_hdr _ahdr; - const struct ip_auth_hdr *ah; - - /* Max length: 3 "AH " */ - sb_add(m, "AH "); - - if (fragment) { - sb_add(m, ")"); - return; - } - - ah = skb_header_pointer(skb, ptr, sizeof(_ahdr), - &_ahdr); - if (ah == NULL) { - /* - * Max length: 26 "INCOMPLETE [65535 - * bytes] )" - */ - sb_add(m, "INCOMPLETE [%u bytes] )", - skb->len - ptr); - return; - } - - /* Length: 15 "SPI=0xF1234567 */ - sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); - - } - - hdrlen = (hp->hdrlen+2)<<2; - break; - case IPPROTO_ESP: - if (logflags & IP6T_LOG_IPOPT) { - struct ip_esp_hdr _esph; - const struct ip_esp_hdr *eh; - - /* Max length: 4 "ESP " */ - sb_add(m, "ESP "); - - if (fragment) { - sb_add(m, ")"); - return; - } - - /* - * Max length: 26 "INCOMPLETE [65535 bytes] )" - */ - eh = skb_header_pointer(skb, ptr, sizeof(_esph), - &_esph); - if (eh == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] )", - skb->len - ptr); - return; - } - - /* Length: 16 "SPI=0xF1234567 )" */ - sb_add(m, "SPI=0x%x )", ntohl(eh->spi) ); - - } - return; - default: - /* Max length: 20 "Unknown Ext Hdr 255" */ - sb_add(m, "Unknown Ext Hdr %u", currenthdr); - return; - } - if (logflags & IP6T_LOG_IPOPT) - sb_add(m, ") "); - - currenthdr = hp->nexthdr; - ptr += hdrlen; - } - - switch (currenthdr) { - case IPPROTO_TCP: { - struct tcphdr _tcph; - const struct tcphdr *th; - - /* Max length: 10 "PROTO=TCP " */ - sb_add(m, "PROTO=TCP "); - - if (fragment) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - th = skb_header_pointer(skb, ptr, sizeof(_tcph), &_tcph); - if (th == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr); - return; - } - - /* Max length: 20 "SPT=65535 DPT=65535 " */ - sb_add(m, "SPT=%u DPT=%u ", - ntohs(th->source), ntohs(th->dest)); - /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ - if (logflags & IP6T_LOG_TCPSEQ) - sb_add(m, "SEQ=%u ACK=%u ", - ntohl(th->seq), ntohl(th->ack_seq)); - /* Max length: 13 "WINDOW=65535 " */ - sb_add(m, "WINDOW=%u ", ntohs(th->window)); - /* Max length: 9 "RES=0x3C " */ - sb_add(m, "RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(th) & TCP_RESERVED_BITS) >> 22)); - /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */ - if (th->cwr) - sb_add(m, "CWR "); - if (th->ece) - sb_add(m, "ECE "); - if (th->urg) - sb_add(m, "URG "); - if (th->ack) - sb_add(m, "ACK "); - if (th->psh) - sb_add(m, "PSH "); - if (th->rst) - sb_add(m, "RST "); - if (th->syn) - sb_add(m, "SYN "); - if (th->fin) - sb_add(m, "FIN "); - /* Max length: 11 "URGP=65535 " */ - sb_add(m, "URGP=%u ", ntohs(th->urg_ptr)); - - if ((logflags & IP6T_LOG_TCPOPT) && - th->doff * 4 > sizeof(struct tcphdr)) { - u_int8_t _opt[60 - sizeof(struct tcphdr)]; - const u_int8_t *op; - unsigned int i; - unsigned int optsize = th->doff * 4 - - sizeof(struct tcphdr); - - op = skb_header_pointer(skb, - ptr + sizeof(struct tcphdr), - optsize, _opt); - if (op == NULL) { - sb_add(m, "OPT (TRUNCATED)"); - return; - } - - /* Max length: 127 "OPT (" 15*4*2chars ") " */ - sb_add(m, "OPT ("); - for (i =0; i < optsize; i++) - sb_add(m, "%02X", op[i]); - sb_add(m, ") "); - } - break; - } - case IPPROTO_UDP: - case IPPROTO_UDPLITE: { - struct udphdr _udph; - const struct udphdr *uh; - - if (currenthdr == IPPROTO_UDP) - /* Max length: 10 "PROTO=UDP " */ - sb_add(m, "PROTO=UDP " ); - else /* Max length: 14 "PROTO=UDPLITE " */ - sb_add(m, "PROTO=UDPLITE "); - - if (fragment) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - uh = skb_header_pointer(skb, ptr, sizeof(_udph), &_udph); - if (uh == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr); - return; - } - - /* Max length: 20 "SPT=65535 DPT=65535 " */ - sb_add(m, "SPT=%u DPT=%u LEN=%u ", - ntohs(uh->source), ntohs(uh->dest), - ntohs(uh->len)); - break; - } - case IPPROTO_ICMPV6: { - struct icmp6hdr _icmp6h; - const struct icmp6hdr *ic; - - /* Max length: 13 "PROTO=ICMPv6 " */ - sb_add(m, "PROTO=ICMPv6 "); - - if (fragment) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - ic = skb_header_pointer(skb, ptr, sizeof(_icmp6h), &_icmp6h); - if (ic == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr); - return; - } - - /* Max length: 18 "TYPE=255 CODE=255 " */ - sb_add(m, "TYPE=%u CODE=%u ", ic->icmp6_type, ic->icmp6_code); - - switch (ic->icmp6_type) { - case ICMPV6_ECHO_REQUEST: - case ICMPV6_ECHO_REPLY: - /* Max length: 19 "ID=65535 SEQ=65535 " */ - sb_add(m, "ID=%u SEQ=%u ", - ntohs(ic->icmp6_identifier), - ntohs(ic->icmp6_sequence)); - break; - case ICMPV6_MGM_QUERY: - case ICMPV6_MGM_REPORT: - case ICMPV6_MGM_REDUCTION: - break; - - case ICMPV6_PARAMPROB: - /* Max length: 17 "POINTER=ffffffff " */ - sb_add(m, "POINTER=%08x ", ntohl(ic->icmp6_pointer)); - /* Fall through */ - case ICMPV6_DEST_UNREACH: - case ICMPV6_PKT_TOOBIG: - case ICMPV6_TIME_EXCEED: - /* Max length: 3+maxlen */ - if (recurse) { - sb_add(m, "["); - dump_packet(m, info, skb, - ptr + sizeof(_icmp6h), 0); - sb_add(m, "] "); - } - - /* Max length: 10 "MTU=65535 " */ - if (ic->icmp6_type == ICMPV6_PKT_TOOBIG) - sb_add(m, "MTU=%u ", ntohl(ic->icmp6_mtu)); - } - break; - } - /* Max length: 10 "PROTO=255 " */ - default: - sb_add(m, "PROTO=%u ", currenthdr); - } - - /* Max length: 15 "UID=4294967295 " */ - if ((logflags & IP6T_LOG_UID) && recurse && skb->sk) { - read_lock_bh(&skb->sk->sk_callback_lock); - if (skb->sk->sk_socket && skb->sk->sk_socket->file) - sb_add(m, "UID=%u GID=%u ", - skb->sk->sk_socket->file->f_cred->fsuid, - skb->sk->sk_socket->file->f_cred->fsgid); - read_unlock_bh(&skb->sk->sk_callback_lock); - } - - /* Max length: 16 "MARK=0xFFFFFFFF " */ - if (!recurse && skb->mark) - sb_add(m, "MARK=0x%x ", skb->mark); -} - -static void dump_mac_header(struct sbuff *m, - const struct nf_loginfo *info, - const struct sk_buff *skb) -{ - struct net_device *dev = skb->dev; - unsigned int logflags = 0; - - if (info->type == NF_LOG_TYPE_LOG) - logflags = info->u.log.logflags; - - if (!(logflags & IP6T_LOG_MACDECODE)) - goto fallback; - - switch (dev->type) { - case ARPHRD_ETHER: - sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", - eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, - ntohs(eth_hdr(skb)->h_proto)); - return; - default: - break; - } - -fallback: - sb_add(m, "MAC="); - if (dev->hard_header_len && - skb->mac_header != skb->network_header) { - const unsigned char *p = skb_mac_header(skb); - unsigned int len = dev->hard_header_len; - unsigned int i; - - if (dev->type == ARPHRD_SIT && - (p -= ETH_HLEN) < skb->head) - p = NULL; - - if (p != NULL) { - sb_add(m, "%02x", *p++); - for (i = 1; i < len; i++) - sb_add(m, ":%02x", *p++); - } - sb_add(m, " "); - - if (dev->type == ARPHRD_SIT) { - const struct iphdr *iph = - (struct iphdr *)skb_mac_header(skb); - sb_add(m, "TUNNEL=%pI4->%pI4 ", &iph->saddr, &iph->daddr); - } - } else - sb_add(m, " "); -} - -static struct nf_loginfo default_loginfo = { - .type = NF_LOG_TYPE_LOG, - .u = { - .log = { - .level = 5, - .logflags = NF_LOG_MASK, - }, - }, -}; - -static void -ip6t_log_packet(u_int8_t pf, - unsigned int hooknum, - const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct nf_loginfo *loginfo, - const char *prefix) -{ - struct sbuff *m = sb_open(); - - if (!loginfo) - loginfo = &default_loginfo; - - sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level, - prefix, - in ? in->name : "", - out ? out->name : ""); - - if (in != NULL) - dump_mac_header(m, loginfo, skb); - - dump_packet(m, loginfo, skb, skb_network_offset(skb), 1); - - sb_close(m); -} - -static unsigned int -log_tg6(struct sk_buff *skb, const struct xt_action_param *par) -{ - const struct ip6t_log_info *loginfo = par->targinfo; - struct nf_loginfo li; - - li.type = NF_LOG_TYPE_LOG; - li.u.log.level = loginfo->level; - li.u.log.logflags = loginfo->logflags; - - ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in, par->out, - &li, loginfo->prefix); - return XT_CONTINUE; -} - - -static int log_tg6_check(const struct xt_tgchk_param *par) -{ - const struct ip6t_log_info *loginfo = par->targinfo; - - if (loginfo->level >= 8) { - pr_debug("level %u >= 8\n", loginfo->level); - return -EINVAL; - } - if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') { - pr_debug("prefix not null-terminated\n"); - return -EINVAL; - } - return 0; -} - -static struct xt_target log_tg6_reg __read_mostly = { - .name = "LOG", - .family = NFPROTO_IPV6, - .target = log_tg6, - .targetsize = sizeof(struct ip6t_log_info), - .checkentry = log_tg6_check, - .me = THIS_MODULE, -}; - -static struct nf_logger ip6t_logger __read_mostly = { - .name = "ip6t_LOG", - .logfn = &ip6t_log_packet, - .me = THIS_MODULE, -}; - -static int __init log_tg6_init(void) -{ - int ret; - - ret = xt_register_target(&log_tg6_reg); - if (ret < 0) - return ret; - nf_log_register(NFPROTO_IPV6, &ip6t_logger); - return 0; -} - -static void __exit log_tg6_exit(void) -{ - nf_log_unregister(&ip6t_logger); - xt_unregister_target(&log_tg6_reg); -} - -module_init(log_tg6_init); -module_exit(log_tg6_exit); diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index f8ac4ef0b794..b895d8b13215 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -524,6 +524,15 @@ config NETFILTER_XT_TARGET_LED For more information on the LEDs available on your system, see Documentation/leds/leds-class.txt +config NETFILTER_XT_TARGET_LOG + tristate "LOG target support" + default m if NETFILTER_ADVANCED=n + help + This option adds a `LOG' target, which allows you to create rules in + any iptables table which records the packet header to the syslog. + + To compile it as a module, choose M here. If unsure, say N. + config NETFILTER_XT_TARGET_MARK tristate '"MARK" target support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 40f4c3d636c5..a28c2a6563f8 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o +obj-$(CONFIG_NETFILTER_XT_TARGET_LOG) += xt_LOG.o obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o diff --git a/net/netfilter/xt_LOG.c b/net/netfilter/xt_LOG.c new file mode 100644 index 000000000000..1595608a892d --- /dev/null +++ b/net/netfilter/xt_LOG.c @@ -0,0 +1,921 @@ +/* + * This is a module which is used for logging packets. + */ + +/* (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2004 Netfilter Core Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static struct nf_loginfo default_loginfo = { + .type = NF_LOG_TYPE_LOG, + .u = { + .log = { + .level = 5, + .logflags = NF_LOG_MASK, + }, + }, +}; + +static int dump_udp_header(struct sbuff *m, const struct sk_buff *skb, + u8 proto, int fragment, unsigned int offset) +{ + struct udphdr _udph; + const struct udphdr *uh; + + if (proto == IPPROTO_UDP) + /* Max length: 10 "PROTO=UDP " */ + sb_add(m, "PROTO=UDP "); + else /* Max length: 14 "PROTO=UDPLITE " */ + sb_add(m, "PROTO=UDPLITE "); + + if (fragment) + goto out; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph); + if (uh == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset); + + return 1; + } + + /* Max length: 20 "SPT=65535 DPT=65535 " */ + sb_add(m, "SPT=%u DPT=%u LEN=%u ", ntohs(uh->source), ntohs(uh->dest), + ntohs(uh->len)); + +out: + return 0; +} + +static int dump_tcp_header(struct sbuff *m, const struct sk_buff *skb, + u8 proto, int fragment, unsigned int offset, + unsigned int logflags) +{ + struct tcphdr _tcph; + const struct tcphdr *th; + + /* Max length: 10 "PROTO=TCP " */ + sb_add(m, "PROTO=TCP "); + + if (fragment) + return 0; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph); + if (th == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset); + return 1; + } + + /* Max length: 20 "SPT=65535 DPT=65535 " */ + sb_add(m, "SPT=%u DPT=%u ", ntohs(th->source), ntohs(th->dest)); + /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ + if (logflags & XT_LOG_TCPSEQ) + sb_add(m, "SEQ=%u ACK=%u ", ntohl(th->seq), ntohl(th->ack_seq)); + + /* Max length: 13 "WINDOW=65535 " */ + sb_add(m, "WINDOW=%u ", ntohs(th->window)); + /* Max length: 9 "RES=0x3C " */ + sb_add(m, "RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(th) & + TCP_RESERVED_BITS) >> 22)); + /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */ + if (th->cwr) + sb_add(m, "CWR "); + if (th->ece) + sb_add(m, "ECE "); + if (th->urg) + sb_add(m, "URG "); + if (th->ack) + sb_add(m, "ACK "); + if (th->psh) + sb_add(m, "PSH "); + if (th->rst) + sb_add(m, "RST "); + if (th->syn) + sb_add(m, "SYN "); + if (th->fin) + sb_add(m, "FIN "); + /* Max length: 11 "URGP=65535 " */ + sb_add(m, "URGP=%u ", ntohs(th->urg_ptr)); + + if ((logflags & XT_LOG_TCPOPT) && th->doff*4 > sizeof(struct tcphdr)) { + u_int8_t _opt[60 - sizeof(struct tcphdr)]; + const u_int8_t *op; + unsigned int i; + unsigned int optsize = th->doff*4 - sizeof(struct tcphdr); + + op = skb_header_pointer(skb, offset + sizeof(struct tcphdr), + optsize, _opt); + if (op == NULL) { + sb_add(m, "OPT (TRUNCATED)"); + return 1; + } + + /* Max length: 127 "OPT (" 15*4*2chars ") " */ + sb_add(m, "OPT ("); + for (i = 0; i < optsize; i++) + sb_add(m, "%02X", op[i]); + + sb_add(m, ") "); + } + + return 0; +} + +/* One level of recursion won't kill us */ +static void dump_ipv4_packet(struct sbuff *m, + const struct nf_loginfo *info, + const struct sk_buff *skb, + unsigned int iphoff) +{ + struct iphdr _iph; + const struct iphdr *ih; + unsigned int logflags; + + if (info->type == NF_LOG_TYPE_LOG) + logflags = info->u.log.logflags; + else + logflags = NF_LOG_MASK; + + ih = skb_header_pointer(skb, iphoff, sizeof(_iph), &_iph); + if (ih == NULL) { + sb_add(m, "TRUNCATED"); + return; + } + + /* Important fields: + * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */ + /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */ + sb_add(m, "SRC=%pI4 DST=%pI4 ", + &ih->saddr, &ih->daddr); + + /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */ + sb_add(m, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ", + ntohs(ih->tot_len), ih->tos & IPTOS_TOS_MASK, + ih->tos & IPTOS_PREC_MASK, ih->ttl, ntohs(ih->id)); + + /* Max length: 6 "CE DF MF " */ + if (ntohs(ih->frag_off) & IP_CE) + sb_add(m, "CE "); + if (ntohs(ih->frag_off) & IP_DF) + sb_add(m, "DF "); + if (ntohs(ih->frag_off) & IP_MF) + sb_add(m, "MF "); + + /* Max length: 11 "FRAG:65535 " */ + if (ntohs(ih->frag_off) & IP_OFFSET) + sb_add(m, "FRAG:%u ", ntohs(ih->frag_off) & IP_OFFSET); + + if ((logflags & XT_LOG_IPOPT) && + ih->ihl * 4 > sizeof(struct iphdr)) { + const unsigned char *op; + unsigned char _opt[4 * 15 - sizeof(struct iphdr)]; + unsigned int i, optsize; + + optsize = ih->ihl * 4 - sizeof(struct iphdr); + op = skb_header_pointer(skb, iphoff+sizeof(_iph), + optsize, _opt); + if (op == NULL) { + sb_add(m, "TRUNCATED"); + return; + } + + /* Max length: 127 "OPT (" 15*4*2chars ") " */ + sb_add(m, "OPT ("); + for (i = 0; i < optsize; i++) + sb_add(m, "%02X", op[i]); + sb_add(m, ") "); + } + + switch (ih->protocol) { + case IPPROTO_TCP: + if (dump_tcp_header(m, skb, ih->protocol, + ntohs(ih->frag_off) & IP_OFFSET, + iphoff+ih->ihl*4, logflags)) + return; + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + if (dump_udp_header(m, skb, ih->protocol, + ntohs(ih->frag_off) & IP_OFFSET, + iphoff+ih->ihl*4)) + return; + case IPPROTO_ICMP: { + struct icmphdr _icmph; + const struct icmphdr *ich; + static const size_t required_len[NR_ICMP_TYPES+1] + = { [ICMP_ECHOREPLY] = 4, + [ICMP_DEST_UNREACH] + = 8 + sizeof(struct iphdr), + [ICMP_SOURCE_QUENCH] + = 8 + sizeof(struct iphdr), + [ICMP_REDIRECT] + = 8 + sizeof(struct iphdr), + [ICMP_ECHO] = 4, + [ICMP_TIME_EXCEEDED] + = 8 + sizeof(struct iphdr), + [ICMP_PARAMETERPROB] + = 8 + sizeof(struct iphdr), + [ICMP_TIMESTAMP] = 20, + [ICMP_TIMESTAMPREPLY] = 20, + [ICMP_ADDRESS] = 12, + [ICMP_ADDRESSREPLY] = 12 }; + + /* Max length: 11 "PROTO=ICMP " */ + sb_add(m, "PROTO=ICMP "); + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + ich = skb_header_pointer(skb, iphoff + ih->ihl * 4, + sizeof(_icmph), &_icmph); + if (ich == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); + break; + } + + /* Max length: 18 "TYPE=255 CODE=255 " */ + sb_add(m, "TYPE=%u CODE=%u ", ich->type, ich->code); + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + if (ich->type <= NR_ICMP_TYPES && + required_len[ich->type] && + skb->len-iphoff-ih->ihl*4 < required_len[ich->type]) { + sb_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); + break; + } + + switch (ich->type) { + case ICMP_ECHOREPLY: + case ICMP_ECHO: + /* Max length: 19 "ID=65535 SEQ=65535 " */ + sb_add(m, "ID=%u SEQ=%u ", + ntohs(ich->un.echo.id), + ntohs(ich->un.echo.sequence)); + break; + + case ICMP_PARAMETERPROB: + /* Max length: 14 "PARAMETER=255 " */ + sb_add(m, "PARAMETER=%u ", + ntohl(ich->un.gateway) >> 24); + break; + case ICMP_REDIRECT: + /* Max length: 24 "GATEWAY=255.255.255.255 " */ + sb_add(m, "GATEWAY=%pI4 ", &ich->un.gateway); + /* Fall through */ + case ICMP_DEST_UNREACH: + case ICMP_SOURCE_QUENCH: + case ICMP_TIME_EXCEEDED: + /* Max length: 3+maxlen */ + if (!iphoff) { /* Only recurse once. */ + sb_add(m, "["); + dump_ipv4_packet(m, info, skb, + iphoff + ih->ihl*4+sizeof(_icmph)); + sb_add(m, "] "); + } + + /* Max length: 10 "MTU=65535 " */ + if (ich->type == ICMP_DEST_UNREACH && + ich->code == ICMP_FRAG_NEEDED) + sb_add(m, "MTU=%u ", ntohs(ich->un.frag.mtu)); + } + break; + } + /* Max Length */ + case IPPROTO_AH: { + struct ip_auth_hdr _ahdr; + const struct ip_auth_hdr *ah; + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + /* Max length: 9 "PROTO=AH " */ + sb_add(m, "PROTO=AH "); + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + ah = skb_header_pointer(skb, iphoff+ih->ihl*4, + sizeof(_ahdr), &_ahdr); + if (ah == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); + break; + } + + /* Length: 15 "SPI=0xF1234567 " */ + sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); + break; + } + case IPPROTO_ESP: { + struct ip_esp_hdr _esph; + const struct ip_esp_hdr *eh; + + /* Max length: 10 "PROTO=ESP " */ + sb_add(m, "PROTO=ESP "); + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + eh = skb_header_pointer(skb, iphoff+ih->ihl*4, + sizeof(_esph), &_esph); + if (eh == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); + break; + } + + /* Length: 15 "SPI=0xF1234567 " */ + sb_add(m, "SPI=0x%x ", ntohl(eh->spi)); + break; + } + /* Max length: 10 "PROTO 255 " */ + default: + sb_add(m, "PROTO=%u ", ih->protocol); + } + + /* Max length: 15 "UID=4294967295 " */ + if ((logflags & XT_LOG_UID) && !iphoff && skb->sk) { + read_lock_bh(&skb->sk->sk_callback_lock); + if (skb->sk->sk_socket && skb->sk->sk_socket->file) + sb_add(m, "UID=%u GID=%u ", + skb->sk->sk_socket->file->f_cred->fsuid, + skb->sk->sk_socket->file->f_cred->fsgid); + read_unlock_bh(&skb->sk->sk_callback_lock); + } + + /* Max length: 16 "MARK=0xFFFFFFFF " */ + if (!iphoff && skb->mark) + sb_add(m, "MARK=0x%x ", skb->mark); + + /* Proto Max log string length */ + /* IP: 40+46+6+11+127 = 230 */ + /* TCP: 10+max(25,20+30+13+9+32+11+127) = 252 */ + /* UDP: 10+max(25,20) = 35 */ + /* UDPLITE: 14+max(25,20) = 39 */ + /* ICMP: 11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */ + /* ESP: 10+max(25)+15 = 50 */ + /* AH: 9+max(25)+15 = 49 */ + /* unknown: 10 */ + + /* (ICMP allows recursion one level deep) */ + /* maxlen = IP + ICMP + IP + max(TCP,UDP,ICMP,unknown) */ + /* maxlen = 230+ 91 + 230 + 252 = 803 */ +} + +static void dump_ipv4_mac_header(struct sbuff *m, + const struct nf_loginfo *info, + const struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + unsigned int logflags = 0; + + if (info->type == NF_LOG_TYPE_LOG) + logflags = info->u.log.logflags; + + if (!(logflags & XT_LOG_MACDECODE)) + goto fallback; + + switch (dev->type) { + case ARPHRD_ETHER: + sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", + eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, + ntohs(eth_hdr(skb)->h_proto)); + return; + default: + break; + } + +fallback: + sb_add(m, "MAC="); + if (dev->hard_header_len && + skb->mac_header != skb->network_header) { + const unsigned char *p = skb_mac_header(skb); + unsigned int i; + + sb_add(m, "%02x", *p++); + for (i = 1; i < dev->hard_header_len; i++, p++) + sb_add(m, ":%02x", *p); + } + sb_add(m, " "); +} + +static void +log_packet_common(struct sbuff *m, + u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level, + prefix, + in ? in->name : "", + out ? out->name : ""); +#ifdef CONFIG_BRIDGE_NETFILTER + if (skb->nf_bridge) { + const struct net_device *physindev; + const struct net_device *physoutdev; + + physindev = skb->nf_bridge->physindev; + if (physindev && in != physindev) + sb_add(m, "PHYSIN=%s ", physindev->name); + physoutdev = skb->nf_bridge->physoutdev; + if (physoutdev && out != physoutdev) + sb_add(m, "PHYSOUT=%s ", physoutdev->name); + } +#endif +} + + +static void +ipt_log_packet(u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + struct sbuff *m = sb_open(); + + if (!loginfo) + loginfo = &default_loginfo; + + log_packet_common(m, pf, hooknum, skb, in, out, loginfo, prefix); + + if (in != NULL) + dump_ipv4_mac_header(m, loginfo, skb); + + dump_ipv4_packet(m, loginfo, skb, 0); + + sb_close(m); +} + +#if IS_ENABLED(CONFIG_IPV6) +/* One level of recursion won't kill us */ +static void dump_ipv6_packet(struct sbuff *m, + const struct nf_loginfo *info, + const struct sk_buff *skb, unsigned int ip6hoff, + int recurse) +{ + u_int8_t currenthdr; + int fragment; + struct ipv6hdr _ip6h; + const struct ipv6hdr *ih; + unsigned int ptr; + unsigned int hdrlen = 0; + unsigned int logflags; + + if (info->type == NF_LOG_TYPE_LOG) + logflags = info->u.log.logflags; + else + logflags = NF_LOG_MASK; + + ih = skb_header_pointer(skb, ip6hoff, sizeof(_ip6h), &_ip6h); + if (ih == NULL) { + sb_add(m, "TRUNCATED"); + return; + } + + /* Max length: 88 "SRC=0000.0000.0000.0000.0000.0000.0000.0000 DST=0000.0000.0000.0000.0000.0000.0000.0000 " */ + sb_add(m, "SRC=%pI6 DST=%pI6 ", &ih->saddr, &ih->daddr); + + /* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */ + sb_add(m, "LEN=%Zu TC=%u HOPLIMIT=%u FLOWLBL=%u ", + ntohs(ih->payload_len) + sizeof(struct ipv6hdr), + (ntohl(*(__be32 *)ih) & 0x0ff00000) >> 20, + ih->hop_limit, + (ntohl(*(__be32 *)ih) & 0x000fffff)); + + fragment = 0; + ptr = ip6hoff + sizeof(struct ipv6hdr); + currenthdr = ih->nexthdr; + while (currenthdr != NEXTHDR_NONE && ip6t_ext_hdr(currenthdr)) { + struct ipv6_opt_hdr _hdr; + const struct ipv6_opt_hdr *hp; + + hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); + if (hp == NULL) { + sb_add(m, "TRUNCATED"); + return; + } + + /* Max length: 48 "OPT (...) " */ + if (logflags & XT_LOG_IPOPT) + sb_add(m, "OPT ( "); + + switch (currenthdr) { + case IPPROTO_FRAGMENT: { + struct frag_hdr _fhdr; + const struct frag_hdr *fh; + + sb_add(m, "FRAG:"); + fh = skb_header_pointer(skb, ptr, sizeof(_fhdr), + &_fhdr); + if (fh == NULL) { + sb_add(m, "TRUNCATED "); + return; + } + + /* Max length: 6 "65535 " */ + sb_add(m, "%u ", ntohs(fh->frag_off) & 0xFFF8); + + /* Max length: 11 "INCOMPLETE " */ + if (fh->frag_off & htons(0x0001)) + sb_add(m, "INCOMPLETE "); + + sb_add(m, "ID:%08x ", ntohl(fh->identification)); + + if (ntohs(fh->frag_off) & 0xFFF8) + fragment = 1; + + hdrlen = 8; + + break; + } + case IPPROTO_DSTOPTS: + case IPPROTO_ROUTING: + case IPPROTO_HOPOPTS: + if (fragment) { + if (logflags & XT_LOG_IPOPT) + sb_add(m, ")"); + return; + } + hdrlen = ipv6_optlen(hp); + break; + /* Max Length */ + case IPPROTO_AH: + if (logflags & XT_LOG_IPOPT) { + struct ip_auth_hdr _ahdr; + const struct ip_auth_hdr *ah; + + /* Max length: 3 "AH " */ + sb_add(m, "AH "); + + if (fragment) { + sb_add(m, ")"); + return; + } + + ah = skb_header_pointer(skb, ptr, sizeof(_ahdr), + &_ahdr); + if (ah == NULL) { + /* + * Max length: 26 "INCOMPLETE [65535 + * bytes] )" + */ + sb_add(m, "INCOMPLETE [%u bytes] )", + skb->len - ptr); + return; + } + + /* Length: 15 "SPI=0xF1234567 */ + sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); + + } + + hdrlen = (hp->hdrlen+2)<<2; + break; + case IPPROTO_ESP: + if (logflags & XT_LOG_IPOPT) { + struct ip_esp_hdr _esph; + const struct ip_esp_hdr *eh; + + /* Max length: 4 "ESP " */ + sb_add(m, "ESP "); + + if (fragment) { + sb_add(m, ")"); + return; + } + + /* + * Max length: 26 "INCOMPLETE [65535 bytes] )" + */ + eh = skb_header_pointer(skb, ptr, sizeof(_esph), + &_esph); + if (eh == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] )", + skb->len - ptr); + return; + } + + /* Length: 16 "SPI=0xF1234567 )" */ + sb_add(m, "SPI=0x%x )", ntohl(eh->spi)); + + } + return; + default: + /* Max length: 20 "Unknown Ext Hdr 255" */ + sb_add(m, "Unknown Ext Hdr %u", currenthdr); + return; + } + if (logflags & XT_LOG_IPOPT) + sb_add(m, ") "); + + currenthdr = hp->nexthdr; + ptr += hdrlen; + } + + switch (currenthdr) { + case IPPROTO_TCP: + if (dump_tcp_header(m, skb, currenthdr, fragment, ptr, + logflags)) + return; + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + if (dump_udp_header(m, skb, currenthdr, fragment, ptr)) + return; + case IPPROTO_ICMPV6: { + struct icmp6hdr _icmp6h; + const struct icmp6hdr *ic; + + /* Max length: 13 "PROTO=ICMPv6 " */ + sb_add(m, "PROTO=ICMPv6 "); + + if (fragment) + break; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + ic = skb_header_pointer(skb, ptr, sizeof(_icmp6h), &_icmp6h); + if (ic == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr); + return; + } + + /* Max length: 18 "TYPE=255 CODE=255 " */ + sb_add(m, "TYPE=%u CODE=%u ", ic->icmp6_type, ic->icmp6_code); + + switch (ic->icmp6_type) { + case ICMPV6_ECHO_REQUEST: + case ICMPV6_ECHO_REPLY: + /* Max length: 19 "ID=65535 SEQ=65535 " */ + sb_add(m, "ID=%u SEQ=%u ", + ntohs(ic->icmp6_identifier), + ntohs(ic->icmp6_sequence)); + break; + case ICMPV6_MGM_QUERY: + case ICMPV6_MGM_REPORT: + case ICMPV6_MGM_REDUCTION: + break; + + case ICMPV6_PARAMPROB: + /* Max length: 17 "POINTER=ffffffff " */ + sb_add(m, "POINTER=%08x ", ntohl(ic->icmp6_pointer)); + /* Fall through */ + case ICMPV6_DEST_UNREACH: + case ICMPV6_PKT_TOOBIG: + case ICMPV6_TIME_EXCEED: + /* Max length: 3+maxlen */ + if (recurse) { + sb_add(m, "["); + dump_ipv6_packet(m, info, skb, + ptr + sizeof(_icmp6h), 0); + sb_add(m, "] "); + } + + /* Max length: 10 "MTU=65535 " */ + if (ic->icmp6_type == ICMPV6_PKT_TOOBIG) + sb_add(m, "MTU=%u ", ntohl(ic->icmp6_mtu)); + } + break; + } + /* Max length: 10 "PROTO=255 " */ + default: + sb_add(m, "PROTO=%u ", currenthdr); + } + + /* Max length: 15 "UID=4294967295 " */ + if ((logflags & XT_LOG_UID) && recurse && skb->sk) { + read_lock_bh(&skb->sk->sk_callback_lock); + if (skb->sk->sk_socket && skb->sk->sk_socket->file) + sb_add(m, "UID=%u GID=%u ", + skb->sk->sk_socket->file->f_cred->fsuid, + skb->sk->sk_socket->file->f_cred->fsgid); + read_unlock_bh(&skb->sk->sk_callback_lock); + } + + /* Max length: 16 "MARK=0xFFFFFFFF " */ + if (!recurse && skb->mark) + sb_add(m, "MARK=0x%x ", skb->mark); +} + +static void dump_ipv6_mac_header(struct sbuff *m, + const struct nf_loginfo *info, + const struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + unsigned int logflags = 0; + + if (info->type == NF_LOG_TYPE_LOG) + logflags = info->u.log.logflags; + + if (!(logflags & XT_LOG_MACDECODE)) + goto fallback; + + switch (dev->type) { + case ARPHRD_ETHER: + sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", + eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, + ntohs(eth_hdr(skb)->h_proto)); + return; + default: + break; + } + +fallback: + sb_add(m, "MAC="); + if (dev->hard_header_len && + skb->mac_header != skb->network_header) { + const unsigned char *p = skb_mac_header(skb); + unsigned int len = dev->hard_header_len; + unsigned int i; + + if (dev->type == ARPHRD_SIT) { + p -= ETH_HLEN; + + if (p < skb->head) + p = NULL; + } + + if (p != NULL) { + sb_add(m, "%02x", *p++); + for (i = 1; i < len; i++) + sb_add(m, ":%02x", *p++); + } + sb_add(m, " "); + + if (dev->type == ARPHRD_SIT) { + const struct iphdr *iph = + (struct iphdr *)skb_mac_header(skb); + sb_add(m, "TUNNEL=%pI4->%pI4 ", &iph->saddr, + &iph->daddr); + } + } else + sb_add(m, " "); +} + +static void +ip6t_log_packet(u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + struct sbuff *m = sb_open(); + + if (!loginfo) + loginfo = &default_loginfo; + + log_packet_common(m, pf, hooknum, skb, in, out, loginfo, prefix); + + if (in != NULL) + dump_ipv6_mac_header(m, loginfo, skb); + + dump_ipv6_packet(m, loginfo, skb, skb_network_offset(skb), 1); + + sb_close(m); +} +#endif + +static unsigned int +log_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct xt_log_info *loginfo = par->targinfo; + struct nf_loginfo li; + + li.type = NF_LOG_TYPE_LOG; + li.u.log.level = loginfo->level; + li.u.log.logflags = loginfo->logflags; + + if (par->family == NFPROTO_IPV4) + ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); +#if IS_ENABLED(CONFIG_IPV6) + else if (par->family == NFPROTO_IPV6) + ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); +#endif + else + WARN_ON_ONCE(1); + + return XT_CONTINUE; +} + +static int log_tg_check(const struct xt_tgchk_param *par) +{ + const struct xt_log_info *loginfo = par->targinfo; + + if (par->family != NFPROTO_IPV4 && par->family != NFPROTO_IPV6) + return -EINVAL; + + if (loginfo->level >= 8) { + pr_debug("level %u >= 8\n", loginfo->level); + return -EINVAL; + } + + if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') { + pr_debug("prefix is not null-terminated\n"); + return -EINVAL; + } + + return 0; +} + +static struct xt_target log_tg_regs[] __read_mostly = { + { + .name = "LOG", + .family = NFPROTO_IPV4, + .target = log_tg, + .targetsize = sizeof(struct xt_log_info), + .checkentry = log_tg_check, + .me = THIS_MODULE, + }, +#if IS_ENABLED(CONFIG_IPV6) + { + .name = "LOG", + .family = NFPROTO_IPV6, + .target = log_tg, + .targetsize = sizeof(struct xt_log_info), + .checkentry = log_tg_check, + .me = THIS_MODULE, + }, +#endif +}; + +static struct nf_logger ipt_log_logger __read_mostly = { + .name = "ipt_LOG", + .logfn = &ipt_log_packet, + .me = THIS_MODULE, +}; + +#if IS_ENABLED(CONFIG_IPV6) +static struct nf_logger ip6t_log_logger __read_mostly = { + .name = "ip6t_LOG", + .logfn = &ip6t_log_packet, + .me = THIS_MODULE, +}; +#endif + +static int __init log_tg_init(void) +{ + int ret; + + ret = xt_register_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs)); + if (ret < 0) + return ret; + + nf_log_register(NFPROTO_IPV4, &ipt_log_logger); +#if IS_ENABLED(CONFIG_IPV6) + nf_log_register(NFPROTO_IPV6, &ip6t_log_logger); +#endif + return 0; +} + +static void __exit log_tg_exit(void) +{ + nf_log_unregister(&ipt_log_logger); +#if IS_ENABLED(CONFIG_IPV6) + nf_log_unregister(&ip6t_log_logger); +#endif + xt_unregister_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs)); +} + +module_init(log_tg_init); +module_exit(log_tg_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Netfilter Core Team "); +MODULE_AUTHOR("Jan Rekorajski "); +MODULE_DESCRIPTION("Xtables: IPv4/IPv6 packet logging"); +MODULE_ALIAS("ipt_LOG"); +MODULE_ALIAS("ip6t_LOG"); -- cgit From 5f1f815103eb13823d34875807c1710b8e2b0f09 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Wed, 29 Feb 2012 04:05:41 +0100 Subject: netfilter: remove ipt_SAME.h and ipt_realm.h These two headers are not required anymore, they have been replaced by xt_SAME.h and xt_realm.h. Florian Westphal pointed out this. Cc: "David S. Miller" Cc: Florian Westphal Signed-off-by: WANG Cong --- include/linux/netfilter_ipv4/Kbuild | 2 -- include/linux/netfilter_ipv4/ipt_SAME.h | 20 -------------------- include/linux/netfilter_ipv4/ipt_realm.h | 7 ------- 3 files changed, 29 deletions(-) delete mode 100644 include/linux/netfilter_ipv4/ipt_SAME.h delete mode 100644 include/linux/netfilter_ipv4/ipt_realm.h (limited to 'include') diff --git a/include/linux/netfilter_ipv4/Kbuild b/include/linux/netfilter_ipv4/Kbuild index f9930c87fff3..31f8bec95650 100644 --- a/include/linux/netfilter_ipv4/Kbuild +++ b/include/linux/netfilter_ipv4/Kbuild @@ -4,11 +4,9 @@ header-y += ipt_CLUSTERIP.h header-y += ipt_ECN.h header-y += ipt_LOG.h header-y += ipt_REJECT.h -header-y += ipt_SAME.h header-y += ipt_TTL.h header-y += ipt_ULOG.h header-y += ipt_addrtype.h header-y += ipt_ah.h header-y += ipt_ecn.h -header-y += ipt_realm.h header-y += ipt_ttl.h diff --git a/include/linux/netfilter_ipv4/ipt_SAME.h b/include/linux/netfilter_ipv4/ipt_SAME.h deleted file mode 100644 index 5bca78267afd..000000000000 --- a/include/linux/netfilter_ipv4/ipt_SAME.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _IPT_SAME_H -#define _IPT_SAME_H - -#include - -#define IPT_SAME_MAX_RANGE 10 - -#define IPT_SAME_NODST 0x01 - -struct ipt_same_info { - unsigned char info; - __u32 rangesize; - __u32 ipnum; - __u32 *iparray; - - /* hangs off end. */ - struct nf_nat_range range[IPT_SAME_MAX_RANGE]; -}; - -#endif /*_IPT_SAME_H*/ diff --git a/include/linux/netfilter_ipv4/ipt_realm.h b/include/linux/netfilter_ipv4/ipt_realm.h deleted file mode 100644 index b3996eaa0188..000000000000 --- a/include/linux/netfilter_ipv4/ipt_realm.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _IPT_REALM_H -#define _IPT_REALM_H - -#include -#define ipt_realm_info xt_realm_info - -#endif /* _IPT_REALM_H */ -- cgit From 33ee44643f10638f0ce6a96d11e30d1fbe877024 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 28 Feb 2012 16:56:31 +0100 Subject: netfilter: nf_ct_tcp: move retransmission and unacknowledged timeout to array This patch moves the retransmission and unacknowledged timeouts to the tcp_timeouts array. This change is required by follow-up patches. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_conntrack_tcp.h | 5 ++++- net/netfilter/nf_conntrack_proto_tcp.c | 27 +++++++++++++-------------- 2 files changed, 17 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/nf_conntrack_tcp.h b/include/linux/netfilter/nf_conntrack_tcp.h index 6e135f97e59a..e59868ae12d4 100644 --- a/include/linux/netfilter/nf_conntrack_tcp.h +++ b/include/linux/netfilter/nf_conntrack_tcp.h @@ -18,7 +18,10 @@ enum tcp_conntrack { TCP_CONNTRACK_LISTEN, /* obsolete */ #define TCP_CONNTRACK_SYN_SENT2 TCP_CONNTRACK_LISTEN TCP_CONNTRACK_MAX, - TCP_CONNTRACK_IGNORE + TCP_CONNTRACK_IGNORE, + TCP_CONNTRACK_RETRANS, + TCP_CONNTRACK_UNACK, + TCP_CONNTRACK_TIMEOUT_MAX }; /* Window scaling is advertised by the sender */ diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 97b9f3ebf28c..57c778546094 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -64,13 +64,7 @@ static const char *const tcp_conntrack_names[] = { #define HOURS * 60 MINS #define DAYS * 24 HOURS -/* RFC1122 says the R2 limit should be at least 100 seconds. - Linux uses 15 packets as limit, which corresponds - to ~13-30min depending on RTO. */ -static unsigned int nf_ct_tcp_timeout_max_retrans __read_mostly = 5 MINS; -static unsigned int nf_ct_tcp_timeout_unacknowledged __read_mostly = 5 MINS; - -static unsigned int tcp_timeouts[TCP_CONNTRACK_MAX] __read_mostly = { +static unsigned int tcp_timeouts[TCP_CONNTRACK_TIMEOUT_MAX] __read_mostly = { [TCP_CONNTRACK_SYN_SENT] = 2 MINS, [TCP_CONNTRACK_SYN_RECV] = 60 SECS, [TCP_CONNTRACK_ESTABLISHED] = 5 DAYS, @@ -80,6 +74,11 @@ static unsigned int tcp_timeouts[TCP_CONNTRACK_MAX] __read_mostly = { [TCP_CONNTRACK_TIME_WAIT] = 2 MINS, [TCP_CONNTRACK_CLOSE] = 10 SECS, [TCP_CONNTRACK_SYN_SENT2] = 2 MINS, +/* RFC1122 says the R2 limit should be at least 100 seconds. + Linux uses 15 packets as limit, which corresponds + to ~13-30min depending on RTO. */ + [TCP_CONNTRACK_RETRANS] = 5 MINS, + [TCP_CONNTRACK_UNACK] = 5 MINS, }; #define sNO TCP_CONNTRACK_NONE @@ -1015,12 +1014,12 @@ static int tcp_packet(struct nf_conn *ct, ct->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT; if (ct->proto.tcp.retrans >= nf_ct_tcp_max_retrans && - tcp_timeouts[new_state] > nf_ct_tcp_timeout_max_retrans) - timeout = nf_ct_tcp_timeout_max_retrans; + tcp_timeouts[new_state] > tcp_timeouts[TCP_CONNTRACK_RETRANS]) + timeout = tcp_timeouts[TCP_CONNTRACK_RETRANS]; else if ((ct->proto.tcp.seen[0].flags | ct->proto.tcp.seen[1].flags) & IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED && - tcp_timeouts[new_state] > nf_ct_tcp_timeout_unacknowledged) - timeout = nf_ct_tcp_timeout_unacknowledged; + tcp_timeouts[new_state] > tcp_timeouts[TCP_CONNTRACK_UNACK]) + timeout = tcp_timeouts[TCP_CONNTRACK_UNACK]; else timeout = tcp_timeouts[new_state]; spin_unlock_bh(&ct->lock); @@ -1301,14 +1300,14 @@ static struct ctl_table tcp_sysctl_table[] = { }, { .procname = "nf_conntrack_tcp_timeout_max_retrans", - .data = &nf_ct_tcp_timeout_max_retrans, + .data = &tcp_timeouts[TCP_CONNTRACK_RETRANS], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "nf_conntrack_tcp_timeout_unacknowledged", - .data = &nf_ct_tcp_timeout_unacknowledged, + .data = &tcp_timeouts[TCP_CONNTRACK_UNACK], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, @@ -1404,7 +1403,7 @@ static struct ctl_table tcp_compat_sysctl_table[] = { }, { .procname = "ip_conntrack_tcp_timeout_max_retrans", - .data = &nf_ct_tcp_timeout_max_retrans, + .data = &tcp_timeouts[TCP_CONNTRACK_RETRANS], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, -- cgit From 2c8503f55fbdfbeff4164f133df804cf4d316290 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 28 Feb 2012 18:23:31 +0100 Subject: netfilter: nf_conntrack: pass timeout array to l4->new and l4->packet This patch defines a new interface for l4 protocol trackers: unsigned int *(*get_timeouts)(struct net *net); that is used to return the array of unsigned int that contains the timeouts that will be applied for this flow. This is passed to the l4proto->new(...) and l4proto->packet(...) functions to specify the timeout policy. This interface allows per-net global timeout configuration (although only DCCP supports this by now) and it will allow custom custom timeout configuration by means of follow-up patches. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 8 +++++-- net/ipv4/netfilter/nf_conntrack_proto_icmp.c | 13 +++++++++--- net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 13 +++++++++--- net/netfilter/nf_conntrack_core.c | 18 ++++++++++------ net/netfilter/nf_conntrack_proto_dccp.c | 16 +++++++++----- net/netfilter/nf_conntrack_proto_generic.c | 29 ++++++++++++++++---------- net/netfilter/nf_conntrack_proto_gre.c | 15 +++++++++---- net/netfilter/nf_conntrack_proto_sctp.c | 14 ++++++++++--- net/netfilter/nf_conntrack_proto_tcp.c | 22 ++++++++++++------- net/netfilter/nf_conntrack_proto_udp.c | 16 ++++++++++---- net/netfilter/nf_conntrack_proto_udplite.c | 16 ++++++++++---- 11 files changed, 128 insertions(+), 52 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index e3d3ee3c06a2..c48b67405aa0 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -39,12 +39,13 @@ struct nf_conntrack_l4proto { unsigned int dataoff, enum ip_conntrack_info ctinfo, u_int8_t pf, - unsigned int hooknum); + unsigned int hooknum, + unsigned int *timeouts); /* Called when a new connection for this protocol found; * returns TRUE if it's OK. If so, packet() called next. */ bool (*new)(struct nf_conn *ct, const struct sk_buff *skb, - unsigned int dataoff); + unsigned int dataoff, unsigned int *timeouts); /* Called when a conntrack entry is destroyed */ void (*destroy)(struct nf_conn *ct); @@ -60,6 +61,9 @@ struct nf_conntrack_l4proto { /* Print out the private part of the conntrack. */ int (*print_conntrack)(struct seq_file *s, struct nf_conn *); + /* Return the array of timeouts for this protocol. */ + unsigned int *(*get_timeouts)(struct net *net); + /* convert protoinfo to nfnetink attributes */ int (*to_nlattr)(struct sk_buff *skb, struct nlattr *nla, struct nf_conn *ct); diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index ab5b27a2916f..6b801124b31f 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -75,25 +75,31 @@ static int icmp_print_tuple(struct seq_file *s, ntohs(tuple->src.u.icmp.id)); } +static unsigned int *icmp_get_timeouts(struct net *net) +{ + return &nf_ct_icmp_timeout; +} + /* Returns verdict for packet, or -1 for invalid. */ static int icmp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, u_int8_t pf, - unsigned int hooknum) + unsigned int hooknum, + unsigned int *timeout) { /* Do not immediately delete the connection after the first successful reply to avoid excessive conntrackd traffic and also to handle correctly ICMP echo reply duplicates. */ - nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmp_timeout); + nf_ct_refresh_acct(ct, ctinfo, skb, *timeout); return NF_ACCEPT; } /* Called when a new connection for this protocol found. */ static bool icmp_new(struct nf_conn *ct, const struct sk_buff *skb, - unsigned int dataoff) + unsigned int dataoff, unsigned int *timeouts) { static const u_int8_t valid_new[] = { [ICMP_ECHO] = 1, @@ -298,6 +304,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly = .invert_tuple = icmp_invert_tuple, .print_tuple = icmp_print_tuple, .packet = icmp_packet, + .get_timeouts = icmp_get_timeouts, .new = icmp_new, .error = icmp_error, .destroy = NULL, diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index 7c05e7eacbc6..2eb9751eb7a8 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -88,25 +88,31 @@ static int icmpv6_print_tuple(struct seq_file *s, ntohs(tuple->src.u.icmp.id)); } +static unsigned int *icmpv6_get_timeouts(struct net *net) +{ + return &nf_ct_icmpv6_timeout; +} + /* Returns verdict for packet, or -1 for invalid. */ static int icmpv6_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, u_int8_t pf, - unsigned int hooknum) + unsigned int hooknum, + unsigned int *timeout) { /* Do not immediately delete the connection after the first successful reply to avoid excessive conntrackd traffic and also to handle correctly ICMP echo reply duplicates. */ - nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmpv6_timeout); + nf_ct_refresh_acct(ct, ctinfo, skb, *timeout); return NF_ACCEPT; } /* Called when a new connection for this protocol found. */ static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb, - unsigned int dataoff) + unsigned int dataoff, unsigned int *timeouts) { static const u_int8_t valid_new[] = { [ICMPV6_ECHO_REQUEST - 128] = 1, @@ -293,6 +299,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 __read_mostly = .invert_tuple = icmpv6_invert_tuple, .print_tuple = icmpv6_print_tuple, .packet = icmpv6_packet, + .get_timeouts = icmpv6_get_timeouts, .new = icmpv6_new, .error = icmpv6_error, #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index ed86a3be678e..d18995eea1c6 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -763,7 +763,8 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, struct nf_conntrack_l3proto *l3proto, struct nf_conntrack_l4proto *l4proto, struct sk_buff *skb, - unsigned int dataoff, u32 hash) + unsigned int dataoff, u32 hash, + unsigned int *timeouts) { struct nf_conn *ct; struct nf_conn_help *help; @@ -782,7 +783,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, if (IS_ERR(ct)) return (struct nf_conntrack_tuple_hash *)ct; - if (!l4proto->new(ct, skb, dataoff)) { + if (!l4proto->new(ct, skb, dataoff, timeouts)) { nf_conntrack_free(ct); pr_debug("init conntrack: can't track with proto module\n"); return NULL; @@ -848,7 +849,8 @@ resolve_normal_ct(struct net *net, struct nf_conn *tmpl, struct nf_conntrack_l3proto *l3proto, struct nf_conntrack_l4proto *l4proto, int *set_reply, - enum ip_conntrack_info *ctinfo) + enum ip_conntrack_info *ctinfo, + unsigned int *timeouts) { struct nf_conntrack_tuple tuple; struct nf_conntrack_tuple_hash *h; @@ -868,7 +870,7 @@ resolve_normal_ct(struct net *net, struct nf_conn *tmpl, h = __nf_conntrack_find_get(net, zone, &tuple, hash); if (!h) { h = init_conntrack(net, tmpl, &tuple, l3proto, l4proto, - skb, dataoff, hash); + skb, dataoff, hash, timeouts); if (!h) return NULL; if (IS_ERR(h)) @@ -909,6 +911,7 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, enum ip_conntrack_info ctinfo; struct nf_conntrack_l3proto *l3proto; struct nf_conntrack_l4proto *l4proto; + unsigned int *timeouts; unsigned int dataoff; u_int8_t protonum; int set_reply = 0; @@ -955,8 +958,11 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, goto out; } + timeouts = l4proto->get_timeouts(net); + ct = resolve_normal_ct(net, tmpl, skb, dataoff, pf, protonum, - l3proto, l4proto, &set_reply, &ctinfo); + l3proto, l4proto, &set_reply, &ctinfo, + timeouts); if (!ct) { /* Not valid part of a connection */ NF_CT_STAT_INC_ATOMIC(net, invalid); @@ -973,7 +979,7 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, NF_CT_ASSERT(skb->nfct); - ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, hooknum); + ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, hooknum, timeouts); if (ret <= 0) { /* Invalid: inverse of the return code tells * the netfilter core what to do */ diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index d6dde6dc09e6..8ea33598a0a7 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -423,7 +423,7 @@ static bool dccp_invert_tuple(struct nf_conntrack_tuple *inv, } static bool dccp_new(struct nf_conn *ct, const struct sk_buff *skb, - unsigned int dataoff) + unsigned int dataoff, unsigned int *timeouts) { struct net *net = nf_ct_net(ct); struct dccp_net *dn; @@ -472,12 +472,17 @@ static u64 dccp_ack_seq(const struct dccp_hdr *dh) ntohl(dhack->dccph_ack_nr_low); } +static unsigned int *dccp_get_timeouts(struct net *net) +{ + return dccp_pernet(net)->dccp_timeout; +} + static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - u_int8_t pf, unsigned int hooknum) + u_int8_t pf, unsigned int hooknum, + unsigned int *timeouts) { struct net *net = nf_ct_net(ct); - struct dccp_net *dn; enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); struct dccp_hdr _dh, *dh; u_int8_t type, old_state, new_state; @@ -559,8 +564,7 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, if (new_state != old_state) nf_conntrack_event_cache(IPCT_PROTOINFO, ct); - dn = dccp_pernet(net); - nf_ct_refresh_acct(ct, ctinfo, skb, dn->dccp_timeout[new_state]); + nf_ct_refresh_acct(ct, ctinfo, skb, timeouts[new_state]); return NF_ACCEPT; } @@ -767,6 +771,7 @@ static struct nf_conntrack_l4proto dccp_proto4 __read_mostly = { .invert_tuple = dccp_invert_tuple, .new = dccp_new, .packet = dccp_packet, + .get_timeouts = dccp_get_timeouts, .error = dccp_error, .print_tuple = dccp_print_tuple, .print_conntrack = dccp_print_conntrack, @@ -789,6 +794,7 @@ static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = { .invert_tuple = dccp_invert_tuple, .new = dccp_new, .packet = dccp_packet, + .get_timeouts = dccp_get_timeouts, .error = dccp_error, .print_tuple = dccp_print_tuple, .print_conntrack = dccp_print_conntrack, diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c index e2091d0c7a2f..0e6c5451db80 100644 --- a/net/netfilter/nf_conntrack_proto_generic.c +++ b/net/netfilter/nf_conntrack_proto_generic.c @@ -40,21 +40,27 @@ static int generic_print_tuple(struct seq_file *s, return 0; } +static unsigned int *generic_get_timeouts(struct net *net) +{ + return &nf_ct_generic_timeout; +} + /* Returns verdict for packet, or -1 for invalid. */ -static int packet(struct nf_conn *ct, - const struct sk_buff *skb, - unsigned int dataoff, - enum ip_conntrack_info ctinfo, - u_int8_t pf, - unsigned int hooknum) +static int generic_packet(struct nf_conn *ct, + const struct sk_buff *skb, + unsigned int dataoff, + enum ip_conntrack_info ctinfo, + u_int8_t pf, + unsigned int hooknum, + unsigned int *timeout) { - nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_generic_timeout); + nf_ct_refresh_acct(ct, ctinfo, skb, *timeout); return NF_ACCEPT; } /* Called when a new connection for this protocol found. */ -static bool new(struct nf_conn *ct, const struct sk_buff *skb, - unsigned int dataoff) +static bool generic_new(struct nf_conn *ct, const struct sk_buff *skb, + unsigned int dataoff, unsigned int *timeouts) { return true; } @@ -93,8 +99,9 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_generic __read_mostly = .pkt_to_tuple = generic_pkt_to_tuple, .invert_tuple = generic_invert_tuple, .print_tuple = generic_print_tuple, - .packet = packet, - .new = new, + .packet = generic_packet, + .get_timeouts = generic_get_timeouts, + .new = generic_new, #ifdef CONFIG_SYSCTL .ctl_table_header = &generic_sysctl_header, .ctl_table = generic_sysctl_table, diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index 8144f22d159a..1bf01c95658b 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -235,13 +235,19 @@ static int gre_print_conntrack(struct seq_file *s, struct nf_conn *ct) (ct->proto.gre.stream_timeout / HZ)); } +static unsigned int *gre_get_timeouts(struct net *net) +{ + return gre_timeouts; +} + /* Returns verdict for packet, and may modify conntrack */ static int gre_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, u_int8_t pf, - unsigned int hooknum) + unsigned int hooknum, + unsigned int *timeouts) { /* If we've seen traffic both ways, this is a GRE connection. * Extend timeout. */ @@ -260,15 +266,15 @@ static int gre_packet(struct nf_conn *ct, /* Called when a new connection for this protocol found. */ static bool gre_new(struct nf_conn *ct, const struct sk_buff *skb, - unsigned int dataoff) + unsigned int dataoff, unsigned int *timeouts) { pr_debug(": "); nf_ct_dump_tuple(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); /* initialize to sane value. Ideally a conntrack helper * (e.g. in case of pptp) is increasing them */ - ct->proto.gre.stream_timeout = gre_timeouts[GRE_CT_REPLIED]; - ct->proto.gre.timeout = gre_timeouts[GRE_CT_UNREPLIED]; + ct->proto.gre.stream_timeout = timeouts[GRE_CT_REPLIED]; + ct->proto.gre.timeout = timeouts[GRE_CT_UNREPLIED]; return true; } @@ -295,6 +301,7 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = { .invert_tuple = gre_invert_tuple, .print_tuple = gre_print_tuple, .print_conntrack = gre_print_conntrack, + .get_timeouts = gre_get_timeouts, .packet = gre_packet, .new = gre_new, .destroy = gre_destroy, diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index afa69136061a..2a0703371e24 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -279,13 +279,19 @@ static int sctp_new_state(enum ip_conntrack_dir dir, return sctp_conntracks[dir][i][cur_state]; } +static unsigned int *sctp_get_timeouts(struct net *net) +{ + return sctp_timeouts; +} + /* Returns verdict for packet, or -NF_ACCEPT for invalid. */ static int sctp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, u_int8_t pf, - unsigned int hooknum) + unsigned int hooknum, + unsigned int *timeouts) { enum sctp_conntrack new_state, old_state; enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); @@ -370,7 +376,7 @@ static int sctp_packet(struct nf_conn *ct, } spin_unlock_bh(&ct->lock); - nf_ct_refresh_acct(ct, ctinfo, skb, sctp_timeouts[new_state]); + nf_ct_refresh_acct(ct, ctinfo, skb, timeouts[new_state]); if (old_state == SCTP_CONNTRACK_COOKIE_ECHOED && dir == IP_CT_DIR_REPLY && @@ -390,7 +396,7 @@ out: /* Called when a new connection for this protocol found. */ static bool sctp_new(struct nf_conn *ct, const struct sk_buff *skb, - unsigned int dataoff) + unsigned int dataoff, unsigned int *timeouts) { enum sctp_conntrack new_state; const struct sctphdr *sh; @@ -664,6 +670,7 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4 __read_mostly = { .print_tuple = sctp_print_tuple, .print_conntrack = sctp_print_conntrack, .packet = sctp_packet, + .get_timeouts = sctp_get_timeouts, .new = sctp_new, .me = THIS_MODULE, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) @@ -694,6 +701,7 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 __read_mostly = { .print_tuple = sctp_print_tuple, .print_conntrack = sctp_print_conntrack, .packet = sctp_packet, + .get_timeouts = sctp_get_timeouts, .new = sctp_new, .me = THIS_MODULE, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 57c778546094..8372bb43feb0 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -813,13 +813,19 @@ static int tcp_error(struct net *net, struct nf_conn *tmpl, return NF_ACCEPT; } +static unsigned int *tcp_get_timeouts(struct net *net) +{ + return tcp_timeouts; +} + /* Returns verdict for packet, or -1 for invalid. */ static int tcp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, u_int8_t pf, - unsigned int hooknum) + unsigned int hooknum, + unsigned int *timeouts) { struct net *net = nf_ct_net(ct); struct nf_conntrack_tuple *tuple; @@ -1014,14 +1020,14 @@ static int tcp_packet(struct nf_conn *ct, ct->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT; if (ct->proto.tcp.retrans >= nf_ct_tcp_max_retrans && - tcp_timeouts[new_state] > tcp_timeouts[TCP_CONNTRACK_RETRANS]) - timeout = tcp_timeouts[TCP_CONNTRACK_RETRANS]; + timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS]) + timeout = timeouts[TCP_CONNTRACK_RETRANS]; else if ((ct->proto.tcp.seen[0].flags | ct->proto.tcp.seen[1].flags) & IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED && - tcp_timeouts[new_state] > tcp_timeouts[TCP_CONNTRACK_UNACK]) - timeout = tcp_timeouts[TCP_CONNTRACK_UNACK]; + timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK]) + timeout = timeouts[TCP_CONNTRACK_UNACK]; else - timeout = tcp_timeouts[new_state]; + timeout = timeouts[new_state]; spin_unlock_bh(&ct->lock); if (new_state != old_state) @@ -1053,7 +1059,7 @@ static int tcp_packet(struct nf_conn *ct, /* Called when a new connection for this protocol found. */ static bool tcp_new(struct nf_conn *ct, const struct sk_buff *skb, - unsigned int dataoff) + unsigned int dataoff, unsigned int *timeouts) { enum tcp_conntrack new_state; const struct tcphdr *th; @@ -1444,6 +1450,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly = .print_tuple = tcp_print_tuple, .print_conntrack = tcp_print_conntrack, .packet = tcp_packet, + .get_timeouts = tcp_get_timeouts, .new = tcp_new, .error = tcp_error, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) @@ -1476,6 +1483,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6 __read_mostly = .print_tuple = tcp_print_tuple, .print_conntrack = tcp_print_conntrack, .packet = tcp_packet, + .get_timeouts = tcp_get_timeouts, .new = tcp_new, .error = tcp_error, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c index 5b24ff882f95..70e005992d5b 100644 --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -71,32 +71,38 @@ static int udp_print_tuple(struct seq_file *s, ntohs(tuple->dst.u.udp.port)); } +static unsigned int *udp_get_timeouts(struct net *net) +{ + return udp_timeouts; +} + /* Returns verdict for packet, and may modify conntracktype */ static int udp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, u_int8_t pf, - unsigned int hooknum) + unsigned int hooknum, + unsigned int *timeouts) { /* If we've seen traffic both ways, this is some kind of UDP stream. Extend timeout. */ if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { nf_ct_refresh_acct(ct, ctinfo, skb, - udp_timeouts[UDP_CT_REPLIED]); + timeouts[UDP_CT_REPLIED]); /* Also, more likely to be important, and not a probe */ if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status)) nf_conntrack_event_cache(IPCT_ASSURED, ct); } else { nf_ct_refresh_acct(ct, ctinfo, skb, - udp_timeouts[UDP_CT_UNREPLIED]); + timeouts[UDP_CT_UNREPLIED]); } return NF_ACCEPT; } /* Called when a new connection for this protocol found. */ static bool udp_new(struct nf_conn *ct, const struct sk_buff *skb, - unsigned int dataoff) + unsigned int dataoff, unsigned int *timeouts) { return true; } @@ -196,6 +202,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 __read_mostly = .invert_tuple = udp_invert_tuple, .print_tuple = udp_print_tuple, .packet = udp_packet, + .get_timeouts = udp_get_timeouts, .new = udp_new, .error = udp_error, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) @@ -224,6 +231,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6 __read_mostly = .invert_tuple = udp_invert_tuple, .print_tuple = udp_print_tuple, .packet = udp_packet, + .get_timeouts = udp_get_timeouts, .new = udp_new, .error = udp_error, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) diff --git a/net/netfilter/nf_conntrack_proto_udplite.c b/net/netfilter/nf_conntrack_proto_udplite.c index e73071743e01..0b32ccb1d515 100644 --- a/net/netfilter/nf_conntrack_proto_udplite.c +++ b/net/netfilter/nf_conntrack_proto_udplite.c @@ -68,32 +68,38 @@ static int udplite_print_tuple(struct seq_file *s, ntohs(tuple->dst.u.udp.port)); } +static unsigned int *udplite_get_timeouts(struct net *net) +{ + return udplite_timeouts; +} + /* Returns verdict for packet, and may modify conntracktype */ static int udplite_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, u_int8_t pf, - unsigned int hooknum) + unsigned int hooknum, + unsigned int *timeouts) { /* If we've seen traffic both ways, this is some kind of UDP stream. Extend timeout. */ if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { nf_ct_refresh_acct(ct, ctinfo, skb, - udplite_timeouts[UDPLITE_CT_REPLIED]); + timeouts[UDPLITE_CT_REPLIED]); /* Also, more likely to be important, and not a probe */ if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status)) nf_conntrack_event_cache(IPCT_ASSURED, ct); } else { nf_ct_refresh_acct(ct, ctinfo, skb, - udplite_timeouts[UDPLITE_CT_UNREPLIED]); + timeouts[UDPLITE_CT_UNREPLIED]); } return NF_ACCEPT; } /* Called when a new connection for this protocol found. */ static bool udplite_new(struct nf_conn *ct, const struct sk_buff *skb, - unsigned int dataoff) + unsigned int dataoff, unsigned int *timeouts) { return true; } @@ -181,6 +187,7 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4 __read_mostly = .invert_tuple = udplite_invert_tuple, .print_tuple = udplite_print_tuple, .packet = udplite_packet, + .get_timeouts = udplite_get_timeouts, .new = udplite_new, .error = udplite_error, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) @@ -205,6 +212,7 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6 __read_mostly = .invert_tuple = udplite_invert_tuple, .print_tuple = udplite_print_tuple, .packet = udplite_packet, + .get_timeouts = udplite_get_timeouts, .new = udplite_new, .error = udplite_error, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) -- cgit From 50978462300f74dc48aea4a38471cb69bdf741a5 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 28 Feb 2012 19:13:48 +0100 Subject: netfilter: add cttimeout infrastructure for fine timeout tuning This patch adds the infrastructure to add fine timeout tuning over nfnetlink. Now you can use the NFNL_SUBSYS_CTNETLINK_TIMEOUT subsystem to create/delete/dump timeout objects that contain some specific timeout policy for one flow. The follow up patches will allow you attach timeout policy object to conntrack via the CT target and the conntrack extension infrastructure. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/Kbuild | 1 + include/linux/netfilter/nfnetlink.h | 3 +- include/linux/netfilter/nfnetlink_cttimeout.h | 114 +++++++ include/net/netfilter/nf_conntrack_l4proto.h | 11 + net/ipv4/netfilter/nf_conntrack_proto_icmp.c | 47 +++ net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 47 +++ net/netfilter/Kconfig | 11 + net/netfilter/Makefile | 1 + net/netfilter/nf_conntrack_proto_dccp.c | 70 +++++ net/netfilter/nf_conntrack_proto_generic.c | 48 +++ net/netfilter/nf_conntrack_proto_gre.c | 55 ++++ net/netfilter/nf_conntrack_proto_sctp.c | 69 +++++ net/netfilter/nf_conntrack_proto_tcp.c | 127 ++++++++ net/netfilter/nf_conntrack_proto_udp.c | 64 ++++ net/netfilter/nf_conntrack_proto_udplite.c | 66 ++++ net/netfilter/nfnetlink_cttimeout.c | 398 +++++++++++++++++++++++++ 16 files changed, 1131 insertions(+), 1 deletion(-) create mode 100644 include/linux/netfilter/nfnetlink_cttimeout.h create mode 100644 net/netfilter/nfnetlink_cttimeout.c (limited to 'include') diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index aaa72aaa21de..1697036336b6 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild @@ -10,6 +10,7 @@ header-y += nfnetlink.h header-y += nfnetlink_acct.h header-y += nfnetlink_compat.h header-y += nfnetlink_conntrack.h +header-y += nfnetlink_cttimeout.h header-y += nfnetlink_log.h header-y += nfnetlink_queue.h header-y += x_tables.h diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index b64454c2f79f..6fd1f0d07e64 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h @@ -49,7 +49,8 @@ struct nfgenmsg { #define NFNL_SUBSYS_OSF 5 #define NFNL_SUBSYS_IPSET 6 #define NFNL_SUBSYS_ACCT 7 -#define NFNL_SUBSYS_COUNT 8 +#define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8 +#define NFNL_SUBSYS_COUNT 9 #ifdef __KERNEL__ diff --git a/include/linux/netfilter/nfnetlink_cttimeout.h b/include/linux/netfilter/nfnetlink_cttimeout.h new file mode 100644 index 000000000000..a2810a7c5e30 --- /dev/null +++ b/include/linux/netfilter/nfnetlink_cttimeout.h @@ -0,0 +1,114 @@ +#ifndef _CTTIMEOUT_NETLINK_H +#define _CTTIMEOUT_NETLINK_H +#include + +enum ctnl_timeout_msg_types { + IPCTNL_MSG_TIMEOUT_NEW, + IPCTNL_MSG_TIMEOUT_GET, + IPCTNL_MSG_TIMEOUT_DELETE, + + IPCTNL_MSG_TIMEOUT_MAX +}; + +enum ctattr_timeout { + CTA_TIMEOUT_UNSPEC, + CTA_TIMEOUT_NAME, + CTA_TIMEOUT_L3PROTO, + CTA_TIMEOUT_L4PROTO, + CTA_TIMEOUT_DATA, + CTA_TIMEOUT_USE, + __CTA_TIMEOUT_MAX +}; +#define CTA_TIMEOUT_MAX (__CTA_TIMEOUT_MAX - 1) + +enum ctattr_timeout_generic { + CTA_TIMEOUT_GENERIC_UNSPEC, + CTA_TIMEOUT_GENERIC_TIMEOUT, + __CTA_TIMEOUT_GENERIC_MAX +}; +#define CTA_TIMEOUT_GENERIC_MAX (__CTA_TIMEOUT_GENERIC_MAX - 1) + +enum ctattr_timeout_tcp { + CTA_TIMEOUT_TCP_UNSPEC, + CTA_TIMEOUT_TCP_SYN_SENT, + CTA_TIMEOUT_TCP_SYN_RECV, + CTA_TIMEOUT_TCP_ESTABLISHED, + CTA_TIMEOUT_TCP_FIN_WAIT, + CTA_TIMEOUT_TCP_CLOSE_WAIT, + CTA_TIMEOUT_TCP_LAST_ACK, + CTA_TIMEOUT_TCP_TIME_WAIT, + CTA_TIMEOUT_TCP_CLOSE, + CTA_TIMEOUT_TCP_SYN_SENT2, + CTA_TIMEOUT_TCP_RETRANS, + CTA_TIMEOUT_TCP_UNACK, + __CTA_TIMEOUT_TCP_MAX +}; +#define CTA_TIMEOUT_TCP_MAX (__CTA_TIMEOUT_TCP_MAX - 1) + +enum ctattr_timeout_udp { + CTA_TIMEOUT_UDP_UNSPEC, + CTA_TIMEOUT_UDP_UNREPLIED, + CTA_TIMEOUT_UDP_REPLIED, + __CTA_TIMEOUT_UDP_MAX +}; +#define CTA_TIMEOUT_UDP_MAX (__CTA_TIMEOUT_UDP_MAX - 1) + +enum ctattr_timeout_udplite { + CTA_TIMEOUT_UDPLITE_UNSPEC, + CTA_TIMEOUT_UDPLITE_UNREPLIED, + CTA_TIMEOUT_UDPLITE_REPLIED, + __CTA_TIMEOUT_UDPLITE_MAX +}; +#define CTA_TIMEOUT_UDPLITE_MAX (__CTA_TIMEOUT_UDPLITE_MAX - 1) + +enum ctattr_timeout_icmp { + CTA_TIMEOUT_ICMP_UNSPEC, + CTA_TIMEOUT_ICMP_TIMEOUT, + __CTA_TIMEOUT_ICMP_MAX +}; +#define CTA_TIMEOUT_ICMP_MAX (__CTA_TIMEOUT_ICMP_MAX - 1) + +enum ctattr_timeout_dccp { + CTA_TIMEOUT_DCCP_UNSPEC, + CTA_TIMEOUT_DCCP_REQUEST, + CTA_TIMEOUT_DCCP_RESPOND, + CTA_TIMEOUT_DCCP_PARTOPEN, + CTA_TIMEOUT_DCCP_OPEN, + CTA_TIMEOUT_DCCP_CLOSEREQ, + CTA_TIMEOUT_DCCP_CLOSING, + CTA_TIMEOUT_DCCP_TIMEWAIT, + __CTA_TIMEOUT_DCCP_MAX +}; +#define CTA_TIMEOUT_DCCP_MAX (__CTA_TIMEOUT_DCCP_MAX - 1) + +enum ctattr_timeout_sctp { + CTA_TIMEOUT_SCTP_UNSPEC, + CTA_TIMEOUT_SCTP_CLOSED, + CTA_TIMEOUT_SCTP_COOKIE_WAIT, + CTA_TIMEOUT_SCTP_COOKIE_ECHOED, + CTA_TIMEOUT_SCTP_ESTABLISHED, + CTA_TIMEOUT_SCTP_SHUTDOWN_SENT, + CTA_TIMEOUT_SCTP_SHUTDOWN_RECD, + CTA_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT, + __CTA_TIMEOUT_SCTP_MAX +}; +#define CTA_TIMEOUT_SCTP_MAX (__CTA_TIMEOUT_SCTP_MAX - 1) + +enum ctattr_timeout_icmpv6 { + CTA_TIMEOUT_ICMPV6_UNSPEC, + CTA_TIMEOUT_ICMPV6_TIMEOUT, + __CTA_TIMEOUT_ICMPV6_MAX +}; +#define CTA_TIMEOUT_ICMPV6_MAX (__CTA_TIMEOUT_ICMPV6_MAX - 1) + +enum ctattr_timeout_gre { + CTA_TIMEOUT_GRE_UNSPEC, + CTA_TIMEOUT_GRE_UNREPLIED, + CTA_TIMEOUT_GRE_REPLIED, + __CTA_TIMEOUT_GRE_MAX +}; +#define CTA_TIMEOUT_GRE_MAX (__CTA_TIMEOUT_GRE_MAX - 1) + +#define CTNL_TIMEOUT_NAME_MAX 32 + +#endif diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index c48b67405aa0..90c67c7db7e9 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -83,6 +83,17 @@ struct nf_conntrack_l4proto { size_t nla_size; +#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) + struct { + size_t obj_size; + int (*nlattr_to_obj)(struct nlattr *tb[], void *data); + int (*obj_to_nlattr)(struct sk_buff *skb, const void *data); + + unsigned int nlattr_max; + const struct nla_policy *nla_policy; + } ctnl_timeout; +#endif + #ifdef CONFIG_SYSCTL struct ctl_table_header **ctl_table_header; struct ctl_table *ctl_table; diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index 6b801124b31f..7cbe9cb261c2 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -269,6 +269,44 @@ static int icmp_nlattr_tuple_size(void) } #endif +#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) + +#include +#include + +static int icmp_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +{ + unsigned int *timeout = data; + + if (tb[CTA_TIMEOUT_ICMP_TIMEOUT]) { + *timeout = + ntohl(nla_get_be32(tb[CTA_TIMEOUT_ICMP_TIMEOUT])) * HZ; + } else { + /* Set default ICMP timeout. */ + *timeout = nf_ct_icmp_timeout; + } + return 0; +} + +static int +icmp_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data) +{ + const unsigned int *timeout = data; + + NLA_PUT_BE32(skb, CTA_TIMEOUT_ICMP_TIMEOUT, htonl(*timeout / HZ)); + + return 0; + +nla_put_failure: + return -ENOSPC; +} + +static const struct nla_policy +icmp_timeout_nla_policy[CTA_TIMEOUT_ICMP_MAX+1] = { + [CTA_TIMEOUT_ICMP_TIMEOUT] = { .type = NLA_U32 }, +}; +#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ + #ifdef CONFIG_SYSCTL static struct ctl_table_header *icmp_sysctl_header; static struct ctl_table icmp_sysctl_table[] = { @@ -315,6 +353,15 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly = .nlattr_to_tuple = icmp_nlattr_to_tuple, .nla_policy = icmp_nla_policy, #endif +#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) + .ctnl_timeout = { + .nlattr_to_obj = icmp_timeout_nlattr_to_obj, + .obj_to_nlattr = icmp_timeout_obj_to_nlattr, + .nlattr_max = CTA_TIMEOUT_ICMP_MAX, + .obj_size = sizeof(unsigned int), + .nla_policy = icmp_timeout_nla_policy, + }, +#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ #ifdef CONFIG_SYSCTL .ctl_table_header = &icmp_sysctl_header, .ctl_table = icmp_sysctl_table, diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index 2eb9751eb7a8..92cc9f2931ae 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -276,6 +276,44 @@ static int icmpv6_nlattr_tuple_size(void) } #endif +#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) + +#include +#include + +static int icmpv6_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +{ + unsigned int *timeout = data; + + if (tb[CTA_TIMEOUT_ICMPV6_TIMEOUT]) { + *timeout = + ntohl(nla_get_be32(tb[CTA_TIMEOUT_ICMPV6_TIMEOUT])) * HZ; + } else { + /* Set default ICMPv6 timeout. */ + *timeout = nf_ct_icmpv6_timeout; + } + return 0; +} + +static int +icmpv6_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data) +{ + const unsigned int *timeout = data; + + NLA_PUT_BE32(skb, CTA_TIMEOUT_ICMPV6_TIMEOUT, htonl(*timeout / HZ)); + + return 0; + +nla_put_failure: + return -ENOSPC; +} + +static const struct nla_policy +icmpv6_timeout_nla_policy[CTA_TIMEOUT_ICMPV6_MAX+1] = { + [CTA_TIMEOUT_ICMPV6_TIMEOUT] = { .type = NLA_U32 }, +}; +#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ + #ifdef CONFIG_SYSCTL static struct ctl_table_header *icmpv6_sysctl_header; static struct ctl_table icmpv6_sysctl_table[] = { @@ -308,6 +346,15 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 __read_mostly = .nlattr_to_tuple = icmpv6_nlattr_to_tuple, .nla_policy = icmpv6_nla_policy, #endif +#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) + .ctnl_timeout = { + .nlattr_to_obj = icmpv6_timeout_nlattr_to_obj, + .obj_to_nlattr = icmpv6_timeout_obj_to_nlattr, + .nlattr_max = CTA_TIMEOUT_ICMP_MAX, + .obj_size = sizeof(unsigned int), + .nla_policy = icmpv6_timeout_nla_policy, + }, +#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ #ifdef CONFIG_SYSCTL .ctl_table_header = &icmpv6_sysctl_header, .ctl_table = icmpv6_sysctl_table, diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index b895d8b13215..f3efb6570dd9 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -314,6 +314,17 @@ config NF_CT_NETLINK help This option enables support for a netlink-based userspace interface +config NF_CT_NETLINK_TIMEOUT + tristate 'Connection tracking timeout tuning via Netlink' + select NETFILTER_NETLINK + depends on NETFILTER_ADVANCED + help + This option enables support for connection tracking timeout + fine-grain tuning. This allows you to attach specific timeout + policies to flows, instead of using the global timeout policy. + + If unsure, say `N'. + endif # NF_CONNTRACK # transparent proxy support diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index a28c2a6563f8..cf7cdd630bdd 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_NF_CT_PROTO_UDPLITE) += nf_conntrack_proto_udplite.o # netlink interface for nf_conntrack obj-$(CONFIG_NF_CT_NETLINK) += nf_conntrack_netlink.o +obj-$(CONFIG_NF_CT_NETLINK_TIMEOUT) += nfnetlink_cttimeout.o # connection tracking helpers nf_conntrack_h323-objs := nf_conntrack_h323_main.o nf_conntrack_h323_asn1.o diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index 8ea33598a0a7..24fdce256cb0 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -706,8 +706,60 @@ static int dccp_nlattr_size(void) return nla_total_size(0) /* CTA_PROTOINFO_DCCP */ + nla_policy_len(dccp_nla_policy, CTA_PROTOINFO_DCCP_MAX + 1); } + #endif +#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) + +#include +#include + +static int dccp_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +{ + struct dccp_net *dn = dccp_pernet(&init_net); + unsigned int *timeouts = data; + int i; + + /* set default DCCP timeouts. */ + for (i=0; idccp_timeout[i]; + + /* there's a 1:1 mapping between attributes and protocol states. */ + for (i=CTA_TIMEOUT_DCCP_UNSPEC+1; i +#include + +static int generic_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +{ + unsigned int *timeout = data; + + if (tb[CTA_TIMEOUT_GENERIC_TIMEOUT]) + *timeout = + ntohl(nla_get_be32(tb[CTA_TIMEOUT_GENERIC_TIMEOUT])) * HZ; + else { + /* Set default generic timeout. */ + *timeout = nf_ct_generic_timeout; + } + + return 0; +} + +static int +generic_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data) +{ + const unsigned int *timeout = data; + + NLA_PUT_BE32(skb, CTA_TIMEOUT_GENERIC_TIMEOUT, htonl(*timeout / HZ)); + + return 0; + +nla_put_failure: + return -ENOSPC; +} + +static const struct nla_policy +generic_timeout_nla_policy[CTA_TIMEOUT_GENERIC_MAX+1] = { + [CTA_TIMEOUT_GENERIC_TIMEOUT] = { .type = NLA_U32 }, +}; +#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ + #ifdef CONFIG_SYSCTL static struct ctl_table_header *generic_sysctl_header; static struct ctl_table generic_sysctl_table[] = { @@ -102,6 +141,15 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_generic __read_mostly = .packet = generic_packet, .get_timeouts = generic_get_timeouts, .new = generic_new, +#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) + .ctnl_timeout = { + .nlattr_to_obj = generic_timeout_nlattr_to_obj, + .obj_to_nlattr = generic_timeout_obj_to_nlattr, + .nlattr_max = CTA_TIMEOUT_GENERIC_MAX, + .obj_size = sizeof(unsigned int), + .nla_policy = generic_timeout_nla_policy, + }, +#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ #ifdef CONFIG_SYSCTL .ctl_table_header = &generic_sysctl_header, .ctl_table = generic_sysctl_table, diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index 1bf01c95658b..659648c4b14a 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -292,6 +292,52 @@ static void gre_destroy(struct nf_conn *ct) nf_ct_gre_keymap_destroy(master); } +#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) + +#include +#include + +static int gre_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +{ + unsigned int *timeouts = data; + + /* set default timeouts for GRE. */ + timeouts[GRE_CT_UNREPLIED] = gre_timeouts[GRE_CT_UNREPLIED]; + timeouts[GRE_CT_REPLIED] = gre_timeouts[GRE_CT_REPLIED]; + + if (tb[CTA_TIMEOUT_GRE_UNREPLIED]) { + timeouts[GRE_CT_UNREPLIED] = + ntohl(nla_get_be32(tb[CTA_TIMEOUT_GRE_UNREPLIED])) * HZ; + } + if (tb[CTA_TIMEOUT_GRE_REPLIED]) { + timeouts[GRE_CT_REPLIED] = + ntohl(nla_get_be32(tb[CTA_TIMEOUT_GRE_REPLIED])) * HZ; + } + return 0; +} + +static int +gre_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data) +{ + const unsigned int *timeouts = data; + + NLA_PUT_BE32(skb, CTA_TIMEOUT_GRE_UNREPLIED, + htonl(timeouts[GRE_CT_UNREPLIED] / HZ)); + NLA_PUT_BE32(skb, CTA_TIMEOUT_GRE_REPLIED, + htonl(timeouts[GRE_CT_REPLIED] / HZ)); + return 0; + +nla_put_failure: + return -ENOSPC; +} + +static const struct nla_policy +gre_timeout_nla_policy[CTA_TIMEOUT_GRE_MAX+1] = { + [CTA_TIMEOUT_GRE_UNREPLIED] = { .type = NLA_U32 }, + [CTA_TIMEOUT_GRE_REPLIED] = { .type = NLA_U32 }, +}; +#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ + /* protocol helper struct */ static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = { .l3proto = AF_INET, @@ -312,6 +358,15 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = { .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, .nla_policy = nf_ct_port_nla_policy, #endif +#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) + .ctnl_timeout = { + .nlattr_to_obj = gre_timeout_nlattr_to_obj, + .obj_to_nlattr = gre_timeout_obj_to_nlattr, + .nlattr_max = CTA_TIMEOUT_GRE_MAX, + .obj_size = sizeof(unsigned int) * GRE_CT_MAX, + .nla_policy = gre_timeout_nla_policy, + }, +#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ }; static int proto_gre_net_init(struct net *net) diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 2a0703371e24..72b5088592dc 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -549,6 +549,57 @@ static int sctp_nlattr_size(void) } #endif +#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) + +#include +#include + +static int sctp_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +{ + unsigned int *timeouts = data; + int i; + + /* set default SCTP timeouts. */ + for (i=0; i +#include + +static int tcp_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +{ + unsigned int *timeouts = data; + int i; + + /* set default TCP timeouts. */ + for (i=0; i +#include + +static int udp_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +{ + unsigned int *timeouts = data; + + /* set default timeouts for UDP. */ + timeouts[UDP_CT_UNREPLIED] = udp_timeouts[UDP_CT_UNREPLIED]; + timeouts[UDP_CT_REPLIED] = udp_timeouts[UDP_CT_REPLIED]; + + if (tb[CTA_TIMEOUT_UDP_UNREPLIED]) { + timeouts[UDP_CT_UNREPLIED] = + ntohl(nla_get_be32(tb[CTA_TIMEOUT_UDP_UNREPLIED])) * HZ; + } + if (tb[CTA_TIMEOUT_UDP_REPLIED]) { + timeouts[UDP_CT_REPLIED] = + ntohl(nla_get_be32(tb[CTA_TIMEOUT_UDP_REPLIED])) * HZ; + } + return 0; +} + +static int +udp_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data) +{ + const unsigned int *timeouts = data; + + NLA_PUT_BE32(skb, CTA_TIMEOUT_UDP_UNREPLIED, + htonl(timeouts[UDP_CT_UNREPLIED] / HZ)); + NLA_PUT_BE32(skb, CTA_TIMEOUT_UDP_REPLIED, + htonl(timeouts[UDP_CT_REPLIED] / HZ)); + return 0; + +nla_put_failure: + return -ENOSPC; +} + +static const struct nla_policy +udp_timeout_nla_policy[CTA_TIMEOUT_UDP_MAX+1] = { + [CTA_TIMEOUT_UDP_UNREPLIED] = { .type = NLA_U32 }, + [CTA_TIMEOUT_UDP_REPLIED] = { .type = NLA_U32 }, +}; +#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ + #ifdef CONFIG_SYSCTL static unsigned int udp_sysctl_table_users; static struct ctl_table_header *udp_sysctl_header; @@ -211,6 +257,15 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 __read_mostly = .nlattr_tuple_size = nf_ct_port_nlattr_tuple_size, .nla_policy = nf_ct_port_nla_policy, #endif +#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) + .ctnl_timeout = { + .nlattr_to_obj = udp_timeout_nlattr_to_obj, + .obj_to_nlattr = udp_timeout_obj_to_nlattr, + .nlattr_max = CTA_TIMEOUT_UDP_MAX, + .obj_size = sizeof(unsigned int) * CTA_TIMEOUT_UDP_MAX, + .nla_policy = udp_timeout_nla_policy, + }, +#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ #ifdef CONFIG_SYSCTL .ctl_table_users = &udp_sysctl_table_users, .ctl_table_header = &udp_sysctl_header, @@ -240,6 +295,15 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6 __read_mostly = .nlattr_tuple_size = nf_ct_port_nlattr_tuple_size, .nla_policy = nf_ct_port_nla_policy, #endif +#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) + .ctnl_timeout = { + .nlattr_to_obj = udp_timeout_nlattr_to_obj, + .obj_to_nlattr = udp_timeout_obj_to_nlattr, + .nlattr_max = CTA_TIMEOUT_UDP_MAX, + .obj_size = sizeof(unsigned int) * CTA_TIMEOUT_UDP_MAX, + .nla_policy = udp_timeout_nla_policy, + }, +#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ #ifdef CONFIG_SYSCTL .ctl_table_users = &udp_sysctl_table_users, .ctl_table_header = &udp_sysctl_header, diff --git a/net/netfilter/nf_conntrack_proto_udplite.c b/net/netfilter/nf_conntrack_proto_udplite.c index 0b32ccb1d515..e0606392cda0 100644 --- a/net/netfilter/nf_conntrack_proto_udplite.c +++ b/net/netfilter/nf_conntrack_proto_udplite.c @@ -156,6 +156,52 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl, return NF_ACCEPT; } +#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) + +#include +#include + +static int udplite_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +{ + unsigned int *timeouts = data; + + /* set default timeouts for UDPlite. */ + timeouts[UDPLITE_CT_UNREPLIED] = udplite_timeouts[UDPLITE_CT_UNREPLIED]; + timeouts[UDPLITE_CT_REPLIED] = udplite_timeouts[UDPLITE_CT_REPLIED]; + + if (tb[CTA_TIMEOUT_UDPLITE_UNREPLIED]) { + timeouts[UDPLITE_CT_UNREPLIED] = + ntohl(nla_get_be32(tb[CTA_TIMEOUT_UDPLITE_UNREPLIED])) * HZ; + } + if (tb[CTA_TIMEOUT_UDPLITE_REPLIED]) { + timeouts[UDPLITE_CT_REPLIED] = + ntohl(nla_get_be32(tb[CTA_TIMEOUT_UDPLITE_REPLIED])) * HZ; + } + return 0; +} + +static int +udplite_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data) +{ + const unsigned int *timeouts = data; + + NLA_PUT_BE32(skb, CTA_TIMEOUT_UDPLITE_UNREPLIED, + htonl(timeouts[UDPLITE_CT_UNREPLIED] / HZ)); + NLA_PUT_BE32(skb, CTA_TIMEOUT_UDPLITE_REPLIED, + htonl(timeouts[UDPLITE_CT_REPLIED] / HZ)); + return 0; + +nla_put_failure: + return -ENOSPC; +} + +static const struct nla_policy +udplite_timeout_nla_policy[CTA_TIMEOUT_UDPLITE_MAX+1] = { + [CTA_TIMEOUT_UDPLITE_UNREPLIED] = { .type = NLA_U32 }, + [CTA_TIMEOUT_UDPLITE_REPLIED] = { .type = NLA_U32 }, +}; +#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ + #ifdef CONFIG_SYSCTL static unsigned int udplite_sysctl_table_users; static struct ctl_table_header *udplite_sysctl_header; @@ -196,6 +242,16 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4 __read_mostly = .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, .nla_policy = nf_ct_port_nla_policy, #endif +#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) + .ctnl_timeout = { + .nlattr_to_obj = udplite_timeout_nlattr_to_obj, + .obj_to_nlattr = udplite_timeout_obj_to_nlattr, + .nlattr_max = CTA_TIMEOUT_UDPLITE_MAX, + .obj_size = sizeof(unsigned int) * + CTA_TIMEOUT_UDPLITE_MAX, + .nla_policy = udplite_timeout_nla_policy, + }, +#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ #ifdef CONFIG_SYSCTL .ctl_table_users = &udplite_sysctl_table_users, .ctl_table_header = &udplite_sysctl_header, @@ -221,6 +277,16 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6 __read_mostly = .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, .nla_policy = nf_ct_port_nla_policy, #endif +#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) + .ctnl_timeout = { + .nlattr_to_obj = udplite_timeout_nlattr_to_obj, + .obj_to_nlattr = udplite_timeout_obj_to_nlattr, + .nlattr_max = CTA_TIMEOUT_UDPLITE_MAX, + .obj_size = sizeof(unsigned int) * + CTA_TIMEOUT_UDPLITE_MAX, + .nla_policy = udplite_timeout_nla_policy, + }, +#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ #ifdef CONFIG_SYSCTL .ctl_table_users = &udplite_sysctl_table_users, .ctl_table_header = &udplite_sysctl_header, diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c new file mode 100644 index 000000000000..b860d5217189 --- /dev/null +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -0,0 +1,398 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * (C) 2012 by Vyatta Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation (or any later at your option). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pablo Neira Ayuso "); +MODULE_DESCRIPTION("cttimeout: Extended Netfilter Connection Tracking timeout tuning"); + +struct ctnl_timeout { + struct list_head head; + struct rcu_head rcu_head; + atomic_t refcnt; + char name[CTNL_TIMEOUT_NAME_MAX]; + __u16 l3num; + __u8 l4num; + char data[0]; +}; + +static LIST_HEAD(cttimeout_list); + +static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = { + [CTA_TIMEOUT_NAME] = { .type = NLA_NUL_STRING }, + [CTA_TIMEOUT_L3PROTO] = { .type = NLA_U16 }, + [CTA_TIMEOUT_L4PROTO] = { .type = NLA_U8 }, + [CTA_TIMEOUT_DATA] = { .type = NLA_NESTED }, +}; + +static int +ctnl_timeout_parse_policy(struct ctnl_timeout *timeout, + struct nf_conntrack_l4proto *l4proto, + const struct nlattr *attr) +{ + int ret = 0; + + if (likely(l4proto->ctnl_timeout.nlattr_to_obj)) { + struct nlattr *tb[l4proto->ctnl_timeout.nlattr_max+1]; + + nla_parse_nested(tb, l4proto->ctnl_timeout.nlattr_max, + attr, l4proto->ctnl_timeout.nla_policy); + + ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, &timeout->data); + } + return ret; +} + +static int +cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]) +{ + __u16 l3num; + __u8 l4num; + struct nf_conntrack_l4proto *l4proto; + struct ctnl_timeout *timeout, *matching = NULL; + char *name; + int ret; + + if (!cda[CTA_TIMEOUT_NAME] || + !cda[CTA_TIMEOUT_L3PROTO] || + !cda[CTA_TIMEOUT_L4PROTO] || + !cda[CTA_TIMEOUT_DATA]) + return -EINVAL; + + name = nla_data(cda[CTA_TIMEOUT_NAME]); + l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO])); + l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]); + + list_for_each_entry(timeout, &cttimeout_list, head) { + if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0) + continue; + + if (nlh->nlmsg_flags & NLM_F_EXCL) + return -EEXIST; + + matching = timeout; + break; + } + + l4proto = __nf_ct_l4proto_find(l3num, l4num); + + /* This protocol is not supportted, skip. */ + if (l4proto->l4proto != l4num) + return -EOPNOTSUPP; + + if (matching) { + if (nlh->nlmsg_flags & NLM_F_REPLACE) { + /* You cannot replace one timeout policy by another of + * different kind, sorry. + */ + if (matching->l3num != l3num || + matching->l4num != l4num) + return -EINVAL; + + ret = ctnl_timeout_parse_policy(matching, l4proto, + cda[CTA_TIMEOUT_DATA]); + return ret; + } + return -EBUSY; + } + + timeout = kzalloc(sizeof(struct ctnl_timeout) + + l4proto->ctnl_timeout.obj_size, GFP_KERNEL); + if (timeout == NULL) + return -ENOMEM; + + ret = ctnl_timeout_parse_policy(timeout, l4proto, + cda[CTA_TIMEOUT_DATA]); + if (ret < 0) + goto err; + + strcpy(timeout->name, nla_data(cda[CTA_TIMEOUT_NAME])); + timeout->l3num = l3num; + timeout->l4num = l4num; + atomic_set(&timeout->refcnt, 1); + list_add_tail_rcu(&timeout->head, &cttimeout_list); + + return 0; +err: + kfree(timeout); + return ret; +} + +static int +ctnl_timeout_fill_info(struct sk_buff *skb, u32 pid, u32 seq, u32 type, + int event, struct ctnl_timeout *timeout) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfmsg; + unsigned int flags = pid ? NLM_F_MULTI : 0; + struct nf_conntrack_l4proto *l4proto; + + event |= NFNL_SUBSYS_CTNETLINK_TIMEOUT << 8; + nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags); + if (nlh == NULL) + goto nlmsg_failure; + + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = AF_UNSPEC; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = 0; + + NLA_PUT_STRING(skb, CTA_TIMEOUT_NAME, timeout->name); + NLA_PUT_BE16(skb, CTA_TIMEOUT_L3PROTO, htons(timeout->l3num)); + NLA_PUT_U8(skb, CTA_TIMEOUT_L4PROTO, timeout->l4num); + NLA_PUT_BE32(skb, CTA_TIMEOUT_USE, + htonl(atomic_read(&timeout->refcnt))); + + l4proto = __nf_ct_l4proto_find(timeout->l3num, timeout->l4num); + + /* If the timeout object does not match the layer 4 protocol tracker, + * then skip dumping the data part since we don't know how to + * interpret it. This may happen for UPDlite, SCTP and DCCP since + * you can unload the module. + */ + if (timeout->l4num != l4proto->l4proto) + goto out; + + if (likely(l4proto->ctnl_timeout.obj_to_nlattr)) { + struct nlattr *nest_parms; + int ret; + + nest_parms = nla_nest_start(skb, + CTA_TIMEOUT_DATA | NLA_F_NESTED); + if (!nest_parms) + goto nla_put_failure; + + ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, &timeout->data); + if (ret < 0) + goto nla_put_failure; + + nla_nest_end(skb, nest_parms); + } +out: + nlmsg_end(skb, nlh); + return skb->len; + +nlmsg_failure: +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -1; +} + +static int +ctnl_timeout_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct ctnl_timeout *cur, *last; + + if (cb->args[2]) + return 0; + + last = (struct ctnl_timeout *)cb->args[1]; + if (cb->args[1]) + cb->args[1] = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(cur, &cttimeout_list, head) { + if (last && cur != last) + continue; + + if (ctnl_timeout_fill_info(skb, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, + NFNL_MSG_TYPE(cb->nlh->nlmsg_type), + IPCTNL_MSG_TIMEOUT_NEW, cur) < 0) { + cb->args[1] = (unsigned long)cur; + break; + } + } + if (!cb->args[1]) + cb->args[2] = 1; + rcu_read_unlock(); + return skb->len; +} + +static int +cttimeout_get_timeout(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]) +{ + int ret = -ENOENT; + char *name; + struct ctnl_timeout *cur; + + if (nlh->nlmsg_flags & NLM_F_DUMP) { + struct netlink_dump_control c = { + .dump = ctnl_timeout_dump, + }; + return netlink_dump_start(ctnl, skb, nlh, &c); + } + + if (!cda[CTA_TIMEOUT_NAME]) + return -EINVAL; + name = nla_data(cda[CTA_TIMEOUT_NAME]); + + list_for_each_entry(cur, &cttimeout_list, head) { + struct sk_buff *skb2; + + if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0) + continue; + + skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (skb2 == NULL) { + ret = -ENOMEM; + break; + } + + ret = ctnl_timeout_fill_info(skb2, NETLINK_CB(skb).pid, + nlh->nlmsg_seq, + NFNL_MSG_TYPE(nlh->nlmsg_type), + IPCTNL_MSG_TIMEOUT_NEW, cur); + if (ret <= 0) { + kfree_skb(skb2); + break; + } + ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, + MSG_DONTWAIT); + if (ret > 0) + ret = 0; + + /* this avoids a loop in nfnetlink. */ + return ret == -EAGAIN ? -ENOBUFS : ret; + } + return ret; +} + +/* try to delete object, fail if it is still in use. */ +static int ctnl_timeout_try_del(struct ctnl_timeout *timeout) +{ + int ret = 0; + + /* we want to avoid races with nf_ct_timeout_find_get. */ + if (atomic_dec_and_test(&timeout->refcnt)) { + /* We are protected by nfnl mutex. */ + list_del_rcu(&timeout->head); + kfree_rcu(timeout, rcu_head); + } else { + /* still in use, restore reference counter. */ + atomic_inc(&timeout->refcnt); + ret = -EBUSY; + } + return ret; +} + +static int +cttimeout_del_timeout(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]) +{ + char *name; + struct ctnl_timeout *cur; + int ret = -ENOENT; + + if (!cda[CTA_TIMEOUT_NAME]) { + list_for_each_entry(cur, &cttimeout_list, head) + ctnl_timeout_try_del(cur); + + return 0; + } + name = nla_data(cda[CTA_TIMEOUT_NAME]); + + list_for_each_entry(cur, &cttimeout_list, head) { + if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0) + continue; + + ret = ctnl_timeout_try_del(cur); + if (ret < 0) + return ret; + + break; + } + return ret; +} + +static const struct nfnl_callback cttimeout_cb[IPCTNL_MSG_TIMEOUT_MAX] = { + [IPCTNL_MSG_TIMEOUT_NEW] = { .call = cttimeout_new_timeout, + .attr_count = CTA_TIMEOUT_MAX, + .policy = cttimeout_nla_policy }, + [IPCTNL_MSG_TIMEOUT_GET] = { .call = cttimeout_get_timeout, + .attr_count = CTA_TIMEOUT_MAX, + .policy = cttimeout_nla_policy }, + [IPCTNL_MSG_TIMEOUT_DELETE] = { .call = cttimeout_del_timeout, + .attr_count = CTA_TIMEOUT_MAX, + .policy = cttimeout_nla_policy }, +}; + +static const struct nfnetlink_subsystem cttimeout_subsys = { + .name = "conntrack_timeout", + .subsys_id = NFNL_SUBSYS_CTNETLINK_TIMEOUT, + .cb_count = IPCTNL_MSG_TIMEOUT_MAX, + .cb = cttimeout_cb, +}; + +MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_TIMEOUT); + +static int __init cttimeout_init(void) +{ + int ret; + + ret = nfnetlink_subsys_register(&cttimeout_subsys); + if (ret < 0) { + pr_err("cttimeout_init: cannot register cttimeout with " + "nfnetlink.\n"); + goto err_out; + } + return 0; + +err_out: + return ret; +} + +static void __exit cttimeout_exit(void) +{ + struct ctnl_timeout *cur, *tmp; + + pr_info("cttimeout: unregistering from nfnetlink.\n"); + + nfnetlink_subsys_unregister(&cttimeout_subsys); + list_for_each_entry_safe(cur, tmp, &cttimeout_list, head) { + list_del_rcu(&cur->head); + /* We are sure that our objects have no clients at this point, + * it's safe to release them all without checking refcnt. + */ + kfree_rcu(cur, rcu_head); + } +} + +module_init(cttimeout_init); +module_exit(cttimeout_exit); -- cgit From dd705072412225a97784fe38feee2ebf8d14814d Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 28 Feb 2012 23:36:48 +0100 Subject: netfilter: nf_ct_ext: add timeout extension This patch adds the timeout extension, which allows you to attach specific timeout policies to flows. This extension is only used by the template conntrack. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_extend.h | 4 ++ include/net/netfilter/nf_conntrack_timeout.h | 78 ++++++++++++++++++++++++++++ net/netfilter/Kconfig | 10 ++++ net/netfilter/Makefile | 1 + net/netfilter/nf_conntrack_core.c | 7 +++ net/netfilter/nf_conntrack_timeout.c | 60 +++++++++++++++++++++ net/netfilter/nfnetlink_cttimeout.c | 10 ---- 7 files changed, 160 insertions(+), 10 deletions(-) create mode 100644 include/net/netfilter/nf_conntrack_timeout.h create mode 100644 net/netfilter/nf_conntrack_timeout.c (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 2dcf31703acb..96755c3798a5 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -19,6 +19,9 @@ enum nf_ct_ext_id { #endif #ifdef CONFIG_NF_CONNTRACK_TIMESTAMP NF_CT_EXT_TSTAMP, +#endif +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT + NF_CT_EXT_TIMEOUT, #endif NF_CT_EXT_NUM, }; @@ -29,6 +32,7 @@ enum nf_ct_ext_id { #define NF_CT_EXT_ECACHE_TYPE struct nf_conntrack_ecache #define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone #define NF_CT_EXT_TSTAMP_TYPE struct nf_conn_tstamp +#define NF_CT_EXT_TIMEOUT_TYPE struct nf_conn_timeout /* Extensions: optional stuff which isn't permanently in struct. */ struct nf_ct_ext { diff --git a/include/net/netfilter/nf_conntrack_timeout.h b/include/net/netfilter/nf_conntrack_timeout.h new file mode 100644 index 000000000000..0e04db4a0865 --- /dev/null +++ b/include/net/netfilter/nf_conntrack_timeout.h @@ -0,0 +1,78 @@ +#ifndef _NF_CONNTRACK_TIMEOUT_H +#define _NF_CONNTRACK_TIMEOUT_H + +#include +#include +#include +#include +#include + +#define CTNL_TIMEOUT_NAME_MAX 32 + +struct ctnl_timeout { + struct list_head head; + struct rcu_head rcu_head; + atomic_t refcnt; + char name[CTNL_TIMEOUT_NAME_MAX]; + __u16 l3num; + __u8 l4num; + char data[0]; +}; + +struct nf_conn_timeout { + struct ctnl_timeout *timeout; +}; + +#define NF_CT_TIMEOUT_EXT_DATA(__t) (unsigned int *) &((__t)->timeout->data) + +static inline +struct nf_conn_timeout *nf_ct_timeout_find(const struct nf_conn *ct) +{ +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT + return nf_ct_ext_find(ct, NF_CT_EXT_TIMEOUT); +#else + return NULL; +#endif +} + +static inline +struct nf_conn_timeout *nf_ct_timeout_ext_add(struct nf_conn *ct, + struct ctnl_timeout *timeout, + gfp_t gfp) +{ +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT + struct nf_conn_timeout *timeout_ext; + + timeout_ext = nf_ct_ext_add(ct, NF_CT_EXT_TIMEOUT, gfp); + if (timeout_ext == NULL) + return NULL; + + timeout_ext->timeout = timeout; + + return timeout_ext; +#else + return NULL; +#endif +}; + +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT +extern int nf_conntrack_timeout_init(struct net *net); +extern void nf_conntrack_timeout_fini(struct net *net); +#else +static inline int nf_conntrack_timeout_init(struct net *net) +{ + return 0; +} + +static inline void nf_conntrack_timeout_fini(struct net *net) +{ + return; +} +#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ + +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT +extern struct ctnl_timeout *(*nf_ct_timeout_find_get_hook)(const char *name); +extern void (*nf_ct_timeout_put_hook)(struct ctnl_timeout *timeout); +#endif + +#endif /* _NF_CONNTRACK_TIMEOUT_H */ diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index f3efb6570dd9..0c6f67e8f2e5 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -103,6 +103,16 @@ config NF_CONNTRACK_EVENTS If unsure, say `N'. +config NF_CONNTRACK_TIMEOUT + bool 'Connection tracking timeout' + depends on NETFILTER_ADVANCED + help + This option enables support for connection tracking timeout + extension. This allows you to attach timeout policies to flow + via the CT target. + + If unsure, say `N'. + config NF_CONNTRACK_TIMESTAMP bool 'Connection tracking timestamping' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index cf7cdd630bdd..ca3676586f51 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -1,6 +1,7 @@ netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o nf_conntrack_acct.o +nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMEOUT) += nf_conntrack_timeout.o nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index d18995eea1c6..75398c535719 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -1333,6 +1334,7 @@ static void nf_conntrack_cleanup_net(struct net *net) } nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); + nf_conntrack_timeout_fini(net); nf_conntrack_ecache_fini(net); nf_conntrack_tstamp_fini(net); nf_conntrack_acct_fini(net); @@ -1564,9 +1566,14 @@ static int nf_conntrack_init_net(struct net *net) ret = nf_conntrack_ecache_init(net); if (ret < 0) goto err_ecache; + ret = nf_conntrack_timeout_init(net); + if (ret < 0) + goto err_timeout; return 0; +err_timeout: + nf_conntrack_timeout_fini(net); err_ecache: nf_conntrack_tstamp_fini(net); err_tstamp: diff --git a/net/netfilter/nf_conntrack_timeout.c b/net/netfilter/nf_conntrack_timeout.c new file mode 100644 index 000000000000..a878ce5b252c --- /dev/null +++ b/net/netfilter/nf_conntrack_timeout.c @@ -0,0 +1,60 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * (C) 2012 by Vyatta Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation (or any later at your option). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct ctnl_timeout * +(*nf_ct_timeout_find_get_hook)(const char *name) __read_mostly; +EXPORT_SYMBOL_GPL(nf_ct_timeout_find_get_hook); + +void (*nf_ct_timeout_put_hook)(struct ctnl_timeout *timeout) __read_mostly; +EXPORT_SYMBOL_GPL(nf_ct_timeout_put_hook); + +static struct nf_ct_ext_type timeout_extend __read_mostly = { + .len = sizeof(struct nf_conn_timeout), + .align = __alignof__(struct nf_conn_timeout), + .id = NF_CT_EXT_TIMEOUT, +}; + +int nf_conntrack_timeout_init(struct net *net) +{ + int ret = 0; + + if (net_eq(net, &init_net)) { + ret = nf_ct_extend_register(&timeout_extend); + if (ret < 0) { + printk(KERN_ERR "nf_ct_timeout: Unable to register " + "timeout extension.\n"); + return ret; + } + } + + return 0; +} + +void nf_conntrack_timeout_fini(struct net *net) +{ + if (net_eq(net, &init_net)) + nf_ct_extend_unregister(&timeout_extend); +} diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index b860d5217189..29b989715162 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -37,16 +37,6 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Pablo Neira Ayuso "); MODULE_DESCRIPTION("cttimeout: Extended Netfilter Connection Tracking timeout tuning"); -struct ctnl_timeout { - struct list_head head; - struct rcu_head rcu_head; - atomic_t refcnt; - char name[CTNL_TIMEOUT_NAME_MAX]; - __u16 l3num; - __u8 l4num; - char data[0]; -}; - static LIST_HEAD(cttimeout_list); static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = { -- cgit From 24de58f465165298aaa8f286b2592f0163706cfe Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 29 Feb 2012 02:19:19 +0100 Subject: netfilter: xt_CT: allow to attach timeout policy + glue code This patch allows you to attach the timeout policy via the CT target, it adds a new revision of the target to ensure backward compatibility. Moreover, it also contains the glue code to stick the timeout object defined via nfnetlink_cttimeout to the given flow. Example usage (it requires installing the nfct tool and libnetfilter_cttimeout): 1) create the timeout policy: nfct timeout add tcp-policy0 inet tcp \ established 1000 close 10 time_wait 10 last_ack 10 2) attach the timeout policy to the packet: iptables -I PREROUTING -t raw -p tcp -j CT --timeout tcp-policy0 You have to install the following user-space software: a) libnetfilter_cttimeout: git://git.netfilter.org/libnetfilter_cttimeout b) nfct: git://git.netfilter.org/nfct You also have to get iptables with -j CT --timeout support. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/xt_CT.h | 12 ++ net/netfilter/nf_conntrack_core.c | 11 +- net/netfilter/nfnetlink_cttimeout.c | 41 +++++++ net/netfilter/xt_CT.c | 220 +++++++++++++++++++++++++++++++++--- 4 files changed, 268 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/xt_CT.h b/include/linux/netfilter/xt_CT.h index b56e76811c04..a064b8af360c 100644 --- a/include/linux/netfilter/xt_CT.h +++ b/include/linux/netfilter/xt_CT.h @@ -16,4 +16,16 @@ struct xt_ct_target_info { struct nf_conn *ct __attribute__((aligned(8))); }; +struct xt_ct_target_info_v1 { + __u16 flags; + __u16 zone; + __u32 ct_events; + __u32 exp_events; + char helper[16]; + char timeout[32]; + + /* Used internally by the kernel */ + struct nf_conn *ct __attribute__((aligned(8))); +}; + #endif /* _XT_CT_H */ diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 75398c535719..81e2aa4ca1fe 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -912,6 +912,7 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, enum ip_conntrack_info ctinfo; struct nf_conntrack_l3proto *l3proto; struct nf_conntrack_l4proto *l4proto; + struct nf_conn_timeout *timeout_ext; unsigned int *timeouts; unsigned int dataoff; u_int8_t protonum; @@ -959,7 +960,15 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, goto out; } - timeouts = l4proto->get_timeouts(net); + /* Decide what timeout policy we want to apply to this flow. */ + if (tmpl) { + timeout_ext = nf_ct_timeout_find(tmpl); + if (timeout_ext) + timeouts = NF_CT_TIMEOUT_EXT_DATA(timeout_ext); + else + timeouts = l4proto->get_timeouts(net); + } else + timeouts = l4proto->get_timeouts(net); ct = resolve_normal_ct(net, tmpl, skb, dataoff, pf, protonum, l3proto, l4proto, &set_reply, &ctinfo, diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index 29b989715162..fec29a43de4d 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -331,6 +332,38 @@ cttimeout_del_timeout(struct sock *ctnl, struct sk_buff *skb, return ret; } +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT +static struct ctnl_timeout *ctnl_timeout_find_get(const char *name) +{ + struct ctnl_timeout *timeout, *matching = NULL; + + rcu_read_lock(); + list_for_each_entry_rcu(timeout, &cttimeout_list, head) { + if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0) + continue; + + if (!try_module_get(THIS_MODULE)) + goto err; + + if (!atomic_inc_not_zero(&timeout->refcnt)) { + module_put(THIS_MODULE); + goto err; + } + matching = timeout; + break; + } +err: + rcu_read_unlock(); + return matching; +} + +static void ctnl_timeout_put(struct ctnl_timeout *timeout) +{ + atomic_dec(&timeout->refcnt); + module_put(THIS_MODULE); +} +#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ + static const struct nfnl_callback cttimeout_cb[IPCTNL_MSG_TIMEOUT_MAX] = { [IPCTNL_MSG_TIMEOUT_NEW] = { .call = cttimeout_new_timeout, .attr_count = CTA_TIMEOUT_MAX, @@ -362,6 +395,10 @@ static int __init cttimeout_init(void) "nfnetlink.\n"); goto err_out; } +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT + RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, ctnl_timeout_find_get); + RCU_INIT_POINTER(nf_ct_timeout_put_hook, ctnl_timeout_put); +#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ return 0; err_out: @@ -382,6 +419,10 @@ static void __exit cttimeout_exit(void) */ kfree_rcu(cur, rcu_head); } +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT + RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, NULL); + RCU_INIT_POINTER(nf_ct_timeout_put_hook, NULL); +#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ } module_init(cttimeout_init); diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c index 0221d10de75a..b873445df444 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -16,10 +16,11 @@ #include #include #include +#include #include -static unsigned int xt_ct_target(struct sk_buff *skb, - const struct xt_action_param *par) +static unsigned int xt_ct_target_v0(struct sk_buff *skb, + const struct xt_action_param *par) { const struct xt_ct_target_info *info = par->targinfo; struct nf_conn *ct = info->ct; @@ -35,6 +36,23 @@ static unsigned int xt_ct_target(struct sk_buff *skb, return XT_CONTINUE; } +static unsigned int xt_ct_target_v1(struct sk_buff *skb, + const struct xt_action_param *par) +{ + const struct xt_ct_target_info_v1 *info = par->targinfo; + struct nf_conn *ct = info->ct; + + /* Previously seen (loopback)? Ignore. */ + if (skb->nfct != NULL) + return XT_CONTINUE; + + atomic_inc(&ct->ct_general.use); + skb->nfct = &ct->ct_general; + skb->nfctinfo = IP_CT_NEW; + + return XT_CONTINUE; +} + static u8 xt_ct_find_proto(const struct xt_tgchk_param *par) { if (par->family == NFPROTO_IPV4) { @@ -53,7 +71,7 @@ static u8 xt_ct_find_proto(const struct xt_tgchk_param *par) return 0; } -static int xt_ct_tg_check(const struct xt_tgchk_param *par) +static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par) { struct xt_ct_target_info *info = par->targinfo; struct nf_conntrack_tuple t; @@ -130,7 +148,137 @@ err1: return ret; } -static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par) +static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par) +{ + struct xt_ct_target_info_v1 *info = par->targinfo; + struct nf_conntrack_tuple t; + struct nf_conn_help *help; + struct nf_conn *ct; + int ret = 0; + u8 proto; + + if (info->flags & ~XT_CT_NOTRACK) + return -EINVAL; + + if (info->flags & XT_CT_NOTRACK) { + ct = nf_ct_untracked_get(); + atomic_inc(&ct->ct_general.use); + goto out; + } + +#ifndef CONFIG_NF_CONNTRACK_ZONES + if (info->zone) + goto err1; +#endif + + ret = nf_ct_l3proto_try_module_get(par->family); + if (ret < 0) + goto err1; + + memset(&t, 0, sizeof(t)); + ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL); + ret = PTR_ERR(ct); + if (IS_ERR(ct)) + goto err2; + + ret = 0; + if ((info->ct_events || info->exp_events) && + !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events, + GFP_KERNEL)) + goto err3; + + if (info->helper[0]) { + ret = -ENOENT; + proto = xt_ct_find_proto(par); + if (!proto) { + pr_info("You must specify a L4 protocol, " + "and not use inversions on it.\n"); + goto err3; + } + + ret = -ENOMEM; + help = nf_ct_helper_ext_add(ct, GFP_KERNEL); + if (help == NULL) + goto err3; + + ret = -ENOENT; + help->helper = nf_conntrack_helper_try_module_get(info->helper, + par->family, + proto); + if (help->helper == NULL) { + pr_info("No such helper \"%s\"\n", info->helper); + goto err3; + } + } + +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT + if (info->timeout) { + typeof(nf_ct_timeout_find_get_hook) timeout_find_get; + struct ctnl_timeout *timeout; + struct nf_conn_timeout *timeout_ext; + + timeout_find_get = + rcu_dereference(nf_ct_timeout_find_get_hook); + + if (timeout_find_get) { + const struct ipt_entry *e = par->entryinfo; + + if (e->ip.invflags & IPT_INV_PROTO) { + ret = -EINVAL; + pr_info("You cannot use inversion on " + "L4 protocol\n"); + goto err3; + } + timeout = timeout_find_get(info->timeout); + if (timeout == NULL) { + ret = -ENOENT; + pr_info("No such timeout policy \"%s\"\n", + info->timeout); + goto err3; + } + if (timeout->l3num != par->family) { + ret = -EINVAL; + pr_info("Timeout policy `%s' can only be " + "used by L3 protocol number %d\n", + info->timeout, timeout->l3num); + goto err3; + } + if (timeout->l4num != e->ip.proto) { + ret = -EINVAL; + pr_info("Timeout policy `%s' can only be " + "used by L4 protocol number %d\n", + info->timeout, timeout->l4num); + goto err3; + } + timeout_ext = nf_ct_timeout_ext_add(ct, timeout, + GFP_KERNEL); + if (timeout_ext == NULL) { + ret = -ENOMEM; + goto err3; + } + } else { + ret = -ENOENT; + pr_info("Timeout policy base is empty\n"); + goto err3; + } + } +#endif + + __set_bit(IPS_TEMPLATE_BIT, &ct->status); + __set_bit(IPS_CONFIRMED_BIT, &ct->status); +out: + info->ct = ct; + return 0; + +err3: + nf_conntrack_free(ct); +err2: + nf_ct_l3proto_module_put(par->family); +err1: + return ret; +} + +static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par) { struct xt_ct_target_info *info = par->targinfo; struct nf_conn *ct = info->ct; @@ -146,25 +294,67 @@ static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par) nf_ct_put(info->ct); } -static struct xt_target xt_ct_tg __read_mostly = { - .name = "CT", - .family = NFPROTO_UNSPEC, - .targetsize = sizeof(struct xt_ct_target_info), - .checkentry = xt_ct_tg_check, - .destroy = xt_ct_tg_destroy, - .target = xt_ct_target, - .table = "raw", - .me = THIS_MODULE, +static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par) +{ + struct xt_ct_target_info_v1 *info = par->targinfo; + struct nf_conn *ct = info->ct; + struct nf_conn_help *help; +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT + struct nf_conn_timeout *timeout_ext; + typeof(nf_ct_timeout_put_hook) timeout_put; +#endif + if (!nf_ct_is_untracked(ct)) { + help = nfct_help(ct); + if (help) + module_put(help->helper->me); + + nf_ct_l3proto_module_put(par->family); + +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT + timeout_put = rcu_dereference(nf_ct_timeout_put_hook); + + if (timeout_put) { + timeout_ext = nf_ct_timeout_find(ct); + if (timeout_ext) + timeout_put(timeout_ext->timeout); + } +#endif + } + nf_ct_put(info->ct); +} + +static struct xt_target xt_ct_tg_reg[] __read_mostly = { + { + .name = "CT", + .family = NFPROTO_UNSPEC, + .targetsize = sizeof(struct xt_ct_target_info), + .checkentry = xt_ct_tg_check_v0, + .destroy = xt_ct_tg_destroy_v0, + .target = xt_ct_target_v0, + .table = "raw", + .me = THIS_MODULE, + }, + { + .name = "CT", + .family = NFPROTO_UNSPEC, + .revision = 1, + .targetsize = sizeof(struct xt_ct_target_info_v1), + .checkentry = xt_ct_tg_check_v1, + .destroy = xt_ct_tg_destroy_v1, + .target = xt_ct_target_v1, + .table = "raw", + .me = THIS_MODULE, + }, }; static int __init xt_ct_tg_init(void) { - return xt_register_target(&xt_ct_tg); + return xt_register_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg)); } static void __exit xt_ct_tg_exit(void) { - xt_unregister_target(&xt_ct_tg); + xt_unregister_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg)); } module_init(xt_ct_tg_init); -- cgit From ace30d73ef09fd5f95b24c5c1c5aa11963981494 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 1 Mar 2012 02:56:59 +0000 Subject: netfilter: xt_LOG: add __printf() to sb_add() Helps to find format mismatches at compile time Suggested-by: David Laight Signed-off-by: Eric Dumazet --- include/net/netfilter/xt_log.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/netfilter/xt_log.h b/include/net/netfilter/xt_log.h index 0dfb34a5b53c..7e1544e8f70d 100644 --- a/include/net/netfilter/xt_log.h +++ b/include/net/netfilter/xt_log.h @@ -6,7 +6,7 @@ struct sbuff { }; static struct sbuff emergency, *emergency_ptr = &emergency; -static int sb_add(struct sbuff *m, const char *f, ...) +static __printf(2, 3) int sb_add(struct sbuff *m, const char *f, ...) { va_list args; int len; -- cgit From 04124681f104c1980024ff249a34a77a249fd2bc Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 8 Mar 2012 01:25:00 -0300 Subject: Bluetooth: fix conding style issues all over the tree Signed-off-by: Gustavo F. Padovan --- drivers/bluetooth/btmrvl_debugfs.c | 23 +- include/net/bluetooth/hci_core.h | 67 +++-- net/bluetooth/hci_core.c | 49 ++-- net/bluetooth/hci_event.c | 110 ++++----- net/bluetooth/l2cap_core.c | 11 +- net/bluetooth/l2cap_sock.c | 3 +- net/bluetooth/mgmt.c | 489 ++++++++++++++++++------------------- net/bluetooth/smp.c | 30 +-- 8 files changed, 384 insertions(+), 398 deletions(-) (limited to 'include') diff --git a/drivers/bluetooth/btmrvl_debugfs.c b/drivers/bluetooth/btmrvl_debugfs.c index 3497347e6dbb..6c20bbb54b71 100644 --- a/drivers/bluetooth/btmrvl_debugfs.c +++ b/drivers/bluetooth/btmrvl_debugfs.c @@ -401,28 +401,29 @@ void btmrvl_debugfs_init(struct hci_dev *hdev) dbg->config_dir = debugfs_create_dir("config", hdev->debugfs); dbg->psmode = debugfs_create_file("psmode", 0644, dbg->config_dir, - priv, &btmrvl_psmode_fops); + priv, &btmrvl_psmode_fops); dbg->pscmd = debugfs_create_file("pscmd", 0644, dbg->config_dir, - priv, &btmrvl_pscmd_fops); + priv, &btmrvl_pscmd_fops); dbg->gpiogap = debugfs_create_file("gpiogap", 0644, dbg->config_dir, - priv, &btmrvl_gpiogap_fops); + priv, &btmrvl_gpiogap_fops); dbg->hsmode = debugfs_create_file("hsmode", 0644, dbg->config_dir, - priv, &btmrvl_hsmode_fops); + priv, &btmrvl_hsmode_fops); dbg->hscmd = debugfs_create_file("hscmd", 0644, dbg->config_dir, - priv, &btmrvl_hscmd_fops); + priv, &btmrvl_hscmd_fops); dbg->hscfgcmd = debugfs_create_file("hscfgcmd", 0644, dbg->config_dir, - priv, &btmrvl_hscfgcmd_fops); + priv, &btmrvl_hscfgcmd_fops); dbg->status_dir = debugfs_create_dir("status", hdev->debugfs); dbg->curpsmode = debugfs_create_file("curpsmode", 0444, - dbg->status_dir, priv, &btmrvl_curpsmode_fops); + dbg->status_dir, priv, + &btmrvl_curpsmode_fops); dbg->psstate = debugfs_create_file("psstate", 0444, dbg->status_dir, - priv, &btmrvl_psstate_fops); + priv, &btmrvl_psstate_fops); dbg->hsstate = debugfs_create_file("hsstate", 0444, dbg->status_dir, - priv, &btmrvl_hsstate_fops); + priv, &btmrvl_hsstate_fops); dbg->txdnldready = debugfs_create_file("txdnldready", 0444, - dbg->status_dir, priv, - &btmrvl_txdnldready_fops); + dbg->status_dir, priv, + &btmrvl_txdnldready_fops); } void btmrvl_debugfs_remove(struct hci_dev *hdev) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index cbbf68a8510d..daefaac51131 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -398,16 +398,16 @@ static inline long inquiry_entry_age(struct inquiry_entry *e) } struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, - bdaddr_t *bdaddr); + bdaddr_t *bdaddr); struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, - bdaddr_t *bdaddr); + bdaddr_t *bdaddr); struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev, - bdaddr_t *bdaddr, - int state); + bdaddr_t *bdaddr, + int state); void hci_inquiry_cache_update_resolve(struct hci_dev *hdev, - struct inquiry_entry *ie); + struct inquiry_entry *ie); bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, - bool name_known, bool *ssp); + bool name_known, bool *ssp); /* ----- HCI Connections ----- */ enum { @@ -669,13 +669,13 @@ int hci_uuids_clear(struct hci_dev *hdev); int hci_link_keys_clear(struct hci_dev *hdev); struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, - bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len); + bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len); struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]); int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, - int new_key, u8 authenticated, u8 tk[16], - u8 enc_size, u16 ediv, u8 rand[8]); + int new_key, u8 authenticated, u8 tk[16], u8 enc_size, u16 ediv, + u8 rand[8]); struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 addr_type); + u8 addr_type); int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_smp_ltks_clear(struct hci_dev *hdev); int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); @@ -931,7 +931,7 @@ static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type) } static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, - u8 data_len) + u8 data_len) { eir[eir_len++] = sizeof(type) + data_len; eir[eir_len++] = type; @@ -978,50 +978,49 @@ int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable); int mgmt_connectable(struct hci_dev *hdev, u8 connectable); int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, - u8 persistent); + u8 persistent); int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u32 flags, u8 *name, - u8 name_len, u8 *dev_class); + u8 addr_type, u32 flags, u8 *name, u8 name_len, + u8 *dev_class); int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type); + u8 link_type, u8 addr_type); int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status); + u8 link_type, u8 addr_type, u8 status); int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 status); + u8 addr_type, u8 status); int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure); int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status); + u8 status); int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status); + u8 status); int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, __le32 value, - u8 confirm_hint); + u8 link_type, u8 addr_type, __le32 value, + u8 confirm_hint); int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status); + u8 link_type, u8 addr_type, u8 status); int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status); + u8 link_type, u8 addr_type, u8 status); int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type); + u8 link_type, u8 addr_type); int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status); + u8 link_type, u8 addr_type, u8 status); int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status); + u8 link_type, u8 addr_type, u8 status); int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 status); + u8 addr_type, u8 status); int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status); int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status); int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, - u8 status); + u8 status); int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, - u8 *randomizer, u8 status); + u8 *randomizer, u8 status); int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status); int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 *dev_class, s8 rssi, - u8 cfm_name, u8 ssp, u8 *eir, - u16 eir_len); + u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, + u8 ssp, u8 *eir, u16 eir_len); int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, s8 rssi, u8 *name, u8 name_len); + u8 addr_type, s8 rssi, u8 *name, u8 name_len); int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status); int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status); int mgmt_discovering(struct hci_dev *hdev, u8 discovering); @@ -1071,6 +1070,6 @@ void hci_le_ltk_neg_reply(struct hci_conn *conn); int hci_do_inquiry(struct hci_dev *hdev, u8 length); int hci_cancel_inquiry(struct hci_dev *hdev); int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window, - int timeout); + int timeout); #endif /* __HCI_CORE_H */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 661d65fc487b..59ec99eb739b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -431,7 +431,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *b } struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, - bdaddr_t *bdaddr) + bdaddr_t *bdaddr) { struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *e; @@ -447,8 +447,8 @@ struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, } struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev, - bdaddr_t *bdaddr, - int state) + bdaddr_t *bdaddr, + int state) { struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *e; @@ -466,7 +466,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev, } void hci_inquiry_cache_update_resolve(struct hci_dev *hdev, - struct inquiry_entry *ie) + struct inquiry_entry *ie) { struct discovery_state *cache = &hdev->discovery; struct list_head *pos = &cache->resolve; @@ -485,7 +485,7 @@ void hci_inquiry_cache_update_resolve(struct hci_dev *hdev, } bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, - bool name_known, bool *ssp) + bool name_known, bool *ssp) { struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *ie; @@ -1264,7 +1264,7 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]) EXPORT_SYMBOL(hci_find_ltk); struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 addr_type) + u8 addr_type) { struct smp_ltk *k; @@ -1278,7 +1278,7 @@ struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, EXPORT_SYMBOL(hci_find_ltk_by_addr); int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, - bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len) + bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len) { struct link_key *key, *old_key; u8 old_key_type, persistent; @@ -1333,8 +1333,8 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, } int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, - int new_key, u8 authenticated, u8 tk[16], - u8 enc_size, u16 ediv, u8 rand[8]) + int new_key, u8 authenticated, u8 tk[16], u8 enc_size, u16 + ediv, u8 rand[8]) { struct smp_ltk *key, *old_key; @@ -1413,7 +1413,7 @@ static void hci_cmd_timer(unsigned long arg) } struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev, - bdaddr_t *bdaddr) + bdaddr_t *bdaddr) { struct oob_data *data; @@ -1453,7 +1453,7 @@ int hci_remote_oob_data_clear(struct hci_dev *hdev) } int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, - u8 *randomizer) + u8 *randomizer) { struct oob_data *data; @@ -1476,8 +1476,7 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, return 0; } -struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, - bdaddr_t *bdaddr) +struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct bdaddr_list *b; @@ -1545,7 +1544,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) static void hci_clear_adv_cache(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, - adv_work.work); + adv_work.work); hci_dev_lock(hdev); @@ -1588,11 +1587,7 @@ static inline int is_connectable_adv(u8 evt_type) } int hci_add_adv_entry(struct hci_dev *hdev, - struct hci_ev_le_advertising_info *ev) -{ - struct adv_entry *entry; - - if (!is_connectable_adv(ev->evt_type)) + struct hci_ev_le_advertising_info *ev) { struct adv_entry *entry; if (!is_connectable_adv(ev->evt_type)) return -EINVAL; /* Only new entries should be added to adv_entries. So, if @@ -1639,7 +1634,7 @@ static void le_scan_enable_req(struct hci_dev *hdev, unsigned long opt) } static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval, - u16 window, int timeout) + u16 window, int timeout) { long timeo = msecs_to_jiffies(3000); struct le_scan_params param; @@ -1657,7 +1652,7 @@ static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval, hci_req_lock(hdev); err = __hci_request(hdev, le_scan_param_req, (unsigned long) ¶m, - timeo); + timeo); if (!err) err = __hci_request(hdev, le_scan_enable_req, 0, timeo); @@ -1667,7 +1662,7 @@ static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval, return err; schedule_delayed_work(&hdev->le_scan_disable, - msecs_to_jiffies(timeout)); + msecs_to_jiffies(timeout)); return 0; } @@ -1675,7 +1670,7 @@ static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval, static void le_scan_disable_work(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, - le_scan_disable.work); + le_scan_disable.work); struct hci_cp_le_set_scan_enable cp; BT_DBG("%s", hdev->name); @@ -1692,12 +1687,12 @@ static void le_scan_work(struct work_struct *work) BT_DBG("%s", hdev->name); - hci_do_le_scan(hdev, param->type, param->interval, - param->window, param->timeout); + hci_do_le_scan(hdev, param->type, param->interval, param->window, + param->timeout); } int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window, - int timeout) + int timeout) { struct le_scan_params *param = &hdev->le_scan_params; @@ -2558,7 +2553,7 @@ static inline void hci_sched_acl_pkt(struct hci_dev *hdev) skb = skb_dequeue(&chan->data_q); hci_conn_enter_active_mode(chan->conn, - bt_cb(skb)->force_active); + bt_cb(skb)->force_active); hci_send_frame(skb); hdev->acl_last_tx = jiffies; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 6a817daf095b..badb7851d116 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -556,7 +556,7 @@ static void hci_setup(struct hci_dev *hdev) if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { u8 mode = 0x01; hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, - sizeof(mode), &mode); + sizeof(mode), &mode); } else { struct hci_cp_write_eir cp; @@ -577,14 +577,14 @@ static void hci_setup(struct hci_dev *hdev) struct hci_cp_read_local_ext_features cp; cp.page = 0x01; - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, - sizeof(cp), &cp); + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp), + &cp); } if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) { u8 enable = 1; - hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, - sizeof(enable), &enable); + hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable), + &enable); } } @@ -628,8 +628,8 @@ static void hci_setup_link_policy(struct hci_dev *hdev) link_policy |= HCI_LP_PARK; link_policy = cpu_to_le16(link_policy); - hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, - sizeof(link_policy), &link_policy); + hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, sizeof(link_policy), + &link_policy); } static void hci_cc_read_local_commands(struct hci_dev *hdev, struct sk_buff *skb) @@ -717,8 +717,8 @@ static void hci_set_le_support(struct hci_dev *hdev) } if (cp.le != !!(hdev->host_features[0] & LMP_HOST_LE)) - hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, - sizeof(cp), &cp); + hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), + &cp); } static void hci_cc_read_local_ext_features(struct hci_dev *hdev, @@ -976,8 +976,8 @@ static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, ACL_LINK, - 0, rp->status); + mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, ACL_LINK, 0, + rp->status); hci_dev_unlock(hdev); } @@ -993,8 +993,7 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev, if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr, - ACL_LINK, 0, - rp->status); + ACL_LINK, 0, rp->status); hci_dev_unlock(hdev); } @@ -1009,7 +1008,7 @@ static void hci_cc_user_passkey_reply(struct hci_dev *hdev, struct sk_buff *skb) if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr, ACL_LINK, - 0, rp->status); + 0, rp->status); hci_dev_unlock(hdev); } @@ -1025,8 +1024,7 @@ static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev, if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr, - ACL_LINK, 0, - rp->status); + ACL_LINK, 0, rp->status); hci_dev_unlock(hdev); } @@ -1337,7 +1335,7 @@ static int hci_outgoing_auth_needed(struct hci_dev *hdev, } static inline int hci_resolve_name(struct hci_dev *hdev, - struct inquiry_entry *e) + struct inquiry_entry *e) { struct hci_cp_remote_name_req cp; @@ -1369,14 +1367,14 @@ static bool hci_resolve_next_name(struct hci_dev *hdev) } static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn, - bdaddr_t *bdaddr, u8 *name, u8 name_len) + bdaddr_t *bdaddr, u8 *name, u8 name_len) { struct discovery_state *discov = &hdev->discovery; struct inquiry_entry *e; if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) - mgmt_device_connected(hdev, bdaddr, ACL_LINK, 0x00, 0, - name, name_len, conn->dev_class); + mgmt_device_connected(hdev, bdaddr, ACL_LINK, 0x00, 0, name, + name_len, conn->dev_class); if (discov->state == DISCOVERY_STOPPED) return; @@ -1393,7 +1391,7 @@ static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn, list_del(&e->list); if (name) mgmt_remote_name(hdev, bdaddr, ACL_LINK, 0x00, - e->data.rssi, name, name_len); + e->data.rssi, name, name_len); } if (hci_resolve_next_name(hdev)) @@ -1602,7 +1600,7 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status) conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); if (conn) mgmt_disconnect_failed(hdev, &conn->dst, conn->type, - conn->dst_type, status); + conn->dst_type, status); hci_dev_unlock(hdev); } @@ -1718,8 +1716,8 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff * name_known = hci_inquiry_cache_update(hdev, &data, false, &ssp); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, 0, !name_known, ssp, - NULL, 0); + info->dev_class, 0, !name_known, ssp, NULL, + 0); } hci_dev_unlock(hdev); @@ -1770,7 +1768,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s struct hci_cp_read_remote_features cp; cp.handle = ev->handle; hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES, - sizeof(cp), &cp); + sizeof(cp), &cp); } /* Set packet type for incoming connection */ @@ -1778,14 +1776,14 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s struct hci_cp_change_conn_ptype cp; cp.handle = ev->handle; cp.pkt_type = cpu_to_le16(conn->pkt_type); - hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE, - sizeof(cp), &cp); + hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE, sizeof(cp), + &cp); } } else { conn->state = BT_CLOSED; if (conn->type == ACL_LINK) mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, - conn->dst_type, ev->status); + conn->dst_type, ev->status); } if (conn->type == ACL_LINK) @@ -1850,8 +1848,8 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk else cp.role = 0x01; /* Remain slave */ - hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, - sizeof(cp), &cp); + hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), + &cp); } else { struct hci_cp_accept_sync_conn_req cp; @@ -1865,7 +1863,7 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk cp.retrans_effort = 0xff; hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ, - sizeof(cp), &cp); + sizeof(cp), &cp); } } else { /* Connection rejected */ @@ -1900,7 +1898,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff conn->dst_type, ev->status); else mgmt_device_disconnected(hdev, &conn->dst, conn->type, - conn->dst_type); + conn->dst_type); } if (ev->status == 0) { @@ -1935,7 +1933,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s } } else { mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type, - ev->status); + ev->status); } clear_bit(HCI_CONN_AUTH_PEND, &conn->flags); @@ -1996,7 +1994,7 @@ static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb if (ev->status == 0) hci_check_pending_name(hdev, conn, &ev->bdaddr, ev->name, - strnlen(ev->name, HCI_MAX_NAME_LENGTH)); + strnlen(ev->name, HCI_MAX_NAME_LENGTH)); else hci_check_pending_name(hdev, conn, &ev->bdaddr, NULL, 0); @@ -2111,8 +2109,8 @@ static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); } else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) mgmt_device_connected(hdev, &conn->dst, conn->type, - conn->dst_type, 0, NULL, 0, - conn->dev_class); + conn->dst_type, 0, NULL, 0, + conn->dev_class); if (!hci_outgoing_auth_needed(hdev, conn)) { conn->state = BT_CONNECTED; @@ -2524,7 +2522,7 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s } static inline void hci_num_comp_blocks_evt(struct hci_dev *hdev, - struct sk_buff *skb) + struct sk_buff *skb) { struct hci_ev_num_comp_blocks *ev = (void *) skb->data; int i; @@ -2816,10 +2814,10 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct data.ssp_mode = 0x00; name_known = hci_inquiry_cache_update(hdev, &data, - false, &ssp); + false, &ssp); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, info->rssi, - !name_known, ssp, NULL, 0); + info->dev_class, info->rssi, + !name_known, ssp, NULL, 0); } } else { struct inquiry_info_with_rssi *info = (void *) (skb->data + 1); @@ -2834,10 +2832,10 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct data.rssi = info->rssi; data.ssp_mode = 0x00; name_known = hci_inquiry_cache_update(hdev, &data, - false, &ssp); + false, &ssp); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, info->rssi, - !name_known, ssp, NULL, 0); + info->dev_class, info->rssi, + !name_known, ssp, NULL, 0); } } @@ -2879,8 +2877,8 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); } else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) mgmt_device_connected(hdev, &conn->dst, conn->type, - conn->dst_type, 0, NULL, 0, - conn->dev_class); + conn->dst_type, 0, NULL, 0, + conn->dev_class); if (!hci_outgoing_auth_needed(hdev, conn)) { conn->state = BT_CONNECTED; @@ -2986,17 +2984,16 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct if (test_bit(HCI_MGMT, &hdev->dev_flags)) name_known = eir_has_data_type(info->data, - sizeof(info->data), - EIR_NAME_COMPLETE); + sizeof(info->data), + EIR_NAME_COMPLETE); else name_known = true; name_known = hci_inquiry_cache_update(hdev, &data, name_known, - &ssp); + &ssp); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, info->rssi, - !name_known, ssp, info->data, - sizeof(info->data)); + info->dev_class, info->rssi, !name_known, + ssp, info->data, sizeof(info->data)); } hci_dev_unlock(hdev); @@ -3157,7 +3154,7 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev, confirm: mgmt_user_confirm_request(hdev, &ev->bdaddr, ACL_LINK, 0, ev->passkey, - confirm_hint); + confirm_hint); unlock: hci_dev_unlock(hdev); @@ -3198,7 +3195,7 @@ static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_ * the mgmt_auth_failed event */ if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status != 0) mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type, - ev->status); + ev->status); hci_conn_put(conn); @@ -3223,7 +3220,7 @@ static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_ } static inline void hci_remote_oob_data_request_evt(struct hci_dev *hdev, - struct sk_buff *skb) + struct sk_buff *skb) { struct hci_ev_remote_oob_data_request *ev = (void *) skb->data; struct oob_data *data; @@ -3289,7 +3286,7 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) mgmt_device_connected(hdev, &ev->bdaddr, conn->type, - conn->dst_type, 0, NULL, 0, NULL); + conn->dst_type, 0, NULL, 0, NULL); conn->sec_level = BT_SECURITY_LOW; conn->handle = __le16_to_cpu(ev->handle); @@ -3320,8 +3317,7 @@ static inline void hci_le_adv_report_evt(struct hci_dev *hdev, rssi = ev->data[ev->length]; mgmt_device_found(hdev, &ev->bdaddr, LE_LINK, ev->bdaddr_type, - NULL, rssi, 0, 1, ev->data, - ev->length); + NULL, rssi, 0, 1, ev->data, ev->length); ptr += sizeof(*ev) + ev->length + 1; } diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 0b1aabff8649..3e450f4a3125 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1546,7 +1546,9 @@ static void l2cap_send_srejtail(struct l2cap_chan *chan) l2cap_send_sframe(chan, control); } -static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, struct msghdr *msg, int len, int count, struct sk_buff *skb) +static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, + struct msghdr *msg, int len, + int count, struct sk_buff *skb) { struct l2cap_conn *conn = chan->conn; struct sk_buff **frag; @@ -1564,7 +1566,8 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, struct msghdr count = min_t(unsigned int, conn->mtu, len); *frag = chan->ops->alloc_skb(chan, count, - msg->msg_flags & MSG_DONTWAIT, &err); + msg->msg_flags & MSG_DONTWAIT, + &err); if (!*frag) return err; @@ -1596,7 +1599,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, count = min_t(unsigned int, (conn->mtu - hlen), len); skb = chan->ops->alloc_skb(chan, count + hlen, - msg->msg_flags & MSG_DONTWAIT, &err); + msg->msg_flags & MSG_DONTWAIT, &err); if (!skb) return ERR_PTR(err); @@ -1631,7 +1634,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, count = min_t(unsigned int, (conn->mtu - hlen), len); skb = chan->ops->alloc_skb(chan, count + hlen, - msg->msg_flags & MSG_DONTWAIT, &err); + msg->msg_flags & MSG_DONTWAIT, &err); if (!skb) return ERR_PTR(err); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 3da56c5c1fc9..c4fe583b0af6 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -926,7 +926,8 @@ static void l2cap_sock_state_change_cb(void *data, int state) } static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, - unsigned long len, int nb, int *err) + unsigned long len, int nb, + int *err) { struct sock *sk = chan->sk; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 4ca009268afb..7fcff8887131 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -234,7 +234,7 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) } static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status, - void *rp, size_t rp_len) + void *rp, size_t rp_len) { struct sk_buff *skb; struct mgmt_hdr *hdr; @@ -267,8 +267,8 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status, return err; } -static int read_version(struct sock *sk, struct hci_dev *hdev, - void *data, u16 data_len) +static int read_version(struct sock *sk, struct hci_dev *hdev, void *data, + u16 data_len) { struct mgmt_rp_read_version rp; @@ -278,11 +278,11 @@ static int read_version(struct sock *sk, struct hci_dev *hdev, put_unaligned_le16(MGMT_REVISION, &rp.revision); return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, 0, &rp, - sizeof(rp)); + sizeof(rp)); } -static int read_commands(struct sock *sk, struct hci_dev *hdev, - void *data, u16 data_len) +static int read_commands(struct sock *sk, struct hci_dev *hdev, void *data, + u16 data_len) { struct mgmt_rp_read_commands *rp; u16 num_commands = ARRAY_SIZE(mgmt_commands); @@ -309,14 +309,14 @@ static int read_commands(struct sock *sk, struct hci_dev *hdev, put_unaligned_le16(mgmt_events[i], opcode); err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_COMMANDS, 0, rp, - rp_size); + rp_size); kfree(rp); return err; } -static int read_index_list(struct sock *sk, struct hci_dev *hdev, - void *data, u16 data_len) +static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, + u16 data_len) { struct mgmt_rp_read_index_list *rp; struct list_head *p; @@ -355,7 +355,7 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, read_unlock(&hci_dev_list_lock); err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, 0, rp, - rp_len); + rp_len); kfree(rp); @@ -600,7 +600,7 @@ static int update_class(struct hci_dev *hdev) static void service_cache_off(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, - service_cache.work); + service_cache.work); if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) return; @@ -629,7 +629,7 @@ static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev) } static int read_controller_info(struct sock *sk, struct hci_dev *hdev, - void *data, u16 data_len) + void *data, u16 data_len) { struct mgmt_rp_read_info rp; @@ -656,7 +656,7 @@ static int read_controller_info(struct sock *sk, struct hci_dev *hdev, hci_dev_unlock(hdev); return cmd_complete(sk, hdev->id, MGMT_OP_READ_INFO, 0, &rp, - sizeof(rp)); + sizeof(rp)); } static void mgmt_pending_free(struct pending_cmd *cmd) @@ -667,8 +667,8 @@ static void mgmt_pending_free(struct pending_cmd *cmd) } static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, - struct hci_dev *hdev, - void *data, u16 len) + struct hci_dev *hdev, void *data, + u16 len) { struct pending_cmd *cmd; @@ -697,8 +697,8 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, } static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, - void (*cb)(struct pending_cmd *cmd, void *data), - void *data) + void (*cb)(struct pending_cmd *cmd, void *data), + void *data) { struct list_head *p, *n; @@ -737,11 +737,11 @@ static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) __le32 settings = cpu_to_le32(get_current_settings(hdev)); return cmd_complete(sk, hdev->id, opcode, 0, &settings, - sizeof(settings)); + sizeof(settings)); } static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) + u16 len) { struct mgmt_mode *cp = data; struct pending_cmd *cmd; @@ -768,7 +768,7 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED, - MGMT_STATUS_BUSY); + MGMT_STATUS_BUSY); goto failed; } @@ -790,8 +790,8 @@ failed: return err; } -static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, - u16 data_len, struct sock *skip_sk) +static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len, + struct sock *skip_sk) { struct sk_buff *skb; struct mgmt_hdr *hdr; @@ -830,7 +830,7 @@ static int new_settings(struct hci_dev *hdev, struct sock *skip) } static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) + u16 len) { struct mgmt_cp_set_discoverable *cp = data; struct pending_cmd *cmd; @@ -843,26 +843,26 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, timeout = get_unaligned_le16(&cp->timeout); if (!cp->val && timeout > 0) return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, - MGMT_STATUS_INVALID_PARAMS); + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock(hdev); if (!hdev_is_powered(hdev) && timeout > 0) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, - MGMT_STATUS_NOT_POWERED); + MGMT_STATUS_NOT_POWERED); goto failed; } if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, - MGMT_STATUS_BUSY); + MGMT_STATUS_BUSY); goto failed; } if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, - MGMT_STATUS_REJECTED); + MGMT_STATUS_REJECTED); goto failed; } @@ -926,7 +926,7 @@ failed: } static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) + u16 len) { struct mgmt_mode *cp = data; struct pending_cmd *cmd; @@ -963,7 +963,7 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, - MGMT_STATUS_BUSY); + MGMT_STATUS_BUSY); goto failed; } @@ -998,7 +998,7 @@ failed: } static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) + u16 len) { struct mgmt_mode *cp = data; int err; @@ -1023,8 +1023,8 @@ failed: return err; } -static int set_link_security(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) +static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) { struct mgmt_mode *cp = data; struct pending_cmd *cmd; @@ -1056,7 +1056,7 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, if (mgmt_pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, - MGMT_STATUS_BUSY); + MGMT_STATUS_BUSY); goto failed; } @@ -1097,7 +1097,7 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, - MGMT_STATUS_NOT_SUPPORTED); + MGMT_STATUS_NOT_SUPPORTED); goto failed; } @@ -1122,8 +1122,8 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) } if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, - MGMT_STATUS_BUSY); + err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, + MGMT_STATUS_BUSY); goto failed; } @@ -1157,7 +1157,7 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) if (!enable_hs) return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, - MGMT_STATUS_NOT_SUPPORTED); + MGMT_STATUS_NOT_SUPPORTED); if (cp->val) set_bit(HCI_HS_ENABLED, &hdev->dev_flags); @@ -1181,7 +1181,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) if (!enable_le || !(hdev->features[4] & LMP_LE)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE, - MGMT_STATUS_NOT_SUPPORTED); + MGMT_STATUS_NOT_SUPPORTED); goto unlock; } @@ -1208,7 +1208,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) if (mgmt_pending_find(MGMT_OP_SET_LE, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE, - MGMT_STATUS_BUSY); + MGMT_STATUS_BUSY); goto unlock; } @@ -1225,8 +1225,8 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) hci_cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); } - err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, - sizeof(hci_cp), &hci_cp); + err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp), + &hci_cp); if (err < 0) { mgmt_pending_remove(cmd); goto unlock; @@ -1250,7 +1250,7 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_status(sk, hdev->id, MGMT_OP_ADD_UUID, - MGMT_STATUS_BUSY); + MGMT_STATUS_BUSY); goto failed; } @@ -1275,7 +1275,7 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_UUID, 0, - hdev->dev_class, 3); + hdev->dev_class, 3); goto failed; } @@ -1318,7 +1318,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID, - MGMT_STATUS_BUSY); + MGMT_STATUS_BUSY); goto unlock; } @@ -1327,7 +1327,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, if (enable_service_cache(hdev)) { err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, - 0, hdev->dev_class, 3); + 0, hdev->dev_class, 3); goto unlock; } @@ -1348,7 +1348,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, if (found == 0) { err = cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID, - MGMT_STATUS_INVALID_PARAMS); + MGMT_STATUS_INVALID_PARAMS); goto unlock; } @@ -1363,7 +1363,7 @@ update_class: if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, 0, - hdev->dev_class, 3); + hdev->dev_class, 3); goto unlock; } @@ -1379,7 +1379,7 @@ unlock: } static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) + u16 len) { struct mgmt_cp_set_dev_class *cp = data; struct pending_cmd *cmd; @@ -1391,7 +1391,7 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, - MGMT_STATUS_BUSY); + MGMT_STATUS_BUSY); goto unlock; } @@ -1400,7 +1400,7 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, if (!hdev_is_powered(hdev)) { err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0, - hdev->dev_class, 3); + hdev->dev_class, 3); goto unlock; } @@ -1417,7 +1417,7 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0, - hdev->dev_class, 3); + hdev->dev_class, 3); goto unlock; } @@ -1447,7 +1447,7 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, BT_ERR("load_link_keys: expected %u bytes, got %u bytes", len, expected_len); return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, - MGMT_STATUS_INVALID_PARAMS); + MGMT_STATUS_INVALID_PARAMS); } BT_DBG("%s debug_keys %u key_count %u", hdev->name, cp->debug_keys, @@ -1468,7 +1468,7 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, struct mgmt_link_key_info *key = &cp->keys[i]; hci_add_link_key(hdev, NULL, 0, &key->addr.bdaddr, key->val, - key->type, key->pin_len); + key->type, key->pin_len); } cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0); @@ -1479,7 +1479,7 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, } static int device_unpaired(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 addr_type, struct sock *skip_sk) + u8 addr_type, struct sock *skip_sk) { struct mgmt_ev_device_unpaired ev; @@ -1487,11 +1487,11 @@ static int device_unpaired(struct hci_dev *hdev, bdaddr_t *bdaddr, ev.addr.type = addr_type; return mgmt_event(MGMT_EV_DEVICE_UNPAIRED, hdev, &ev, sizeof(ev), - skip_sk); + skip_sk); } static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) + u16 len) { struct mgmt_cp_unpair_device *cp = data; struct mgmt_rp_unpair_device rp; @@ -1508,8 +1508,7 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, if (!hdev_is_powered(hdev)) { err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, - MGMT_STATUS_NOT_POWERED, - &rp, sizeof(rp)); + MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp)); goto unlock; } @@ -1520,8 +1519,7 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, if (err < 0) { err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, - MGMT_STATUS_NOT_PAIRED, - &rp, sizeof(rp)); + MGMT_STATUS_NOT_PAIRED, &rp, sizeof(rp)); goto unlock; } @@ -1538,13 +1536,13 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, if (!conn) { err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 0, - &rp, sizeof(rp)); + &rp, sizeof(rp)); device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk); goto unlock; } cmd = mgmt_pending_add(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp, - sizeof(*cp)); + sizeof(*cp)); if (!cmd) { err = -ENOMEM; goto unlock; @@ -1562,7 +1560,7 @@ unlock: } static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) + u16 len) { struct mgmt_cp_disconnect *cp = data; struct hci_cp_disconnect dc; @@ -1576,13 +1574,13 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, if (!test_bit(HCI_UP, &hdev->flags)) { err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_NOT_POWERED); + MGMT_STATUS_NOT_POWERED); goto failed; } if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_BUSY); + MGMT_STATUS_BUSY); goto failed; } @@ -1593,7 +1591,7 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, if (!conn) { err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_NOT_CONNECTED); + MGMT_STATUS_NOT_CONNECTED); goto failed; } @@ -1634,8 +1632,8 @@ static u8 link_to_mgmt(u8 link_type, u8 addr_type) } } -static int get_connections(struct sock *sk, struct hci_dev *hdev, - void *data, u16 data_len) +static int get_connections(struct sock *sk, struct hci_dev *hdev, void *data, + u16 data_len) { struct mgmt_rp_get_connections *rp; struct hci_conn *c; @@ -1649,7 +1647,7 @@ static int get_connections(struct sock *sk, struct hci_dev *hdev, if (!hdev_is_powered(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_GET_CONNECTIONS, - MGMT_STATUS_NOT_POWERED); + MGMT_STATUS_NOT_POWERED); goto unlock; } @@ -1683,7 +1681,7 @@ static int get_connections(struct sock *sk, struct hci_dev *hdev, rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info)); err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONNECTIONS, 0, rp, - rp_len); + rp_len); kfree(rp); @@ -1693,18 +1691,18 @@ unlock: } static int send_pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev, - struct mgmt_cp_pin_code_neg_reply *cp) + struct mgmt_cp_pin_code_neg_reply *cp) { struct pending_cmd *cmd; int err; cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp, - sizeof(*cp)); + sizeof(*cp)); if (!cmd) return -ENOMEM; err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, - sizeof(cp->addr.bdaddr), &cp->addr.bdaddr); + sizeof(cp->addr.bdaddr), &cp->addr.bdaddr); if (err < 0) mgmt_pending_remove(cmd); @@ -1712,7 +1710,7 @@ static int send_pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev, } static int pin_code_reply(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) + u16 len) { struct hci_conn *conn; struct mgmt_cp_pin_code_reply *cp = data; @@ -1726,14 +1724,14 @@ static int pin_code_reply(struct sock *sk, struct hci_dev *hdev, void *data, if (!hdev_is_powered(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, - MGMT_STATUS_NOT_POWERED); + MGMT_STATUS_NOT_POWERED); goto failed; } conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr); if (!conn) { err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, - MGMT_STATUS_NOT_CONNECTED); + MGMT_STATUS_NOT_CONNECTED); goto failed; } @@ -1747,7 +1745,7 @@ static int pin_code_reply(struct sock *sk, struct hci_dev *hdev, void *data, err = send_pin_code_neg_reply(sk, hdev, &ncp); if (err >= 0) err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, - MGMT_STATUS_INVALID_PARAMS); + MGMT_STATUS_INVALID_PARAMS); goto failed; } @@ -1772,7 +1770,7 @@ failed: } static int pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) + void *data, u16 len) { struct mgmt_cp_pin_code_neg_reply *cp = data; int err; @@ -1783,7 +1781,7 @@ static int pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev, if (!hdev_is_powered(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, - MGMT_STATUS_NOT_POWERED); + MGMT_STATUS_NOT_POWERED); goto failed; } @@ -1794,8 +1792,8 @@ failed: return err; } -static int set_io_capability(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) +static int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) { struct mgmt_cp_set_io_capability *cp = data; @@ -1810,8 +1808,8 @@ static int set_io_capability(struct sock *sk, struct hci_dev *hdev, hci_dev_unlock(hdev); - return cmd_complete(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY, 0, - NULL, 0); + return cmd_complete(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY, 0, NULL, + 0); } static inline struct pending_cmd *find_pairing(struct hci_conn *conn) @@ -1841,7 +1839,7 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status) rp.addr.type = link_to_mgmt(conn->type, conn->dst_type); cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status, - &rp, sizeof(rp)); + &rp, sizeof(rp)); /* So we don't get further callbacks for this connection */ conn->connect_cfm_cb = NULL; @@ -1867,7 +1865,7 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status) } static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) + u16 len) { struct mgmt_cp_pair_device *cp = data; struct mgmt_rp_pair_device rp; @@ -1882,7 +1880,7 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, if (!hdev_is_powered(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_PAIR_DEVICE, - MGMT_STATUS_NOT_POWERED); + MGMT_STATUS_NOT_POWERED); goto unlock; } @@ -1894,10 +1892,10 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, if (cp->addr.type == MGMT_ADDR_BREDR) conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level, - auth_type); + auth_type); else conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level, - auth_type); + auth_type); memset(&rp, 0, sizeof(rp)); bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); @@ -1905,15 +1903,15 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, if (IS_ERR(conn)) { err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, - MGMT_STATUS_CONNECT_FAILED, - &rp, sizeof(rp)); + MGMT_STATUS_CONNECT_FAILED, &rp, + sizeof(rp)); goto unlock; } if (conn->connect_cfm_cb) { hci_conn_put(conn); err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, - MGMT_STATUS_BUSY, &rp, sizeof(rp)); + MGMT_STATUS_BUSY, &rp, sizeof(rp)); goto unlock; } @@ -1944,8 +1942,8 @@ unlock: return err; } -static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) +static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) { struct mgmt_addr_info *addr = data; struct pending_cmd *cmd; @@ -1958,14 +1956,14 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, if (!hdev_is_powered(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, - MGMT_STATUS_NOT_POWERED); + MGMT_STATUS_NOT_POWERED); goto unlock; } cmd = mgmt_pending_find(MGMT_OP_PAIR_DEVICE, hdev); if (!cmd) { err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, - MGMT_STATUS_INVALID_PARAMS); + MGMT_STATUS_INVALID_PARAMS); goto unlock; } @@ -1973,22 +1971,22 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, if (bacmp(&addr->bdaddr, &conn->dst) != 0) { err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, - MGMT_STATUS_INVALID_PARAMS); + MGMT_STATUS_INVALID_PARAMS); goto unlock; } pairing_complete(cmd, MGMT_STATUS_CANCELLED); err = cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0, - addr, sizeof(*addr)); + addr, sizeof(*addr)); unlock: hci_dev_unlock(hdev); return err; } static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, - bdaddr_t *bdaddr, u8 type, u16 mgmt_op, - u16 hci_op, __le32 passkey) + bdaddr_t *bdaddr, u8 type, u16 mgmt_op, + u16 hci_op, __le32 passkey) { struct pending_cmd *cmd; struct hci_conn *conn; @@ -1998,7 +1996,7 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, if (!hdev_is_powered(hdev)) { err = cmd_status(sk, hdev->id, mgmt_op, - MGMT_STATUS_NOT_POWERED); + MGMT_STATUS_NOT_POWERED); goto done; } @@ -2009,7 +2007,7 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, if (!conn) { err = cmd_status(sk, hdev->id, mgmt_op, - MGMT_STATUS_NOT_CONNECTED); + MGMT_STATUS_NOT_CONNECTED); goto done; } @@ -2019,10 +2017,10 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, if (!err) err = cmd_status(sk, hdev->id, mgmt_op, - MGMT_STATUS_SUCCESS); + MGMT_STATUS_SUCCESS); else err = cmd_status(sk, hdev->id, mgmt_op, - MGMT_STATUS_FAILED); + MGMT_STATUS_FAILED); goto done; } @@ -2051,8 +2049,8 @@ done: return err; } -static int user_confirm_reply(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) +static int user_confirm_reply(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) { struct mgmt_cp_user_confirm_reply *cp = data; @@ -2060,48 +2058,47 @@ static int user_confirm_reply(struct sock *sk, struct hci_dev *hdev, if (len != sizeof(*cp)) return cmd_status(sk, hdev->id, MGMT_OP_USER_CONFIRM_REPLY, - MGMT_STATUS_INVALID_PARAMS); + MGMT_STATUS_INVALID_PARAMS); return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, - MGMT_OP_USER_CONFIRM_REPLY, - HCI_OP_USER_CONFIRM_REPLY, 0); + MGMT_OP_USER_CONFIRM_REPLY, + HCI_OP_USER_CONFIRM_REPLY, 0); } static int user_confirm_neg_reply(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) + void *data, u16 len) { struct mgmt_cp_user_confirm_neg_reply *cp = data; BT_DBG(""); return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, - MGMT_OP_USER_CONFIRM_NEG_REPLY, - HCI_OP_USER_CONFIRM_NEG_REPLY, 0); + MGMT_OP_USER_CONFIRM_NEG_REPLY, + HCI_OP_USER_CONFIRM_NEG_REPLY, 0); } -static int user_passkey_reply(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) +static int user_passkey_reply(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) { struct mgmt_cp_user_passkey_reply *cp = data; BT_DBG(""); return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, - MGMT_OP_USER_PASSKEY_REPLY, - HCI_OP_USER_PASSKEY_REPLY, - cp->passkey); + MGMT_OP_USER_PASSKEY_REPLY, + HCI_OP_USER_PASSKEY_REPLY, cp->passkey); } static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) + void *data, u16 len) { struct mgmt_cp_user_passkey_neg_reply *cp = data; BT_DBG(""); return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, - MGMT_OP_USER_PASSKEY_NEG_REPLY, - HCI_OP_USER_PASSKEY_NEG_REPLY, 0); + MGMT_OP_USER_PASSKEY_NEG_REPLY, + HCI_OP_USER_PASSKEY_NEG_REPLY, 0); } static int update_name(struct hci_dev *hdev, const char *name) @@ -2114,7 +2111,7 @@ static int update_name(struct hci_dev *hdev, const char *name) } static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) + u16 len) { struct mgmt_cp_set_local_name *cp = data; struct pending_cmd *cmd; @@ -2130,12 +2127,12 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, memcpy(hdev->dev_name, cp->name, sizeof(hdev->dev_name)); err = cmd_complete(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, - data, len); + data, len); if (err < 0) goto failed; err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, data, len, - sk); + sk); goto failed; } @@ -2156,7 +2153,7 @@ failed: } static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev, - void *data, u16 data_len) + void *data, u16 data_len) { struct pending_cmd *cmd; int err; @@ -2167,19 +2164,19 @@ static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev, if (!hdev_is_powered(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, - MGMT_STATUS_NOT_POWERED); + MGMT_STATUS_NOT_POWERED); goto unlock; } if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) { err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, - MGMT_STATUS_NOT_SUPPORTED); + MGMT_STATUS_NOT_SUPPORTED); goto unlock; } if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, - MGMT_STATUS_BUSY); + MGMT_STATUS_BUSY); goto unlock; } @@ -2199,7 +2196,7 @@ unlock: } static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) + void *data, u16 len) { struct mgmt_cp_add_remote_oob_data *cp = data; u8 status; @@ -2211,20 +2208,20 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, if (!hdev_is_powered(hdev)) { err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, - MGMT_STATUS_NOT_POWERED, - &cp->addr, sizeof(cp->addr)); + MGMT_STATUS_NOT_POWERED, &cp->addr, + sizeof(cp->addr)); goto unlock; } err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash, - cp->randomizer); + cp->randomizer); if (err < 0) status = MGMT_STATUS_FAILED; else status = 0; err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status, - &cp->addr, sizeof(cp->addr)); + &cp->addr, sizeof(cp->addr)); unlock: hci_dev_unlock(hdev); @@ -2244,9 +2241,9 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev, if (!hdev_is_powered(hdev)) { err = cmd_complete(sk, hdev->id, - MGMT_OP_REMOVE_REMOTE_OOB_DATA, - MGMT_STATUS_NOT_POWERED, - &cp->addr, sizeof(cp->addr)); + MGMT_OP_REMOVE_REMOTE_OOB_DATA, + MGMT_STATUS_NOT_POWERED, &cp->addr, + sizeof(cp->addr)); goto unlock; } @@ -2257,7 +2254,7 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev, status = 0; err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA, - status, &cp->addr, sizeof(cp->addr)); + status, &cp->addr, sizeof(cp->addr)); unlock: hci_dev_unlock(hdev); @@ -2282,7 +2279,7 @@ int mgmt_interleaved_discovery(struct hci_dev *hdev) } static int start_discovery(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) + void *data, u16 len) { struct mgmt_cp_start_discovery *cp = data; struct pending_cmd *cmd; @@ -2294,13 +2291,13 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, if (!hdev_is_powered(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_NOT_POWERED); + MGMT_STATUS_NOT_POWERED); goto failed; } if (hdev->discovery.state != DISCOVERY_STOPPED) { err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_BUSY); + MGMT_STATUS_BUSY); goto failed; } @@ -2323,7 +2320,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, case DISCOV_TYPE_LE: if (lmp_host_le_capable(hdev)) err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, - LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY); + LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY); else err = -ENOTSUPP; break; @@ -2331,7 +2328,8 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, case DISCOV_TYPE_INTERLEAVED: if (lmp_host_le_capable(hdev) && lmp_bredr_capable(hdev)) err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, - LE_SCAN_WIN, LE_SCAN_TIMEOUT_BREDR_LE); + LE_SCAN_WIN, + LE_SCAN_TIMEOUT_BREDR_LE); else err = -ENOTSUPP; break; @@ -2351,7 +2349,7 @@ failed: } static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) + u16 len) { struct mgmt_cp_stop_discovery *mgmt_cp = data; struct pending_cmd *cmd; @@ -2365,15 +2363,15 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data, if (!hci_discovery_active(hdev)) { err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, - MGMT_STATUS_REJECTED, - &mgmt_cp->type, sizeof(mgmt_cp->type)); + MGMT_STATUS_REJECTED, &mgmt_cp->type, + sizeof(mgmt_cp->type)); goto unlock; } if (hdev->discovery.type != mgmt_cp->type) { err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, - MGMT_STATUS_INVALID_PARAMS, - &mgmt_cp->type, sizeof(mgmt_cp->type)); + MGMT_STATUS_INVALID_PARAMS, &mgmt_cp->type, + sizeof(mgmt_cp->type)); goto unlock; } @@ -2396,14 +2394,14 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data, if (!e) { mgmt_pending_remove(cmd); err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, 0, - &mgmt_cp->type, sizeof(mgmt_cp->type)); + &mgmt_cp->type, sizeof(mgmt_cp->type)); hci_discovery_set_state(hdev, DISCOVERY_STOPPED); goto unlock; } bacpy(&cp.bdaddr, &e->data.bdaddr); - err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL, - sizeof(cp), &cp); + err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp), + &cp); if (err < 0) mgmt_pending_remove(cmd); else @@ -2415,7 +2413,7 @@ unlock: } static int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) + u16 len) { struct mgmt_cp_confirm_name *cp = data; struct inquiry_entry *e; @@ -2427,14 +2425,14 @@ static int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data, if (!hci_discovery_active(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME, - MGMT_STATUS_FAILED); + MGMT_STATUS_FAILED); goto failed; } e = hci_inquiry_cache_lookup_unknown(hdev, &cp->addr.bdaddr); if (!e) { err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME, - MGMT_STATUS_INVALID_PARAMS); + MGMT_STATUS_INVALID_PARAMS); goto failed; } @@ -2454,7 +2452,7 @@ failed: } static int block_device(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) + u16 len) { struct mgmt_cp_block_device *cp = data; u8 status; @@ -2471,7 +2469,7 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data, status = 0; err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status, - &cp->addr, sizeof(cp->addr)); + &cp->addr, sizeof(cp->addr)); hci_dev_unlock(hdev); @@ -2479,7 +2477,7 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data, } static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) + u16 len) { struct mgmt_cp_unblock_device *cp = data; u8 status; @@ -2496,7 +2494,7 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, status = 0; err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status, - &cp->addr, sizeof(cp->addr)); + &cp->addr, sizeof(cp->addr)); hci_dev_unlock(hdev); @@ -2504,7 +2502,7 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, } static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) + void *data, u16 len) { struct mgmt_mode *cp = data; struct hci_cp_write_page_scan_activity acp; @@ -2515,11 +2513,11 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, if (!hdev_is_powered(hdev)) return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_NOT_POWERED); + MGMT_STATUS_NOT_POWERED); if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_REJECTED); + MGMT_STATUS_REJECTED); hci_dev_lock(hdev); @@ -2533,30 +2531,30 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, acp.window = 0x0012; /* default 11.25 msec page scan window */ - err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, - sizeof(acp), &acp); + err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, sizeof(acp), + &acp); if (err < 0) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_FAILED); + MGMT_STATUS_FAILED); goto done; } err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); if (err < 0) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_FAILED); + MGMT_STATUS_FAILED); goto done; } err = cmd_complete(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, 0, - NULL, 0); + NULL, 0); done: hci_dev_unlock(hdev); return err; } static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, - void *cp_data, u16 len) + void *cp_data, u16 len) { struct mgmt_cp_load_long_term_keys *cp = cp_data; u16 key_count, expected_len; @@ -2570,7 +2568,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, BT_ERR("load_keys: expected %u bytes, got %u bytes", len, expected_len); return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, - EINVAL); + EINVAL); } BT_DBG("%s key_count %u", hdev->name, key_count); @@ -2589,8 +2587,8 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, type = HCI_SMP_LTK_SLAVE; hci_add_ltk(hdev, &key->addr.bdaddr, key->addr.type, - type, 0, key->authenticated, key->val, - key->enc_size, key->ediv, key->rand); + type, 0, key->authenticated, key->val, + key->enc_size, key->ediv, key->rand); } hci_dev_unlock(hdev); @@ -2599,8 +2597,8 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, } struct mgmt_handler { - int (*func) (struct sock *sk, struct hci_dev *hdev, - void *data, u16 data_len); + int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, + u16 data_len); bool var_len; size_t data_len; } mgmt_handlers[] = { @@ -2685,7 +2683,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) hdev = hci_dev_get(index); if (!hdev) { err = cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_INDEX); + MGMT_STATUS_INVALID_INDEX); goto done; } } @@ -2694,14 +2692,14 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) mgmt_handlers[opcode].func == NULL) { BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, index, opcode, - MGMT_STATUS_UNKNOWN_COMMAND); + MGMT_STATUS_UNKNOWN_COMMAND); goto done; } if ((hdev && opcode < MGMT_OP_READ_INFO) || (!hdev && opcode >= MGMT_OP_READ_INFO)) { err = cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_INDEX); + MGMT_STATUS_INVALID_INDEX); goto done; } @@ -2710,7 +2708,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) if ((handler->var_len && len < handler->data_len) || (!handler->var_len && len != handler->data_len)) { err = cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_PARAMS); + MGMT_STATUS_INVALID_PARAMS); goto done; } @@ -2829,7 +2827,7 @@ int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) } mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, - &match); + &match); if (changed) err = new_settings(hdev, match.sk); @@ -2855,7 +2853,7 @@ int mgmt_connectable(struct hci_dev *hdev, u8 connectable) } mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp, - &match); + &match); if (changed) err = new_settings(hdev, match.sk); @@ -2872,17 +2870,16 @@ int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) if (scan & SCAN_PAGE) mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, - cmd_status_rsp, &mgmt_err); + cmd_status_rsp, &mgmt_err); if (scan & SCAN_INQUIRY) mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, - cmd_status_rsp, &mgmt_err); + cmd_status_rsp, &mgmt_err); return 0; } -int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, - u8 persistent) +int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, u8 persistent) { struct mgmt_ev_new_link_key ev; @@ -2917,13 +2914,13 @@ int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent) memcpy(ev.key.rand, key->rand, sizeof(key->rand)); memcpy(ev.key.val, key->val, sizeof(key->val)); - return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, - &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), + NULL); } int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u32 flags, u8 *name, - u8 name_len, u8 *dev_class) + u8 addr_type, u32 flags, u8 *name, u8 name_len, + u8 *dev_class) { char buf[512]; struct mgmt_ev_device_connected *ev = (void *) buf; @@ -2936,16 +2933,16 @@ int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, if (name_len > 0) eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, - name, name_len); + name, name_len); if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0) eir_len = eir_append_data(&ev->eir[eir_len], eir_len, - EIR_CLASS_OF_DEV, dev_class, 3); + EIR_CLASS_OF_DEV, dev_class, 3); put_unaligned_le16(eir_len, &ev->eir_len); return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf, - sizeof(*ev) + eir_len, NULL); + sizeof(*ev) + eir_len, NULL); } static void disconnect_rsp(struct pending_cmd *cmd, void *data) @@ -2958,7 +2955,7 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data) rp.addr.type = cp->addr.type; cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, 0, &rp, - sizeof(rp)); + sizeof(rp)); *sk = cmd->sk; sock_hold(*sk); @@ -2984,7 +2981,7 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data) } int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type) + u8 link_type, u8 addr_type) { struct mgmt_addr_info ev; struct sock *sk = NULL; @@ -2996,19 +2993,19 @@ int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, ev.type = link_to_mgmt(link_type, addr_type); err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev), - sk); + sk); if (sk) - sock_put(sk); + sock_put(sk); mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, - hdev); + hdev); return err; } int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status) + u8 link_type, u8 addr_type, u8 status) { struct mgmt_rp_disconnect rp; struct pending_cmd *cmd; @@ -3022,7 +3019,7 @@ int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, rp.addr.type = link_to_mgmt(link_type, addr_type); err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, - mgmt_status(status), &rp, sizeof(rp)); + mgmt_status(status), &rp, sizeof(rp)); mgmt_pending_remove(cmd); @@ -3032,7 +3029,7 @@ int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, } int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 status) + u8 addr_type, u8 status) { struct mgmt_ev_connect_failed ev; @@ -3052,11 +3049,11 @@ int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure) ev.secure = secure; return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev), - NULL); + NULL); } int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status) + u8 status) { struct pending_cmd *cmd; struct mgmt_rp_pin_code_reply rp; @@ -3070,7 +3067,7 @@ int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, rp.addr.type = MGMT_ADDR_BREDR; err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, - mgmt_status(status), &rp, sizeof(rp)); + mgmt_status(status), &rp, sizeof(rp)); mgmt_pending_remove(cmd); @@ -3078,7 +3075,7 @@ int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, } int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status) + u8 status) { struct pending_cmd *cmd; struct mgmt_rp_pin_code_reply rp; @@ -3092,7 +3089,7 @@ int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, rp.addr.type = MGMT_ADDR_BREDR; err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, - mgmt_status(status), &rp, sizeof(rp)); + mgmt_status(status), &rp, sizeof(rp)); mgmt_pending_remove(cmd); @@ -3100,8 +3097,8 @@ int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, } int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, __le32 value, - u8 confirm_hint) + u8 link_type, u8 addr_type, __le32 value, + u8 confirm_hint) { struct mgmt_ev_user_confirm_request ev; @@ -3113,7 +3110,7 @@ int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, put_unaligned_le32(value, &ev.value); return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev), - NULL); + NULL); } int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr, @@ -3127,7 +3124,7 @@ int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr, ev.addr.type = link_to_mgmt(link_type, addr_type); return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev), - NULL); + NULL); } static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, @@ -3145,7 +3142,7 @@ static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, bacpy(&rp.addr.bdaddr, bdaddr); rp.addr.type = link_to_mgmt(link_type, addr_type); err = cmd_complete(cmd->sk, hdev->id, opcode, mgmt_status(status), - &rp, sizeof(rp)); + &rp, sizeof(rp)); mgmt_pending_remove(cmd); @@ -3153,35 +3150,35 @@ static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, } int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status) + u8 link_type, u8 addr_type, u8 status) { return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, - status, MGMT_OP_USER_CONFIRM_REPLY); + status, MGMT_OP_USER_CONFIRM_REPLY); } int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status) + u8 link_type, u8 addr_type, u8 status) { return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, - status, MGMT_OP_USER_CONFIRM_NEG_REPLY); + status, MGMT_OP_USER_CONFIRM_NEG_REPLY); } int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status) + u8 link_type, u8 addr_type, u8 status) { return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, - status, MGMT_OP_USER_PASSKEY_REPLY); + status, MGMT_OP_USER_PASSKEY_REPLY); } int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status) + u8 link_type, u8 addr_type, u8 status) { return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, - status, MGMT_OP_USER_PASSKEY_NEG_REPLY); + status, MGMT_OP_USER_PASSKEY_NEG_REPLY); } int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 status) + u8 addr_type, u8 status) { struct mgmt_ev_auth_failed ev; @@ -3201,7 +3198,7 @@ int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status) if (status) { u8 mgmt_err = mgmt_status(status); mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, - cmd_status_rsp, &mgmt_err); + cmd_status_rsp, &mgmt_err); return 0; } @@ -3214,7 +3211,7 @@ int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status) } mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp, - &match); + &match); if (changed) err = new_settings(hdev, match.sk); @@ -3249,11 +3246,11 @@ int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) u8 mgmt_err = mgmt_status(status); if (enable && test_and_clear_bit(HCI_SSP_ENABLED, - &hdev->dev_flags)) + &hdev->dev_flags)) err = new_settings(hdev, NULL); - mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, - cmd_status_rsp, &mgmt_err); + mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, cmd_status_rsp, + &mgmt_err); return err; } @@ -3287,7 +3284,7 @@ static void class_rsp(struct pending_cmd *cmd, void *data) struct cmd_lookup *match = data; cmd_complete(cmd->sk, cmd->index, cmd->opcode, match->mgmt_status, - match->hdev->dev_class, 3); + match->hdev->dev_class, 3); list_del(&cmd->list); @@ -3300,7 +3297,7 @@ static void class_rsp(struct pending_cmd *cmd, void *data) } int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, - u8 status) + u8 status) { struct cmd_lookup match = { NULL, hdev, mgmt_status(status) }; int err = 0; @@ -3312,8 +3309,8 @@ int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, class_rsp, &match); if (!status) - err = mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, - dev_class, 3, NULL); + err = mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, + 3, NULL); if (match.sk) sock_put(match.sk); @@ -3347,19 +3344,19 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) if (status) { err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, - mgmt_status(status)); + mgmt_status(status)); goto failed; } err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, &ev, - sizeof(ev)); + sizeof(ev)); if (err < 0) goto failed; send_event: if (changed) err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, - sizeof(ev), cmd ? cmd->sk : NULL); + sizeof(ev), cmd ? cmd->sk : NULL); update_eir(hdev); @@ -3370,7 +3367,7 @@ failed: } int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, - u8 *randomizer, u8 status) + u8 *randomizer, u8 status) { struct pending_cmd *cmd; int err; @@ -3382,9 +3379,8 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, return -ENOENT; if (status) { - err = cmd_status(cmd->sk, hdev->id, - MGMT_OP_READ_LOCAL_OOB_DATA, - mgmt_status(status)); + err = cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, + mgmt_status(status)); } else { struct mgmt_rp_read_local_oob_data rp; @@ -3392,8 +3388,8 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer)); err = cmd_complete(cmd->sk, hdev->id, - MGMT_OP_READ_LOCAL_OOB_DATA, - 0, &rp, sizeof(rp)); + MGMT_OP_READ_LOCAL_OOB_DATA, 0, &rp, + sizeof(rp)); } mgmt_pending_remove(cmd); @@ -3411,11 +3407,11 @@ int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) u8 mgmt_err = mgmt_status(status); if (enable && test_and_clear_bit(HCI_LE_ENABLED, - &hdev->dev_flags)) - err = new_settings(hdev, NULL); + &hdev->dev_flags)) + err = new_settings(hdev, NULL); mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, - cmd_status_rsp, &mgmt_err); + cmd_status_rsp, &mgmt_err); return err; } @@ -3440,8 +3436,8 @@ int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) } int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 *dev_class, s8 rssi, - u8 cfm_name, u8 ssp, u8 *eir, u16 eir_len) + u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 + ssp, u8 *eir, u16 eir_len) { char buf[512]; struct mgmt_ev_device_found *ev = (void *) buf; @@ -3466,7 +3462,7 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV)) eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, - dev_class, 3); + dev_class, 3); put_unaligned_le16(eir_len, &ev->eir_len); @@ -3476,7 +3472,7 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, } int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, s8 rssi, u8 *name, u8 name_len) + u8 addr_type, s8 rssi, u8 *name, u8 name_len) { struct mgmt_ev_device_found *ev; char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2]; @@ -3491,12 +3487,12 @@ int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, ev->rssi = rssi; eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name, - name_len); + name_len); put_unaligned_le16(eir_len, &ev->eir_len); return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, - sizeof(*ev) + eir_len, NULL); + sizeof(*ev) + eir_len, NULL); } int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status) @@ -3514,7 +3510,7 @@ int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status) type = hdev->discovery.type; err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status), - &type, sizeof(type)); + &type, sizeof(type)); mgmt_pending_remove(cmd); return err; @@ -3530,8 +3526,7 @@ int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status) return -ENOENT; err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status), - &hdev->discovery.type, - sizeof(hdev->discovery.type)); + &hdev->discovery.type, sizeof(hdev->discovery.type)); mgmt_pending_remove(cmd); return err; @@ -3552,8 +3547,8 @@ int mgmt_discovering(struct hci_dev *hdev, u8 discovering) if (cmd != NULL) { u8 type = hdev->discovery.type; - cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, - &type, sizeof(type)); + cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, &type, + sizeof(type)); mgmt_pending_remove(cmd); } @@ -3575,7 +3570,7 @@ int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) ev.addr.type = type; return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); + cmd ? cmd->sk : NULL); } int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) @@ -3589,7 +3584,7 @@ int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) ev.addr.type = type; return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); + cmd ? cmd->sk : NULL); } module_param(enable_hs, bool, 0644); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 75937d73d8ae..8f56282c247d 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -264,7 +264,7 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send) clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->flags); mgmt_auth_failed(conn->hcon->hdev, conn->dst, hcon->type, - hcon->dst_type, reason); + hcon->dst_type, reason); if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) { cancel_delayed_work_sync(&conn->security_timer); @@ -384,12 +384,11 @@ static void confirm_work(struct work_struct *work) if (conn->hcon->out) ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, 0, - conn->src, conn->hcon->dst_type, conn->dst, - res); + conn->src, conn->hcon->dst_type, conn->dst, res); else ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, - conn->hcon->dst_type, conn->dst, 0, conn->src, - res); + conn->hcon->dst_type, conn->dst, 0, conn->src, + res); if (ret) { reason = SMP_UNSPECIFIED; goto error; @@ -424,12 +423,10 @@ static void random_work(struct work_struct *work) if (hcon->out) ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, 0, - conn->src, hcon->dst_type, conn->dst, - res); + conn->src, hcon->dst_type, conn->dst, res); else ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, - hcon->dst_type, conn->dst, 0, conn->src, - res); + hcon->dst_type, conn->dst, 0, conn->src, res); if (ret) { reason = SMP_UNSPECIFIED; goto error; @@ -454,7 +451,7 @@ static void random_work(struct work_struct *work) swap128(key, stk); memset(stk + smp->enc_key_size, 0, - SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); + SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) { reason = SMP_UNSPECIFIED; @@ -480,8 +477,8 @@ static void random_work(struct work_struct *work) SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); hci_add_ltk(hcon->hdev, conn->dst, hcon->dst_type, - HCI_SMP_STK_SLAVE, 0, 0, stk, - smp->enc_key_size, ediv, rand); + HCI_SMP_STK_SLAVE, 0, 0, stk, smp->enc_key_size, + ediv, rand); } return; @@ -829,8 +826,8 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) hci_dev_lock(hdev); authenticated = (conn->hcon->sec_level == BT_SECURITY_HIGH); hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type, - HCI_SMP_LTK, 1, authenticated, smp->tk, - smp->enc_key_size, rp->ediv, rp->rand); + HCI_SMP_LTK, 1, authenticated, smp->tk, smp->enc_key_size, + rp->ediv, rp->rand); smp_distribute_keys(conn, 1); hci_dev_unlock(hdev); @@ -954,9 +951,8 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) authenticated = hcon->sec_level == BT_SECURITY_HIGH; hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type, - HCI_SMP_LTK_SLAVE, 1, authenticated, - enc.ltk, smp->enc_key_size, - ediv, ident.rand); + HCI_SMP_LTK_SLAVE, 1, authenticated, + enc.ltk, smp->enc_key_size, ediv, ident.rand); ident.ediv = cpu_to_le16(ediv); -- cgit From 82492a355fac112908271faa74f473a38c1fb647 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Wed, 7 Mar 2012 02:06:24 +0000 Subject: af_iucv: add shutdown for HS transport AF_IUCV sockets offer a shutdown function. This patch makes sure shutdown works for HS transport as well. Signed-off-by: Ursula Braun Signed-off-by: Frank Blaschka Signed-off-by: David S. Miller --- include/net/iucv/af_iucv.h | 1 + net/iucv/af_iucv.c | 79 ++++++++++++++++++++++++++++++---------------- 2 files changed, 53 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/net/iucv/af_iucv.h b/include/net/iucv/af_iucv.h index 2e1d5ecc2d1b..cc7c19732389 100644 --- a/include/net/iucv/af_iucv.h +++ b/include/net/iucv/af_iucv.h @@ -62,6 +62,7 @@ struct sock_msg_q { #define AF_IUCV_FLAG_SYN 0x2 #define AF_IUCV_FLAG_FIN 0x4 #define AF_IUCV_FLAG_WIN 0x8 +#define AF_IUCV_FLAG_SHT 0x10 struct af_iucv_trans_hdr { u16 magic; diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index 31537c5eb17e..07d7d55a1b93 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -165,8 +165,6 @@ static int afiucv_pm_freeze(struct device *dev) read_lock(&iucv_sk_list.lock); sk_for_each(sk, node, &iucv_sk_list.head) { iucv = iucv_sk(sk); - skb_queue_purge(&iucv->send_skb_q); - skb_queue_purge(&iucv->backlog_skb_q); switch (sk->sk_state) { case IUCV_DISCONN: case IUCV_CLOSING: @@ -405,7 +403,19 @@ static struct sock *__iucv_get_sock_by_name(char *nm) static void iucv_sock_destruct(struct sock *sk) { skb_queue_purge(&sk->sk_receive_queue); - skb_queue_purge(&sk->sk_write_queue); + skb_queue_purge(&sk->sk_error_queue); + + sk_mem_reclaim(sk); + + if (!sock_flag(sk, SOCK_DEAD)) { + pr_err("Attempt to release alive iucv socket %p\n", sk); + return; + } + + WARN_ON(atomic_read(&sk->sk_rmem_alloc)); + WARN_ON(atomic_read(&sk->sk_wmem_alloc)); + WARN_ON(sk->sk_wmem_queued); + WARN_ON(sk->sk_forward_alloc); } /* Cleanup Listen */ @@ -1342,6 +1352,8 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock, rlen = skb->len; /* real length of skb */ copied = min_t(unsigned int, rlen, len); + if (!rlen) + sk->sk_shutdown = sk->sk_shutdown | RCV_SHUTDOWN; cskb = skb; if (skb_copy_datagram_iovec(cskb, 0, msg->msg_iov, copied)) { @@ -1493,42 +1505,47 @@ static int iucv_sock_shutdown(struct socket *sock, int how) lock_sock(sk); switch (sk->sk_state) { + case IUCV_LISTEN: case IUCV_DISCONN: case IUCV_CLOSING: case IUCV_CLOSED: err = -ENOTCONN; goto fail; - default: - sk->sk_shutdown |= how; break; } if (how == SEND_SHUTDOWN || how == SHUTDOWN_MASK) { - txmsg.class = 0; - txmsg.tag = 0; - err = pr_iucv->message_send(iucv->path, &txmsg, IUCV_IPRMDATA, - 0, (void *) iprm_shutdown, 8); - if (err) { - switch (err) { - case 1: - err = -ENOTCONN; - break; - case 2: - err = -ECONNRESET; - break; - default: - err = -ENOTCONN; - break; + if (iucv->transport == AF_IUCV_TRANS_IUCV) { + txmsg.class = 0; + txmsg.tag = 0; + err = pr_iucv->message_send(iucv->path, &txmsg, + IUCV_IPRMDATA, 0, (void *) iprm_shutdown, 8); + if (err) { + switch (err) { + case 1: + err = -ENOTCONN; + break; + case 2: + err = -ECONNRESET; + break; + default: + err = -ENOTCONN; + break; + } } - } + } else + iucv_send_ctrl(sk, AF_IUCV_FLAG_SHT); } + sk->sk_shutdown |= how; if (how == RCV_SHUTDOWN || how == SHUTDOWN_MASK) { - err = pr_iucv->path_quiesce(iucv->path, NULL); - if (err) - err = -ENOTCONN; - + if (iucv->transport == AF_IUCV_TRANS_IUCV) { + err = pr_iucv->path_quiesce(iucv->path, NULL); + if (err) + err = -ENOTCONN; +/* skb_queue_purge(&sk->sk_receive_queue); */ + } skb_queue_purge(&sk->sk_receive_queue); } @@ -2066,8 +2083,13 @@ static int afiucv_hs_callback_rx(struct sock *sk, struct sk_buff *skb) return NET_RX_SUCCESS; } + if (sk->sk_shutdown & RCV_SHUTDOWN) { + kfree_skb(skb); + return NET_RX_SUCCESS; + } + /* write stuff from iucv_msg to skb cb */ - if (skb->len <= sizeof(struct af_iucv_trans_hdr)) { + if (skb->len < sizeof(struct af_iucv_trans_hdr)) { kfree_skb(skb); return NET_RX_SUCCESS; } @@ -2173,7 +2195,10 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, kfree_skb(skb); break; } - /* fall through */ + /* fall through and receive non-zero length data */ + case (AF_IUCV_FLAG_SHT): + /* shutdown request */ + /* fall through and receive zero length data */ case 0: /* plain data frame */ memcpy(CB_TRGCLS(skb), &trans_hdr->iucv_hdr.class, -- cgit From 079c9534a96da9a85a2a2f9715851050fbfbf749 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 28 Feb 2012 14:49:23 +0000 Subject: vt:tackle kbd_table Keyboard struct lifetime is easy, but the locking is not and is completely ignored by the existing code. Tackle this one head on - Make the kbd_table private so we can run down all direct users - Hoick the relevant ioctl handlers into the keyboard layer - Lock them with the keyboard lock so they don't change mid keypress - Add helpers for things like console stop/start so we isolate the poking around properly - Tweak the braille console so it still builds There are a couple of FIXME locking cases left for ioctls that are so hideous they should be addressed in a later patch. After this patch the kbd_table is private and all the keyboard jiggery pokery is in one place. This update fixes speakup and also a memory leak in the original. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/braille/braille_console.c | 9 +- drivers/staging/speakup/main.c | 8 +- drivers/tty/sysrq.c | 6 +- drivers/tty/vt/keyboard.c | 621 +++++++++++++++++++++++- drivers/tty/vt/selection.c | 9 +- drivers/tty/vt/vt.c | 27 +- drivers/tty/vt/vt_ioctl.c | 325 +------------ include/linux/kbd_kern.h | 7 +- include/linux/keyboard.h | 2 - include/linux/vt_kern.h | 23 + 10 files changed, 660 insertions(+), 377 deletions(-) (limited to 'include') diff --git a/drivers/accessibility/braille/braille_console.c b/drivers/accessibility/braille/braille_console.c index c339a0880e6e..d21167bfc865 100644 --- a/drivers/accessibility/braille/braille_console.c +++ b/drivers/accessibility/braille/braille_console.c @@ -244,16 +244,13 @@ static int keyboard_notifier_call(struct notifier_block *blk, switch (val) { case KVAL(K_CAPS): - on_off = vc_kbd_led(kbd_table + fg_console, - VC_CAPSLOCK); + on_off = vt_get_leds(fg_console, VC_CAPSLOCK); break; case KVAL(K_NUM): - on_off = vc_kbd_led(kbd_table + fg_console, - VC_NUMLOCK); + on_off = vt_get_leds(fg_console, VC_NUMLOCK); break; case KVAL(K_HOLD): - on_off = vc_kbd_led(kbd_table + fg_console, - VC_SCROLLOCK); + on_off = vt_get_leds(fg_console, VC_SCROLLOCK); break; } if (on_off == 1) diff --git a/drivers/staging/speakup/main.c b/drivers/staging/speakup/main.c index c7b03f0ef2dd..92b34e29ad06 100644 --- a/drivers/staging/speakup/main.c +++ b/drivers/staging/speakup/main.c @@ -1731,15 +1731,15 @@ static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag) switch (value) { case KVAL(K_CAPS): label = msg_get(MSG_KEYNAME_CAPSLOCK); - on_off = (vc_kbd_led(kbd_table + vc->vc_num, VC_CAPSLOCK)); + on_off = vt_get_leds(fg_console, VC_CAPSLOCK); break; case KVAL(K_NUM): label = msg_get(MSG_KEYNAME_NUMLOCK); - on_off = (vc_kbd_led(kbd_table + vc->vc_num, VC_NUMLOCK)); + on_off = vt_get_leds(fg_console, VC_NUMLOCK); break; case KVAL(K_HOLD): label = msg_get(MSG_KEYNAME_SCROLLLOCK); - on_off = (vc_kbd_led(kbd_table + vc->vc_num, VC_SCROLLOCK)); + on_off = vt_get_leds(fg_console, VC_SCROLLOCK); if (speakup_console[vc->vc_num]) speakup_console[vc->vc_num]->tty_stopped = on_off; break; @@ -2020,7 +2020,7 @@ speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym, if (type >= 0xf0) type -= 0xf0; if (type == KT_PAD - && (vc_kbd_led(kbd_table + fg_console, VC_NUMLOCK))) { + && (vt_get_leds(fg_console, VC_NUMLOCK))) { if (up_flag) { spk_keydown = 0; goto out; diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 8db9125133b8..ecb8e2203ac8 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -110,11 +110,9 @@ static struct sysrq_key_op sysrq_SAK_op = { #ifdef CONFIG_VT static void sysrq_handle_unraw(int key) { - struct kbd_struct *kbd = &kbd_table[fg_console]; - - if (kbd) - kbd->kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE; + vt_reset_unicode(fg_console); } + static struct sysrq_key_op sysrq_unraw_op = { .handler = sysrq_handle_unraw, .help_msg = "unRaw", diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 898e359c5424..70d0593d3bc6 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -68,8 +68,6 @@ extern void ctrl_alt_del(void); #define KBD_DEFLOCK 0 -void compute_shiftstate(void); - /* * Handler Tables. */ @@ -100,35 +98,29 @@ static fn_handler_fn *fn_handler[] = { FN_HANDLERS }; * Variables exported for vt_ioctl.c */ -/* maximum values each key_handler can handle */ -const int max_vals[] = { - 255, ARRAY_SIZE(func_table) - 1, ARRAY_SIZE(fn_handler) - 1, NR_PAD - 1, - NR_DEAD - 1, 255, 3, NR_SHIFT - 1, 255, NR_ASCII - 1, NR_LOCK - 1, - 255, NR_LOCK - 1, 255, NR_BRL - 1 -}; - -const int NR_TYPES = ARRAY_SIZE(max_vals); - -struct kbd_struct kbd_table[MAX_NR_CONSOLES]; -EXPORT_SYMBOL_GPL(kbd_table); -static struct kbd_struct *kbd = kbd_table; - struct vt_spawn_console vt_spawn_con = { .lock = __SPIN_LOCK_UNLOCKED(vt_spawn_con.lock), .pid = NULL, .sig = 0, }; -/* - * Variables exported for vt.c - */ - -int shift_state = 0; /* * Internal Data. */ +static struct kbd_struct kbd_table[MAX_NR_CONSOLES]; +static struct kbd_struct *kbd = kbd_table; + +/* maximum values each key_handler can handle */ +static const int max_vals[] = { + 255, ARRAY_SIZE(func_table) - 1, ARRAY_SIZE(fn_handler) - 1, NR_PAD - 1, + NR_DEAD - 1, 255, 3, NR_SHIFT - 1, 255, NR_ASCII - 1, NR_LOCK - 1, + 255, NR_LOCK - 1, 255, NR_BRL - 1 +}; + +static const int NR_TYPES = ARRAY_SIZE(max_vals); + static struct input_handler kbd_handler; static DEFINE_SPINLOCK(kbd_event_lock); static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */ @@ -138,6 +130,8 @@ static int npadch = -1; /* -1 or number assembled on pad */ static unsigned int diacr; static char rep; /* flag telling character repeat */ +static int shift_state = 0; + static unsigned char ledstate = 0xff; /* undefined */ static unsigned char ledioctl; @@ -188,7 +182,7 @@ static int getkeycode_helper(struct input_handle *handle, void *data) return d->error == 0; /* stop as soon as we successfully get one */ } -int getkeycode(unsigned int scancode) +static int getkeycode(unsigned int scancode) { struct getset_keycode_data d = { .ke = { @@ -215,7 +209,7 @@ static int setkeycode_helper(struct input_handle *handle, void *data) return d->error == 0; /* stop as soon as we successfully set one */ } -int setkeycode(unsigned int scancode, unsigned int keycode) +static int setkeycode(unsigned int scancode, unsigned int keycode) { struct getset_keycode_data d = { .ke = { @@ -383,9 +377,11 @@ static void to_utf8(struct vc_data *vc, uint c) /* * Called after returning from RAW mode or when changing consoles - recompute * shift_down[] and shift_state from key_down[] maybe called when keymap is - * undefined, so that shiftkey release is seen + * undefined, so that shiftkey release is seen. The caller must hold the + * kbd_event_lock. */ -void compute_shiftstate(void) + +static void do_compute_shiftstate(void) { unsigned int i, j, k, sym, val; @@ -418,6 +414,15 @@ void compute_shiftstate(void) } } +/* We still have to export this method to vt.c */ +void compute_shiftstate(void) +{ + unsigned long flags; + spin_lock_irqsave(&kbd_event_lock, flags); + do_compute_shiftstate(); + spin_unlock_irqrestore(&kbd_event_lock, flags); +} + /* * We have a combining character DIACR here, followed by the character CH. * If the combination occurs in the table, return the corresponding value. @@ -637,7 +642,7 @@ static void fn_SAK(struct vc_data *vc) static void fn_null(struct vc_data *vc) { - compute_shiftstate(); + do_compute_shiftstate(); } /* @@ -990,6 +995,8 @@ unsigned char getledstate(void) void setledstate(struct kbd_struct *kbd, unsigned int led) { + unsigned long flags; + spin_lock_irqsave(&kbd_event_lock, flags); if (!(led & ~7)) { ledioctl = led; kbd->ledmode = LED_SHOW_IOCTL; @@ -997,6 +1004,7 @@ void setledstate(struct kbd_struct *kbd, unsigned int led) kbd->ledmode = LED_SHOW_FLAGS; set_leds(); + spin_unlock_irqrestore(&kbd_event_lock, flags); } static inline unsigned char getleds(void) @@ -1036,6 +1044,75 @@ static int kbd_update_leds_helper(struct input_handle *handle, void *data) return 0; } +/** + * vt_get_leds - helper for braille console + * @console: console to read + * @flag: flag we want to check + * + * Check the status of a keyboard led flag and report it back + */ +int vt_get_leds(int console, int flag) +{ + unsigned long flags; + struct kbd_struct * kbd = kbd_table + console; + int ret; + + spin_lock_irqsave(&kbd_event_lock, flags); + ret = vc_kbd_led(kbd, flag); + spin_unlock_irqrestore(&kbd_event_lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(vt_get_leds); + +/** + * vt_set_led_state - set LED state of a console + * @console: console to set + * @leds: LED bits + * + * Set the LEDs on a console. This is a wrapper for the VT layer + * so that we can keep kbd knowledge internal + */ +void vt_set_led_state(int console, int leds) +{ + struct kbd_struct * kbd = kbd_table + console; + setledstate(kbd, leds); +} + +/** + * vt_kbd_con_start - Keyboard side of console start + * @console: console + * + * Handle console start. This is a wrapper for the VT layer + * so that we can keep kbd knowledge internal + */ +void vt_kbd_con_start(int console) +{ + struct kbd_struct * kbd = kbd_table + console; + unsigned long flags; + spin_lock_irqsave(&kbd_event_lock, flags); + clr_vc_kbd_led(kbd, VC_SCROLLOCK); + set_leds(); + spin_unlock_irqrestore(&kbd_event_lock, flags); +} + +/** + * vt_kbd_con_stop - Keyboard side of console stop + * @console: console + * + * Handle console stop. This is a wrapper for the VT layer + * so that we can keep kbd knowledge internal + */ +void vt_kbd_con_stop(int console) +{ + struct kbd_struct * kbd = kbd_table + console; + unsigned long flags; + spin_lock_irqsave(&kbd_event_lock, flags); + set_vc_kbd_led(kbd, VC_SCROLLOCK); + set_leds(); + spin_unlock_irqrestore(&kbd_event_lock, flags); +} + /* * This is the tasklet that updates LED state on all keyboards * attached to the box. The reason we use tasklet is that we @@ -1255,7 +1332,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) if (rc == NOTIFY_STOP || !key_map) { atomic_notifier_call_chain(&keyboard_notifier_list, KBD_UNBOUND_KEYCODE, ¶m); - compute_shiftstate(); + do_compute_shiftstate(); kbd->slockstate = 0; return; } @@ -1615,3 +1692,495 @@ int vt_do_diacrit(unsigned int cmd, void __user *up, int perm) } return ret; } + +/** + * vt_do_kdskbmode - set keyboard mode ioctl + * @console: the console to use + * @arg: the requested mode + * + * Update the keyboard mode bits while holding the correct locks. + * Return 0 for success or an error code. + */ +int vt_do_kdskbmode(int console, unsigned int arg) +{ + struct kbd_struct * kbd = kbd_table + console; + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&kbd_event_lock, flags); + switch(arg) { + case K_RAW: + kbd->kbdmode = VC_RAW; + break; + case K_MEDIUMRAW: + kbd->kbdmode = VC_MEDIUMRAW; + break; + case K_XLATE: + kbd->kbdmode = VC_XLATE; + do_compute_shiftstate(); + break; + case K_UNICODE: + kbd->kbdmode = VC_UNICODE; + do_compute_shiftstate(); + break; + case K_OFF: + kbd->kbdmode = VC_OFF; + break; + default: + ret = -EINVAL; + } + spin_unlock_irqrestore(&kbd_event_lock, flags); + return ret; +} + +/** + * vt_do_kdskbmeta - set keyboard meta state + * @console: the console to use + * @arg: the requested meta state + * + * Update the keyboard meta bits while holding the correct locks. + * Return 0 for success or an error code. + */ +int vt_do_kdskbmeta(int console, unsigned int arg) +{ + struct kbd_struct * kbd = kbd_table + console; + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&kbd_event_lock, flags); + switch(arg) { + case K_METABIT: + clr_vc_kbd_mode(kbd, VC_META); + break; + case K_ESCPREFIX: + set_vc_kbd_mode(kbd, VC_META); + break; + default: + ret = -EINVAL; + } + spin_unlock_irqrestore(&kbd_event_lock, flags); + return ret; +} + +int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc, + int perm) +{ + struct kbkeycode tmp; + int kc = 0; + + if (copy_from_user(&tmp, user_kbkc, sizeof(struct kbkeycode))) + return -EFAULT; + switch (cmd) { + case KDGETKEYCODE: + kc = getkeycode(tmp.scancode); + if (kc >= 0) + kc = put_user(kc, &user_kbkc->keycode); + break; + case KDSETKEYCODE: + if (!perm) + return -EPERM; + kc = setkeycode(tmp.scancode, tmp.keycode); + break; + } + return kc; +} + +#define i (tmp.kb_index) +#define s (tmp.kb_table) +#define v (tmp.kb_value) + +int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, + int console) +{ + struct kbd_struct * kbd = kbd_table + console; + struct kbentry tmp; + ushort *key_map, *new_map, val, ov; + unsigned long flags; + + if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry))) + return -EFAULT; + + if (!capable(CAP_SYS_TTY_CONFIG)) + perm = 0; + + switch (cmd) { + case KDGKBENT: + /* Ensure another thread doesn't free it under us */ + spin_lock_irqsave(&kbd_event_lock, flags); + key_map = key_maps[s]; + if (key_map) { + val = U(key_map[i]); + if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES) + val = K_HOLE; + } else + val = (i ? K_HOLE : K_NOSUCHMAP); + spin_unlock_irqrestore(&kbd_event_lock, flags); + return put_user(val, &user_kbe->kb_value); + case KDSKBENT: + if (!perm) + return -EPERM; + if (!i && v == K_NOSUCHMAP) { + spin_lock_irqsave(&kbd_event_lock, flags); + /* deallocate map */ + key_map = key_maps[s]; + if (s && key_map) { + key_maps[s] = NULL; + if (key_map[0] == U(K_ALLOCATED)) { + kfree(key_map); + keymap_count--; + } + } + spin_unlock_irqrestore(&kbd_event_lock, flags); + break; + } + + if (KTYP(v) < NR_TYPES) { + if (KVAL(v) > max_vals[KTYP(v)]) + return -EINVAL; + } else + if (kbd->kbdmode != VC_UNICODE) + return -EINVAL; + + /* ++Geert: non-PC keyboards may generate keycode zero */ +#if !defined(__mc68000__) && !defined(__powerpc__) + /* assignment to entry 0 only tests validity of args */ + if (!i) + break; +#endif + + new_map = kmalloc(sizeof(plain_map), GFP_KERNEL); + if (!new_map) + return -ENOMEM; + spin_lock_irqsave(&kbd_event_lock, flags); + key_map = key_maps[s]; + if (key_map == NULL) { + int j; + + if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && + !capable(CAP_SYS_RESOURCE)) { + spin_unlock_irqrestore(&kbd_event_lock, flags); + kfree(new_map); + return -EPERM; + } + key_maps[s] = new_map; + key_map[0] = U(K_ALLOCATED); + for (j = 1; j < NR_KEYS; j++) + key_map[j] = U(K_HOLE); + keymap_count++; + } else + kfree(new_map); + + ov = U(key_map[i]); + if (v == ov) + goto out; + /* + * Attention Key. + */ + if (((ov == K_SAK) || (v == K_SAK)) && !capable(CAP_SYS_ADMIN)) { + spin_unlock_irqrestore(&kbd_event_lock, flags); + return -EPERM; + } + key_map[i] = U(v); + if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT)) + do_compute_shiftstate(); +out: + spin_unlock_irqrestore(&kbd_event_lock, flags); + break; + } + return 0; +} +#undef i +#undef s +#undef v + +/* FIXME: This one needs untangling and locking */ +int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) +{ + struct kbsentry *kbs; + char *p; + u_char *q; + u_char __user *up; + int sz; + int delta; + char *first_free, *fj, *fnw; + int i, j, k; + int ret; + + if (!capable(CAP_SYS_TTY_CONFIG)) + perm = 0; + + kbs = kmalloc(sizeof(*kbs), GFP_KERNEL); + if (!kbs) { + ret = -ENOMEM; + goto reterr; + } + + /* we mostly copy too much here (512bytes), but who cares ;) */ + if (copy_from_user(kbs, user_kdgkb, sizeof(struct kbsentry))) { + ret = -EFAULT; + goto reterr; + } + kbs->kb_string[sizeof(kbs->kb_string)-1] = '\0'; + i = kbs->kb_func; + + switch (cmd) { + case KDGKBSENT: + sz = sizeof(kbs->kb_string) - 1; /* sz should have been + a struct member */ + up = user_kdgkb->kb_string; + p = func_table[i]; + if(p) + for ( ; *p && sz; p++, sz--) + if (put_user(*p, up++)) { + ret = -EFAULT; + goto reterr; + } + if (put_user('\0', up)) { + ret = -EFAULT; + goto reterr; + } + kfree(kbs); + return ((p && *p) ? -EOVERFLOW : 0); + case KDSKBSENT: + if (!perm) { + ret = -EPERM; + goto reterr; + } + + q = func_table[i]; + first_free = funcbufptr + (funcbufsize - funcbufleft); + for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) + ; + if (j < MAX_NR_FUNC) + fj = func_table[j]; + else + fj = first_free; + + delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string); + if (delta <= funcbufleft) { /* it fits in current buf */ + if (j < MAX_NR_FUNC) { + memmove(fj + delta, fj, first_free - fj); + for (k = j; k < MAX_NR_FUNC; k++) + if (func_table[k]) + func_table[k] += delta; + } + if (!q) + func_table[i] = fj; + funcbufleft -= delta; + } else { /* allocate a larger buffer */ + sz = 256; + while (sz < funcbufsize - funcbufleft + delta) + sz <<= 1; + fnw = kmalloc(sz, GFP_KERNEL); + if(!fnw) { + ret = -ENOMEM; + goto reterr; + } + + if (!q) + func_table[i] = fj; + if (fj > funcbufptr) + memmove(fnw, funcbufptr, fj - funcbufptr); + for (k = 0; k < j; k++) + if (func_table[k]) + func_table[k] = fnw + (func_table[k] - funcbufptr); + + if (first_free > fj) { + memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj); + for (k = j; k < MAX_NR_FUNC; k++) + if (func_table[k]) + func_table[k] = fnw + (func_table[k] - funcbufptr) + delta; + } + if (funcbufptr != func_buf) + kfree(funcbufptr); + funcbufptr = fnw; + funcbufleft = funcbufleft - delta + sz - funcbufsize; + funcbufsize = sz; + } + strcpy(func_table[i], kbs->kb_string); + break; + } + ret = 0; +reterr: + kfree(kbs); + return ret; +} + +int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm) +{ + struct kbd_struct * kbd = kbd_table + console; + unsigned long flags; + unsigned char ucval; + + switch(cmd) { + /* the ioctls below read/set the flags usually shown in the leds */ + /* don't use them - they will go away without warning */ + case KDGKBLED: + spin_lock_irqsave(&kbd_event_lock, flags); + ucval = kbd->ledflagstate | (kbd->default_ledflagstate << 4); + spin_unlock_irqrestore(&kbd_event_lock, flags); + return put_user(ucval, (char __user *)arg); + + case KDSKBLED: + if (!perm) + return -EPERM; + if (arg & ~0x77) + return -EINVAL; + spin_lock_irqsave(&kbd_event_lock, flags); + kbd->ledflagstate = (arg & 7); + kbd->default_ledflagstate = ((arg >> 4) & 7); + set_leds(); + spin_unlock_irqrestore(&kbd_event_lock, flags); + break; + + /* the ioctls below only set the lights, not the functions */ + /* for those, see KDGKBLED and KDSKBLED above */ + case KDGETLED: + ucval = getledstate(); + return put_user(ucval, (char __user *)arg); + + case KDSETLED: + if (!perm) + return -EPERM; + setledstate(kbd, arg); + return 0; + } + return -ENOIOCTLCMD; +} + +int vt_do_kdgkbmode(int console) +{ + struct kbd_struct * kbd = kbd_table + console; + /* This is a spot read so needs no locking */ + switch (kbd->kbdmode) { + case VC_RAW: + return K_RAW; + case VC_MEDIUMRAW: + return K_MEDIUMRAW; + case VC_UNICODE: + return K_UNICODE; + case VC_OFF: + return K_OFF; + default: + return K_XLATE; + } +} + +/** + * vt_do_kdgkbmeta - report meta status + * @console: console to report + * + * Report the meta flag status of this console + */ +int vt_do_kdgkbmeta(int console) +{ + struct kbd_struct * kbd = kbd_table + console; + /* Again a spot read so no locking */ + return vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT; +} + +/** + * vt_reset_unicode - reset the unicode status + * @console: console being reset + * + * Restore the unicode console state to its default + */ +void vt_reset_unicode(int console) +{ + unsigned long flags; + + spin_lock_irqsave(&kbd_event_lock, flags); + kbd_table[console].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE; + spin_unlock_irqrestore(&kbd_event_lock, flags); +} + +/** + * vt_get_shiftstate - shift bit state + * + * Report the shift bits from the keyboard state. We have to export + * this to support some oddities in the vt layer. + */ +int vt_get_shift_state(void) +{ + /* Don't lock as this is a transient report */ + return shift_state; +} + +/** + * vt_reset_keyboard - reset keyboard state + * @console: console to reset + * + * Reset the keyboard bits for a console as part of a general console + * reset event + */ +void vt_reset_keyboard(int console) +{ + struct kbd_struct * kbd = kbd_table + console; + unsigned long flags; + + spin_lock_irqsave(&kbd_event_lock, flags); + set_vc_kbd_mode(kbd, VC_REPEAT); + clr_vc_kbd_mode(kbd, VC_CKMODE); + clr_vc_kbd_mode(kbd, VC_APPLIC); + clr_vc_kbd_mode(kbd, VC_CRLF); + kbd->lockstate = 0; + kbd->slockstate = 0; + kbd->ledmode = LED_SHOW_FLAGS; + kbd->ledflagstate = kbd->default_ledflagstate; + /* do not do set_leds here because this causes an endless tasklet loop + when the keyboard hasn't been initialized yet */ + spin_unlock_irqrestore(&kbd_event_lock, flags); +} + +/** + * vt_get_kbd_mode_bit - read keyboard status bits + * @console: console to read from + * @bit: mode bit to read + * + * Report back a vt mode bit. We do this without locking so the + * caller must be sure that there are no synchronization needs + */ + +int vt_get_kbd_mode_bit(int console, int bit) +{ + struct kbd_struct * kbd = kbd_table + console; + return vc_kbd_mode(kbd, bit); +} + +/** + * vt_set_kbd_mode_bit - read keyboard status bits + * @console: console to read from + * @bit: mode bit to read + * + * Set a vt mode bit. We do this without locking so the + * caller must be sure that there are no synchronization needs + */ + +void vt_set_kbd_mode_bit(int console, int bit) +{ + struct kbd_struct * kbd = kbd_table + console; + unsigned long flags; + + spin_lock_irqsave(&kbd_event_lock, flags); + set_vc_kbd_mode(kbd, bit); + spin_unlock_irqrestore(&kbd_event_lock, flags); +} + +/** + * vt_clr_kbd_mode_bit - read keyboard status bits + * @console: console to read from + * @bit: mode bit to read + * + * Report back a vt mode bit. We do this without locking so the + * caller must be sure that there are no synchronization needs + */ + +void vt_clr_kbd_mode_bit(int console, int bit) +{ + struct kbd_struct * kbd = kbd_table + console; + unsigned long flags; + + spin_lock_irqsave(&kbd_event_lock, flags); + clr_vc_kbd_mode(kbd, bit); + spin_unlock_irqrestore(&kbd_event_lock, flags); +} diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 7a0a12ae5458..738e45a35131 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -30,6 +30,7 @@ extern void poke_blanked_console(void); +/* FIXME: all this needs locking */ /* Variables for selection control. */ /* Use a dynamic buffer, instead of static (Dec 1994) */ struct vc_data *sel_cons; /* must not be deallocated */ @@ -138,7 +139,7 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t char *bp, *obp; int i, ps, pe, multiplier; u16 c; - struct kbd_struct *kbd = kbd_table + fg_console; + int mode; poke_blanked_console(); @@ -182,7 +183,11 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t clear_selection(); sel_cons = vc_cons[fg_console].d; } - use_unicode = kbd && kbd->kbdmode == VC_UNICODE; + mode = vt_do_kdgkbmode(fg_console); + if (mode == K_UNICODE) + use_unicode = 1; + else + use_unicode = 0; switch (sel_mode) { diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index e716839fab6e..ecdcc8a8f0ca 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1028,9 +1028,9 @@ void vc_deallocate(unsigned int currcons) * VT102 emulator */ -#define set_kbd(vc, x) set_vc_kbd_mode(kbd_table + (vc)->vc_num, (x)) -#define clr_kbd(vc, x) clr_vc_kbd_mode(kbd_table + (vc)->vc_num, (x)) -#define is_kbd(vc, x) vc_kbd_mode(kbd_table + (vc)->vc_num, (x)) +#define set_kbd(vc, x) vt_set_kbd_mode_bit((vc)->vc_num, (x)) +#define clr_kbd(vc, x) vt_clr_kbd_mode_bit((vc)->vc_num, (x)) +#define is_kbd(vc, x) vt_get_kbd_mode_bit((vc)->vc_num, (x)) #define decarm VC_REPEAT #define decckm VC_CKMODE @@ -1652,16 +1652,7 @@ static void reset_terminal(struct vc_data *vc, int do_clear) vc->vc_deccm = global_cursor_default; vc->vc_decim = 0; - set_kbd(vc, decarm); - clr_kbd(vc, decckm); - clr_kbd(vc, kbdapplic); - clr_kbd(vc, lnm); - kbd_table[vc->vc_num].lockstate = 0; - kbd_table[vc->vc_num].slockstate = 0; - kbd_table[vc->vc_num].ledmode = LED_SHOW_FLAGS; - kbd_table[vc->vc_num].ledflagstate = kbd_table[vc->vc_num].default_ledflagstate; - /* do not do set_leds here because this causes an endless tasklet loop - when the keyboard hasn't been initialized yet */ + vt_reset_keyboard(vc->vc_num); vc->vc_cursor_type = cur_default; vc->vc_complement_mask = vc->vc_s_complement_mask; @@ -1979,7 +1970,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) case 'q': /* DECLL - but only 3 leds */ /* map 0,1,2,3 to 0,1,2,4 */ if (vc->vc_par[0] < 4) - setledstate(kbd_table + vc->vc_num, + vt_set_led_state(vc->vc_num, (vc->vc_par[0] < 3) ? vc->vc_par[0] : 4); return; case 'r': @@ -2642,7 +2633,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) * kernel-internal variable; programs not closely * related to the kernel should not use this. */ - data = shift_state; + data = vt_get_shift_state(); ret = __put_user(data, p); break; case TIOCL_GETMOUSEREPORTING: @@ -2753,8 +2744,7 @@ static void con_stop(struct tty_struct *tty) console_num = tty->index; if (!vc_cons_allocated(console_num)) return; - set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); - set_leds(); + vt_kbd_con_stop(console_num); } /* @@ -2768,8 +2758,7 @@ static void con_start(struct tty_struct *tty) console_num = tty->index; if (!vc_cons_allocated(console_num)) return; - clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); - set_leds(); + vt_kbd_con_start(console_num); } static void con_flush_chars(struct tty_struct *tty) diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 80af0f9bef5b..28ca0aa8664f 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -195,232 +195,7 @@ int vt_waitactive(int n) #define GPLAST 0x3df #define GPNUM (GPLAST - GPFIRST + 1) -#define i (tmp.kb_index) -#define s (tmp.kb_table) -#define v (tmp.kb_value) -static inline int -do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, struct kbd_struct *kbd) -{ - struct kbentry tmp; - ushort *key_map, val, ov; - - if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry))) - return -EFAULT; - - if (!capable(CAP_SYS_TTY_CONFIG)) - perm = 0; - - switch (cmd) { - case KDGKBENT: - key_map = key_maps[s]; - if (key_map) { - val = U(key_map[i]); - if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES) - val = K_HOLE; - } else - val = (i ? K_HOLE : K_NOSUCHMAP); - return put_user(val, &user_kbe->kb_value); - case KDSKBENT: - if (!perm) - return -EPERM; - if (!i && v == K_NOSUCHMAP) { - /* deallocate map */ - key_map = key_maps[s]; - if (s && key_map) { - key_maps[s] = NULL; - if (key_map[0] == U(K_ALLOCATED)) { - kfree(key_map); - keymap_count--; - } - } - break; - } - - if (KTYP(v) < NR_TYPES) { - if (KVAL(v) > max_vals[KTYP(v)]) - return -EINVAL; - } else - if (kbd->kbdmode != VC_UNICODE) - return -EINVAL; - - /* ++Geert: non-PC keyboards may generate keycode zero */ -#if !defined(__mc68000__) && !defined(__powerpc__) - /* assignment to entry 0 only tests validity of args */ - if (!i) - break; -#endif - - if (!(key_map = key_maps[s])) { - int j; - - if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && - !capable(CAP_SYS_RESOURCE)) - return -EPERM; - - key_map = kmalloc(sizeof(plain_map), - GFP_KERNEL); - if (!key_map) - return -ENOMEM; - key_maps[s] = key_map; - key_map[0] = U(K_ALLOCATED); - for (j = 1; j < NR_KEYS; j++) - key_map[j] = U(K_HOLE); - keymap_count++; - } - ov = U(key_map[i]); - if (v == ov) - break; /* nothing to do */ - /* - * Attention Key. - */ - if (((ov == K_SAK) || (v == K_SAK)) && !capable(CAP_SYS_ADMIN)) - return -EPERM; - key_map[i] = U(v); - if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT)) - compute_shiftstate(); - break; - } - return 0; -} -#undef i -#undef s -#undef v - -static inline int -do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc, int perm) -{ - struct kbkeycode tmp; - int kc = 0; - - if (copy_from_user(&tmp, user_kbkc, sizeof(struct kbkeycode))) - return -EFAULT; - switch (cmd) { - case KDGETKEYCODE: - kc = getkeycode(tmp.scancode); - if (kc >= 0) - kc = put_user(kc, &user_kbkc->keycode); - break; - case KDSETKEYCODE: - if (!perm) - return -EPERM; - kc = setkeycode(tmp.scancode, tmp.keycode); - break; - } - return kc; -} - -static inline int -do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) -{ - struct kbsentry *kbs; - char *p; - u_char *q; - u_char __user *up; - int sz; - int delta; - char *first_free, *fj, *fnw; - int i, j, k; - int ret; - - if (!capable(CAP_SYS_TTY_CONFIG)) - perm = 0; - - kbs = kmalloc(sizeof(*kbs), GFP_KERNEL); - if (!kbs) { - ret = -ENOMEM; - goto reterr; - } - - /* we mostly copy too much here (512bytes), but who cares ;) */ - if (copy_from_user(kbs, user_kdgkb, sizeof(struct kbsentry))) { - ret = -EFAULT; - goto reterr; - } - kbs->kb_string[sizeof(kbs->kb_string)-1] = '\0'; - i = kbs->kb_func; - - switch (cmd) { - case KDGKBSENT: - sz = sizeof(kbs->kb_string) - 1; /* sz should have been - a struct member */ - up = user_kdgkb->kb_string; - p = func_table[i]; - if(p) - for ( ; *p && sz; p++, sz--) - if (put_user(*p, up++)) { - ret = -EFAULT; - goto reterr; - } - if (put_user('\0', up)) { - ret = -EFAULT; - goto reterr; - } - kfree(kbs); - return ((p && *p) ? -EOVERFLOW : 0); - case KDSKBSENT: - if (!perm) { - ret = -EPERM; - goto reterr; - } - q = func_table[i]; - first_free = funcbufptr + (funcbufsize - funcbufleft); - for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) - ; - if (j < MAX_NR_FUNC) - fj = func_table[j]; - else - fj = first_free; - - delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string); - if (delta <= funcbufleft) { /* it fits in current buf */ - if (j < MAX_NR_FUNC) { - memmove(fj + delta, fj, first_free - fj); - for (k = j; k < MAX_NR_FUNC; k++) - if (func_table[k]) - func_table[k] += delta; - } - if (!q) - func_table[i] = fj; - funcbufleft -= delta; - } else { /* allocate a larger buffer */ - sz = 256; - while (sz < funcbufsize - funcbufleft + delta) - sz <<= 1; - fnw = kmalloc(sz, GFP_KERNEL); - if(!fnw) { - ret = -ENOMEM; - goto reterr; - } - - if (!q) - func_table[i] = fj; - if (fj > funcbufptr) - memmove(fnw, funcbufptr, fj - funcbufptr); - for (k = 0; k < j; k++) - if (func_table[k]) - func_table[k] = fnw + (func_table[k] - funcbufptr); - - if (first_free > fj) { - memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj); - for (k = j; k < MAX_NR_FUNC; k++) - if (func_table[k]) - func_table[k] = fnw + (func_table[k] - funcbufptr) + delta; - } - if (funcbufptr != func_buf) - kfree(funcbufptr); - funcbufptr = fnw; - funcbufleft = funcbufleft - delta + sz - funcbufsize; - funcbufsize = sz; - } - strcpy(func_table[i], kbs->kb_string); - break; - } - ret = 0; -reterr: - kfree(kbs); - return ret; -} static inline int do_fontx_ioctl(int cmd, struct consolefontdesc __user *user_cfd, int perm, struct console_font_op *op) @@ -497,7 +272,6 @@ int vt_ioctl(struct tty_struct *tty, { struct vc_data *vc = tty->driver_data; struct console_font_op op; /* used in multiple places here */ - struct kbd_struct * kbd; unsigned int console; unsigned char ucval; unsigned int uival; @@ -523,7 +297,6 @@ int vt_ioctl(struct tty_struct *tty, if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG)) perm = 1; - kbd = kbd_table + console; switch (cmd) { case TIOCLINUX: ret = tioclinux(tty, arg); @@ -565,7 +338,8 @@ int vt_ioctl(struct tty_struct *tty, * this is naive. */ ucval = KB_101; - goto setchar; + ret = put_user(ucval, (char __user *)arg); + break; /* * These cannot be implemented on any machine that implements @@ -670,68 +444,25 @@ int vt_ioctl(struct tty_struct *tty, case KDSKBMODE: if (!perm) goto eperm; - switch(arg) { - case K_RAW: - kbd->kbdmode = VC_RAW; - break; - case K_MEDIUMRAW: - kbd->kbdmode = VC_MEDIUMRAW; - break; - case K_XLATE: - kbd->kbdmode = VC_XLATE; - compute_shiftstate(); - break; - case K_UNICODE: - kbd->kbdmode = VC_UNICODE; - compute_shiftstate(); - break; - case K_OFF: - kbd->kbdmode = VC_OFF; - break; - default: - ret = -EINVAL; - goto out; - } - tty_ldisc_flush(tty); + ret = vt_do_kdskbmode(console, arg); + if (ret == 0) + tty_ldisc_flush(tty); break; case KDGKBMODE: - switch (kbd->kbdmode) { - case VC_RAW: - uival = K_RAW; - break; - case VC_MEDIUMRAW: - uival = K_MEDIUMRAW; - break; - case VC_UNICODE: - uival = K_UNICODE; - break; - case VC_OFF: - uival = K_OFF; - break; - default: - uival = K_XLATE; - break; - } - goto setint; + uival = vt_do_kdgkbmode(console); + ret = put_user(uival, (int __user *)arg); + break; /* this could be folded into KDSKBMODE, but for compatibility reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */ case KDSKBMETA: - switch(arg) { - case K_METABIT: - clr_vc_kbd_mode(kbd, VC_META); - break; - case K_ESCPREFIX: - set_vc_kbd_mode(kbd, VC_META); - break; - default: - ret = -EINVAL; - } + ret = vt_do_kdskbmeta(console, arg); break; case KDGKBMETA: - uival = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT); + /* FIXME: should review whether this is worth locking */ + uival = vt_do_kdgkbmeta(console); setint: ret = put_user(uival, (int __user *)arg); break; @@ -740,17 +471,17 @@ int vt_ioctl(struct tty_struct *tty, case KDSETKEYCODE: if(!capable(CAP_SYS_TTY_CONFIG)) perm = 0; - ret = do_kbkeycode_ioctl(cmd, up, perm); + ret = vt_do_kbkeycode_ioctl(cmd, up, perm); break; case KDGKBENT: case KDSKBENT: - ret = do_kdsk_ioctl(cmd, up, perm, kbd); + ret = vt_do_kdsk_ioctl(cmd, up, perm, console); break; case KDGKBSENT: case KDSKBSENT: - ret = do_kdgkb_ioctl(cmd, up, perm); + ret = vt_do_kdgkb_ioctl(cmd, up, perm); break; /* Diacritical processing. Handled in keyboard.c as it has @@ -765,33 +496,10 @@ int vt_ioctl(struct tty_struct *tty, /* the ioctls below read/set the flags usually shown in the leds */ /* don't use them - they will go away without warning */ case KDGKBLED: - ucval = kbd->ledflagstate | (kbd->default_ledflagstate << 4); - goto setchar; - case KDSKBLED: - if (!perm) - goto eperm; - if (arg & ~0x77) { - ret = -EINVAL; - break; - } - kbd->ledflagstate = (arg & 7); - kbd->default_ledflagstate = ((arg >> 4) & 7); - set_leds(); - break; - - /* the ioctls below only set the lights, not the functions */ - /* for those, see KDGKBLED and KDSKBLED above */ case KDGETLED: - ucval = getledstate(); - setchar: - ret = put_user(ucval, (char __user *)arg); - break; - case KDSETLED: - if (!perm) - goto eperm; - setledstate(kbd, arg); + ret = vt_do_kdskled(console, cmd, arg, perm); break; /* @@ -1286,7 +994,7 @@ eperm: void reset_vc(struct vc_data *vc) { vc->vc_mode = KD_TEXT; - kbd_table[vc->vc_num].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE; + vt_reset_unicode(vc->vc_num); vc->vt_mode.mode = VT_AUTO; vc->vt_mode.waitv = 0; vc->vt_mode.relsig = 0; @@ -1309,6 +1017,7 @@ void vc_SAK(struct work_struct *work) console_lock(); vc = vc_con->d; if (vc) { + /* FIXME: review tty ref counting */ tty = vc->port.tty; /* * SAK should also work in all raw modes and reset diff --git a/include/linux/kbd_kern.h b/include/linux/kbd_kern.h index ec2d17bc1f1e..daf4a3a40ee0 100644 --- a/include/linux/kbd_kern.h +++ b/include/linux/kbd_kern.h @@ -7,8 +7,6 @@ extern struct tasklet_struct keyboard_tasklet; -extern int shift_state; - extern char *func_table[MAX_NR_FUNC]; extern char func_buf[]; extern char *funcbufptr; @@ -65,8 +63,6 @@ struct kbd_struct { #define VC_META 4 /* 0 - meta, 1 - meta=prefix with ESC */ }; -extern struct kbd_struct kbd_table[]; - extern int kbd_init(void); extern unsigned char getledstate(void); @@ -79,6 +75,7 @@ extern void (*kbd_ledfunc)(unsigned int led); extern int set_console(int nr); extern void schedule_console_callback(void); +/* FIXME: review locking for vt.c callers */ static inline void set_leds(void) { tasklet_schedule(&keyboard_tasklet); @@ -142,8 +139,6 @@ static inline void chg_vc_kbd_led(struct kbd_struct * kbd, int flag) struct console; -int getkeycode(unsigned int scancode); -int setkeycode(unsigned int scancode, unsigned int keycode); void compute_shiftstate(void); /* defkeymap.c */ diff --git a/include/linux/keyboard.h b/include/linux/keyboard.h index 33a63f62d57f..86e5214ae735 100644 --- a/include/linux/keyboard.h +++ b/include/linux/keyboard.h @@ -24,8 +24,6 @@ #ifdef __KERNEL__ struct notifier_block; -extern const int NR_TYPES; -extern const int max_vals[]; extern unsigned short *key_maps[MAX_NR_KEYMAPS]; extern unsigned short plain_map[NR_KEYS]; diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 5d8726ad5729..e33d77f15bda 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -169,5 +169,28 @@ extern void hide_boot_cursor(bool hide); /* keyboard provided interfaces */ extern int vt_do_diacrit(unsigned int cmd, void __user *up, int eperm); +extern int vt_do_kdskbmode(int console, unsigned int arg); +extern int vt_do_kdskbmeta(int console, unsigned int arg); +extern int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc, + int perm); +extern int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, + int perm, int console); +extern int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, + int perm); +extern int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm); +extern int vt_do_kdgkbmode(int console); +extern int vt_do_kdgkbmeta(int console); +extern void vt_reset_unicode(int console); +extern int vt_get_shift_state(void); +extern void vt_reset_keyboard(int console); +extern int vt_get_leds(int console, int flag); +extern int vt_get_kbd_mode_bit(int console, int bit); +extern void vt_set_kbd_mode_bit(int console, int bit); +extern void vt_clr_kbd_mode_bit(int console, int bit); +extern void vt_set_led_state(int console, int leds); +extern void vt_set_led_state(int console, int leds); +extern void vt_kbd_con_start(int console); +extern void vt_kbd_con_stop(int console); + #endif /* _VT_KERN_H */ -- cgit From f44d4eb54432a0109ff15b2669c91f061428ff39 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Wed, 7 Mar 2012 21:31:13 +0100 Subject: mac80211: update ieee80211_tx_rate_control kerneldoc * add entry for rate_idx_mcs_mask * fix order of entries to represent the structs' order Signed-off-by: Simon Wunderlich Signed-off-by: John W. Linville --- include/net/mac80211.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index c06974accfa6..f7917f765cbc 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3568,6 +3568,8 @@ enum rate_control_changed { * @hw: The hardware the algorithm is invoked for. * @sband: The band this frame is being transmitted on. * @bss_conf: the current BSS configuration + * @skb: the skb that will be transmitted, the control information in it needs + * to be filled in * @reported_rate: The rate control algorithm can fill this in to indicate * which rate should be reported to userspace as the current rate and * used for rate calculations in the mesh network. @@ -3575,12 +3577,11 @@ enum rate_control_changed { * RTS threshold * @short_preamble: whether mac80211 will request short-preamble transmission * if the selected rate supports it - * @max_rate_idx: user-requested maximum rate (not MCS for now) + * @max_rate_idx: user-requested maximum (legacy) rate * (deprecated; this will be removed once drivers get updated to use * rate_idx_mask) - * @rate_idx_mask: user-requested rate mask (not MCS for now) - * @skb: the skb that will be transmitted, the control information in it needs - * to be filled in + * @rate_idx_mask: user-requested (legacy) rate mask + * @rate_idx_mcs_mask: user-requested MCS rate mask * @bss: whether this frame is sent out in AP or IBSS mode */ struct ieee80211_tx_rate_control { -- cgit From 1a54a76d5171f3ffd89eb69f6f38d535724e3d05 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:51:50 +0100 Subject: TTY: let alloc_tty_driver deduce the owner automatically Like the rest of the kernel, make a stub from alloc_tty_driver which calls __alloc_tty_driver with proper owner. This will save us one more assignment on the driver side. Also this fixes some drivers which didn't set the owner. This allowed user to remove the module from the system even though a tty from the driver is still open. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 5 +++-- include/linux/tty_driver.h | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index f105ce5c8e6e..bd95cea3173b 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -3049,7 +3049,7 @@ void tty_unregister_device(struct tty_driver *driver, unsigned index) } EXPORT_SYMBOL(tty_unregister_device); -struct tty_driver *alloc_tty_driver(int lines) +struct tty_driver *__alloc_tty_driver(int lines, struct module *owner) { struct tty_driver *driver; @@ -3058,11 +3058,12 @@ struct tty_driver *alloc_tty_driver(int lines) kref_init(&driver->kref); driver->magic = TTY_DRIVER_MAGIC; driver->num = lines; + driver->owner = owner; /* later we'll move allocation of tables here */ } return driver; } -EXPORT_SYMBOL(alloc_tty_driver); +EXPORT_SYMBOL(__alloc_tty_driver); static void destruct_tty_driver(struct kref *kref) { diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 5cf685086dd3..6e65493a5e6c 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -234,6 +234,7 @@ * if provided (otherwise EINVAL will be returned). */ +#include #include #include #include @@ -324,7 +325,7 @@ struct tty_driver { extern struct list_head tty_drivers; -extern struct tty_driver *alloc_tty_driver(int lines); +extern struct tty_driver *__alloc_tty_driver(int lines, struct module *owner); extern void put_tty_driver(struct tty_driver *driver); extern void tty_set_operations(struct tty_driver *driver, const struct tty_operations *op); @@ -332,6 +333,8 @@ extern struct tty_driver *tty_find_polling_driver(char *name, int *line); extern void tty_driver_kref_put(struct tty_driver *driver); +#define alloc_tty_driver(lines) __alloc_tty_driver(lines, THIS_MODULE) + static inline struct tty_driver *tty_driver_kref_get(struct tty_driver *d) { kref_get(&d->kref); -- cgit From 87cab16beb882d3f9e61a2c0184fa7cf76de1f90 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:51:51 +0100 Subject: TTY: remove minor_num from tty_driver It was added back in 2004 and never used for anything real. Remove the only assignment in the tree as well. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pti.c | 1 - include/linux/tty_driver.h | 1 - 2 files changed, 2 deletions(-) (limited to 'include') diff --git a/drivers/misc/pti.c b/drivers/misc/pti.c index 471ff4c85cd8..9a35db3d27fc 100644 --- a/drivers/misc/pti.c +++ b/drivers/misc/pti.c @@ -920,7 +920,6 @@ static int __init pti_init(void) pti_tty_driver->name = TTYNAME; pti_tty_driver->major = 0; pti_tty_driver->minor_start = PTITTY_MINOR_START; - pti_tty_driver->minor_num = PTITTY_MINOR_NUM; pti_tty_driver->num = PTITTY_MINOR_NUM; pti_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM; pti_tty_driver->subtype = SYSTEM_TYPE_SYSCONS; diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 6e65493a5e6c..e064f1704e20 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -299,7 +299,6 @@ struct tty_driver { int name_base; /* offset of printed name */ int major; /* major device number */ int minor_start; /* start of minor device number */ - int minor_num; /* number of *possible* devices */ int num; /* number of devices allocated */ short type; /* type of tty driver */ short subtype; /* subtype of tty driver */ -- cgit From 91cedcde1e5feede6c1e4c2086ec4f3c84c56d4f Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:51:55 +0100 Subject: TTY: serial, simplify ASYNC_USR_MASK By using ASYNC_SPD_MASK instead of the single speed bits. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- include/linux/serial.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/serial.h b/include/linux/serial.h index 3d86517fe7d5..441980ecc4e5 100644 --- a/include/linux/serial.h +++ b/include/linux/serial.h @@ -152,8 +152,8 @@ struct serial_uart_config { #define ASYNC_AUTOPROBE (1U << ASYNCB_AUTOPROBE) #define ASYNC_FLAGS ((1U << (ASYNCB_LAST_USER + 1)) - 1) -#define ASYNC_USR_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI| \ - ASYNC_CALLOUT_NOHUP|ASYNC_SPD_SHI|ASYNC_LOW_LATENCY) +#define ASYNC_USR_MASK (ASYNC_SPD_MASK|ASYNC_CALLOUT_NOHUP| \ + ASYNC_LOW_LATENCY) #define ASYNC_SPD_CUST (ASYNC_SPD_HI|ASYNC_SPD_VHI) #define ASYNC_SPD_WARP (ASYNC_SPD_HI|ASYNC_SPD_SHI) #define ASYNC_SPD_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI) -- cgit From 26b23209c0ea5503824df60b8f218fb04b80cad0 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:51:56 +0100 Subject: TTY: tty_driver, document tty->ops->shutdown limitation Note that tty->ops->shutdown is called from whatever context the user drops the last tty reference from. E.g. if one takes a reference in an ISR, tty close happens on other CPU and the final tty put is from the ISR, tty->ops->shutdown will be called from that hard irq context. We would have a problem in vt if we start using tty refcounting from other contexts than user there. It is because vt's shutdown uses mutexes. This is yet to be fixed. Signed-off-by: Jiri Slaby Reported-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- include/linux/tty_driver.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index e064f1704e20..6e6dbb7447b6 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -50,6 +50,8 @@ * Note that tty_shutdown() is not called if ops->shutdown is defined. * This means one is responsible to take care of calling ops->remove (e.g. * via tty_driver_remove_tty) and releasing tty->termios. + * Note that this hook may be called from *all* the contexts where one + * uses tty refcounting (e.g. tty_port_tty_get). * * * void (*cleanup)(struct tty_struct * tty); -- cgit From 44a1bfd95d0a6c0096e79a883197596e1ce83cc3 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:52:00 +0100 Subject: TTY: serialP, remove DECLARE_WAITQUEUE check The macro is always defined now. This was there only for historical reasons. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- include/linux/serialP.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'include') diff --git a/include/linux/serialP.h b/include/linux/serialP.h index e811a615f696..ec27b34bbbd6 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -79,15 +79,9 @@ struct async_struct { int io_type; struct work_struct work; struct tasklet_struct tlet; -#ifdef DECLARE_WAITQUEUE wait_queue_head_t open_wait; wait_queue_head_t close_wait; wait_queue_head_t delta_msr_wait; -#else - struct wait_queue *open_wait; - struct wait_queue *close_wait; - struct wait_queue *delta_msr_wait; -#endif struct async_struct *next_port; /* For the linked list */ struct async_struct *prev_port; }; -- cgit From 9c8efecc91c02056340ae19612315f3225e6dbe2 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:52:12 +0100 Subject: TTY: serialP, remove unused material First, remove unused macro and rs_multiport_struct structure. Nobody uses them at all. Further, the 2 drivers (they are below) which use the rest of structures from serialP.h (async_struct and serial_state) do not use all the members. Remove the members: * which are unused or * which are only initialized and never used for something real. Everybody should avoid the structures with a looong distance. Finally, remove the ALPHA kludge MCR quirks. They are 1:1 copy from 8250.h. No need to redefine them here. The 2 promised users of the structures: arch/ia64/hp/sim/simserial.c drivers/tty/amiserial.c Signed-off-by: Jiri Slaby Cc: Geert Uytterhoeven Cc: Tony Luck Cc: Fenghua Yu Signed-off-by: Greg Kroah-Hartman --- arch/ia64/hp/sim/simserial.c | 10 +------- drivers/tty/amiserial.c | 6 ----- include/linux/serialP.h | 59 -------------------------------------------- 3 files changed, 1 insertion(+), 74 deletions(-) (limited to 'include') diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index 2a2fe0c56119..9890b58960a7 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -72,7 +72,7 @@ static char *serial_version = "0.6"; */ static struct serial_state rs_table[NR_PORTS]={ /* UART CLK PORT IRQ FLAGS */ - { 0, BASE_BAUD, 0x3F8, 0, STD_COM_FLAGS,0,PORT_16550 } /* ttyS0 */ + { BASE_BAUD, 0x3F8, 0, STD_COM_FLAGS, PORT_16550 } /* ttyS0 */ }; /* @@ -194,11 +194,6 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) * ------------------------------------------------------------------- */ -static void do_softint(struct work_struct *private_) -{ - printk(KERN_ERR "simserial: do_softint called\n"); -} - static int rs_put_char(struct tty_struct *tty, unsigned char ch) { struct async_struct *info = (struct async_struct *)tty->driver_data; @@ -641,13 +636,10 @@ static int get_async_struct(int line, struct async_struct **ret_info) } init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); - init_waitqueue_head(&info->delta_msr_wait); - info->magic = SERIAL_MAGIC; info->port = sstate->port; info->flags = sstate->flags; info->xmit_fifo_size = sstate->xmit_fifo_size; info->line = line; - INIT_WORK(&info->work, do_softint); info->state = sstate; if (sstate->info) { kfree(info); diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 753286257554..c6d8913dd6f6 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -473,7 +473,6 @@ static irqreturn_t ser_rx_int(int irq, void *dev_id) return IRQ_NONE; receive_chars(info); - info->last_active = jiffies; #ifdef SERIAL_DEBUG_INTR printk("end.\n"); #endif @@ -494,7 +493,6 @@ static irqreturn_t ser_tx_int(int irq, void *dev_id) return IRQ_NONE; transmit_chars(info); - info->last_active = jiffies; #ifdef SERIAL_DEBUG_INTR printk("end.\n"); #endif @@ -828,7 +826,6 @@ static void change_speed(struct async_struct *info, mb(); } - info->LCR = cval; /* Save LCR */ local_irq_restore(flags); } @@ -1743,7 +1740,6 @@ static int get_async_struct(int line, struct async_struct **ret_info) init_waitqueue_head(&info->close_wait); init_waitqueue_head(&info->delta_msr_wait); #endif - info->magic = SERIAL_MAGIC; info->port = sstate->port; info->flags = sstate->flags; info->xmit_fifo_size = sstate->xmit_fifo_size; @@ -1840,7 +1836,6 @@ static inline void line_info(struct seq_file *m, struct serial_state *state) if (!info) { info = &scr_info; /* This is just for serial_{in,out} */ - info->magic = SERIAL_MAGIC; info->flags = state->flags; info->quot = 0; info->tty = NULL; @@ -1987,7 +1982,6 @@ static int __init amiga_serial_probe(struct platform_device *pdev) goto fail_put_tty_driver; state = rs_table; - state->magic = SSTATE_MAGIC; state->port = (int)&custom.serdatr; /* Just to give it a value */ state->line = 0; state->custom_divisor = 0; diff --git a/include/linux/serialP.h b/include/linux/serialP.h index ec27b34bbbd6..c1acdb2c8584 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -26,32 +26,23 @@ #include struct serial_state { - int magic; int baud_base; unsigned long port; int irq; int flags; - int hub6; int type; int line; - int revision; /* Chip revision (950) */ int xmit_fifo_size; int custom_divisor; int count; - u8 *iomem_base; - u16 iomem_reg_shift; unsigned short close_delay; unsigned short closing_wait; /* time to wait before closing */ struct async_icount icount; - int io_type; struct async_struct *info; - struct pci_dev *dev; }; struct async_struct { - int magic; unsigned long port; - int hub6; int flags; int xmit_fifo_size; struct serial_state *state; @@ -63,21 +54,12 @@ struct async_struct { int x_char; /* xon/xoff character */ int close_delay; unsigned short closing_wait; - unsigned short closing_wait2; /* obsolete */ int IER; /* Interrupt Enable Register */ int MCR; /* Modem control register */ - int LCR; /* Line control register */ - int ACR; /* 16950 Additional Control Reg. */ unsigned long event; - unsigned long last_active; int line; int blocked_open; /* # of blocked opens */ struct circ_buf xmit; - spinlock_t xmit_lock; - u8 *iomem_base; - u16 iomem_reg_shift; - int io_type; - struct work_struct work; struct tasklet_struct tlet; wait_queue_head_t open_wait; wait_queue_head_t close_wait; @@ -86,51 +68,10 @@ struct async_struct { struct async_struct *prev_port; }; -#define CONFIGURED_SERIAL_PORT(info) ((info)->port || ((info)->iomem_base)) - -#define SERIAL_MAGIC 0x5301 -#define SSTATE_MAGIC 0x5302 - /* * Events are used to schedule things to happen at timer-interrupt * time, instead of at rs interrupt time. */ #define RS_EVENT_WRITE_WAKEUP 0 -/* - * Multiport serial configuration structure --- internal structure - */ -struct rs_multiport_struct { - int port1; - unsigned char mask1, match1; - int port2; - unsigned char mask2, match2; - int port3; - unsigned char mask3, match3; - int port4; - unsigned char mask4, match4; - int port_monitor; -}; - -#if defined(__alpha__) && !defined(CONFIG_PCI) -/* - * Digital did something really horribly wrong with the OUT1 and OUT2 - * lines on at least some ALPHA's. The failure mode is that if either - * is cleared, the machine locks up with endless interrupts. - * - * This is still used by arch/mips/au1000/common/serial.c for some weird - * reason (mips != alpha!) - */ -#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1) -#elif defined(CONFIG_SBC8560) -/* - * WindRiver did something similarly broken on their SBC8560 board. The - * UART tristates its IRQ output while OUT2 is clear, but they pulled - * the interrupt line _up_ instead of down, so if we register the IRQ - * while the UART is in that state, we die in an IRQ storm. */ -#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2) -#else -#define ALPHA_KLUDGE_MCR 0 -#endif - #endif /* _LINUX_SERIAL_H */ -- cgit From c5f0508b992ad081ba378a59b2404966f9f89429 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:52:13 +0100 Subject: TTY: amiserial, remove tasklet for tty_wakeup tty_wakeup is safe to be called from all contexts. No need to schedule a tasklet for that. Let's call it directly like in other drivers. This allows us to kill another member of async_struct structure. (If we remove the dummy uses in simserial.) Signed-off-by: Jiri Slaby Cc: Geert Uytterhoeven Cc: Tony Luck Cc: Fenghua Yu Signed-off-by: Greg Kroah-Hartman --- arch/ia64/hp/sim/simserial.c | 2 -- drivers/tty/amiserial.c | 42 ++---------------------------------------- include/linux/serialP.h | 8 -------- 3 files changed, 2 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index 9890b58960a7..0d324e85379e 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -572,7 +572,6 @@ static void rs_close(struct tty_struct *tty, struct file * filp) shutdown(info); rs_flush_buffer(tty); tty_ldisc_flush(tty); - info->event = 0; info->tty = NULL; if (info->blocked_open) { if (info->close_delay) @@ -610,7 +609,6 @@ static void rs_hangup(struct tty_struct *tty) return; shutdown(info); - info->event = 0; state->count = 0; info->flags &= ~ASYNC_NORMAL_ACTIVE; info->tty = NULL; diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index c6d8913dd6f6..d5fac8626988 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -231,17 +231,6 @@ static void rs_start(struct tty_struct *tty) * ----------------------------------------------------------------------- */ -/* - * This routine is used by the interrupt handler to schedule - * processing in the software interrupt portion of the driver. - */ -static void rs_sched_event(struct async_struct *info, - int event) -{ - info->event |= 1 << event; - tasklet_schedule(&info->tlet); -} - static void receive_chars(struct async_struct *info) { int status; @@ -359,7 +348,7 @@ static void transmit_chars(struct async_struct *info) if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) < WAKEUP_CHARS) - rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + tty_wakeup(info->tty); #ifdef SERIAL_DEBUG_INTR printk("THRE..."); @@ -427,7 +416,7 @@ static void check_modem_status(struct async_struct *info) /* set a pending Tx Interrupt, transmitter should restart now */ custom.intreq = IF_SETCLR | IF_TBE; mb(); - rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + tty_wakeup(info->tty); return; } } else { @@ -506,29 +495,6 @@ static irqreturn_t ser_tx_int(int irq, void *dev_id) * ------------------------------------------------------------------- */ -/* - * This routine is used to handle the "bottom half" processing for the - * serial driver, known also the "software interrupt" processing. - * This processing is done at the kernel interrupt level, after the - * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This - * is where time-consuming activities which can not be done in the - * interrupt driver proper are done; the interrupt driver schedules - * them using rs_sched_event(), and they get done here. - */ - -static void do_softint(unsigned long private_) -{ - struct async_struct *info = (struct async_struct *) private_; - struct tty_struct *tty; - - tty = info->tty; - if (!tty) - return; - - if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) - tty_wakeup(tty); -} - /* * --------------------------------------------------------------- * Low level utility subroutines for the serial driver: routines to @@ -1506,7 +1472,6 @@ static void rs_close(struct tty_struct *tty, struct file * filp) tty_ldisc_flush(tty); tty->closing = 0; - info->event = 0; info->tty = NULL; if (info->blocked_open) { if (info->close_delay) { @@ -1597,7 +1562,6 @@ static void rs_hangup(struct tty_struct *tty) rs_flush_buffer(tty); shutdown(info); - info->event = 0; state->count = 0; info->flags &= ~ASYNC_NORMAL_ACTIVE; info->tty = NULL; @@ -1744,7 +1708,6 @@ static int get_async_struct(int line, struct async_struct **ret_info) info->flags = sstate->flags; info->xmit_fifo_size = sstate->xmit_fifo_size; info->line = line; - tasklet_init(&info->tlet, do_softint, (unsigned long)info); info->state = sstate; if (sstate->info) { kfree(info); @@ -2050,7 +2013,6 @@ static int __exit amiga_serial_remove(struct platform_device *pdev) struct async_struct *info = state->info; /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ - tasklet_kill(&info->tlet); if ((error = tty_unregister_driver(serial_driver))) printk("SERIAL: failed to unregister serial driver (%d)\n", error); diff --git a/include/linux/serialP.h b/include/linux/serialP.h index c1acdb2c8584..beaf39f819d6 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -56,11 +56,9 @@ struct async_struct { unsigned short closing_wait; int IER; /* Interrupt Enable Register */ int MCR; /* Modem control register */ - unsigned long event; int line; int blocked_open; /* # of blocked opens */ struct circ_buf xmit; - struct tasklet_struct tlet; wait_queue_head_t open_wait; wait_queue_head_t close_wait; wait_queue_head_t delta_msr_wait; @@ -68,10 +66,4 @@ struct async_struct { struct async_struct *prev_port; }; -/* - * Events are used to schedule things to happen at timer-interrupt - * time, instead of at rs interrupt time. - */ -#define RS_EVENT_WRITE_WAKEUP 0 - #endif /* _LINUX_SERIAL_H */ -- cgit From d1c3414c2a9d10ef7f0f7665f5d2947cd088c093 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Mon, 5 Mar 2012 08:47:41 -0700 Subject: drivercore: Add driver probe deferral mechanism Allow drivers to report at probe time that they cannot get all the resources required by the device, and should be retried at a later time. This should completely solve the problem of getting devices initialized in the right order. Right now this is mostly handled by mucking about with initcall ordering which is a complete hack, and doesn't even remotely handle the case where device drivers are in modules. This approach completely sidesteps the issues by allowing driver registration to occur in any order, and any driver can request to be retried after a few more other drivers get probed. v4: - Integrate Manjunath's addition of a separate workqueue - Change -EAGAIN to -EPROBE_DEFER for drivers to trigger deferral - Update comment blocks to reflect how the code really works v3: - Hold off workqueue scheduling until late_initcall so that the bulk of driver probes are complete before we start retrying deferred devices. - Tested with simple use cases. Still needs more testing though. Using it to get rid of the gpio early_initcall madness, or to replace the ASoC internal probe deferral code would be ideal. v2: - added locking so it should no longer be utterly broken in that regard - remove device from deferred list at device_del time. - Still completely untested with any real use case, but has been boot tested. Signed-off-by: Grant Likely Cc: Mark Brown Cc: Arnd Bergmann Cc: Dilan Lee Cc: Manjunath GKondaiah Cc: Alan Stern Cc: Tony Lindgren Cc: Alan Cox Reviewed-by: Mark Brown Acked-by: David Daney Reviewed-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 1 + drivers/base/core.c | 2 + drivers/base/dd.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/device.h | 5 ++ include/linux/errno.h | 1 + 5 files changed, 146 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/base/base.h b/drivers/base/base.h index b858dfd9a37c..2c13deae5f82 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -105,6 +105,7 @@ extern void bus_remove_driver(struct device_driver *drv); extern void driver_detach(struct device_driver *drv); extern int driver_probe_device(struct device_driver *drv, struct device *dev); +extern void driver_deferred_probe_del(struct device *dev); static inline int driver_match_device(struct device_driver *drv, struct device *dev) { diff --git a/drivers/base/core.c b/drivers/base/core.c index 74dda4f697f9..d4ff7adce38c 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -644,6 +644,7 @@ void device_initialize(struct device *dev) { dev->kobj.kset = devices_kset; kobject_init(&dev->kobj, &device_ktype); + INIT_LIST_HEAD(&dev->deferred_probe); INIT_LIST_HEAD(&dev->dma_pools); mutex_init(&dev->mutex); lockdep_set_novalidate_class(&dev->mutex); @@ -1188,6 +1189,7 @@ void device_del(struct device *dev) device_remove_file(dev, &uevent_attr); device_remove_attrs(dev); bus_remove_device(dev); + driver_deferred_probe_del(dev); /* * Some platform devices are driven without driver attached diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 142e3d600f14..442b7641a086 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -28,6 +28,133 @@ #include "base.h" #include "power/power.h" +/* + * Deferred Probe infrastructure. + * + * Sometimes driver probe order matters, but the kernel doesn't always have + * dependency information which means some drivers will get probed before a + * resource it depends on is available. For example, an SDHCI driver may + * first need a GPIO line from an i2c GPIO controller before it can be + * initialized. If a required resource is not available yet, a driver can + * request probing to be deferred by returning -EPROBE_DEFER from its probe hook + * + * Deferred probe maintains two lists of devices, a pending list and an active + * list. A driver returning -EPROBE_DEFER causes the device to be added to the + * pending list. A successful driver probe will trigger moving all devices + * from the pending to the active list so that the workqueue will eventually + * retry them. + * + * The deferred_probe_mutex must be held any time the deferred_probe_*_list + * of the (struct device*)->deferred_probe pointers are manipulated + */ +static DEFINE_MUTEX(deferred_probe_mutex); +static LIST_HEAD(deferred_probe_pending_list); +static LIST_HEAD(deferred_probe_active_list); +static struct workqueue_struct *deferred_wq; + +/** + * deferred_probe_work_func() - Retry probing devices in the active list. + */ +static void deferred_probe_work_func(struct work_struct *work) +{ + struct device *dev; + /* + * This block processes every device in the deferred 'active' list. + * Each device is removed from the active list and passed to + * bus_probe_device() to re-attempt the probe. The loop continues + * until every device in the active list is removed and retried. + * + * Note: Once the device is removed from the list and the mutex is + * released, it is possible for the device get freed by another thread + * and cause a illegal pointer dereference. This code uses + * get/put_device() to ensure the device structure cannot disappear + * from under our feet. + */ + mutex_lock(&deferred_probe_mutex); + while (!list_empty(&deferred_probe_active_list)) { + dev = list_first_entry(&deferred_probe_active_list, + typeof(*dev), deferred_probe); + list_del_init(&dev->deferred_probe); + + get_device(dev); + + /* Drop the mutex while probing each device; the probe path + * may manipulate the deferred list */ + mutex_unlock(&deferred_probe_mutex); + dev_dbg(dev, "Retrying from deferred list\n"); + bus_probe_device(dev); + mutex_lock(&deferred_probe_mutex); + + put_device(dev); + } + mutex_unlock(&deferred_probe_mutex); +} +static DECLARE_WORK(deferred_probe_work, deferred_probe_work_func); + +static void driver_deferred_probe_add(struct device *dev) +{ + mutex_lock(&deferred_probe_mutex); + if (list_empty(&dev->deferred_probe)) { + dev_dbg(dev, "Added to deferred list\n"); + list_add(&dev->deferred_probe, &deferred_probe_pending_list); + } + mutex_unlock(&deferred_probe_mutex); +} + +void driver_deferred_probe_del(struct device *dev) +{ + mutex_lock(&deferred_probe_mutex); + if (!list_empty(&dev->deferred_probe)) { + dev_dbg(dev, "Removed from deferred list\n"); + list_del_init(&dev->deferred_probe); + } + mutex_unlock(&deferred_probe_mutex); +} + +static bool driver_deferred_probe_enable = false; +/** + * driver_deferred_probe_trigger() - Kick off re-probing deferred devices + * + * This functions moves all devices from the pending list to the active + * list and schedules the deferred probe workqueue to process them. It + * should be called anytime a driver is successfully bound to a device. + */ +static void driver_deferred_probe_trigger(void) +{ + if (!driver_deferred_probe_enable) + return; + + /* A successful probe means that all the devices in the pending list + * should be triggered to be reprobed. Move all the deferred devices + * into the active list so they can be retried by the workqueue */ + mutex_lock(&deferred_probe_mutex); + list_splice_tail_init(&deferred_probe_pending_list, + &deferred_probe_active_list); + mutex_unlock(&deferred_probe_mutex); + + /* Kick the re-probe thread. It may already be scheduled, but + * it is safe to kick it again. */ + queue_work(deferred_wq, &deferred_probe_work); +} + +/** + * deferred_probe_initcall() - Enable probing of deferred devices + * + * We don't want to get in the way when the bulk of drivers are getting probed. + * Instead, this initcall makes sure that deferred probing is delayed until + * late_initcall time. + */ +static int deferred_probe_initcall(void) +{ + deferred_wq = create_singlethread_workqueue("deferwq"); + if (WARN_ON(!deferred_wq)) + return -ENOMEM; + + driver_deferred_probe_enable = true; + driver_deferred_probe_trigger(); + return 0; +} +late_initcall(deferred_probe_initcall); static void driver_bound(struct device *dev) { @@ -42,6 +169,11 @@ static void driver_bound(struct device *dev) klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); + /* Make sure the device is no longer in one of the deferred lists + * and kick off retrying all pending devices */ + driver_deferred_probe_del(dev); + driver_deferred_probe_trigger(); + if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_BOUND_DRIVER, dev); @@ -142,7 +274,11 @@ probe_failed: driver_sysfs_remove(dev); dev->driver = NULL; - if (ret != -ENODEV && ret != -ENXIO) { + if (ret == -EPROBE_DEFER) { + /* Driver requested deferred probing */ + dev_info(dev, "Driver %s requests probe deferral\n", drv->name); + driver_deferred_probe_add(dev); + } else if (ret != -ENODEV && ret != -ENXIO) { /* driver matched but the probe failed */ printk(KERN_WARNING "%s: probe of %s failed with error %d\n", diff --git a/include/linux/device.h b/include/linux/device.h index f62e21689fdd..22d6938ddbb4 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -585,6 +585,10 @@ struct device_dma_parameters { * @mutex: Mutex to synchronize calls to its driver. * @bus: Type of bus device is on. * @driver: Which driver has allocated this + * @deferred_probe: entry in deferred_probe_list which is used to retry the + * binding of drivers which were unable to get all the resources + * needed by the device; typically because it depends on another + * driver getting probed first. * @platform_data: Platform data specific to the device. * Example: For devices on custom boards, as typical of embedded * and SOC based hardware, Linux often uses platform_data to point @@ -644,6 +648,7 @@ struct device { struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ + struct list_head deferred_probe; void *platform_data; /* Platform specific data, device core doesn't touch it */ struct dev_pm_info power; diff --git a/include/linux/errno.h b/include/linux/errno.h index 46685832ed99..2d09bfa5c262 100644 --- a/include/linux/errno.h +++ b/include/linux/errno.h @@ -16,6 +16,7 @@ #define ERESTARTNOHAND 514 /* restart if no handler.. */ #define ENOIOCTLCMD 515 /* No ioctl command */ #define ERESTART_RESTARTBLOCK 516 /* restart by calling sys_restart_syscall */ +#define EPROBE_DEFER 517 /* Driver requests probe retry */ /* Defined for the NFSv3 protocol */ #define EBADHANDLE 521 /* Illegal NFS file handle */ -- cgit From ef8a3fd6e5e12e8989dae97ba5491c2e39369af9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 8 Mar 2012 12:17:22 -0800 Subject: driver core: move the deferred probe pointer into the private area Nothing outside of the driver core needs to get to the deferred probe pointer, so move it inside the private area of 'struct device' so no one tries to mess around with it. Cc: Grant Likely Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 5 +++++ drivers/base/core.c | 2 +- drivers/base/dd.c | 18 ++++++++++-------- include/linux/device.h | 5 ----- 4 files changed, 16 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/base/base.h b/drivers/base/base.h index 2c13deae5f82..6ee17bb391a9 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -59,6 +59,10 @@ struct driver_private { * @knode_parent - node in sibling list * @knode_driver - node in driver list * @knode_bus - node in bus list + * @deferred_probe - entry in deferred_probe_list which is used to retry the + * binding of drivers which were unable to get all the resources needed by + * the device; typically because it depends on another driver getting + * probed first. * @driver_data - private pointer for driver specific info. Will turn into a * list soon. * @device - pointer back to the struct class that this structure is @@ -71,6 +75,7 @@ struct device_private { struct klist_node knode_parent; struct klist_node knode_driver; struct klist_node knode_bus; + struct list_head deferred_probe; void *driver_data; struct device *device; }; diff --git a/drivers/base/core.c b/drivers/base/core.c index d4ff7adce38c..7050a75dde38 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -644,7 +644,6 @@ void device_initialize(struct device *dev) { dev->kobj.kset = devices_kset; kobject_init(&dev->kobj, &device_ktype); - INIT_LIST_HEAD(&dev->deferred_probe); INIT_LIST_HEAD(&dev->dma_pools); mutex_init(&dev->mutex); lockdep_set_novalidate_class(&dev->mutex); @@ -922,6 +921,7 @@ int device_private_init(struct device *dev) dev->p->device = dev; klist_init(&dev->p->klist_children, klist_children_get, klist_children_put); + INIT_LIST_HEAD(&dev->p->deferred_probe); return 0; } diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 442b7641a086..9fa888e08059 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -45,7 +45,7 @@ * retry them. * * The deferred_probe_mutex must be held any time the deferred_probe_*_list - * of the (struct device*)->deferred_probe pointers are manipulated + * of the (struct device*)->p->deferred_probe pointers are manipulated */ static DEFINE_MUTEX(deferred_probe_mutex); static LIST_HEAD(deferred_probe_pending_list); @@ -58,6 +58,7 @@ static struct workqueue_struct *deferred_wq; static void deferred_probe_work_func(struct work_struct *work) { struct device *dev; + struct device_private *private; /* * This block processes every device in the deferred 'active' list. * Each device is removed from the active list and passed to @@ -72,9 +73,10 @@ static void deferred_probe_work_func(struct work_struct *work) */ mutex_lock(&deferred_probe_mutex); while (!list_empty(&deferred_probe_active_list)) { - dev = list_first_entry(&deferred_probe_active_list, - typeof(*dev), deferred_probe); - list_del_init(&dev->deferred_probe); + private = list_first_entry(&deferred_probe_active_list, + typeof(*dev->p), deferred_probe); + dev = private->device; + list_del_init(&private->deferred_probe); get_device(dev); @@ -94,9 +96,9 @@ static DECLARE_WORK(deferred_probe_work, deferred_probe_work_func); static void driver_deferred_probe_add(struct device *dev) { mutex_lock(&deferred_probe_mutex); - if (list_empty(&dev->deferred_probe)) { + if (list_empty(&dev->p->deferred_probe)) { dev_dbg(dev, "Added to deferred list\n"); - list_add(&dev->deferred_probe, &deferred_probe_pending_list); + list_add(&dev->p->deferred_probe, &deferred_probe_pending_list); } mutex_unlock(&deferred_probe_mutex); } @@ -104,9 +106,9 @@ static void driver_deferred_probe_add(struct device *dev) void driver_deferred_probe_del(struct device *dev) { mutex_lock(&deferred_probe_mutex); - if (!list_empty(&dev->deferred_probe)) { + if (!list_empty(&dev->p->deferred_probe)) { dev_dbg(dev, "Removed from deferred list\n"); - list_del_init(&dev->deferred_probe); + list_del_init(&dev->p->deferred_probe); } mutex_unlock(&deferred_probe_mutex); } diff --git a/include/linux/device.h b/include/linux/device.h index 22d6938ddbb4..f62e21689fdd 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -585,10 +585,6 @@ struct device_dma_parameters { * @mutex: Mutex to synchronize calls to its driver. * @bus: Type of bus device is on. * @driver: Which driver has allocated this - * @deferred_probe: entry in deferred_probe_list which is used to retry the - * binding of drivers which were unable to get all the resources - * needed by the device; typically because it depends on another - * driver getting probed first. * @platform_data: Platform data specific to the device. * Example: For devices on custom boards, as typical of embedded * and SOC based hardware, Linux often uses platform_data to point @@ -648,7 +644,6 @@ struct device { struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ - struct list_head deferred_probe; void *platform_data; /* Platform specific data, device core doesn't touch it */ struct dev_pm_info power; -- cgit From 979b6d89766ed573bca6a6e902193c4cad502909 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:52:15 +0100 Subject: TTY: simserial, use only one copy of async flags The same as for amiserial. Use only one instance of the flags. Also remove them from async_struct now. Nobody else uses them. Signed-off-by: Jiri Slaby Cc: Tony Luck Cc: Fenghua Yu Signed-off-by: Greg Kroah-Hartman --- arch/ia64/hp/sim/simserial.c | 48 +++++++++++++++++++++----------------------- include/linux/serialP.h | 1 - 2 files changed, 23 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index 8f68972b015f..a08a53f033b4 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -46,7 +46,7 @@ #define NR_PORTS 1 /* only one port for now */ -#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? IRQF_SHARED : IRQF_DISABLED) +#define IRQ_T(state) ((state->flags & ASYNC_SHARE_IRQ) ? IRQF_SHARED : IRQF_DISABLED) static char *serial_name = "SimSerial driver"; static char *serial_version = "0.6"; @@ -455,12 +455,11 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) static void shutdown(struct async_struct * info) { unsigned long flags; - struct serial_state *state; + struct serial_state *state = info->state; int retval; - if (!(info->flags & ASYNC_INITIALIZED)) return; - - state = info->state; + if (!(state->flags & ASYNC_INITIALIZED)) + return; #ifdef SIMSERIAL_DEBUG printk("Shutting down serial port %d (irq %d)....", info->line, @@ -487,7 +486,8 @@ static void shutdown(struct async_struct * info) if (IRQ_ports[state->irq]) { free_irq(state->irq, NULL); retval = request_irq(state->irq, rs_interrupt_single, - IRQ_T(info), "serial", NULL); + IRQ_T(state), "serial", + NULL); if (retval) printk(KERN_ERR "serial shutdown: request_irq: error %d" @@ -503,7 +503,7 @@ static void shutdown(struct async_struct * info) if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); - info->flags &= ~ASYNC_INITIALIZED; + state->flags &= ~ASYNC_INITIALIZED; } local_irq_restore(flags); } @@ -560,7 +560,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) local_irq_restore(flags); return; } - info->flags |= ASYNC_CLOSING; + state->flags |= ASYNC_CLOSING; local_irq_restore(flags); /* @@ -576,7 +576,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) schedule_timeout_interruptible(info->close_delay); wake_up_interruptible(&info->open_wait); } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + state->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); wake_up_interruptible(&info->close_wait); } @@ -600,15 +600,13 @@ static void rs_hangup(struct tty_struct *tty) printk("rs_hangup: called\n"); #endif - state = info->state; - rs_flush_buffer(tty); - if (info->flags & ASYNC_CLOSING) + if (state->flags & ASYNC_CLOSING) return; shutdown(info); state->count = 0; - info->flags &= ~ASYNC_NORMAL_ACTIVE; + state->flags &= ~ASYNC_NORMAL_ACTIVE; info->tty = NULL; wake_up_interruptible(&info->open_wait); } @@ -633,7 +631,6 @@ static int get_async_struct(int line, struct async_struct **ret_info) init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); info->port = sstate->port; - info->flags = sstate->flags; info->xmit_fifo_size = sstate->xmit_fifo_size; info->line = line; info->state = sstate; @@ -661,7 +658,7 @@ startup(struct async_struct *info) local_irq_save(flags); - if (info->flags & ASYNC_INITIALIZED) { + if (state->flags & ASYNC_INITIALIZED) { free_page(page); goto errout; } @@ -691,7 +688,8 @@ startup(struct async_struct *info) } else handler = rs_interrupt_single; - retval = request_irq(state->irq, handler, IRQ_T(info), "simserial", NULL); + retval = request_irq(state->irq, handler, IRQ_T(state), + "simserial", NULL); if (retval) goto errout; } @@ -721,17 +719,17 @@ startup(struct async_struct *info) * Set up the tty->alt_speed kludge */ if (info->tty) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) info->tty->alt_speed = 115200; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) info->tty->alt_speed = 230400; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; } - info->flags |= ASYNC_INITIALIZED; + state->flags |= ASYNC_INITIALIZED; local_irq_restore(flags); return 0; @@ -762,7 +760,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp) #ifdef SIMSERIAL_DEBUG printk("rs_open %s, count = %d\n", tty->name, info->state->count); #endif - info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + info->tty->low_latency = (info->state->flags & ASYNC_LOW_LATENCY) ? 1 : 0; if (!tmp_buf) { page = get_zeroed_page(GFP_KERNEL); @@ -778,11 +776,11 @@ static int rs_open(struct tty_struct *tty, struct file * filp) * If the port is the middle of closing, bail out now */ if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - if (info->flags & ASYNC_CLOSING) + (info->state->flags & ASYNC_CLOSING)) { + if (info->state->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); #ifdef SERIAL_DO_RESTART - return ((info->flags & ASYNC_HUP_NOTIFY) ? + return ((info->state->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); #else return -EAGAIN; diff --git a/include/linux/serialP.h b/include/linux/serialP.h index beaf39f819d6..6741f57cc2ae 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -43,7 +43,6 @@ struct serial_state { struct async_struct { unsigned long port; - int flags; int xmit_fifo_size; struct serial_state *state; struct tty_struct *tty; -- cgit From d852256389f1bcf506710ea5de77debde40013b9 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:52:16 +0100 Subject: TTY: simserial/amiserial, use one instance of other members This means: * close_delay * closing_wait * line * port * xmit_fifo_size This actually fixes a bug in amiserial. It initializes one and uses the other of the close delays. Yes, duplicating structure members is evil. Signed-off-by: Jiri Slaby Cc: Geert Uytterhoeven Cc: Tony Luck Cc: Fenghua Yu Signed-off-by: Greg Kroah-Hartman --- arch/ia64/hp/sim/simserial.c | 9 +++------ drivers/tty/amiserial.c | 19 ++++++++----------- include/linux/serialP.h | 5 ----- 3 files changed, 11 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index a08a53f033b4..d32b759b23f1 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -553,7 +553,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) } if (--state->count < 0) { printk(KERN_ERR "rs_close: bad serial port count for ttys%d: %d\n", - info->line, state->count); + state->line, state->count); state->count = 0; } if (state->count) { @@ -572,8 +572,8 @@ static void rs_close(struct tty_struct *tty, struct file * filp) tty_ldisc_flush(tty); info->tty = NULL; if (info->blocked_open) { - if (info->close_delay) - schedule_timeout_interruptible(info->close_delay); + if (state->close_delay) + schedule_timeout_interruptible(state->close_delay); wake_up_interruptible(&info->open_wait); } state->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); @@ -630,9 +630,6 @@ static int get_async_struct(int line, struct async_struct **ret_info) } init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); - info->port = sstate->port; - info->xmit_fifo_size = sstate->xmit_fifo_size; - info->line = line; info->state = sstate; if (sstate->info) { kfree(info); diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 8556ca022dbc..5540216e64fd 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -723,7 +723,7 @@ static void change_speed(struct async_struct *info, if (!quot) quot = baud_base / 9600; info->quot = quot; - info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); + info->timeout = ((info->state->xmit_fifo_size*HZ*bits*quot) / baud_base); info->timeout += HZ/50; /* Add .02 seconds of slop */ /* CTS flow control flag and modem status interrupts */ @@ -1425,7 +1425,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) } if (--state->count < 0) { printk("rs_close: bad serial port count for ttys%d: %d\n", - info->line, state->count); + state->line, state->count); state->count = 0; } if (state->count) { @@ -1439,8 +1439,8 @@ static void rs_close(struct tty_struct *tty, struct file * filp) * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); + if (state->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, state->closing_wait); /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the @@ -1470,8 +1470,8 @@ static void rs_close(struct tty_struct *tty, struct file * filp) tty->closing = 0; info->tty = NULL; if (info->blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs(info->close_delay)); + if (state->close_delay) { + msleep_interruptible(jiffies_to_msecs(state->close_delay)); } wake_up_interruptible(&info->open_wait); } @@ -1492,7 +1492,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent")) return; - if (info->xmit_fifo_size == 0) + if (info->state->xmit_fifo_size == 0) return; /* Just in case.... */ orig_jiffies = jiffies; @@ -1505,7 +1505,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) * Note: we have to use pretty tight timings here to satisfy * the NIST-PCTS. */ - char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; + char_time = (info->timeout - HZ/50) / info->state->xmit_fifo_size; char_time = char_time / 5; if (char_time == 0) char_time = 1; @@ -1700,9 +1700,6 @@ static int get_async_struct(int line, struct async_struct **ret_info) init_waitqueue_head(&info->close_wait); init_waitqueue_head(&info->delta_msr_wait); #endif - info->port = sstate->port; - info->xmit_fifo_size = sstate->xmit_fifo_size; - info->line = line; info->state = sstate; if (sstate->info) { kfree(info); diff --git a/include/linux/serialP.h b/include/linux/serialP.h index 6741f57cc2ae..6ce488c46589 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -42,8 +42,6 @@ struct serial_state { }; struct async_struct { - unsigned long port; - int xmit_fifo_size; struct serial_state *state; struct tty_struct *tty; int read_status_mask; @@ -51,11 +49,8 @@ struct async_struct { int timeout; int quot; int x_char; /* xon/xoff character */ - int close_delay; - unsigned short closing_wait; int IER; /* Interrupt Enable Register */ int MCR; /* Modem control register */ - int line; int blocked_open; /* # of blocked opens */ struct circ_buf xmit; wait_queue_head_t open_wait; -- cgit From 964105b501071e8a0e9feb1d0e4b3e46508bc38e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:52:17 +0100 Subject: TTY: simserial, remove support of shared interrupts It never worked there. The ISR was never written for that kind of stuff. So remove all that crap with a hash of linked lists and pass the pointer directly to the ISR. BTW this answers the question there: * I don't know exactly why they don't use the dev_id opaque data * pointer instead of this extra lookup table -> Because they thought they will support more devices bound to a single interrupt w/o IRQF_SHARED. They would need exactly the hash there. What I don't understand is rebinding of the interrupt in the shutdown path. They perhaps meant to do just synchronize_irq? In any case, this is all gone and free_irq there properly. By removing the hash we save some bits (exactly NR_IRQS * 8 bytes of .bss and over a kilo of .text): before: text data bss dec hex filename 19600 320 8227 28147 6df3 ../a/ia64/arch/ia64/hp/sim/simserial.o after: text data bss dec hex filename 18568 320 28 18916 49e4 ../a/ia64/arch/ia64/hp/sim/simserial.o Note that a shared interrupt could not work too. request_irq requires data parameter to be non-NULL. So the whole IRQ_T exercise was pointless. Finally, this helps us remove another two members of async_struct :). Signed-off-by: Jiri Slaby Cc: Tony Luck Cc: Fenghua Yu Signed-off-by: Greg Kroah-Hartman --- arch/ia64/hp/sim/simserial.c | 64 +++++--------------------------------------- include/linux/serialP.h | 2 -- 2 files changed, 7 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index d32b759b23f1..c35552df035e 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -92,8 +92,6 @@ static struct serial_uart_config uart_config[] = { struct tty_driver *hp_simserial_driver; -static struct async_struct *IRQ_ports[NR_IRQS]; - static struct console *console; static unsigned char *tmp_buf; @@ -167,14 +165,9 @@ static void receive_chars(struct tty_struct *tty) */ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) { - struct async_struct * info; + struct async_struct *info = dev_id; - /* - * I don't know exactly why they don't use the dev_id opaque data - * pointer instead of this extra lookup table - */ - info = IRQ_ports[irq]; - if (!info || !info->tty) { + if (!info->tty) { printk(KERN_INFO "simrs_interrupt_single: info|tty=0 info=%p problem\n", info); return IRQ_NONE; } @@ -456,7 +449,6 @@ static void shutdown(struct async_struct * info) { unsigned long flags; struct serial_state *state = info->state; - int retval; if (!(state->flags & ASYNC_INITIALIZED)) return; @@ -468,33 +460,8 @@ static void shutdown(struct async_struct * info) local_irq_save(flags); { - /* - * First unlink the serial port from the IRQ chain... - */ - if (info->next_port) - info->next_port->prev_port = info->prev_port; - if (info->prev_port) - info->prev_port->next_port = info->next_port; - else - IRQ_ports[state->irq] = info->next_port; - - /* - * Free the IRQ, if necessary - */ - if (state->irq && (!IRQ_ports[state->irq] || - !IRQ_ports[state->irq]->next_port)) { - if (IRQ_ports[state->irq]) { - free_irq(state->irq, NULL); - retval = request_irq(state->irq, rs_interrupt_single, - IRQ_T(state), "serial", - NULL); - - if (retval) - printk(KERN_ERR "serial shutdown: request_irq: error %d" - " Couldn't reacquire IRQ.\n", retval); - } else - free_irq(state->irq, NULL); - } + if (state->irq) + free_irq(state->irq, info); if (info->xmit.buf) { free_page((unsigned long) info->xmit.buf); @@ -645,7 +612,6 @@ startup(struct async_struct *info) { unsigned long flags; int retval=0; - irq_handler_t handler; struct serial_state *state= info->state; unsigned long page; @@ -677,29 +643,13 @@ startup(struct async_struct *info) /* * Allocate the IRQ if necessary */ - if (state->irq && (!IRQ_ports[state->irq] || - !IRQ_ports[state->irq]->next_port)) { - if (IRQ_ports[state->irq]) { - retval = -EBUSY; - goto errout; - } else - handler = rs_interrupt_single; - - retval = request_irq(state->irq, handler, IRQ_T(state), - "simserial", NULL); + if (state->irq) { + retval = request_irq(state->irq, rs_interrupt_single, + IRQ_T(state), "simserial", info); if (retval) goto errout; } - /* - * Insert serial port into IRQ chain. - */ - info->prev_port = NULL; - info->next_port = IRQ_ports[state->irq]; - if (info->next_port) - info->next_port->prev_port = info; - IRQ_ports[state->irq] = info; - if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); info->xmit.head = info->xmit.tail = 0; diff --git a/include/linux/serialP.h b/include/linux/serialP.h index 6ce488c46589..b8543f902453 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -56,8 +56,6 @@ struct async_struct { wait_queue_head_t open_wait; wait_queue_head_t close_wait; wait_queue_head_t delta_msr_wait; - struct async_struct *next_port; /* For the linked list */ - struct async_struct *prev_port; }; #endif /* _LINUX_SERIAL_H */ -- cgit From 916b765675b7044bd5895b7430a2aa2c63ea4545 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:52:20 +0100 Subject: TTY: serialP, merge serial_state and async_struct This is the final step to get rid of the one of the structures. A further cleanup will follow. And I struct serial_state deserves cease to exist after a switch to tty_port too. While changing the lines, it removes also pointless tty->driver_data casts. Signed-off-by: Jiri Slaby Cc: Geert Uytterhoeven Cc: Tony Luck Cc: Fenghua Yu Signed-off-by: Greg Kroah-Hartman --- arch/ia64/hp/sim/simserial.c | 160 +++++++++-------------- drivers/tty/amiserial.c | 301 +++++++++++++++++-------------------------- include/linux/serialP.h | 14 +- 3 files changed, 191 insertions(+), 284 deletions(-) (limited to 'include') diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index 8b5a1342e119..7b6e60e9167b 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -163,7 +163,7 @@ static void receive_chars(struct tty_struct *tty) */ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) { - struct async_struct *info = dev_id; + struct serial_state *info = dev_id; if (!info->tty) { printk(KERN_INFO "simrs_interrupt_single: info|tty=0 info=%p problem\n", info); @@ -185,7 +185,7 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) static int rs_put_char(struct tty_struct *tty, unsigned char ch) { - struct async_struct *info = (struct async_struct *)tty->driver_data; + struct serial_state *info = tty->driver_data; unsigned long flags; if (!tty || !info->xmit.buf) @@ -202,12 +202,11 @@ static int rs_put_char(struct tty_struct *tty, unsigned char ch) return 1; } -static void transmit_chars(struct async_struct *info, int *intr_done) +static void transmit_chars(struct serial_state *info, int *intr_done) { int count; unsigned long flags; - local_irq_save(flags); if (info->x_char) { @@ -215,7 +214,7 @@ static void transmit_chars(struct async_struct *info, int *intr_done) console->write(console, &c, 1); - info->state->icount.tx++; + info->icount.tx++; info->x_char = 0; goto out; @@ -256,7 +255,7 @@ out: static void rs_flush_chars(struct tty_struct *tty) { - struct async_struct *info = (struct async_struct *)tty->driver_data; + struct serial_state *info = tty->driver_data; if (info->xmit.head == info->xmit.tail || tty->stopped || tty->hw_stopped || !info->xmit.buf) @@ -269,8 +268,8 @@ static void rs_flush_chars(struct tty_struct *tty) static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count) { + struct serial_state *info = tty->driver_data; int c, ret = 0; - struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; if (!tty || !info->xmit.buf || !tmp_buf) return 0; @@ -303,21 +302,21 @@ static int rs_write(struct tty_struct * tty, static int rs_write_room(struct tty_struct *tty) { - struct async_struct *info = (struct async_struct *)tty->driver_data; + struct serial_state *info = tty->driver_data; return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); } static int rs_chars_in_buffer(struct tty_struct *tty) { - struct async_struct *info = (struct async_struct *)tty->driver_data; + struct serial_state *info = tty->driver_data; return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); } static void rs_flush_buffer(struct tty_struct *tty) { - struct async_struct *info = (struct async_struct *)tty->driver_data; + struct serial_state *info = tty->driver_data; unsigned long flags; local_irq_save(flags); @@ -333,7 +332,7 @@ static void rs_flush_buffer(struct tty_struct *tty) */ static void rs_send_xchar(struct tty_struct *tty, char ch) { - struct async_struct *info = (struct async_struct *)tty->driver_data; + struct serial_state *info = tty->driver_data; info->x_char = ch; if (ch) { @@ -362,7 +361,7 @@ static void rs_throttle(struct tty_struct * tty) static void rs_unthrottle(struct tty_struct * tty) { - struct async_struct *info = (struct async_struct *)tty->driver_data; + struct serial_state *info = tty->driver_data; if (I_IXOFF(tty)) { if (info->x_char) @@ -443,23 +442,22 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. */ -static void shutdown(struct async_struct * info) +static void shutdown(struct serial_state *info) { unsigned long flags; - struct serial_state *state = info->state; - if (!(state->flags & ASYNC_INITIALIZED)) + if (!(info->flags & ASYNC_INITIALIZED)) return; #ifdef SIMSERIAL_DEBUG - printk("Shutting down serial port %d (irq %d)....", info->line, - state->irq); + printk("Shutting down serial port %d (irq %d)...\n", info->line, + info->irq); #endif local_irq_save(flags); { - if (state->irq) - free_irq(state->irq, info); + if (info->irq) + free_irq(info->irq, info); if (info->xmit.buf) { free_page((unsigned long) info->xmit.buf); @@ -468,7 +466,7 @@ static void shutdown(struct async_struct * info) if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); - state->flags &= ~ASYNC_INITIALIZED; + info->flags &= ~ASYNC_INITIALIZED; } local_irq_restore(flags); } @@ -485,13 +483,11 @@ static void shutdown(struct async_struct * info) */ static void rs_close(struct tty_struct *tty, struct file * filp) { - struct async_struct * info = (struct async_struct *)tty->driver_data; - struct serial_state *state; + struct serial_state *info = tty->driver_data; unsigned long flags; - if (!info ) return; - - state = info->state; + if (!info) + return; local_irq_save(flags); if (tty_hung_up_p(filp)) { @@ -502,30 +498,30 @@ static void rs_close(struct tty_struct *tty, struct file * filp) return; } #ifdef SIMSERIAL_DEBUG - printk("rs_close ttys%d, count = %d\n", info->line, state->count); + printk("rs_close ttys%d, count = %d\n", info->line, info->count); #endif - if ((tty->count == 1) && (state->count != 1)) { + if ((tty->count == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. state->count should always + * structure will be freed. info->count should always * be one in these conditions. If it's greater than * one, we've got real problems, since it means the * serial port won't be shutdown. */ printk(KERN_ERR "rs_close: bad serial port count; tty->count is 1, " - "state->count is %d\n", state->count); - state->count = 1; + "info->count is %d\n", info->count); + info->count = 1; } - if (--state->count < 0) { + if (--info->count < 0) { printk(KERN_ERR "rs_close: bad serial port count for ttys%d: %d\n", - state->line, state->count); - state->count = 0; + info->line, info->count); + info->count = 0; } - if (state->count) { + if (info->count) { local_irq_restore(flags); return; } - state->flags |= ASYNC_CLOSING; + info->flags |= ASYNC_CLOSING; local_irq_restore(flags); /* @@ -537,11 +533,11 @@ static void rs_close(struct tty_struct *tty, struct file * filp) tty_ldisc_flush(tty); info->tty = NULL; if (info->blocked_open) { - if (state->close_delay) - schedule_timeout_interruptible(state->close_delay); + if (info->close_delay) + schedule_timeout_interruptible(info->close_delay); wake_up_interruptible(&info->open_wait); } - state->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); wake_up_interruptible(&info->close_wait); } @@ -558,59 +554,28 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) */ static void rs_hangup(struct tty_struct *tty) { - struct async_struct * info = (struct async_struct *)tty->driver_data; - struct serial_state *state = info->state; + struct serial_state *info = tty->driver_data; #ifdef SIMSERIAL_DEBUG printk("rs_hangup: called\n"); #endif rs_flush_buffer(tty); - if (state->flags & ASYNC_CLOSING) + if (info->flags & ASYNC_CLOSING) return; shutdown(info); - state->count = 0; - state->flags &= ~ASYNC_NORMAL_ACTIVE; + info->count = 0; + info->flags &= ~ASYNC_NORMAL_ACTIVE; info->tty = NULL; wake_up_interruptible(&info->open_wait); } -static int get_async_struct(int line, struct async_struct **ret_info) -{ - struct async_struct *info; - struct serial_state *sstate; - - sstate = rs_table + line; - sstate->count++; - if (sstate->info) { - *ret_info = sstate->info; - return 0; - } - info = kzalloc(sizeof(struct async_struct), GFP_KERNEL); - if (!info) { - sstate->count--; - return -ENOMEM; - } - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - info->state = sstate; - if (sstate->info) { - kfree(info); - *ret_info = sstate->info; - return 0; - } - *ret_info = sstate->info = info; - return 0; -} - -static int -startup(struct async_struct *info) +static int startup(struct serial_state *state) { unsigned long flags; int retval=0; - struct serial_state *state= info->state; unsigned long page; page = get_zeroed_page(GFP_KERNEL); @@ -625,17 +590,18 @@ startup(struct async_struct *info) } if (!state->port || !state->type) { - if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); + if (state->tty) + set_bit(TTY_IO_ERROR, &state->tty->flags); free_page(page); goto errout; } - if (info->xmit.buf) + if (state->xmit.buf) free_page(page); else - info->xmit.buf = (unsigned char *) page; + state->xmit.buf = (unsigned char *) page; #ifdef SIMSERIAL_DEBUG - printk("startup: ttys%d (irq %d)...", info->line, state->irq); + printk("startup: ttys%d (irq %d)...", state->line, state->irq); #endif /* @@ -643,14 +609,15 @@ startup(struct async_struct *info) */ if (state->irq) { retval = request_irq(state->irq, rs_interrupt_single, 0, - "simserial", info); + "simserial", state); if (retval) goto errout; } - if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); + if (state->tty) + clear_bit(TTY_IO_ERROR, &state->tty->flags); - info->xmit.head = info->xmit.tail = 0; + state->xmit.head = state->xmit.tail = 0; #if 0 /* @@ -663,15 +630,15 @@ startup(struct async_struct *info) /* * Set up the tty->alt_speed kludge */ - if (info->tty) { + if (state->tty) { if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->tty->alt_speed = 57600; + state->tty->alt_speed = 57600; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->tty->alt_speed = 115200; + state->tty->alt_speed = 115200; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->tty->alt_speed = 230400; + state->tty->alt_speed = 230400; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->tty->alt_speed = 460800; + state->tty->alt_speed = 460800; } state->flags |= ASYNC_INITIALIZED; @@ -692,20 +659,18 @@ errout: */ static int rs_open(struct tty_struct *tty, struct file * filp) { - struct async_struct *info; + struct serial_state *info = rs_table + tty->index; int retval; unsigned long page; - retval = get_async_struct(tty->index, &info); - if (retval) - return retval; - tty->driver_data = info; + info->count++; info->tty = tty; + tty->driver_data = info; #ifdef SIMSERIAL_DEBUG - printk("rs_open %s, count = %d\n", tty->name, info->state->count); + printk("rs_open %s, count = %d\n", tty->name, info->count); #endif - info->tty->low_latency = (info->state->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; if (!tmp_buf) { page = get_zeroed_page(GFP_KERNEL); @@ -720,12 +685,11 @@ static int rs_open(struct tty_struct *tty, struct file * filp) /* * If the port is the middle of closing, bail out now */ - if (tty_hung_up_p(filp) || - (info->state->flags & ASYNC_CLOSING)) { - if (info->state->flags & ASYNC_CLOSING) + if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); #ifdef SERIAL_DO_RESTART - return ((info->state->flags & ASYNC_HUP_NOTIFY) ? + return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); #else return -EAGAIN; @@ -865,6 +829,8 @@ simrs_init (void) * Let's have a little bit of fun ! */ for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + init_waitqueue_head(&state->open_wait); + init_waitqueue_head(&state->close_wait); if (state->type == PORT_UNKNOWN) continue; diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 7607c6ebd39a..410e8e7e6bfe 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -45,7 +45,7 @@ #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) #define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ - tty->name, (info->state->flags), serial_driver->refcount,info->count,tty->count,s) + tty->name, (info->flags), serial_driver->refcount,info->count,tty->count,s) #else #define DBG_CNT(s) #endif @@ -102,7 +102,7 @@ static struct tty_driver *serial_driver; static unsigned char current_ctl_bits; -static void change_speed(struct async_struct *info, struct ktermios *old); +static void change_speed(struct serial_state *info, struct ktermios *old); static void rs_wait_until_sent(struct tty_struct *tty, int timeout); @@ -115,7 +115,7 @@ static struct serial_state rs_table[1]; #define serial_isroot() (capable(CAP_SYS_ADMIN)) -static inline int serial_paranoia_check(struct async_struct *info, +static inline int serial_paranoia_check(struct serial_state *info, char *name, const char *routine) { #ifdef SERIAL_PARANOIA_CHECK @@ -168,7 +168,7 @@ static __inline__ void rtsdtr_ctrl(int bits) */ static void rs_stop(struct tty_struct *tty) { - struct async_struct *info = tty->driver_data; + struct serial_state *info = tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->name, "rs_stop")) @@ -188,7 +188,7 @@ static void rs_stop(struct tty_struct *tty) static void rs_start(struct tty_struct *tty) { - struct async_struct *info = tty->driver_data; + struct serial_state *info = tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->name, "rs_start")) @@ -229,7 +229,7 @@ static void rs_start(struct tty_struct *tty) * ----------------------------------------------------------------------- */ -static void receive_chars(struct async_struct *info) +static void receive_chars(struct serial_state *info) { int status; int serdatr; @@ -238,7 +238,7 @@ static void receive_chars(struct async_struct *info) struct async_icount *icount; int oe = 0; - icount = &info->state->icount; + icount = &info->icount; status = UART_LSR_DR; /* We obviously have a character! */ serdatr = custom.serdatr; @@ -295,7 +295,7 @@ static void receive_chars(struct async_struct *info) printk("handling break...."); #endif flag = TTY_BREAK; - if (info->state->flags & ASYNC_SAK) + if (info->flags & ASYNC_SAK) do_SAK(tty); } else if (status & UART_LSR_PE) flag = TTY_PARITY; @@ -318,14 +318,14 @@ out: return; } -static void transmit_chars(struct async_struct *info) +static void transmit_chars(struct serial_state *info) { custom.intreq = IF_TBE; mb(); if (info->x_char) { custom.serdat = info->x_char | 0x100; mb(); - info->state->icount.tx++; + info->icount.tx++; info->x_char = 0; return; } @@ -341,7 +341,7 @@ static void transmit_chars(struct async_struct *info) custom.serdat = info->xmit.buf[info->xmit.tail++] | 0x100; mb(); info->xmit.tail = info->xmit.tail & (SERIAL_XMIT_SIZE-1); - info->state->icount.tx++; + info->icount.tx++; if (CIRC_CNT(info->xmit.head, info->xmit.tail, @@ -358,7 +358,7 @@ static void transmit_chars(struct async_struct *info) } } -static void check_modem_status(struct async_struct *info) +static void check_modem_status(struct serial_state *info) { unsigned char status = ciab.pra & (SER_DCD | SER_CTS | SER_DSR); unsigned char dstatus; @@ -369,14 +369,14 @@ static void check_modem_status(struct async_struct *info) current_ctl_bits = status; if (dstatus) { - icount = &info->state->icount; + icount = &info->icount; /* update input line counters */ if (dstatus & SER_DSR) icount->dsr++; if (dstatus & SER_DCD) { icount->dcd++; #ifdef CONFIG_HARD_PPS - if ((info->state->flags & ASYNC_HARDPPS_CD) && + if ((info->flags & ASYNC_HARDPPS_CD) && !(status & SER_DCD)) hardpps(); #endif @@ -386,7 +386,7 @@ static void check_modem_status(struct async_struct *info) wake_up_interruptible(&info->delta_msr_wait); } - if ((info->state->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) { + if ((info->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) { #if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) printk("ttyS%d CD now %s...", info->line, (!(status & SER_DCD)) ? "on" : "off"); @@ -401,7 +401,7 @@ static void check_modem_status(struct async_struct *info) tty_hangup(info->tty); } } - if (info->state->flags & ASYNC_CTS_FLOW) { + if (info->flags & ASYNC_CTS_FLOW) { if (info->tty->hw_stopped) { if (!(status & SER_CTS)) { #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) @@ -437,7 +437,7 @@ static void check_modem_status(struct async_struct *info) static irqreturn_t ser_vbl_int( int irq, void *data) { /* vbl is just a periodic interrupt we tie into to update modem status */ - struct async_struct *info = data; + struct serial_state *info = data; /* * TBD - is it better to unregister from this interrupt or to * ignore it if MSI is clear ? @@ -449,14 +449,13 @@ static irqreturn_t ser_vbl_int( int irq, void *data) static irqreturn_t ser_rx_int(int irq, void *dev_id) { - struct serial_state *state = dev_id; - struct async_struct *info = state->info; + struct serial_state *info = dev_id; #ifdef SERIAL_DEBUG_INTR printk("ser_rx_int..."); #endif - if (!info || !info->tty) + if (!info->tty) return IRQ_NONE; receive_chars(info); @@ -468,15 +467,14 @@ static irqreturn_t ser_rx_int(int irq, void *dev_id) static irqreturn_t ser_tx_int(int irq, void *dev_id) { - struct serial_state *state = dev_id; - struct async_struct *info = state->info; + struct serial_state *info = dev_id; if (custom.serdatr & SDR_TBE) { #ifdef SERIAL_DEBUG_INTR printk("ser_tx_int..."); #endif - if (!info || !info->tty) + if (!info->tty) return IRQ_NONE; transmit_chars(info); @@ -502,7 +500,7 @@ static irqreturn_t ser_tx_int(int irq, void *dev_id) * --------------------------------------------------------------- */ -static int startup(struct async_struct * info) +static int startup(struct serial_state *info) { unsigned long flags; int retval=0; @@ -514,7 +512,7 @@ static int startup(struct async_struct * info) local_irq_save(flags); - if (info->state->flags & ASYNC_INITIALIZED) { + if (info->flags & ASYNC_INITIALIZED) { free_page(page); goto errout; } @@ -565,13 +563,13 @@ static int startup(struct async_struct * info) * Set up the tty->alt_speed kludge */ if (info->tty) { - if ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; - if ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) info->tty->alt_speed = 115200; - if ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) info->tty->alt_speed = 230400; - if ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; } @@ -580,7 +578,7 @@ static int startup(struct async_struct * info) */ change_speed(info, NULL); - info->state->flags |= ASYNC_INITIALIZED; + info->flags |= ASYNC_INITIALIZED; local_irq_restore(flags); return 0; @@ -593,15 +591,15 @@ errout: * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. */ -static void shutdown(struct async_struct * info) +static void shutdown(struct serial_state *info) { unsigned long flags; struct serial_state *state; - if (!(info->state->flags & ASYNC_INITIALIZED)) + if (!(info->flags & ASYNC_INITIALIZED)) return; - state = info->state; + state = info; #ifdef SERIAL_DEBUG_OPEN printk("Shutting down serial port %d ....\n", info->line); @@ -640,7 +638,7 @@ static void shutdown(struct async_struct * info) if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); - info->state->flags &= ~ASYNC_INITIALIZED; + info->flags &= ~ASYNC_INITIALIZED; local_irq_restore(flags); } @@ -649,7 +647,7 @@ static void shutdown(struct async_struct * info) * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */ -static void change_speed(struct async_struct *info, +static void change_speed(struct serial_state *info, struct ktermios *old_termios) { int quot = 0, baud_base, baud; @@ -683,10 +681,10 @@ static void change_speed(struct async_struct *info, baud = tty_get_baud_rate(info->tty); if (!baud) baud = 9600; /* B0 transition handled in rs_set_termios */ - baud_base = info->state->baud_base; + baud_base = info->baud_base; if (baud == 38400 && - ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) - quot = info->state->custom_divisor; + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->custom_divisor; else { if (baud == 134) /* Special case since 134 is really 134.5 */ @@ -703,8 +701,8 @@ static void change_speed(struct async_struct *info, if (!baud) baud = 9600; if (baud == 38400 && - ((info->state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) - quot = info->state->custom_divisor; + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->custom_divisor; else { if (baud == 134) /* Special case since 134 is really 134.5 */ @@ -717,22 +715,22 @@ static void change_speed(struct async_struct *info, if (!quot) quot = baud_base / 9600; info->quot = quot; - info->timeout = ((info->state->xmit_fifo_size*HZ*bits*quot) / baud_base); + info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); info->timeout += HZ/50; /* Add .02 seconds of slop */ /* CTS flow control flag and modem status interrupts */ info->IER &= ~UART_IER_MSI; - if (info->state->flags & ASYNC_HARDPPS_CD) + if (info->flags & ASYNC_HARDPPS_CD) info->IER |= UART_IER_MSI; if (cflag & CRTSCTS) { - info->state->flags |= ASYNC_CTS_FLOW; + info->flags |= ASYNC_CTS_FLOW; info->IER |= UART_IER_MSI; } else - info->state->flags &= ~ASYNC_CTS_FLOW; + info->flags &= ~ASYNC_CTS_FLOW; if (cflag & CLOCAL) - info->state->flags &= ~ASYNC_CHECK_CD; + info->flags &= ~ASYNC_CHECK_CD; else { - info->state->flags |= ASYNC_CHECK_CD; + info->flags |= ASYNC_CHECK_CD; info->IER |= UART_IER_MSI; } /* TBD: @@ -791,7 +789,7 @@ static void change_speed(struct async_struct *info, static int rs_put_char(struct tty_struct *tty, unsigned char ch) { - struct async_struct *info; + struct serial_state *info; unsigned long flags; info = tty->driver_data; @@ -818,7 +816,7 @@ static int rs_put_char(struct tty_struct *tty, unsigned char ch) static void rs_flush_chars(struct tty_struct *tty) { - struct async_struct *info = tty->driver_data; + struct serial_state *info = tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->name, "rs_flush_chars")) @@ -843,11 +841,9 @@ static void rs_flush_chars(struct tty_struct *tty) static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count) { int c, ret = 0; - struct async_struct *info; + struct serial_state *info = tty->driver_data; unsigned long flags; - info = tty->driver_data; - if (serial_paranoia_check(info, tty->name, "rs_write")) return 0; @@ -891,7 +887,7 @@ static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count static int rs_write_room(struct tty_struct *tty) { - struct async_struct *info = tty->driver_data; + struct serial_state *info = tty->driver_data; if (serial_paranoia_check(info, tty->name, "rs_write_room")) return 0; @@ -900,7 +896,7 @@ static int rs_write_room(struct tty_struct *tty) static int rs_chars_in_buffer(struct tty_struct *tty) { - struct async_struct *info = tty->driver_data; + struct serial_state *info = tty->driver_data; if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer")) return 0; @@ -909,7 +905,7 @@ static int rs_chars_in_buffer(struct tty_struct *tty) static void rs_flush_buffer(struct tty_struct *tty) { - struct async_struct *info = tty->driver_data; + struct serial_state *info = tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->name, "rs_flush_buffer")) @@ -926,7 +922,7 @@ static void rs_flush_buffer(struct tty_struct *tty) */ static void rs_send_xchar(struct tty_struct *tty, char ch) { - struct async_struct *info = tty->driver_data; + struct serial_state *info = tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->name, "rs_send_char")) @@ -961,7 +957,7 @@ static void rs_send_xchar(struct tty_struct *tty, char ch) */ static void rs_throttle(struct tty_struct * tty) { - struct async_struct *info = tty->driver_data; + struct serial_state *info = tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; @@ -986,7 +982,7 @@ static void rs_throttle(struct tty_struct * tty) static void rs_unthrottle(struct tty_struct * tty) { - struct async_struct *info = tty->driver_data; + struct serial_state *info = tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; @@ -1017,11 +1013,10 @@ static void rs_unthrottle(struct tty_struct * tty) * ------------------------------------------------------------ */ -static int get_serial_info(struct async_struct * info, +static int get_serial_info(struct serial_state *state, struct serial_struct __user * retinfo) { struct serial_struct tmp; - struct serial_state *state = info->state; if (!retinfo) return -EFAULT; @@ -1043,11 +1038,11 @@ static int get_serial_info(struct async_struct * info, return 0; } -static int set_serial_info(struct async_struct * info, +static int set_serial_info(struct serial_state *state, struct serial_struct __user * new_info) { struct serial_struct new_serial; - struct serial_state old_state, *state; + struct serial_state old_state; unsigned int change_irq,change_port; int retval = 0; @@ -1055,7 +1050,6 @@ static int set_serial_info(struct async_struct * info, return -EFAULT; tty_lock(); - state = info->state; old_state = *state; change_irq = new_serial.irq != state->irq; @@ -1094,7 +1088,7 @@ static int set_serial_info(struct async_struct * info, state->custom_divisor = new_serial.custom_divisor; state->close_delay = new_serial.close_delay * HZ/100; state->closing_wait = new_serial.closing_wait * HZ/100; - info->tty->low_latency = (state->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + state->tty->low_latency = (state->flags & ASYNC_LOW_LATENCY) ? 1 : 0; check_and_exit: if (state->flags & ASYNC_INITIALIZED) { @@ -1102,17 +1096,17 @@ check_and_exit: (state->flags & ASYNC_SPD_MASK)) || (old_state.custom_divisor != state->custom_divisor)) { if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->tty->alt_speed = 57600; + state->tty->alt_speed = 57600; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->tty->alt_speed = 115200; + state->tty->alt_speed = 115200; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->tty->alt_speed = 230400; + state->tty->alt_speed = 230400; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->tty->alt_speed = 460800; - change_speed(info, NULL); + state->tty->alt_speed = 460800; + change_speed(state, NULL); } } else - retval = startup(info); + retval = startup(state); tty_unlock(); return retval; } @@ -1128,7 +1122,7 @@ check_and_exit: * transmit holding register is empty. This functionality * allows an RS485 driver to be written in user space. */ -static int get_lsr_info(struct async_struct * info, unsigned int __user *value) +static int get_lsr_info(struct serial_state *info, unsigned int __user *value) { unsigned char status; unsigned int result; @@ -1147,7 +1141,7 @@ static int get_lsr_info(struct async_struct * info, unsigned int __user *value) static int rs_tiocmget(struct tty_struct *tty) { - struct async_struct * info = tty->driver_data; + struct serial_state *info = tty->driver_data; unsigned char control, status; unsigned long flags; @@ -1170,7 +1164,7 @@ static int rs_tiocmget(struct tty_struct *tty) static int rs_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { - struct async_struct * info = tty->driver_data; + struct serial_state *info = tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->name, "rs_ioctl")) @@ -1197,7 +1191,7 @@ static int rs_tiocmset(struct tty_struct *tty, unsigned int set, */ static int rs_break(struct tty_struct *tty, int break_state) { - struct async_struct * info = tty->driver_data; + struct serial_state *info = tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->name, "rs_break")) @@ -1222,12 +1216,12 @@ static int rs_break(struct tty_struct *tty, int break_state) static int rs_get_icount(struct tty_struct *tty, struct serial_icounter_struct *icount) { - struct async_struct *info = tty->driver_data; + struct serial_state *info = tty->driver_data; struct async_icount cnow; unsigned long flags; local_irq_save(flags); - cnow = info->state->icount; + cnow = info->icount; local_irq_restore(flags); icount->cts = cnow.cts; icount->dsr = cnow.dsr; @@ -1247,7 +1241,7 @@ static int rs_get_icount(struct tty_struct *tty, static int rs_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { - struct async_struct * info = tty->driver_data; + struct serial_state *info = tty->driver_data; struct async_icount cprev, cnow; /* kernel counter temps */ void __user *argp = (void __user *)arg; unsigned long flags; @@ -1275,7 +1269,7 @@ static int rs_ioctl(struct tty_struct *tty, case TIOCSERGSTRUCT: if (copy_to_user(argp, - info, sizeof(struct async_struct))) + info, sizeof(struct serial_state))) return -EFAULT; return 0; @@ -1288,7 +1282,7 @@ static int rs_ioctl(struct tty_struct *tty, case TIOCMIWAIT: local_irq_save(flags); /* note the counters on entry */ - cprev = info->state->icount; + cprev = info->icount; local_irq_restore(flags); while (1) { interruptible_sleep_on(&info->delta_msr_wait); @@ -1296,7 +1290,7 @@ static int rs_ioctl(struct tty_struct *tty, if (signal_pending(current)) return -ERESTARTSYS; local_irq_save(flags); - cnow = info->state->icount; /* atomic copy */ + cnow = info->icount; /* atomic copy */ local_irq_restore(flags); if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) @@ -1325,7 +1319,7 @@ static int rs_ioctl(struct tty_struct *tty, static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { - struct async_struct *info = tty->driver_data; + struct serial_state *info = tty->driver_data; unsigned long flags; unsigned int cflag = tty->termios->c_cflag; @@ -1385,15 +1379,12 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) */ static void rs_close(struct tty_struct *tty, struct file * filp) { - struct async_struct * info = tty->driver_data; - struct serial_state *state; + struct serial_state *state = tty->driver_data; unsigned long flags; - if (!info || serial_paranoia_check(info, tty->name, "rs_close")) + if (!state || serial_paranoia_check(state, tty->name, "rs_close")) return; - state = info->state; - local_irq_save(flags); if (tty_hung_up_p(filp)) { @@ -1403,7 +1394,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) } #ifdef SERIAL_DEBUG_OPEN - printk("rs_close ttys%d, count = %d\n", info->line, state->count); + printk("rs_close ttys%d, count = %d\n", state->line, state->count); #endif if ((tty->count == 1) && (state->count != 1)) { /* @@ -1441,7 +1432,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) * interrupt driver to stop checking the data ready bit in the * line status register. */ - info->read_status_mask &= ~UART_LSR_DR; + state->read_status_mask &= ~UART_LSR_DR; if (state->flags & ASYNC_INITIALIZED) { /* disable receive interrupts */ custom.intena = IF_RBF; @@ -1455,22 +1446,22 @@ static void rs_close(struct tty_struct *tty, struct file * filp) * has completely drained; this is especially * important if there is a transmit FIFO! */ - rs_wait_until_sent(tty, info->timeout); + rs_wait_until_sent(tty, state->timeout); } - shutdown(info); + shutdown(state); rs_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; - info->tty = NULL; - if (info->blocked_open) { + state->tty = NULL; + if (state->blocked_open) { if (state->close_delay) { msleep_interruptible(jiffies_to_msecs(state->close_delay)); } - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&state->open_wait); } state->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); + wake_up_interruptible(&state->close_wait); local_irq_restore(flags); } @@ -1479,14 +1470,14 @@ static void rs_close(struct tty_struct *tty, struct file * filp) */ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) { - struct async_struct * info = tty->driver_data; + struct serial_state *info = tty->driver_data; unsigned long orig_jiffies, char_time; int lsr; if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent")) return; - if (info->state->xmit_fifo_size == 0) + if (info->xmit_fifo_size == 0) return; /* Just in case.... */ orig_jiffies = jiffies; @@ -1499,7 +1490,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) * Note: we have to use pretty tight timings here to satisfy * the NIST-PCTS. */ - char_time = (info->timeout - HZ/50) / info->state->xmit_fifo_size; + char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; char_time = char_time / 5; if (char_time == 0) char_time = 1; @@ -1542,18 +1533,15 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) */ static void rs_hangup(struct tty_struct *tty) { - struct async_struct * info = tty->driver_data; - struct serial_state *state = info->state; + struct serial_state *info = tty->driver_data; if (serial_paranoia_check(info, tty->name, "rs_hangup")) return; - state = info->state; - rs_flush_buffer(tty); shutdown(info); - state->count = 0; - state->flags &= ~ASYNC_NORMAL_ACTIVE; + info->count = 0; + info->flags &= ~ASYNC_NORMAL_ACTIVE; info->tty = NULL; wake_up_interruptible(&info->open_wait); } @@ -1564,14 +1552,13 @@ static void rs_hangup(struct tty_struct *tty) * ------------------------------------------------------------ */ static int block_til_ready(struct tty_struct *tty, struct file * filp, - struct async_struct *info) + struct serial_state *info) { #ifdef DECLARE_WAITQUEUE DECLARE_WAITQUEUE(wait, current); #else struct wait_queue wait = { current, NULL }; #endif - struct serial_state *state = info->state; int retval; int do_clocal = 0, extra_count = 0; unsigned long flags; @@ -1581,11 +1568,11 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, * until it's done, and then try again. */ if (tty_hung_up_p(filp) || - (state->flags & ASYNC_CLOSING)) { - if (state->flags & ASYNC_CLOSING) + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); #ifdef SERIAL_DO_RESTART - return ((state->flags & ASYNC_HUP_NOTIFY) ? + return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); #else return -EAGAIN; @@ -1598,7 +1585,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - state->flags |= ASYNC_NORMAL_ACTIVE; + info->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -1608,7 +1595,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, state->count is dropped by one, so that + * this loop, info->count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ @@ -1616,12 +1603,12 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, add_wait_queue(&info->open_wait, &wait); #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready before block: ttys%d, count = %d\n", - state->line, state->count); + info->line, info->count); #endif local_irq_save(flags); if (!tty_hung_up_p(filp)) { extra_count = 1; - state->count--; + info->count--; } local_irq_restore(flags); info->blocked_open++; @@ -1632,9 +1619,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, local_irq_restore(flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(state->flags & ASYNC_INITIALIZED)) { + !(info->flags & ASYNC_INITIALIZED)) { #ifdef SERIAL_DO_RESTART - if (state->flags & ASYNC_HUP_NOTIFY) + if (info->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; @@ -1643,7 +1630,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, #endif break; } - if (!(state->flags & ASYNC_CLOSING) && + if (!(info->flags & ASYNC_CLOSING) && (do_clocal || (!(ciab.pra & SER_DCD)) )) break; if (signal_pending(current)) { @@ -1652,7 +1639,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, } #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready blocking: ttys%d, count = %d\n", - info->line, state->count); + info->line, info->count); #endif tty_unlock(); schedule(); @@ -1661,46 +1648,15 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, __set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); if (extra_count) - state->count++; + info->count++; info->blocked_open--; #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready after blocking: ttys%d, count = %d\n", - info->line, state->count); + info->line, info->count); #endif if (retval) return retval; - state->flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} - -static int get_async_struct(int line, struct async_struct **ret_info) -{ - struct async_struct *info; - struct serial_state *sstate; - - sstate = rs_table + line; - sstate->count++; - if (sstate->info) { - *ret_info = sstate->info; - return 0; - } - info = kzalloc(sizeof(struct async_struct), GFP_KERNEL); - if (!info) { - sstate->count--; - return -ENOMEM; - } -#ifdef DECLARE_WAITQUEUE - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - init_waitqueue_head(&info->delta_msr_wait); -#endif - info->state = sstate; - if (sstate->info) { - kfree(info); - *ret_info = sstate->info; - return 0; - } - *ret_info = sstate->info = info; + info->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -1712,32 +1668,29 @@ static int get_async_struct(int line, struct async_struct **ret_info) */ static int rs_open(struct tty_struct *tty, struct file * filp) { - struct async_struct *info; + struct serial_state *info = rs_table + tty->index; int retval; - retval = get_async_struct(tty->index, &info); - if (retval) { - return retval; - } - tty->driver_data = info; + info->count++; info->tty = tty; + tty->driver_data = info; if (serial_paranoia_check(info, tty->name, "rs_open")) return -ENODEV; #ifdef SERIAL_DEBUG_OPEN - printk("rs_open %s, count = %d\n", tty->name, info->state->count); + printk("rs_open %s, count = %d\n", tty->name, info->count); #endif - info->tty->low_latency = (info->state->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; /* * If the port is the middle of closing, bail out now */ if (tty_hung_up_p(filp) || - (info->state->flags & ASYNC_CLOSING)) { - if (info->state->flags & ASYNC_CLOSING) + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); #ifdef SERIAL_DO_RESTART - return ((info->state->flags & ASYNC_HUP_NOTIFY) ? + return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); #else return -EAGAIN; @@ -1773,24 +1726,14 @@ static int rs_open(struct tty_struct *tty, struct file * filp) static inline void line_info(struct seq_file *m, struct serial_state *state) { - struct async_struct *info = state->info, scr_info; char stat_buf[30], control, status; unsigned long flags; seq_printf(m, "%d: uart:amiga_builtin",state->line); - /* - * Figure out the current RS-232 lines - */ - if (!info) { - info = &scr_info; /* This is just for serial_{in,out} */ - - info->quot = 0; - info->tty = NULL; - } local_irq_save(flags); status = ciab.pra; - control = info ? info->MCR : status; + control = (state->flags & ASYNC_INITIALIZED) ? state->MCR : status; local_irq_restore(flags); stat_buf[0] = 0; @@ -1806,9 +1749,8 @@ static inline void line_info(struct seq_file *m, struct serial_state *state) if(!(status & SER_DCD)) strcat(stat_buf, "|CD"); - if (info->quot) { - seq_printf(m, " baud:%d", state->baud_base / info->quot); - } + if (state->quot) + seq_printf(m, " baud:%d", state->baud_base / state->quot); seq_printf(m, " tx:%d rx:%d", state->icount.tx, state->icount.rx); @@ -1938,6 +1880,9 @@ static int __init amiga_serial_probe(struct platform_device *pdev) state->icount.rx = state->icount.tx = 0; state->icount.frame = state->icount.parity = 0; state->icount.overrun = state->icount.brk = 0; + init_waitqueue_head(&state->open_wait); + init_waitqueue_head(&state->close_wait); + init_waitqueue_head(&state->delta_msr_wait); printk(KERN_INFO "ttyS%d is the amiga builtin serial port\n", state->line); @@ -1993,7 +1938,6 @@ static int __exit amiga_serial_remove(struct platform_device *pdev) { int error; struct serial_state *state = platform_get_drvdata(pdev); - struct async_struct *info = state->info; /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ if ((error = tty_unregister_driver(serial_driver))) @@ -2001,11 +1945,8 @@ static int __exit amiga_serial_remove(struct platform_device *pdev) error); put_tty_driver(serial_driver); - rs_table[0].info = NULL; - kfree(info); - - free_irq(IRQ_AMIGA_TBE, rs_table); - free_irq(IRQ_AMIGA_RBF, rs_table); + free_irq(IRQ_AMIGA_TBE, state); + free_irq(IRQ_AMIGA_RBF, state); platform_set_drvdata(pdev, NULL); diff --git a/include/linux/serialP.h b/include/linux/serialP.h index b8543f902453..984f5ba8da4e 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -38,24 +38,24 @@ struct serial_state { unsigned short close_delay; unsigned short closing_wait; /* time to wait before closing */ struct async_icount icount; - struct async_struct *info; -}; -struct async_struct { - struct serial_state *state; - struct tty_struct *tty; + /* amiserial */ int read_status_mask; int ignore_status_mask; int timeout; int quot; - int x_char; /* xon/xoff character */ int IER; /* Interrupt Enable Register */ int MCR; /* Modem control register */ + wait_queue_head_t delta_msr_wait; + /* simserial */ + int x_char; /* xon/xoff character */ int blocked_open; /* # of blocked opens */ struct circ_buf xmit; wait_queue_head_t open_wait; wait_queue_head_t close_wait; - wait_queue_head_t delta_msr_wait; + struct tty_struct *tty; + /* /simserial */ + /* /amiserial */ }; #endif /* _LINUX_SERIAL_H */ -- cgit From 87758791c99715433841f1c054b49166506513e4 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:52:24 +0100 Subject: TTY: amiserial/simserial, use tty_port Add tty_port to serial_state and start using common tty port members from tty_port in amiserial and simserial. The rest will follow one by one. Signed-off-by: Jiri Slaby Cc: Geert Uytterhoeven Cc: Tony Luck Cc: Fenghua Yu Signed-off-by: Greg Kroah-Hartman --- arch/ia64/hp/sim/simserial.c | 24 ++++++++--------- drivers/tty/amiserial.c | 63 ++++++++++++++++++++++---------------------- include/linux/serialP.h | 7 ++--- 3 files changed, 45 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index a76a27ed3de0..614c091b203f 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -165,7 +165,7 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) { struct serial_state *info = dev_id; - if (!info->tty) { + if (!info->tport.tty) { printk(KERN_INFO "simrs_interrupt_single: info|tty=0 info=%p problem\n", info); return IRQ_NONE; } @@ -173,7 +173,7 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) * pretty simple in our case, because we only get interrupts * on inbound traffic */ - receive_chars(info->tty); + receive_chars(info->tport.tty); return IRQ_HANDLED; } @@ -533,14 +533,14 @@ static void rs_close(struct tty_struct *tty, struct file * filp) shutdown(tty, info); rs_flush_buffer(tty); tty_ldisc_flush(tty); - info->tty = NULL; - if (info->blocked_open) { + info->tport.tty = NULL; + if (info->tport.blocked_open) { if (info->close_delay) schedule_timeout_interruptible(info->close_delay); - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->tport.open_wait); } info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); + wake_up_interruptible(&info->tport.close_wait); } /* @@ -569,8 +569,8 @@ static void rs_hangup(struct tty_struct *tty) info->count = 0; info->flags &= ~ASYNC_NORMAL_ACTIVE; - info->tty = NULL; - wake_up_interruptible(&info->open_wait); + info->tport.tty = NULL; + wake_up_interruptible(&info->tport.open_wait); } @@ -662,8 +662,9 @@ static int rs_open(struct tty_struct *tty, struct file * filp) unsigned long page; info->count++; - info->tty = tty; + info->tport.tty = tty; tty->driver_data = info; + tty->port = &info->tport; #ifdef SIMSERIAL_DEBUG printk("rs_open %s, count = %d\n", tty->name, info->count); @@ -685,7 +686,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp) */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { if (info->flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->close_wait); + interruptible_sleep_on(&info->tport.close_wait); #ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); @@ -827,8 +828,7 @@ simrs_init (void) * Let's have a little bit of fun ! */ for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { - init_waitqueue_head(&state->open_wait); - init_waitqueue_head(&state->close_wait); + tty_port_init(&state->tport); if (state->type == PORT_UNKNOWN) continue; diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 5b87744748d5..71d3331d6e84 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -234,7 +234,7 @@ static void receive_chars(struct serial_state *info) { int status; int serdatr; - struct tty_struct *tty = info->tty; + struct tty_struct *tty = info->tport.tty; unsigned char ch, flag; struct async_icount *icount; int oe = 0; @@ -331,8 +331,8 @@ static void transmit_chars(struct serial_state *info) return; } if (info->xmit.head == info->xmit.tail - || info->tty->stopped - || info->tty->hw_stopped) { + || info->tport.tty->stopped + || info->tport.tty->hw_stopped) { info->IER &= ~UART_IER_THRI; custom.intena = IF_TBE; mb(); @@ -347,7 +347,7 @@ static void transmit_chars(struct serial_state *info) if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) < WAKEUP_CHARS) - tty_wakeup(info->tty); + tty_wakeup(info->tport.tty); #ifdef SERIAL_DEBUG_INTR printk("THRE..."); @@ -384,7 +384,7 @@ static void check_modem_status(struct serial_state *info) } if (dstatus & SER_CTS) icount->cts++; - wake_up_interruptible(&info->delta_msr_wait); + wake_up_interruptible(&info->tport.delta_msr_wait); } if ((info->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) { @@ -393,29 +393,29 @@ static void check_modem_status(struct serial_state *info) (!(status & SER_DCD)) ? "on" : "off"); #endif if (!(status & SER_DCD)) - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->tport.open_wait); else { #ifdef SERIAL_DEBUG_OPEN printk("doing serial hangup..."); #endif - if (info->tty) - tty_hangup(info->tty); + if (info->tport.tty) + tty_hangup(info->tport.tty); } } if (info->flags & ASYNC_CTS_FLOW) { - if (info->tty->hw_stopped) { + if (info->tport.tty->hw_stopped) { if (!(status & SER_CTS)) { #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx start..."); #endif - info->tty->hw_stopped = 0; + info->tport.tty->hw_stopped = 0; info->IER |= UART_IER_THRI; custom.intena = IF_SETCLR | IF_TBE; mb(); /* set a pending Tx Interrupt, transmitter should restart now */ custom.intreq = IF_SETCLR | IF_TBE; mb(); - tty_wakeup(info->tty); + tty_wakeup(info->tport.tty); return; } } else { @@ -423,7 +423,7 @@ static void check_modem_status(struct serial_state *info) #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx stop..."); #endif - info->tty->hw_stopped = 1; + info->tport.tty->hw_stopped = 1; info->IER &= ~UART_IER_THRI; /* disable Tx interrupt and remove any pending interrupts */ custom.intena = IF_TBE; @@ -456,7 +456,7 @@ static irqreturn_t ser_rx_int(int irq, void *dev_id) printk("ser_rx_int..."); #endif - if (!info->tty) + if (!info->tport.tty) return IRQ_NONE; receive_chars(info); @@ -475,7 +475,7 @@ static irqreturn_t ser_tx_int(int irq, void *dev_id) printk("ser_tx_int..."); #endif - if (!info->tty) + if (!info->tport.tty) return IRQ_NONE; transmit_chars(info); @@ -607,7 +607,7 @@ static void shutdown(struct tty_struct *tty, struct serial_state *info) * clear delta_msr_wait queue to avoid mem leaks: we may free the irq * here so the queue might never be waken up */ - wake_up_interruptible(&info->delta_msr_wait); + wake_up_interruptible(&info->tport.delta_msr_wait); /* * Free the IRQ, if necessary @@ -1274,7 +1274,7 @@ static int rs_ioctl(struct tty_struct *tty, cprev = info->icount; local_irq_restore(flags); while (1) { - interruptible_sleep_on(&info->delta_msr_wait); + interruptible_sleep_on(&info->tport.delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; @@ -1442,15 +1442,15 @@ static void rs_close(struct tty_struct *tty, struct file * filp) tty_ldisc_flush(tty); tty->closing = 0; - state->tty = NULL; - if (state->blocked_open) { + state->tport.tty = NULL; + if (state->tport.blocked_open) { if (state->close_delay) { msleep_interruptible(jiffies_to_msecs(state->close_delay)); } - wake_up_interruptible(&state->open_wait); + wake_up_interruptible(&state->tport.open_wait); } state->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&state->close_wait); + wake_up_interruptible(&state->tport.close_wait); local_irq_restore(flags); } @@ -1531,8 +1531,8 @@ static void rs_hangup(struct tty_struct *tty) shutdown(tty, info); info->count = 0; info->flags &= ~ASYNC_NORMAL_ACTIVE; - info->tty = NULL; - wake_up_interruptible(&info->open_wait); + info->tport.tty = NULL; + wake_up_interruptible(&info->tport.open_wait); } /* @@ -1559,7 +1559,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { if (info->flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->close_wait); + interruptible_sleep_on(&info->tport.close_wait); #ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); @@ -1589,7 +1589,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&info->open_wait, &wait); + add_wait_queue(&info->tport.open_wait, &wait); #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready before block: ttys%d, count = %d\n", info->line, info->count); @@ -1600,7 +1600,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, info->count--; } local_irq_restore(flags); - info->blocked_open++; + info->tport.blocked_open++; while (1) { local_irq_save(flags); if (tty->termios->c_cflag & CBAUD) @@ -1635,10 +1635,10 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, tty_lock(); } __set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); + remove_wait_queue(&info->tport.open_wait, &wait); if (extra_count) info->count++; - info->blocked_open--; + info->tport.blocked_open--; #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready after blocking: ttys%d, count = %d\n", info->line, info->count); @@ -1661,8 +1661,9 @@ static int rs_open(struct tty_struct *tty, struct file * filp) int retval; info->count++; - info->tty = tty; + info->tport.tty = tty; tty->driver_data = info; + tty->port = &info->tport; if (serial_paranoia_check(info, tty->name, "rs_open")) return -ENODEV; @@ -1677,7 +1678,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp) if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { if (info->flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->close_wait); + interruptible_sleep_on(&info->tport.close_wait); #ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); @@ -1869,9 +1870,7 @@ static int __init amiga_serial_probe(struct platform_device *pdev) state->icount.rx = state->icount.tx = 0; state->icount.frame = state->icount.parity = 0; state->icount.overrun = state->icount.brk = 0; - init_waitqueue_head(&state->open_wait); - init_waitqueue_head(&state->close_wait); - init_waitqueue_head(&state->delta_msr_wait); + tty_port_init(&state->tport); printk(KERN_INFO "ttyS%d is the amiga builtin serial port\n", state->line); diff --git a/include/linux/serialP.h b/include/linux/serialP.h index 984f5ba8da4e..32d45b869cbc 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -23,6 +23,7 @@ #include #include #include +#include #include struct serial_state { @@ -38,6 +39,7 @@ struct serial_state { unsigned short close_delay; unsigned short closing_wait; /* time to wait before closing */ struct async_icount icount; + struct tty_port tport; /* amiserial */ int read_status_mask; @@ -46,14 +48,9 @@ struct serial_state { int quot; int IER; /* Interrupt Enable Register */ int MCR; /* Modem control register */ - wait_queue_head_t delta_msr_wait; /* simserial */ int x_char; /* xon/xoff character */ - int blocked_open; /* # of blocked opens */ struct circ_buf xmit; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; - struct tty_struct *tty; /* /simserial */ /* /amiserial */ }; -- cgit From 799be6ff2fd7294f428a9e68a7786490c862c1af Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:52:25 +0100 Subject: TTY: amiserial/simserial, use close delays from tty_port Note that previously simserial set the delay to 0. So we preserve that. BUT, is it correct? Signed-off-by: Jiri Slaby Cc: Geert Uytterhoeven Cc: Tony Luck Cc: Fenghua Yu Signed-off-by: Greg Kroah-Hartman --- arch/ia64/hp/sim/simserial.c | 5 +++-- drivers/tty/amiserial.c | 20 +++++++++----------- include/linux/serialP.h | 2 -- 3 files changed, 12 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index 614c091b203f..fb324b345e88 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -535,8 +535,8 @@ static void rs_close(struct tty_struct *tty, struct file * filp) tty_ldisc_flush(tty); info->tport.tty = NULL; if (info->tport.blocked_open) { - if (info->close_delay) - schedule_timeout_interruptible(info->close_delay); + if (info->tport.close_delay) + schedule_timeout_interruptible(info->tport.close_delay); wake_up_interruptible(&info->tport.open_wait); } info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); @@ -829,6 +829,7 @@ simrs_init (void) */ for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { tty_port_init(&state->tport); + state->tport.close_delay = 0; /* XXX really 0? */ if (state->type == PORT_UNKNOWN) continue; diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 71d3331d6e84..06e3a0990c8b 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1022,8 +1022,8 @@ static int get_serial_info(struct serial_state *state, tmp.flags = state->flags; tmp.xmit_fifo_size = state->xmit_fifo_size; tmp.baud_base = state->baud_base; - tmp.close_delay = state->close_delay; - tmp.closing_wait = state->closing_wait; + tmp.close_delay = state->tport.close_delay; + tmp.closing_wait = state->tport.closing_wait; tmp.custom_divisor = state->custom_divisor; tty_unlock(); if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) @@ -1052,7 +1052,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, if (!serial_isroot()) { if ((new_serial.baud_base != state->baud_base) || - (new_serial.close_delay != state->close_delay) || + (new_serial.close_delay != state->tport.close_delay) || (new_serial.xmit_fifo_size != state->xmit_fifo_size) || ((new_serial.flags & ~ASYNC_USR_MASK) != (state->flags & ~ASYNC_USR_MASK))) @@ -1077,8 +1077,8 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, state->flags = ((state->flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); state->custom_divisor = new_serial.custom_divisor; - state->close_delay = new_serial.close_delay * HZ/100; - state->closing_wait = new_serial.closing_wait * HZ/100; + state->tport.close_delay = new_serial.close_delay * HZ/100; + state->tport.closing_wait = new_serial.closing_wait * HZ/100; tty->low_latency = (state->flags & ASYNC_LOW_LATENCY) ? 1 : 0; check_and_exit: @@ -1413,8 +1413,8 @@ static void rs_close(struct tty_struct *tty, struct file * filp) * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; - if (state->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, state->closing_wait); + if (state->tport.closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, state->tport.closing_wait); /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the @@ -1444,8 +1444,8 @@ static void rs_close(struct tty_struct *tty, struct file * filp) tty->closing = 0; state->tport.tty = NULL; if (state->tport.blocked_open) { - if (state->close_delay) { - msleep_interruptible(jiffies_to_msecs(state->close_delay)); + if (state->tport.close_delay) { + msleep_interruptible(jiffies_to_msecs(state->tport.close_delay)); } wake_up_interruptible(&state->tport.open_wait); } @@ -1863,8 +1863,6 @@ static int __init amiga_serial_probe(struct platform_device *pdev) state->port = (int)&custom.serdatr; /* Just to give it a value */ state->line = 0; state->custom_divisor = 0; - state->close_delay = 5*HZ/10; - state->closing_wait = 30*HZ; state->icount.cts = state->icount.dsr = state->icount.rng = state->icount.dcd = 0; state->icount.rx = state->icount.tx = 0; diff --git a/include/linux/serialP.h b/include/linux/serialP.h index 32d45b869cbc..997edd008b92 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -36,8 +36,6 @@ struct serial_state { int xmit_fifo_size; int custom_divisor; int count; - unsigned short close_delay; - unsigned short closing_wait; /* time to wait before closing */ struct async_icount icount; struct tty_port tport; -- cgit From 12c8035435fa16e3f6b18049bb1d7815c00a7a58 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:52:26 +0100 Subject: TTY: amiserial/simserial, use count from tty_port Nothing special. Just remove count from serial_state and change all users to use tty_port. Signed-off-by: Jiri Slaby Cc: Geert Uytterhoeven Cc: Tony Luck Cc: Fenghua Yu Signed-off-by: Greg Kroah-Hartman --- arch/ia64/hp/sim/simserial.c | 24 ++++++++++++------------ drivers/tty/amiserial.c | 34 +++++++++++++++++----------------- include/linux/serialP.h | 1 - 3 files changed, 29 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index fb324b345e88..baa2b1ec00a0 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -500,26 +500,26 @@ static void rs_close(struct tty_struct *tty, struct file * filp) return; } #ifdef SIMSERIAL_DEBUG - printk("rs_close ttys%d, count = %d\n", info->line, info->count); + printk("rs_close ttys%d, count = %d\n", info->line, info->tport.count); #endif - if ((tty->count == 1) && (info->count != 1)) { + if ((tty->count == 1) && (info->tport.count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. info->count should always + * structure will be freed. info->tport.count should always * be one in these conditions. If it's greater than * one, we've got real problems, since it means the * serial port won't be shutdown. */ printk(KERN_ERR "rs_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; + "info->tport.count is %d\n", info->tport.count); + info->tport.count = 1; } - if (--info->count < 0) { + if (--info->tport.count < 0) { printk(KERN_ERR "rs_close: bad serial port count for ttys%d: %d\n", - info->line, info->count); - info->count = 0; + info->line, info->tport.count); + info->tport.count = 0; } - if (info->count) { + if (info->tport.count) { local_irq_restore(flags); return; } @@ -567,7 +567,7 @@ static void rs_hangup(struct tty_struct *tty) return; shutdown(tty, info); - info->count = 0; + info->tport.count = 0; info->flags &= ~ASYNC_NORMAL_ACTIVE; info->tport.tty = NULL; wake_up_interruptible(&info->tport.open_wait); @@ -661,13 +661,13 @@ static int rs_open(struct tty_struct *tty, struct file * filp) int retval; unsigned long page; - info->count++; + info->tport.count++; info->tport.tty = tty; tty->driver_data = info; tty->port = &info->tport; #ifdef SIMSERIAL_DEBUG - printk("rs_open %s, count = %d\n", tty->name, info->count); + printk("rs_open %s, count = %d\n", tty->name, info->tport.count); #endif tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 06e3a0990c8b..8ad64a0f1251 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1383,26 +1383,26 @@ static void rs_close(struct tty_struct *tty, struct file * filp) } #ifdef SERIAL_DEBUG_OPEN - printk("rs_close ttys%d, count = %d\n", state->line, state->count); + printk("rs_close ttys%d, count = %d\n", state->line, state->tport.count); #endif - if ((tty->count == 1) && (state->count != 1)) { + if ((tty->count == 1) && (state->tport.count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. state->count should always + * structure will be freed. state->tport.count should always * be one in these conditions. If it's greater than * one, we've got real problems, since it means the * serial port won't be shutdown. */ printk("rs_close: bad serial port count; tty->count is 1, " - "state->count is %d\n", state->count); - state->count = 1; + "state->tport.count is %d\n", state->tport.count); + state->tport.count = 1; } - if (--state->count < 0) { + if (--state->tport.count < 0) { printk("rs_close: bad serial port count for ttys%d: %d\n", - state->line, state->count); - state->count = 0; + state->line, state->tport.count); + state->tport.count = 0; } - if (state->count) { + if (state->tport.count) { DBG_CNT("before DEC-2"); local_irq_restore(flags); return; @@ -1529,7 +1529,7 @@ static void rs_hangup(struct tty_struct *tty) rs_flush_buffer(tty); shutdown(tty, info); - info->count = 0; + info->tport.count = 0; info->flags &= ~ASYNC_NORMAL_ACTIVE; info->tport.tty = NULL; wake_up_interruptible(&info->tport.open_wait); @@ -1584,7 +1584,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that + * this loop, info->tport.count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ @@ -1592,12 +1592,12 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, add_wait_queue(&info->tport.open_wait, &wait); #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready before block: ttys%d, count = %d\n", - info->line, info->count); + info->line, info->tport.count); #endif local_irq_save(flags); if (!tty_hung_up_p(filp)) { extra_count = 1; - info->count--; + info->tport.count--; } local_irq_restore(flags); info->tport.blocked_open++; @@ -1628,7 +1628,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, } #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready blocking: ttys%d, count = %d\n", - info->line, info->count); + info->line, info->tport.count); #endif tty_unlock(); schedule(); @@ -1637,11 +1637,11 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, __set_current_state(TASK_RUNNING); remove_wait_queue(&info->tport.open_wait, &wait); if (extra_count) - info->count++; + info->tport.count++; info->tport.blocked_open--; #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready after blocking: ttys%d, count = %d\n", - info->line, info->count); + info->line, info->tport.count); #endif if (retval) return retval; @@ -1660,7 +1660,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp) struct serial_state *info = rs_table + tty->index; int retval; - info->count++; + info->tport.count++; info->tport.tty = tty; tty->driver_data = info; tty->port = &info->tport; diff --git a/include/linux/serialP.h b/include/linux/serialP.h index 997edd008b92..a6612b9c7e84 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -35,7 +35,6 @@ struct serial_state { int line; int xmit_fifo_size; int custom_divisor; - int count; struct async_icount icount; struct tty_port tport; -- cgit From 01bd730d92bd002adc3f3317d8e3328c629b436c Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:52:27 +0100 Subject: TTY: amiserial/simserial, use flags from tty_port This changes flags' type to ulong which is appropriate for all the set/clear_bits performed in the drivers.. Signed-off-by: Jiri Slaby Cc: Geert Uytterhoeven Cc: Tony Luck Cc: Fenghua Yu Signed-off-by: Greg Kroah-Hartman --- arch/ia64/hp/sim/simserial.c | 33 +++++------ drivers/tty/amiserial.c | 129 ++++++++++++++++++++++--------------------- include/linux/serialP.h | 1 - 3 files changed, 83 insertions(+), 80 deletions(-) (limited to 'include') diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index baa2b1ec00a0..c65c49d31e7f 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -448,7 +448,7 @@ static void shutdown(struct tty_struct *tty, struct serial_state *info) { unsigned long flags; - if (!(info->flags & ASYNC_INITIALIZED)) + if (!(info->tport.flags & ASYNC_INITIALIZED)) return; #ifdef SIMSERIAL_DEBUG @@ -468,7 +468,7 @@ static void shutdown(struct tty_struct *tty, struct serial_state *info) set_bit(TTY_IO_ERROR, &tty->flags); - info->flags &= ~ASYNC_INITIALIZED; + info->tport.flags &= ~ASYNC_INITIALIZED; } local_irq_restore(flags); } @@ -523,7 +523,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) local_irq_restore(flags); return; } - info->flags |= ASYNC_CLOSING; + info->tport.flags |= ASYNC_CLOSING; local_irq_restore(flags); /* @@ -539,7 +539,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) schedule_timeout_interruptible(info->tport.close_delay); wake_up_interruptible(&info->tport.open_wait); } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + info->tport.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); wake_up_interruptible(&info->tport.close_wait); } @@ -563,12 +563,12 @@ static void rs_hangup(struct tty_struct *tty) #endif rs_flush_buffer(tty); - if (info->flags & ASYNC_CLOSING) + if (info->tport.flags & ASYNC_CLOSING) return; shutdown(tty, info); info->tport.count = 0; - info->flags &= ~ASYNC_NORMAL_ACTIVE; + info->tport.flags &= ~ASYNC_NORMAL_ACTIVE; info->tport.tty = NULL; wake_up_interruptible(&info->tport.open_wait); } @@ -576,6 +576,7 @@ static void rs_hangup(struct tty_struct *tty) static int startup(struct tty_struct *tty, struct serial_state *state) { + struct tty_port *port = &state->tport; unsigned long flags; int retval=0; unsigned long page; @@ -586,7 +587,7 @@ static int startup(struct tty_struct *tty, struct serial_state *state) local_irq_save(flags); - if (state->flags & ASYNC_INITIALIZED) { + if (port->flags & ASYNC_INITIALIZED) { free_page(page); goto errout; } @@ -630,16 +631,16 @@ static int startup(struct tty_struct *tty, struct serial_state *state) /* * Set up the tty->alt_speed kludge */ - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) tty->alt_speed = 57600; - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) tty->alt_speed = 115200; - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) tty->alt_speed = 230400; - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) tty->alt_speed = 460800; - state->flags |= ASYNC_INITIALIZED; + port->flags |= ASYNC_INITIALIZED; local_irq_restore(flags); return 0; @@ -669,7 +670,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp) #ifdef SIMSERIAL_DEBUG printk("rs_open %s, count = %d\n", tty->name, info->tport.count); #endif - tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + tty->low_latency = (info->tport.flags & ASYNC_LOW_LATENCY) ? 1 : 0; if (!tmp_buf) { page = get_zeroed_page(GFP_KERNEL); @@ -684,11 +685,11 @@ static int rs_open(struct tty_struct *tty, struct file * filp) /* * If the port is the middle of closing, bail out now */ - if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - if (info->flags & ASYNC_CLOSING) + if (tty_hung_up_p(filp) || (info->tport.flags & ASYNC_CLOSING)) { + if (info->tport.flags & ASYNC_CLOSING) interruptible_sleep_on(&info->tport.close_wait); #ifdef SERIAL_DO_RESTART - return ((info->flags & ASYNC_HUP_NOTIFY) ? + return ((info->tport.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); #else return -EAGAIN; diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 8ad64a0f1251..7d798262d0c2 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -45,7 +45,7 @@ #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) #define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ - tty->name, (info->flags), serial_driver->refcount,info->count,tty->count,s) + tty->name, (info->tport.flags), serial_driver->refcount,info->count,tty->count,s) #else #define DBG_CNT(s) #endif @@ -296,7 +296,7 @@ static void receive_chars(struct serial_state *info) printk("handling break...."); #endif flag = TTY_BREAK; - if (info->flags & ASYNC_SAK) + if (info->tport.flags & ASYNC_SAK) do_SAK(tty); } else if (status & UART_LSR_PE) flag = TTY_PARITY; @@ -377,7 +377,7 @@ static void check_modem_status(struct serial_state *info) if (dstatus & SER_DCD) { icount->dcd++; #ifdef CONFIG_HARD_PPS - if ((info->flags & ASYNC_HARDPPS_CD) && + if ((info->tport.flags & ASYNC_HARDPPS_CD) && !(status & SER_DCD)) hardpps(); #endif @@ -387,7 +387,7 @@ static void check_modem_status(struct serial_state *info) wake_up_interruptible(&info->tport.delta_msr_wait); } - if ((info->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) { + if ((info->tport.flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) { #if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) printk("ttyS%d CD now %s...", info->line, (!(status & SER_DCD)) ? "on" : "off"); @@ -402,7 +402,7 @@ static void check_modem_status(struct serial_state *info) tty_hangup(info->tport.tty); } } - if (info->flags & ASYNC_CTS_FLOW) { + if (info->tport.flags & ASYNC_CTS_FLOW) { if (info->tport.tty->hw_stopped) { if (!(status & SER_CTS)) { #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) @@ -503,6 +503,7 @@ static irqreturn_t ser_tx_int(int irq, void *dev_id) static int startup(struct tty_struct *tty, struct serial_state *info) { + struct tty_port *port = &info->tport; unsigned long flags; int retval=0; unsigned long page; @@ -513,7 +514,7 @@ static int startup(struct tty_struct *tty, struct serial_state *info) local_irq_save(flags); - if (info->flags & ASYNC_INITIALIZED) { + if (port->flags & ASYNC_INITIALIZED) { free_page(page); goto errout; } @@ -560,13 +561,13 @@ static int startup(struct tty_struct *tty, struct serial_state *info) /* * Set up the tty->alt_speed kludge */ - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) tty->alt_speed = 57600; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) tty->alt_speed = 115200; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) tty->alt_speed = 230400; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) tty->alt_speed = 460800; /* @@ -574,7 +575,7 @@ static int startup(struct tty_struct *tty, struct serial_state *info) */ change_speed(tty, info, NULL); - info->flags |= ASYNC_INITIALIZED; + port->flags |= ASYNC_INITIALIZED; local_irq_restore(flags); return 0; @@ -592,7 +593,7 @@ static void shutdown(struct tty_struct *tty, struct serial_state *info) unsigned long flags; struct serial_state *state; - if (!(info->flags & ASYNC_INITIALIZED)) + if (!(info->tport.flags & ASYNC_INITIALIZED)) return; state = info; @@ -633,7 +634,7 @@ static void shutdown(struct tty_struct *tty, struct serial_state *info) set_bit(TTY_IO_ERROR, &tty->flags); - info->flags &= ~ASYNC_INITIALIZED; + info->tport.flags &= ~ASYNC_INITIALIZED; local_irq_restore(flags); } @@ -645,6 +646,7 @@ static void shutdown(struct tty_struct *tty, struct serial_state *info) static void change_speed(struct tty_struct *tty, struct serial_state *info, struct ktermios *old_termios) { + struct tty_port *port = &info->tport; int quot = 0, baud_base, baud; unsigned cflag, cval = 0; int bits; @@ -675,8 +677,7 @@ static void change_speed(struct tty_struct *tty, struct serial_state *info, if (!baud) baud = 9600; /* B0 transition handled in rs_set_termios */ baud_base = info->baud_base; - if (baud == 38400 && - ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + if (baud == 38400 && (port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) quot = info->custom_divisor; else { if (baud == 134) @@ -694,7 +695,7 @@ static void change_speed(struct tty_struct *tty, struct serial_state *info, if (!baud) baud = 9600; if (baud == 38400 && - ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + (port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) quot = info->custom_divisor; else { if (baud == 134) @@ -713,17 +714,17 @@ static void change_speed(struct tty_struct *tty, struct serial_state *info, /* CTS flow control flag and modem status interrupts */ info->IER &= ~UART_IER_MSI; - if (info->flags & ASYNC_HARDPPS_CD) + if (port->flags & ASYNC_HARDPPS_CD) info->IER |= UART_IER_MSI; if (cflag & CRTSCTS) { - info->flags |= ASYNC_CTS_FLOW; + port->flags |= ASYNC_CTS_FLOW; info->IER |= UART_IER_MSI; } else - info->flags &= ~ASYNC_CTS_FLOW; + port->flags &= ~ASYNC_CTS_FLOW; if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; + port->flags &= ~ASYNC_CHECK_CD; else { - info->flags |= ASYNC_CHECK_CD; + port->flags |= ASYNC_CHECK_CD; info->IER |= UART_IER_MSI; } /* TBD: @@ -1019,7 +1020,7 @@ static int get_serial_info(struct serial_state *state, tmp.line = state->line; tmp.port = state->port; tmp.irq = state->irq; - tmp.flags = state->flags; + tmp.flags = state->tport.flags; tmp.xmit_fifo_size = state->xmit_fifo_size; tmp.baud_base = state->baud_base; tmp.close_delay = state->tport.close_delay; @@ -1034,6 +1035,7 @@ static int get_serial_info(struct serial_state *state, static int set_serial_info(struct tty_struct *tty, struct serial_state *state, struct serial_struct __user * new_info) { + struct tty_port *port = &state->tport; struct serial_struct new_serial; bool change_spd; int retval = 0; @@ -1042,7 +1044,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, return -EFAULT; tty_lock(); - change_spd = ((new_serial.flags ^ state->flags) & ASYNC_SPD_MASK) || + change_spd = ((new_serial.flags ^ port->flags) & ASYNC_SPD_MASK) || new_serial.custom_divisor != state->custom_divisor; if (new_serial.irq != state->irq || new_serial.port != state->port || new_serial.xmit_fifo_size != state->xmit_fifo_size) { @@ -1052,12 +1054,12 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, if (!serial_isroot()) { if ((new_serial.baud_base != state->baud_base) || - (new_serial.close_delay != state->tport.close_delay) || + (new_serial.close_delay != port->close_delay) || (new_serial.xmit_fifo_size != state->xmit_fifo_size) || ((new_serial.flags & ~ASYNC_USR_MASK) != - (state->flags & ~ASYNC_USR_MASK))) + (port->flags & ~ASYNC_USR_MASK))) return -EPERM; - state->flags = ((state->flags & ~ASYNC_USR_MASK) | + port->flags = ((port->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); state->custom_divisor = new_serial.custom_divisor; goto check_and_exit; @@ -1074,23 +1076,23 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, */ state->baud_base = new_serial.baud_base; - state->flags = ((state->flags & ~ASYNC_FLAGS) | + port->flags = ((port->flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); state->custom_divisor = new_serial.custom_divisor; - state->tport.close_delay = new_serial.close_delay * HZ/100; - state->tport.closing_wait = new_serial.closing_wait * HZ/100; - tty->low_latency = (state->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + port->close_delay = new_serial.close_delay * HZ/100; + port->closing_wait = new_serial.closing_wait * HZ/100; + tty->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; check_and_exit: - if (state->flags & ASYNC_INITIALIZED) { + if (port->flags & ASYNC_INITIALIZED) { if (change_spd) { - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) tty->alt_speed = 57600; - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) tty->alt_speed = 115200; - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) tty->alt_speed = 230400; - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) tty->alt_speed = 460800; change_speed(tty, state, NULL); } @@ -1407,7 +1409,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) local_irq_restore(flags); return; } - state->flags |= ASYNC_CLOSING; + state->tport.flags |= ASYNC_CLOSING; /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. @@ -1422,7 +1424,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) * line status register. */ state->read_status_mask &= ~UART_LSR_DR; - if (state->flags & ASYNC_INITIALIZED) { + if (state->tport.flags & ASYNC_INITIALIZED) { /* disable receive interrupts */ custom.intena = IF_RBF; mb(); @@ -1449,7 +1451,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) } wake_up_interruptible(&state->tport.open_wait); } - state->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + state->tport.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); wake_up_interruptible(&state->tport.close_wait); local_irq_restore(flags); } @@ -1530,7 +1532,7 @@ static void rs_hangup(struct tty_struct *tty) rs_flush_buffer(tty); shutdown(tty, info); info->tport.count = 0; - info->flags &= ~ASYNC_NORMAL_ACTIVE; + info->tport.flags &= ~ASYNC_NORMAL_ACTIVE; info->tport.tty = NULL; wake_up_interruptible(&info->tport.open_wait); } @@ -1548,6 +1550,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, #else struct wait_queue wait = { current, NULL }; #endif + struct tty_port *port = &info->tport; int retval; int do_clocal = 0, extra_count = 0; unsigned long flags; @@ -1557,11 +1560,11 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, * until it's done, and then try again. */ if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - if (info->flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->tport.close_wait); + (port->flags & ASYNC_CLOSING)) { + if (port->flags & ASYNC_CLOSING) + interruptible_sleep_on(&port->close_wait); #ifdef SERIAL_DO_RESTART - return ((info->flags & ASYNC_HUP_NOTIFY) ? + return ((port->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); #else return -EAGAIN; @@ -1574,7 +1577,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - info->flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -1584,23 +1587,23 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, info->tport.count is dropped by one, so that + * this loop, port->count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&info->tport.open_wait, &wait); + add_wait_queue(&port->open_wait, &wait); #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready before block: ttys%d, count = %d\n", - info->line, info->tport.count); + info->line, port->count); #endif local_irq_save(flags); if (!tty_hung_up_p(filp)) { extra_count = 1; - info->tport.count--; + port->count--; } local_irq_restore(flags); - info->tport.blocked_open++; + port->blocked_open++; while (1) { local_irq_save(flags); if (tty->termios->c_cflag & CBAUD) @@ -1608,9 +1611,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, local_irq_restore(flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { + !(port->flags & ASYNC_INITIALIZED)) { #ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) + if (port->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; @@ -1619,7 +1622,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, #endif break; } - if (!(info->flags & ASYNC_CLOSING) && + if (!(port->flags & ASYNC_CLOSING) && (do_clocal || (!(ciab.pra & SER_DCD)) )) break; if (signal_pending(current)) { @@ -1628,24 +1631,24 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, } #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready blocking: ttys%d, count = %d\n", - info->line, info->tport.count); + info->line, port->count); #endif tty_unlock(); schedule(); tty_lock(); } __set_current_state(TASK_RUNNING); - remove_wait_queue(&info->tport.open_wait, &wait); + remove_wait_queue(&port->open_wait, &wait); if (extra_count) - info->tport.count++; - info->tport.blocked_open--; + port->count++; + port->blocked_open--; #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready after blocking: ttys%d, count = %d\n", - info->line, info->tport.count); + info->line, port->count); #endif if (retval) return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; + port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -1670,17 +1673,17 @@ static int rs_open(struct tty_struct *tty, struct file * filp) #ifdef SERIAL_DEBUG_OPEN printk("rs_open %s, count = %d\n", tty->name, info->count); #endif - tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + tty->low_latency = (info->tport.flags & ASYNC_LOW_LATENCY) ? 1 : 0; /* * If the port is the middle of closing, bail out now */ if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - if (info->flags & ASYNC_CLOSING) + (info->tport.flags & ASYNC_CLOSING)) { + if (info->tport.flags & ASYNC_CLOSING) interruptible_sleep_on(&info->tport.close_wait); #ifdef SERIAL_DO_RESTART - return ((info->flags & ASYNC_HUP_NOTIFY) ? + return ((info->tport.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); #else return -EAGAIN; @@ -1723,7 +1726,7 @@ static inline void line_info(struct seq_file *m, struct serial_state *state) local_irq_save(flags); status = ciab.pra; - control = (state->flags & ASYNC_INITIALIZED) ? state->MCR : status; + control = (state->tport.flags & ASYNC_INITIALIZED) ? state->MCR : status; local_irq_restore(flags); stat_buf[0] = 0; diff --git a/include/linux/serialP.h b/include/linux/serialP.h index a6612b9c7e84..e5e8442c08d6 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -30,7 +30,6 @@ struct serial_state { int baud_base; unsigned long port; int irq; - int flags; int type; int line; int xmit_fifo_size; -- cgit From 3c4782dcd9b8d02e79f0f0bd1fe6e30a79790526 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:52:31 +0100 Subject: TTY: simserial no longer needs serialP Let's do a spin-off of serial_state structure with only needed elements. And remove serialP crap from includes. Signed-off-by: Jiri Slaby Cc: Tony Luck Cc: Fenghua Yu Signed-off-by: Greg Kroah-Hartman --- arch/ia64/hp/sim/simserial.c | 9 ++++++++- include/linux/serialP.h | 2 -- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index 3698a2fe221d..120aad4d5362 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -27,10 +27,10 @@ #include #include #include +#include #include #include #include -#include #include #include @@ -46,6 +46,13 @@ #define NR_PORTS 1 /* only one port for now */ +struct serial_state { + struct tty_port tport; + struct circ_buf xmit; + int irq; + int x_char; +}; + static char *serial_name = "SimSerial driver"; static char *serial_version = "0.6"; diff --git a/include/linux/serialP.h b/include/linux/serialP.h index e5e8442c08d6..9a04dec1589a 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -44,10 +44,8 @@ struct serial_state { int quot; int IER; /* Interrupt Enable Register */ int MCR; /* Modem control register */ - /* simserial */ int x_char; /* xon/xoff character */ struct circ_buf xmit; - /* /simserial */ /* /amiserial */ }; -- cgit From ff169e5cbec29d33765687c7131673316011b328 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:52:44 +0100 Subject: TTY: amiserial, stop using serial_state->{irq,type,line} * instead of line, use tty->index or iterator... * irq and type are left unset. So get rid of them. Signed-off-by: Jiri Slaby Cc: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/amiserial.c | 23 ++++++++++------------- include/linux/serialP.h | 3 --- 2 files changed, 10 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index a9f5da64eef2..b182bccf3eab 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1008,7 +1008,7 @@ static void rs_unthrottle(struct tty_struct * tty) * ------------------------------------------------------------ */ -static int get_serial_info(struct serial_state *state, +static int get_serial_info(struct tty_struct *tty, struct serial_state *state, struct serial_struct __user * retinfo) { struct serial_struct tmp; @@ -1017,10 +1017,8 @@ static int get_serial_info(struct serial_state *state, return -EFAULT; memset(&tmp, 0, sizeof(tmp)); tty_lock(); - tmp.type = state->type; - tmp.line = state->line; + tmp.line = tty->index; tmp.port = state->port; - tmp.irq = state->irq; tmp.flags = state->tport.flags; tmp.xmit_fifo_size = state->xmit_fifo_size; tmp.baud_base = state->baud_base; @@ -1047,7 +1045,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, tty_lock(); change_spd = ((new_serial.flags ^ port->flags) & ASYNC_SPD_MASK) || new_serial.custom_divisor != state->custom_divisor; - if (new_serial.irq != state->irq || new_serial.port != state->port || + if (new_serial.irq || new_serial.port != state->port || new_serial.xmit_fifo_size != state->xmit_fifo_size) { tty_unlock(); return -EINVAL; @@ -1250,7 +1248,7 @@ static int rs_ioctl(struct tty_struct *tty, switch (cmd) { case TIOCGSERIAL: - return get_serial_info(info, argp); + return get_serial_info(tty, info, argp); case TIOCSSERIAL: return set_serial_info(tty, info, argp); case TIOCSERCONFIG: @@ -1403,7 +1401,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) } if (--port->count < 0) { printk("rs_close: bad serial port count for ttys%d: %d\n", - state->line, port->count); + tty->index, port->count); port->count = 0; } if (port->count) { @@ -1720,12 +1718,13 @@ static int rs_open(struct tty_struct *tty, struct file * filp) * /proc fs routines.... */ -static inline void line_info(struct seq_file *m, struct serial_state *state) +static inline void line_info(struct seq_file *m, int line, + struct serial_state *state) { char stat_buf[30], control, status; unsigned long flags; - seq_printf(m, "%d: uart:amiga_builtin",state->line); + seq_printf(m, "%d: uart:amiga_builtin", line); local_irq_save(flags); status = ciab.pra; @@ -1771,7 +1770,7 @@ static inline void line_info(struct seq_file *m, struct serial_state *state) static int rs_proc_show(struct seq_file *m, void *v) { seq_printf(m, "serinfo:1.0 driver:%s\n", serial_version); - line_info(m, &rs_table[0]); + line_info(m, 0, &rs_table[0]); return 0; } @@ -1867,7 +1866,6 @@ static int __init amiga_serial_probe(struct platform_device *pdev) state = rs_table; state->port = (int)&custom.serdatr; /* Just to give it a value */ - state->line = 0; state->custom_divisor = 0; state->icount.cts = state->icount.dsr = state->icount.rng = state->icount.dcd = 0; @@ -1876,8 +1874,7 @@ static int __init amiga_serial_probe(struct platform_device *pdev) state->icount.overrun = state->icount.brk = 0; tty_port_init(&state->tport); - printk(KERN_INFO "ttyS%d is the amiga builtin serial port\n", - state->line); + printk(KERN_INFO "ttyS0 is the amiga builtin serial port\n"); /* Hardware set up */ diff --git a/include/linux/serialP.h b/include/linux/serialP.h index 9a04dec1589a..77afbdb134ae 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -29,9 +29,6 @@ struct serial_state { int baud_base; unsigned long port; - int irq; - int type; - int line; int xmit_fifo_size; int custom_divisor; struct async_icount icount; -- cgit From 6fe18d26b1c33d5cb748f8694ee1a59dc5578da4 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 5 Mar 2012 14:52:45 +0100 Subject: TTY: amiserial no longer needs serialP amiserial is the last user of serialP.h. Let's move struct serial_state directly to amiserial and remove serialP crap from includes. Finally, remove the header from the tree completely. Signed-off-by: Jiri Slaby Cc: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/amiserial.c | 20 +++++++++++++++++++- include/linux/serialP.h | 49 ------------------------------------------------- 2 files changed, 19 insertions(+), 50 deletions(-) delete mode 100644 include/linux/serialP.h (limited to 'include') diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index b182bccf3eab..613d6a3908d3 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -58,7 +58,6 @@ #include #include -#include #include static char *serial_version = "4.30"; @@ -70,6 +69,7 @@ static char *serial_version = "4.30"; #include #include #include +#include #include #include #include @@ -92,6 +92,24 @@ static char *serial_version = "4.30"; #include #include +struct serial_state { + struct tty_port tport; + struct circ_buf xmit; + struct async_icount icount; + + unsigned long port; + int baud_base; + int xmit_fifo_size; + int custom_divisor; + int read_status_mask; + int ignore_status_mask; + int timeout; + int quot; + int IER; /* Interrupt Enable Register */ + int MCR; /* Modem control register */ + int x_char; /* xon/xoff character */ +}; + #define custom amiga_custom static char *serial_name = "Amiga-builtin serial driver"; diff --git a/include/linux/serialP.h b/include/linux/serialP.h deleted file mode 100644 index 77afbdb134ae..000000000000 --- a/include/linux/serialP.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Private header file for the (dumb) serial driver - * - * Copyright (C) 1997 by Theodore Ts'o. - * - * Redistribution of this file is permitted under the terms of the GNU - * Public License (GPL) - */ - -#ifndef _LINUX_SERIALP_H -#define _LINUX_SERIALP_H - -/* - * This is our internal structure for each serial port's state. - * - * Many fields are paralleled by the structure used by the serial_struct - * structure. - * - * For definitions of the flags field, see tty.h - */ - -#include -#include -#include -#include -#include -#include - -struct serial_state { - int baud_base; - unsigned long port; - int xmit_fifo_size; - int custom_divisor; - struct async_icount icount; - struct tty_port tport; - - /* amiserial */ - int read_status_mask; - int ignore_status_mask; - int timeout; - int quot; - int IER; /* Interrupt Enable Register */ - int MCR; /* Modem control register */ - int x_char; /* xon/xoff character */ - struct circ_buf xmit; - /* /amiserial */ -}; - -#endif /* _LINUX_SERIAL_H */ -- cgit From 3cc3615749dbd1b891512d5c9a5bf4559cfa9741 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Tue, 6 Mar 2012 17:29:22 +0100 Subject: usb: cdc-wdm: adding usb_cdc_wdm_register subdriver support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver can be used as a subdriver of another USB driver, allowing it to export a Device Managment interface consisting of a single interrupt endpoint with no dedicated USB interface. Some devices provide a Device Management function combined with a wwan function in a single USB interface having three endpoints (bulk in/out + interrupt). If the interrupt endpoint is used exclusively for DM notifications, then this driver can support that as a subdriver provided that the wwan driver calls the appropriate entry points on probe, suspend, resume, pre_reset, post_reset and disconnect. The main driver must have full control over all interface related settings, including the needs_remote_wakeup flag. A manage_power function must be provided by the main driver. A manage_power stub doing direct flag manipulation is used in normal driver mode. Signed-off-by: Bjørn Mork Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-wdm.c | 63 ++++++++++++++++++++++++++++++++++++++++++--- include/linux/usb/cdc-wdm.h | 19 ++++++++++++++ 2 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 include/linux/usb/cdc-wdm.h (limited to 'include') diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 46827373ecf9..c6f6560d436c 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -23,6 +23,7 @@ #include #include #include +#include /* * Version Information @@ -116,6 +117,7 @@ struct wdm_device { int rerr; struct list_head device_list; + int (*manage_power)(struct usb_interface *, int); }; static struct usb_driver wdm_driver; @@ -580,7 +582,6 @@ static int wdm_open(struct inode *inode, struct file *file) dev_err(&desc->intf->dev, "Error autopm - %d\n", rv); goto out; } - intf->needs_remote_wakeup = 1; /* using write lock to protect desc->count */ mutex_lock(&desc->wlock); @@ -597,6 +598,8 @@ static int wdm_open(struct inode *inode, struct file *file) rv = 0; } mutex_unlock(&desc->wlock); + if (desc->count == 1) + desc->manage_power(intf, 1); usb_autopm_put_interface(desc->intf); out: mutex_unlock(&wdm_mutex); @@ -618,7 +621,7 @@ static int wdm_release(struct inode *inode, struct file *file) dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); kill_urbs(desc); if (!test_bit(WDM_DISCONNECTING, &desc->flags)) - desc->intf->needs_remote_wakeup = 0; + desc->manage_power(desc->intf, 0); } mutex_unlock(&wdm_mutex); return 0; @@ -665,7 +668,8 @@ static void wdm_rxwork(struct work_struct *work) /* --- hotplug --- */ -static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep, u16 bufsize) +static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep, + u16 bufsize, int (*manage_power)(struct usb_interface *, int)) { int rv = -ENOMEM; struct wdm_device *desc; @@ -750,6 +754,8 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor desc ); + desc->manage_power = manage_power; + spin_lock(&wdm_device_list_lock); list_add(&desc->device_list, &wdm_device_list); spin_unlock(&wdm_device_list_lock); @@ -766,6 +772,19 @@ err: return rv; } +static int wdm_manage_power(struct usb_interface *intf, int on) +{ + /* need autopm_get/put here to ensure the usbcore sees the new value */ + int rv = usb_autopm_get_interface(intf); + if (rv < 0) + goto err; + + intf->needs_remote_wakeup = on; + usb_autopm_put_interface(intf); +err: + return rv; +} + static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) { int rv = -EINVAL; @@ -809,12 +828,48 @@ next_desc: goto err; ep = &iface->endpoint[0].desc; - rv = wdm_create(intf, ep, maxcom); + rv = wdm_create(intf, ep, maxcom, &wdm_manage_power); err: return rv; } +/** + * usb_cdc_wdm_register - register a WDM subdriver + * @intf: usb interface the subdriver will associate with + * @ep: interrupt endpoint to monitor for notifications + * @bufsize: maximum message size to support for read/write + * + * Create WDM usb class character device and associate it with intf + * without binding, allowing another driver to manage the interface. + * + * The subdriver will manage the given interrupt endpoint exclusively + * and will issue control requests referring to the given intf. It + * will otherwise avoid interferring, and in particular not do + * usb_set_intfdata/usb_get_intfdata on intf. + * + * The return value is a pointer to the subdriver's struct usb_driver. + * The registering driver is responsible for calling this subdriver's + * disconnect, suspend, resume, pre_reset and post_reset methods from + * its own. + */ +struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf, + struct usb_endpoint_descriptor *ep, + int bufsize, + int (*manage_power)(struct usb_interface *, int)) +{ + int rv = -EINVAL; + + rv = wdm_create(intf, ep, bufsize, manage_power); + if (rv < 0) + goto err; + + return &wdm_driver; +err: + return ERR_PTR(rv); +} +EXPORT_SYMBOL(usb_cdc_wdm_register); + static void wdm_disconnect(struct usb_interface *intf) { struct wdm_device *desc; diff --git a/include/linux/usb/cdc-wdm.h b/include/linux/usb/cdc-wdm.h new file mode 100644 index 000000000000..719c332620fa --- /dev/null +++ b/include/linux/usb/cdc-wdm.h @@ -0,0 +1,19 @@ +/* + * USB CDC Device Management subdriver + * + * Copyright (c) 2012 Bjørn Mork + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#ifndef __LINUX_USB_CDC_WDM_H +#define __LINUX_USB_CDC_WDM_H + +extern struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf, + struct usb_endpoint_descriptor *ep, + int bufsize, + int (*manage_power)(struct usb_interface *, int)); + +#endif /* __LINUX_USB_CDC_WDM_H */ -- cgit From 0343c5543b1d3ffa08e6716d82afb62648b80eba Mon Sep 17 00:00:00 2001 From: Benjamin Poirier Date: Thu, 8 Mar 2012 05:55:58 +0000 Subject: sctp: Export sctp_do_peeloff lookup sctp_association within sctp_do_peeloff() to enable its use outside of the sctp code with minimal knowledge of the former. Signed-off-by: Benjamin Poirier Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 1 + net/sctp/socket.c | 24 +++++++++--------------- 2 files changed, 10 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index d3685615a8b0..6ee44b24864a 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -413,6 +413,7 @@ static inline sctp_assoc_t sctp_assoc2id(const struct sctp_association *asoc) /* Look up the association by its id. */ struct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id); +int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp); /* A macro to walk a list of skbs. */ #define sctp_skb_for_each(pos, head, tmp) \ diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 408ebd0e7330..06b42b7f5a02 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4170,14 +4170,16 @@ static int sctp_getsockopt_autoclose(struct sock *sk, int len, char __user *optv } /* Helper routine to branch off an association to a new socket. */ -SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc, - struct socket **sockp) +int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp) { - struct sock *sk = asoc->base.sk; + struct sctp_association *asoc = sctp_id2assoc(sk, id); struct socket *sock; struct sctp_af *af; int err = 0; + if (!asoc) + return -EINVAL; + /* An association cannot be branched off from an already peeled-off * socket, nor is this supported for tcp style sockets. */ @@ -4206,13 +4208,13 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc, return err; } +EXPORT_SYMBOL(sctp_do_peeloff); static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval, int __user *optlen) { sctp_peeloff_arg_t peeloff; struct socket *newsock; int retval = 0; - struct sctp_association *asoc; if (len < sizeof(sctp_peeloff_arg_t)) return -EINVAL; @@ -4220,15 +4222,7 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval if (copy_from_user(&peeloff, optval, len)) return -EFAULT; - asoc = sctp_id2assoc(sk, peeloff.associd); - if (!asoc) { - retval = -EINVAL; - goto out; - } - - SCTP_DEBUG_PRINTK("%s: sk: %p asoc: %p\n", __func__, sk, asoc); - - retval = sctp_do_peeloff(asoc, &newsock); + retval = sctp_do_peeloff(sk, peeloff.associd, &newsock); if (retval < 0) goto out; @@ -4239,8 +4233,8 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval goto out; } - SCTP_DEBUG_PRINTK("%s: sk: %p asoc: %p newsk: %p sd: %d\n", - __func__, sk, asoc, newsock->sk, retval); + SCTP_DEBUG_PRINTK("%s: sk: %p newsk: %p sd: %d\n", + __func__, sk, newsock->sk, retval); /* Return the fd mapped to the new socket. */ peeloff.sd = retval; -- cgit From cb5d76999029ae7a517cb07dfa732c1b5a934fc2 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 9 Feb 2012 23:21:05 +0100 Subject: perf: Add ABI reference sizes This patch adds reference sizes for revision 1 and 2 of the perf_event ABI, i.e., the size of the perf_event_attr struct. With Rev1: config2 was added = +8 bytes With Rev2: branch_sample_type was added = +8 bytes Adds the definition for Rev1, Rev2. This is useful for tools trying to decode the revision numbers based on the size of the struct. Signed-off-by: Stephane Eranian Cc: peterz@infradead.org Cc: acme@redhat.com Cc: robert.richter@amd.com Cc: ming.m.lin@intel.com Cc: andi@firstfloor.org Cc: asharma@fb.com Cc: ravitillo@lbl.gov Cc: vweaver1@eecs.utk.edu Cc: khandual@linux.vnet.ibm.com Cc: dsahern@gmail.com Link: http://lkml.kernel.org/r/1328826068-11713-16-git-send-email-eranian@google.com Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index fbbf5e598368..bd9f55a5958d 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -192,6 +192,8 @@ enum perf_event_read_format { }; #define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ +#define PERF_ATTR_SIZE_VER1 72 /* add: config2 */ +#define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ /* * Hardware event_id to monitor via a performance monitoring event: -- cgit From 4bcdf1d0b652bc33d52f2322b77463e4dc58abf8 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Fri, 9 Mar 2012 14:59:26 +0100 Subject: genirq: Get rid of unnecessary irqaction field in task_struct When a new thread handler is created, an irqaction is passed to it as data. Not only that irqaction is stored in task_struct by the handler for later use, but also a structure associated with the kernel thread keeps this value as long as the thread exists. This fix kicks irqaction out off task_struct. Yes, I introduce new bit field. But it allows not only to eliminate the duplicate, but also shortens size of task_struct. Reported-by: Oleg Nesterov Signed-off-by: Alexander Gordeev Link: http://lkml.kernel.org/r/20120309135925.GB2114@dhcp-26-207.brq.redhat.com Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 10 +++++----- kernel/irq/manage.c | 19 +++++++++++-------- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 7d379a6bfd88..07f537a371ce 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1319,6 +1319,11 @@ struct task_struct { unsigned sched_reset_on_fork:1; unsigned sched_contributes_to_load:1; +#ifdef CONFIG_GENERIC_HARDIRQS + /* IRQ handler threads */ + unsigned irq_thread:1; +#endif + pid_t pid; pid_t tgid; @@ -1427,11 +1432,6 @@ struct task_struct { * mempolicy */ spinlock_t alloc_lock; -#ifdef CONFIG_GENERIC_HARDIRQS - /* IRQ handler threads */ - struct irqaction *irqaction; -#endif - /* Protection of the PI data structures: */ raw_spinlock_t pi_lock; diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index c0730ad8a117..0fa3ce998ecb 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -780,7 +780,7 @@ static int irq_thread(void *data) handler_fn = irq_thread_fn; sched_setscheduler(current, SCHED_FIFO, ¶m); - current->irqaction = action; + current->irq_thread = 1; while (!irq_wait_for_interrupt(action)) { @@ -818,10 +818,10 @@ static int irq_thread(void *data) irq_finalize_oneshot(desc, action, true); /* - * Clear irqaction. Otherwise exit_irq_thread() would make + * Clear irq_thread. Otherwise exit_irq_thread() would make * fuzz about an active irq thread going into nirvana. */ - current->irqaction = NULL; + current->irq_thread = 0; return 0; } @@ -832,27 +832,30 @@ void exit_irq_thread(void) { struct task_struct *tsk = current; struct irq_desc *desc; + struct irqaction *action; - if (!tsk->irqaction) + if (!tsk->irq_thread) return; + action = kthread_data(tsk); + printk(KERN_ERR "exiting task \"%s\" (%d) is an active IRQ thread (irq %d)\n", - tsk->comm ? tsk->comm : "", tsk->pid, tsk->irqaction->irq); + tsk->comm ? tsk->comm : "", tsk->pid, action->irq); - desc = irq_to_desc(tsk->irqaction->irq); + desc = irq_to_desc(action->irq); /* * Prevent a stale desc->threads_oneshot. Must be called * before setting the IRQTF_DIED flag. */ - irq_finalize_oneshot(desc, tsk->irqaction, true); + irq_finalize_oneshot(desc, action, true); /* * Set the THREAD DIED flag to prevent further wakeups of the * soon to be gone threaded handler. */ - set_bit(IRQTF_DIED, &tsk->irqaction->flags); + set_bit(IRQTF_DIED, &action->flags); } static void irq_setup_forced_threading(struct irqaction *new) -- cgit From 927353a75602dd97144352f53177e18093fdd198 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 8 Mar 2012 19:12:12 -0500 Subject: serial: introduce generic port in/out helpers Looking at the existing serial drivers (esp. the 8250 derived variants) we see a common trend. They create a hardware specific port struct, which in turn contains a generic serial_port struct. The other trend, is that they all create some sort of shortcut to go through the hardware specific struct, to the serial_port struct, which has the basic in/out operations within. Looking for the serial_in and serial_out in several drivers shows this. Rather than let this continue, lets create a generic set of similar helper wrappers that can be used on a struct port, so we can eliminate bouncing out through hardware specific struct pointers just to come back into struct port where possible. Signed-off-by: Paul Gortmaker Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- include/linux/serial_core.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'include') diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 585bfd03d2ee..f51bf2e70c69 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -383,6 +383,16 @@ struct uart_port { void *private_data; /* generic platform data pointer */ }; +static inline int serial_port_in(struct uart_port *up, int offset) +{ + return up->serial_in(up, offset); +} + +static inline void serial_port_out(struct uart_port *up, int offset, int value) +{ + up->serial_out(up, offset, value); +} + /* * This is the state information which is persistent across opens. */ -- cgit From ba57b4db2624793c6eb8f2c051c9f7b8a6e7b6a6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 7 Mar 2012 20:45:32 -0500 Subject: ipv4: Make ip_call_ra_chain() return bool. Signed-off-by: David S. Miller --- include/net/ip.h | 2 +- net/ipv4/ip_input.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/ip.h b/include/net/ip.h index 775009f9eaba..b53d65f24f7b 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -388,7 +388,7 @@ static inline int sk_mc_loop(struct sock *sk) return 1; } -extern int ip_call_ra_chain(struct sk_buff *skb); +extern bool ip_call_ra_chain(struct sk_buff *skb); /* * Functions provided by ip_fragment.c diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 073a9b01c40c..70afe5bf1945 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -148,7 +148,7 @@ /* * Process Router Attention IP option (RFC 2113) */ -int ip_call_ra_chain(struct sk_buff *skb) +bool ip_call_ra_chain(struct sk_buff *skb) { struct ip_ra_chain *ra; u8 protocol = ip_hdr(skb)->protocol; @@ -167,7 +167,7 @@ int ip_call_ra_chain(struct sk_buff *skb) net_eq(sock_net(sk), dev_net(dev))) { if (ip_is_fragment(ip_hdr(skb))) { if (ip_defrag(skb, IP_DEFRAG_CALL_RA_CHAIN)) - return 1; + return true; } if (last) { struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); @@ -180,9 +180,9 @@ int ip_call_ra_chain(struct sk_buff *skb) if (last) { raw_rcv(last, skb); - return 1; + return true; } - return 0; + return false; } static int ip_local_deliver_finish(struct sk_buff *skb) -- cgit From bdcc0924c8c6e6362bb93b6120631f257aac6f87 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 7 Mar 2012 20:53:36 -0500 Subject: net: Use bool in skbuff.h helper functions. In particular do this for skb_is_nonlinear(), skb_is_gso(), and skb_is_gso_v6(). Signed-off-by: David S. Miller --- include/linux/skbuff.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 79ef8209bbb7..8dc8257eab8b 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1173,7 +1173,7 @@ static inline struct sk_buff *__skb_dequeue_tail(struct sk_buff_head *list) } -static inline int skb_is_nonlinear(const struct sk_buff *skb) +static inline bool skb_is_nonlinear(const struct sk_buff *skb) { return skb->data_len; } @@ -2469,12 +2469,12 @@ static inline struct sec_path *skb_sec_path(struct sk_buff *skb) } #endif -static inline int skb_is_gso(const struct sk_buff *skb) +static inline bool skb_is_gso(const struct sk_buff *skb) { return skb_shinfo(skb)->gso_size; } -static inline int skb_is_gso_v6(const struct sk_buff *skb) +static inline bool skb_is_gso_v6(const struct sk_buff *skb) { return skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6; } -- cgit From 4d29515f5a2e062c3fe82bfb574da70ed544ffad Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 7 Mar 2012 21:02:35 -0500 Subject: net: Use bool in netdevice.h helpers. Specifically use it in napi_disable_pending(), napi_schedule_prep(), napi_reschedule(), netif_tx_queue_stopped(), netif_queue_stopped(), netif_xmit_stopped(), netif_xmit_frozen_or_stopped(), netif_running(), __netif_subqueue_stopped(), netif_subqueue_stopped(), netif_is_multiquue(), netif_carrier_ok(), netif_dormant(), netif_oper_up(), netif_device_present(), __netif_tx_trylock(), net_gso_ok(), skb_gso_ok(), netif_needs_gso(), and netif_is_bond_slave(). Signed-off-by: David S. Miller --- include/linux/netdevice.h | 52 +++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a89933bc4f2f..b195a34440bb 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -417,7 +417,7 @@ typedef rx_handler_result_t rx_handler_func_t(struct sk_buff **pskb); extern void __napi_schedule(struct napi_struct *n); -static inline int napi_disable_pending(struct napi_struct *n) +static inline bool napi_disable_pending(struct napi_struct *n) { return test_bit(NAPI_STATE_DISABLE, &n->state); } @@ -431,7 +431,7 @@ static inline int napi_disable_pending(struct napi_struct *n) * insure only one NAPI poll instance runs. We also make * sure there is no pending NAPI disable. */ -static inline int napi_schedule_prep(struct napi_struct *n) +static inline bool napi_schedule_prep(struct napi_struct *n) { return !napi_disable_pending(n) && !test_and_set_bit(NAPI_STATE_SCHED, &n->state); @@ -451,13 +451,13 @@ static inline void napi_schedule(struct napi_struct *n) } /* Try to reschedule poll. Called by dev->poll() after napi_complete(). */ -static inline int napi_reschedule(struct napi_struct *napi) +static inline bool napi_reschedule(struct napi_struct *napi) { if (napi_schedule_prep(napi)) { __napi_schedule(napi); - return 1; + return true; } - return 0; + return false; } /** @@ -1868,7 +1868,7 @@ static inline void netif_tx_stop_all_queues(struct net_device *dev) } } -static inline int netif_tx_queue_stopped(const struct netdev_queue *dev_queue) +static inline bool netif_tx_queue_stopped(const struct netdev_queue *dev_queue) { return test_bit(__QUEUE_STATE_DRV_XOFF, &dev_queue->state); } @@ -1879,17 +1879,17 @@ static inline int netif_tx_queue_stopped(const struct netdev_queue *dev_queue) * * Test if transmit queue on device is currently unable to send. */ -static inline int netif_queue_stopped(const struct net_device *dev) +static inline bool netif_queue_stopped(const struct net_device *dev) { return netif_tx_queue_stopped(netdev_get_tx_queue(dev, 0)); } -static inline int netif_xmit_stopped(const struct netdev_queue *dev_queue) +static inline bool netif_xmit_stopped(const struct netdev_queue *dev_queue) { return dev_queue->state & QUEUE_STATE_ANY_XOFF; } -static inline int netif_xmit_frozen_or_stopped(const struct netdev_queue *dev_queue) +static inline bool netif_xmit_frozen_or_stopped(const struct netdev_queue *dev_queue) { return dev_queue->state & QUEUE_STATE_ANY_XOFF_OR_FROZEN; } @@ -1954,7 +1954,7 @@ static inline void netdev_reset_queue(struct net_device *dev_queue) * * Test if the device has been brought up. */ -static inline int netif_running(const struct net_device *dev) +static inline bool netif_running(const struct net_device *dev) { return test_bit(__LINK_STATE_START, &dev->state); } @@ -2004,16 +2004,16 @@ static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index) * * Check individual transmit queue of a device with multiple transmit queues. */ -static inline int __netif_subqueue_stopped(const struct net_device *dev, - u16 queue_index) +static inline bool __netif_subqueue_stopped(const struct net_device *dev, + u16 queue_index) { struct netdev_queue *txq = netdev_get_tx_queue(dev, queue_index); return netif_tx_queue_stopped(txq); } -static inline int netif_subqueue_stopped(const struct net_device *dev, - struct sk_buff *skb) +static inline bool netif_subqueue_stopped(const struct net_device *dev, + struct sk_buff *skb) { return __netif_subqueue_stopped(dev, skb_get_queue_mapping(skb)); } @@ -2052,7 +2052,7 @@ static inline u16 skb_tx_hash(const struct net_device *dev, * * Check if device has multiple transmit queues */ -static inline int netif_is_multiqueue(const struct net_device *dev) +static inline bool netif_is_multiqueue(const struct net_device *dev) { return dev->num_tx_queues > 1; } @@ -2188,7 +2188,7 @@ extern void linkwatch_forget_dev(struct net_device *dev); * * Check if carrier is present on device */ -static inline int netif_carrier_ok(const struct net_device *dev) +static inline bool netif_carrier_ok(const struct net_device *dev) { return !test_bit(__LINK_STATE_NOCARRIER, &dev->state); } @@ -2240,7 +2240,7 @@ static inline void netif_dormant_off(struct net_device *dev) * * Check if carrier is present on device */ -static inline int netif_dormant(const struct net_device *dev) +static inline bool netif_dormant(const struct net_device *dev) { return test_bit(__LINK_STATE_DORMANT, &dev->state); } @@ -2252,7 +2252,7 @@ static inline int netif_dormant(const struct net_device *dev) * * Check if carrier is operational */ -static inline int netif_oper_up(const struct net_device *dev) +static inline bool netif_oper_up(const struct net_device *dev) { return (dev->operstate == IF_OPER_UP || dev->operstate == IF_OPER_UNKNOWN /* backward compat */); @@ -2264,7 +2264,7 @@ static inline int netif_oper_up(const struct net_device *dev) * * Check if device has not been removed from system. */ -static inline int netif_device_present(struct net_device *dev) +static inline bool netif_device_present(struct net_device *dev) { return test_bit(__LINK_STATE_PRESENT, &dev->state); } @@ -2334,9 +2334,9 @@ static inline void __netif_tx_lock_bh(struct netdev_queue *txq) txq->xmit_lock_owner = smp_processor_id(); } -static inline int __netif_tx_trylock(struct netdev_queue *txq) +static inline bool __netif_tx_trylock(struct netdev_queue *txq) { - int ok = spin_trylock(&txq->_xmit_lock); + bool ok = spin_trylock(&txq->_xmit_lock); if (likely(ok)) txq->xmit_lock_owner = smp_processor_id(); return ok; @@ -2614,7 +2614,7 @@ void netif_stacked_transfer_operstate(const struct net_device *rootdev, netdev_features_t netif_skb_features(struct sk_buff *skb); -static inline int net_gso_ok(netdev_features_t features, int gso_type) +static inline bool net_gso_ok(netdev_features_t features, int gso_type) { netdev_features_t feature = gso_type << NETIF_F_GSO_SHIFT; @@ -2629,14 +2629,14 @@ static inline int net_gso_ok(netdev_features_t features, int gso_type) return (features & feature) == feature; } -static inline int skb_gso_ok(struct sk_buff *skb, netdev_features_t features) +static inline bool skb_gso_ok(struct sk_buff *skb, netdev_features_t features) { return net_gso_ok(features, skb_shinfo(skb)->gso_type) && (!skb_has_frag_list(skb) || (features & NETIF_F_FRAGLIST)); } -static inline int netif_needs_gso(struct sk_buff *skb, - netdev_features_t features) +static inline bool netif_needs_gso(struct sk_buff *skb, + netdev_features_t features) { return skb_is_gso(skb) && (!skb_gso_ok(skb, features) || unlikely(skb->ip_summed != CHECKSUM_PARTIAL)); @@ -2648,7 +2648,7 @@ static inline void netif_set_gso_max_size(struct net_device *dev, dev->gso_max_size = size; } -static inline int netif_is_bond_slave(struct net_device *dev) +static inline bool netif_is_bond_slave(struct net_device *dev) { return dev->flags & IFF_SLAVE && dev->priv_flags & IFF_BONDING; } -- cgit From cd4946188aac597d187a765127fd26fa3644c29f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 25 Feb 2012 11:25:58 +0100 Subject: driver-core: Allow additional parameters for module_driver Allow module_driver take additional parameters which will be passed to the register and unregister function calls. This allows it to be used in cases where additional parameters are required (e.g. usb_serial_register_drivers). Signed-off-by: Lars-Peter Clausen Signed-off-by: Greg Kroah-Hartman --- include/linux/device.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/device.h b/include/linux/device.h index b63fb393aa58..bccccef520dc 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1007,19 +1007,20 @@ extern long sysfs_deprecated; * @__driver: driver name * @__register: register function for this driver type * @__unregister: unregister function for this driver type + * @...: Additional arguments to be passed to __register and __unregister. * * Use this macro to construct bus specific macros for registering * drivers, and do not use it on its own. */ -#define module_driver(__driver, __register, __unregister) \ +#define module_driver(__driver, __register, __unregister, ...) \ static int __init __driver##_init(void) \ { \ - return __register(&(__driver)); \ + return __register(&(__driver) , ##__VA_ARGS__); \ } \ module_init(__driver##_init); \ static void __exit __driver##_exit(void) \ { \ - __unregister(&(__driver)); \ + __unregister(&(__driver) , ##__VA_ARGS__); \ } \ module_exit(__driver##_exit); -- cgit From b790f5d1260b4c962bd066cd34ae982943c27fe1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 9 Mar 2012 16:38:14 -0800 Subject: USB: serial: use module_driver() macro Now that module_driver() can handle varargs, use it instead of rolling our own version. Cc: Lars-Peter Clausen Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/serial.h | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 7b1db841e2a8..fbb666b1b670 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -416,23 +416,10 @@ do { \ * module may only use this macro once, and calling it replaces * module_init() and module_exit() * - * Note, we can't use the generic module_driver() call here, due to the - * two parameters in the usb_serial_* functions, so we roll our own here - * :( */ #define module_usb_serial_driver(__usb_driver, __serial_drivers) \ -static int __init usb_serial_driver_init(void) \ -{ \ - return usb_serial_register_drivers(&(__usb_driver), \ - (__serial_drivers)); \ -} \ -module_init(usb_serial_driver_init); \ -static void __exit usb_serial_driver_exit(void) \ -{ \ - return usb_serial_deregister_drivers(&(__usb_driver), \ - (__serial_drivers)); \ -} \ -module_exit(usb_serial_driver_exit); + module_driver(__usb_driver, usb_serial_register_drivers, \ + usb_serial_deregister_drivers, __serial_drivers) #endif /* __LINUX_USB_SERIAL_H */ -- cgit From 43db362d3adda9e0a915ddb9a8d1a41186e19179 Mon Sep 17 00:00:00 2001 From: Maciej Żenczykowski Date: Sun, 11 Mar 2012 12:51:50 +0000 Subject: net: get rid of some pointless casts to sockaddr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The following 4 functions: move_addr_to_kernel move_addr_to_user verify_iovec verify_compat_iovec are always effectively called with a sockaddr_storage. Make this explicit by changing their signature. This removes a large number of casts from sockaddr_storage to sockaddr. Signed-off-by: Maciej Żenczykowski Signed-off-by: David S. Miller --- include/linux/socket.h | 4 ++-- include/net/compat.h | 2 +- net/compat.c | 2 +- net/core/iovec.c | 2 +- net/socket.c | 36 ++++++++++++++---------------------- 5 files changed, 19 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/linux/socket.h b/include/linux/socket.h index d0e77f607a79..da2d3e2543f3 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -326,11 +326,11 @@ extern int csum_partial_copy_fromiovecend(unsigned char *kdata, int offset, unsigned int len, __wsum *csump); -extern int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode); +extern int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode); extern int memcpy_toiovec(struct iovec *v, unsigned char *kdata, int len); extern int memcpy_toiovecend(const struct iovec *v, unsigned char *kdata, int offset, int len); -extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr *kaddr); +extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr); extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data); struct timespec; diff --git a/include/net/compat.h b/include/net/compat.h index 9ee75edcc295..a974ae92d182 100644 --- a/include/net/compat.h +++ b/include/net/compat.h @@ -41,7 +41,7 @@ extern int compat_sock_get_timestampns(struct sock *, struct timespec __user *); #endif /* defined(CONFIG_COMPAT) */ extern int get_compat_msghdr(struct msghdr *, struct compat_msghdr __user *); -extern int verify_compat_iovec(struct msghdr *, struct iovec *, struct sockaddr *, int); +extern int verify_compat_iovec(struct msghdr *, struct iovec *, struct sockaddr_storage *, int); extern asmlinkage long compat_sys_sendmsg(int,struct compat_msghdr __user *,unsigned); extern asmlinkage long compat_sys_sendmmsg(int, struct compat_mmsghdr __user *, unsigned, unsigned); diff --git a/net/compat.c b/net/compat.c index 6def90e0a112..64b4515a64e6 100644 --- a/net/compat.c +++ b/net/compat.c @@ -79,7 +79,7 @@ int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg) /* I've named the args so it is easy to tell whose space the pointers are in. */ int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov, - struct sockaddr *kern_address, int mode) + struct sockaddr_storage *kern_address, int mode) { int tot_len; diff --git a/net/core/iovec.c b/net/core/iovec.c index c40f27e7d208..7e7aeb01de45 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c @@ -35,7 +35,7 @@ * in any case. */ -int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode) +int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode) { int size, ct, err; diff --git a/net/socket.c b/net/socket.c index 28a96af484b4..12a48d846223 100644 --- a/net/socket.c +++ b/net/socket.c @@ -181,7 +181,7 @@ static DEFINE_PER_CPU(int, sockets_in_use); * invalid addresses -EFAULT is returned. On a success 0 is returned. */ -int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr *kaddr) +int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr) { if (ulen < 0 || ulen > sizeof(struct sockaddr_storage)) return -EINVAL; @@ -209,7 +209,7 @@ int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr *kaddr) * specified. Zero is returned for a success. */ -static int move_addr_to_user(struct sockaddr *kaddr, int klen, +static int move_addr_to_user(struct sockaddr_storage *kaddr, int klen, void __user *uaddr, int __user *ulen) { int err; @@ -1449,7 +1449,7 @@ SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen) sock = sockfd_lookup_light(fd, &err, &fput_needed); if (sock) { - err = move_addr_to_kernel(umyaddr, addrlen, (struct sockaddr *)&address); + err = move_addr_to_kernel(umyaddr, addrlen, &address); if (err >= 0) { err = security_socket_bind(sock, (struct sockaddr *)&address, @@ -1556,7 +1556,7 @@ SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr, err = -ECONNABORTED; goto out_fd; } - err = move_addr_to_user((struct sockaddr *)&address, + err = move_addr_to_user(&address, len, upeer_sockaddr, upeer_addrlen); if (err < 0) goto out_fd; @@ -1605,7 +1605,7 @@ SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr, sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; - err = move_addr_to_kernel(uservaddr, addrlen, (struct sockaddr *)&address); + err = move_addr_to_kernel(uservaddr, addrlen, &address); if (err < 0) goto out_put; @@ -1645,7 +1645,7 @@ SYSCALL_DEFINE3(getsockname, int, fd, struct sockaddr __user *, usockaddr, err = sock->ops->getname(sock, (struct sockaddr *)&address, &len, 0); if (err) goto out_put; - err = move_addr_to_user((struct sockaddr *)&address, len, usockaddr, usockaddr_len); + err = move_addr_to_user(&address, len, usockaddr, usockaddr_len); out_put: fput_light(sock->file, fput_needed); @@ -1677,7 +1677,7 @@ SYSCALL_DEFINE3(getpeername, int, fd, struct sockaddr __user *, usockaddr, sock->ops->getname(sock, (struct sockaddr *)&address, &len, 1); if (!err) - err = move_addr_to_user((struct sockaddr *)&address, len, usockaddr, + err = move_addr_to_user(&address, len, usockaddr, usockaddr_len); fput_light(sock->file, fput_needed); } @@ -1716,7 +1716,7 @@ SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len, msg.msg_controllen = 0; msg.msg_namelen = 0; if (addr) { - err = move_addr_to_kernel(addr, addr_len, (struct sockaddr *)&address); + err = move_addr_to_kernel(addr, addr_len, &address); if (err < 0) goto out_put; msg.msg_name = (struct sockaddr *)&address; @@ -1779,7 +1779,7 @@ SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size, err = sock_recvmsg(sock, &msg, size, flags); if (err >= 0 && addr != NULL) { - err2 = move_addr_to_user((struct sockaddr *)&address, + err2 = move_addr_to_user(&address, msg.msg_namelen, addr, addr_len); if (err2 < 0) err = err2; @@ -1933,13 +1933,9 @@ static int __sys_sendmsg(struct socket *sock, struct msghdr __user *msg, /* This will also move the address data into kernel space */ if (MSG_CMSG_COMPAT & flags) { - err = verify_compat_iovec(msg_sys, iov, - (struct sockaddr *)&address, - VERIFY_READ); + err = verify_compat_iovec(msg_sys, iov, &address, VERIFY_READ); } else - err = verify_iovec(msg_sys, iov, - (struct sockaddr *)&address, - VERIFY_READ); + err = verify_iovec(msg_sys, iov, &address, VERIFY_READ); if (err < 0) goto out_freeiov; total_len = err; @@ -2143,13 +2139,9 @@ static int __sys_recvmsg(struct socket *sock, struct msghdr __user *msg, uaddr = (__force void __user *)msg_sys->msg_name; uaddr_len = COMPAT_NAMELEN(msg); if (MSG_CMSG_COMPAT & flags) { - err = verify_compat_iovec(msg_sys, iov, - (struct sockaddr *)&addr, - VERIFY_WRITE); + err = verify_compat_iovec(msg_sys, iov, &addr, VERIFY_WRITE); } else - err = verify_iovec(msg_sys, iov, - (struct sockaddr *)&addr, - VERIFY_WRITE); + err = verify_iovec(msg_sys, iov, &addr, VERIFY_WRITE); if (err < 0) goto out_freeiov; total_len = err; @@ -2166,7 +2158,7 @@ static int __sys_recvmsg(struct socket *sock, struct msghdr __user *msg, len = err; if (uaddr != NULL) { - err = move_addr_to_user((struct sockaddr *)&addr, + err = move_addr_to_user(&addr, msg_sys->msg_namelen, uaddr, uaddr_len); if (err < 0) -- cgit From f99298bfa7c42da8d27c2b42050941471c0866ab Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Mon, 12 Dec 2011 16:45:28 +0800 Subject: xHCI: BESL calculation based on USB2.0 LPM errata The latest released errata for USB2.0 ECN LPM adds new fields to USB2.0 extension descriptor, defines two BESL values for device: baseline BESL and deep BESL. Baseline BESL value communicates a nominal power savings design point and the deep BESL value communicates a significant power savings design point. If device indicates BESL value, driver will use a value count in both host BESL and device BESL. Use baseline BESL value as default. Signed-off-by: Andiry Xu Tested-by: Jason Fan Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci.c | 51 ++++++++++++++++++++++++++----------------------- include/linux/usb/ch9.h | 5 +++++ 2 files changed, 32 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index a629ad860329..262400c10075 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3614,26 +3614,38 @@ static int xhci_besl_encoding[16] = {125, 150, 200, 300, 400, 500, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000}; /* Calculate HIRD/BESL for USB2 PORTPMSC*/ -static int xhci_calculate_hird_besl(int u2del, bool use_besl) +static int xhci_calculate_hird_besl(struct xhci_hcd *xhci, + struct usb_device *udev) { - int hird; + int u2del, besl, besl_host; + int besl_device = 0; + u32 field; + + u2del = HCS_U2_LATENCY(xhci->hcs_params3); + field = le32_to_cpu(udev->bos->ext_cap->bmAttributes); - if (use_besl) { - for (hird = 0; hird < 16; hird++) { - if (xhci_besl_encoding[hird] >= u2del) + if (field & USB_BESL_SUPPORT) { + for (besl_host = 0; besl_host < 16; besl_host++) { + if (xhci_besl_encoding[besl_host] >= u2del) break; } + /* Use baseline BESL value as default */ + if (field & USB_BESL_BASELINE_VALID) + besl_device = USB_GET_BESL_BASELINE(field); + else if (field & USB_BESL_DEEP_VALID) + besl_device = USB_GET_BESL_DEEP(field); } else { if (u2del <= 50) - hird = 0; + besl_host = 0; else - hird = (u2del - 51) / 75 + 1; - - if (hird > 15) - hird = 15; + besl_host = (u2del - 51) / 75 + 1; } - return hird; + besl = besl_host + besl_device; + if (besl > 15) + besl = 15; + + return besl; } static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd, @@ -3646,7 +3658,7 @@ static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd, u32 temp, dev_id; unsigned int port_num; unsigned long flags; - int u2del, hird; + int hird; int ret; if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support || @@ -3692,12 +3704,7 @@ static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd, * HIRD or BESL shoule be used. See USB2.0 LPM errata. */ pm_addr = port_array[port_num] + 1; - u2del = HCS_U2_LATENCY(xhci->hcs_params3); - if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2)) - hird = xhci_calculate_hird_besl(u2del, 1); - else - hird = xhci_calculate_hird_besl(u2del, 0); - + hird = xhci_calculate_hird_besl(xhci, udev); temp = PORT_L1DS(udev->slot_id) | PORT_HIRD(hird); xhci_writel(xhci, temp, pm_addr); @@ -3776,7 +3783,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, u32 temp; unsigned int port_num; unsigned long flags; - int u2del, hird; + int hird; if (hcd->speed == HCD_USB3 || !xhci->hw_lpm_support || !udev->lpm_capable) @@ -3799,11 +3806,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n", enable ? "enable" : "disable", port_num); - u2del = HCS_U2_LATENCY(xhci->hcs_params3); - if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2)) - hird = xhci_calculate_hird_besl(u2del, 1); - else - hird = xhci_calculate_hird_besl(u2del, 0); + hird = xhci_calculate_hird_besl(xhci, udev); if (enable) { temp &= ~PORT_HIRD_MASK; diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 3b6f628880f8..af21f3115919 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -789,6 +789,11 @@ struct usb_ext_cap_descriptor { /* Link Power Management */ __u8 bDevCapabilityType; __le32 bmAttributes; #define USB_LPM_SUPPORT (1 << 1) /* supports LPM */ +#define USB_BESL_SUPPORT (1 << 2) /* supports BESL */ +#define USB_BESL_BASELINE_VALID (1 << 3) /* Baseline BESL valid*/ +#define USB_BESL_DEEP_VALID (1 << 4) /* Deep BESL valid */ +#define USB_GET_BESL_BASELINE(p) (((p) & (0xf << 8)) >> 8) +#define USB_GET_BESL_DEEP(p) (((p) & (0xf << 12)) >> 12) } __attribute__((packed)); #define USB_DT_USB_EXT_CAP_SIZE 7 -- cgit From 4486ea987efdaa546bdda569e3dfacdc14a9fb13 Mon Sep 17 00:00:00 2001 From: Bala Shanmugam Date: Wed, 7 Mar 2012 17:27:12 +0530 Subject: cfg80211: Add background scan period attribute. Receive background scan period as part of connect command and pass the same to the driver. Signed-off-by: Bala Shanmugam Signed-off-by: John W. Linville --- include/linux/nl80211.h | 10 ++++++++++ include/net/cfg80211.h | 3 +++ net/wireless/nl80211.c | 8 ++++++++ net/wireless/wext-sme.c | 3 +++ 4 files changed, 24 insertions(+) (limited to 'include') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index c37d67add090..e474f6e780cc 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -369,6 +369,11 @@ * %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT. + * Background scan period can optionally be + * specified in %NL80211_ATTR_BG_SCAN_PERIOD, + * if not specified default background scan configuration + * in driver is used and if period value is 0, bg scan will be disabled. + * This attribute is ignored if driver does not support roam scan. * It is also sent as an event, with the BSSID and response IEs when the * connection is established or failed to be established. This can be * determined by the STATUS_CODE attribute. @@ -1207,6 +1212,9 @@ enum nl80211_commands { * this attribute is (depending on the driver capabilities) added to * received frames indicated with %NL80211_CMD_FRAME. * + * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds + * or 0 to disable background scan. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1456,6 +1464,8 @@ enum nl80211_attrs { NL80211_ATTR_RX_SIGNAL_DBM, + NL80211_ATTR_BG_SCAN_PERIOD, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 57c9fddc2acf..66f460325e24 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1203,6 +1203,8 @@ struct cfg80211_ibss_params { * @key_idx: index of WEP key for shared key authentication * @key: WEP key for shared key authentication * @flags: See &enum cfg80211_assoc_req_flags + * @bg_scan_period: Background scan period in seconds + * or -1 to indicate that default value is to be used. * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask * will be used in ht_capa. Un-supported values will be ignored. * @ht_capa_mask: The bits of ht_capa which are to be used. @@ -1220,6 +1222,7 @@ struct cfg80211_connect_params { const u8 *key; u8 key_len, key_idx; u32 flags; + int bg_scan_period; struct ieee80211_ht_cap ht_capa; struct ieee80211_ht_cap ht_capa_mask; }; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 39dbdf2adb12..4c1eb9472ddb 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -205,6 +205,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { }, [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 }, [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 }, + [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 }, }; /* policy for the key attributes */ @@ -5116,6 +5117,13 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) wiphy = &rdev->wiphy; + connect.bg_scan_period = -1; + if (info->attrs[NL80211_ATTR_BG_SCAN_PERIOD] && + (wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) { + connect.bg_scan_period = + nla_get_u16(info->attrs[NL80211_ATTR_BG_SCAN_PERIOD]); + } + if (info->attrs[NL80211_ATTR_MAC]) connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 326750b99151..7c01c2f3b6cf 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -30,6 +30,9 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, wdev->wext.connect.ie = wdev->wext.ie; wdev->wext.connect.ie_len = wdev->wext.ie_len; + /* Use default background scan period */ + wdev->wext.connect.bg_scan_period = -1; + if (wdev->wext.keys) { wdev->wext.keys->def = wdev->wext.default_key; wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key; -- cgit From 177958e9679c23537411066cc41b205635dacb14 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 9 Mar 2012 12:49:21 +0100 Subject: mac80211: remove tx_sync When the station state callback was added, this was no longer needed in theory. With the iwlwifi changes to remove use of it landing, we can kill the entire tx-sync framework again, RIP. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 41 ----------------------------------------- net/mac80211/driver-ops.h | 35 ----------------------------------- net/mac80211/driver-trace.h | 43 ------------------------------------------- net/mac80211/ieee80211_i.h | 1 - net/mac80211/mlme.c | 41 ----------------------------------------- 5 files changed, 161 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f7917f765cbc..44e4dfcb5722 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1765,20 +1765,6 @@ enum ieee80211_ampdu_mlme_action { IEEE80211_AMPDU_TX_OPERATIONAL, }; -/** - * enum ieee80211_tx_sync_type - TX sync type - * @IEEE80211_TX_SYNC_AUTH: sync TX for authentication - * (and possibly also before direct probe) - * @IEEE80211_TX_SYNC_ASSOC: sync TX for association - * @IEEE80211_TX_SYNC_ACTION: sync TX for action frame - * (not implemented yet) - */ -enum ieee80211_tx_sync_type { - IEEE80211_TX_SYNC_AUTH, - IEEE80211_TX_SYNC_ASSOC, - IEEE80211_TX_SYNC_ACTION, -}; - /** * enum ieee80211_frame_release_type - frame release reason * @IEEE80211_FRAME_RELEASE_PSPOLL: frame released for PS-Poll @@ -1889,26 +1875,6 @@ enum ieee80211_frame_release_type { * of the bss parameters has changed when a call is made. The callback * can sleep. * - * @tx_sync: Called before a frame is sent to an AP/GO. In the GO case, the - * driver should sync with the GO's powersaving so the device doesn't - * transmit the frame while the GO is asleep. In the regular AP case - * it may be used by drivers for devices implementing other restrictions - * on talking to APs, e.g. due to regulatory enforcement or just HW - * restrictions. - * This function is called for every authentication, association and - * action frame separately since applications might attempt to auth - * with multiple APs before chosing one to associate to. If it returns - * an error, the corresponding authentication, association or frame - * transmission is aborted and reported as having failed. It is always - * called after tuning to the correct channel. - * The callback might be called multiple times before @finish_tx_sync - * (but @finish_tx_sync will be called once for each) but in practice - * this is unlikely to happen. It can also refuse in that case if the - * driver cannot handle that situation. - * This callback can sleep. - * @finish_tx_sync: Called as a counterpart to @tx_sync, unless that returned - * an error. This callback can sleep. - * * @prepare_multicast: Prepare for multicast filter configuration. * This callback is optional, and its return value is passed * to configure_filter(). This callback must be atomic. @@ -2180,13 +2146,6 @@ struct ieee80211_ops { struct ieee80211_bss_conf *info, u32 changed); - int (*tx_sync)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - const u8 *bssid, enum ieee80211_tx_sync_type type); - void (*finish_tx_sync)(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - const u8 *bssid, - enum ieee80211_tx_sync_type type); - u64 (*prepare_multicast)(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list); void (*configure_filter)(struct ieee80211_hw *hw, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 70dfb6415c20..af4691fed645 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -168,41 +168,6 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, trace_drv_return_void(local); } -static inline int drv_tx_sync(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - const u8 *bssid, - enum ieee80211_tx_sync_type type) -{ - int ret = 0; - - might_sleep(); - - check_sdata_in_driver(sdata); - - trace_drv_tx_sync(local, sdata, bssid, type); - if (local->ops->tx_sync) - ret = local->ops->tx_sync(&local->hw, &sdata->vif, - bssid, type); - trace_drv_return_int(local, ret); - return ret; -} - -static inline void drv_finish_tx_sync(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - const u8 *bssid, - enum ieee80211_tx_sync_type type) -{ - might_sleep(); - - check_sdata_in_driver(sdata); - - trace_drv_finish_tx_sync(local, sdata, bssid, type); - if (local->ops->finish_tx_sync) - local->ops->finish_tx_sync(&local->hw, &sdata->vif, - bssid, type); - trace_drv_return_void(local); -} - static inline u64 drv_prepare_multicast(struct ieee80211_local *local, struct netdev_hw_addr_list *mc_list) { diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 384e2f08c187..7034209ad3f4 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -308,49 +308,6 @@ TRACE_EVENT(drv_bss_info_changed, ) ); -DECLARE_EVENT_CLASS(tx_sync_evt, - TP_PROTO(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - const u8 *bssid, - enum ieee80211_tx_sync_type type), - TP_ARGS(local, sdata, bssid, type), - - TP_STRUCT__entry( - LOCAL_ENTRY - VIF_ENTRY - __array(char, bssid, ETH_ALEN) - __field(u32, sync_type) - ), - - TP_fast_assign( - LOCAL_ASSIGN; - VIF_ASSIGN; - memcpy(__entry->bssid, bssid, ETH_ALEN); - __entry->sync_type = type; - ), - - TP_printk( - LOCAL_PR_FMT VIF_PR_FMT " bssid:%pM type:%d", - LOCAL_PR_ARG, VIF_PR_ARG, __entry->bssid, __entry->sync_type - ) -); - -DEFINE_EVENT(tx_sync_evt, drv_tx_sync, - TP_PROTO(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - const u8 *bssid, - enum ieee80211_tx_sync_type type), - TP_ARGS(local, sdata, bssid, type) -); - -DEFINE_EVENT(tx_sync_evt, drv_finish_tx_sync, - TP_PROTO(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - const u8 *bssid, - enum ieee80211_tx_sync_type type), - TP_ARGS(local, sdata, bssid, type) -); - TRACE_EVENT(drv_prepare_multicast, TP_PROTO(struct ieee80211_local *local, int mc_count), diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 796b13bfc953..3c62ded64e32 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -388,7 +388,6 @@ struct ieee80211_mgd_auth_data { u8 key[WLAN_KEY_LEN_WEP104]; u8 key_len, key_idx; - bool synced; bool done; size_t ie_len; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c08924aeac00..1d09df248c56 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1770,11 +1770,6 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, lockdep_assert_held(&sdata->u.mgd.mtx); - if (auth_data->synced) - drv_finish_tx_sync(sdata->local, sdata, - auth_data->bss->bssid, - IEEE80211_TX_SYNC_AUTH); - if (!assoc) { sta_info_destroy_addr(sdata, auth_data->bss->bssid); @@ -1862,10 +1857,6 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: authenticated\n", sdata->name); out: - if (ifmgd->auth_data->synced) - drv_finish_tx_sync(sdata->local, sdata, bssid, - IEEE80211_TX_SYNC_AUTH); - ifmgd->auth_data->synced = false; ifmgd->auth_data->done = true; ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC; run_again(ifmgd, ifmgd->auth_data->timeout); @@ -2005,11 +1996,6 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, lockdep_assert_held(&sdata->u.mgd.mtx); - if (assoc_data->synced) - drv_finish_tx_sync(sdata->local, sdata, - assoc_data->bss->bssid, - IEEE80211_TX_SYNC_ASSOC); - if (!assoc) { sta_info_destroy_addr(sdata, assoc_data->bss->bssid); @@ -2255,14 +2241,6 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, } else { printk(KERN_DEBUG "%s: associated\n", sdata->name); - /* tell driver about sync done first */ - if (assoc_data->synced) { - drv_finish_tx_sync(sdata->local, sdata, - assoc_data->bss->bssid, - IEEE80211_TX_SYNC_ASSOC); - assoc_data->synced = false; - } - if (!ieee80211_assoc_success(sdata, *bss, mgmt, len)) { /* oops -- internal error -- send timeout for now */ ieee80211_destroy_assoc_data(sdata, true); @@ -2747,14 +2725,6 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) if (WARN_ON_ONCE(!auth_data)) return -EINVAL; - if (!auth_data->synced) { - int ret = drv_tx_sync(local, sdata, auth_data->bss->bssid, - IEEE80211_TX_SYNC_AUTH); - if (ret) - return ret; - } - auth_data->synced = true; - auth_data->tries++; if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) { @@ -2811,14 +2781,6 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata) lockdep_assert_held(&sdata->u.mgd.mtx); - if (!assoc_data->synced) { - int ret = drv_tx_sync(local, sdata, assoc_data->bss->bssid, - IEEE80211_TX_SYNC_ASSOC); - if (ret) - return ret; - } - assoc_data->synced = true; - assoc_data->tries++; if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) { printk(KERN_DEBUG "%s: association with %pM timed out\n", @@ -3245,9 +3207,6 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, err = ieee80211_probe_auth(sdata); if (err) { - if (auth_data->synced) - drv_finish_tx_sync(local, sdata, req->bss->bssid, - IEEE80211_TX_SYNC_AUTH); sta_info_destroy_addr(sdata, req->bss->bssid); goto err_clear; } -- cgit From 3ccf3e8306156a28213adc720aba807e9a901ad5 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 27 Feb 2012 10:47:00 +0100 Subject: printk/sched: Introduce special printk_sched() for those awkward moments There's a few awkward printk()s inside of scheduler guts that people prefer to keep but really are rather deadlock prone. Fudge around it by storing the text in a per-cpu buffer and poll it using the existing printk_tick() handler. This will drop output when its more frequent than once a tick, however only the affinity thing could possible go that fast and for that just one should suffice to notify the admin he's done something silly.. Signed-off-by: Peter Zijlstra Cc: Linus Torvalds Cc: Andrew Morton Link: http://lkml.kernel.org/n/tip-wua3lmkt3dg8nfts66o6brne@git.kernel.org Signed-off-by: Ingo Molnar --- include/linux/printk.h | 10 ++++++++++ kernel/printk.c | 40 +++++++++++++++++++++++++++++++++++++--- kernel/sched/core.c | 2 +- kernel/sched/rt.c | 8 +++++++- 4 files changed, 55 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/printk.h b/include/linux/printk.h index f0e22f75143f..1f77a4174ee0 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -100,6 +100,11 @@ int vprintk(const char *fmt, va_list args); asmlinkage __printf(1, 2) __cold int printk(const char *fmt, ...); +/* + * Special printk facility for scheduler use only, _DO_NOT_USE_ ! + */ +__printf(1, 2) __cold int printk_sched(const char *fmt, ...); + /* * Please don't use printk_ratelimit(), because it shares ratelimiting state * with all other unrelated printk_ratelimit() callsites. Instead use @@ -127,6 +132,11 @@ int printk(const char *s, ...) { return 0; } +static inline __printf(1, 2) __cold +int printk_sched(const char *s, ...) +{ + return 0; +} static inline int printk_ratelimit(void) { return 0; diff --git a/kernel/printk.c b/kernel/printk.c index 13c0a1143f49..7ca7ba591e21 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -1208,13 +1208,47 @@ int is_console_locked(void) return console_locked; } +/* + * Delayed printk facility, for scheduler-internal messages: + */ +#define PRINTK_BUF_SIZE 512 + +#define PRINTK_PENDING_WAKEUP 0x01 +#define PRINTK_PENDING_SCHED 0x02 + static DEFINE_PER_CPU(int, printk_pending); +static DEFINE_PER_CPU(char [PRINTK_BUF_SIZE], printk_sched_buf); + +int printk_sched(const char *fmt, ...) +{ + unsigned long flags; + va_list args; + char *buf; + int r; + + local_irq_save(flags); + buf = __get_cpu_var(printk_sched_buf); + + va_start(args, fmt); + r = vsnprintf(buf, PRINTK_BUF_SIZE, fmt, args); + va_end(args); + + __this_cpu_or(printk_pending, PRINTK_PENDING_SCHED); + local_irq_restore(flags); + + return r; +} void printk_tick(void) { if (__this_cpu_read(printk_pending)) { - __this_cpu_write(printk_pending, 0); - wake_up_interruptible(&log_wait); + int pending = __this_cpu_xchg(printk_pending, 0); + if (pending & PRINTK_PENDING_SCHED) { + char *buf = __get_cpu_var(printk_sched_buf); + printk(KERN_WARNING "[sched_delayed] %s", buf); + } + if (pending & PRINTK_PENDING_WAKEUP) + wake_up_interruptible(&log_wait); } } @@ -1228,7 +1262,7 @@ int printk_needs_cpu(int cpu) void wake_up_klogd(void) { if (waitqueue_active(&log_wait)) - this_cpu_write(printk_pending, 1); + this_cpu_or(printk_pending, PRINTK_PENDING_WAKEUP); } /** diff --git a/kernel/sched/core.c b/kernel/sched/core.c index b1ccce819ce2..8781cec7c3e6 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1284,7 +1284,7 @@ static int select_fallback_rq(int cpu, struct task_struct *p) * leave kernel. */ if (p->mm && printk_ratelimit()) { - printk(KERN_INFO "process %d (%s) no longer affine to cpu%d\n", + printk_sched("process %d (%s) no longer affine to cpu%d\n", task_pid_nr(p), p->comm, cpu); } diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 7f7e7cdcb472..b60dad720173 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -864,8 +864,14 @@ static int sched_rt_runtime_exceeded(struct rt_rq *rt_rq) * but accrue some time due to boosting. */ if (likely(rt_b->rt_runtime)) { + static bool once = false; + rt_rq->rt_throttled = 1; - printk_once(KERN_WARNING "sched: RT throttling activated\n"); + + if (!once) { + once = true; + printk_sched("sched: RT throttling activated\n"); + } } else { /* * In case we did anyway, make it go away, -- cgit From afd465030acb4098abcb6b965a5aebc7ea2209e0 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 12 Mar 2012 07:03:32 +0000 Subject: net: ipv4: Standardize prefixes for message logging Add #define pr_fmt(fmt) as appropriate. Add "IPv4: ", "TCP: ", and "IPsec: " to appropriate files. Standardize on "UDPLite: " for appropriate uses. Some prefixes were previously "UDPLITE: " and "UDP-Lite: ". Add KBUILD_MODNAME ": " to icmp and gre. Remove embedded prefixes as appropriate. Add missing "\n" to pr_info in gre.c. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/net/udplite.h | 4 ++-- net/ipv4/af_inet.c | 2 ++ net/ipv4/ah4.c | 9 ++++++--- net/ipv4/esp4.c | 2 ++ net/ipv4/gre.c | 6 ++++-- net/ipv4/icmp.c | 6 ++++-- net/ipv4/ip_fragment.c | 8 +++++--- net/ipv4/ip_gre.c | 2 ++ net/ipv4/ip_input.c | 2 ++ net/ipv4/ip_options.c | 2 ++ net/ipv4/route.c | 4 +++- net/ipv4/tcp.c | 11 +++++++---- net/ipv4/tcp_cong.c | 8 +++++--- net/ipv4/tcp_input.c | 2 ++ net/ipv4/tcp_ipv4.c | 6 +++--- net/ipv4/tcp_probe.c | 4 +++- net/ipv4/tcp_timer.c | 14 ++++++++------ net/ipv4/udp.c | 34 ++++++++++++++-------------------- net/ipv4/udplite.c | 3 +++ net/ipv4/xfrm4_tunnel.c | 2 ++ 20 files changed, 81 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/include/net/udplite.h b/include/net/udplite.h index 5f097ca7d5c5..71375459a884 100644 --- a/include/net/udplite.h +++ b/include/net/udplite.h @@ -40,7 +40,7 @@ static inline int udplite_checksum_init(struct sk_buff *skb, struct udphdr *uh) * checksum. UDP-Lite (like IPv6) mandates checksums, hence packets * with a zero checksum field are illegal. */ if (uh->check == 0) { - LIMIT_NETDEBUG(KERN_DEBUG "UDPLITE: zeroed checksum field\n"); + LIMIT_NETDEBUG(KERN_DEBUG "UDPLite: zeroed checksum field\n"); return 1; } @@ -52,7 +52,7 @@ static inline int udplite_checksum_init(struct sk_buff *skb, struct udphdr *uh) /* * Coverage length violates RFC 3828: log and discard silently. */ - LIMIT_NETDEBUG(KERN_DEBUG "UDPLITE: bad csum coverage %d/%d\n", + LIMIT_NETDEBUG(KERN_DEBUG "UDPLite: bad csum coverage %d/%d\n", cscov, skb->len); return 1; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index b7a946611f2f..fdf49fd44bb4 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -65,6 +65,8 @@ * 2 of the License, or (at your option) any later version. */ +#define pr_fmt(fmt) "IPv4: " fmt + #include #include #include diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index b5524660c81d..fd508b526014 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c @@ -1,3 +1,5 @@ +#define pr_fmt(fmt) "IPsec: " fmt + #include #include #include @@ -445,9 +447,10 @@ static int ah_init_state(struct xfrm_state *x) if (aalg_desc->uinfo.auth.icv_fullbits/8 != crypto_ahash_digestsize(ahash)) { - pr_info("AH: %s digestsize %u != %hu\n", - x->aalg->alg_name, crypto_ahash_digestsize(ahash), - aalg_desc->uinfo.auth.icv_fullbits/8); + pr_info("%s: %s digestsize %u != %hu\n", + __func__, x->aalg->alg_name, + crypto_ahash_digestsize(ahash), + aalg_desc->uinfo.auth.icv_fullbits / 8); goto error; } diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 0a86dbe9454b..89a47b35905d 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -1,3 +1,5 @@ +#define pr_fmt(fmt) "IPsec: " fmt + #include #include #include diff --git a/net/ipv4/gre.c b/net/ipv4/gre.c index 8cb1ebb7cd74..42a491055c76 100644 --- a/net/ipv4/gre.c +++ b/net/ipv4/gre.c @@ -10,6 +10,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -118,10 +120,10 @@ static const struct net_protocol net_gre_protocol = { static int __init gre_init(void) { - pr_info("GRE over IPv4 demultiplexor driver"); + pr_info("GRE over IPv4 demultiplexor driver\n"); if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) { - pr_err("gre: can't add protocol\n"); + pr_err("can't add protocol\n"); return -EAGAIN; } diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 5c7323b7810f..9664d353ccd8 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -62,6 +62,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -670,7 +672,7 @@ static void icmp_unreach(struct sk_buff *skb) break; case ICMP_FRAG_NEEDED: if (ipv4_config.no_pmtu_disc) { - LIMIT_NETDEBUG(KERN_INFO "ICMP: %pI4: fragmentation needed and DF set\n", + LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: fragmentation needed and DF set\n"), &iph->daddr); } else { info = ip_rt_frag_needed(net, iph, @@ -681,7 +683,7 @@ static void icmp_unreach(struct sk_buff *skb) } break; case ICMP_SR_FAILED: - LIMIT_NETDEBUG(KERN_INFO "ICMP: %pI4: Source Route Failed\n", + LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: Source Route Failed\n"), &iph->daddr); break; default: diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index b2fd333abd59..3727e234c884 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -20,6 +20,8 @@ * Patrick McHardy : LRU queue of frag heads for evictor. */ +#define pr_fmt(fmt) "IPv4: " fmt + #include #include #include @@ -299,7 +301,7 @@ static inline struct ipq *ip_find(struct net *net, struct iphdr *iph, u32 user) return container_of(q, struct ipq, q); out_nomem: - LIMIT_NETDEBUG(KERN_ERR "ip_frag_create: no memory left !\n"); + LIMIT_NETDEBUG(KERN_ERR pr_fmt("ip_frag_create: no memory left !\n")); return NULL; } @@ -637,8 +639,8 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, return 0; out_nomem: - LIMIT_NETDEBUG(KERN_ERR "IP: queue_glue: no memory for gluing " - "queue %p\n", qp); + LIMIT_NETDEBUG(KERN_ERR pr_fmt("queue_glue: no memory for gluing queue %p\n"), + qp); err = -ENOMEM; goto out_fail; out_oversize: diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 185e0a75ecec..b57532d4742c 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -10,6 +10,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index de8e9be4aac8..f3f1108940f5 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -113,6 +113,8 @@ * 2 of the License, or (at your option) any later version. */ +#define pr_fmt(fmt) "IPv4: " fmt + #include #include #include diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index e25ee7c8bdde..a0d0d9d9b870 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -9,6 +9,8 @@ * */ +#define pr_fmt(fmt) "IPv4: " fmt + #include #include #include diff --git a/net/ipv4/route.c b/net/ipv4/route.c index cbda6de0a77c..12ccf880eb88 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -62,6 +62,8 @@ * 2 of the License, or (at your option) any later version. */ +#define pr_fmt(fmt) "IPv4: " fmt + #include #include #include @@ -1298,7 +1300,7 @@ restart: } if (net_ratelimit()) - pr_warn("ipv4: Neighbour table overflow\n"); + pr_warn("Neighbour table overflow\n"); rt_drop(rt); return ERR_PTR(-ENOBUFS); } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 2baba1bed810..cfd7edda0a8e 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -245,6 +245,8 @@ * TCP_CLOSE socket is finished */ +#define pr_fmt(fmt) "TCP: " fmt + #include #include #include @@ -1675,7 +1677,8 @@ do_prequeue: if (tp->ucopy.dma_cookie < 0) { - pr_alert("dma_cookie < 0\n"); + pr_alert("%s: dma_cookie < 0\n", + __func__); /* Exception. Bailout! */ if (!copied) @@ -1884,9 +1887,9 @@ bool tcp_check_oom(struct sock *sk, int shift) out_of_socket_memory = tcp_out_of_memory(sk); if (too_many_orphans && net_ratelimit()) - pr_info("TCP: too many orphaned sockets\n"); + pr_info("too many orphaned sockets\n"); if (out_of_socket_memory && net_ratelimit()) - pr_info("TCP: out of memory -- consider tuning tcp_mem\n"); + pr_info("out of memory -- consider tuning tcp_mem\n"); return too_many_orphans || out_of_socket_memory; } @@ -3311,7 +3314,7 @@ void __init tcp_init(void) sysctl_tcp_rmem[1] = 87380; sysctl_tcp_rmem[2] = max(87380, max_share); - pr_info("TCP: Hash tables configured (established %u bind %u)\n", + pr_info("Hash tables configured (established %u bind %u)\n", tcp_hashinfo.ehash_mask + 1, tcp_hashinfo.bhash_size); tcp_register_congestion_control(&tcp_reno); diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 67bab75f441f..272a84593c85 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -6,6 +6,8 @@ * Copyright (C) 2005 Stephen Hemminger */ +#define pr_fmt(fmt) "TCP: " fmt + #include #include #include @@ -41,17 +43,17 @@ int tcp_register_congestion_control(struct tcp_congestion_ops *ca) /* all algorithms must implement ssthresh and cong_avoid ops */ if (!ca->ssthresh || !ca->cong_avoid) { - pr_err("TCP %s does not implement required ops\n", ca->name); + pr_err("%s does not implement required ops\n", ca->name); return -EINVAL; } spin_lock(&tcp_cong_list_lock); if (tcp_ca_find(ca->name)) { - pr_notice("TCP %s already registered\n", ca->name); + pr_notice("%s already registered\n", ca->name); ret = -EEXIST; } else { list_add_tail_rcu(&ca->list, &tcp_cong_list); - pr_info("TCP %s registered\n", ca->name); + pr_info("%s registered\n", ca->name); } spin_unlock(&tcp_cong_list_lock); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 23ca3663d1e8..68d4057cba00 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -61,6 +61,8 @@ * Pasi Sarolahti: F-RTO for dealing with spurious RTOs */ +#define pr_fmt(fmt) "TCP: " fmt + #include #include #include diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 257dba66eaca..fe9f604ed1e2 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -50,6 +50,7 @@ * a single port at the same time. */ +#define pr_fmt(fmt) "TCP: " fmt #include #include @@ -876,8 +877,7 @@ int tcp_syn_flood_action(struct sock *sk, lopt = inet_csk(sk)->icsk_accept_queue.listen_opt; if (!lopt->synflood_warned) { lopt->synflood_warned = 1; - pr_info("%s: Possible SYN flooding on port %d. %s. " - " Check SNMP counters.\n", + pr_info("%s: Possible SYN flooding on port %d. %s. Check SNMP counters.\n", proto, ntohs(tcp_hdr(skb)->dest), msg); } return want_cookie; @@ -1399,7 +1399,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) * to destinations, already remembered * to the moment of synflood. */ - LIMIT_NETDEBUG(KERN_DEBUG "TCP: drop open request from %pI4/%u\n", + LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI4/%u\n"), &saddr, ntohs(tcp_hdr(skb)->source)); goto drop_and_release; } diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c index 85ee7eb7e38e..a981cdc0a6e9 100644 --- a/net/ipv4/tcp_probe.c +++ b/net/ipv4/tcp_probe.c @@ -18,6 +18,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -239,7 +241,7 @@ static __init int tcpprobe_init(void) if (ret) goto err1; - pr_info("TCP probe registered (port=%d) bufsize=%u\n", port, bufsize); + pr_info("probe registered (port=%d) bufsize=%u\n", port, bufsize); return 0; err1: proc_net_remove(&init_net, procname); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index cd2e0723266d..34d4a02c2f16 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -333,16 +333,18 @@ void tcp_retransmit_timer(struct sock *sk) */ struct inet_sock *inet = inet_sk(sk); if (sk->sk_family == AF_INET) { - LIMIT_NETDEBUG(KERN_DEBUG "TCP: Peer %pI4:%u/%u unexpectedly shrunk window %u:%u (repaired)\n", - &inet->inet_daddr, ntohs(inet->inet_dport), - inet->inet_num, tp->snd_una, tp->snd_nxt); + LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("Peer %pI4:%u/%u unexpectedly shrunk window %u:%u (repaired)\n"), + &inet->inet_daddr, + ntohs(inet->inet_dport), inet->inet_num, + tp->snd_una, tp->snd_nxt); } #if IS_ENABLED(CONFIG_IPV6) else if (sk->sk_family == AF_INET6) { struct ipv6_pinfo *np = inet6_sk(sk); - LIMIT_NETDEBUG(KERN_DEBUG "TCP: Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n", - &np->daddr, ntohs(inet->inet_dport), - inet->inet_num, tp->snd_una, tp->snd_nxt); + LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n"), + &np->daddr, + ntohs(inet->inet_dport), inet->inet_num, + tp->snd_una, tp->snd_nxt); } #endif if (tcp_time_stamp - tp->rcv_tstamp > TCP_RTO_MAX) { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 7c41ab84e72e..d6f5feeb3eaf 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -77,6 +77,8 @@ * 2 of the License, or (at your option) any later version. */ +#define pr_fmt(fmt) "UDP: " fmt + #include #include #include @@ -975,7 +977,7 @@ back_from_confirm: /* ... which is an evident application bug. --ANK */ release_sock(sk); - LIMIT_NETDEBUG(KERN_DEBUG "udp cork app bug 2\n"); + LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("cork app bug 2\n")); err = -EINVAL; goto out; } @@ -1054,7 +1056,7 @@ int udp_sendpage(struct sock *sk, struct page *page, int offset, if (unlikely(!up->pending)) { release_sock(sk); - LIMIT_NETDEBUG(KERN_DEBUG "udp cork app bug 3\n"); + LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("udp cork app bug 3\n")); return -EINVAL; } @@ -1447,9 +1449,8 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) * provided by the application." */ if (up->pcrlen == 0) { /* full coverage was set */ - LIMIT_NETDEBUG(KERN_WARNING "UDPLITE: partial coverage " - "%d while full coverage %d requested\n", - UDP_SKB_CB(skb)->cscov, skb->len); + LIMIT_NETDEBUG(KERN_WARNING "UDPLite: partial coverage %d while full coverage %d requested\n", + UDP_SKB_CB(skb)->cscov, skb->len); goto drop; } /* The next case involves violating the min. coverage requested @@ -1459,9 +1460,8 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) * Therefore the above ...()->partial_cov statement is essential. */ if (UDP_SKB_CB(skb)->cscov < up->pcrlen) { - LIMIT_NETDEBUG(KERN_WARNING - "UDPLITE: coverage %d too small, need min %d\n", - UDP_SKB_CB(skb)->cscov, up->pcrlen); + LIMIT_NETDEBUG(KERN_WARNING "UDPLite: coverage %d too small, need min %d\n", + UDP_SKB_CB(skb)->cscov, up->pcrlen); goto drop; } } @@ -1689,13 +1689,10 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, short_packet: LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: short packet: From %pI4:%u %d/%d to %pI4:%u\n", - proto == IPPROTO_UDPLITE ? "-Lite" : "", - &saddr, - ntohs(uh->source), - ulen, - skb->len, - &daddr, - ntohs(uh->dest)); + proto == IPPROTO_UDPLITE ? "Lite" : "", + &saddr, ntohs(uh->source), + ulen, skb->len, + &daddr, ntohs(uh->dest)); goto drop; csum_error: @@ -1704,11 +1701,8 @@ csum_error: * the network is concerned, anyway) as per 4.1.3.4 (MUST). */ LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: bad checksum. From %pI4:%u to %pI4:%u ulen %d\n", - proto == IPPROTO_UDPLITE ? "-Lite" : "", - &saddr, - ntohs(uh->source), - &daddr, - ntohs(uh->dest), + proto == IPPROTO_UDPLITE ? "Lite" : "", + &saddr, ntohs(uh->source), &daddr, ntohs(uh->dest), ulen); drop: UDP_INC_STATS_BH(net, UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE); diff --git a/net/ipv4/udplite.c b/net/ipv4/udplite.c index 280bfb12adcc..2c46acd4cc36 100644 --- a/net/ipv4/udplite.c +++ b/net/ipv4/udplite.c @@ -10,6 +10,9 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ + +#define pr_fmt(fmt) "UDPLite: " fmt + #include #include "udp_impl.h" diff --git a/net/ipv4/xfrm4_tunnel.c b/net/ipv4/xfrm4_tunnel.c index 5b48f49df0ba..05a5df2febc9 100644 --- a/net/ipv4/xfrm4_tunnel.c +++ b/net/ipv4/xfrm4_tunnel.c @@ -3,6 +3,8 @@ * Copyright (C) 2003 David S. Miller (davem@redhat.com) */ +#define pr_fmt(fmt) "IPsec: " fmt + #include #include #include -- cgit From 5c4903549c05bbb373479e0ce2992573c120654a Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 7 Feb 2012 02:29:01 +0000 Subject: net: Fix issue with netdev_tx_reset_queue not resetting queue from XOFF state We are seeing dev_watchdog hangs on several drivers. I suspect this is due to the __QUEUE_STATE_STACK_XOFF bit being set prior to a reset for link change, and then not being cleared by netdev_tx_reset_queue. This change corrects that. In addition we were seeing dev_watchdog hangs on igb after running the ethtool tests. We found this to be due to the fact that the ethtool test runs the same logic as ndo_start_xmit, but we were never clearing the XOFF flag since the loopback test in ethtool does not do byte queue accounting. Signed-off-by: Alexander Duyck Tested-by: Stephen Ko Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 3 ++- include/linux/netdevice.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index fda824735e18..e96cef89f121 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -2752,6 +2752,8 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, txdctl |= E1000_TXDCTL_QUEUE_ENABLE; wr32(E1000_TXDCTL(reg_idx), txdctl); + + netdev_tx_reset_queue(txring_txq(ring)); } /** @@ -3244,7 +3246,6 @@ static void igb_clean_tx_ring(struct igb_ring *tx_ring) buffer_info = &tx_ring->tx_buffer_info[i]; igb_unmap_and_free_tx_resource(tx_ring, buffer_info); } - netdev_tx_reset_queue(txring_txq(tx_ring)); size = sizeof(struct igb_tx_buffer) * tx_ring->count; memset(tx_ring->tx_buffer_info, 0, size); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b195a34440bb..4bf314fe2145 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1939,6 +1939,7 @@ static inline void netdev_completed_queue(struct net_device *dev, static inline void netdev_tx_reset_queue(struct netdev_queue *q) { #ifdef CONFIG_BQL + clear_bit(__QUEUE_STATE_STACK_XOFF, &q->state); dql_reset(&q->dql); #endif } -- cgit From b37c0fbe3f6dfba1f8ad2aed47fb40578a254635 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 7 Feb 2012 02:29:06 +0000 Subject: net: Add memory barriers to prevent possible race in byte queue limits This change adds a memory barrier to the byte queue limit code to address a possible race as has been seen in the past with the netif_stop_queue/netif_wake_queue logic. Signed-off-by: Alexander Duyck Tested-by: Stephen Ko Signed-off-by: Jeff Kirsher --- include/linux/netdevice.h | 49 +++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 4bf314fe2145..4535a4ea9760 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1899,12 +1899,22 @@ static inline void netdev_tx_sent_queue(struct netdev_queue *dev_queue, { #ifdef CONFIG_BQL dql_queued(&dev_queue->dql, bytes); - if (unlikely(dql_avail(&dev_queue->dql) < 0)) { - set_bit(__QUEUE_STATE_STACK_XOFF, &dev_queue->state); - if (unlikely(dql_avail(&dev_queue->dql) >= 0)) - clear_bit(__QUEUE_STATE_STACK_XOFF, - &dev_queue->state); - } + + if (likely(dql_avail(&dev_queue->dql) >= 0)) + return; + + set_bit(__QUEUE_STATE_STACK_XOFF, &dev_queue->state); + + /* + * The XOFF flag must be set before checking the dql_avail below, + * because in netdev_tx_completed_queue we update the dql_completed + * before checking the XOFF flag. + */ + smp_mb(); + + /* check again in case another CPU has just made room avail */ + if (unlikely(dql_avail(&dev_queue->dql) >= 0)) + clear_bit(__QUEUE_STATE_STACK_XOFF, &dev_queue->state); #endif } @@ -1917,16 +1927,23 @@ static inline void netdev_tx_completed_queue(struct netdev_queue *dev_queue, unsigned pkts, unsigned bytes) { #ifdef CONFIG_BQL - if (likely(bytes)) { - dql_completed(&dev_queue->dql, bytes); - if (unlikely(test_bit(__QUEUE_STATE_STACK_XOFF, - &dev_queue->state) && - dql_avail(&dev_queue->dql) >= 0)) { - if (test_and_clear_bit(__QUEUE_STATE_STACK_XOFF, - &dev_queue->state)) - netif_schedule_queue(dev_queue); - } - } + if (unlikely(!bytes)) + return; + + dql_completed(&dev_queue->dql, bytes); + + /* + * Without the memory barrier there is a small possiblity that + * netdev_tx_sent_queue will miss the update and cause the queue to + * be stopped forever + */ + smp_mb(); + + if (dql_avail(&dev_queue->dql) < 0) + return; + + if (test_and_clear_bit(__QUEUE_STATE_STACK_XOFF, &dev_queue->state)) + netif_schedule_queue(dev_queue); #endif } -- cgit From 9993bc635d01a6ee7f6b833b4ee65ce7c06350b1 Mon Sep 17 00:00:00 2001 From: Salman Qazi Date: Fri, 9 Mar 2012 16:41:01 -0800 Subject: sched/x86: Fix overflow in cyc2ns_offset When a machine boots up, the TSC generally gets reset. However, when kexec is used to boot into a kernel, the TSC value would be carried over from the previous kernel. The computation of cycns_offset in set_cyc2ns_scale is prone to an overflow, if the machine has been up more than 208 days prior to the kexec. The overflow happens when we multiply *scale, even though there is enough room to store the final answer. We fix this issue by decomposing tsc_now into the quotient and remainder of division by CYC2NS_SCALE_FACTOR and then performing the multiplication separately on the two components. Refactor code to share the calculation with the previous fix in __cycles_2_ns(). Signed-off-by: Salman Qazi Acked-by: John Stultz Acked-by: Peter Zijlstra Cc: Paul Turner Cc: john stultz Link: http://lkml.kernel.org/r/20120310004027.19291.88460.stgit@dungbeetle.mtv.corp.google.com Signed-off-by: Ingo Molnar --- arch/x86/include/asm/timer.h | 8 ++------ arch/x86/kernel/tsc.c | 3 ++- include/linux/kernel.h | 13 +++++++++++++ 3 files changed, 17 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/timer.h b/arch/x86/include/asm/timer.h index 431793e5d484..34baa0eb5d0c 100644 --- a/arch/x86/include/asm/timer.h +++ b/arch/x86/include/asm/timer.h @@ -57,14 +57,10 @@ DECLARE_PER_CPU(unsigned long long, cyc2ns_offset); static inline unsigned long long __cycles_2_ns(unsigned long long cyc) { - unsigned long long quot; - unsigned long long rem; int cpu = smp_processor_id(); unsigned long long ns = per_cpu(cyc2ns_offset, cpu); - quot = (cyc >> CYC2NS_SCALE_FACTOR); - rem = cyc & ((1ULL << CYC2NS_SCALE_FACTOR) - 1); - ns += quot * per_cpu(cyc2ns, cpu) + - ((rem * per_cpu(cyc2ns, cpu)) >> CYC2NS_SCALE_FACTOR); + ns += mult_frac(cyc, per_cpu(cyc2ns, cpu), + (1UL << CYC2NS_SCALE_FACTOR)); return ns; } diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index a62c201c97ec..183c5925a9fe 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c @@ -620,7 +620,8 @@ static void set_cyc2ns_scale(unsigned long cpu_khz, int cpu) if (cpu_khz) { *scale = (NSEC_PER_MSEC << CYC2NS_SCALE_FACTOR)/cpu_khz; - *offset = ns_now - (tsc_now * *scale >> CYC2NS_SCALE_FACTOR); + *offset = ns_now - mult_frac(tsc_now, *scale, + (1UL << CYC2NS_SCALE_FACTOR)); } sched_clock_idle_wakeup_event(0); diff --git a/include/linux/kernel.h b/include/linux/kernel.h index e8343422240a..d801acb5e680 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -85,6 +85,19 @@ } \ ) +/* + * Multiplies an integer by a fraction, while avoiding unnecessary + * overflow or loss of precision. + */ +#define mult_frac(x, numer, denom)( \ +{ \ + typeof(x) quot = (x) / (denom); \ + typeof(x) rem = (x) % (denom); \ + (quot * (numer)) + ((rem * (numer)) / (denom)); \ +} \ +) + + #define _RET_IP_ (unsigned long)__builtin_return_address(0) #define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; }) -- cgit From 7b8bcff2e0f11981dd6840f9feefe0914e4ea521 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 13 Mar 2012 13:57:04 +0100 Subject: cfg80211: clarify timestamp in cfg80211_inform_bss This is intended to be the timestamp sent by the peer in the beacon/probe response, not any form of host timestamp. Clarify the documentation and variable names. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 7 +++---- net/wireless/scan.c | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 66f460325e24..69b7ad3a9925 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2697,7 +2697,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, * @wiphy: the wiphy reporting the BSS * @channel: The channel the frame was received on * @bssid: the BSSID of the BSS - * @timestamp: the TSF timestamp sent by the peer + * @tsf: the TSF sent by the peer in the beacon/probe response (or 0) * @capability: the capability field sent by the peer * @beacon_interval: the beacon interval announced by the peer * @ie: additional IEs sent by the peer @@ -2713,9 +2713,8 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, struct cfg80211_bss * __must_check cfg80211_inform_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, - const u8 *bssid, - u64 timestamp, u16 capability, u16 beacon_interval, - const u8 *ie, size_t ielen, + const u8 *bssid, u64 tsf, u16 capability, + u16 beacon_interval, const u8 *ie, size_t ielen, s32 signal, gfp_t gfp); struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, diff --git a/net/wireless/scan.c b/net/wireless/scan.c index afde7e5f0010..70faadf16a32 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -734,9 +734,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, struct cfg80211_bss* cfg80211_inform_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, - const u8 *bssid, - u64 timestamp, u16 capability, u16 beacon_interval, - const u8 *ie, size_t ielen, + const u8 *bssid, u64 tsf, u16 capability, + u16 beacon_interval, const u8 *ie, size_t ielen, s32 signal, gfp_t gfp) { struct cfg80211_internal_bss *res; @@ -758,7 +757,7 @@ cfg80211_inform_bss(struct wiphy *wiphy, memcpy(res->pub.bssid, bssid, ETH_ALEN); res->pub.channel = channel; res->pub.signal = signal; - res->pub.tsf = timestamp; + res->pub.tsf = tsf; res->pub.beacon_interval = beacon_interval; res->pub.capability = capability; /* -- cgit From e9ac0745c734d39cb55ce45f1fb03a85c972b35a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 13 Mar 2012 14:29:30 +0100 Subject: mac80211: rename bss_conf timestamp to last_tsf This value is not really very useful by itself, yet some drivers (including iwlwifi until I can figure out what it should do) use it. At least rename it to "last_tsf" to indicate the meaning and add a note that it may be really old. I suspect the value may become useful combined with the rx_status->mactime, but we don't (yet) store that value and pass it to the driver. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/common.c | 2 +- drivers/net/wireless/iwlwifi/iwl-agn-rxon.c | 2 +- drivers/net/wireless/rt2x00/rt2x00config.c | 2 +- include/net/mac80211.h | 5 +++-- net/mac80211/driver-trace.h | 2 +- net/mac80211/mlme.c | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index b42052b47d8e..e5ac04739bcc 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -5355,7 +5355,7 @@ il_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (changes & BSS_CHANGED_ASSOC) { D_MAC80211("ASSOC %d\n", bss_conf->assoc); if (bss_conf->assoc) { - il->timestamp = bss_conf->timestamp; + il->timestamp = bss_conf->last_tsf; if (!il_is_rfkill(il)) il->ops->post_associate(il); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c index 36909077f994..2e1a31797a9e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c @@ -822,7 +822,7 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw, if (changes & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { - priv->timestamp = bss_conf->timestamp; + priv->timestamp = bss_conf->last_tsf; ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; } else { /* diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c index d7c0f86c9e43..293676bfa571 100644 --- a/drivers/net/wireless/rt2x00/rt2x00config.c +++ b/drivers/net/wireless/rt2x00/rt2x00config.c @@ -102,7 +102,7 @@ void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev, /* Update the AID, this is needed for dynamic PS support */ rt2x00dev->aid = bss_conf->assoc ? bss_conf->aid : 0; - rt2x00dev->last_beacon = bss_conf->timestamp; + rt2x00dev->last_beacon = bss_conf->last_tsf; /* Update global beacon interval time, this is needed for PS support */ rt2x00dev->beacon_int = bss_conf->beacon_int; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 44e4dfcb5722..9a012be615ff 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -229,7 +229,8 @@ enum ieee80211_rssi_event { * valid in station mode only while @assoc is true and if also * requested by %IEEE80211_HW_NEED_DTIM_PERIOD (cf. also hw conf * @ps_dtim_period) - * @timestamp: beacon timestamp + * @last_tsf: last beacon's/probe response's TSF timestamp (could be old + * as it may have been received during scanning long ago) * @beacon_int: beacon interval * @assoc_capability: capabilities taken from assoc resp * @basic_rates: bitmap of basic rates, each bit stands for an @@ -276,7 +277,7 @@ struct ieee80211_bss_conf { u8 dtim_period; u16 beacon_int; u16 assoc_capability; - u64 timestamp; + u64 last_tsf; u32 basic_rates; int mcast_rate[IEEE80211_NUM_BANDS]; u16 ht_operation_mode; diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 7034209ad3f4..21d6f5290a1c 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -296,7 +296,7 @@ TRACE_EVENT(drv_bss_info_changed, __entry->dtimper = info->dtim_period; __entry->bcnint = info->beacon_int; __entry->assoc_cap = info->assoc_capability; - __entry->timestamp = info->timestamp; + __entry->timestamp = info->last_tsf; __entry->basic_rates = info->basic_rates; __entry->enable_beacon = info->enable_beacon; __entry->ht_operation_mode = info->ht_operation_mode; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 39c13939008b..90d1db36cdef 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1318,7 +1318,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, bss_info_changed |= BSS_CHANGED_ASSOC; /* set timing information */ bss_conf->beacon_int = cbss->beacon_interval; - bss_conf->timestamp = cbss->tsf; + bss_conf->last_tsf = cbss->tsf; bss_info_changed |= BSS_CHANGED_BEACON_INT; bss_info_changed |= ieee80211_handle_bss_capability(sdata, -- cgit From 8816230e13d0c3c6ba51916d20e6d204646abf03 Mon Sep 17 00:00:00 2001 From: Huajun Li Date: Mon, 12 Mar 2012 21:00:19 +0800 Subject: USB: dynamically allocate usb_device children pointers instead of using a fix array Non-hub device has no child, and even a real USB hub has ports far less than USB_MAXCHILDREN, so there is no need using a fix array for child devices, just allocate it dynamically according real port number. Signed-off-by: Huajun Li Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 10 +++++++--- include/linux/usb.h | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 72c51cdce906..28664eb7f555 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1047,8 +1047,10 @@ static int hub_configure(struct usb_hub *hub, dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild, (hdev->maxchild == 1) ? "" : "s"); + hdev->children = kzalloc(hdev->maxchild * + sizeof(struct usb_device *), GFP_KERNEL); hub->port_owners = kzalloc(hdev->maxchild * sizeof(void *), GFP_KERNEL); - if (!hub->port_owners) { + if (!hdev->children || !hub->port_owners) { ret = -ENOMEM; goto fail; } @@ -1279,7 +1281,8 @@ static unsigned highspeed_hubs; static void hub_disconnect(struct usb_interface *intf) { - struct usb_hub *hub = usb_get_intfdata (intf); + struct usb_hub *hub = usb_get_intfdata(intf); + struct usb_device *hdev = interface_to_usbdev(intf); /* Take the hub off the event list and don't let it be added again */ spin_lock_irq(&hub_event_lock); @@ -1301,6 +1304,7 @@ static void hub_disconnect(struct usb_interface *intf) highspeed_hubs--; usb_free_urb(hub->urb); + kfree(hdev->children); kfree(hub->port_owners); kfree(hub->descriptor); kfree(hub->status); @@ -1676,7 +1680,7 @@ void usb_disconnect(struct usb_device **pdev) usb_lock_device(udev); /* Free up all the children before we remove this device */ - for (i = 0; i < USB_MAXCHILDREN; i++) { + for (i = 0; i < udev->maxchild; i++) { if (udev->children[i]) usb_disconnect(&udev->children[i]); } diff --git a/include/linux/usb.h b/include/linux/usb.h index 0c51663f2733..73b68d1f2cb0 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -501,7 +501,7 @@ struct usb_device { #endif int maxchild; - struct usb_device *children[USB_MAXCHILDREN]; + struct usb_device **children; u32 quirks; atomic_t urbnum; -- cgit From e485ceac9ebd43901ef0ce13622385d509e072e7 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 10 Mar 2012 15:32:08 -0800 Subject: Drivers: hv: Add new message types to enhance KVP Add additional KVP (Key Value Pair) protocol messages to enhance KVP functionality for Linux guests on Hyper-V. As part of this, patch define an explicit version negoitiation message. Reviewed-by: Haiyang Zhang Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.c | 5 +++-- include/linux/hyperv.h | 30 +++++++++++++++++++++++++++--- tools/hv/hv_kvp_daemon.c | 2 +- 3 files changed, 31 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 0ef4c1f6ca54..779109b6f4f0 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -78,7 +78,7 @@ kvp_register(void) if (msg) { kvp_msg = (struct hv_kvp_msg *)msg->data; - version = kvp_msg->body.kvp_version; + version = kvp_msg->body.kvp_register.version; msg->id.idx = CN_KVP_IDX; msg->id.val = CN_KVP_VAL; @@ -122,7 +122,8 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) * to the host. But first, cancel the timeout. */ if (cancel_delayed_work_sync(&kvp_work)) - kvp_respond_to_host(data->data.key, data->data.value, + kvp_respond_to_host(data->data.key, + data->data.value, !strlen(data->data.key)); } } diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index e57a6c6ee0e8..a2d8c547f91b 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -149,7 +149,11 @@ struct hv_kvp_exchg_msg_value { __u32 key_size; __u32 value_size; __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; - __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; + union { + __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; + __u32 value_u32; + __u64 value_u64; + }; } __attribute__((packed)); struct hv_kvp_msg_enumerate { @@ -157,11 +161,31 @@ struct hv_kvp_msg_enumerate { struct hv_kvp_exchg_msg_value data; } __attribute__((packed)); +struct hv_kvp_msg_get { + struct hv_kvp_exchg_msg_value data; +}; + +struct hv_kvp_msg_set { + struct hv_kvp_exchg_msg_value data; +}; + +struct hv_kvp_msg_delete { + __u32 key_size; + __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; +}; + +struct hv_kvp_register { + __u8 version[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; +}; + struct hv_kvp_msg { struct hv_kvp_hdr kvp_hdr; union { - struct hv_kvp_msg_enumerate kvp_enum_data; - char kvp_version[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; + struct hv_kvp_msg_get kvp_get; + struct hv_kvp_msg_set kvp_set; + struct hv_kvp_msg_delete kvp_delete; + struct hv_kvp_msg_enumerate kvp_enum_data; + struct hv_kvp_register kvp_register; } body; } __attribute__((packed)); diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 4ebf70380582..00d3f7c099e0 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -378,7 +378,7 @@ int main(void) * Driver is registering with us; stash away the version * information. */ - p = (char *)hv_msg->body.kvp_version; + p = (char *)hv_msg->body.kvp_register.version; lic_version = malloc(strlen(p) + 1); if (lic_version) { strcpy(lic_version, p); -- cgit From fa3364b5a2d79b0c94a912b371c92bd3d06bc8fb Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 13 Mar 2012 01:04:47 +0100 Subject: USB: OHCI: Add a generic platform device driver This adds a generic driver for platform devices. It works like the PCI driver and is based on it. This is for devices which do not have an own bus but their OHCI controller works like a PCI controller. It will be used for the Broadcom bcma and ssb USB OHCI controller. Acked-by: Alan Stern Signed-off-by: Hauke Mehrtens Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 10 ++ drivers/usb/host/ohci-hcd.c | 5 + drivers/usb/host/ohci-platform.c | 194 +++++++++++++++++++++++++++++++++++++++ include/linux/usb/ohci_pdriver.h | 38 ++++++++ 4 files changed, 247 insertions(+) create mode 100644 drivers/usb/host/ohci-platform.c create mode 100644 include/linux/usb/ohci_pdriver.h (limited to 'include') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index b17995829514..1dd6e13077a4 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -397,6 +397,16 @@ config USB_CNS3XXX_OHCI Enable support for the CNS3XXX SOC's on-chip OHCI controller. It is needed for low-speed USB 1.0 device support. +config USB_OHCI_HCD_PLATFORM + bool "Generic OHCI driver for a platform device" + depends on USB_OHCI_HCD && EXPERIMENTAL + default n + ---help--- + Adds an OHCI host driver for a generic platform device, which + provieds a memory space and an irq. + + If unsure, say N. + config USB_OHCI_BIG_ENDIAN_DESC bool depends on USB_OHCI_HCD diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index c1c550f2a517..ddd54c70d0fd 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1121,6 +1121,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ohci_xls_driver #endif +#ifdef CONFIG_USB_OHCI_HCD_PLATFORM +#include "ohci-platform.c" +#define PLATFORM_DRIVER ohci_platform_driver +#endif + #if !defined(PCI_DRIVER) && \ !defined(PLATFORM_DRIVER) && \ !defined(OMAP1_PLATFORM_DRIVER) && \ diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c new file mode 100644 index 000000000000..ec5c6791c8b4 --- /dev/null +++ b/drivers/usb/host/ohci-platform.c @@ -0,0 +1,194 @@ +/* + * Generic platform ohci driver + * + * Copyright 2007 Michael Buesch + * Copyright 2011-2012 Hauke Mehrtens + * + * Derived from the OCHI-SSB driver + * Derived from the OHCI-PCI driver + * Copyright 1999 Roman Weissgaerber + * Copyright 2000-2002 David Brownell + * Copyright 1999 Linus Torvalds + * Copyright 1999 Gregory P. Smith + * + * Licensed under the GNU/GPL. See COPYING for details. + */ +#include +#include + +static int ohci_platform_reset(struct usb_hcd *hcd) +{ + struct platform_device *pdev = to_platform_device(hcd->self.controller); + struct usb_ohci_pdata *pdata = pdev->dev.platform_data; + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int err; + + if (pdata->big_endian_desc) + ohci->flags |= OHCI_QUIRK_BE_DESC; + if (pdata->big_endian_mmio) + ohci->flags |= OHCI_QUIRK_BE_MMIO; + if (pdata->no_big_frame_no) + ohci->flags |= OHCI_QUIRK_FRAME_NO; + + ohci_hcd_init(ohci); + err = ohci_init(ohci); + + return err; +} + +static int ohci_platform_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int err; + + err = ohci_run(ohci); + if (err < 0) { + ohci_err(ohci, "can't start\n"); + ohci_stop(hcd); + } + + return err; +} + +static const struct hc_driver ohci_platform_hc_driver = { + .description = hcd_name, + .product_desc = "Generic Platform OHCI Controller", + .hcd_priv_size = sizeof(struct ohci_hcd), + + .irq = ohci_irq, + .flags = HCD_MEMORY | HCD_USB11, + + .reset = ohci_platform_reset, + .start = ohci_platform_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + .get_frame_number = ohci_get_frame, + + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + + .start_port_reset = ohci_start_port_reset, +}; + +static int __devinit ohci_platform_probe(struct platform_device *dev) +{ + struct usb_hcd *hcd; + struct resource *res_mem; + int irq; + int err = -ENOMEM; + + BUG_ON(!dev->dev.platform_data); + + if (usb_disabled()) + return -ENODEV; + + irq = platform_get_irq(dev, 0); + if (irq < 0) { + pr_err("no irq provieded"); + return irq; + } + + res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res_mem) { + pr_err("no memory recourse provieded"); + return -ENXIO; + } + + hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, + dev_name(&dev->dev)); + if (!hcd) + return -ENOMEM; + + hcd->rsrc_start = res_mem->start; + hcd->rsrc_len = resource_size(res_mem); + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + pr_err("controller already in use"); + err = -EBUSY; + goto err_put_hcd; + } + + hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) + goto err_release_region; + err = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (err) + goto err_iounmap; + + platform_set_drvdata(dev, hcd); + + return err; + +err_iounmap: + iounmap(hcd->regs); +err_release_region: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_put_hcd: + usb_put_hcd(hcd); + return err; +} + +static int __devexit ohci_platform_remove(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + platform_set_drvdata(dev, NULL); + + return 0; +} + +#ifdef CONFIG_PM + +static int ohci_platform_suspend(struct device *dev) +{ + return 0; +} + +static int ohci_platform_resume(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + + ohci_finish_controller_resume(hcd); + return 0; +} + +#else /* !CONFIG_PM */ +#define ohci_platform_suspend NULL +#define ohci_platform_resume NULL +#endif /* CONFIG_PM */ + +static const struct platform_device_id ohci_platform_table[] = { + { "ohci-platform", 0 }, + { } +}; +MODULE_DEVICE_TABLE(platform, ohci_platform_table); + +static const struct dev_pm_ops ohci_platform_pm_ops = { + .suspend = ohci_platform_suspend, + .resume = ohci_platform_resume, +}; + +static struct platform_driver ohci_platform_driver = { + .id_table = ohci_platform_table, + .probe = ohci_platform_probe, + .remove = __devexit_p(ohci_platform_remove), + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = "ohci-platform", + .pm = &ohci_platform_pm_ops, + } +}; diff --git a/include/linux/usb/ohci_pdriver.h b/include/linux/usb/ohci_pdriver.h new file mode 100644 index 000000000000..2808f2a9cce8 --- /dev/null +++ b/include/linux/usb/ohci_pdriver.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2012 Hauke Mehrtens + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __USB_CORE_OHCI_PDRIVER_H +#define __USB_CORE_OHCI_PDRIVER_H + +/** + * struct usb_ohci_pdata - platform_data for generic ohci driver + * + * @big_endian_desc: BE descriptors + * @big_endian_mmio: BE registers + * @no_big_frame_no: no big endian frame_no shift + * + * These are general configuration options for the OHCI controller. All of + * these options are activating more or less workarounds for some hardware. + */ +struct usb_ohci_pdata { + unsigned big_endian_desc:1; + unsigned big_endian_mmio:1; + unsigned no_big_frame_no:1; +}; + +#endif /* __USB_CORE_OHCI_PDRIVER_H */ -- cgit From 7a7a4a592f42d9abf3b6cc40620b3f79fef49246 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 13 Mar 2012 01:04:48 +0100 Subject: USB: EHCI: Add a generic platform device driver This adds a generic driver for platform devices. It works like the PCI driver and is based on it. This is for devices which do not have an own bus but their EHCI controller works like a PCI controller. It will be used for the Broadcom bcma and ssb USB EHCI controller. Acked-by: Alan Stern Signed-off-by: Hauke Mehrtens Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 10 ++ drivers/usb/host/ehci-hcd.c | 5 + drivers/usb/host/ehci-platform.c | 198 +++++++++++++++++++++++++++++++++++++++ include/linux/usb/ehci_pdriver.h | 46 +++++++++ 4 files changed, 259 insertions(+) create mode 100644 drivers/usb/host/ehci-platform.c create mode 100644 include/linux/usb/ehci_pdriver.h (limited to 'include') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 1dd6e13077a4..cbd3df8477ba 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -407,6 +407,16 @@ config USB_OHCI_HCD_PLATFORM If unsure, say N. +config USB_EHCI_HCD_PLATFORM + bool "Generic EHCI driver for a platform device" + depends on USB_EHCI_HCD && EXPERIMENTAL + default n + ---help--- + Adds an EHCI host driver for a generic platform device, which + provieds a memory space and an irq. + + If unsure, say N. + config USB_OHCI_BIG_ENDIAN_DESC bool depends on USB_OHCI_HCD diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 06e2548b549c..c97b2b6378d6 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1376,6 +1376,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_ls1x_driver #endif +#ifdef CONFIG_USB_EHCI_HCD_PLATFORM +#include "ehci-platform.c" +#define PLATFORM_DRIVER ehci_platform_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ !defined(XILINX_OF_PLATFORM_DRIVER) diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c new file mode 100644 index 000000000000..d238b4e24bb6 --- /dev/null +++ b/drivers/usb/host/ehci-platform.c @@ -0,0 +1,198 @@ +/* + * Generic platform ehci driver + * + * Copyright 2007 Steven Brown + * Copyright 2010-2012 Hauke Mehrtens + * + * Derived from the ohci-ssb driver + * Copyright 2007 Michael Buesch + * + * Derived from the EHCI-PCI driver + * Copyright (c) 2000-2004 by David Brownell + * + * Derived from the ohci-pci driver + * Copyright 1999 Roman Weissgaerber + * Copyright 2000-2002 David Brownell + * Copyright 1999 Linus Torvalds + * Copyright 1999 Gregory P. Smith + * + * Licensed under the GNU/GPL. See COPYING for details. + */ +#include +#include + +static int ehci_platform_reset(struct usb_hcd *hcd) +{ + struct platform_device *pdev = to_platform_device(hcd->self.controller); + struct usb_ehci_pdata *pdata = pdev->dev.platform_data; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + hcd->has_tt = pdata->has_tt; + ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug; + ehci->big_endian_desc = pdata->big_endian_desc; + ehci->big_endian_mmio = pdata->big_endian_mmio; + + ehci->caps = hcd->regs + pdata->caps_offset; + retval = ehci_setup(hcd); + if (retval) + return retval; + + if (pdata->port_power_on) + ehci_port_power(ehci, 1); + if (pdata->port_power_off) + ehci_port_power(ehci, 0); + + return 0; +} + +static const struct hc_driver ehci_platform_hc_driver = { + .description = hcd_name, + .product_desc = "Generic Platform EHCI Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + .reset = ehci_platform_reset, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + .get_frame_number = ehci_get_frame, + + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +#if defined(CONFIG_PM) + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +#endif + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + .update_device = ehci_update_device, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int __devinit ehci_platform_probe(struct platform_device *dev) +{ + struct usb_hcd *hcd; + struct resource *res_mem; + int irq; + int err = -ENOMEM; + + BUG_ON(!dev->dev.platform_data); + + if (usb_disabled()) + return -ENODEV; + + irq = platform_get_irq(dev, 0); + if (irq < 0) { + pr_err("no irq provieded"); + return irq; + } + res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res_mem) { + pr_err("no memory recourse provieded"); + return -ENXIO; + } + + hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, + dev_name(&dev->dev)); + if (!hcd) + return -ENOMEM; + + hcd->rsrc_start = res_mem->start; + hcd->rsrc_len = resource_size(res_mem); + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + pr_err("controller already in use"); + err = -EBUSY; + goto err_put_hcd; + } + + hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) + goto err_release_region; + err = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (err) + goto err_iounmap; + + platform_set_drvdata(dev, hcd); + + return err; + +err_iounmap: + iounmap(hcd->regs); +err_release_region: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_put_hcd: + usb_put_hcd(hcd); + return err; +} + +static int __devexit ehci_platform_remove(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + platform_set_drvdata(dev, NULL); + + return 0; +} + +#ifdef CONFIG_PM + +static int ehci_platform_suspend(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + bool wakeup = device_may_wakeup(dev); + + ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), wakeup); + return 0; +} + +static int ehci_platform_resume(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + + ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd)); + return 0; +} + +#else /* !CONFIG_PM */ +#define ehci_platform_suspend NULL +#define ehci_platform_resume NULL +#endif /* CONFIG_PM */ + +static const struct platform_device_id ehci_platform_table[] = { + { "ehci-platform", 0 }, + { } +}; +MODULE_DEVICE_TABLE(platform, ehci_platform_table); + +static const struct dev_pm_ops ehci_platform_pm_ops = { + .suspend = ehci_platform_suspend, + .resume = ehci_platform_resume, +}; + +static struct platform_driver ehci_platform_driver = { + .id_table = ehci_platform_table, + .probe = ehci_platform_probe, + .remove = __devexit_p(ehci_platform_remove), + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = "ehci-platform", + .pm = &ehci_platform_pm_ops, + } +}; diff --git a/include/linux/usb/ehci_pdriver.h b/include/linux/usb/ehci_pdriver.h new file mode 100644 index 000000000000..1894f42fe3f7 --- /dev/null +++ b/include/linux/usb/ehci_pdriver.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 Hauke Mehrtens + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __USB_CORE_EHCI_PDRIVER_H +#define __USB_CORE_EHCI_PDRIVER_H + +/** + * struct usb_ehci_pdata - platform_data for generic ehci driver + * + * @caps_offset: offset of the EHCI Capability Registers to the start of + * the io memory region provided to the driver. + * @has_tt: set to 1 if TT is integrated in root hub. + * @port_power_on: set to 1 if the controller needs a power up after + * initialization. + * @port_power_off: set to 1 if the controller needs to be powered down + * after initialization. + * + * These are general configuration options for the EHCI controller. All of + * these options are activating more or less workarounds for some hardware. + */ +struct usb_ehci_pdata { + int caps_offset; + unsigned has_tt:1; + unsigned has_synopsys_hc_bug:1; + unsigned big_endian_desc:1; + unsigned big_endian_mmio:1; + unsigned port_power_on:1; + unsigned port_power_off:1; +}; + +#endif /* __USB_CORE_EHCI_PDRIVER_H */ -- cgit From f910381a55cdaa097030291f272f6e6e4380c39a Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Thu, 15 Mar 2012 12:36:13 -0400 Subject: math: Introduce div64_long Add a div64_long macro which is used to devide a 64bit number by a long (which can be 4 bytes on 32bit systems and 8 bytes on 64bit systems). Suggested-by: Thomas Gleixner Signed-off-by: Sasha Levin Cc: johnstul@us.ibm.com Link: http://lkml.kernel.org/r/1331829374-31543-1-git-send-email-levinsasha928@gmail.com Signed-off-by: Thomas Gleixner --- include/linux/math64.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/math64.h b/include/linux/math64.h index 23fcdfcba81b..b8ba85544721 100644 --- a/include/linux/math64.h +++ b/include/linux/math64.h @@ -6,6 +6,8 @@ #if BITS_PER_LONG == 64 +#define div64_long(x,y) div64_s64((x),(y)) + /** * div_u64_rem - unsigned 64bit divide with 32bit divisor with remainder * @@ -45,6 +47,8 @@ static inline s64 div64_s64(s64 dividend, s64 divisor) #elif BITS_PER_LONG == 32 +#define div64_long(x,y) div_s64((x),(y)) + #ifndef div_u64_rem static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) { -- cgit From fa3d5b85c681518b6e4ec515814dcb2d5b702b89 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 16 Mar 2012 08:02:25 -0700 Subject: Drivers: hv: Support the newly introduced KVP messages in the driver Support the newly defined KVP message types. It turns out that the host pushes a set of standard key value pairs as soon as the guest opens the KVP channel. Since we cannot handle these tuples until the user level daemon loads up, defer reading the KVP channel until the user level daemon is launched. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Reviewed-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.c | 218 ++++++++++++++++++++++++++++++++++++----------- include/linux/hyperv.h | 2 + tools/hv/hv_kvp_daemon.c | 7 ++ 3 files changed, 176 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 779109b6f4f0..cfe60b02e3e8 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -42,9 +42,10 @@ static struct { bool active; /* transaction status - active or not */ int recv_len; /* number of bytes received. */ - int index; /* current index */ + struct hv_kvp_msg *kvp_msg; /* current message */ struct vmbus_channel *recv_channel; /* chn we got the request */ u64 recv_req_id; /* request ID. */ + void *kvp_context; /* for the channel callback */ } kvp_transaction; static void kvp_send_key(struct work_struct *dummy); @@ -110,12 +111,15 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) struct hv_kvp_msg_enumerate *data; message = (struct hv_kvp_msg *)msg->data; - if (message->kvp_hdr.operation == KVP_OP_REGISTER) { + switch (message->kvp_hdr.operation) { + case KVP_OP_REGISTER: pr_info("KVP: user-mode registering done.\n"); kvp_register(); - } + kvp_transaction.active = false; + hv_kvp_onchannelcallback(kvp_transaction.kvp_context); + break; - if (message->kvp_hdr.operation == KVP_OP_ENUMERATE) { + default: data = &message->body.kvp_enum_data; /* * Complete the transaction by forwarding the key value @@ -133,21 +137,104 @@ kvp_send_key(struct work_struct *dummy) { struct cn_msg *msg; struct hv_kvp_msg *message; - int index = kvp_transaction.index; + struct hv_kvp_msg *in_msg; + __u8 operation = kvp_transaction.kvp_msg->kvp_hdr.operation; + __u8 pool = kvp_transaction.kvp_msg->kvp_hdr.pool; + __u32 val32; + __u64 val64; msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC); + if (!msg) + return; - if (msg) { - msg->id.idx = CN_KVP_IDX; - msg->id.val = CN_KVP_VAL; + msg->id.idx = CN_KVP_IDX; + msg->id.val = CN_KVP_VAL; - message = (struct hv_kvp_msg *)msg->data; - message->kvp_hdr.operation = KVP_OP_ENUMERATE; - message->body.kvp_enum_data.index = index; - msg->len = sizeof(struct hv_kvp_msg); - cn_netlink_send(msg, 0, GFP_ATOMIC); - kfree(msg); + message = (struct hv_kvp_msg *)msg->data; + message->kvp_hdr.operation = operation; + message->kvp_hdr.pool = pool; + in_msg = kvp_transaction.kvp_msg; + + /* + * The key/value strings sent from the host are encoded in + * in utf16; convert it to utf8 strings. + * The host assures us that the utf16 strings will not exceed + * the max lengths specified. We will however, reserve room + * for the string terminating character - in the utf16s_utf8s() + * function we limit the size of the buffer where the converted + * string is placed to HV_KVP_EXCHANGE_MAX_*_SIZE -1 to gaurantee + * that the strings can be properly terminated! + */ + + switch (message->kvp_hdr.operation) { + case KVP_OP_SET: + switch (in_msg->body.kvp_set.data.value_type) { + case REG_SZ: + /* + * The value is a string - utf16 encoding. + */ + message->body.kvp_set.data.value_size = + utf16s_to_utf8s( + (wchar_t *)in_msg->body.kvp_set.data.value, + in_msg->body.kvp_set.data.value_size, + UTF16_LITTLE_ENDIAN, + message->body.kvp_set.data.value, + HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1) + 1; + break; + + case REG_U32: + /* + * The value is a 32 bit scalar. + * We save this as a utf8 string. + */ + val32 = in_msg->body.kvp_set.data.value_u32; + message->body.kvp_set.data.value_size = + sprintf(message->body.kvp_set.data.value, + "%d", val32) + 1; + break; + + case REG_U64: + /* + * The value is a 64 bit scalar. + * We save this as a utf8 string. + */ + val64 = in_msg->body.kvp_set.data.value_u64; + message->body.kvp_set.data.value_size = + sprintf(message->body.kvp_set.data.value, + "%llu", val64) + 1; + break; + + } + case KVP_OP_GET: + message->body.kvp_set.data.key_size = + utf16s_to_utf8s( + (wchar_t *)in_msg->body.kvp_set.data.key, + in_msg->body.kvp_set.data.key_size, + UTF16_LITTLE_ENDIAN, + message->body.kvp_set.data.key, + HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; + break; + + case KVP_OP_DELETE: + message->body.kvp_delete.key_size = + utf16s_to_utf8s( + (wchar_t *)in_msg->body.kvp_delete.key, + in_msg->body.kvp_delete.key_size, + UTF16_LITTLE_ENDIAN, + message->body.kvp_delete.key, + HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; + break; + + case KVP_OP_ENUMERATE: + message->body.kvp_enum_data.index = + in_msg->body.kvp_enum_data.index; + break; } + + msg->len = sizeof(struct hv_kvp_msg); + cn_netlink_send(msg, 0, GFP_ATOMIC); + kfree(msg); + return; } @@ -159,10 +246,11 @@ static void kvp_respond_to_host(char *key, char *value, int error) { struct hv_kvp_msg *kvp_msg; - struct hv_kvp_msg_enumerate *kvp_data; + struct hv_kvp_exchg_msg_value *kvp_data; char *key_name; struct icmsg_hdr *icmsghdrp; - int keylen, valuelen; + int keylen = 0; + int valuelen = 0; u32 buf_len; struct vmbus_channel *channel; u64 req_id; @@ -189,6 +277,9 @@ kvp_respond_to_host(char *key, char *value, int error) kvp_transaction.active = false; + icmsghdrp = (struct icmsg_hdr *) + &recv_buffer[sizeof(struct vmbuspipe_hdr)]; + if (channel->onchannel_callback == NULL) /* * We have raced with util driver being unloaded; @@ -196,41 +287,66 @@ kvp_respond_to_host(char *key, char *value, int error) */ return; - icmsghdrp = (struct icmsg_hdr *) - &recv_buffer[sizeof(struct vmbuspipe_hdr)]; - kvp_msg = (struct hv_kvp_msg *) - &recv_buffer[sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - kvp_data = &kvp_msg->body.kvp_enum_data; - key_name = key; /* * If the error parameter is set, terminate the host's enumeration. */ if (error) { /* - * We don't support this index or the we have timedout; + * Something failed or the we have timedout; * terminate the host-side iteration by returning an error. */ icmsghdrp->status = HV_E_FAIL; goto response_done; } + icmsghdrp->status = HV_S_OK; + + kvp_msg = (struct hv_kvp_msg *) + &recv_buffer[sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + + switch (kvp_transaction.kvp_msg->kvp_hdr.operation) { + case KVP_OP_GET: + kvp_data = &kvp_msg->body.kvp_get.data; + goto copy_value; + + case KVP_OP_SET: + case KVP_OP_DELETE: + goto response_done; + + default: + break; + } + + kvp_data = &kvp_msg->body.kvp_enum_data.data; + key_name = key; + /* * The windows host expects the key/value pair to be encoded - * in utf16. + * in utf16. Ensure that the key/value size reported to the host + * will be less than or equal to the MAX size (including the + * terminating character). */ keylen = utf8s_to_utf16s(key_name, strlen(key_name), UTF16_HOST_ENDIAN, - (wchar_t *) kvp_data->data.key, - HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2); - kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */ + (wchar_t *) kvp_data->key, + (HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2) - 2); + kvp_data->key_size = 2*(keylen + 1); /* utf16 encoding */ + +copy_value: valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN, - (wchar_t *) kvp_data->data.value, - HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2); - kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */ + (wchar_t *) kvp_data->value, + (HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2); + kvp_data->value_size = 2*(valuelen + 1); /* utf16 encoding */ - kvp_data->data.value_type = REG_SZ; /* all our values are strings */ - icmsghdrp->status = HV_S_OK; + /* + * If the utf8s to utf16s conversion failed; notify host + * of the error. + */ + if ((keylen < 0) || (valuelen < 0)) + icmsghdrp->status = HV_E_FAIL; + + kvp_data->value_type = REG_SZ; /* all our values are strings */ response_done: icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; @@ -257,11 +373,18 @@ void hv_kvp_onchannelcallback(void *context) u64 requestid; struct hv_kvp_msg *kvp_msg; - struct hv_kvp_msg_enumerate *kvp_data; struct icmsg_hdr *icmsghdrp; struct icmsg_negotiate *negop = NULL; + if (kvp_transaction.active) { + /* + * We will defer processing this callback once + * the current transaction is complete. + */ + kvp_transaction.kvp_context = context; + return; + } vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid); @@ -276,29 +399,16 @@ void hv_kvp_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; - kvp_data = &kvp_msg->body.kvp_enum_data; - - /* - * We only support the "get" operation on - * "KVP_POOL_AUTO" pool. - */ - - if ((kvp_msg->kvp_hdr.pool != KVP_POOL_AUTO) || - (kvp_msg->kvp_hdr.operation != - KVP_OP_ENUMERATE)) { - icmsghdrp->status = HV_E_FAIL; - goto callback_done; - } - /* * Stash away this global state for completing the * transaction; note transactions are serialized. */ + kvp_transaction.recv_len = recvlen; kvp_transaction.recv_channel = channel; kvp_transaction.recv_req_id = requestid; kvp_transaction.active = true; - kvp_transaction.index = kvp_data->index; + kvp_transaction.kvp_msg = kvp_msg; /* * Get the information from the @@ -316,8 +426,6 @@ void hv_kvp_onchannelcallback(void *context) } -callback_done: - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; @@ -338,6 +446,14 @@ hv_kvp_init(struct hv_util_service *srv) return err; recv_buffer = srv->recv_buffer; + /* + * When this driver loads, the user level daemon that + * processes the host requests may not yet be running. + * Defer processing channel callbacks until the daemon + * has registered. + */ + kvp_transaction.active = true; + return 0; } diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index a2d8c547f91b..e88a979107b5 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -119,6 +119,8 @@ */ #define REG_SZ 1 +#define REG_U32 4 +#define REG_U64 8 enum hv_kvp_exchg_op { KVP_OP_GET = 0, diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 00d3f7c099e0..a98878c874be 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -389,10 +389,16 @@ int main(void) } continue; + case KVP_OP_SET: + case KVP_OP_GET: + case KVP_OP_DELETE: default: break; } + if (hv_msg->kvp_hdr.operation != KVP_OP_ENUMERATE) + goto kvp_done; + hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; key_name = (char *)hv_msg->body.kvp_enum_data.data.key; key_value = (char *)hv_msg->body.kvp_enum_data.data.value; @@ -454,6 +460,7 @@ int main(void) * already in the receive buffer. Update the cn_msg header to * reflect the key value that has been added to the message */ +kvp_done: incoming_cn_msg->id.idx = CN_KVP_IDX; incoming_cn_msg->id.val = CN_KVP_VAL; -- cgit From adc80ae60eae24a43a357bf5b30fb496f34aa605 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 16 Mar 2012 08:02:27 -0700 Subject: Tools: hv: Support enumeration from all the pools We have only supported enumeration only from the AUTO pool. Now support enumeration from all the available pools. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Reviewed-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.c | 7 +-- include/linux/hyperv.h | 1 + tools/hv/hv_kvp_daemon.c | 124 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 122 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index cfe60b02e3e8..6186025209ce 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -289,14 +289,15 @@ kvp_respond_to_host(char *key, char *value, int error) /* - * If the error parameter is set, terminate the host's enumeration. + * If the error parameter is set, terminate the host's enumeration + * on this pool. */ if (error) { /* * Something failed or the we have timedout; - * terminate the host-side iteration by returning an error. + * terminate the current host-side iteration. */ - icmsghdrp->status = HV_E_FAIL; + icmsghdrp->status = HV_S_CONT; goto response_done; } diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index e88a979107b5..5852545e6bba 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -952,6 +952,7 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver); #define HV_S_OK 0x00000000 #define HV_E_FAIL 0x80004005 +#define HV_S_CONT 0x80070103 #define HV_ERROR_NOT_SUPPORTED 0x80070032 #define HV_ERROR_MACHINE_LOCKED 0x800704F7 diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 2fb9c3d09d7f..146fd6147e84 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -148,6 +148,51 @@ static void kvp_update_file(int pool) kvp_release_lock(pool); } +static void kvp_update_mem_state(int pool) +{ + FILE *filep; + size_t records_read = 0; + struct kvp_record *record = kvp_file_info[pool].records; + struct kvp_record *readp; + int num_blocks = kvp_file_info[pool].num_blocks; + int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; + + kvp_acquire_lock(pool); + + filep = fopen(kvp_file_info[pool].fname, "r"); + if (!filep) { + kvp_release_lock(pool); + syslog(LOG_ERR, "Failed to open file, pool: %d", pool); + exit(-1); + } + while (!feof(filep)) { + readp = &record[records_read]; + records_read += fread(readp, sizeof(struct kvp_record), + ENTRIES_PER_BLOCK * num_blocks, + filep); + + if (!feof(filep)) { + /* + * We have more data to read. + */ + num_blocks++; + record = realloc(record, alloc_unit * num_blocks); + + if (record == NULL) { + syslog(LOG_ERR, "malloc failed"); + exit(-1); + } + continue; + } + break; + } + + kvp_file_info[pool].num_blocks = num_blocks; + kvp_file_info[pool].records = record; + kvp_file_info[pool].num_records = records_read; + + kvp_release_lock(pool); +} static int kvp_file_init(void) { int ret, fd; @@ -223,8 +268,16 @@ static int kvp_key_delete(int pool, __u8 *key, int key_size) { int i; int j, k; - int num_records = kvp_file_info[pool].num_records; - struct kvp_record *record = kvp_file_info[pool].records; + int num_records; + struct kvp_record *record; + + /* + * First update the in-memory state. + */ + kvp_update_mem_state(pool); + + num_records = kvp_file_info[pool].num_records; + record = kvp_file_info[pool].records; for (i = 0; i < num_records; i++) { if (memcmp(key, record[i].key, key_size)) @@ -259,14 +312,23 @@ static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value, { int i; int j, k; - int num_records = kvp_file_info[pool].num_records; - struct kvp_record *record = kvp_file_info[pool].records; - int num_blocks = kvp_file_info[pool].num_blocks; + int num_records; + struct kvp_record *record; + int num_blocks; if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) return 1; + /* + * First update the in-memory state. + */ + kvp_update_mem_state(pool); + + num_records = kvp_file_info[pool].num_records; + record = kvp_file_info[pool].records; + num_blocks = kvp_file_info[pool].num_blocks; + for (i = 0; i < num_records; i++) { if (memcmp(key, record[i].key, key_size)) continue; @@ -304,13 +366,21 @@ static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value, int value_size) { int i; - int num_records = kvp_file_info[pool].num_records; - struct kvp_record *record = kvp_file_info[pool].records; + int num_records; + struct kvp_record *record; if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) return 1; + /* + * First update the in-memory state. + */ + kvp_update_mem_state(pool); + + num_records = kvp_file_info[pool].num_records; + record = kvp_file_info[pool].records; + for (i = 0; i < num_records; i++) { if (memcmp(key, record[i].key, key_size)) continue; @@ -324,6 +394,31 @@ static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value, return 1; } +static void kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, + __u8 *value, int value_size) +{ + struct kvp_record *record; + + /* + * First update our in-memory database. + */ + kvp_update_mem_state(pool); + record = kvp_file_info[pool].records; + + if (index >= kvp_file_info[pool].num_records) { + /* + * This is an invalid index; terminate enumeration; + * - a NULL value will do the trick. + */ + strcpy(value, ""); + return; + } + + memcpy(key, record[index].key, key_size); + memcpy(value, record[index].value, value_size); +} + + void kvp_get_os_info(void) { FILE *file; @@ -678,6 +773,21 @@ int main(void) if (hv_msg->kvp_hdr.operation != KVP_OP_ENUMERATE) goto kvp_done; + /* + * If the pool is KVP_POOL_AUTO, dynamically generate + * both the key and the value; if not read from the + * appropriate pool. + */ + if (hv_msg->kvp_hdr.pool != KVP_POOL_AUTO) { + kvp_pool_enumerate(hv_msg->kvp_hdr.pool, + hv_msg->body.kvp_enum_data.index, + hv_msg->body.kvp_enum_data.data.key, + HV_KVP_EXCHANGE_MAX_KEY_SIZE, + hv_msg->body.kvp_enum_data.data.value, + HV_KVP_EXCHANGE_MAX_VALUE_SIZE); + goto kvp_done; + } + hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; key_name = (char *)hv_msg->body.kvp_enum_data.data.key; key_value = (char *)hv_msg->body.kvp_enum_data.data.value; -- cgit From 124d37e9f088a8f56494b0264d63d22555f53fef Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Thu, 15 Mar 2012 05:25:58 +0000 Subject: arp: allow arp processing to honor per interface arp_accept sysctl I found recently that the arp_process function which handles all of our received arp frames, is using IPV4_DEVCONF_ALL macro to check the state of the arp_process flag. This seems wrong, as it implies that either none or all of the network interfaces accept gratuitous arps. This patch corrects that, allowing per-interface arp_accept configuration to deviate from the all setting. Note this also brings us into line with the way the arp_filter setting is handled during arp_process execution. Tested this myself on my home network, and confirmed it works as expected. Signed-off-by: Neil Horman CC: "David S. Miller" Signed-off-by: David S. Miller --- include/linux/inetdevice.h | 1 + net/ipv4/arp.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 5f8146695b7f..597f4a9f3240 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -139,6 +139,7 @@ static inline void ipv4_devconf_setall(struct in_device *in_dev) IN_DEV_ORCONF((in_dev), ACCEPT_REDIRECTS))) #define IN_DEV_ARPFILTER(in_dev) IN_DEV_ORCONF((in_dev), ARPFILTER) +#define IN_DEV_ARP_ACCEPT(in_dev) IN_DEV_ORCONF((in_dev), ARP_ACCEPT) #define IN_DEV_ARP_ANNOUNCE(in_dev) IN_DEV_MAXCONF((in_dev), ARP_ANNOUNCE) #define IN_DEV_ARP_IGNORE(in_dev) IN_DEV_MAXCONF((in_dev), ARP_IGNORE) #define IN_DEV_ARP_NOTIFY(in_dev) IN_DEV_MAXCONF((in_dev), ARP_NOTIFY) diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 63e49890ad31..73f46d691abc 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -889,7 +889,7 @@ static int arp_process(struct sk_buff *skb) n = __neigh_lookup(&arp_tbl, &sip, dev, 0); - if (IPV4_DEVCONF_ALL(dev_net(dev), ARP_ACCEPT)) { + if (IN_DEV_ARP_ACCEPT(in_dev)) { /* Unsolicited ARP is not accepted by default. It is possible, that this option should be enabled for some devices (strip is candidate) -- cgit From c8628155ece363487b57d33441ea0359018c0fa7 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 18 Mar 2012 11:07:47 +0000 Subject: tcp: reduce out_of_order memory use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With increasing receive window sizes, but speed of light not improved that much, out of order queue can contain a huge number of skbs, waiting to be moved to receive_queue when missing packets can fill the holes. Some devices happen to use fat skbs (truesize of 4096 + sizeof(struct sk_buff)) to store regular (MTU <= 1500) frames. This makes highly probable sk_rmem_alloc hits sk_rcvbuf limit, which can be 4Mbytes in many cases. When limit is hit, tcp stack calls tcp_collapse_ofo_queue(), a true latency killer and cpu cache blower. Doing the coalescing attempt each time we add a frame in ofo queue permits to keep memory use tight and in many cases avoid the tcp_collapse() thing later. Tested on various wireless setups (b43, ath9k, ...) known to use big skb truesize, this patch removed the "packets collapsed in receive queue due to low socket buffer" I had before. This also reduced average memory used by tcp sockets. With help from Neal Cardwell. Signed-off-by: Eric Dumazet Cc: Neal Cardwell Cc: Yuchung Cheng Cc: H.K. Jerry Chu Cc: Tom Herbert Cc: Ilpo Järvinen Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/linux/snmp.h | 1 + net/ipv4/proc.c | 1 + net/ipv4/tcp_input.c | 19 ++++++++++++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/snmp.h b/include/linux/snmp.h index 8ee8af4e6da9..2e68f5ba0389 100644 --- a/include/linux/snmp.h +++ b/include/linux/snmp.h @@ -233,6 +233,7 @@ enum LINUX_MIB_TCPREQQFULLDOCOOKIES, /* TCPReqQFullDoCookies */ LINUX_MIB_TCPREQQFULLDROP, /* TCPReqQFullDrop */ LINUX_MIB_TCPRETRANSFAIL, /* TCPRetransFail */ + LINUX_MIB_TCPRCVCOALESCE, /* TCPRcvCoalesce */ __LINUX_MIB_MAX }; diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 02d61079f08b..8af0d44e4e22 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -257,6 +257,7 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPReqQFullDoCookies", LINUX_MIB_TCPREQQFULLDOCOOKIES), SNMP_MIB_ITEM("TCPReqQFullDrop", LINUX_MIB_TCPREQQFULLDROP), SNMP_MIB_ITEM("TCPRetransFail", LINUX_MIB_TCPRETRANSFAIL), + SNMP_MIB_ITEM("TCPRcvCoalesce", LINUX_MIB_TCPRCVCOALESCE), SNMP_MIB_SENTINEL }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index fa7de12c4a52..e886e2f7fa8d 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4484,7 +4484,24 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) end_seq = TCP_SKB_CB(skb)->end_seq; if (seq == TCP_SKB_CB(skb1)->end_seq) { - __skb_queue_after(&tp->out_of_order_queue, skb1, skb); + /* Packets in ofo can stay in queue a long time. + * Better try to coalesce them right now + * to avoid future tcp_collapse_ofo_queue(), + * probably the most expensive function in tcp stack. + */ + if (skb->len <= skb_tailroom(skb1) && !tcp_hdr(skb)->fin) { + NET_INC_STATS_BH(sock_net(sk), + LINUX_MIB_TCPRCVCOALESCE); + BUG_ON(skb_copy_bits(skb, 0, + skb_put(skb1, skb->len), + skb->len)); + TCP_SKB_CB(skb1)->end_seq = end_seq; + TCP_SKB_CB(skb1)->ack_seq = TCP_SKB_CB(skb)->ack_seq; + __kfree_skb(skb); + skb = NULL; + } else { + __skb_queue_after(&tp->out_of_order_queue, skb1, skb); + } if (!tp->rx_opt.num_sacks || tp->selective_acks[0].end_seq != seq) -- cgit From a3f671b3152919e72af261333402c0f1272bdf59 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 18 Mar 2012 17:37:56 +0000 Subject: if_vlan: Remove VLAN_ETH_ALEN define and the 1 use of it Just use ETH_ALEN. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 13aff1e2183b..33a6e1951d4d 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -18,10 +18,9 @@ #include #include -#define VLAN_HLEN 4 /* The additional bytes (on top of the Ethernet header) - * that VLAN requires. +#define VLAN_HLEN 4 /* The additional bytes required by VLAN + * (in addition to the Ethernet header) */ -#define VLAN_ETH_ALEN 6 /* Octets in one ethernet addr */ #define VLAN_ETH_HLEN 18 /* Total octets in header. */ #define VLAN_ETH_ZLEN 64 /* Min. octets in frame sans FCS */ @@ -177,7 +176,7 @@ static inline struct sk_buff *vlan_insert_tag(struct sk_buff *skb, u16 vlan_tci) veth = (struct vlan_ethhdr *)skb_push(skb, VLAN_HLEN); /* Move the mac addresses to the beginning of the new header. */ - memmove(skb->data, skb->data + VLAN_HLEN, 2 * VLAN_ETH_ALEN); + memmove(skb->data, skb->data + VLAN_HLEN, 2 * ETH_ALEN); skb->mac_header -= VLAN_HLEN; /* first, the ethernet type */ -- cgit From cdbee74ce74cd66de42a6f6d0edfa74fa9a66f2a Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Fri, 16 Mar 2012 23:08:11 +0000 Subject: net: do not do gso for CHECKSUM_UNNECESSARY in netif_needs_gso This is related to fixing the bug of dropping FCoE frames when disabling tx ip checksum by 'ethtool -K ethx tx off'. The FCoE protocol stack driver would use CHECKSUM_UNNECESSARY on tx path instead of CHECKSUM_PARTIAL (as indicated in the 2/2 of this series). To do so, netif_needs_gso() has to be changed here to not do gso for both CHECKSUM_PARTIAL and CHECKSUM_UNNECESSARY. Ref. to original discussion thread: http://patchwork.ozlabs.org/patch/146567/ Signed-off-by: Yi Zou Tested-by: Ross Brattain Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 4535a4ea9760..261067b94559 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2657,7 +2657,8 @@ static inline bool netif_needs_gso(struct sk_buff *skb, netdev_features_t features) { return skb_is_gso(skb) && (!skb_gso_ok(skb, features) || - unlikely(skb->ip_summed != CHECKSUM_PARTIAL)); + unlikely((skb->ip_summed != CHECKSUM_PARTIAL) && + (skb->ip_summed != CHECKSUM_UNNECESSARY))); } static inline void netif_set_gso_max_size(struct net_device *dev, -- cgit From 3af79302b400e05b45e377993a8870869500466b Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Mon, 19 Mar 2012 11:12:41 +0000 Subject: net: update the usage of CHECKSUM_UNNECESSARY As suggested by Ben, this adds the clarification on the usage of CHECKSUM_UNNECESSARY on the outgoing patch. Also add the usage description of NETIF_F_FCOE_CRC and CHECKSUM_UNNECESSARY for the kernel FCoE protocol driver. This is a follow-up to the following: http://patchwork.ozlabs.org/patch/147315/ Signed-off-by: Yi Zou Cc: Ben Hutchings Cc: Jeff Kirsher Cc: www.Open-FCoE.org Signed-off-by: David S. Miller --- include/linux/skbuff.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 8dc8257eab8b..a2b9953b582d 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -94,6 +94,13 @@ * about CHECKSUM_UNNECESSARY. 8) * NETIF_F_IPV6_CSUM about as dumb as the last one but does IPv6 instead. * + * UNNECESSARY: device will do per protocol specific csum. Protocol drivers + * that do not want net to perform the checksum calculation should use + * this flag in their outgoing skbs. + * NETIF_F_FCOE_CRC this indicates the device can do FCoE FC CRC + * offload. Correspondingly, the FCoE protocol driver + * stack should use CHECKSUM_UNNECESSARY. + * * Any questions? No questions, good. --ANK */ -- cgit From df828598a755732e717b0adca82f884e44d37576 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Sun, 18 Mar 2012 20:17:54 +0000 Subject: netdev: driver: ethernet: Add TI CPSW driver This patch adds support for TI's CPSW driver. The three port switch gigabit ethernet subsystem provides ethernet packet communication and can be configured as an ethernet switch. Supports 10/100/1000 Mbps. Signed-off-by: Cyril Chemparathy Signed-off-by: Sriramakrishnan A G Signed-off-by: Mugunthan V N Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/Kconfig | 11 + drivers/net/ethernet/ti/Makefile | 2 + drivers/net/ethernet/ti/cpsw.c | 1018 ++++++++++++++++++++++++++++++++++++ include/linux/platform_data/cpsw.h | 55 ++ 4 files changed, 1086 insertions(+) create mode 100644 drivers/net/ethernet/ti/cpsw.c create mode 100644 include/linux/platform_data/cpsw.h (limited to 'include') diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig index de76c70ec8fb..b42252c4bec8 100644 --- a/drivers/net/ethernet/ti/Kconfig +++ b/drivers/net/ethernet/ti/Kconfig @@ -49,6 +49,17 @@ config TI_DAVINCI_CPDMA To compile this driver as a module, choose M here: the module will be called davinci_cpdma. This is recommended. +config TI_CPSW + tristate "TI CPSW Switch Support" + depends on ARM && (ARCH_DAVINCI || SOC_OMAPAM33XX) + select TI_DAVINCI_CPDMA + select TI_DAVINCI_MDIO + ---help--- + This driver supports TI's CPSW Ethernet Switch. + + To compile this driver as a module, choose M here: the module + will be called cpsw. + config TLAN tristate "TI ThunderLAN support" depends on (PCI || EISA) diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile index aedb3af74e5a..91bd8bba78ff 100644 --- a/drivers/net/ethernet/ti/Makefile +++ b/drivers/net/ethernet/ti/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_CPMAC) += cpmac.o obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci_emac.o obj-$(CONFIG_TI_DAVINCI_MDIO) += davinci_mdio.o obj-$(CONFIG_TI_DAVINCI_CPDMA) += davinci_cpdma.o +obj-$(CONFIG_TI_CPSW) += ti_cpsw.o +ti_cpsw-y := cpsw_ale.o cpsw.o diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c new file mode 100644 index 000000000000..c68c9d96312e --- /dev/null +++ b/drivers/net/ethernet/ti/cpsw.c @@ -0,0 +1,1018 @@ +/* + * Texas Instruments Ethernet Switch Driver + * + * Copyright (C) 2012 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "cpsw_ale.h" +#include "davinci_cpdma.h" + +#define CPSW_DEBUG (NETIF_MSG_HW | NETIF_MSG_WOL | \ + NETIF_MSG_DRV | NETIF_MSG_LINK | \ + NETIF_MSG_IFUP | NETIF_MSG_INTR | \ + NETIF_MSG_PROBE | NETIF_MSG_TIMER | \ + NETIF_MSG_IFDOWN | NETIF_MSG_RX_ERR | \ + NETIF_MSG_TX_ERR | NETIF_MSG_TX_DONE | \ + NETIF_MSG_PKTDATA | NETIF_MSG_TX_QUEUED | \ + NETIF_MSG_RX_STATUS) + +#define cpsw_info(priv, type, format, ...) \ +do { \ + if (netif_msg_##type(priv) && net_ratelimit()) \ + dev_info(priv->dev, format, ## __VA_ARGS__); \ +} while (0) + +#define cpsw_err(priv, type, format, ...) \ +do { \ + if (netif_msg_##type(priv) && net_ratelimit()) \ + dev_err(priv->dev, format, ## __VA_ARGS__); \ +} while (0) + +#define cpsw_dbg(priv, type, format, ...) \ +do { \ + if (netif_msg_##type(priv) && net_ratelimit()) \ + dev_dbg(priv->dev, format, ## __VA_ARGS__); \ +} while (0) + +#define cpsw_notice(priv, type, format, ...) \ +do { \ + if (netif_msg_##type(priv) && net_ratelimit()) \ + dev_notice(priv->dev, format, ## __VA_ARGS__); \ +} while (0) + +#define CPSW_MAJOR_VERSION(reg) (reg >> 8 & 0x7) +#define CPSW_MINOR_VERSION(reg) (reg & 0xff) +#define CPSW_RTL_VERSION(reg) ((reg >> 11) & 0x1f) + +#define CPDMA_RXTHRESH 0x0c0 +#define CPDMA_RXFREE 0x0e0 +#define CPDMA_TXHDP 0x00 +#define CPDMA_RXHDP 0x20 +#define CPDMA_TXCP 0x40 +#define CPDMA_RXCP 0x60 + +#define cpsw_dma_regs(base, offset) \ + (void __iomem *)((base) + (offset)) +#define cpsw_dma_rxthresh(base, offset) \ + (void __iomem *)((base) + (offset) + CPDMA_RXTHRESH) +#define cpsw_dma_rxfree(base, offset) \ + (void __iomem *)((base) + (offset) + CPDMA_RXFREE) +#define cpsw_dma_txhdp(base, offset) \ + (void __iomem *)((base) + (offset) + CPDMA_TXHDP) +#define cpsw_dma_rxhdp(base, offset) \ + (void __iomem *)((base) + (offset) + CPDMA_RXHDP) +#define cpsw_dma_txcp(base, offset) \ + (void __iomem *)((base) + (offset) + CPDMA_TXCP) +#define cpsw_dma_rxcp(base, offset) \ + (void __iomem *)((base) + (offset) + CPDMA_RXCP) + +#define CPSW_POLL_WEIGHT 64 +#define CPSW_MIN_PACKET_SIZE 60 +#define CPSW_MAX_PACKET_SIZE (1500 + 14 + 4 + 4) + +#define RX_PRIORITY_MAPPING 0x76543210 +#define TX_PRIORITY_MAPPING 0x33221100 +#define CPDMA_TX_PRIORITY_MAP 0x76543210 + +#define cpsw_enable_irq(priv) \ + do { \ + u32 i; \ + for (i = 0; i < priv->num_irqs; i++) \ + enable_irq(priv->irqs_table[i]); \ + } while (0); +#define cpsw_disable_irq(priv) \ + do { \ + u32 i; \ + for (i = 0; i < priv->num_irqs; i++) \ + disable_irq_nosync(priv->irqs_table[i]); \ + } while (0); + +static int debug_level; +module_param(debug_level, int, 0); +MODULE_PARM_DESC(debug_level, "cpsw debug level (NETIF_MSG bits)"); + +static int ale_ageout = 10; +module_param(ale_ageout, int, 0); +MODULE_PARM_DESC(ale_ageout, "cpsw ale ageout interval (seconds)"); + +static int rx_packet_max = CPSW_MAX_PACKET_SIZE; +module_param(rx_packet_max, int, 0); +MODULE_PARM_DESC(rx_packet_max, "maximum receive packet size (bytes)"); + +struct cpsw_ss_regs { + u32 id_ver; + u32 soft_reset; + u32 control; + u32 int_control; + u32 rx_thresh_en; + u32 rx_en; + u32 tx_en; + u32 misc_en; +}; + +struct cpsw_regs { + u32 id_ver; + u32 control; + u32 soft_reset; + u32 stat_port_en; + u32 ptype; +}; + +struct cpsw_slave_regs { + u32 max_blks; + u32 blk_cnt; + u32 flow_thresh; + u32 port_vlan; + u32 tx_pri_map; + u32 ts_ctl; + u32 ts_seq_ltype; + u32 ts_vlan; + u32 sa_lo; + u32 sa_hi; +}; + +struct cpsw_host_regs { + u32 max_blks; + u32 blk_cnt; + u32 flow_thresh; + u32 port_vlan; + u32 tx_pri_map; + u32 cpdma_tx_pri_map; + u32 cpdma_rx_chan_map; +}; + +struct cpsw_sliver_regs { + u32 id_ver; + u32 mac_control; + u32 mac_status; + u32 soft_reset; + u32 rx_maxlen; + u32 __reserved_0; + u32 rx_pause; + u32 tx_pause; + u32 __reserved_1; + u32 rx_pri_map; +}; + +struct cpsw_slave { + struct cpsw_slave_regs __iomem *regs; + struct cpsw_sliver_regs __iomem *sliver; + int slave_num; + u32 mac_control; + struct cpsw_slave_data *data; + struct phy_device *phy; +}; + +struct cpsw_priv { + spinlock_t lock; + struct platform_device *pdev; + struct net_device *ndev; + struct resource *cpsw_res; + struct resource *cpsw_ss_res; + struct napi_struct napi; + struct device *dev; + struct cpsw_platform_data data; + struct cpsw_regs __iomem *regs; + struct cpsw_ss_regs __iomem *ss_regs; + struct cpsw_host_regs __iomem *host_port_regs; + u32 msg_enable; + struct net_device_stats stats; + int rx_packet_max; + int host_port; + struct clk *clk; + u8 mac_addr[ETH_ALEN]; + struct cpsw_slave *slaves; + struct cpdma_ctlr *dma; + struct cpdma_chan *txch, *rxch; + struct cpsw_ale *ale; + /* snapshot of IRQ numbers */ + u32 irqs_table[4]; + u32 num_irqs; +}; + +#define napi_to_priv(napi) container_of(napi, struct cpsw_priv, napi) +#define for_each_slave(priv, func, arg...) \ + do { \ + int idx; \ + for (idx = 0; idx < (priv)->data.slaves; idx++) \ + (func)((priv)->slaves + idx, ##arg); \ + } while (0) + +static void cpsw_intr_enable(struct cpsw_priv *priv) +{ + __raw_writel(0xFF, &priv->ss_regs->tx_en); + __raw_writel(0xFF, &priv->ss_regs->rx_en); + + cpdma_ctlr_int_ctrl(priv->dma, true); + return; +} + +static void cpsw_intr_disable(struct cpsw_priv *priv) +{ + __raw_writel(0, &priv->ss_regs->tx_en); + __raw_writel(0, &priv->ss_regs->rx_en); + + cpdma_ctlr_int_ctrl(priv->dma, false); + return; +} + +void cpsw_tx_handler(void *token, int len, int status) +{ + struct sk_buff *skb = token; + struct net_device *ndev = skb->dev; + struct cpsw_priv *priv = netdev_priv(ndev); + + if (unlikely(netif_queue_stopped(ndev))) + netif_start_queue(ndev); + priv->stats.tx_packets++; + priv->stats.tx_bytes += len; + dev_kfree_skb_any(skb); +} + +void cpsw_rx_handler(void *token, int len, int status) +{ + struct sk_buff *skb = token; + struct net_device *ndev = skb->dev; + struct cpsw_priv *priv = netdev_priv(ndev); + int ret = 0; + + /* free and bail if we are shutting down */ + if (unlikely(!netif_running(ndev)) || + unlikely(!netif_carrier_ok(ndev))) { + dev_kfree_skb_any(skb); + return; + } + if (likely(status >= 0)) { + skb_put(skb, len); + skb->protocol = eth_type_trans(skb, ndev); + netif_receive_skb(skb); + priv->stats.rx_bytes += len; + priv->stats.rx_packets++; + skb = NULL; + } + + if (unlikely(!netif_running(ndev))) { + if (skb) + dev_kfree_skb_any(skb); + return; + } + + if (likely(!skb)) { + skb = netdev_alloc_skb_ip_align(ndev, priv->rx_packet_max); + if (WARN_ON(!skb)) + return; + + ret = cpdma_chan_submit(priv->rxch, skb, skb->data, + skb_tailroom(skb), GFP_KERNEL); + } + WARN_ON(ret < 0); +} + +static irqreturn_t cpsw_interrupt(int irq, void *dev_id) +{ + struct cpsw_priv *priv = dev_id; + + if (likely(netif_running(priv->ndev))) { + cpsw_intr_disable(priv); + cpsw_disable_irq(priv); + napi_schedule(&priv->napi); + } + return IRQ_HANDLED; +} + +static inline int cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num) +{ + if (priv->host_port == 0) + return slave_num + 1; + else + return slave_num; +} + +static int cpsw_poll(struct napi_struct *napi, int budget) +{ + struct cpsw_priv *priv = napi_to_priv(napi); + int num_tx, num_rx; + + num_tx = cpdma_chan_process(priv->txch, 128); + num_rx = cpdma_chan_process(priv->rxch, budget); + + if (num_rx || num_tx) + cpsw_dbg(priv, intr, "poll %d rx, %d tx pkts\n", + num_rx, num_tx); + + if (num_rx < budget) { + napi_complete(napi); + cpsw_intr_enable(priv); + cpdma_ctlr_eoi(priv->dma); + cpsw_enable_irq(priv); + } + + return num_rx; +} + +static inline void soft_reset(const char *module, void __iomem *reg) +{ + unsigned long timeout = jiffies + HZ; + + __raw_writel(1, reg); + do { + cpu_relax(); + } while ((__raw_readl(reg) & 1) && time_after(timeout, jiffies)); + + WARN(__raw_readl(reg) & 1, "failed to soft-reset %s\n", module); +} + +#define mac_hi(mac) (((mac)[0] << 0) | ((mac)[1] << 8) | \ + ((mac)[2] << 16) | ((mac)[3] << 24)) +#define mac_lo(mac) (((mac)[4] << 0) | ((mac)[5] << 8)) + +static void cpsw_set_slave_mac(struct cpsw_slave *slave, + struct cpsw_priv *priv) +{ + __raw_writel(mac_hi(priv->mac_addr), &slave->regs->sa_hi); + __raw_writel(mac_lo(priv->mac_addr), &slave->regs->sa_lo); +} + +static void _cpsw_adjust_link(struct cpsw_slave *slave, + struct cpsw_priv *priv, bool *link) +{ + struct phy_device *phy = slave->phy; + u32 mac_control = 0; + u32 slave_port; + + if (!phy) + return; + + slave_port = cpsw_get_slave_port(priv, slave->slave_num); + + if (phy->link) { + mac_control = priv->data.mac_control; + + /* enable forwarding */ + cpsw_ale_control_set(priv->ale, slave_port, + ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); + + if (phy->speed == 1000) + mac_control |= BIT(7); /* GIGABITEN */ + if (phy->duplex) + mac_control |= BIT(0); /* FULLDUPLEXEN */ + *link = true; + } else { + mac_control = 0; + /* disable forwarding */ + cpsw_ale_control_set(priv->ale, slave_port, + ALE_PORT_STATE, ALE_PORT_STATE_DISABLE); + } + + if (mac_control != slave->mac_control) { + phy_print_status(phy); + __raw_writel(mac_control, &slave->sliver->mac_control); + } + + slave->mac_control = mac_control; +} + +static void cpsw_adjust_link(struct net_device *ndev) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + bool link = false; + + for_each_slave(priv, _cpsw_adjust_link, priv, &link); + + if (link) { + netif_carrier_on(ndev); + if (netif_running(ndev)) + netif_wake_queue(ndev); + } else { + netif_carrier_off(ndev); + netif_stop_queue(ndev); + } +} + +static inline int __show_stat(char *buf, int maxlen, const char *name, u32 val) +{ + static char *leader = "........................................"; + + if (!val) + return 0; + else + return snprintf(buf, maxlen, "%s %s %10d\n", name, + leader + strlen(name), val); +} + +static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) +{ + char name[32]; + u32 slave_port; + + sprintf(name, "slave-%d", slave->slave_num); + + soft_reset(name, &slave->sliver->soft_reset); + + /* setup priority mapping */ + __raw_writel(RX_PRIORITY_MAPPING, &slave->sliver->rx_pri_map); + __raw_writel(TX_PRIORITY_MAPPING, &slave->regs->tx_pri_map); + + /* setup max packet size, and mac address */ + __raw_writel(priv->rx_packet_max, &slave->sliver->rx_maxlen); + cpsw_set_slave_mac(slave, priv); + + slave->mac_control = 0; /* no link yet */ + + slave_port = cpsw_get_slave_port(priv, slave->slave_num); + + cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, + 1 << slave_port, 0, ALE_MCAST_FWD_2); + + slave->phy = phy_connect(priv->ndev, slave->data->phy_id, + &cpsw_adjust_link, 0, slave->data->phy_if); + if (IS_ERR(slave->phy)) { + dev_err(priv->dev, "phy %s not found on slave %d\n", + slave->data->phy_id, slave->slave_num); + slave->phy = NULL; + } else { + dev_info(priv->dev, "phy found : id is : 0x%x\n", + slave->phy->phy_id); + phy_start(slave->phy); + } +} + +static void cpsw_init_host_port(struct cpsw_priv *priv) +{ + /* soft reset the controller and initialize ale */ + soft_reset("cpsw", &priv->regs->soft_reset); + cpsw_ale_start(priv->ale); + + /* switch to vlan unaware mode */ + cpsw_ale_control_set(priv->ale, 0, ALE_VLAN_AWARE, 0); + + /* setup host port priority mapping */ + __raw_writel(CPDMA_TX_PRIORITY_MAP, + &priv->host_port_regs->cpdma_tx_pri_map); + __raw_writel(0, &priv->host_port_regs->cpdma_rx_chan_map); + + cpsw_ale_control_set(priv->ale, priv->host_port, + ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); + + cpsw_ale_add_ucast(priv->ale, priv->mac_addr, priv->host_port, 0); + cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, + 1 << priv->host_port, 0, ALE_MCAST_FWD_2); +} + +static int cpsw_ndo_open(struct net_device *ndev) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + int i, ret; + u32 reg; + + cpsw_intr_disable(priv); + netif_carrier_off(ndev); + + ret = clk_enable(priv->clk); + if (ret < 0) { + dev_err(priv->dev, "unable to turn on device clock\n"); + return ret; + } + + reg = __raw_readl(&priv->regs->id_ver); + + dev_info(priv->dev, "initializing cpsw version %d.%d (%d)\n", + CPSW_MAJOR_VERSION(reg), CPSW_MINOR_VERSION(reg), + CPSW_RTL_VERSION(reg)); + + /* initialize host and slave ports */ + cpsw_init_host_port(priv); + for_each_slave(priv, cpsw_slave_open, priv); + + /* setup tx dma to fixed prio and zero offset */ + cpdma_control_set(priv->dma, CPDMA_TX_PRIO_FIXED, 1); + cpdma_control_set(priv->dma, CPDMA_RX_BUFFER_OFFSET, 0); + + /* disable priority elevation and enable statistics on all ports */ + __raw_writel(0, &priv->regs->ptype); + + /* enable statistics collection only on the host port */ + __raw_writel(0x7, &priv->regs->stat_port_en); + + if (WARN_ON(!priv->data.rx_descs)) + priv->data.rx_descs = 128; + + for (i = 0; i < priv->data.rx_descs; i++) { + struct sk_buff *skb; + + ret = -ENOMEM; + skb = netdev_alloc_skb_ip_align(priv->ndev, + priv->rx_packet_max); + if (!skb) + break; + ret = cpdma_chan_submit(priv->rxch, skb, skb->data, + skb_tailroom(skb), GFP_KERNEL); + if (WARN_ON(ret < 0)) + break; + } + /* continue even if we didn't manage to submit all receive descs */ + cpsw_info(priv, ifup, "submitted %d rx descriptors\n", i); + + cpdma_ctlr_start(priv->dma); + cpsw_intr_enable(priv); + napi_enable(&priv->napi); + cpdma_ctlr_eoi(priv->dma); + + return 0; +} + +static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_priv *priv) +{ + if (!slave->phy) + return; + phy_stop(slave->phy); + phy_disconnect(slave->phy); + slave->phy = NULL; +} + +static int cpsw_ndo_stop(struct net_device *ndev) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + + cpsw_info(priv, ifdown, "shutting down cpsw device\n"); + cpsw_intr_disable(priv); + cpdma_ctlr_int_ctrl(priv->dma, false); + cpdma_ctlr_stop(priv->dma); + netif_stop_queue(priv->ndev); + napi_disable(&priv->napi); + netif_carrier_off(priv->ndev); + cpsw_ale_stop(priv->ale); + for_each_slave(priv, cpsw_slave_stop, priv); + clk_disable(priv->clk); + return 0; +} + +static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + int ret; + + ndev->trans_start = jiffies; + + if (skb_padto(skb, CPSW_MIN_PACKET_SIZE)) { + cpsw_err(priv, tx_err, "packet pad failed\n"); + priv->stats.tx_dropped++; + return NETDEV_TX_OK; + } + + ret = cpdma_chan_submit(priv->txch, skb, skb->data, + skb->len, GFP_KERNEL); + if (unlikely(ret != 0)) { + cpsw_err(priv, tx_err, "desc submit failed\n"); + goto fail; + } + + return NETDEV_TX_OK; +fail: + priv->stats.tx_dropped++; + netif_stop_queue(ndev); + return NETDEV_TX_BUSY; +} + +static void cpsw_ndo_change_rx_flags(struct net_device *ndev, int flags) +{ + /* + * The switch cannot operate in promiscuous mode without substantial + * headache. For promiscuous mode to work, we would need to put the + * ALE in bypass mode and route all traffic to the host port. + * Subsequently, the host will need to operate as a "bridge", learn, + * and flood as needed. For now, we simply complain here and + * do nothing about it :-) + */ + if ((flags & IFF_PROMISC) && (ndev->flags & IFF_PROMISC)) + dev_err(&ndev->dev, "promiscuity ignored!\n"); + + /* + * The switch cannot filter multicast traffic unless it is configured + * in "VLAN Aware" mode. Unfortunately, VLAN awareness requires a + * whole bunch of additional logic that this driver does not implement + * at present. + */ + if ((flags & IFF_ALLMULTI) && !(ndev->flags & IFF_ALLMULTI)) + dev_err(&ndev->dev, "multicast traffic cannot be filtered!\n"); +} + +static void cpsw_ndo_tx_timeout(struct net_device *ndev) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + + cpsw_err(priv, tx_err, "transmit timeout, restarting dma\n"); + priv->stats.tx_errors++; + cpsw_intr_disable(priv); + cpdma_ctlr_int_ctrl(priv->dma, false); + cpdma_chan_stop(priv->txch); + cpdma_chan_start(priv->txch); + cpdma_ctlr_int_ctrl(priv->dma, true); + cpsw_intr_enable(priv); + cpdma_ctlr_eoi(priv->dma); +} + +static struct net_device_stats *cpsw_ndo_get_stats(struct net_device *ndev) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + return &priv->stats; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void cpsw_ndo_poll_controller(struct net_device *ndev) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + + cpsw_intr_disable(priv); + cpdma_ctlr_int_ctrl(priv->dma, false); + cpsw_interrupt(ndev->irq, priv); + cpdma_ctlr_int_ctrl(priv->dma, true); + cpsw_intr_enable(priv); + cpdma_ctlr_eoi(priv->dma); +} +#endif + +static const struct net_device_ops cpsw_netdev_ops = { + .ndo_open = cpsw_ndo_open, + .ndo_stop = cpsw_ndo_stop, + .ndo_start_xmit = cpsw_ndo_start_xmit, + .ndo_change_rx_flags = cpsw_ndo_change_rx_flags, + .ndo_validate_addr = eth_validate_addr, + .ndo_tx_timeout = cpsw_ndo_tx_timeout, + .ndo_get_stats = cpsw_ndo_get_stats, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = cpsw_ndo_poll_controller, +#endif +}; + +static void cpsw_get_drvinfo(struct net_device *ndev, + struct ethtool_drvinfo *info) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + strcpy(info->driver, "TI CPSW Driver v1.0"); + strcpy(info->version, "1.0"); + strcpy(info->bus_info, priv->pdev->name); +} + +static u32 cpsw_get_msglevel(struct net_device *ndev) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + return priv->msg_enable; +} + +static void cpsw_set_msglevel(struct net_device *ndev, u32 value) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + priv->msg_enable = value; +} + +static const struct ethtool_ops cpsw_ethtool_ops = { + .get_drvinfo = cpsw_get_drvinfo, + .get_msglevel = cpsw_get_msglevel, + .set_msglevel = cpsw_set_msglevel, + .get_link = ethtool_op_get_link, +}; + +static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv) +{ + void __iomem *regs = priv->regs; + int slave_num = slave->slave_num; + struct cpsw_slave_data *data = priv->data.slave_data + slave_num; + + slave->data = data; + slave->regs = regs + data->slave_reg_ofs; + slave->sliver = regs + data->sliver_reg_ofs; +} + +static int __devinit cpsw_probe(struct platform_device *pdev) +{ + struct cpsw_platform_data *data = pdev->dev.platform_data; + struct net_device *ndev; + struct cpsw_priv *priv; + struct cpdma_params dma_params; + struct cpsw_ale_params ale_params; + void __iomem *regs; + struct resource *res; + int ret = 0, i, k = 0; + + if (!data) { + pr_err("platform data missing\n"); + return -ENODEV; + } + + ndev = alloc_etherdev(sizeof(struct cpsw_priv)); + if (!ndev) { + pr_err("error allocating net_device\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, ndev); + priv = netdev_priv(ndev); + spin_lock_init(&priv->lock); + priv->data = *data; + priv->pdev = pdev; + priv->ndev = ndev; + priv->dev = &ndev->dev; + priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG); + priv->rx_packet_max = max(rx_packet_max, 128); + + if (is_valid_ether_addr(data->slave_data[0].mac_addr)) { + memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN); + pr_info("Detected MACID = %pM", priv->mac_addr); + } else { + random_ether_addr(priv->mac_addr); + pr_info("Random MACID = %pM", priv->mac_addr); + } + + memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN); + + priv->slaves = kzalloc(sizeof(struct cpsw_slave) * data->slaves, + GFP_KERNEL); + if (!priv->slaves) { + ret = -EBUSY; + goto clean_ndev_ret; + } + for (i = 0; i < data->slaves; i++) + priv->slaves[i].slave_num = i; + + priv->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(priv->dev, "failed to get device clock)\n"); + ret = -EBUSY; + } + + priv->cpsw_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!priv->cpsw_res) { + dev_err(priv->dev, "error getting i/o resource\n"); + ret = -ENOENT; + goto clean_clk_ret; + } + + if (!request_mem_region(priv->cpsw_res->start, + resource_size(priv->cpsw_res), ndev->name)) { + dev_err(priv->dev, "failed request i/o region\n"); + ret = -ENXIO; + goto clean_clk_ret; + } + + regs = ioremap(priv->cpsw_res->start, resource_size(priv->cpsw_res)); + if (!regs) { + dev_err(priv->dev, "unable to map i/o region\n"); + goto clean_cpsw_iores_ret; + } + priv->regs = regs; + priv->host_port = data->host_port_num; + priv->host_port_regs = regs + data->host_port_reg_ofs; + + priv->cpsw_ss_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!priv->cpsw_ss_res) { + dev_err(priv->dev, "error getting i/o resource\n"); + ret = -ENOENT; + goto clean_clk_ret; + } + + if (!request_mem_region(priv->cpsw_ss_res->start, + resource_size(priv->cpsw_ss_res), ndev->name)) { + dev_err(priv->dev, "failed request i/o region\n"); + ret = -ENXIO; + goto clean_clk_ret; + } + + regs = ioremap(priv->cpsw_ss_res->start, + resource_size(priv->cpsw_ss_res)); + if (!regs) { + dev_err(priv->dev, "unable to map i/o region\n"); + goto clean_cpsw_ss_iores_ret; + } + priv->ss_regs = regs; + + for_each_slave(priv, cpsw_slave_init, priv); + + memset(&dma_params, 0, sizeof(dma_params)); + dma_params.dev = &pdev->dev; + dma_params.dmaregs = cpsw_dma_regs((u32)priv->regs, + data->cpdma_reg_ofs); + dma_params.rxthresh = cpsw_dma_rxthresh((u32)priv->regs, + data->cpdma_reg_ofs); + dma_params.rxfree = cpsw_dma_rxfree((u32)priv->regs, + data->cpdma_reg_ofs); + dma_params.txhdp = cpsw_dma_txhdp((u32)priv->regs, + data->cpdma_sram_ofs); + dma_params.rxhdp = cpsw_dma_rxhdp((u32)priv->regs, + data->cpdma_sram_ofs); + dma_params.txcp = cpsw_dma_txcp((u32)priv->regs, + data->cpdma_sram_ofs); + dma_params.rxcp = cpsw_dma_rxcp((u32)priv->regs, + data->cpdma_sram_ofs); + + dma_params.num_chan = data->channels; + dma_params.has_soft_reset = true; + dma_params.min_packet_size = CPSW_MIN_PACKET_SIZE; + dma_params.desc_mem_size = data->bd_ram_size; + dma_params.desc_align = 16; + dma_params.has_ext_regs = true; + dma_params.desc_mem_phys = data->no_bd_ram ? 0 : + (u32 __force)priv->cpsw_res->start + data->bd_ram_ofs; + dma_params.desc_hw_addr = data->hw_ram_addr ? + data->hw_ram_addr : dma_params.desc_mem_phys ; + + priv->dma = cpdma_ctlr_create(&dma_params); + if (!priv->dma) { + dev_err(priv->dev, "error initializing dma\n"); + ret = -ENOMEM; + goto clean_iomap_ret; + } + + priv->txch = cpdma_chan_create(priv->dma, tx_chan_num(0), + cpsw_tx_handler); + priv->rxch = cpdma_chan_create(priv->dma, rx_chan_num(0), + cpsw_rx_handler); + + if (WARN_ON(!priv->txch || !priv->rxch)) { + dev_err(priv->dev, "error initializing dma channels\n"); + ret = -ENOMEM; + goto clean_dma_ret; + } + + memset(&ale_params, 0, sizeof(ale_params)); + ale_params.dev = &ndev->dev; + ale_params.ale_regs = (void *)((u32)priv->regs) + + ((u32)data->ale_reg_ofs); + ale_params.ale_ageout = ale_ageout; + ale_params.ale_entries = data->ale_entries; + ale_params.ale_ports = data->slaves; + + priv->ale = cpsw_ale_create(&ale_params); + if (!priv->ale) { + dev_err(priv->dev, "error initializing ale engine\n"); + ret = -ENODEV; + goto clean_dma_ret; + } + + ndev->irq = platform_get_irq(pdev, 0); + if (ndev->irq < 0) { + dev_err(priv->dev, "error getting irq resource\n"); + ret = -ENOENT; + goto clean_ale_ret; + } + + while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) { + for (i = res->start; i <= res->end; i++) { + if (request_irq(i, cpsw_interrupt, IRQF_DISABLED, + dev_name(&pdev->dev), priv)) { + dev_err(priv->dev, "error attaching irq\n"); + goto clean_ale_ret; + } + priv->irqs_table[k] = i; + priv->num_irqs = k; + } + k++; + } + + ndev->flags |= IFF_ALLMULTI; /* see cpsw_ndo_change_rx_flags() */ + + ndev->netdev_ops = &cpsw_netdev_ops; + SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops); + netif_napi_add(ndev, &priv->napi, cpsw_poll, CPSW_POLL_WEIGHT); + + /* register the network device */ + SET_NETDEV_DEV(ndev, &pdev->dev); + ret = register_netdev(ndev); + if (ret) { + dev_err(priv->dev, "error registering net device\n"); + ret = -ENODEV; + goto clean_irq_ret; + } + + cpsw_notice(priv, probe, "initialized device (regs %x, irq %d)\n", + priv->cpsw_res->start, ndev->irq); + + return 0; + +clean_irq_ret: + free_irq(ndev->irq, priv); +clean_ale_ret: + cpsw_ale_destroy(priv->ale); +clean_dma_ret: + cpdma_chan_destroy(priv->txch); + cpdma_chan_destroy(priv->rxch); + cpdma_ctlr_destroy(priv->dma); +clean_iomap_ret: + iounmap(priv->regs); +clean_cpsw_ss_iores_ret: + release_mem_region(priv->cpsw_ss_res->start, + resource_size(priv->cpsw_ss_res)); +clean_cpsw_iores_ret: + release_mem_region(priv->cpsw_res->start, + resource_size(priv->cpsw_res)); +clean_clk_ret: + clk_put(priv->clk); + kfree(priv->slaves); +clean_ndev_ret: + free_netdev(ndev); + return ret; +} + +static int __devexit cpsw_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct cpsw_priv *priv = netdev_priv(ndev); + + pr_info("removing device"); + platform_set_drvdata(pdev, NULL); + + free_irq(ndev->irq, priv); + cpsw_ale_destroy(priv->ale); + cpdma_chan_destroy(priv->txch); + cpdma_chan_destroy(priv->rxch); + cpdma_ctlr_destroy(priv->dma); + iounmap(priv->regs); + release_mem_region(priv->cpsw_res->start, + resource_size(priv->cpsw_res)); + release_mem_region(priv->cpsw_ss_res->start, + resource_size(priv->cpsw_ss_res)); + clk_put(priv->clk); + kfree(priv->slaves); + free_netdev(ndev); + + return 0; +} + +static int cpsw_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct net_device *ndev = platform_get_drvdata(pdev); + + if (netif_running(ndev)) + cpsw_ndo_stop(ndev); + return 0; +} + +static int cpsw_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct net_device *ndev = platform_get_drvdata(pdev); + + if (netif_running(ndev)) + cpsw_ndo_open(ndev); + return 0; +} + +static const struct dev_pm_ops cpsw_pm_ops = { + .suspend = cpsw_suspend, + .resume = cpsw_resume, +}; + +static struct platform_driver cpsw_driver = { + .driver = { + .name = "cpsw", + .owner = THIS_MODULE, + .pm = &cpsw_pm_ops, + }, + .probe = cpsw_probe, + .remove = __devexit_p(cpsw_remove), +}; + +static int __init cpsw_init(void) +{ + return platform_driver_register(&cpsw_driver); +} +late_initcall(cpsw_init); + +static void __exit cpsw_exit(void) +{ + platform_driver_unregister(&cpsw_driver); +} +module_exit(cpsw_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Cyril Chemparathy "); +MODULE_AUTHOR("Mugunthan V N "); +MODULE_DESCRIPTION("TI CPSW Ethernet driver"); diff --git a/include/linux/platform_data/cpsw.h b/include/linux/platform_data/cpsw.h new file mode 100644 index 000000000000..c4e23d029498 --- /dev/null +++ b/include/linux/platform_data/cpsw.h @@ -0,0 +1,55 @@ +/* + * Texas Instruments Ethernet Switch Driver + * + * Copyright (C) 2012 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __CPSW_H__ +#define __CPSW_H__ + +#include + +struct cpsw_slave_data { + u32 slave_reg_ofs; + u32 sliver_reg_ofs; + const char *phy_id; + int phy_if; + u8 mac_addr[ETH_ALEN]; +}; + +struct cpsw_platform_data { + u32 ss_reg_ofs; /* Subsystem control register offset */ + u32 channels; /* number of cpdma channels (symmetric) */ + u32 cpdma_reg_ofs; /* cpdma register offset */ + u32 cpdma_sram_ofs; /* cpdma sram offset */ + + u32 slaves; /* number of slave cpgmac ports */ + struct cpsw_slave_data *slave_data; + + u32 ale_reg_ofs; /* address lookup engine reg offset */ + u32 ale_entries; /* ale table size */ + + u32 host_port_reg_ofs; /* cpsw cpdma host port registers */ + u32 host_port_num; /* The port number for the host port */ + + u32 hw_stats_reg_ofs; /* cpsw hardware statistics counters */ + + u32 bd_ram_ofs; /* embedded buffer descriptor RAM offset*/ + u32 bd_ram_size; /*buffer descriptor ram size */ + u32 hw_ram_addr; /*if the HW address for BD RAM is different */ + bool no_bd_ram; /* no embedded BD ram*/ + + u32 rx_descs; /* Number of Rx Descriptios */ + + u32 mac_control; /* Mac control register */ +}; + +#endif /* __CPSW_H__ */ -- cgit From bf362f750bea1372aff3b5c405b50560a1b28981 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 19 Mar 2012 00:01:09 +0000 Subject: constify struct pci_dev * in obvious cases Signed-off-by: Al Viro Signed-off-by: Jiri Kosina --- arch/alpha/include/asm/machvec.h | 2 +- arch/alpha/kernel/sys_dp264.c | 2 +- include/linux/pci.h | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/arch/alpha/include/asm/machvec.h b/arch/alpha/include/asm/machvec.h index 13cd42743810..72dbf2359270 100644 --- a/arch/alpha/include/asm/machvec.h +++ b/arch/alpha/include/asm/machvec.h @@ -90,7 +90,7 @@ struct alpha_machine_vector void (*kill_arch)(int); u8 (*pci_swizzle)(struct pci_dev *, u8 *); - int (*pci_map_irq)(struct pci_dev *, u8, u8); + int (*pci_map_irq)(const struct pci_dev *, u8, u8); struct pci_ops *pci_ops; struct _alpha_agp_info *(*agp_info)(void); diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c index bb7f0c7cb17a..13f0717fc7fe 100644 --- a/arch/alpha/kernel/sys_dp264.c +++ b/arch/alpha/kernel/sys_dp264.c @@ -366,7 +366,7 @@ clipper_init_irq(void) */ static int __init -isa_irq_fixup(struct pci_dev *dev, int irq) +isa_irq_fixup(const struct pci_dev *dev, int irq) { u8 irq8; diff --git a/include/linux/pci.h b/include/linux/pci.h index a16b1df3deff..766afe93d5d3 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -746,28 +746,28 @@ int pci_bus_write_config_dword(struct pci_bus *bus, unsigned int devfn, int where, u32 val); struct pci_ops *pci_bus_set_ops(struct pci_bus *bus, struct pci_ops *ops); -static inline int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val) +static inline int pci_read_config_byte(const struct pci_dev *dev, int where, u8 *val) { return pci_bus_read_config_byte(dev->bus, dev->devfn, where, val); } -static inline int pci_read_config_word(struct pci_dev *dev, int where, u16 *val) +static inline int pci_read_config_word(const struct pci_dev *dev, int where, u16 *val) { return pci_bus_read_config_word(dev->bus, dev->devfn, where, val); } -static inline int pci_read_config_dword(struct pci_dev *dev, int where, +static inline int pci_read_config_dword(const struct pci_dev *dev, int where, u32 *val) { return pci_bus_read_config_dword(dev->bus, dev->devfn, where, val); } -static inline int pci_write_config_byte(struct pci_dev *dev, int where, u8 val) +static inline int pci_write_config_byte(const struct pci_dev *dev, int where, u8 val) { return pci_bus_write_config_byte(dev->bus, dev->devfn, where, val); } -static inline int pci_write_config_word(struct pci_dev *dev, int where, u16 val) +static inline int pci_write_config_word(const struct pci_dev *dev, int where, u16 val) { return pci_bus_write_config_word(dev->bus, dev->devfn, where, val); } -static inline int pci_write_config_dword(struct pci_dev *dev, int where, +static inline int pci_write_config_dword(const struct pci_dev *dev, int where, u32 val) { return pci_bus_write_config_dword(dev->bus, dev->devfn, where, val); -- cgit